|
|
|
@ -1,237 +1,175 @@
|
|
|
|
|
using System.Buffers.Binary;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using DotNext;
|
|
|
|
|
namespace RemoteUpdate;
|
|
|
|
|
|
|
|
|
|
static class Global
|
|
|
|
|
static class RemoteUpdateClientAddr
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// FPGA 远程更新器地址
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class RemoteUpdaterAddr
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 基地址
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 BaseAddr = 0x2000_0000;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写比特流-读写地址——控制位 <br/>
|
|
|
|
|
/// [31:24]: 保留 <br/>
|
|
|
|
|
/// [23:17]: 保留 <br/>
|
|
|
|
|
/// [16: 8]: 保留 <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -,{bitstream_wr_num}, flash_wr_en} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 WriteCtrl = 0x2000_0000;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写比特流-只写地址——FIFO入口
|
|
|
|
|
/// [31:0]: 写比特流数据入口
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 WriteFIFO = 0x2000_0001;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写比特流-只读地址——标志位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -, -, wr_fifo_full} <br/>
|
|
|
|
|
/// [23:17]: {-, -, -, -, -, -, -, wr_fifo_empty} <br/>
|
|
|
|
|
/// [16: 8]: {-, -, -, -, -, -, -, bitstream_wr_done} <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -, -, -, clear_bs_done} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 WriteSign = 0x2000_0002;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 读比特流-读写地址——控制位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -,{ bs_crc32_ok }} <br/>
|
|
|
|
|
/// [23:17]: {-, -, -, -, -, -, -, crc_check_en} <br/>
|
|
|
|
|
/// [16: 8]: {-, -, -, -, -, -, -, bitstream_up2cpu_en} <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -,{bitstream_rd_num},flash_rd_en} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 ReadCtrl = 0x2000_0003;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 读比特流-只读地址——FIFO出口
|
|
|
|
|
/// [31:0]: 读比特流数据出口
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 ReadFIFO = 0x2000_0004;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 读比特流-只读地址——CRC校验值
|
|
|
|
|
/// [31:0]: CRC校验值 bs_readback_crc
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 ReadCRC = 0x2000_0005;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 读比特流-只读地址——标志位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -, -, rd_fifo_afull} <br/>
|
|
|
|
|
/// [23:17]: {-, -, -, -, -, -, -, rd_fifo_empty} <br/>
|
|
|
|
|
/// [16: 8]: {-, -, -, -, -, -, -, bitstream_rd_done} <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -, -, -, bs_readback_crc_valid} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 ReadSign = 0x2000_0006;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 单独擦除开关-读写地址——控制位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [23:17]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [16: 8]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -, -, -, clear_sw_en} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 ClearCtrl = 0x2000_0007;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 单独擦除开关-只读地址——标志位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -, -, time_out_reg} <br/>
|
|
|
|
|
/// [23:17]: { flash_flag_status[15:8]} <br/>
|
|
|
|
|
/// [16: 8]: { flash_flag_status[7:0]} <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -, -, -, clear_sw_done} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 ClearSign = 0x2000_0008;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写开关-读写地址——控制位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [23:17]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [16: 8]: {-, -, -, -, -, -, { open_sw_num}} <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -, -, -, write_sw_code_en} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 WriteSwitchCtrl = 0x2000_0009;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写开关-只读地址——标志位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [23:17]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [16: 8]: {-, -, -, -, -, -, -, ipal_busy} <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -, -, -, open_sw_code_done} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 WriteSwitchSign = 0x2000_000A;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 热启动开关-读写地址——控制位 <br/>
|
|
|
|
|
/// [31:24]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [23:17]: {-, -, -, -, -, -, -, - } <br/>
|
|
|
|
|
/// [16: 8]: {-, -, -, -, -, -, } <br/>
|
|
|
|
|
/// [ 7: 0]: {-, -, -, -, -, -, -, hotreset_en} <br/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const UInt32 HotResetCtrl = 0x2000_000B;
|
|
|
|
|
}
|
|
|
|
|
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>
|
|
|
|
|
/// FPGA远程更新
|
|
|
|
|
/// [TODO:description]
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class RemoteUpdater
|
|
|
|
|
public class RemoteUpdateClient
|
|
|
|
|
{
|
|
|
|
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
|
|
|
|
readonly int timeout = 1000;
|
|
|
|
|
const int FLASH_SECTOR_LENGTH = 4 * 1024;
|
|
|
|
|
|
|
|
|
|
int port { get; }
|
|
|
|
|
string address { get; }
|
|
|
|
|
private IPEndPoint remoteEP;
|
|
|
|
|
readonly int timeout = 2000;
|
|
|
|
|
readonly int timeoutForWait = 60 * 1000;
|
|
|
|
|
|
|
|
|
|
readonly int port;
|
|
|
|
|
readonly string address;
|
|
|
|
|
private IPEndPoint ep;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 初始化 FPGA 远程更新器
|
|
|
|
|
/// [TODO:description]
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="address">目标设备的 IP 地址</param>
|
|
|
|
|
/// <param name="port">目标设备的端口号</param>
|
|
|
|
|
public RemoteUpdater(string address, int port)
|
|
|
|
|
/// <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 RemoteUpdateClient(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.remoteEP = new IPEndPoint(IPAddress.Parse(address), port);
|
|
|
|
|
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
|
|
|
|
|
this.timeout = timeout;
|
|
|
|
|
this.timeoutForWait = timeoutForWait;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Writes a bitstream to the FPGA device.
|
|
|
|
|
/// [TODO:description]
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bitstreamNum">The bitstream number (0-3).</param>
|
|
|
|
|
/// <param name="bitstream">The bitstream data to write.</param>
|
|
|
|
|
/// <returns>A result indicating success or failure.</returns>
|
|
|
|
|
public async ValueTask<Result<bool>> WriteBitstream(int bitstreamNum, byte[] bitstream)
|
|
|
|
|
/// <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)
|
|
|
|
|
{
|
|
|
|
|
if (bitstreamNum < 0 || bitstreamNum > 0b11)
|
|
|
|
|
// Assert
|
|
|
|
|
if (writeSectorNum <= 0 || writeSectorNum >= FLASH_SECTOR_LENGTH)
|
|
|
|
|
return new(new ArgumentException(
|
|
|
|
|
"The number of bitstream can't be negative or over 3(0b11)", nameof(bitstreamNum)));
|
|
|
|
|
|
|
|
|
|
if (bitstream.Length % (4 * 1024) != 0)
|
|
|
|
|
$"Write sector num should be 1 ~ 4096, but given {writeSectorNum}", nameof(writeSectorNum)));
|
|
|
|
|
if (bytesData.Length % (4 * 1024) != 0)
|
|
|
|
|
return new(new ArgumentException(
|
|
|
|
|
"Bitstream doesn't match 4KB align, which should be divided by 4 * 1024", nameof(bitstream)));
|
|
|
|
|
|
|
|
|
|
// Clear Data
|
|
|
|
|
await MsgBus.UDPServer.ClearUDPData(this.address);
|
|
|
|
|
|
|
|
|
|
logger.Trace($"Clear up udp server {this.address} receive data");
|
|
|
|
|
|
|
|
|
|
$"The length of data should be divided by 4096, bug given {bytesData.Length}", nameof(bytesData)));
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.WriteAddr(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.WriteCtrl, (UInt32)((bitstreamNum << 1) | 0b1), timeout);
|
|
|
|
|
this.ep, RemoteUpdateClientAddr.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.remoteEP, Global.RemoteUpdaterAddr.ClearSign, 0x00_01, 0x00_01, 60 * 1000);
|
|
|
|
|
// if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
// if (!ret.Value) return new(new Exception("Clear switch failed even over time"));
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.ReadAddrWithWait(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.WriteSign, 0x00_00_00_01, 0x00_00_00_01, 60 * 1000);
|
|
|
|
|
this.ep, RemoteUpdateClientAddr.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("Clear bitstream failed even over time"));
|
|
|
|
|
if (!ret.Value) return new(new Exception(
|
|
|
|
|
$"Flash clear failed after {this.timeoutForWait} milliseconds"));
|
|
|
|
|
}
|
|
|
|
|
logger.Trace($"Device {this.address} remote update init finished");
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.WriteAddr(this.remoteEP, Global.RemoteUpdaterAddr.WriteFIFO, bitstream);
|
|
|
|
|
var ret = await UDPClientPool.WriteAddr(this.ep, RemoteUpdateClientAddr.WriteFIFO, bytesData, this.timeout);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Send bitstream failed"));
|
|
|
|
|
if (!ret.Value) return new(new Exception("Send data to flash failed"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.ReadAddrWithWait(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.WriteSign, 0x00_00_01_00, 0x00_00_01_00, timeout);
|
|
|
|
|
this.ep, RemoteUpdateClientAddr.WriteSign,
|
|
|
|
|
0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Write bitstream failed even over time"));
|
|
|
|
|
}
|
|
|
|
|
logger.Trace($"Device {this.address} write bitstream finished");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Checks the CRC of a bitstream on the FPGA device.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bitstreamNum">The bitstream number (0-3).</param>
|
|
|
|
|
/// <param name="checkSum">The expected CRC checksum value.</param>
|
|
|
|
|
/// <returns>A result indicating whether the CRC matches.</returns>
|
|
|
|
|
public async ValueTask<Result<bool>> CheckBitstreamCRC(int bitstreamNum, UInt32 checkSum)
|
|
|
|
|
{
|
|
|
|
|
// Clear Data
|
|
|
|
|
await MsgBus.UDPServer.ClearUDPData(this.address);
|
|
|
|
|
|
|
|
|
|
logger.Trace($"Clear up udp server {this.address} receive data");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.WriteAddr(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.ReadCtrl,
|
|
|
|
|
(UInt32)((bitstreamNum << 1) | 0b1), timeout);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Enable read failed"));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.ReadAddrWithWait(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.ReadSign, 0x00_00_01_00, 0x00_00_01_00, 60 * 1000);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Read bitstream failed even over time"));
|
|
|
|
|
}
|
|
|
|
|
logger.Trace($"Device {this.address} remote update read bitstream finished");
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.ReadAddr(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.ReadCRC, timeout);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (ret.Value.Options.Data is null) return new(new Exception("Receive null when read sign"));
|
|
|
|
|
|
|
|
|
|
// Check CRC
|
|
|
|
|
var crcCode = Common.Number.BytesToUInt32(ret.Value.Options.Data);
|
|
|
|
|
if (!crcCode.IsSuccessful) return new(crcCode.Error);
|
|
|
|
|
|
|
|
|
|
var retCRC = crcCode.Value == checkSum;
|
|
|
|
|
logger.Trace($"Device {this.address} remote update check crc finished");
|
|
|
|
|
|
|
|
|
|
logger.Debug($"Expected CRC32/Mpeg Code: 0x{checkSum.ToString("X").PadLeft(8, '0')}");
|
|
|
|
|
logger.Debug($"Actually CRC32/Mpeg Code: 0x{crcCode.Value.ToString("X").PadLeft(8, '0')}");
|
|
|
|
|
return retCRC;
|
|
|
|
|
return ret.Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -240,80 +178,292 @@ public class RemoteUpdater
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
|
|
|
|
/// <returns>[TODO:return]</returns>
|
|
|
|
|
public async ValueTask<Result<bool>> HotReset(int bitstreamNum)
|
|
|
|
|
private async ValueTask<Result<bool>> EnableBitstream(int bitstreamNum)
|
|
|
|
|
{
|
|
|
|
|
// Clear Data
|
|
|
|
|
// 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 - 4] = 0x80; // 0b10000000
|
|
|
|
|
bytesData[FLASH_SECTOR_LENGTH - 3] = 0xCC; // 0b11001100
|
|
|
|
|
bytesData[FLASH_SECTOR_LENGTH - 2] = 0xB4; // 0b10110100
|
|
|
|
|
bytesData[FLASH_SECTOR_LENGTH - 1] = 0x29; // 0b00101001
|
|
|
|
|
|
|
|
|
|
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 = 0; i < FLASH_SECTOR_LENGTH; i += 4)
|
|
|
|
|
bytesData[i] = BinaryPrimitives.ReverseEndianness((byte)0xA0);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = new byte[] { 0xAB, 0x00, 0x00, 0x01 };
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x04, 4);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x0B };
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x08, 4);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = new byte[] { 0xAB, 0xC0, 0x00, 0x01 };
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x34, 4);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x38, 4);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = new byte[] { 0xAC, 0x00, 0x00, 0x01 };
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x3C, 4);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = Common.Number.NumberToBytes(FlashAddr.Bitstream[bitstreamNum], 4).Value;
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x40, 4);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = new byte[] { 0xA8, 0x80, 0x00, 0x01 };
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x44, 4);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x0F };
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
|
bytesSrc[i] = BinaryPrimitives.ReverseEndianness(bytesSrc[i]);
|
|
|
|
|
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, RemoteUpdateClientAddr.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, RemoteUpdateClientAddr.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, RemoteUpdateClientAddr.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, RemoteUpdateClientAddr.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($"Expected CRC: \t 0x{Convert.ToString(checkSum, 16)}");
|
|
|
|
|
logger.Debug($"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>> HotRest(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, RemoteUpdateClientAddr.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>
|
|
|
|
|
/// <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);
|
|
|
|
|
|
|
|
|
|
logger.Trace($"Clear up udp server {this.address} receive data");
|
|
|
|
|
|
|
|
|
|
logger.Trace("Clear udp data finished");
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.WriteAddr(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.ClearCtrl,
|
|
|
|
|
(UInt32)(0b1), timeout);
|
|
|
|
|
var ret = await InitRemoteUpdate(bitstreamNum);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Run clear failed"));
|
|
|
|
|
if (!ret.Value) return new(new Exception("Init remote update failed"));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.ReadAddrWithWait(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.ClearSign, 0x00_00_00_01, 0x00_00_00_01, 60 * 1000);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Clear failed even over time"));
|
|
|
|
|
}
|
|
|
|
|
logger.Trace($"Device {this.address} remote update clear finished");
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.WriteAddr(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.WriteSwitchCtrl,
|
|
|
|
|
(UInt32)((bitstreamNum << 1) | 0b1), timeout);
|
|
|
|
|
var ret = await WriteFlash(FlashAddr.Bitstream[bitstreamNum], bitstreamBlockNum, bytesData);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Run write switch code failed"));
|
|
|
|
|
if (!ret.Value) return new(new Exception("Write bitstream failed"));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.ReadAddrWithWait(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.WriteSwitchSign,
|
|
|
|
|
0x00_00_00_01, 0x00_00_00_01, 60 * 1000);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Write switch code failed even over time"));
|
|
|
|
|
}
|
|
|
|
|
logger.Trace($"Device {this.address} remote update write switch open finished");
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
var ret = await UDPClientPool.WriteAddr(
|
|
|
|
|
this.remoteEP, Global.RemoteUpdaterAddr.HotResetCtrl, (UInt32)(0b1), timeout);
|
|
|
|
|
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 HotRest(bitstreamNum);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
return ret.Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Updates the bitstream on the FPGA device.
|
|
|
|
|
/// [TODO:description]
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bitstreamNum">The bitstream number (0-3).</param>
|
|
|
|
|
/// <param name="bitstream">The bitstream data to update.</param>
|
|
|
|
|
/// <returns>A result indicating success or failure.</returns>
|
|
|
|
|
public async ValueTask<Result<bool>> UpdateBitstream(int bitstreamNum, byte[] bitstream)
|
|
|
|
|
/// <param name="bitstreamNum">[TODO:parameter]</param>
|
|
|
|
|
/// <returns>[TODO:return]</returns>
|
|
|
|
|
public async ValueTask<Result<bool>> HotResetBitstream(int bitstreamNum)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
var ret = await WriteBitstream(bitstreamNum, bitstream);
|
|
|
|
|
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(bitstream);
|
|
|
|
|
await MsgBus.UDPServer.ClearUDPData(this.address);
|
|
|
|
|
logger.Trace("Clear udp data finished");
|
|
|
|
|
|
|
|
|
|
var ret = await CheckBitstreamCRC(bitstreamNum, checkSum.ToUInt32());
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
// if (!ret.Value) return new(new Exception("Check bitstream crc32/mpeg-2 failed"));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
var ret = await HotReset(bitstreamNum);
|
|
|
|
|
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 HotRest(bitstreamNum);
|
|
|
|
|
if (!ret.IsSuccessful) return new(ret.Error);
|
|
|
|
|
if (!ret.Value) return new(new Exception("Hot reset failed"));
|
|
|
|
|
return ret.Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async ValueTask<Result<bool>> UploadBitstreams(
|
|
|
|
|
int enableBitstreamNum,
|
|
|
|
|
byte[]? goldenBitream,
|
|
|
|
|
byte[]? bitstream1,
|
|
|
|
|
byte[]? bitstream2,
|
|
|
|
|
byte[]? bitstream3)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|