更新 SJA1000_CAN.cs
This commit is contained in:
296
SJA1000_CAN.cs
296
SJA1000_CAN.cs
@@ -1,20 +1,26 @@
|
|||||||
//
|
//
|
||||||
// SJA1000 CAN 控制器外设实现(简化版,支持发送/接收队列,可配置长度)
|
// SJA1000 CAN 控制器外设实现(简化版,支持接收队列,可配置长度)
|
||||||
// 仅支持 PeliCAN 模式的标准帧,不考虑 BasicCAN、扩展帧、错误处理、多帧、验收滤波和发送中断。
|
// 仅支持 PeliCAN 模式的标准帧,不考虑 BasicCAN、扩展帧、错误处理、多帧、验收滤波和发送中断。
|
||||||
// 寄存器地址映射基于 PeliCAN 模式,包含 MOD, CMR, SR, IR, IER, BTR0, BTR1, OCR, RXERR, TXERR,
|
// 寄存器地址映射基于 PeliCAN 模式,包含 MOD, CMR, SR, IR, IER, BTR0, BTR1, OCR, RXERR, TXERR,
|
||||||
// 发送缓冲区(地址 16-26)、RBSA 和 CDR。命令寄存器位2=RRB,位3=CDO。
|
// 发送缓冲区(地址 16-26)、RBSA 和 CDR。命令寄存器位2=RRB,位3=CDO。
|
||||||
// 所有寄存器可随时读写,无复位模式限制。
|
// 所有寄存器可随时读写,无复位模式限制。
|
||||||
//
|
//
|
||||||
// 队列长度可通过修改静态字段 MaxTxQueueSize 和 MaxRxQueueSize 调整。
|
// 接收队列长度可通过修改静态字段 MaxRxQueueSize 调整。
|
||||||
// 发送队列满时,CPU 发送请求被忽略,并设置 TBS=0(发送缓冲区忙);
|
|
||||||
// 接收队列满时,外部注入的新帧被丢弃。
|
// 接收队列满时,外部注入的新帧被丢弃。
|
||||||
//
|
//
|
||||||
|
// 发送采用立即发送模式(无波特率延迟),每帧发送完成后触发 FrameTransmitted 事件并转发到 TCP 客户端。
|
||||||
|
// TCP Server 为静态共享实例,监听端口 10000,将发送的帧数据转发给所有连接的客户端。
|
||||||
// 日志开关:EnableVerboseLog = false 可关闭所有日志输出。
|
// 日志开关:EnableVerboseLog = false 可关闭所有日志输出。
|
||||||
|
// 实现了 IDisposable 接口,TCP 服务器由所有实例共享,仅当最后一个实例销毁时停止服务器。
|
||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
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.Core;
|
||||||
using Antmicro.Renode.Logging;
|
using Antmicro.Renode.Logging;
|
||||||
using Antmicro.Renode.Peripherals.Bus;
|
using Antmicro.Renode.Peripherals.Bus;
|
||||||
@@ -24,17 +30,28 @@ using Antmicro.Renode.Time;
|
|||||||
namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 简化版 SJA1000 CAN 控制器(PeliCAN 模式,仅标准帧)
|
/// 简化版 SJA1000 CAN 控制器(PeliCAN 模式,仅标准帧,立即发送,TCP 转发)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SJA1000_CAN : IDoubleWordPeripheral, IBytePeripheral, IKnownSize
|
public class SJA1000_CAN : IDoubleWordPeripheral, IBytePeripheral, IKnownSize, IDisposable
|
||||||
{
|
{
|
||||||
// 日志开关(设置为 false 可屏蔽所有日志)
|
// 日志开关(设置为 false 可屏蔽所有日志)
|
||||||
public static bool EnableVerboseLog = false;
|
public static bool EnableVerboseLog = false;
|
||||||
|
|
||||||
// 队列长度配置(可修改)
|
// 接收队列长度配置(可修改)
|
||||||
public static int MaxTxQueueSize = 64; // 发送队列最大帧数
|
|
||||||
public static int MaxRxQueueSize = 64; // 接收队列最大帧数
|
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)
|
public SJA1000_CAN(IMachine machine)
|
||||||
{
|
{
|
||||||
this.machine = machine;
|
this.machine = machine;
|
||||||
@@ -42,8 +59,10 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
|
|
||||||
txBuffer = new byte[11];
|
txBuffer = new byte[11];
|
||||||
rxBuffer = new byte[11];
|
rxBuffer = new byte[11];
|
||||||
txFrameQueue = new Queue<byte[]>(); // 发送队列
|
rxFrameQueue = new Queue<byte[]>();
|
||||||
rxFrameQueue = new Queue<byte[]>(); // 接收队列
|
|
||||||
|
// 启动共享 TCP Server(增加引用计数)
|
||||||
|
StartSharedTcpServer();
|
||||||
|
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
@@ -60,33 +79,153 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
rbsa = 0;
|
rbsa = 0;
|
||||||
cdr = 0;
|
cdr = 0;
|
||||||
|
|
||||||
sr_tbs = 1; // 发送缓冲区初始为空闲
|
sr_tbs = 1; // 发送缓冲区始终空闲(立即发送模式)
|
||||||
sr_rbs = 0; // 接收缓冲区初始为空
|
sr_rbs = 0; // 接收缓冲区初始为空
|
||||||
ir = 0;
|
ir = 0;
|
||||||
|
|
||||||
Array.Clear(txBuffer, 0, txBuffer.Length);
|
Array.Clear(txBuffer, 0, txBuffer.Length);
|
||||||
Array.Clear(rxBuffer, 0, rxBuffer.Length);
|
Array.Clear(rxBuffer, 0, rxBuffer.Length);
|
||||||
txFrameQueue.Clear();
|
|
||||||
rxFrameQueue.Clear();
|
rxFrameQueue.Clear();
|
||||||
|
|
||||||
lastInterruptState = false; // 重置中断边沿状态
|
lastInterruptState = false;
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
|
|
||||||
if (EnableVerboseLog)
|
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 接口实现 ================
|
// ================ 共享 TCP Server 管理 ================
|
||||||
|
private void StartSharedTcpServer()
|
||||||
public uint ReadDoubleWord(long offset)
|
|
||||||
{
|
{
|
||||||
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)
|
public byte ReadByte(long offset)
|
||||||
{
|
{
|
||||||
@@ -106,7 +245,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Registers.SR:
|
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);
|
if (EnableVerboseLog) this.Log(LogLevel.Info, "Read SR: TBS={0}, RBS={1} -> 0x{2:X2}", sr_tbs, sr_rbs, value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -114,7 +253,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
value = ir;
|
value = ir;
|
||||||
ir = 0;
|
ir = 0;
|
||||||
UpdateIrFromRbs();
|
UpdateIrFromRbs();
|
||||||
// 中断被软件清除,重置边沿检测状态,允许下次条件满足时再次触发中断
|
|
||||||
lastInterruptState = false;
|
lastInterruptState = false;
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
if (EnableVerboseLog) this.Log(LogLevel.Info, "Read IR: 0x{0:X2}, cleared", value);
|
if (EnableVerboseLog) this.Log(LogLevel.Info, "Read IR: 0x{0:X2}, cleared", value);
|
||||||
@@ -147,7 +285,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
value = cdr;
|
value = cdr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 发送/接收缓冲区地址 16-26
|
|
||||||
case Registers.TX_FRAME_INFO:
|
case Registers.TX_FRAME_INFO:
|
||||||
case Registers.TX_ID1:
|
case Registers.TX_ID1:
|
||||||
case Registers.TX_ID2:
|
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 (EnableVerboseLog) this.Log(LogLevel.Info, "Write CMR: 0x{0:X2}", value);
|
||||||
if ((value & CMR_TR) != 0)
|
if ((value & CMR_TR) != 0)
|
||||||
{
|
{
|
||||||
// 发送请求
|
// 发送请求(立即发送模式)
|
||||||
if (sr_tbs == 1)
|
if (sr_tbs == 1)
|
||||||
{
|
{
|
||||||
var frame = new byte[11];
|
// 复制当前帧
|
||||||
|
byte[] frame = new byte[11];
|
||||||
|
Array.Copy(txBuffer, frame, 11);
|
||||||
|
// 清空发送缓冲区(准备下一帧)
|
||||||
|
Array.Clear(txBuffer, 0, txBuffer.Length);
|
||||||
|
|
||||||
if (txFrameQueue.Count < (MaxTxQueueSize - 1))
|
// 立即发送帧数据
|
||||||
{
|
FrameTransmitted?.Invoke(frame);
|
||||||
Array.Copy(txBuffer, frame, 11);
|
BroadcastFrame(frame);
|
||||||
txFrameQueue.Enqueue(frame);
|
|
||||||
Array.Clear(txBuffer, 0, txBuffer.Length);
|
// 保持发送缓冲区空闲(始终允许写入)
|
||||||
sr_tbs = 1;
|
sr_tbs = 1;
|
||||||
if (EnableVerboseLog) this.Log(LogLevel.Info, "Frame enqueued for transmission, queue size: {0}", txFrameQueue.Count);
|
|
||||||
}
|
if (EnableVerboseLog) this.Log(LogLevel.Info, "Frame transmitted immediately");
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -230,12 +358,16 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
}
|
}
|
||||||
if ((value & CMR_RRB) != 0)
|
if ((value & CMR_RRB) != 0)
|
||||||
{
|
{
|
||||||
|
// 释放接收缓冲器
|
||||||
|
sr_rbs = 0;
|
||||||
|
UpdateIrFromRbs();
|
||||||
|
UpdateInterrupts();
|
||||||
LoadNextRxFrame();
|
LoadNextRxFrame();
|
||||||
|
UpdateInterrupts();
|
||||||
if (EnableVerboseLog) this.Log(LogLevel.Info, "Receive buffer released");
|
if (EnableVerboseLog) this.Log(LogLevel.Info, "Receive buffer released");
|
||||||
}
|
}
|
||||||
if ((value & CMR_CDO) != 0)
|
if ((value & CMR_CDO) != 0)
|
||||||
{
|
{
|
||||||
// 清除数据溢出(无操作,但保留接口)
|
|
||||||
if (EnableVerboseLog) this.Log(LogLevel.Info, "Clear data overflow (no effect)");
|
if (EnableVerboseLog) this.Log(LogLevel.Info, "Clear data overflow (no effect)");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -284,7 +416,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
if (EnableVerboseLog) this.Log(LogLevel.Info, "Write CDR: 0x{0:X2}", value);
|
if (EnableVerboseLog) this.Log(LogLevel.Info, "Write CDR: 0x{0:X2}", value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 发送缓冲区写入
|
|
||||||
case Registers.TX_FRAME_INFO:
|
case Registers.TX_FRAME_INFO:
|
||||||
case Registers.TX_ID1:
|
case Registers.TX_ID1:
|
||||||
case Registers.TX_ID2:
|
case Registers.TX_ID2:
|
||||||
@@ -315,8 +446,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================ 内部辅助方法 ================
|
// ================ 接收与中断管理 ================
|
||||||
|
|
||||||
private void UpdateIrFromRbs()
|
private void UpdateIrFromRbs()
|
||||||
{
|
{
|
||||||
if (sr_rbs == 1)
|
if (sr_rbs == 1)
|
||||||
@@ -331,35 +461,32 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
if (currentInterrupt && !lastInterruptState)
|
if (currentInterrupt && !lastInterruptState)
|
||||||
{
|
{
|
||||||
IRQ.Set(true);
|
IRQ.Set(true);
|
||||||
machine.ScheduleAction(TimeInterval.FromMicroseconds(1), _ =>
|
machine.ScheduleAction(TimeInterval.FromMicroseconds(1), _ => IRQ.Set(false));
|
||||||
{
|
|
||||||
IRQ.Set(false);
|
|
||||||
});
|
|
||||||
if (EnableVerboseLog) this.Log(LogLevel.Info, "Interrupt pulse generated (RI)");
|
if (EnableVerboseLog) this.Log(LogLevel.Info, "Interrupt pulse generated (RI)");
|
||||||
}
|
}
|
||||||
lastInterruptState = currentInterrupt;
|
lastInterruptState = currentInterrupt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly object lockObject = new object();
|
private readonly object lockObject = new object();
|
||||||
|
private bool lastInterruptState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取发送队列中所有帧的字符串表示(每帧以空格分隔的十六进制字节,帧间用换行分隔)
|
/// 获取接收队列中所有帧的字符串表示(每帧以空格分隔的十六进制字节,帧间用换行分隔)
|
||||||
/// 调用后清空发送队列,并恢复发送缓冲区状态(如果之前因队列满被锁定)。
|
/// 注意:此方法仅用于调试,会影响接收队列。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetTxBufferDataString()
|
public string GetRxBufferDataString()
|
||||||
{
|
{
|
||||||
lock (lockObject)
|
lock (lockObject)
|
||||||
{
|
{
|
||||||
if (txFrameQueue.Count == 0)
|
if (rxFrameQueue.Count == 0)
|
||||||
{
|
{
|
||||||
if (EnableVerboseLog) Console.WriteLine("GetTxBufferDataString: no frames in TX queue");
|
if (EnableVerboseLog) Console.WriteLine("GetRxBufferDataString: no frames in RX queue");
|
||||||
if (sr_tbs == 0) sr_tbs = 1;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var sb = new StringBuilder();
|
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++)
|
for (int i = 0; i < frame.Length; i++)
|
||||||
{
|
{
|
||||||
if (i > 0) sb.Append(" ");
|
if (i > 0) sb.Append(" ");
|
||||||
@@ -368,16 +495,16 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
}
|
}
|
||||||
string result = sb.ToString().TrimEnd();
|
string result = sb.ToString().TrimEnd();
|
||||||
sr_tbs = 1;
|
if (EnableVerboseLog) Console.WriteLine("GetRxBufferDataString returning: " + result);
|
||||||
if (EnableVerboseLog) Console.WriteLine("GetTxBufferDataString returning: " + result);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 外部接口注入一帧接收数据(11字节的十六进制字符串,空格分隔)
|
/// 外部接口注入一帧接收数据(可变长度的十六进制字符串,空格分隔)
|
||||||
/// 可多次调用以注入多帧,帧将存入接收队列。
|
/// 可多次调用以注入多帧,帧将存入接收队列。
|
||||||
/// 若接收队列已满,则新帧被丢弃。
|
/// 若接收队列已满,则新帧被丢弃。
|
||||||
|
/// 输入字符串长度可变(0~11 字节),不足 11 字节时自动补零,超出 11 字节时截断并警告。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SendRxBufferDataString(string frameString)
|
public void SendRxBufferDataString(string frameString)
|
||||||
{
|
{
|
||||||
@@ -389,13 +516,15 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
string[] parts = frameString.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
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");
|
if (EnableVerboseLog) Console.WriteLine($"SendRxBufferDataString: input has {length} bytes, truncating to 11");
|
||||||
return;
|
length = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] frame = new byte[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();
|
string part = parts[i].Trim();
|
||||||
if (!byte.TryParse(part, System.Globalization.NumberStyles.HexNumber,
|
if (!byte.TryParse(part, System.Globalization.NumberStyles.HexNumber,
|
||||||
@@ -409,7 +538,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
|
|
||||||
if (rxFrameQueue.Count >= MaxRxQueueSize)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,6 +548,7 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
if (sr_rbs == 0)
|
if (sr_rbs == 0)
|
||||||
{
|
{
|
||||||
LoadNextRxFrame();
|
LoadNextRxFrame();
|
||||||
|
UpdateInterrupts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,26 +561,38 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
Array.Copy(frame, rxBuffer, 11);
|
Array.Copy(frame, rxBuffer, 11);
|
||||||
sr_rbs = 1;
|
sr_rbs = 1;
|
||||||
UpdateIrFromRbs();
|
UpdateIrFromRbs();
|
||||||
UpdateInterrupts();
|
if (EnableVerboseLog) Console.WriteLine("Loaded next RX frame, remaining: {0}", rxFrameQueue.Count);
|
||||||
if (EnableVerboseLog) Console.WriteLine("Loaded next RX frame into buffer, queue remaining: {0}", rxFrameQueue.Count);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sr_rbs = 0;
|
sr_rbs = 0;
|
||||||
UpdateIrFromRbs();
|
UpdateIrFromRbs();
|
||||||
UpdateInterrupts();
|
|
||||||
if (EnableVerboseLog) Console.WriteLine("RX queue empty, buffer cleared");
|
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 long Size => 0x80;
|
||||||
|
|
||||||
public GPIO IRQ { get; }
|
public GPIO IRQ { get; }
|
||||||
|
|
||||||
// ================ 寄存器枚举 ================
|
// ================ 寄存器枚举 ================
|
||||||
|
|
||||||
private enum Registers : long
|
private enum Registers : long
|
||||||
{
|
{
|
||||||
MOD = 0x00,
|
MOD = 0x00,
|
||||||
@@ -479,12 +621,10 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ================ 常量位定义 ================
|
// ================ 常量位定义 ================
|
||||||
|
|
||||||
private const byte CMR_TR = 0x01;
|
private const byte CMR_TR = 0x01;
|
||||||
private const byte CMR_RRB = 0x04;
|
private const byte CMR_RRB = 0x04;
|
||||||
private const byte CMR_CDO = 0x08;
|
private const byte CMR_CDO = 0x08;
|
||||||
|
|
||||||
private const byte SR_BS = 0x80;
|
|
||||||
private const byte SR_TBS = 0x04;
|
private const byte SR_TBS = 0x04;
|
||||||
private const byte SR_RBS = 0x01;
|
private const byte SR_RBS = 0x01;
|
||||||
|
|
||||||
@@ -492,7 +632,6 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
private const byte IER_RIE = 0x01;
|
private const byte IER_RIE = 0x01;
|
||||||
|
|
||||||
// ================ 私有字段 ================
|
// ================ 私有字段 ================
|
||||||
|
|
||||||
private readonly IMachine machine;
|
private readonly IMachine machine;
|
||||||
|
|
||||||
private byte mod;
|
private byte mod;
|
||||||
@@ -507,12 +646,9 @@ namespace Antmicro.Renode.Peripherals.CustomPeripherals
|
|||||||
private byte sr_rbs;
|
private byte sr_rbs;
|
||||||
private byte ir;
|
private byte ir;
|
||||||
|
|
||||||
private bool lastInterruptState; // 用于中断边沿检测
|
|
||||||
|
|
||||||
private readonly byte[] txBuffer;
|
private readonly byte[] txBuffer;
|
||||||
private readonly byte[] rxBuffer;
|
private readonly byte[] rxBuffer;
|
||||||
|
|
||||||
private readonly Queue<byte[]> txFrameQueue;
|
private readonly Queue<byte[]> rxFrameQueue; // 仅接收队列
|
||||||
private readonly Queue<byte[]> rxFrameQueue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user