This repository has been archived on 2025-10-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FPGA_WebLab/server/src/Peripherals/LogicAnalyzerClient.cs

354 lines
12 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.
using System.Collections;
using System.Net;
using Common;
using DotNext;
namespace Peripherals.LogicAnalyzerClient;
static class AnalyzerAddr
{
const UInt32 BASE = 0x9000_0000;
/// <summary>
/// 0x0000_0000 R/W [ 0] capture on: 置1开始等待捕获0停止捕获。捕获到信号后该位自动清零。 <br/>
/// [ 8] capture force: 置1则强制捕获信号自动置0。 <br/>
/// [16] capture busy: 1为逻辑分析仪正在捕获信号。 <br/>
/// [24] capture done: 1为逻辑分析仪内存完整存储了此次捕获的信号。 <br/>
/// 配置顺序:若[0]为0则将其置1随后不断获取[0]若其变为0则表示触发成功。随后不断获取[24]若其为1则表示捕获完成。 <br/>
/// </summary>
public const UInt32 CAPTURE_MODE = BASE + 0x0000_0000;
/// <summary>
/// 0x0000_0001 R/W [1:0] global trig mode: 00: 全局与 (&amp;) <br/>
/// 01: 全局或 (&#124;) <br/>
/// 10: 全局非与(~&amp;) <br/>
/// 11: 全局非或(~&#124;) <br/>
/// </summary>
public const UInt32 GLOBAL_TRIG_MODE = BASE + 0x0000_0001;
/// <summary>
/// 0x0000_0010 - 0x0000_0017 R/W [5:0] 信号M的触发操作符共8路 <br/>
/// [5:3] M's Operator: 000 == <br/>
/// 001 != <br/>
/// 010 &lt; <br/>
/// 011 &lt;= <br/>
/// 100 &gt; <br/>
/// 101 &gt;= <br/>
/// [2:0] M's Value: 000 LOGIC 0 <br/>
/// 001 LOGIC 1 <br/>
/// 010 X(not care) <br/>
/// 011 RISE <br/>
/// 100 FALL <br/>
/// 101 RISE OR FALL <br/>
/// 110 NOCHANGE <br/>
/// 111 SOME NUMBER <br/>
/// </summary>
public static readonly UInt32[] SIGNAL_TRIG_MODE = {
BASE + 0x0000_0010,
BASE + 0x0000_0011,
BASE + 0x0000_0012,
BASE + 0x0000_0013,
BASE + 0x0000_0014,
BASE + 0x0000_0015,
BASE + 0x0000_0016,
BASE + 0x0000_0017,
};
/// <summary>
/// 0x0100_0000 - 0x0100_03FF 只读 32位波形存储得到的32位数据中低八位最先捕获高八位最后捕获。<br/>
/// 共1024个地址每个地址存储4组深度为4096。<br/>
/// </summary>
public const UInt32 CAPTURE_DATA_ADDR = BASE + 0x0100_0000;
public const Int32 CAPTURE_DATA_LENGTH = 1024;
}
/// <summary>
/// 逻辑分析仪运行状态枚举
/// </summary>
[Flags]
public enum CaptureStatus
{
/// <summary>
/// 无状态标志
/// </summary>
None = 0,
/// <summary>
/// 捕获使能位置1开始等待捕获0停止捕获。捕获到信号后该位自动清零
/// </summary>
CaptureOn = 1 << 0, // [0] 捕获使能
/// <summary>
/// 强制捕获位置1则强制捕获信号自动置0
/// </summary>
CaptureForce = 1 << 8, // [8] 强制捕获
/// <summary>
/// 捕获忙碌位1为逻辑分析仪正在捕获信号
/// </summary>
CaptureBusy = 1 << 16, // [16] 捕获进行中
/// <summary>
/// 捕获完成位1为逻辑分析仪内存完整存储了此次捕获的信号
/// </summary>
CaptureDone = 1 << 24 // [24] 捕获完成
}
/// <summary>
/// 全局触发模式枚举,定义多路信号触发条件的逻辑组合方式
/// </summary>
public enum GlobalCaptureMode
{
/// <summary>
/// 全局与模式,所有触发条件都必须满足
/// </summary>
AND = 0b00,
/// <summary>
/// 全局或模式,任一触发条件满足即可
/// </summary>
OR = 0b01,
/// <summary>
/// 全局非与模式,不是所有触发条件都满足
/// </summary>
NAND = 0b10,
/// <summary>
/// 全局非或模式,所有触发条件都不满足
/// </summary>
NOR = 0b11
}
/// <summary>
/// 信号M的操作符枚举
/// </summary>
public enum SignalOperator : byte
{
/// <summary>
/// 等于操作符
/// </summary>
Equal = 0b000, // ==
/// <summary>
/// 不等于操作符
/// </summary>
NotEqual = 0b001, // !=
/// <summary>
/// 小于操作符
/// </summary>
LessThan = 0b010, // <
/// <summary>
/// 小于等于操作符
/// </summary>
LessThanOrEqual = 0b011, // <=
/// <summary>
/// 大于操作符
/// </summary>
GreaterThan = 0b100, // >
/// <summary>
/// 大于等于操作符
/// </summary>
GreaterThanOrEqual = 0b101 // >=
}
/// <summary>
/// 信号M的值枚举
/// </summary>
public enum SignalValue : byte
{
/// <summary>
/// 逻辑0电平
/// </summary>
Logic0 = 0b000, // LOGIC 0
/// <summary>
/// 逻辑1电平
/// </summary>
Logic1 = 0b001, // LOGIC 1
/// <summary>
/// 不关心该信号状态
/// </summary>
NotCare = 0b010, // X(not care)
/// <summary>
/// 上升沿触发
/// </summary>
Rise = 0b011, // RISE
/// <summary>
/// 下降沿触发
/// </summary>
Fall = 0b100, // FALL
/// <summary>
/// 上升沿或下降沿触发
/// </summary>
RiseOrFall = 0b101, // RISE OR FALL
/// <summary>
/// 信号无变化
/// </summary>
NoChange = 0b110, // NOCHANGE
/// <summary>
/// 特定数值
/// </summary>
SomeNumber = 0b111 // SOME NUMBER
}
/// <summary>
/// FPGA逻辑分析仪客户端用于控制FPGA上的逻辑分析仪模块进行信号捕获和分析
/// </summary>
public class Analyzer
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
readonly int timeout = 2000;
readonly int taskID;
readonly int port;
readonly string address;
private IPEndPoint ep;
/// <summary>
/// 初始化逻辑分析仪客户端
/// </summary>
/// <param name="address">FPGA设备的IP地址</param>
/// <param name="port">通信端口号</param>
/// <param name="taskID">任务标识符</param>
/// <param name="timeout">通信超时时间毫秒默认2000ms</param>
/// <exception cref="ArgumentException">当timeout为负数时抛出</exception>
public Analyzer(string address, int port, int taskID, int timeout = 2000)
{
if (timeout < 0)
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
this.address = address;
this.taskID = taskID;
this.port = port;
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
this.timeout = timeout;
}
/// <summary>
/// 控制逻辑分析仪的捕获模式
/// </summary>
/// <param name="captureOn">是否开始捕获</param>
/// <param name="force">是否强制捕获</param>
/// <returns>操作结果成功返回true否则返回异常信息</returns>
public async ValueTask<Result<bool>> SetCaptureMode(bool captureOn, bool force)
{
// 构造寄存器值
UInt32 value = 0;
if (captureOn) value |= 1 << 0;
if (force) value |= 1 << 8;
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, AnalyzerAddr.CAPTURE_MODE, value, this.timeout);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to set capture mode: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error("WriteAddr to CAPTURE_MODE returned false");
return new(new Exception("Failed to set capture mode"));
}
return true;
}
/// <summary>
/// 读取逻辑分析仪捕获运行状态
/// </summary>
/// <returns>操作结果,成功返回寄存器值,否则返回异常信息</returns>
public async ValueTask<Result<CaptureStatus>> ReadCaptureStatus()
{
var ret = await UDPClientPool.ReadAddr(this.ep, this.taskID, AnalyzerAddr.CAPTURE_MODE, this.timeout);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to read capture status: {ret.Error}");
return new(ret.Error);
}
if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 4)
{
logger.Error("ReadAddr returned invalid data for capture status");
return new(new Exception("Failed to read capture status"));
}
UInt32 status = Number.BytesToUInt32(ret.Value.Options.Data).Value;
return (CaptureStatus)status;
}
/// <summary>
/// 设置全局触发模式
/// </summary>
/// <param name="mode">全局触发模式0:与, 1:或, 2:非与, 3:非或)</param>
/// <returns>操作结果成功返回true否则返回异常信息</returns>
public async ValueTask<Result<bool>> SetGlobalTrigMode(GlobalCaptureMode mode)
{
var ret = await UDPClientPool.WriteAddr(
this.ep, this.taskID, AnalyzerAddr.GLOBAL_TRIG_MODE, (byte)mode, this.timeout);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to set global trigger mode: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error("WriteAddr to GLOBAL_TRIG_MODE returned false");
return new(new Exception("Failed to set global trigger mode"));
}
return true;
}
/// <summary>
/// 设置指定信号通道的触发模式
/// </summary>
/// <param name="signalIndex">信号通道索引0-7</param>
/// <param name="op">触发操作符</param>
/// <param name="val">触发信号值</param>
/// <returns>操作结果成功返回true否则返回异常信息</returns>
public async ValueTask<Result<bool>> SetSignalTrigMode(int signalIndex, SignalOperator op, SignalValue val)
{
if (signalIndex < 0 || signalIndex >= AnalyzerAddr.SIGNAL_TRIG_MODE.Length)
return new(new ArgumentException($"Signal index must be 0~{AnalyzerAddr.SIGNAL_TRIG_MODE.Length}"));
// 计算模式值: [2:0] 信号值, [5:3] 操作符
UInt32 mode = ((UInt32)op << 3) | (UInt32) val;
var addr = AnalyzerAddr.SIGNAL_TRIG_MODE[signalIndex];
var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, addr, mode, this.timeout);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to set signal trigger mode: {ret.Error}");
return new(ret.Error);
}
if (!ret.Value)
{
logger.Error("WriteAddr to SIGNAL_TRIG_MODE returned false");
return new(new Exception("Failed to set signal trigger mode"));
}
return true;
}
/// <summary>
/// 读取捕获的波形数据
/// </summary>
/// <returns>操作结果成功返回byte[],否则返回异常信息</returns>
public async ValueTask<Result<byte[]>> ReadCaptureData()
{
var ret = await UDPClientPool.ReadAddr4BytesAsync(
this.ep,
this.taskID,
AnalyzerAddr.CAPTURE_DATA_ADDR,
AnalyzerAddr.CAPTURE_DATA_LENGTH,
this.timeout
);
if (!ret.IsSuccessful)
{
logger.Error($"Failed to read capture data: {ret.Error}");
return new(ret.Error);
}
var data = ret.Value;
if (data == null || data.Length != AnalyzerAddr.CAPTURE_DATA_LENGTH * 4)
{
logger.Error($"Capture data length mismatch: {data?.Length}");
return new(new Exception("Capture data length mismatch"));
}
var reversed = Common.Number.ReverseBytes(data, 4).Value;
return reversed;
}
}