diff --git a/server/src/RemoteUpdate.cs b/server/src/RemoteUpdate.cs
new file mode 100644
index 0000000..a302c68
--- /dev/null
+++ b/server/src/RemoteUpdate.cs
@@ -0,0 +1,166 @@
+using System.Net;
+using DotNext;
+namespace RemoteUpdate;
+
+static class Global
+{
+ ///
+ /// FPGA 远程更新器地址
+ ///
+ public class RemoteUpdaterAddr
+ {
+ ///
+ /// 基地址
+ ///
+ public const UInt32 BaseAddr = 0x3000_0000;
+ ///
+ /// 写比特流-读写地址——控制位
+ /// [31:24]: 保留
+ /// [23:17]: 保留
+ /// [16: 8]: 保留
+ /// [ 7: 0]: {-, -, -, -, -,{bitstream_wr_num}, flash_wr_en}
+ ///
+ public const UInt32 WriteCtrl = 0x3000_0000;
+ ///
+ /// 写比特流-只写地址——FIFO入口
+ /// [31:0]: 写比特流数据入口
+ ///
+ public const UInt32 WriteFIFO = 0x3000_0001;
+ ///
+ /// 写比特流-只读地址——标志位
+ /// [31:24]: {-, -, -, -, -, -, -, wr_fifo_full}
+ /// [23:17]: {-, -, -, -, -, -, -, wr_fifo_empty}
+ /// [16: 8]: {-, -, -, -, -, -, -, bitstream_wr_done}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, clear_bs_done}
+ ///
+ public const UInt32 WriteSign = 0x3000_0002;
+ ///
+ /// 读比特流-读写地址——控制位
+ /// [31:24]: {-, -, -, -, -, -,{ bs_crc32_ok }}
+ /// [23:17]: {-, -, -, -, -, -, -, crc_check_en}
+ /// [16: 8]: {-, -, -, -, -, -, -, bitstream_up2cpu_en}
+ /// [ 7: 0]: {-, -, -, -, -,{bitstream_rd_num},flash_rd_en}
+ ///
+ public const UInt32 ReadCtrl = 0x3000_0003;
+ ///
+ /// 读比特流-只读地址——FIFO出口
+ /// [31:0]: 读比特流数据出口
+ ///
+ public const UInt32 ReadFIFO = 0x3000_0004;
+ ///
+ /// 读比特流-只读地址——CRC校验值
+ /// [31:0]: CRC校验值 bs_readback_crc
+ ///
+ public const UInt32 ReadCRC = 0x3000_0005;
+ ///
+ /// 读比特流-只读地址——标志位
+ /// [31:24]: {-, -, -, -, -, -, -, rd_fifo_afull}
+ /// [23:17]: {-, -, -, -, -, -, -, rd_fifo_empty}
+ /// [16: 8]: {-, -, -, -, -, -, -, bitstream_rd_done}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, bs_readback_crc_valid}
+ ///
+ public const UInt32 ReadSign = 0x3000_0006;
+ ///
+ /// 单独擦除开关-读写地址——控制位
+ /// [31:24]: {-, -, -, -, -, -, -, - }
+ /// [23:17]: {-, -, -, -, -, -, -, - }
+ /// [16: 8]: {-, -, -, -, -, -, -, - }
+ /// [ 7: 0]: {-, -, -, -, -, -, -, clear_sw_en}
+ ///
+ public const UInt32 ClearCtrl = 0x3000_0007;
+ ///
+ /// 单独擦除开关-只读地址——标志位
+ /// [31:24]: {-, -, -, -, -, -, -, time_out_reg}
+ /// [23:17]: { flash_flag_status[15:8]}
+ /// [16: 8]: { flash_flag_status[7:0]}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, clear_sw_done}
+ ///
+ public const UInt32 ClearSign = 0x3000_0008;
+ ///
+ /// 写开关-读写地址——标志位
+ /// [31:24]: {-, -, -, -, -, -, -, - }
+ /// [23:17]: {-, -, -, -, -, -, -, - }
+ /// [16: 8]: {-, -, -, -, -, -, { open_sw_num}}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, write_sw_code_en}
+ ///
+ public const UInt32 WriteSwitchSign = 0x3000_0009;
+ ///
+ /// 写开关-只读地址——控制位
+ /// [31:24]: {-, -, -, -, -, -, -, - }
+ /// [23:17]: {-, -, -, -, -, -, -, - }
+ /// [16: 8]: {-, -, -, -, -, -, -, ipal_busy}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, open_sw_code_done}
+ ///
+ public const UInt32 WriteSwitchCtrl = 0x3000_0010;
+ ///
+ /// 热启动开关-读写地址——控制位
+ /// [31:24]: {-, -, -, -, -, -, -, - }
+ /// [23:17]: {-, -, -, -, -, -, -, - }
+ /// [16: 8]: {-, -, -, -, -, -, }
+ /// [ 7: 0]: {-, -, -, -, -, -, -, hotreset_en}
+ ///
+ public const UInt32 HotResetCtrl = 0x3000_0011;
+ }
+
+}
+
+///
+/// FPGA远程更新
+///
+public class RemoteUpdater
+{
+ private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+ const int timeout = 1000;
+
+ int port { get; }
+ string address { get; }
+ private IPEndPoint remoteEP;
+
+ ///
+ /// 初始化 FPGA 远程更新器
+ ///
+ /// 目标设备的 IP 地址
+ /// 目标设备的端口号
+ public RemoteUpdater(string address, int port)
+ {
+ this.address = address;
+ this.port = port;
+ this.remoteEP = new IPEndPoint(IPAddress.Parse(address), port);
+ }
+
+ public async ValueTask> UpdateBitstream(int bitstreamNum, byte[] bitstream)
+ {
+ if (bitstreamNum < 0 || bitstreamNum > 0b11)
+ return new(new ArgumentException(
+ "The number of bitstream can't be negative or over 3(0b11)", nameof(bitstreamNum)));
+
+ if (bitstream.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");
+
+
+ {
+ var ret = await UDPClientPool.WriteAddr(
+ this.remoteEP, Global.RemoteUpdaterAddr.WriteCtrl, (UInt32)((bitstreamNum << 1) | 0b1), timeout);
+ if (!ret.IsSuccessful) return new(ret.Error);
+ else if (!ret.Value) return new(new Exception("Enable write flash failed"));
+ }
+
+ {
+ var ret = await UDPClientPool.ReadAddr(
+ this.remoteEP, Global.RemoteUpdaterAddr.ClearSign, timeout);
+ if (!ret.IsSuccessful) return new(ret.Error);
+ else if (!ret.Value.IsSuccessful) return new(new Exception("Read clear switch sign failed"));
+ }
+
+
+ return true;
+ }
+
+}
diff --git a/server/src/UdpClientPool.cs b/server/src/UdpClientPool.cs
index c86aab2..25c3925 100644
--- a/server/src/UdpClientPool.cs
+++ b/server/src/UdpClientPool.cs
@@ -1,6 +1,8 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
+using DotNext;
+using WebProtocol;
///
/// UDP客户端发送池
@@ -161,4 +163,90 @@ public class UDPClientPool
return isSuccessful;
}
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public static async ValueTask> ReadAddr(
+ IPEndPoint endPoint, uint devAddr, int timeout = 1000)
+ {
+ 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(endPoint, 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(
+ endPoint.Address.ToString(), endPoint.Port, timeout);
+ if (!retPack.IsSuccessful) return new(retPack.Error);
+ else if (!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()}"));
+
+ return retPack;
+ }
+
+ public static async ValueTask> ReadAddrWithCheck(
+ IPEndPoint endPoint, uint devAddr, int timeout = 1000)
+ {
+ }
+
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public static async ValueTask> WriteAddr(
+ IPEndPoint endPoint, UInt32 devAddr, UInt32 data, int timeout = 1000)
+ {
+ var ret = false;
+ var opts = new SendAddrPackOptions();
+
+ opts.BurstType = BurstType.FixedBurst;
+ opts.BurstLength = 0;
+ opts.CommandID = 0;
+ opts.Address = devAddr;
+
+ // Write Jtag State Register
+ opts.IsWrite = true;
+ ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
+ if (!ret) return new(new Exception("Send 1st address package failed!"));
+ // Send Data Package
+ ret = await UDPClientPool.SendDataPackAsync(endPoint,
+ new SendDataPackage(Common.Number.NumberToBytes(data, 4).Value));
+ if (!ret) return new(new Exception("Send data package failed!"));
+
+ // Check Msg Bus
+ if (!MsgBus.IsRunning)
+ return new(new Exception("Message bus not working!"));
+
+ // Wait for Write Ack
+ var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(
+ endPoint.Address.ToString(), endPoint.Port, timeout);
+ if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
+
+ return udpWriteAck.Value.IsSuccessful;
+ }
}