From 4e752d4c9ef48408cd65eae1455750dec9e26acd Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Wed, 23 Apr 2025 20:22:49 +0800 Subject: [PATCH 1/4] maybe fix bug: download bitstream failed --- .justfile | 17 +++- server/src/Controllers.cs | 164 +++++++++++++++++++++++++++++++------- server/src/JtagClient.cs | 88 +++++++++++++++----- server/src/UdpServer.cs | 8 +- server/src/WebProtocol.cs | 6 +- 5 files changed, 229 insertions(+), 54 deletions(-) diff --git a/.justfile b/.justfile index 551a2f3..7651ade 100644 --- a/.justfile +++ b/.justfile @@ -3,20 +3,35 @@ pwd echo +# 清空构建文件 clean: rm -rf "server/bin" rm -rf "server/obj" rm -rf "server.test/bin" rm -rf "server.test/obj" + rm -rf "dist" +# 生成Restful API到网页客户端 +gen-api: + cd server && dotnet run & + npx nswag openapi2tsclient /input:http://localhost:5000/swagger/v1/swagger.json /output:src/APIClient.ts + pkill server + +# 构建服务器,包含win与linux平台 [working-directory: "server"] -publish: _show-dir +build-server: _show-dir dotnet publish --self-contained false -t:PublishAllRids +# 运行服务器 [working-directory: "server"] run-server: _show-dir dotnet run + +# 运行网页客户端 +run-web: + npm run dev +# 运行测试用例测试服务器 [working-directory: "server.test"] test-server: _show-dir dotnet test --logger "console;verbosity=detailed" diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs index e76db1a..23a0db9 100644 --- a/server/src/Controllers.cs +++ b/server/src/Controllers.cs @@ -251,46 +251,150 @@ public class JtagController : ControllerBase { // 读取文件 var filePath = Directory.GetFiles(fileDir)[0]; - var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open); - if (fileStream is null || fileStream.Length <= 0) - return TypedResults.BadRequest("Wrong bitstream, Please upload it again"); - // 定义缓冲区大小: 32KB - byte[] buffer = new byte[32 * 1024]; - long totalBytesRead = 0; - - // 使用异步流读取文件 - using (var memoryStream = new MemoryStream()) + using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open)) { - int bytesRead; - while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0) + if (fileStream is null || fileStream.Length <= 0) + return TypedResults.BadRequest("Wrong bitstream, Please upload it again"); + + // 定义缓冲区大小: 32KB + byte[] buffer = new byte[32 * 1024]; + long totalBytesRead = 0; + + // 使用异步流读取文件 + using (var memoryStream = new MemoryStream()) { - await memoryStream.WriteAsync(buffer, 0, bytesRead); - totalBytesRead += bytesRead; + int bytesRead; + while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + await memoryStream.WriteAsync(buffer, 0, bytesRead); + totalBytesRead += bytesRead; + } + + // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存) + var fileBytes = memoryStream.ToArray(); + + // 下载比特流 + var jtagCtrl = new JtagClient.Jtag(address, port); + var ret = await jtagCtrl.DownloadBitstream(fileBytes); + + if (ret.IsSuccessful) + { + logger.Info($"Device {address} dowload bitstream successfully"); + return TypedResults.Ok(ret.Value); + } + else + { + logger.Error(ret.Error); + return TypedResults.InternalServerError(ret.Error); + } } - // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存) - var fileBytes = memoryStream.ToArray(); - - // 下载比特流 - var jtagCtrl = new JtagClient.Jtag(address, port); - var ret = await jtagCtrl.DownloadBitstream(fileBytes); - - if (ret.IsSuccessful) - { - logger.Info($"Device {address} dowload bitstream successfully"); - return TypedResults.Ok(ret.Value); - } - else - { - logger.Error(ret.Error); - return TypedResults.InternalServerError(ret.Error); - } } + } catch (Exception error) { return TypedResults.InternalServerError(error); } + finally + { + + } } } + +[ApiController] +[Route("api/[controller]")] +public class Log : ControllerBase +{ + private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + + private readonly string _logFilePath = Directory.GetFiles(Directory.GetCurrentDirectory())[0]; + + [HttpGet] + public async Task Index() + { + if (!System.IO.File.Exists(_logFilePath)) + { + Problem("日志文件不存在"); + } + + // 请求头 + Response.Headers.Add("Content-Type", "text/event-stream"); + Response.Headers.Add("Cache-Control", "no-cache"); + Response.Headers.Add("Connection", "keep-alive"); + + long position = 0; + while (!HttpContext.RequestAborted.IsCancellationRequested) + { + try + { + if (System.IO.File.Exists(_logFilePath)) + { + using (var stream = new FileStream(_logFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + stream.Position = position; + using (var reader = new StreamReader(stream)) + { + string? line; + while ((line = await reader.ReadLineAsync()) != null) + { + // await WriteEvent(line); + await Response.WriteAsync($"data: {line}\n\n"); + await Response.Body.FlushAsync(); + position = stream.Position; + } + if (line is null) break; + } + } + } + else + { + await WriteEvent("日志文件被删除"); + break; + } + } + catch (Exception ex) + { + await WriteEvent($"错误: {ex.Message}"); + } + + await Task.Delay(1000, HttpContext.RequestAborted); // 每秒检查一次 + } + + // 结束 + return new EmptyResult(); + } + + private async Task WriteEvent(string message) + { + await Response.WriteAsync($"data: {message}\n\n"); + await Response.Body.FlushAsync(); + } + + [HttpGet("example")] + public async Task SseExample() + { + // 请求头 + Response.Headers.Add("Content-Type", "text/event-stream"); + Response.Headers.Add("Cache-Control", "no-cache"); + Response.Headers.Add("Connection", "keep-alive"); + + // 假设不断得到新数据 + for (int i = 0; i < 10000; i++) + { + // 每个数据参照 SSE 返回数据格式进行组装 + var content = "event: message\n" + + "data: {\"color\":\"66ccff\"}\n\n"; + + // 立刻写入响应 + await Response.WriteAsync(content); + await Response.Body.FlushAsync(); + } + + // 结束 + return new EmptyResult(); + } +} + diff --git a/server/src/JtagClient.cs b/server/src/JtagClient.cs index 6c5791e..07a3468 100644 --- a/server/src/JtagClient.cs +++ b/server/src/JtagClient.cs @@ -220,6 +220,8 @@ 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; @@ -274,7 +276,7 @@ public class Jtag return Convert.ToUInt32(Common.Number.BytesToNumber(retPackOpts.Data).Value); } - public async ValueTask> WriteFIFO(UInt32 devAddr, UInt32 data) + public async ValueTask> WriteFIFO(UInt32 devAddr, UInt32 data, UInt32 delayMilliseconds = 0) { var ret = false; var opts = new SendAddrPackOptions(); @@ -303,6 +305,9 @@ public class Jtag else if (!udpWriteAck.Value.IsSuccessful) return new(new Exception("Send address package failed")); + // Delay some time before read register + await Task.Delay(TimeSpan.FromMilliseconds(delayMilliseconds)); + // Read Jtag State Register opts.IsWrite = false; opts.Address = JtagAddr.STATE; @@ -317,7 +322,7 @@ public class Jtag return udpDataResp.Value; } - async ValueTask> WriteFIFO(UInt32 devAddr, byte[] dataArray) + async ValueTask> WriteFIFO(UInt32 devAddr, byte[] dataArray, UInt32 delayMilliseconds = 0) { var ret = false; var opts = new SendAddrPackOptions(); @@ -334,20 +339,21 @@ public class Jtag var writeTimes = dataArray.Length / (256 * (32 / 8)) + 1; 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.IsWrite = true; - opts.BurstLength = isLastData ? (byte)(dataArray.Length - i * (256 * (32 / 8)) - 1) : (byte)255; + opts.BurstLength = ((byte)(sendDataArray.Length / 4 - 1)); ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts)); if (!ret) return new(new Exception("Send 1st address package failed!")); + // Send Data Package - ret = await UDPClientPool.SendDataPackAsync(ep, - new SendDataPackage( - isLastData ? - dataArray[(i * (256 * (32 / 8)))..] : - dataArray[(i * (256 * (32 / 8)))..((i + 1) * (256 * (32 / 8)) - 1)] - ) - ); + ret = await UDPClientPool.SendDataPackAsync(ep, new SendDataPackage(sendDataArray)); if (!ret) return new(new Exception("Send data package failed!")); // Wait for Write Ack @@ -357,6 +363,9 @@ public class Jtag return new(new Exception("Send address package failed")); } + // Delay some time before read register + await Task.Delay(TimeSpan.FromMilliseconds(delayMilliseconds)); + // Read Jtag State Register opts.IsWrite = false; opts.BurstLength = 0; @@ -373,10 +382,10 @@ public class Jtag } async ValueTask> WriteFIFO - (UInt32 devAddr, UInt32 data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF) + (UInt32 devAddr, UInt32 data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 delayMilliseconds = 0) { var ret = false; - var retPack = await WriteFIFO(devAddr, data); + var retPack = await WriteFIFO(devAddr, data, delayMilliseconds); if (!retPack.IsSuccessful) return new(retPack.Error); if (retPack.Value.Options.Data is null) @@ -394,10 +403,10 @@ public class Jtag } async ValueTask> WriteFIFO - (UInt32 devAddr, byte[] data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF) + (UInt32 devAddr, byte[] data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 delayMilliseconds = 0) { var ret = false; - var retPack = await WriteFIFO(devAddr, data); + var retPack = await WriteFIFO(devAddr, data, delayMilliseconds); if (retPack.Value.Options.Data is null) return new(new Exception($"Data is Null, package: {retPack.Value.Options.ToString()}")); @@ -413,6 +422,45 @@ public class Jtag return ret; } + async ValueTask> WaitForWriteFIFO + (UInt32 devAddr, byte[] data, UInt32 result, + UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 timeout = 10_000, UInt32 cycle = 500) + { + { + var wrRet = await WriteFIFO(devAddr, data, result, resultMask); + + if (!wrRet.IsSuccessful) return new(wrRet.Error); + if (wrRet.Value) return true; + } + + // Wait some time + var ret = false; + var startTime = DateTime.Now; + var isTimeout = false; + var timeleft = TimeSpan.FromMilliseconds(timeout); + while (!isTimeout) + { + // Check whether timeout + var elapsed = DateTime.Now - startTime; + isTimeout = elapsed >= TimeSpan.FromMilliseconds(timeout); + if (isTimeout) break; + timeleft = TimeSpan.FromMilliseconds(timeout) - elapsed; + + // Check FIFO + var retPack = await ReadFIFO(JtagAddr.STATE); + if (Common.Number.BitsCheck(retPack.Value, result, resultMask)) + { + ret = true; + break; + } + + // Wait + await Task.Delay(TimeSpan.FromMilliseconds(cycle)); + } + + return ret; + } + async ValueTask> ClearAllRegisters() { @@ -447,7 +495,7 @@ public class Jtag 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); + 0x01_00_00_00, JtagState.CMD_EXEC_FINISH, (milliseconds / CLOCK_FREQ) + 100); } async ValueTask> ExecRDCmd(uint command) @@ -472,7 +520,7 @@ public class Jtag return await ClearWriteDataReg(); } - async ValueTask> LoadDRCareInput(byte[] bytesArray) + async ValueTask> 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)")); @@ -481,13 +529,17 @@ public class Jtag var ret = await WriteFIFO( JtagAddr.WRITE_CMD, Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREI, JtagCmd.LEN_CMD_JTAG, bytesLen, 28).Value, - 0x01_00_00_00, JtagState.CMD_EXEC_FINISH); + 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); + var ret = await WaitForWriteFIFO( + JtagAddr.WRITE_DATA, + bytesArray, 0x01_00_00_00, + JtagState.CMD_EXEC_FINISH, + timeout, cycle); if (!ret.IsSuccessful) return new(ret.Error); else if (!ret.Value) return new(new Exception("Write Data Failed")); diff --git a/server/src/UdpServer.cs b/server/src/UdpServer.cs index b17937b..87278f2 100644 --- a/server/src/UdpServer.cs +++ b/server/src/UdpServer.cs @@ -116,6 +116,7 @@ public class UDPServer /// /// 目标IP地址 /// 超时时间 + /// 延迟时间 /// 调用函数名称 /// 调用函数位置 /// @@ -124,7 +125,7 @@ public class UDPServer /// Optional 存在时,为最先收到的数据 /// public async ValueTask> FindDataAsync( - string ipAddr, int timeout = 1000, + string ipAddr, int timeout = 1000, int cycle = 0, [CallerMemberName] string callerName = "", [CallerLineNumber] int callerLineNum = 0) { @@ -153,6 +154,8 @@ public class UDPServer break; } } + + await Task.Delay(cycle); } if (data is null) @@ -171,8 +174,9 @@ public class UDPServer /// /// IP地址 /// 超时时间 + /// 延迟时间 /// 数据列表 - public async ValueTask>> GetDataArrayAsync(string ipAddr, int timeout = 1000) + public async ValueTask>> GetDataArrayAsync(string ipAddr, int timeout = 1000, int cycle = 0) { List? data = null; diff --git a/server/src/WebProtocol.cs b/server/src/WebProtocol.cs index 88a99df..707a7e1 100644 --- a/server/src/WebProtocol.cs +++ b/server/src/WebProtocol.cs @@ -20,10 +20,10 @@ namespace WebProtocol /// Package Burst Type public enum BurstType { - /// Extended Type - ExtendBurst = 0b00, /// Fixed Type - FixedBurst = 0b01, + FixedBurst = 0b00, + /// Extended Type + ExtendBurst = 0b01, } From c76dabfdb791ec716b379494b3afad272345f773 Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Thu, 24 Apr 2025 14:16:38 +0800 Subject: [PATCH 2/4] add new jtag cmd : read status reg --- server/Program.cs | 13 +++++ server/src/Common.cs | 11 ++++ server/src/Controllers.cs | 104 +++++++++++++++++++++++++++++--------- server/src/JtagClient.cs | 34 +++++++++++++ src/views/JtagTest.vue | 36 ++++++++++++- 5 files changed, 172 insertions(+), 26 deletions(-) diff --git a/server/Program.cs b/server/Program.cs index 9e111eb..cfba4f5 100644 --- a/server/Program.cs +++ b/server/Program.cs @@ -36,6 +36,18 @@ try options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; }); + // Add CORS policy + builder.Services.AddCors(options => + { + options.AddPolicy("Development", policy => + { + policy + .AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); + }); + // Add Swagger builder.Services.AddControllers(); builder.Services.AddOpenApiDocument(options => @@ -75,6 +87,7 @@ try app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); + app.UseCors(); app.UseAuthorization(); // if (app.Environment.IsDevelopment()) diff --git a/server/src/Common.cs b/server/src/Common.cs index bc126ef..7cee773 100644 --- a/server/src/Common.cs +++ b/server/src/Common.cs @@ -175,4 +175,15 @@ namespace Common } + public class String + { + public static string Reverse(string s) + { + char[] charArray = s.ToCharArray(); + Array.Reverse(charArray); + return new string(charArray); + } + + } + } diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs index 23a0db9..fc6ed81 100644 --- a/server/src/Controllers.cs +++ b/server/src/Controllers.cs @@ -1,6 +1,6 @@ using System.Net; using Common; -using DotNext; +using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using WebProtocol; @@ -196,6 +196,62 @@ public class JtagController : ControllerBase } } + /// + /// 获取状态寄存器 + /// + /// 设备地址 + /// 设备端口 + [HttpGet("ReadStatusReg")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async ValueTask ReadStatusReg(string address, int port) + { + var jtagCtrl = new JtagClient.Jtag(address, port); + var ret = await jtagCtrl.ReadStatusReg(); + + if (ret.IsSuccessful) + { + var binaryValue = Common.String.Reverse(Convert.ToString(ret.Value, 2).PadLeft(32, '0')); + logger.Info($"Read device {address} Status Register: 0b{binaryValue}"); + return TypedResults.Ok(new + { + original = ret.Value, + binary = binaryValue, + id_err = binaryValue[0], + crc_err = binaryValue[1], + aut_err = binaryValue[2], + rbcrc_err = binaryValue[3], + timeout = binaryValue[4], + wakeup_over = binaryValue[5], + wakedown_over = binaryValue[6], + m = Common.String.Reverse(binaryValue[7..10]), + init_complete = binaryValue[10], + init_n = binaryValue[11], + done = binaryValue[12], + done_i = binaryValue[13], + glogen = binaryValue[14], + glogen_fb = binaryValue[15], + gouten = binaryValue[16], + grsn = binaryValue[17], + gwen = binaryValue[18], + pll_lock = binaryValue[19], + fallback = binaryValue[21], + ipal_m = Common.String.Reverse(binaryValue[22..24]), + flg_x8 = binaryValue[24], + flg_x16 = binaryValue[25], + flg_x32 = binaryValue[26], + over_temp = binaryValue[27], + prcfg_err = binaryValue[28], + prcfg_over = binaryValue[29], + }); + } + else + { + logger.Error(ret.Error); + return TypedResults.InternalServerError(ret.Error); + } + } + /// /// 上传比特流文件 /// @@ -312,6 +368,7 @@ public class Log : ControllerBase private readonly string _logFilePath = Directory.GetFiles(Directory.GetCurrentDirectory())[0]; + [EnableCors("Development")] [HttpGet] public async Task Index() { @@ -373,28 +430,27 @@ public class Log : ControllerBase await Response.Body.FlushAsync(); } - [HttpGet("example")] - public async Task SseExample() - { - // 请求头 - Response.Headers.Add("Content-Type", "text/event-stream"); - Response.Headers.Add("Cache-Control", "no-cache"); - Response.Headers.Add("Connection", "keep-alive"); - - // 假设不断得到新数据 - for (int i = 0; i < 10000; i++) - { - // 每个数据参照 SSE 返回数据格式进行组装 - var content = "event: message\n" - + "data: {\"color\":\"66ccff\"}\n\n"; - - // 立刻写入响应 - await Response.WriteAsync(content); - await Response.Body.FlushAsync(); - } - - // 结束 - return new EmptyResult(); - } + // [EnableCors("Development")] + // [HttpGet("example")] + // public async Task SseExample(HttpContext ctx, ItemService service, CancellationToken ct) + // { + // // 请求头 + // ctx.Response.Headers.Add("Content-Type", "text/event-stream"); + // // Response.Headers.Add("Content-Type", "text/event-stream"); + // // Response.Headers.Add("Cache-Control", "no-cache"); + // // Response.Headers.Add("Connection", "keep-alive"); + // + // while (!ct.IsCancellationRequested) + // { + // var item = await service.WaitForNewItem(); + // + // await ctx.Response.WriteAsync($"data: "); + // await JsonSerializer.SerializeAsync(ctx.Response.Body, item); + // await ctx.Response.WriteAsync($"\n\n"); + // await ctx.Response.Body.FlushAsync(); + // + // service.Reset(); + // } + // } } diff --git a/server/src/JtagClient.cs b/server/src/JtagClient.cs index 07a3468..b4581ff 100644 --- a/server/src/JtagClient.cs +++ b/server/src/JtagClient.cs @@ -596,6 +596,40 @@ public class Jtag return retData.Value; } + public async ValueTask> ReadStatusReg() + { + // Clear Data + await MsgBus.UDPServer.ClearUDPData(this.address); + + logger.Trace($"Clear up udp server {this.address} receive data"); + + Result 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(4); + if (!retData.IsSuccessful) + { + return new(new Exception("Read Status Reg Failed")); + } + + return retData.Value; + } + public async ValueTask> DownloadBitstream(byte[] bitstream) { // Clear Data diff --git a/src/views/JtagTest.vue b/src/views/JtagTest.vue index c20d582..8337c9b 100644 --- a/src/views/JtagTest.vue +++ b/src/views/JtagTest.vue @@ -3,13 +3,45 @@
- +

{{ logText }}

+
- + diff --git a/src/views/JtagTest.vue b/src/views/JtagTest.vue index 8337c9b..7fb5887 100644 --- a/src/views/JtagTest.vue +++ b/src/views/JtagTest.vue @@ -3,44 +3,38 @@
-

{{ logText }}

-