// // SJA1000 CAN 控制器外设实现(简化版,支持接收队列,可配置长度) // 仅支持 PeliCAN 模式的标准帧,不考虑 BasicCAN、扩展帧、错误处理、多帧、验收滤波和发送中断。 // 寄存器地址映射基于 PeliCAN 模式,包含 MOD, CMR, SR, IR, IER, BTR0, BTR1, OCR, RXERR, TXERR, // 发送缓冲区(地址 16-26)、RBSA 和 CDR。命令寄存器位2=RRB,位3=CDO。 // 所有寄存器可随时读写,无复位模式限制。 // // 接收队列长度可通过修改静态字段 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; using Antmicro.Renode.Utilities; using Antmicro.Renode.Time; namespace Antmicro.Renode.Peripherals.CustomPeripherals { /// /// 简化版 SJA1000 CAN 控制器(PeliCAN 模式,仅标准帧,立即发送,TCP 转发) /// public class SJA1000_CAN : IDoubleWordPeripheral, IBytePeripheral, IKnownSize, IDisposable { // 日志开关(设置为 false 可屏蔽所有日志) public static bool EnableVerboseLog = false; // 接收队列长度配置(可修改) 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; IRQ = new GPIO(); txBuffer = new byte[11]; rxBuffer = new byte[11]; rxFrameQueue = new Queue(); // 启动共享 TCP Server(增加引用计数) StartSharedTcpServer(); Reset(); } public void Reset() { mod = 0; ier = 0; btr0 = 0; btr1 = 0; ocr = 0; rxerr = 0; txerr = 0; rbsa = 0; cdr = 0; sr_tbs = 1; // 发送缓冲区始终空闲(立即发送模式) sr_rbs = 0; // 接收缓冲区初始为空 ir = 0; Array.Clear(txBuffer, 0, txBuffer.Length); Array.Clear(rxBuffer, 0, rxBuffer.Length); rxFrameQueue.Clear(); lastInterruptState = false; UpdateInterrupts(); if (EnableVerboseLog) this.Log(LogLevel.Info, $"SJA1000 CAN controller reset (rxQueueMax={MaxRxQueueSize})"); } // ================ 共享 TCP Server 管理 ================ private void StartSharedTcpServer() { 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)"); } } private void StopSharedTcpServer() { 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; lock (lockObject) { switch ((Registers)offset) { case Registers.MOD: value = mod; if (EnableVerboseLog) this.Log(LogLevel.Info, "Read MOD: 0x{0:X2}", value); break; case Registers.CMR: if (EnableVerboseLog) this.Log(LogLevel.Info, "Read CMR (always 0)"); value = 0; break; case Registers.SR: 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; case Registers.IR: value = ir; ir = 0; UpdateIrFromRbs(); lastInterruptState = false; UpdateInterrupts(); if (EnableVerboseLog) this.Log(LogLevel.Info, "Read IR: 0x{0:X2}, cleared", value); break; case Registers.IER: value = ier; if (EnableVerboseLog) this.Log(LogLevel.Info, "Read IER: 0x{0:X2}", value); break; case Registers.BTR0: value = btr0; break; case Registers.BTR1: value = btr1; break; case Registers.OCR: value = ocr; break; case Registers.RXERR: value = rxerr; break; case Registers.TXERR: value = txerr; break; case Registers.RBSA: value = rbsa; break; case Registers.CDR: value = cdr; break; case Registers.TX_FRAME_INFO: case Registers.TX_ID1: case Registers.TX_ID2: case Registers.TX_DATA1: case Registers.TX_DATA2: case Registers.TX_DATA3: case Registers.TX_DATA4: case Registers.TX_DATA5: case Registers.TX_DATA6: case Registers.TX_DATA7: case Registers.TX_DATA8: int bufIndex = (int)(offset - (long)Registers.TX_FRAME_INFO) / 4; if (sr_rbs == 1) { value = rxBuffer[bufIndex]; if (EnableVerboseLog) this.Log(LogLevel.Info, "Read RX buffer[{0}]: 0x{1:X2}", bufIndex, value); } else { value = txBuffer[bufIndex]; if (EnableVerboseLog) this.Log(LogLevel.Info, "Read TX buffer[{0}]: 0x{1:X2}", bufIndex, value); } break; default: if (EnableVerboseLog) this.Log(LogLevel.Info, "Read from unimplemented offset 0x{0:X}", offset); value = 0; break; } } return value; } public void WriteByte(long offset, byte value) { lock (lockObject) { switch ((Registers)offset) { case Registers.MOD: mod = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write MOD: 0x{0:X2}", value); break; case Registers.CMR: if (EnableVerboseLog) this.Log(LogLevel.Info, "Write CMR: 0x{0:X2}", value); if ((value & CMR_TR) != 0) { // 发送请求(立即发送模式) if (sr_tbs == 1) { // 复制当前帧 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 { if (EnableVerboseLog) this.Log(LogLevel.Warning, "Send request while TBS=0 ignored"); } } 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; case Registers.SR: if (EnableVerboseLog) this.Log(LogLevel.Warning, "Attempted write to read-only SR"); break; case Registers.IR: if (EnableVerboseLog) this.Log(LogLevel.Warning, "Attempted write to read-only IR"); break; case Registers.IER: ier = (byte)(value & 0x01); UpdateIrFromRbs(); UpdateInterrupts(); if (EnableVerboseLog) this.Log(LogLevel.Info, "Write IER: 0x{0:X2}", ier); break; case Registers.BTR0: btr0 = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write BTR0: 0x{0:X2}", value); break; case Registers.BTR1: btr1 = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write BTR1: 0x{0:X2}", value); break; case Registers.OCR: ocr = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write OCR: 0x{0:X2}", value); break; case Registers.RXERR: rxerr = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write RXERR: 0x{0:X2}", value); break; case Registers.TXERR: txerr = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write TXERR: 0x{0:X2}", value); break; case Registers.RBSA: rbsa = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write RBSA: 0x{0:X2}", value); break; case Registers.CDR: cdr = value; 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: case Registers.TX_DATA1: case Registers.TX_DATA2: case Registers.TX_DATA3: case Registers.TX_DATA4: case Registers.TX_DATA5: case Registers.TX_DATA6: case Registers.TX_DATA7: case Registers.TX_DATA8: int bufIndex = (int)(offset - (long)Registers.TX_FRAME_INFO) / 4; if (sr_tbs == 1) { txBuffer[bufIndex] = value; if (EnableVerboseLog) this.Log(LogLevel.Info, "Write TX buffer[{0}]: 0x{1:X2}", bufIndex, value); } else { if (EnableVerboseLog) this.Log(LogLevel.Warning, "Write to TX buffer while TBS=0 ignored"); } break; default: if (EnableVerboseLog) this.Log(LogLevel.Info, "Write to unimplemented offset 0x{0:X} = 0x{1:X2}", offset, value); break; } } } // ================ 接收与中断管理 ================ private void UpdateIrFromRbs() { if (sr_rbs == 1) ir |= IR_RI; else ir = (byte)(ir & ~IR_RI); } private void UpdateInterrupts() { bool currentInterrupt = ((ir & IR_RI) != 0) && ((ier & IER_RIE) != 0); if (currentInterrupt && !lastInterruptState) { IRQ.Set(true); 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 GetRxBufferDataString() { lock (lockObject) { if (rxFrameQueue.Count == 0) { if (EnableVerboseLog) Console.WriteLine("GetRxBufferDataString: no frames in RX queue"); return null; } var sb = new StringBuilder(); while (rxFrameQueue.Count > 0) { var frame = rxFrameQueue.Dequeue(); for (int i = 0; i < frame.Length; i++) { if (i > 0) sb.Append(" "); sb.Append(frame[i].ToString("X2")); } sb.AppendLine(); } string result = sb.ToString().TrimEnd(); if (EnableVerboseLog) Console.WriteLine("GetRxBufferDataString returning: " + result); return result; } } /// /// 外部接口注入一帧接收数据(可变长度的十六进制字符串,空格分隔) /// 可多次调用以注入多帧,帧将存入接收队列。 /// 若接收队列已满,则新帧被丢弃。 /// 输入字符串长度可变(0~11 字节),不足 11 字节时自动补零,超出 11 字节时截断并警告。 /// public void SendRxBufferDataString(string frameString) { lock (lockObject) { if (string.IsNullOrWhiteSpace(frameString)) { if (EnableVerboseLog) Console.WriteLine("SendRxBufferDataString: empty string, ignored"); return; } string[] parts = frameString.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); int length = parts.Length; if (length > 11) { if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: input has {length} bytes, truncating to 11"); length = 11; } byte[] frame = new byte[11]; for (int i = 0; i < length; i++) { string part = parts[i].Trim(); if (!byte.TryParse(part, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out byte b)) { if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: invalid hex byte '{part}' at index {i}, set to 0"); b = 0; } frame[i] = b; } if (rxFrameQueue.Count >= MaxRxQueueSize) { if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: RX queue full, frame dropped"); return; } rxFrameQueue.Enqueue(frame); if (EnableVerboseLog) Console.WriteLine($"Frame enqueued to RX queue, size: {rxFrameQueue.Count}"); if (sr_rbs == 0) { LoadNextRxFrame(); UpdateInterrupts(); } } } private void LoadNextRxFrame() { if (rxFrameQueue.Count > 0) { var frame = rxFrameQueue.Dequeue(); Array.Copy(frame, rxBuffer, 11); sr_rbs = 1; UpdateIrFromRbs(); if (EnableVerboseLog) Console.WriteLine("Loaded next RX frame, remaining: {0}", rxFrameQueue.Count); } else { sr_rbs = 0; UpdateIrFromRbs(); 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, CMR = 0x04, SR = 0x08, IR = 0x0C, IER = 0x10, BTR0 = 0x18, BTR1 = 0x1C, OCR = 0x20, RXERR = 0x38, TXERR = 0x3C, TX_FRAME_INFO = 0x40, TX_ID1 = 0x44, TX_ID2 = 0x48, TX_DATA1 = 0x4C, TX_DATA2 = 0x50, TX_DATA3 = 0x54, TX_DATA4 = 0x58, TX_DATA5 = 0x5C, TX_DATA6 = 0x60, TX_DATA7 = 0x64, TX_DATA8 = 0x68, RBSA = 0x78, CDR = 0x7C, } // ================ 常量位定义 ================ private const byte CMR_TR = 0x01; private const byte CMR_RRB = 0x04; private const byte CMR_CDO = 0x08; private const byte SR_TBS = 0x04; private const byte SR_RBS = 0x01; private const byte IR_RI = 0x01; private const byte IER_RIE = 0x01; // ================ 私有字段 ================ private readonly IMachine machine; private byte mod; private byte ier; private byte btr0, btr1; private byte ocr; private byte rxerr, txerr; private byte rbsa; private byte cdr; private byte sr_tbs; private byte sr_rbs; private byte ir; private readonly byte[] txBuffer; private readonly byte[] rxBuffer; private readonly Queue rxFrameQueue; // 仅接收队列 } }