style: 继续调整后端
This commit is contained in:
0
server/src/Peripherals/CameraClient.cs
Normal file
0
server/src/Peripherals/CameraClient.cs
Normal file
866
server/src/Peripherals/JtagClient.cs
Normal file
866
server/src/Peripherals/JtagClient.cs
Normal file
@@ -0,0 +1,866 @@
|
||||
using System.Collections;
|
||||
using System.Net;
|
||||
using DotNext;
|
||||
using Newtonsoft.Json;
|
||||
using WebProtocol;
|
||||
|
||||
namespace Peripherals.JtagClient;
|
||||
|
||||
/// <summary>
|
||||
/// Global Constant Jtag Address
|
||||
/// </summary>
|
||||
public static class JtagAddr
|
||||
{
|
||||
/// <summary>
|
||||
/// Jtag State Reg
|
||||
/// </summary>
|
||||
public const UInt32 STATE = 0x10_00_00_00;
|
||||
/// <summary>
|
||||
/// Jtag Read Reg
|
||||
/// </summary>
|
||||
public const UInt32 READ_DATA = 0x10_00_00_01;
|
||||
/// <summary>
|
||||
/// Jtag Write Reg
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_DATA = 0x10_00_00_02;
|
||||
/// <summary>
|
||||
/// Jtag Write Command
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_CMD = 0x10_00_00_03;
|
||||
/// <summary>
|
||||
/// Jtag Speed Control
|
||||
/// </summary>
|
||||
public const UInt32 SPEED_CTRL = 0x10_00_00_04;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Jtag 状态寄存器的掩码及其确认函数
|
||||
/// </summary>
|
||||
public static class JtagState
|
||||
{
|
||||
|
||||
/*
|
||||
[0] 移位数据读fifo清空,1为有效,清空后自动置0
|
||||
[1] 移位数据fifo读入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
[2] 移位数据fifo读入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
[7:3] 保留
|
||||
|
||||
[8] 移位数据写fifo清空,1为有效,清空后自动置0
|
||||
[9] 移位数据fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
[10] 移位数据fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
[15:11]保留
|
||||
|
||||
[16] 移位命令写fifo清空,1为有效,清空后自动置0
|
||||
[17] 移位命令fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
[18] 移位命令fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
[23:19]保留
|
||||
|
||||
[24] CMD执行完毕标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// 移位数据读fifo清空,1为有效,清空后自动置0,实际一直为零
|
||||
/// </summary>
|
||||
public const UInt32 READ_DATA_FIFO_CLEAR = 0b0001 << 0;
|
||||
/// <summary>
|
||||
/// 移位数据fifo读入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
/// </summary>
|
||||
public const UInt32 READ_DATA_FIFO_EMPTY = 0b0010 << 0;
|
||||
/// <summary>
|
||||
/// 移位数据fifo读入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
/// </summary>
|
||||
public const UInt32 READ_DATA_FIFO_AFULL = 0b0100 << 0;
|
||||
/// <summary>
|
||||
/// 移位数据读fifo
|
||||
/// </summary>
|
||||
public const UInt32 READ_DATA_FIFO = 0b0111 << 0;
|
||||
|
||||
/// <summary>
|
||||
/// 移位数据写fifo清空,1为有效,清空后自动置0
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_DATA_FIFO_CLEAR = 0b0001 << 8;
|
||||
/// <summary>
|
||||
/// 移位数据fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_DATA_FIFO_EMPTY = 0b0010 << 8;
|
||||
/// <summary>
|
||||
/// 移位数据fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_DATA_FIFO_AFULL = 0b0100 << 8;
|
||||
/// <summary>
|
||||
/// 移位数据写fifo
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_DATA_FIFO = 0b0111 << 8;
|
||||
|
||||
/// <summary>
|
||||
/// 移位命令写fifo清空,1为有效,清空后自动置0
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_CMD_FIFO_CLEAR = 0b0001 << 16;
|
||||
/// <summary>
|
||||
/// 移位命令fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_CMD_FIFO_EMPTY = 0b0010 << 16;
|
||||
/// <summary>
|
||||
/// 移位命令fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_CMD_FIFO_AFULL = 0b0100 << 16;
|
||||
/// <summary>
|
||||
/// 移位命令写fifo
|
||||
/// </summary>
|
||||
public const UInt32 WRITE_CMD_FIFO = 0b0111 << 16;
|
||||
|
||||
/// <summary>
|
||||
/// CMD执行完毕标识,只读,对JTAG_STATE_REG的写不改变其值
|
||||
/// </summary>
|
||||
public const UInt32 CMD_EXEC_FINISH = 0b0001 << 24;
|
||||
/// <summary>
|
||||
/// 全部FIFO
|
||||
/// </summary>
|
||||
public const UInt32 ALL_FIFO = READ_DATA_FIFO | WRITE_DATA_FIFO | WRITE_CMD_FIFO;
|
||||
/// <summary>
|
||||
/// 全部寄存器
|
||||
/// </summary>
|
||||
public const UInt32 ALL_REG = READ_DATA_FIFO | WRITE_DATA_FIFO | WRITE_CMD_FIFO | CMD_EXEC_FINISH;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Command bits of Jtag
|
||||
/// </summary>
|
||||
public static class JtagCmd
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The length of JTAG_DR_XXXX
|
||||
/// </summary>
|
||||
public const UInt32 LEN_JTAG_DR = 10;
|
||||
/// <summary>
|
||||
/// 旁路指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_BYPASS = 0b1111111111;
|
||||
/// <summary>
|
||||
/// 采样指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_SAMPLE = 0b1010000000;
|
||||
/// <summary>
|
||||
/// 预装指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_PRELOAD = 0b1010000000;
|
||||
/// <summary>
|
||||
/// 外测试指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_EXTEST = 0b1010000001;
|
||||
/// <summary>
|
||||
/// 内测试指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_INTEST = 0b1010000010;
|
||||
/// <summary>
|
||||
/// 标识指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_IDCODE = 0b1010000011;
|
||||
/// <summary>
|
||||
/// 高阻指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_HIGHZ = 0b1010000101;
|
||||
/// <summary>
|
||||
/// 复位指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_JRST = 0b1010001010;
|
||||
/// <summary>
|
||||
/// 配置指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_CFGI = 0b1010001011;
|
||||
/// <summary>
|
||||
/// 回读指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_CFGO = 0b1010001100;
|
||||
/// <summary>
|
||||
/// 唤醒指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_JWAKEUP = 0b1010001101;
|
||||
/// <summary>
|
||||
/// 读UID指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_READ_UID = 0b0101001100;
|
||||
/// <summary>
|
||||
/// 读状态寄存器指令
|
||||
/// </summary>
|
||||
public const UInt32 JTAG_DR_RDSR = 0b0101011001;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The length of CMD_JTAG_XXXX_XXXX
|
||||
/// </summary>
|
||||
public const UInt32 LEN_CMD_JTAG = 4;
|
||||
/// <summary>
|
||||
/// 设定JTAG默认状态为TEST_LOGIC_RESET态 (JTAG复位)
|
||||
/// </summary>
|
||||
public const UInt32 CMD_JTAG_CLOSE_TEST = 0b0000;
|
||||
/// <summary>
|
||||
/// 设定JTAG默认状态为RUN_TEST_IDLE态 (JTAG空闲)
|
||||
/// </summary>
|
||||
public const UInt32 CMD_JTAG_RUN_TEST = 0b0001;
|
||||
/// <summary>
|
||||
/// JTAG进入SHIFTIR循环cycle_num次后回到设定的默认状态
|
||||
/// </summary>
|
||||
public const UInt32 CMD_JTAG_LOAD_IR = 0b0010;
|
||||
/// <summary>
|
||||
/// JTAG进入SHIFTDR循环cycle_num次后回到设定的默认状态,同时输入shift_in
|
||||
/// </summary>
|
||||
public const UInt32 CMD_JTAG_LOAD_DR_CAREI = 0b0011;
|
||||
/// <summary>
|
||||
/// JTAG进入SHIFTDR循环cycle_num次后回到设定的默认状态,同时输出shift_out
|
||||
/// </summary>
|
||||
public const UInt32 CMD_JTAG_LOAD_DR_CAREO = 0b0100;
|
||||
/// <summary>
|
||||
/// JTAG进入RUN_TEST_IDLE态循环cycle_num次
|
||||
/// </summary>
|
||||
public const UInt32 CMD_JTAG_IDLE_DELAY = 0b0101;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JTAG 状态寄存器
|
||||
/// </summary>
|
||||
public class JtagStatusReg
|
||||
{
|
||||
/// <summary>
|
||||
/// ID 错误标志
|
||||
/// </summary>
|
||||
public bool id_err { get; set; }
|
||||
/// <summary>
|
||||
/// CRC 错误标志
|
||||
/// </summary>
|
||||
public bool crc_err { get; set; }
|
||||
/// <summary>
|
||||
/// 自动测试错误标志
|
||||
/// </summary>
|
||||
public bool aut_err { get; set; }
|
||||
/// <summary>
|
||||
/// 回读 CRC 错误标志
|
||||
/// </summary>
|
||||
public bool rbcrc_err { get; set; }
|
||||
/// <summary>
|
||||
/// 超时标志
|
||||
/// </summary>
|
||||
public bool timeout { get; set; }
|
||||
/// <summary>
|
||||
/// 唤醒完成标志
|
||||
/// </summary>
|
||||
public bool wakeup_over { get; set; }
|
||||
/// <summary>
|
||||
/// 休眠完成标志
|
||||
/// </summary>
|
||||
public bool wakedown_over { get; set; }
|
||||
/// <summary>
|
||||
/// 模式位
|
||||
/// </summary>
|
||||
public string m { get; set; }
|
||||
/// <summary>
|
||||
/// 初始化完成标志
|
||||
/// </summary>
|
||||
public bool init_complete { get; set; }
|
||||
/// <summary>
|
||||
/// 初始化状态(低电平有效)
|
||||
/// </summary>
|
||||
public bool init_n { get; set; }
|
||||
/// <summary>
|
||||
/// 完成标志
|
||||
/// </summary>
|
||||
public bool done { get; set; }
|
||||
/// <summary>
|
||||
/// 内部完成标志
|
||||
/// </summary>
|
||||
public bool done_i { get; set; }
|
||||
/// <summary>
|
||||
/// 全局逻辑使能标志
|
||||
/// </summary>
|
||||
public bool glogen { get; set; }
|
||||
/// <summary>
|
||||
/// 全局逻辑反馈标志
|
||||
/// </summary>
|
||||
public bool glogen_fb { get; set; }
|
||||
/// <summary>
|
||||
/// 全局输出使能标志
|
||||
/// </summary>
|
||||
public bool gouten { get; set; }
|
||||
/// <summary>
|
||||
/// 全局复位标志
|
||||
/// </summary>
|
||||
public bool grsn { get; set; }
|
||||
/// <summary>
|
||||
/// 全局写使能标志
|
||||
/// </summary>
|
||||
public bool gwen { get; set; }
|
||||
/// <summary>
|
||||
/// PLL 锁定标志
|
||||
/// </summary>
|
||||
public bool pll_lock { get; set; }
|
||||
/// <summary>
|
||||
/// 回退标志
|
||||
/// </summary>
|
||||
public bool fallback { get; set; }
|
||||
/// <summary>
|
||||
/// IPAL 模式位
|
||||
/// </summary>
|
||||
public string ipal_m { get; set; }
|
||||
/// <summary>
|
||||
/// 8 位标志
|
||||
/// </summary>
|
||||
public bool flg_x8 { get; set; }
|
||||
/// <summary>
|
||||
/// 16 位标志
|
||||
/// </summary>
|
||||
public bool flg_x16 { get; set; }
|
||||
/// <summary>
|
||||
/// 32 位标志
|
||||
/// </summary>
|
||||
public bool flg_x32 { get; set; }
|
||||
/// <summary>
|
||||
/// 过温标志
|
||||
/// </summary>
|
||||
public bool over_temp { get; set; }
|
||||
/// <summary>
|
||||
/// 配置错误标志
|
||||
/// </summary>
|
||||
public bool prcfg_err { get; set; }
|
||||
/// <summary>
|
||||
/// 配置完成标志
|
||||
/// </summary>
|
||||
public bool prcfg_over { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,从 32 位代码解析 JTAG 状态寄存器
|
||||
/// </summary>
|
||||
/// <param name="code">32 位状态寄存器值</param>
|
||||
public JtagStatusReg(UInt32 code)
|
||||
{
|
||||
this.id_err = Common.Number.ToBit(code, 0).Value;
|
||||
this.crc_err = Common.Number.ToBit(code, 1).Value;
|
||||
this.aut_err = Common.Number.ToBit(code, 2).Value;
|
||||
this.rbcrc_err = Common.Number.ToBit(code, 3).Value;
|
||||
this.timeout = Common.Number.ToBit(code, 4).Value;
|
||||
this.wakeup_over = Common.Number.ToBit(code, 5).Value;
|
||||
this.wakedown_over = Common.Number.ToBit(code, 6).Value;
|
||||
this.m = Convert.ToString(((code >> 7) & 0b111), 2);
|
||||
this.init_complete = Common.Number.ToBit(code, 10).Value;
|
||||
this.init_n = Common.Number.ToBit(code, 11).Value;
|
||||
this.done = Common.Number.ToBit(code, 12).Value;
|
||||
this.done_i = Common.Number.ToBit(code, 13).Value;
|
||||
this.glogen = Common.Number.ToBit(code, 14).Value;
|
||||
this.glogen_fb = Common.Number.ToBit(code, 15).Value;
|
||||
this.gouten = Common.Number.ToBit(code, 16).Value;
|
||||
this.grsn = Common.Number.ToBit(code, 17).Value;
|
||||
this.gwen = Common.Number.ToBit(code, 18).Value;
|
||||
this.pll_lock = Common.Number.ToBit(code, 19).Value;
|
||||
this.fallback = Common.Number.ToBit(code, 21).Value;
|
||||
this.ipal_m = Convert.ToString(((code >> 22) & 0b11), 2);
|
||||
this.flg_x8 = Common.Number.ToBit(code, 24).Value;
|
||||
this.flg_x16 = Common.Number.ToBit(code, 25).Value;
|
||||
this.flg_x32 = Common.Number.ToBit(code, 26).Value;
|
||||
this.over_temp = Common.Number.ToBit(code, 27).Value;
|
||||
this.prcfg_err = Common.Number.ToBit(code, 28).Value;
|
||||
this.prcfg_over = Common.Number.ToBit(code, 29).Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为Json字符串
|
||||
/// </summary>
|
||||
/// <returns>Json字符串</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonConvert.SerializeObject(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Jtag控制器
|
||||
/// </summary>
|
||||
public class Jtag
|
||||
{
|
||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
private const int CLOCK_FREQ = 50; // MHz
|
||||
|
||||
readonly int timeout;
|
||||
|
||||
readonly int port;
|
||||
readonly string address;
|
||||
private IPEndPoint ep;
|
||||
|
||||
/// <summary>
|
||||
/// Jtag 构造函数
|
||||
/// </summary>
|
||||
/// <param name="address">目标 IP 地址</param>
|
||||
/// <param name="port">目标 UDP 端口</param>
|
||||
/// <param name="timeout">超时时间(毫秒)</param>
|
||||
public Jtag(string address, int port, int timeout = 2000)
|
||||
{
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
async ValueTask<Result<uint>> ReadFIFO(uint devAddr)
|
||||
{
|
||||
var ret = false;
|
||||
var opts = new SendAddrPackOptions();
|
||||
|
||||
opts.BurstType = BurstType.FixedBurst;
|
||||
opts.BurstLength = 0;
|
||||
opts.CommandID = 0;
|
||||
opts.Address = devAddr;
|
||||
|
||||
// Read Jtag State Register
|
||||
opts.IsWrite = false;
|
||||
ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts));
|
||||
if (!ret) return new(new Exception("Send Address Package Failed!"));
|
||||
|
||||
// Wait for Read Ack
|
||||
if (!MsgBus.IsRunning)
|
||||
return new(new Exception("Message Bus not Working!"));
|
||||
|
||||
var retPack = await MsgBus.UDPServer.WaitForDataAsync(address, 0, port);
|
||||
if (!retPack.IsSuccessful || !retPack.Value.IsSuccessful)
|
||||
return new(new Exception("Send address package failed"));
|
||||
|
||||
var retPackOpts = retPack.Value.Options;
|
||||
if (retPackOpts.Data is null)
|
||||
return new(new Exception($"Data is Null, package: {retPackOpts.ToString()}"));
|
||||
|
||||
var retPackLen = retPackOpts.Data.Length;
|
||||
if (retPackLen != 4)
|
||||
return new(new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes"));
|
||||
|
||||
return Convert.ToUInt32(Common.Number.BytesToUInt64(retPackOpts.Data).Value);
|
||||
}
|
||||
|
||||
async ValueTask<Result<bool>> WriteFIFO
|
||||
(UInt32 devAddr, UInt32 data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 delayMilliseconds = 0)
|
||||
{
|
||||
{
|
||||
var ret = await UDPClientPool.WriteAddr(this.ep, 0, devAddr, data, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Write FIFO failed"));
|
||||
}
|
||||
|
||||
// Delay some time before read register
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(delayMilliseconds));
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddrWithWait(this.ep, 0, JtagAddr.STATE, result, resultMask, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
async ValueTask<Result<bool>> WriteFIFO
|
||||
(UInt32 devAddr, byte[] data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 delayMilliseconds = 0)
|
||||
{
|
||||
{
|
||||
var ret = await UDPClientPool.WriteAddr(this.ep, 0, devAddr, data, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Write FIFO failed"));
|
||||
}
|
||||
|
||||
// Delay some time before read register
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(delayMilliseconds));
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddrWithWait(this.ep, 0, JtagAddr.STATE, result, resultMask, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 JTAG 寄存器
|
||||
/// </summary>
|
||||
/// <returns>指示清除是否成功的异步结果</returns>
|
||||
async ValueTask<Result<bool>> ClearAllRegisters()
|
||||
{
|
||||
return await WriteFIFO(JtagAddr.STATE, 0xFF_FF_FF_FF, 0x01_02_02_02, JtagState.ALL_REG);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除 JTAG 写数据寄存器
|
||||
/// </summary>
|
||||
/// <returns>指示清除是否成功的异步结果</returns>
|
||||
async ValueTask<Result<bool>> ClearWriteDataReg()
|
||||
{
|
||||
return await WriteFIFO(JtagAddr.STATE, 0x00_00_11_00, 0x01_00_02_00, JtagState.WRITE_DATA_FIFO | JtagState.CMD_EXEC_FINISH);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭 JTAG 测试模式
|
||||
/// </summary>
|
||||
/// <returns>指示操作是否成功的异步结果</returns>
|
||||
async ValueTask<Result<bool>> CloseTest()
|
||||
{
|
||||
return await WriteFIFO(
|
||||
JtagAddr.WRITE_CMD,
|
||||
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_CLOSE_TEST, JtagCmd.LEN_CMD_JTAG, 0, 28).Value,
|
||||
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动 JTAG 测试模式
|
||||
/// </summary>
|
||||
/// <returns>指示操作是否成功的异步结果</returns>
|
||||
async ValueTask<Result<bool>> RunTest()
|
||||
{
|
||||
return await WriteFIFO(
|
||||
JtagAddr.WRITE_CMD,
|
||||
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_RUN_TEST, JtagCmd.LEN_CMD_JTAG, 0, 28).Value,
|
||||
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置 JTAG 空闲延迟时间
|
||||
/// </summary>
|
||||
/// <param name="milliseconds">延迟时间(毫秒)</param>
|
||||
/// <returns>指示操作是否成功的异步结果</returns>
|
||||
async ValueTask<Result<bool>> IdleDelay(UInt32 milliseconds)
|
||||
{
|
||||
if (milliseconds > Math.Pow(2, 28)) return new(new Exception("Timespan is over 2^28 milliseconds"));
|
||||
|
||||
return await WriteFIFO(
|
||||
JtagAddr.WRITE_CMD,
|
||||
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_IDLE_DELAY, JtagCmd.LEN_CMD_JTAG, milliseconds, 28).Value,
|
||||
0x01_00_00_00, JtagState.CMD_EXEC_FINISH, (milliseconds / CLOCK_FREQ) + 100);
|
||||
}
|
||||
|
||||
async ValueTask<Result<bool>> ExecRDCmd(uint command)
|
||||
{
|
||||
{
|
||||
var ret = await WriteFIFO(
|
||||
JtagAddr.WRITE_DATA,
|
||||
Common.Number.MultiBitsToNumber(0, 22, command, JtagCmd.LEN_JTAG_DR).Value, 0, 0);
|
||||
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Write JTAG_DR_IDCODE Failed"));
|
||||
}
|
||||
{
|
||||
var ret = await WriteFIFO(
|
||||
JtagAddr.WRITE_CMD,
|
||||
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_IR, JtagCmd.LEN_CMD_JTAG, 10, 28).Value,
|
||||
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
||||
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Write CMD_JTAG_LOAD_IR Failed"));
|
||||
}
|
||||
return await ClearWriteDataReg();
|
||||
}
|
||||
|
||||
async ValueTask<Result<bool>> LoadDRCareInput(byte[] bytesArray, UInt32 timeout = 10_000, UInt32 cycle = 500)
|
||||
{
|
||||
var bytesLen = ((uint)(bytesArray.Length * 8));
|
||||
if (bytesLen > Math.Pow(2, 28)) return new(new Exception("Length is over 2^(28 - 3)"));
|
||||
|
||||
{
|
||||
var ret = await WriteFIFO(
|
||||
JtagAddr.WRITE_CMD,
|
||||
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREI, JtagCmd.LEN_CMD_JTAG, bytesLen, 28).Value,
|
||||
0, 0);
|
||||
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Write CMD_JTAG_LOAD_DR_CAREI Failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await WriteFIFO(
|
||||
JtagAddr.WRITE_DATA,
|
||||
bytesArray, 0x01_00_00_00,
|
||||
JtagState.CMD_EXEC_FINISH);
|
||||
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
async ValueTask<Result<UInt32>> LoadDRCareOutput(UInt32 UInt32Num)
|
||||
{
|
||||
if (UInt32Num > Math.Pow(2, 23)) return new(new Exception("Length is over 2^(28 - 5)"));
|
||||
|
||||
var ret = await WriteFIFO(
|
||||
JtagAddr.WRITE_CMD,
|
||||
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREO, JtagCmd.LEN_CMD_JTAG, 32 * UInt32Num, 28).Value,
|
||||
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
||||
|
||||
if (ret.Value)
|
||||
return await ReadFIFO(JtagAddr.READ_DATA);
|
||||
else
|
||||
return new(new Exception("LoadDRCareo Failed!"));
|
||||
}
|
||||
|
||||
async ValueTask<Result<UInt32[]>> LoadDRCareOutputArray(UInt32 UInt32Num)
|
||||
{
|
||||
if (UInt32Num > Math.Pow(2, 23)) return new(new Exception("Length is over 2^(28 - 5)"));
|
||||
|
||||
var ret = await WriteFIFO(
|
||||
JtagAddr.WRITE_CMD,
|
||||
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREO, JtagCmd.LEN_CMD_JTAG, 32 * UInt32Num, 28).Value,
|
||||
JtagState.CMD_EXEC_FINISH, JtagState.CMD_EXEC_FINISH);
|
||||
|
||||
if (ret.Value)
|
||||
{
|
||||
var array = new UInt32[UInt32Num];
|
||||
for (int i = 0; i < UInt32Num; i++)
|
||||
{
|
||||
var retData = await ReadFIFO(JtagAddr.READ_DATA);
|
||||
if (!retData.IsSuccessful)
|
||||
return new(new Exception("Read FIFO failed when Load DR"));
|
||||
array[i] = retData.Value;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
else
|
||||
return new(new Exception("LoadDRCareo Failed!"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取 JTAG 设备的 ID 代码
|
||||
/// </summary>
|
||||
/// <returns>包含 ID 代码的异步结果</returns>
|
||||
public async ValueTask<Result<uint>> ReadIDCode()
|
||||
{
|
||||
// Clear Data
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
|
||||
logger.Trace($"Clear up udp server {this.address,0} receive data");
|
||||
|
||||
Result<bool> ret;
|
||||
|
||||
ret = await ClearAllRegisters();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Clear All Registers Failed"));
|
||||
|
||||
ret = await RunTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
|
||||
|
||||
ret = await ExecRDCmd(JtagCmd.JTAG_DR_IDCODE);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_IDCODE Failed"));
|
||||
|
||||
ret = await ClearWriteDataReg();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Clear Write Registers Failed"));
|
||||
|
||||
var retData = await LoadDRCareOutput(1);
|
||||
if (!retData.IsSuccessful)
|
||||
{
|
||||
return new(new Exception("Get ID Code Failed"));
|
||||
}
|
||||
|
||||
return retData.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取 JTAG 设备的状态寄存器
|
||||
/// </summary>
|
||||
/// <returns>包含状态寄存器值的异步结果</returns>
|
||||
public async ValueTask<Result<uint>> ReadStatusReg()
|
||||
{
|
||||
// Clear Data
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
|
||||
logger.Trace($"Clear up udp server {this.address,0} receive data");
|
||||
|
||||
Result<bool> ret;
|
||||
|
||||
ret = await ClearAllRegisters();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Clear All Registers Failed"));
|
||||
|
||||
ret = await RunTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
|
||||
|
||||
ret = await ExecRDCmd(JtagCmd.JTAG_DR_RDSR);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_RDSR Failed"));
|
||||
|
||||
ret = await ClearWriteDataReg();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Clear Write Registers Failed"));
|
||||
|
||||
var retData = await LoadDRCareOutput(1);
|
||||
if (!retData.IsSuccessful)
|
||||
return new(new Exception("Read Status Reg Failed"));
|
||||
|
||||
return retData.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载比特流到 JTAG 设备
|
||||
/// </summary>
|
||||
/// <param name="bitstream">比特流数据</param>
|
||||
/// <returns>指示下载是否成功的异步结果</returns>
|
||||
public async ValueTask<Result<bool>> DownloadBitstream(byte[] bitstream)
|
||||
{
|
||||
// Clear Data
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
|
||||
logger.Trace($"Clear up udp server {this.address,0} receive data");
|
||||
|
||||
Result<bool> ret;
|
||||
|
||||
ret = await CloseTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Close Test Failed"));
|
||||
|
||||
ret = await RunTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
|
||||
|
||||
logger.Trace("Jtag initialize");
|
||||
|
||||
ret = await ExecRDCmd(JtagCmd.JTAG_DR_JRST);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_JRST Failed"));
|
||||
|
||||
ret = await RunTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
|
||||
|
||||
ret = await ExecRDCmd(JtagCmd.JTAG_DR_CFGI);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_CFGI Failed"));
|
||||
|
||||
logger.Trace("Jtag ready to write bitstream");
|
||||
|
||||
ret = await IdleDelay(100000);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed"));
|
||||
|
||||
ret = await LoadDRCareInput(bitstream);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Load Data Failed"));
|
||||
|
||||
logger.Trace("Jtag write bitstream");
|
||||
|
||||
ret = await CloseTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Close Test Failed"));
|
||||
|
||||
ret = await RunTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
|
||||
|
||||
ret = await ExecRDCmd(JtagCmd.JTAG_DR_JWAKEUP);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_JWAKEUP Failed"));
|
||||
|
||||
logger.Trace("Jtag reset device");
|
||||
|
||||
ret = await IdleDelay(10000);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed"));
|
||||
|
||||
var retCode = await ReadStatusReg();
|
||||
if (!retCode.IsSuccessful) return new(retCode.Error);
|
||||
var jtagStatus = new JtagStatusReg(retCode.Value);
|
||||
if (!(jtagStatus.done && jtagStatus.wakeup_over && jtagStatus.init_complete))
|
||||
return new(new Exception("Jtag download bitstream failed"));
|
||||
|
||||
ret = await CloseTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Close Test Failed"));
|
||||
logger.Trace("Jtag download bitstream successfully");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 边界扫描
|
||||
/// </summary>
|
||||
/// <returns>返回所有引脚边界扫描结果</returns>
|
||||
public async ValueTask<Result<BitArray>> BoundaryScan()
|
||||
{
|
||||
var paser = new BsdlParser.Parser();
|
||||
var portNum = paser.GetBoundaryRegsNum().Value;
|
||||
logger.Debug($"Get boundar scan registers number: {portNum}");
|
||||
|
||||
// Clear Data
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
|
||||
logger.Trace($"Clear up udp server {this.address,0} receive data");
|
||||
|
||||
Result<bool> ret;
|
||||
|
||||
ret = await CloseTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Close Test Failed"));
|
||||
|
||||
ret = await RunTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
|
||||
|
||||
logger.Trace("Jtag initialize");
|
||||
|
||||
ret = await ExecRDCmd(JtagCmd.JTAG_DR_SAMPLE);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_SAMPLE Failed"));
|
||||
|
||||
var retData = await LoadDRCareOutputArray(((uint)(portNum % 32 == 0 ? portNum / 32 : portNum / 32 + 1)));
|
||||
if (!retData.IsSuccessful) return new(retData.Error);
|
||||
|
||||
ret = await CloseTest();
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
else if (!ret.Value) return new(new Exception("Jtag Close Test Failed"));
|
||||
|
||||
var byteArray = Common.Number.UInt32ArrayToBytes(retData.Value);
|
||||
if (!byteArray.IsSuccessful) return new(byteArray.Error);
|
||||
|
||||
var bitArray = new BitArray(byteArray.Value);
|
||||
if (bitArray is null)
|
||||
return new(new Exception($"Convert to BitArray failed"));
|
||||
bitArray.Length = portNum;
|
||||
return bitArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行边界扫描并返回逻辑端口的状态
|
||||
/// </summary>
|
||||
/// <returns>包含逻辑端口状态的字典,键为端口ID,值为布尔状态</returns>
|
||||
public async ValueTask<Result<Dictionary<string, bool>>> BoundaryScanLogicalPorts()
|
||||
{
|
||||
var bitArray = await BoundaryScan();
|
||||
if (!bitArray.IsSuccessful) return new(bitArray.Error);
|
||||
|
||||
var paser = new BsdlParser.Parser();
|
||||
var cellList = paser.GetBoundaryLogicalPorts();
|
||||
if (cellList.IsNull) return new(new Exception("Get boundary logical ports failed"));
|
||||
|
||||
var portStatus = new Dictionary<string, bool>();
|
||||
foreach (var cell in cellList.Value)
|
||||
{
|
||||
portStatus.Add(cell.PortID ?? "UnknownPortID", bitArray.Value[cell.CellNumber]);
|
||||
}
|
||||
|
||||
return portStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置JTAG的运行速度。
|
||||
/// </summary>
|
||||
/// <param name="speed">运行速度值。</param>
|
||||
/// <returns>指示操作是否成功的异步结果。</returns>
|
||||
public async ValueTask<Result<bool>> SetSpeed(UInt32 speed)
|
||||
{
|
||||
// Clear Data
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
|
||||
logger.Trace($"Clear up udp server {this.address,0} receive data");
|
||||
|
||||
var ret = await WriteFIFO(
|
||||
JtagAddr.SPEED_CTRL, (speed << 16) | speed,
|
||||
JtagState.CMD_EXEC_FINISH, JtagState.CMD_EXEC_FINISH);
|
||||
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
555
server/src/Peripherals/RemoteUpdateClient.cs
Normal file
555
server/src/Peripherals/RemoteUpdateClient.cs
Normal file
@@ -0,0 +1,555 @@
|
||||
using System.Net;
|
||||
using DotNext;
|
||||
|
||||
namespace Peripherals.RemoteUpdateClient;
|
||||
|
||||
static class RemoteUpdaterAddr
|
||||
{
|
||||
public const UInt32 Base = 0x20_00_00_00;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X00: 写Flash-读写地址——控制位 <br/>
|
||||
/// [31:16]: wr_sector_num <br/>
|
||||
/// [15: 0]: {flash_wr_en,-,-,-, start_wr_sector} <br/>
|
||||
/// </summary>
|
||||
public const UInt32 WriteCtrl = Base + 0x00;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X01: 写Flash-只写地址——FIFO入口 <br/>
|
||||
/// [31:0]: 写比特流数据入口 <br/>
|
||||
/// </summary>
|
||||
public const UInt32 WriteFIFO = Base + 0x01;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X02: 写Flash-只读地址——标志位 <br/>
|
||||
/// [31:24]: {-, -, -, -, -, -, -, wr_fifo_full} <br/>
|
||||
/// [23:16]: {-, -, -, -, -, -, -, wr_fifo_empty} <br/>
|
||||
/// [15: 8]: {-, -, -, -, -, -, -, flash_wr_done} <br/>
|
||||
/// [ 7: 0]: {-, -, -, -, -, -, -, flash_clear_done} <br/>
|
||||
/// </summary>
|
||||
public const UInt32 WriteSign = Base + 0x02;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X03: 读Flash-读写地址——控制位1 <br/>
|
||||
/// [31:16]: rd_sector_num <br/>
|
||||
/// [15: 0]: {flash_rd_en,-,-,-, start_rd_sub_sector} <br/>
|
||||
/// </summary>
|
||||
public const UInt32 ReadCtrl1 = Base + 0x03;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X04: 读Flash-读写地址——控制位2 <br/>
|
||||
/// [31:24]: { } <br/>
|
||||
/// [23:16]: {-, -, -, -, -, -,{ bs_crc32_ok }} <br/>
|
||||
/// [15: 8]: {-, -, -, -, -, -, -, crc_check_en} <br/>
|
||||
/// [ 7: 0]: {-, -, -, -, -, -, -, bitstream_up2cpu_en} <br/>
|
||||
/// </summary>
|
||||
public const UInt32 ReadCtrl2 = Base + 0x04;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X05: 读Flash-只读地址——FIFO出口 <br/>
|
||||
/// [31:0]: 读比特流数据出口 <br/>
|
||||
/// </summary>
|
||||
public const UInt32 ReadFIFO = Base + 0x05;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X06: 读Flash-只读地址——CRC校验值 <br/>
|
||||
/// [31:0]: CRC校验值 bs_readback_crc <br/>
|
||||
/// </summary>
|
||||
public const UInt32 ReadCRC = Base + 0x06;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X07: 读Flash-只读地址——标志位 <br/>
|
||||
/// [31:24]: {-, -, -, -, -, -, -, rd_fifo_afull} <br/>
|
||||
/// [23:16]: {-, -, -, -, -, -, -, rd_fifo_empty} <br/>
|
||||
/// [15: 8]: {-, -, -, -, -, -, -, flash_rd_done} <br/>
|
||||
/// [ 7: 0]: {-, -, -, -, -, -, -, bs_readback_crc_valid} <br/>
|
||||
/// </summary>
|
||||
public const UInt32 ReadSign = Base + 0x07;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X08: 热启动开关-读写地址——控制位 <br/>
|
||||
/// [31: 8]: hotreset_addr <br/>
|
||||
/// [ 7: 0]: {-, -, -, -, -, -, -, hotreset_en} <br/>
|
||||
/// </summary>
|
||||
public const UInt32 HotResetCtrl = Base + 0x08;
|
||||
|
||||
/// <summary>
|
||||
/// ADDR: 0X09: 只读地址 版本号 <br/>
|
||||
/// [31: 0]: FPGA_VERSION[31:0] <br/>
|
||||
/// </summary>
|
||||
public const UInt32 Version = Base + 0x09;
|
||||
}
|
||||
|
||||
static class FlashAddr
|
||||
{
|
||||
public static readonly UInt32[] Switch = { 0xFFFF, 0x0000, 0x2000, 0x4000 };
|
||||
public static readonly UInt32[] Jump = { 0xFFFF, 0x1000, 0x3000, 0x5000 };
|
||||
public static readonly UInt32[] Bitstream = { 0x006000, 0x406000, 0x806000, 0xC06000 };
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
public class RemoteUpdater
|
||||
{
|
||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
const int FLASH_SECTOR_LENGTH = 4 * 1024;
|
||||
|
||||
readonly int timeout = 2000;
|
||||
readonly int timeoutForWait = 60 * 1000;
|
||||
|
||||
readonly int port;
|
||||
readonly string address;
|
||||
private IPEndPoint ep;
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="address">[TODO:parameter]</param>
|
||||
/// <param name="port">[TODO:parameter]</param>
|
||||
/// <param name="timeout">[TODO:parameter]</param>
|
||||
/// <param name="timeoutForWait">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public RemoteUpdater(string address, int port, int timeout = 2000, int timeoutForWait = 60 * 1000)
|
||||
{
|
||||
if (timeout < 0)
|
||||
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
|
||||
this.timeout = timeout;
|
||||
this.timeoutForWait = timeoutForWait;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="flashAddr">[TODO:parameter]</param>
|
||||
/// <param name="writeSectorNum">[TODO:parameter]</param>
|
||||
/// <param name="bytesData">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
private async ValueTask<Result<bool>> WriteFlash(UInt32 flashAddr, int writeSectorNum, byte[] bytesData)
|
||||
{
|
||||
// Assert
|
||||
if (writeSectorNum <= 0 || writeSectorNum >= FLASH_SECTOR_LENGTH)
|
||||
return new(new ArgumentException(
|
||||
$"Write sector num should be 1 ~ 4096, but given {writeSectorNum}", nameof(writeSectorNum)));
|
||||
if (bytesData.Length % (4 * 1024) != 0)
|
||||
return new(new ArgumentException(
|
||||
$"The length of data should be divided by 4096, bug given {bytesData.Length}", nameof(bytesData)));
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.WriteAddr(
|
||||
this.ep, 0, RemoteUpdaterAddr.WriteCtrl,
|
||||
Convert.ToUInt32((writeSectorNum << 16) | (1 << 15) | Convert.ToInt32(flashAddr / 4096)), this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Enable write flash failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddrWithWait(
|
||||
this.ep, 0, RemoteUpdaterAddr.WriteSign,
|
||||
0x00_00_00_01, 0x00_00_00_01, this.timeoutForWait);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception(
|
||||
$"Flash clear failed after {this.timeoutForWait} milliseconds"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.WriteAddr(this.ep, 0, RemoteUpdaterAddr.WriteFIFO, bytesData, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Send data to flash failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddrWithWait(
|
||||
this.ep, 0, RemoteUpdaterAddr.WriteSign,
|
||||
0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
private async ValueTask<Result<bool>> EnableBitstream(int bitstreamNum)
|
||||
{
|
||||
// Assert
|
||||
if (bitstreamNum <= 0 || bitstreamNum > 3)
|
||||
return new(new ArgumentException(
|
||||
$"Bitsteam num should be 1 ~ 3 for EnableBitstream, but given {bitstreamNum}", nameof(bitstreamNum)));
|
||||
|
||||
var bytesData = new byte[FLASH_SECTOR_LENGTH];
|
||||
Array.Fill<byte>(bytesData, 0xFF);
|
||||
bytesData[FLASH_SECTOR_LENGTH - 1] = 0x01;
|
||||
bytesData[FLASH_SECTOR_LENGTH - 2] = 0x33;
|
||||
bytesData[FLASH_SECTOR_LENGTH - 3] = 0x2D;
|
||||
bytesData[FLASH_SECTOR_LENGTH - 4] = 0x94;
|
||||
|
||||
|
||||
var ret = await WriteFlash(FlashAddr.Switch[bitstreamNum], 1, bytesData);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
private async ValueTask<Result<bool>> DisableBitstream(int bitstreamNum)
|
||||
{
|
||||
// Assert
|
||||
if (bitstreamNum <= 0 || bitstreamNum > 3)
|
||||
return new(new ArgumentException(
|
||||
$"Bitsteam num should be 1 ~ 3 for DisableBitstream, but given {bitstreamNum}", nameof(bitstreamNum)));
|
||||
|
||||
var bytesData = new byte[FLASH_SECTOR_LENGTH];
|
||||
Array.Fill<byte>(bytesData, 0xFF);
|
||||
|
||||
var ret = await WriteFlash(FlashAddr.Switch[bitstreamNum], 1, bytesData);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
private async ValueTask<Result<bool>> WriteJumpCmd(int bitstreamNum)
|
||||
{
|
||||
// Assert
|
||||
if (bitstreamNum <= 0 || bitstreamNum > 3)
|
||||
return new(new ArgumentException(
|
||||
$"Bitsteam num should be 1 ~ 3 for WriteJumpCmd, but given {bitstreamNum}", nameof(bitstreamNum)));
|
||||
|
||||
// Init data
|
||||
var bytesData = new byte[FLASH_SECTOR_LENGTH];
|
||||
for (int i = 3; i < FLASH_SECTOR_LENGTH; i += 4)
|
||||
bytesData[i] = (byte)0xA0;
|
||||
|
||||
{
|
||||
var bytesSrc = new byte[] { 0x01, 0x00, 0x00, 0xAB };
|
||||
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x04, 4);
|
||||
}
|
||||
{
|
||||
var bytesSrc = new byte[] { 0x0B, 0x00, 0x00, 0x00 };
|
||||
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x08, 4);
|
||||
}
|
||||
{
|
||||
var bytesSrc = new byte[] { 0x01, 0x00, 0xC0, 0xAB };
|
||||
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x34, 4);
|
||||
}
|
||||
{
|
||||
var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x00 };
|
||||
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x38, 4);
|
||||
}
|
||||
{
|
||||
var bytesSrc = new byte[] { 0x01, 0x00, 0x00, 0xAC };
|
||||
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x3C, 4);
|
||||
}
|
||||
{
|
||||
var bytesSrc = Common.Number.NumberToBytes(FlashAddr.Bitstream[bitstreamNum], 4).Value;
|
||||
Buffer.BlockCopy(Common.Number.ReverseBytes(bytesSrc, 4).Value, 0, bytesData, 0x40, 4);
|
||||
}
|
||||
{
|
||||
var bytesSrc = new byte[] { 0x01, 0x00, 0x80, 0xA8 };
|
||||
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x44, 4);
|
||||
}
|
||||
{
|
||||
var bytesSrc = new byte[] { 0x0F, 0x00, 0x00, 0x00 };
|
||||
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x48, 4);
|
||||
}
|
||||
|
||||
var ret = await WriteFlash(FlashAddr.Jump[bitstreamNum], 1, bytesData);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
private async ValueTask<Result<bool>> InitRemoteUpdate(int bitstreamNum)
|
||||
{
|
||||
// Assert
|
||||
if (bitstreamNum < 0 || bitstreamNum > 3)
|
||||
return new(new ArgumentException(
|
||||
$"Bitsteam num should be 0 ~ 3 for InitRemoteUpdate, but given {bitstreamNum}", nameof(bitstreamNum)));
|
||||
|
||||
for (int i = 1; i <= 3; i++)
|
||||
{
|
||||
{
|
||||
var ret = (i == bitstreamNum) ? await EnableBitstream(i) : await DisableBitstream(i);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception($"Write switch {i} failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await WriteJumpCmd(i);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception($"Write jump {i} failed"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <param name="bitstreamLen">[TODO:parameter]</param>
|
||||
/// <param name="checkSum">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
private async ValueTask<Result<bool>> CheckBitstreamCRC(int bitstreamNum, int bitstreamLen, UInt32 checkSum)
|
||||
{
|
||||
{
|
||||
var ret = await UDPClientPool.WriteAddr(this.ep, 0, RemoteUpdaterAddr.ReadCtrl2, 0x00_00_00_00, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Write read control 2 failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.WriteAddr(
|
||||
this.ep, 0, RemoteUpdaterAddr.ReadCtrl1,
|
||||
Convert.ToUInt32((bitstreamLen << 16) | (1 << 15) | Convert.ToInt32(FlashAddr.Bitstream[bitstreamNum] / 4096)),
|
||||
this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Write read control 1 failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddrWithWait(
|
||||
this.ep, 0, RemoteUpdaterAddr.ReadSign,
|
||||
0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception(
|
||||
$"Read bitstream failed after {this.timeoutForWait} milliseconds"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddr(this.ep, 0, RemoteUpdaterAddr.ReadCRC, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
|
||||
var bytes = ret.Value.Options.Data;
|
||||
if (bytes is null)
|
||||
return new(new Exception("CRC is null"));
|
||||
|
||||
var remoteCRC = Common.Number.BytesToUInt32(bytes);
|
||||
if (!remoteCRC.IsSuccessful) return new(remoteCRC.Error);
|
||||
|
||||
logger.Debug($"Bitstream {bitstreamNum} Expected CRC: \t 0x{Convert.ToString(checkSum, 16)}");
|
||||
logger.Debug($"Bitstream {bitstreamNum} Received CRC: \t 0x{Convert.ToString(remoteCRC.Value, 16)}");
|
||||
|
||||
return remoteCRC.Value == checkSum;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
private async ValueTask<Result<bool>> HotReset(int bitstreamNum)
|
||||
{
|
||||
// Assert
|
||||
if (bitstreamNum < 0 || bitstreamNum > 3)
|
||||
return new(new ArgumentException(
|
||||
$"Bitsteam num should be 0 ~ 3 for HotRest, but given {bitstreamNum}", nameof(bitstreamNum)));
|
||||
|
||||
var ret = await UDPClientPool.WriteAddr(
|
||||
this.ep, 0, RemoteUpdaterAddr.HotResetCtrl,
|
||||
((FlashAddr.Bitstream[bitstreamNum] << 8) | 1), this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public async ValueTask<Result<bool>> HotResetBitstream(int bitstreamNum)
|
||||
{
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
logger.Trace("Clear udp data finished");
|
||||
|
||||
{
|
||||
var ret = await InitRemoteUpdate(bitstreamNum);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Init remote update failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await HotReset(bitstreamNum);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="goldenBitream">[TODO:parameter]</param>
|
||||
/// <param name="bitstream1">[TODO:parameter]</param>
|
||||
/// <param name="bitstream2">[TODO:parameter]</param>
|
||||
/// <param name="bitstream3">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public async ValueTask<Result<bool>> UploadBitstreams(
|
||||
byte[]? goldenBitream,
|
||||
byte[]? bitstream1,
|
||||
byte[]? bitstream2,
|
||||
byte[]? bitstream3)
|
||||
{
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
logger.Trace("Clear udp data finished");
|
||||
|
||||
for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++)
|
||||
{
|
||||
byte[] bytesData;
|
||||
if (bitstreamNum == 0 && goldenBitream is not null)
|
||||
bytesData = goldenBitream;
|
||||
else if (bitstreamNum == 1 && bitstream1 is not null)
|
||||
bytesData = bitstream1;
|
||||
else if (bitstreamNum == 2 && bitstream2 is not null)
|
||||
bytesData = bitstream2;
|
||||
else if (bitstreamNum == 3 && bitstream3 is not null)
|
||||
bytesData = bitstream3;
|
||||
else continue;
|
||||
|
||||
var bitstreamBlockNum = bytesData.Length / (4 * 1024);
|
||||
|
||||
{
|
||||
var ret = await WriteFlash(FlashAddr.Bitstream[bitstreamNum], bitstreamBlockNum, bytesData);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Write bitstream failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var crc = Honoo.IO.Hashing.Crc.Create(Honoo.IO.Hashing.CrcName.CRC32_MPEG_2);
|
||||
var checkSum = crc.ComputeFinal(bytesData);
|
||||
var ret = await CheckBitstreamCRC(bitstreamNum, bitstreamBlockNum, checkSum.ToUInt32());
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) logger.Warn($"Bitstream {bitstreamNum} CRC32 not correct!");
|
||||
else logger.Info($"Bitstream {bitstreamNum} CRC32 calibration passed");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
||||
/// <param name="bytesData">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public async ValueTask<Result<bool>> UpdateBitstream(int bitstreamNum, byte[] bytesData)
|
||||
{
|
||||
if (bytesData.Length % (4 * 1024) != 0)
|
||||
return new(new ArgumentException(
|
||||
$"The length of data should be divided by 4096, bug given {bytesData.Length}", nameof(bytesData)));
|
||||
var bitstreamBlockNum = bytesData.Length / (4 * 1024);
|
||||
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
logger.Trace("Clear udp data finished");
|
||||
|
||||
{
|
||||
var ret = await InitRemoteUpdate(bitstreamNum);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Init remote update failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await WriteFlash(FlashAddr.Bitstream[bitstreamNum], bitstreamBlockNum, bytesData);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return new(new Exception("Write bitstream failed"));
|
||||
}
|
||||
|
||||
{
|
||||
var crc = Honoo.IO.Hashing.Crc.Create(Honoo.IO.Hashing.CrcName.CRC32_MPEG_2);
|
||||
var checkSum = crc.ComputeFinal(bytesData);
|
||||
var ret = await CheckBitstreamCRC(bitstreamNum, bitstreamBlockNum, checkSum.ToUInt32());
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) logger.Warn("CRC32 not correct!");
|
||||
else logger.Info("CRC32 calibration passed");
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await HotReset(bitstreamNum);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <param name="enableBitstreamNum">[TODO:parameter]</param>
|
||||
/// <param name="goldenBitream">[TODO:parameter]</param>
|
||||
/// <param name="bitstream1">[TODO:parameter]</param>
|
||||
/// <param name="bitstream2">[TODO:parameter]</param>
|
||||
/// <param name="bitstream3">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public async ValueTask<Result<bool>> UpdateBitstreams(
|
||||
int enableBitstreamNum,
|
||||
byte[]? goldenBitream,
|
||||
byte[]? bitstream1,
|
||||
byte[]? bitstream2,
|
||||
byte[]? bitstream3)
|
||||
{
|
||||
// Assert
|
||||
if (goldenBitream is null && bitstream1 is null && bitstream2 is null && bitstream3 is null)
|
||||
return new(new ArgumentException(
|
||||
$"At least one bitstream should not be empty"));
|
||||
if ((enableBitstreamNum == 0 && goldenBitream is null) ||
|
||||
(enableBitstreamNum == 1 && bitstream1 is null) ||
|
||||
(enableBitstreamNum == 2 && bitstream2 is null) ||
|
||||
(enableBitstreamNum == 3 && bitstream3 is null))
|
||||
return new(new ArgumentException($"Bitstream {enableBitstreamNum} shouldn't be empty"));
|
||||
|
||||
{
|
||||
var ret = await UploadBitstreams(goldenBitream, bitstream1, bitstream2, bitstream3);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
if (!ret.Value) return false;
|
||||
}
|
||||
|
||||
{
|
||||
var ret = await HotResetBitstream(enableBitstreamNum);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
return ret.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// </summary>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
public async ValueTask<Result<UInt32>> GetVersion()
|
||||
{
|
||||
await MsgBus.UDPServer.ClearUDPData(this.address, 0);
|
||||
logger.Trace("Clear udp data finished");
|
||||
|
||||
{
|
||||
var ret = await UDPClientPool.ReadAddr(this.ep, 0, RemoteUpdaterAddr.Version, this.timeout);
|
||||
if (!ret.IsSuccessful) return new(ret.Error);
|
||||
|
||||
var retData = ret.Value.Options.Data;
|
||||
if (retData is null || retData.Length != 4) return new(new Exception("Failed to read remote update firmware version"));
|
||||
var version = Common.Number.BytesToUInt32(retData);
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user