diff --git a/SJA1000_CAN.cs b/SJA1000_CAN.cs index c9cfdc8..f1b1bf1 100644 --- a/SJA1000_CAN.cs +++ b/SJA1000_CAN.cs @@ -1,20 +1,26 @@ // -// SJA1000 CAN 控制器外设实现(简化版,支持发送/接收队列,可配置长度) +// SJA1000 CAN 控制器外设实现(简化版,支持接收队列,可配置长度) // 仅支持 PeliCAN 模式的标准帧,不考虑 BasicCAN、扩展帧、错误处理、多帧、验收滤波和发送中断。 // 寄存器地址映射基于 PeliCAN 模式,包含 MOD, CMR, SR, IR, IER, BTR0, BTR1, OCR, RXERR, TXERR, // 发送缓冲区(地址 16-26)、RBSA 和 CDR。命令寄存器位2=RRB,位3=CDO。 // 所有寄存器可随时读写,无复位模式限制。 // -// 队列长度可通过修改静态字段 MaxTxQueueSize 和 MaxRxQueueSize 调整。 -// 发送队列满时,CPU 发送请求被忽略,并设置 TBS=0(发送缓冲区忙); +// 接收队列长度可通过修改静态字段 MaxRxQueueSize 调整。 // 接收队列满时,外部注入的新帧被丢弃。 // +// 发送采用立即发送模式(无波特率延迟),每帧发送完成后触发 FrameTransmitted 事件并转发到 TCP 客户端。 +// TCP Server 为静态共享实例,监听端口 10000,将发送的帧数据转发给所有连接的客户端。 // 日志开关:EnableVerboseLog = false 可关闭所有日志输出。 +// 实现了 IDisposable 接口,TCP 服务器由所有实例共享,仅当最后一个实例销毁时停止服务器。 // using System; using System.Text; using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; using Antmicro.Renode.Core; using Antmicro.Renode.Logging; using Antmicro.Renode.Peripherals.Bus; @@ -24,17 +30,28 @@ using Antmicro.Renode.Time; namespace Antmicro.Renode.Peripherals.CustomPeripherals { /// - /// 简化版 SJA1000 CAN 控制器(PeliCAN 模式,仅标准帧) + /// 简化版 SJA1000 CAN 控制器(PeliCAN 模式,仅标准帧,立即发送,TCP 转发) /// - public class SJA1000_CAN : IDoubleWordPeripheral, IBytePeripheral, IKnownSize + public class SJA1000_CAN : IDoubleWordPeripheral, IBytePeripheral, IKnownSize, IDisposable { // 日志开关(设置为 false 可屏蔽所有日志) public static bool EnableVerboseLog = false; - // 队列长度配置(可修改) - public static int MaxTxQueueSize = 64; // 发送队列最大帧数 + // 接收队列长度配置(可修改) public static int MaxRxQueueSize = 64; // 接收队列最大帧数 + // 发送事件:当一帧数据被发送时触发 + public event Action FrameTransmitted; + + // ================ 静态 TCP Server 共享资源 ================ + private static TcpListener sharedTcpListener; + private static readonly List sharedTcpClients = new List(); + private static readonly object sharedTcpLock = new object(); + private static CancellationTokenSource sharedTcpCts; + private static Task sharedTcpServerTask; + private static int sharedTcpRefCount = 0; // 引用计数 + private bool disposed = false; + public SJA1000_CAN(IMachine machine) { this.machine = machine; @@ -42,8 +59,10 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals txBuffer = new byte[11]; rxBuffer = new byte[11]; - txFrameQueue = new Queue(); // 发送队列 - rxFrameQueue = new Queue(); // 接收队列 + rxFrameQueue = new Queue(); + + // 启动共享 TCP Server(增加引用计数) + StartSharedTcpServer(); Reset(); } @@ -60,34 +79,154 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals rbsa = 0; cdr = 0; - sr_tbs = 1; // 发送缓冲区初始为空闲 + sr_tbs = 1; // 发送缓冲区始终空闲(立即发送模式) sr_rbs = 0; // 接收缓冲区初始为空 ir = 0; Array.Clear(txBuffer, 0, txBuffer.Length); Array.Clear(rxBuffer, 0, rxBuffer.Length); - txFrameQueue.Clear(); rxFrameQueue.Clear(); - lastInterruptState = false; // 重置中断边沿状态 + lastInterruptState = false; UpdateInterrupts(); if (EnableVerboseLog) - this.Log(LogLevel.Info, $"SJA1000 CAN controller reset (txQueueMax={MaxTxQueueSize}, rxQueueMax={MaxRxQueueSize})"); + this.Log(LogLevel.Info, $"SJA1000 CAN controller reset (rxQueueMax={MaxRxQueueSize})"); } - // ================ IBusPeripheral 接口实现 ================ - - public uint ReadDoubleWord(long offset) + // ================ 共享 TCP Server 管理 ================ + private void StartSharedTcpServer() { - return ReadByte(offset); + lock (sharedTcpLock) + { + sharedTcpRefCount++; + if (sharedTcpRefCount > 1) + { + if (EnableVerboseLog) + Console.WriteLine("TCP Server already running, using existing instance."); + return; + } + // 确保之前的已停止(安全) + StopSharedTcpServerInternal(); + sharedTcpCts = new CancellationTokenSource(); + sharedTcpServerTask = Task.Run(() => TcpServerLoop(sharedTcpCts.Token), sharedTcpCts.Token); + if (EnableVerboseLog) + Console.WriteLine("TCP Server started on port 10000 (shared)"); + } } - public void WriteDoubleWord(long offset, uint value) + private void StopSharedTcpServer() { - WriteByte(offset, (byte)value); + lock (sharedTcpLock) + { + if (sharedTcpRefCount > 0) + sharedTcpRefCount--; + if (sharedTcpRefCount > 0) + { + if (EnableVerboseLog) + Console.WriteLine("TCP Server still in use, not stopping."); + return; + } + StopSharedTcpServerInternal(); + } } + private static void StopSharedTcpServerInternal() + { + if (sharedTcpCts != null) + { + sharedTcpCts.Cancel(); + try { sharedTcpServerTask?.Wait(1000); } catch { } + sharedTcpCts.Dispose(); + sharedTcpCts = null; + sharedTcpServerTask = null; + } + if (sharedTcpListener != null) + { + try { sharedTcpListener.Stop(); } catch { } + sharedTcpListener = null; + } + lock (sharedTcpLock) + { + foreach (var s in sharedTcpClients) + s?.Close(); + sharedTcpClients.Clear(); + } + if (EnableVerboseLog) + Console.WriteLine("TCP Server stopped."); + } + + private static void TcpServerLoop(CancellationToken token) + { + try + { + sharedTcpListener = new TcpListener(IPAddress.Any, 10000); + sharedTcpListener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + sharedTcpListener.Start(); + while (!token.IsCancellationRequested) + { + var clientTask = sharedTcpListener.AcceptTcpClientAsync(); + clientTask.Wait(token); + if (clientTask.IsCompleted && !token.IsCancellationRequested) + { + var client = clientTask.Result; + var stream = client.GetStream(); + lock (sharedTcpLock) + { + sharedTcpClients.Add(stream); + } + if (EnableVerboseLog) + Console.WriteLine($"TCP client connected from {client.Client.RemoteEndPoint}"); + } + } + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + if (EnableVerboseLog) + Console.WriteLine($"TCP Server error: {ex.Message}"); + } + finally + { + sharedTcpListener?.Stop(); + lock (sharedTcpLock) + { + foreach (var s in sharedTcpClients) + s?.Close(); + sharedTcpClients.Clear(); + } + } + } + + private void BroadcastFrame(byte[] frame) + { + if (frame == null || frame.Length != 11) return; + lock (sharedTcpLock) + { + var clients = sharedTcpClients.ToArray(); + foreach (var stream in clients) + { + try + { + stream.Write(frame, 0, frame.Length); + stream.Flush(); + } + catch (Exception ex) + { + if (EnableVerboseLog) + Console.WriteLine($"Failed to send frame to client: {ex.Message}"); + sharedTcpClients.Remove(stream); + stream.Close(); + } + } + } + } + + // ================ IBusPeripheral 接口实现(完全不变) ================ + + public uint ReadDoubleWord(long offset) => ReadByte(offset); + public void WriteDoubleWord(long offset, uint value) => WriteByte(offset, (byte)value); + public byte ReadByte(long offset) { byte value = 0; @@ -106,7 +245,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals break; case Registers.SR: - value = (byte)((sr_tbs << 2) | sr_rbs); // BS 恒为0 + value = (byte)((sr_tbs << 2) | sr_rbs); if (EnableVerboseLog) this.Log(LogLevel.Info, "Read SR: TBS={0}, RBS={1} -> 0x{2:X2}", sr_tbs, sr_rbs, value); break; @@ -114,7 +253,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals value = ir; ir = 0; UpdateIrFromRbs(); - // 中断被软件清除,重置边沿检测状态,允许下次条件满足时再次触发中断 lastInterruptState = false; UpdateInterrupts(); if (EnableVerboseLog) this.Log(LogLevel.Info, "Read IR: 0x{0:X2}, cleared", value); @@ -147,7 +285,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals value = cdr; break; - // 发送/接收缓冲区地址 16-26 case Registers.TX_FRAME_INFO: case Registers.TX_ID1: case Registers.TX_ID2: @@ -196,32 +333,23 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals if (EnableVerboseLog) this.Log(LogLevel.Info, "Write CMR: 0x{0:X2}", value); if ((value & CMR_TR) != 0) { - // 发送请求 + // 发送请求(立即发送模式) if (sr_tbs == 1) { - var frame = new byte[11]; - - if (txFrameQueue.Count < (MaxTxQueueSize - 1)) - { - Array.Copy(txBuffer, frame, 11); - txFrameQueue.Enqueue(frame); - Array.Clear(txBuffer, 0, txBuffer.Length); - sr_tbs = 1; - if (EnableVerboseLog) this.Log(LogLevel.Info, "Frame enqueued for transmission, queue size: {0}", txFrameQueue.Count); - } - else if (txFrameQueue.Count == (MaxTxQueueSize - 1)) - { - Array.Copy(txBuffer, frame, 11); - txFrameQueue.Enqueue(frame); - Array.Clear(txBuffer, 0, txBuffer.Length); - sr_tbs = 0; - if (EnableVerboseLog) this.Log(LogLevel.Info, "Frame enqueued, TX queue now full, TBS cleared"); - } - else - { - sr_tbs = 0; - if (EnableVerboseLog) this.Log(LogLevel.Warning, "Send request while TBS=0 ignored"); - } + // 复制当前帧 + byte[] frame = new byte[11]; + Array.Copy(txBuffer, frame, 11); + // 清空发送缓冲区(准备下一帧) + Array.Clear(txBuffer, 0, txBuffer.Length); + + // 立即发送帧数据 + FrameTransmitted?.Invoke(frame); + BroadcastFrame(frame); + + // 保持发送缓冲区空闲(始终允许写入) + sr_tbs = 1; + + if (EnableVerboseLog) this.Log(LogLevel.Info, "Frame transmitted immediately"); } else { @@ -230,12 +358,16 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals } if ((value & CMR_RRB) != 0) { + // 释放接收缓冲器 + sr_rbs = 0; + UpdateIrFromRbs(); + UpdateInterrupts(); LoadNextRxFrame(); + UpdateInterrupts(); if (EnableVerboseLog) this.Log(LogLevel.Info, "Receive buffer released"); } if ((value & CMR_CDO) != 0) { - // 清除数据溢出(无操作,但保留接口) if (EnableVerboseLog) this.Log(LogLevel.Info, "Clear data overflow (no effect)"); } break; @@ -284,7 +416,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals if (EnableVerboseLog) this.Log(LogLevel.Info, "Write CDR: 0x{0:X2}", value); break; - // 发送缓冲区写入 case Registers.TX_FRAME_INFO: case Registers.TX_ID1: case Registers.TX_ID2: @@ -315,8 +446,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals } } - // ================ 内部辅助方法 ================ - + // ================ 接收与中断管理 ================ private void UpdateIrFromRbs() { if (sr_rbs == 1) @@ -331,35 +461,32 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals if (currentInterrupt && !lastInterruptState) { IRQ.Set(true); - machine.ScheduleAction(TimeInterval.FromMicroseconds(1), _ => - { - IRQ.Set(false); - }); + machine.ScheduleAction(TimeInterval.FromMicroseconds(1), _ => IRQ.Set(false)); if (EnableVerboseLog) this.Log(LogLevel.Info, "Interrupt pulse generated (RI)"); } lastInterruptState = currentInterrupt; } private readonly object lockObject = new object(); + private bool lastInterruptState; /// - /// 获取发送队列中所有帧的字符串表示(每帧以空格分隔的十六进制字节,帧间用换行分隔) - /// 调用后清空发送队列,并恢复发送缓冲区状态(如果之前因队列满被锁定)。 + /// 获取接收队列中所有帧的字符串表示(每帧以空格分隔的十六进制字节,帧间用换行分隔) + /// 注意:此方法仅用于调试,会影响接收队列。 /// - public string GetTxBufferDataString() + public string GetRxBufferDataString() { lock (lockObject) { - if (txFrameQueue.Count == 0) + if (rxFrameQueue.Count == 0) { - if (EnableVerboseLog) Console.WriteLine("GetTxBufferDataString: no frames in TX queue"); - if (sr_tbs == 0) sr_tbs = 1; + if (EnableVerboseLog) Console.WriteLine("GetRxBufferDataString: no frames in RX queue"); return null; } var sb = new StringBuilder(); - while (txFrameQueue.Count > 0) + while (rxFrameQueue.Count > 0) { - var frame = txFrameQueue.Dequeue(); + var frame = rxFrameQueue.Dequeue(); for (int i = 0; i < frame.Length; i++) { if (i > 0) sb.Append(" "); @@ -368,16 +495,16 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals sb.AppendLine(); } string result = sb.ToString().TrimEnd(); - sr_tbs = 1; - if (EnableVerboseLog) Console.WriteLine("GetTxBufferDataString returning: " + result); + if (EnableVerboseLog) Console.WriteLine("GetRxBufferDataString returning: " + result); return result; } } /// - /// 外部接口注入一帧接收数据(11字节的十六进制字符串,空格分隔) + /// 外部接口注入一帧接收数据(可变长度的十六进制字符串,空格分隔) /// 可多次调用以注入多帧,帧将存入接收队列。 /// 若接收队列已满,则新帧被丢弃。 + /// 输入字符串长度可变(0~11 字节),不足 11 字节时自动补零,超出 11 字节时截断并警告。 /// public void SendRxBufferDataString(string frameString) { @@ -389,13 +516,15 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals return; } string[] parts = frameString.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length != 11) + int length = parts.Length; + if (length > 11) { - if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: expected 11 bytes, got {parts.Length}, ignored"); - return; + if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: input has {length} bytes, truncating to 11"); + length = 11; } + byte[] frame = new byte[11]; - for (int i = 0; i < 11; i++) + for (int i = 0; i < length; i++) { string part = parts[i].Trim(); if (!byte.TryParse(part, System.Globalization.NumberStyles.HexNumber, @@ -409,7 +538,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals if (rxFrameQueue.Count >= MaxRxQueueSize) { - if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: RX queue full (max={MaxRxQueueSize}), frame dropped"); + if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: RX queue full, frame dropped"); return; } @@ -419,6 +548,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals if (sr_rbs == 0) { LoadNextRxFrame(); + UpdateInterrupts(); } } } @@ -431,26 +561,38 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals Array.Copy(frame, rxBuffer, 11); sr_rbs = 1; UpdateIrFromRbs(); - UpdateInterrupts(); - if (EnableVerboseLog) Console.WriteLine("Loaded next RX frame into buffer, queue remaining: {0}", rxFrameQueue.Count); + if (EnableVerboseLog) Console.WriteLine("Loaded next RX frame, remaining: {0}", rxFrameQueue.Count); } else { sr_rbs = 0; UpdateIrFromRbs(); - UpdateInterrupts(); if (EnableVerboseLog) Console.WriteLine("RX queue empty, buffer cleared"); } } + // ================ IDisposable 实现 ================ + public void Dispose() + { + if (!disposed) + { + StopSharedTcpServer(); // 减少引用计数,可能停止服务器 + disposed = true; + } + GC.SuppressFinalize(this); + } + + ~SJA1000_CAN() + { + Dispose(); + } + // ================ 属性 ================ public long Size => 0x80; - public GPIO IRQ { get; } // ================ 寄存器枚举 ================ - private enum Registers : long { MOD = 0x00, @@ -479,12 +621,10 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals } // ================ 常量位定义 ================ - private const byte CMR_TR = 0x01; private const byte CMR_RRB = 0x04; private const byte CMR_CDO = 0x08; - private const byte SR_BS = 0x80; private const byte SR_TBS = 0x04; private const byte SR_RBS = 0x01; @@ -492,7 +632,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals private const byte IER_RIE = 0x01; // ================ 私有字段 ================ - private readonly IMachine machine; private byte mod; @@ -507,12 +646,9 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals private byte sr_rbs; private byte ir; - private bool lastInterruptState; // 用于中断边沿检测 - private readonly byte[] txBuffer; private readonly byte[] rxBuffer; - private readonly Queue txFrameQueue; - private readonly Queue rxFrameQueue; + private readonly Queue rxFrameQueue; // 仅接收队列 } } \ No newline at end of file