Files
simulation_Peripheral/SJA1000_CAN.cs
2026-06-15 17:11:35 +08:00

654 lines
25 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// 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
{
/// <summary>
/// 简化版 SJA1000 CAN 控制器PeliCAN 模式仅标准帧立即发送TCP 转发)
/// </summary>
public class SJA1000_CAN : IDoubleWordPeripheral, IBytePeripheral, IKnownSize, IDisposable
{
// 日志开关(设置为 false 可屏蔽所有日志)
public static bool EnableVerboseLog = false;
// 接收队列长度配置(可修改)
public static int MaxRxQueueSize = 64; // 接收队列最大帧数
// 发送事件:当一帧数据被发送时触发
public event Action<byte[]> FrameTransmitted;
// ================ 静态 TCP Server 共享资源 ================
private static TcpListener sharedTcpListener;
private static readonly List<NetworkStream> sharedTcpClients = new List<NetworkStream>();
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<byte[]>();
// 启动共享 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;
/// <summary>
/// 获取接收队列中所有帧的字符串表示(每帧以空格分隔的十六进制字节,帧间用换行分隔)
/// 注意:此方法仅用于调试,会影响接收队列。
/// </summary>
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;
}
}
/// <summary>
/// 外部接口注入一帧接收数据(可变长度的十六进制字符串,空格分隔)
/// 可多次调用以注入多帧,帧将存入接收队列。
/// 若接收队列已满,则新帧被丢弃。
/// 输入字符串长度可变0~11 字节),不足 11 字节时自动补零,超出 11 字节时截断并警告。
/// </summary>
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<byte[]> rxFrameQueue; // 仅接收队列
}
}