diff --git a/flake.nix b/flake.nix
index 542c239..b099c89 100644
--- a/flake.nix
+++ b/flake.nix
@@ -22,6 +22,7 @@
sqlite
sqls
sql-studio
+ zlib
# Backend
(dotnetCorePackages.combinePackages [
dotnetCorePackages.sdk_9_0
@@ -37,7 +38,8 @@
typescript-language-server
];
shellHook = ''
- export PATH=$PATH:$HOME/.bun/bin
+ export PATH=$PATH:
+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${pkgs.zlib}/lib
'';
};
diff --git a/server.test/CommonTest.cs b/server.test/CommonTest.cs
index 782a428..1f36b90 100644
--- a/server.test/CommonTest.cs
+++ b/server.test/CommonTest.cs
@@ -1,3 +1,4 @@
+using System.Buffers.Binary;
using Common;
namespace server.test;
@@ -40,9 +41,21 @@ public class CommonTest
[Fact]
public void ToBitTest()
{
- Assert.Equal(Number.ToBit(0xFF, 3).Value, true);
- Assert.Equal(Number.ToBit(0x00, 3).Value, false);
- Assert.Equal(Number.ToBit(0xAA, 3).Value, true);
- Assert.Equal(Number.ToBit(0xAA, 2).Value, false);
+ Assert.Equal(true, Number.ToBit(0xFF, 3).Value);
+ Assert.Equal(false, Number.ToBit(0x00, 3).Value);
+ Assert.Equal(true, Number.ToBit(0xAA, 3).Value);
+ Assert.Equal(false, Number.ToBit(0xAA, 2).Value);
+ }
+
+ [Fact]
+ public void ReverseBits()
+ {
+ Assert.Equal((byte)0x05, Common.Number.ReverseBits((byte)0xA0));
+
+ var bytesSrc = new byte[] { 0xAB, 0x00, 0x00, 0x01 };
+ var bytes = new byte[4];
+ for (int i = 0; i < 4; i++)
+ bytes[i] = Common.Number.ReverseBits(bytesSrc[i]);
+ Assert.Equal(new byte[] { 0xD5, 0x00, 0x00, 0x80 }, bytes);
}
}
diff --git a/server.test/UDPServerTest.cs b/server.test/UDPServerTest.cs
index 5cde6f3..3274507 100644
--- a/server.test/UDPServerTest.cs
+++ b/server.test/UDPServerTest.cs
@@ -92,7 +92,7 @@ public class UDPServerTest
Assert.True(ret.HasValue);
var data = ret.Value;
Assert.Equal(address, data.Address);
- Assert.Equal(number, Number.BytesToNumber(data.Data));
+ Assert.Equal(number, Number.BytesToUInt32(data.Data).Value);
}
}
@@ -112,7 +112,7 @@ public class UDPServerTest
Assert.True(ret.IsSuccessful);
var data = ret.Value;
Assert.True(data.IsSuccessful);
- Assert.Equal(number, Number.BytesToNumber(data.ToBytes()));
+ Assert.Equal(number, Number.BytesToUInt32(data.ToBytes()).Value);
}
}
@@ -132,7 +132,7 @@ public class UDPServerTest
Assert.True(ret.IsSuccessful);
var data = ret.Value;
Assert.True(data.IsSuccessful);
- Assert.Equal(number, Number.BytesToNumber(data.ToBytes()));
+ Assert.Equal((UInt64)number, Number.BytesToUInt64(data.ToBytes()).Value);
}
}
}
diff --git a/server/Program.cs b/server/Program.cs
index 02fd163..0b3c10c 100644
--- a/server/Program.cs
+++ b/server/Program.cs
@@ -37,16 +37,17 @@ try
});
// Add CORS policy
- builder.Services.AddCors(options =>
+ if (builder.Environment.IsDevelopment())
{
- options.AddPolicy("Development", policy =>
+ builder.Services.AddCors(options =>
{
- policy
+ options.AddPolicy("Development", policy => policy
.AllowAnyOrigin()
.AllowAnyMethod()
- .AllowAnyHeader();
+ .AllowAnyHeader()
+ );
});
- });
+ }
// Add Swagger
builder.Services.AddControllers();
diff --git a/server/server.csproj b/server/server.csproj
index dca031e..a6dc9b5 100644
--- a/server/server.csproj
+++ b/server/server.csproj
@@ -13,12 +13,15 @@
+
+
+
diff --git a/server/src/Common.cs b/server/src/Common.cs
index 194b409..5639ef7 100644
--- a/server/src/Common.cs
+++ b/server/src/Common.cs
@@ -7,14 +7,48 @@ namespace Common
///
public class Number
{
+ private static readonly byte[] BitReverseTable = new byte[] {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+ };
///
/// 整数转成二进制字节数组
///
/// 整数
/// 整数长度
- /// 是否高位在右边
+ /// 是否高位在数组索引低
/// 二进制字节数组
- public static Result NumberToBytes(ulong num, uint length, bool isRightHigh = false)
+ public static Result NumberToBytes(ulong num, uint length, bool isLowNumHigh = false)
{
if (length > 8)
{
@@ -26,7 +60,7 @@ namespace Common
var arr = new byte[length];
- if (isRightHigh)
+ if (isLowNumHigh)
{
for (var i = 0; i < length; i++)
{
@@ -44,12 +78,12 @@ namespace Common
}
///
- /// 二进制字节数组转成整数
+ /// 二进制字节数组转成64bits整数
///
/// 二进制字节数组
- /// 是否高位在右边
+ /// 是否高位在数组索引低
/// 整数
- public static Result BytesToNumber(byte[] bytes, bool isRightHigh = false)
+ public static Result BytesToUInt64(byte[] bytes, bool isLowNumHigh = false)
{
if (bytes.Length > 8)
{
@@ -59,27 +93,78 @@ namespace Common
));
}
- ulong num = 0;
+ UInt64 num = 0;
int len = bytes.Length;
- if (isRightHigh)
+ try
{
- for (var i = 0; i < len; i++)
+ if (isLowNumHigh)
{
- num += Convert.ToUInt64((UInt64)bytes[len - 1 - i] << (i << 3));
+ for (var i = 0; i < len; i++)
+ {
+ num += Convert.ToUInt64((UInt64)bytes[len - 1 - i] << (i << 3));
+ }
}
- }
- else
- {
- for (var i = 0; i < len; i++)
+ else
{
- num += Convert.ToUInt64((UInt64)bytes[i] << ((int)(len - 1 - i) << 3));
+ for (var i = 0; i < len; i++)
+ {
+ num += Convert.ToUInt64((UInt64)bytes[i] << ((int)(len - 1 - i) << 3));
+ }
}
- }
- return num;
+ return num;
+ }
+ catch (Exception error)
+ {
+ return new(error);
+ }
}
+ ///
+ /// 二进制字节数组转成32bits整数
+ ///
+ /// 二进制字节数组
+ /// 是否高位在数组索引低
+ /// 整数
+ public static Result BytesToUInt32(byte[] bytes, bool isLowNumHigh = false)
+ {
+ if (bytes.Length > 4)
+ {
+ return new(new ArgumentException(
+ "Unsigned long number can't over 4 bytes(32 bits).",
+ nameof(bytes)
+ ));
+ }
+
+ UInt32 num = 0;
+ int len = bytes.Length;
+
+ try
+ {
+ if (isLowNumHigh)
+ {
+ for (var i = 0; i < len; i++)
+ {
+ num += Convert.ToUInt32((UInt32)bytes[len - 1 - i] << (i << 3));
+ }
+ }
+ else
+ {
+ for (var i = 0; i < len; i++)
+ {
+ num += Convert.ToUInt32((UInt32)bytes[i] << ((int)(len - 1 - i) << 3));
+ }
+ }
+
+ return num;
+ }
+ catch (Exception error)
+ {
+ return new(error);
+ }
+
+ }
///
@@ -152,6 +237,12 @@ namespace Common
return (srcBits & mask) == dstBits;
}
+ ///
+ /// 获取整型对应位置的比特
+ ///
+ /// 整型数字
+ /// 位置
+ /// 比特
public static Result ToBit(UInt32 srcBits, int location)
{
if (location < 0)
@@ -199,7 +290,7 @@ namespace Common
"Distance is larger than bytesArray", nameof(distance)));
if (srcBytesLen % distance != 0)
return new(new ArgumentException(
- "The length of bytes can't be divided by 2 without reminder", nameof(distance)));
+ "The length of bytes can't be divided by distance without reminder", nameof(distance)));
var dstBytes = new byte[srcBytesLen];
var buffer = new byte[distance];
@@ -215,7 +306,31 @@ namespace Common
return dstBytes;
}
+ ///
+ /// 反转字节内比特顺序(使用查找表的方法)
+ ///
+ /// 字节
+ /// 反转后的字节
+ public static byte ReverseBits(byte srcByte)
+ {
+ return BitReverseTable[srcByte];
+ }
+ ///
+ /// 反转字节数组的字节内比特顺序(使用查找表的方法)
+ ///
+ /// 字节数组
+ /// 反转后的字节字节数组
+ public static byte[] ReverseBits(byte[] srcBytes)
+ {
+ var bytesLen = srcBytes.Length;
+ var dstBytes = new byte[bytesLen];
+ for (int i = 0; i < bytesLen; i++)
+ {
+ dstBytes[i] = BitReverseTable[srcBytes[i]];
+ }
+ return dstBytes;
+ }
}
///
diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs
index 42405b2..968ab0a 100644
--- a/server/src/Controllers.cs
+++ b/server/src/Controllers.cs
@@ -1,6 +1,7 @@
-using System.Buffers.Binary;
using System.Net;
using Common;
+using DotNext;
+using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using WebProtocol;
@@ -142,6 +143,8 @@ public class JtagController : ControllerBase
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+ private const string BITSTREAM_PATH = "bitstream/Jtag";
+
///
/// 页面
///
@@ -243,7 +246,7 @@ public class JtagController : ControllerBase
// 生成安全的文件名(避免路径遍历攻击)
var fileName = Path.GetRandomFileName();
- var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}");
+ var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
// 如果存在文件,则删除原文件再上传
if (Directory.Exists(uploadsFolder))
@@ -275,7 +278,7 @@ public class JtagController : ControllerBase
public async ValueTask DownloadBitstream(string address, int port)
{
// 检查文件
- var fileDir = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}");
+ var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
if (!Directory.Exists(fileDir))
return TypedResults.BadRequest("Empty bitstream, Please upload it first");
@@ -305,10 +308,6 @@ public class JtagController : ControllerBase
if (!retBuffer.IsSuccessful)
return TypedResults.InternalServerError(retBuffer.Error);
revBuffer = retBuffer.Value;
- for (int i = 0; i < buffer.Length; i++)
- {
- revBuffer[i] = BinaryPrimitives.ReverseEndianness(revBuffer[i]);
- }
await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
totalBytesRead += bytesRead;
@@ -347,6 +346,325 @@ public class JtagController : ControllerBase
}
}
+///
+/// 远程更新
+///
+[ApiController]
+[Route("api/[controller]")]
+public class RemoteUpdater : ControllerBase
+{
+ private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+ private const string BITSTREAM_PATH = "bitstream/RemoteUpdate";
+
+ ///
+ /// 上传远程更新比特流文件
+ ///
+ /// 设备地址
+ /// 黄金比特流文件
+ /// 比特流文件1
+ /// 比特流文件2
+ /// 比特流文件3
+ /// 上传结果
+ [HttpPost("UploadBitstream")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ public async ValueTask UploadBitstreams(
+ string address,
+ IFormFile? goldenBitream,
+ IFormFile? bitstream1,
+ IFormFile? bitstream2,
+ IFormFile? bitstream3)
+ {
+ if ((goldenBitream is null || goldenBitream.Length == 0) &&
+ (bitstream1 is null || bitstream1.Length == 0) &&
+ (bitstream2 is null || bitstream2.Length == 0) &&
+ (bitstream3 is null || bitstream3.Length == 0))
+ return TypedResults.BadRequest("未选择文件");
+
+ // 生成安全的文件名(避免路径遍历攻击)
+ var fileName = Path.GetRandomFileName();
+ var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
+
+ // 如果存在文件,则删除原文件再上传
+ if (Directory.Exists(uploadsFolder))
+ {
+ Directory.Delete(uploadsFolder, true);
+ }
+ Directory.CreateDirectory(uploadsFolder);
+
+ for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++)
+ {
+ IFormFile file;
+ if (bitstreamNum == 0 && goldenBitream is not null)
+ file = goldenBitream;
+ else if (bitstreamNum == 1 && bitstream1 is not null)
+ file = bitstream1;
+ else if (bitstreamNum == 2 && bitstream2 is not null)
+ file = bitstream2;
+ else if (bitstreamNum == 3 && bitstream3 is not null)
+ file = bitstream3;
+ else continue;
+
+ var fileFolder = Path.Combine(uploadsFolder, bitstreamNum.ToString());
+ Directory.CreateDirectory(fileFolder);
+
+ var filePath = Path.Combine(fileFolder, fileName);
+
+ using (var stream = new FileStream(filePath, FileMode.Create))
+ {
+ await file.CopyToAsync(stream);
+ }
+ }
+
+ logger.Info($"Device {address} Upload Bitstream Successfully");
+ return TypedResults.Ok("Bitstream Upload Successfully");
+ }
+
+ private async ValueTask> ProcessBitstream(string filePath)
+ {
+ using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
+ {
+ if (fileStream is null || fileStream.Length <= 0)
+ return new(new ArgumentException("Wrong bitstream path"));
+
+ // 定义缓冲区大小: 32KB
+ byte[] buffer = new byte[32 * 1024];
+ byte[] revBuffer = new byte[32 * 1024];
+ long totalBytesRead = 0;
+
+ // 使用异步流读取文件
+ using (var memoryStream = new MemoryStream())
+ {
+ int bytesRead;
+ while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
+ {
+ // 反转 32bits
+ var retBuffer = Common.Number.ReverseBytes(buffer, 4);
+ if (!retBuffer.IsSuccessful)
+ return new(retBuffer.Error);
+ revBuffer = retBuffer.Value;
+
+ await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
+ totalBytesRead += bytesRead;
+ }
+
+ // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
+ var restStreamLen = memoryStream.Length % (4 * 1024);
+ if (restStreamLen != 0)
+ {
+ var appendLen = ((int)(4 * 1024 - restStreamLen));
+ var bytesAppend = new byte[appendLen];
+ Array.Fill(bytesAppend, 0xFF);
+ await memoryStream.WriteAsync(bytesAppend, 0, appendLen);
+ }
+
+ return new(memoryStream.ToArray());
+ }
+ }
+ }
+
+ ///
+ /// 远程更新单个比特流文件
+ ///
+ /// 设备地址
+ /// 设备端口
+ /// 比特流位号
+ [HttpPost("DownloadBitstream")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ public async ValueTask UpdateBitstream(string address, int port, int bitstreamNum)
+ {
+ // 检查文件
+ var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}/{bitstreamNum}");
+ if (!Directory.Exists(fileDir))
+ return TypedResults.BadRequest("Empty bitstream, Please upload it first");
+
+ try
+ {
+ // 读取文件
+ var filePath = Directory.GetFiles(fileDir)[0];
+
+ var fileBytes = await ProcessBitstream(filePath);
+ if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
+
+ // 下载比特流
+ var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port);
+ var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes.Value);
+
+ if (ret.IsSuccessful)
+ {
+ logger.Info($"Device {address} Update bitstream successfully");
+ return TypedResults.Ok(ret.Value);
+ }
+ else
+ {
+ logger.Error(ret.Error);
+ return TypedResults.InternalServerError(ret.Error);
+ }
+
+ }
+ catch (Exception error)
+ {
+ return TypedResults.InternalServerError(error);
+ }
+ }
+
+
+ ///
+ /// 下载多个比特流文件
+ ///
+ /// 设备地址
+ /// 设备端口
+ /// 比特流编号
+ /// 总共上传比特流的数量
+ [HttpPost("DownloadMultiBitstreams")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ public async ValueTask DownloadMultiBitstreams(string address, int port, int? bitstreamNum)
+ {
+ // 检查文件
+ var bitstreamsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
+ if (!Directory.Exists(bitstreamsFolder))
+ return TypedResults.BadRequest("Empty bitstream, Please upload it first");
+
+ try
+ {
+ var bitstreams = new List() { null, null, null, null };
+ int cnt = 0; // 上传比特流数量
+ for (int i = 0; i < 4; i++)
+ {
+ var bitstreamDir = Path.Combine(bitstreamsFolder, i.ToString());
+ if (!Directory.Exists(bitstreamDir))
+ continue;
+ cnt++;
+
+ // 读取文件
+ var filePath = Directory.GetFiles(bitstreamDir)[0];
+ var fileBytes = await ProcessBitstream(filePath);
+ if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
+ bitstreams[i] = fileBytes.Value;
+ }
+
+ // 下载比特流
+ var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port);
+ {
+ var ret = await remoteUpdater.UploadBitstreams(bitstreams[0], bitstreams[1], bitstreams[2], bitstreams[3]);
+ if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
+ if (!ret.Value) return TypedResults.InternalServerError("Upload MultiBitstreams failed");
+ }
+
+ if (bitstreamNum is not null)
+ {
+ var ret = await remoteUpdater.HotResetBitstream(bitstreamNum ?? 0);
+ if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
+ if (!ret.Value) return TypedResults.InternalServerError("Hot reset failed");
+ }
+ return TypedResults.Ok(cnt);
+ }
+ catch (Exception error)
+ {
+ return TypedResults.InternalServerError(error);
+ }
+ }
+
+
+ ///
+ /// 热复位比特流文件
+ ///
+ /// 设备地址
+ /// 设备端口
+ /// 比特流编号
+ /// 操作结果
+ [HttpPost("HotResetBitstream")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ public async ValueTask HotResetBitstream(string address, int port, int bitstreamNum)
+ {
+ var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port);
+ var ret = await remoteUpdater.HotResetBitstream(bitstreamNum);
+
+ if (ret.IsSuccessful)
+ {
+ logger.Info($"Device {address}即可 Update bitstream successfully");
+ return TypedResults.Ok(ret.Value);
+ }
+ else
+ {
+ logger.Error(ret.Error);
+ return TypedResults.InternalServerError(ret.Error);
+ }
+ }
+}
+
+
+///
+/// 数据控制器
+///
+[ApiController]
+[Route("api/[controller]")]
+public class Data : ControllerBase
+{
+ private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+ ///
+ /// 创建数据库表
+ ///
+ /// 插入的记录数
+ [EnableCors("Development")]
+ [HttpPost("CreateTable")]
+ public IResult CreateTables()
+ {
+ using var db = new Database.AppDataConnection();
+ db.CreateAllTables();
+ return TypedResults.Ok();
+ }
+
+ ///
+ /// 删除数据库表
+ ///
+ /// 插入的记录数
+ [EnableCors("Development")]
+ [HttpDelete("DropTables")]
+ public IResult DropTables()
+ {
+ using var db = new Database.AppDataConnection();
+ db.DropAllTables();
+ return TypedResults.Ok();
+ }
+
+ ///
+ /// 获取所有用户
+ ///
+ /// 用户列表
+ [HttpGet("AllUsers")]
+ public IResult AllUsers()
+ {
+ using var db = new Database.AppDataConnection();
+ var ret = db.User.ToList();
+ return TypedResults.Ok(ret);
+ }
+
+ ///
+ /// 注册新用户
+ ///
+ /// 用户名
+ /// 操作结果
+ [HttpPost("SignUpUser")]
+ public IResult SignUpUser(string name)
+ {
+ if (name.Length > 255)
+ return TypedResults.BadRequest("Name Couln't over 255 characters");
+
+ using var db = new Database.AppDataConnection();
+ var ret = db.AddUser(name);
+ return TypedResults.Ok(ret);
+ }
+}
+
///
/// 日志控制器
///
diff --git a/server/src/Database.cs b/server/src/Database.cs
new file mode 100644
index 0000000..e54d831
--- /dev/null
+++ b/server/src/Database.cs
@@ -0,0 +1,113 @@
+using LinqToDB;
+using LinqToDB.Data;
+using LinqToDB.Mapping;
+
+namespace Database;
+
+///
+/// 用户类,表示用户信息
+///
+public class User
+{
+ ///
+ /// 用户的唯一标识符
+ ///
+ [PrimaryKey]
+ public Guid ID { get; set; } = Guid.NewGuid();
+
+ ///
+ /// 用户的名称
+ ///
+ [NotNull]
+ public required string Name { get; set; }
+}
+
+///
+/// FPGA 板子类,表示板子信息
+///
+public class Board
+{
+ ///
+ /// FPGA 板子的唯一标识符
+ ///
+ [PrimaryKey]
+ public Guid Id { get; set; } = Guid.NewGuid();
+
+ ///
+ /// FPGA 板子的名称
+ ///
+ [NotNull]
+ public required string BoardName { get; set; }
+}
+
+///
+/// 应用程序数据连接类,用于与数据库交互
+///
+public class AppDataConnection : DataConnection
+{
+ static readonly LinqToDB.DataOptions options =
+ new LinqToDB.DataOptions()
+ .UseSQLite($"Data Source={Environment.CurrentDirectory}/Database.sqlite");
+
+ ///
+ /// 初始化应用程序数据连接
+ ///
+ public AppDataConnection() : base(options) { }
+
+
+ ///
+ /// 创建所有数据库表
+ ///
+ public void CreateAllTables()
+ {
+ this.CreateTable();
+ this.CreateTable();
+ }
+
+ ///
+ /// 删除所有数据库表
+ ///
+ public void DropAllTables()
+ {
+ this.DropTable();
+ this.DropTable();
+ }
+
+ ///
+ /// 添加一个新的用户到数据库
+ ///
+ /// 用户的名称
+ /// 插入的记录数
+ public int AddUser(string name)
+ {
+ var user = new User()
+ {
+ Name = name
+ };
+ return this.Insert(user);
+ }
+
+ ///
+ /// 添加一块新的 FPGA 板子到数据库
+ ///
+ /// FPGA 板子的名称
+ /// 插入的记录数
+ public int AddBoard(string name)
+ {
+ var board = new Board()
+ {
+ BoardName = name
+ };
+ return this.Insert(board);
+ }
+
+ ///
+ /// 用户表
+ ///
+ public ITable User => this.GetTable();
+
+ ///
+ /// FPGA 板子表
+ ///
+ public ITable Board => this.GetTable();
+}
diff --git a/server/src/JtagClient.cs b/server/src/JtagClient.cs
index cca5b27..b02ed6e 100644
--- a/server/src/JtagClient.cs
+++ b/server/src/JtagClient.cs
@@ -428,7 +428,7 @@ public class Jtag
if (retPackLen != 4)
return new(new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes"));
- return Convert.ToUInt32(Common.Number.BytesToNumber(retPackOpts.Data).Value);
+ return Convert.ToUInt32(Common.Number.BytesToUInt64(retPackOpts.Data).Value);
}
///
@@ -558,7 +558,7 @@ public class Jtag
return new(new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes"));
if (Common.Number.BitsCheck(
- Common.Number.BytesToNumber(retPack.Value.Options.Data).Value, result, resultMask))
+ Common.Number.BytesToUInt64(retPack.Value.Options.Data).Value, result, resultMask))
ret = true;
return ret;
@@ -578,7 +578,7 @@ public class Jtag
return new(new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes"));
if (Common.Number.BitsCheck(
- Common.Number.BytesToNumber(retPack.Value.Options.Data).Value, result, resultMask))
+ Common.Number.BytesToUInt64(retPack.Value.Options.Data).Value, result, resultMask))
ret = true;
return ret;
diff --git a/server/src/RemoteUpdate.cs b/server/src/RemoteUpdate.cs
new file mode 100644
index 0000000..b6db774
--- /dev/null
+++ b/server/src/RemoteUpdate.cs
@@ -0,0 +1,535 @@
+using System.Net;
+using DotNext;
+namespace RemoteUpdate;
+
+static class RemoteUpdateClientAddr
+{
+ public const UInt32 Base = 0x20_00_00_00;
+
+ ///
+ /// ADDR: 0X00: 写Flash-读写地址——控制位
+ /// [31:16]: wr_sector_num
+ /// [15: 0]: {flash_wr_en,-,-,-, start_wr_sector}
+ ///
+ public const UInt32 WriteCtrl = Base + 0x00;
+
+ ///
+ /// ADDR: 0X01: 写Flash-只写地址——FIFO入口
+ /// [31:0]: 写比特流数据入口
+ ///
+ public const UInt32 WriteFIFO = Base + 0x01;
+
+ ///
+ /// ADDR: 0X02: 写Flash-只读地址——标志位
+ /// [31:24]: {-, -, -, -, -, -, -, wr_fifo_full}
+ /// [23:16]: {-, -, -, -, -, -, -, wr_fifo_empty}
+ /// [15: 8]: {-, -, -, -, -, -, -, flash_wr_done}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, flash_clear_done}
+ ///
+ public const UInt32 WriteSign = Base + 0x02;
+
+ ///
+ /// ADDR: 0X03: 读Flash-读写地址——控制位1
+ /// [31:16]: rd_sector_num
+ /// [15: 0]: {flash_rd_en,-,-,-, start_rd_sub_sector}
+ ///
+ public const UInt32 ReadCtrl1 = Base + 0x03;
+
+ ///
+ /// ADDR: 0X04: 读Flash-读写地址——控制位2
+ /// [31:24]: { }
+ /// [23:16]: {-, -, -, -, -, -,{ bs_crc32_ok }}
+ /// [15: 8]: {-, -, -, -, -, -, -, crc_check_en}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, bitstream_up2cpu_en}
+ ///
+ public const UInt32 ReadCtrl2 = Base + 0x04;
+
+ ///
+ /// ADDR: 0X05: 读Flash-只读地址——FIFO出口
+ /// [31:0]: 读比特流数据出口
+ ///
+ public const UInt32 ReadFIFO = Base + 0x05;
+
+ ///
+ /// ADDR: 0X06: 读Flash-只读地址——CRC校验值
+ /// [31:0]: CRC校验值 bs_readback_crc
+ ///
+ public const UInt32 ReadCRC = Base + 0x06;
+
+ ///
+ /// ADDR: 0X07: 读Flash-只读地址——标志位
+ /// [31:24]: {-, -, -, -, -, -, -, rd_fifo_afull}
+ /// [23:16]: {-, -, -, -, -, -, -, rd_fifo_empty}
+ /// [15: 8]: {-, -, -, -, -, -, -, flash_rd_done}
+ /// [ 7: 0]: {-, -, -, -, -, -, -, bs_readback_crc_valid}
+ ///
+ public const UInt32 ReadSign = Base + 0x07;
+
+ ///
+ /// ADDR: 0X08: 热启动开关-读写地址——控制位
+ /// [31: 8]: hotreset_addr
+ /// [ 7: 0]: {-, -, -, -, -, -, -, hotreset_en}
+ ///
+ public const UInt32 HotResetCtrl = Base + 0x08;
+
+ ///
+ /// ADDR: 0X09: 只读地址 版本号
+ /// [31: 0]: FPGA_VERSION[31:0]
+ ///
+ 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 };
+}
+
+
+
+///
+/// [TODO:description]
+///
+public class RemoteUpdateClient
+{
+ 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;
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ 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.ep = new IPEndPoint(IPAddress.Parse(address), port);
+ this.timeout = timeout;
+ this.timeoutForWait = timeoutForWait;
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ private async ValueTask> 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, 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.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(
+ $"Flash clear failed after {this.timeoutForWait} milliseconds"));
+ }
+
+ {
+ 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 data to flash failed"));
+ }
+
+ {
+ var ret = await UDPClientPool.ReadAddrWithWait(
+ this.ep, RemoteUpdateClientAddr.WriteSign,
+ 0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
+ if (!ret.IsSuccessful) return new(ret.Error);
+ return ret.Value;
+ }
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:return]
+ private async ValueTask> 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(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;
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:return]
+ private async ValueTask> 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(bytesData, 0xFF);
+
+ var ret = await WriteFlash(FlashAddr.Switch[bitstreamNum], 1, bytesData);
+ if (!ret.IsSuccessful) return new(ret.Error);
+ return ret.Value;
+ }
+
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:return]
+ private async ValueTask> 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;
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:return]
+ private async ValueTask> 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;
+ }
+
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ private async ValueTask> 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($"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;
+ }
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:return]
+ private async ValueTask> 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, RemoteUpdateClientAddr.HotResetCtrl,
+ ((FlashAddr.Bitstream[bitstreamNum] << 8) | 1), this.timeout);
+ if (!ret.IsSuccessful) return new(ret.Error);
+ return ret.Value;
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public async ValueTask> HotResetBitstream(int bitstreamNum)
+ {
+ await MsgBus.UDPServer.ClearUDPData(this.address);
+ 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;
+ }
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public async ValueTask> UploadBitstreams(
+ byte[]? goldenBitream,
+ byte[]? bitstream1,
+ byte[]? bitstream2,
+ byte[]? bitstream3)
+ {
+ await MsgBus.UDPServer.ClearUDPData(this.address);
+ 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;
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public async ValueTask> 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 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;
+ }
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public async ValueTask> 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;
+ }
+ }
+
+}
diff --git a/server/src/UdpClientPool.cs b/server/src/UdpClientPool.cs
index c86aab2..2c59a56 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,228 @@ 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;
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public static async ValueTask> ReadAddr(
+ IPEndPoint endPoint, uint devAddr, UInt32 result, UInt32 resultMask, int timeout = 1000)
+ {
+ var address = endPoint.Address.ToString();
+
+ var ret = await ReadAddr(endPoint, devAddr, timeout);
+ if (!ret.IsSuccessful) return new(ret.Error);
+ if (!ret.Value.IsSuccessful)
+ return new(new Exception($"Read device {address} address {devAddr} failed"));
+
+ var retData = ret.Value.Options.Data;
+ if (retData is null)
+ return new(new Exception($"Device {address} receive none"));
+ if (retData.Length != 4)
+ return new(new Exception(
+ $"Device {address} receive data is {retData.Length} bytes instead of 4 bytes"));
+
+ // Check result
+ try
+ {
+ var retCode = Convert.ToUInt32(Common.Number.BytesToUInt64(retData).Value);
+ return Common.Number.BitsCheck(retCode, result, resultMask);
+ }
+ catch (Exception error)
+ {
+ return new(error);
+ }
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public static async ValueTask> ReadAddrWithWait(
+ IPEndPoint endPoint, uint devAddr, UInt32 result, UInt32 resultMask, int timeout = 1000)
+ {
+ var address = endPoint.Address.ToString();
+
+ var startTime = DateTime.Now;
+ while (true)
+ {
+ var elapsed = DateTime.Now - startTime;
+ if (elapsed >= TimeSpan.FromMilliseconds(timeout)) break;
+ var timeleft = TimeSpan.FromMilliseconds(timeout) - elapsed;
+
+ try
+ {
+ var ret = await ReadAddr(endPoint, devAddr, Convert.ToInt32(timeleft.TotalMilliseconds));
+ if (!ret.IsSuccessful) return new(ret.Error);
+ if (!ret.Value.IsSuccessful)
+ return new(new Exception($"Read device {address} address {devAddr} failed"));
+
+ var retData = ret.Value.Options.Data;
+ if (retData is null)
+ return new(new Exception($"Device {address} receive none"));
+ if (retData.Length != 4)
+ return new(new Exception(
+ $"Device {address} receive data is {retData.Length} bytes instead of 4 bytes"));
+
+ // Check result
+ var retCode = Convert.ToUInt32(Common.Number.BytesToUInt64(retData).Value);
+ if (Common.Number.BitsCheck(retCode, result, resultMask)) return true;
+ }
+ catch (Exception error)
+ {
+ return new(error);
+ }
+ }
+
+ return false;
+ }
+
+
+ ///
+ /// [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;
+ }
+
+ ///
+ /// [TODO:description]
+ ///
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:parameter]
+ /// [TODO:return]
+ public static async ValueTask> WriteAddr(IPEndPoint endPoint, UInt32 devAddr, byte[] dataArray, int timeout = 1000)
+ {
+ var ret = false;
+ var opts = new SendAddrPackOptions();
+
+
+ opts.BurstType = BurstType.FixedBurst;
+ opts.CommandID = 0;
+ opts.Address = devAddr;
+
+ // Check Msg Bus
+ if (!MsgBus.IsRunning)
+ return new(new Exception("Message bus not working!"));
+
+ opts.IsWrite = true;
+ var hasRest = dataArray.Length % (256 * (32 / 8)) != 0;
+ var writeTimes = hasRest ?
+ dataArray.Length / (256 * (32 / 8)) + 1 :
+ dataArray.Length / (256 * (32 / 8));
+ for (var i = 0; i < writeTimes; i++)
+ {
+ // Sperate Data Array
+ var isLastData = i == writeTimes - 1;
+ var sendDataArray =
+ isLastData ?
+ dataArray[(i * (256 * (32 / 8)))..] :
+ dataArray[(i * (256 * (32 / 8)))..((i + 1) * (256 * (32 / 8)))];
+
+ // Write Jtag State Register
+ opts.BurstLength = ((byte)(sendDataArray.Length / 4 - 1));
+ 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(sendDataArray));
+ if (!ret) return new(new Exception("Send data package failed!"));
+
+ // Wait for Write Ack
+ var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(endPoint.Address.ToString(), endPoint.Port, timeout);
+ if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
+
+ if (!udpWriteAck.Value.IsSuccessful)
+ return false;
+ }
+
+ return true;
+ }
+
}
diff --git a/server/src/WebProtocol.cs b/server/src/WebProtocol.cs
index 707a7e1..67f542a 100644
--- a/server/src/WebProtocol.cs
+++ b/server/src/WebProtocol.cs
@@ -248,7 +248,7 @@ namespace WebProtocol
));
}
- var address = Common.Number.BytesToNumber(bytes[4..]).Value;
+ var address = Common.Number.BytesToUInt64(bytes[4..]).Value;
return new SendAddrPackage(bytes[1], bytes[2], Convert.ToUInt32(address));
}
}