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; + } }