From 93add0c3153d812fe54a1fde321a7558207eafe7 Mon Sep 17 00:00:00 2001
From: SikongJueluo <selfconfusion@gmail.com>
Date: Mon, 7 Apr 2025 21:56:30 +0800
Subject: [PATCH] add jtag get id code api

---
 server/Program.cs            |   2 +
 server/server.csproj         |   1 +
 server/src/Common.cs         |  30 ++++-
 server/src/JtagController.cs | 214 ++++++++++++++++++++++++++++++++---
 server/src/Router.cs         |  55 +++++++--
 server/src/WebProtocol.cs    |  27 ++++-
 6 files changed, 300 insertions(+), 29 deletions(-)

diff --git a/server/Program.cs b/server/Program.cs
index de3c392..692e916 100644
--- a/server/Program.cs
+++ b/server/Program.cs
@@ -41,6 +41,8 @@ app.MapGet("/", () => "Hello World!");
 app.MapPut("/api/SendString", Router.API.SendString);
 app.MapPut("/api/SendAddrPackage", Router.API.SendAddrPackage);
 app.MapPut("/api/SendDataPackage", Router.API.SendDataPackage);
+app.MapPut("/api/jtag/RunCommand", Router.API.Jtag.RunCommand);
+app.MapPut("/api/jtag/GetIDCode", Router.API.Jtag.GetDeviceIDCode);
 
 app.Run("http://localhost:5000");
 
diff --git a/server/server.csproj b/server/server.csproj
index 72e9b6e..faf5a41 100644
--- a/server/server.csproj
+++ b/server/server.csproj
@@ -12,6 +12,7 @@
     <PackageReference Include="DotNext.Threading" Version="5.19.1" />
     <PackageReference Include="Microsoft.OpenApi" Version="1.6.23" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
+    <PackageReference Include="NLog" Version="5.4.0" />
     <PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
   </ItemGroup>
 
diff --git a/server/src/Common.cs b/server/src/Common.cs
index 714b566..465b096 100644
--- a/server/src/Common.cs
+++ b/server/src/Common.cs
@@ -27,13 +27,13 @@ namespace Common
             {
                 for (var i = 0; i < length; i++)
                 {
-                    arr[i] = Convert.ToByte((num >> (i << 3)) & (0xFF));
+                    arr[i] = Convert.ToByte((num >> ((int)(length - 1 - i) << 3)) & (0xFF));
                 }
             }
             return arr;
         }
 
-        public static Result<ulong> BytesToNumber(byte[] bytes, bool isRightLeft = false)
+        public static Result<ulong> BytesToNumber(byte[] bytes, bool isRightHigh = false)
         {
             if (bytes.Length > 8)
             {
@@ -46,7 +46,7 @@ namespace Common
             ulong num = 0;
             int len = bytes.Length;
 
-            if (isRightLeft)
+            if (isRightHigh)
             {
                 for (var i = 0; i < len; i++)
                 {
@@ -57,13 +57,15 @@ namespace Common
             {
                 for (var i = 0; i < len; i++)
                 {
-                    num += Convert.ToUInt64(bytes[i] << (i << 3));
+                    num += Convert.ToUInt64(bytes[i] << ((int)(len - 1 - i) << 3));
                 }
             }
 
             return num;
         }
 
+        
+
         public static Result<byte[]> MultiBitsToBytes(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len)
         {
             return NumberToBytes(MultiBitsToNumber(bits1, bits1Len, bits2, bits2Len).Value,
@@ -74,10 +76,28 @@ namespace Common
         {
             if (bits1Len + bits2Len > 64) throw new ArgumentException("Two Bits is more than 64 bits");
 
-            ulong num = (bits1 << (int)bits2Len) | bits2;
+            ulong num = (bits1 << Convert.ToInt32(bits2Len)) | bits2;
             return num;
         }
 
+        public static Result<uint> MultiBitsToNumber(uint bits1, uint bits1Len, uint bits2, uint bits2Len)
+        {
+            if (bits1Len + bits2Len > 64) throw new ArgumentException("Two Bits is more than 64 bits");
+
+            uint num = (bits1 << Convert.ToInt32(bits2Len)) | bits2;
+            return num;
+        }
+
+        public static bool BitsCheck(ulong srcBits, ulong dstBits, ulong mask = 0xFFFF_FFFF_FFFF_FFFF)
+        {
+            return (srcBits & mask) == dstBits;
+        }
+
+        public static bool BitsCheck(uint srcBits, uint dstBits, uint mask = 0xFFFF_FFFF)
+        {
+            return (srcBits & mask) == dstBits;
+        }
+
 
         public static Result<byte[]> StringToBytes(string str, int numBase = 16)
         {
diff --git a/server/src/JtagController.cs b/server/src/JtagController.cs
index 0c092be..ae26c01 100644
--- a/server/src/JtagController.cs
+++ b/server/src/JtagController.cs
@@ -28,12 +28,107 @@ public static class JtagAddr
     public const UInt32 WRITE_CMD = 0x10_00_00_03;
 }
 
+/// <summary>
+/// Jtag 状态寄存器的掩码及其确认函数
+/// </summary>
+public static class JtagState
+{
+
+    /*
+    [0]    移位数据读fifo清空,1为有效,清空后自动置0
+    [1]    移位数据fifo读入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
+    [2]    移位数据fifo读入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
+    [7:3]  保留
+
+    [8]    移位数据写fifo清空,1为有效,清空后自动置0
+    [9]    移位数据fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
+    [10]   移位数据fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
+    [15:11]保留
+
+    [16]   移位命令写fifo清空,1为有效,清空后自动置0
+    [17]   移位命令fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
+    [18]   移位命令fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
+    [23:19]保留
+
+    [24]   CMD执行完毕标识,只读,对JTAG_STATE_REG的写不改变其值
+    */
+
+    /// <summary>
+    /// 移位数据读fifo清空,1为有效,清空后自动置0,实际一直为零
+    /// </summary>
+    public const UInt32 READ_DATA_FIFO_CLEAR = 0b0001 << 0;
+    /// <summary>
+    /// 移位数据fifo读入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
+    /// </summary>
+    public const UInt32 READ_DATA_FIFO_EMPTY = 0b0010 << 0;
+    /// <summary>
+    /// 移位数据fifo读入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
+    /// </summary>
+    public const UInt32 READ_DATA_FIFO_AFULL = 0b0100 << 0;
+    /// <summary>
+    /// 移位数据读fifo
+    /// </summary>
+    public const UInt32 READ_DATA_FIFO = 0b0111 << 0;
+
+    /// <summary>
+    /// 移位数据写fifo清空,1为有效,清空后自动置0
+    /// </summary>
+    public const UInt32 WRITE_DATA_FIFO_CLEAR = 0b0001 << 8;
+    /// <summary>
+    /// 移位数据fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
+    /// </summary>
+    public const UInt32 WRITE_DATA_FIFO_EMPTY = 0b0010 << 8;
+    /// <summary>
+    /// 移位数据fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
+    /// </summary>
+    public const UInt32 WRITE_DATA_FIFO_AFULL = 0b0100 << 8;
+    /// <summary>
+    /// 移位数据写fifo
+    /// </summary>
+    public const UInt32 WRITE_DATA_FIFO = 0b0111 << 8;
+
+    /// <summary>
+    /// 移位命令写fifo清空,1为有效,清空后自动置0
+    /// </summary>
+    public const UInt32 WRITE_CMD_FIFO_CLEAR = 0b0001 << 16;
+    /// <summary>
+    /// 移位命令fifo写入口-空标识,只读,对JTAG_STATE_REG的写不改变其值
+    /// </summary>
+    public const UInt32 WRITE_CMD_FIFO_EMPTY = 0b0010 << 16;
+    /// <summary>
+    /// 移位命令fifo写入口-满标识,只读,对JTAG_STATE_REG的写不改变其值
+    /// </summary>
+    public const UInt32 WRITE_CMD_FIFO_AFULL = 0b0100 << 16;
+    /// <summary>
+    /// 移位命令写fifo
+    /// </summary>
+    public const UInt32 WRITE_CMD_FIFO = 0b0111 << 16;
+
+    /// <summary>
+    /// CMD执行完毕标识,只读,对JTAG_STATE_REG的写不改变其值
+    /// </summary>
+    public const UInt32 CMD_EXEC_FINISH = 0b0001 << 24;
+    /// <summary>
+    /// 全部FIFO
+    /// </summary>
+    public const UInt32 ALL_FIFO = READ_DATA_FIFO | WRITE_DATA_FIFO | WRITE_CMD_FIFO;
+    /// <summary>
+    /// 全部寄存器
+    /// </summary>
+    public const UInt32 ALL_REG = READ_DATA_FIFO | WRITE_DATA_FIFO | WRITE_CMD_FIFO | CMD_EXEC_FINISH;
+
+}
+
 /// <summary>
 /// The Command bits of Jtag
 /// </summary>
 public static class JtagCmd
 {
 
+    /// <summary>
+    /// The length of JTAG_DR_XXXX
+    /// </summary>
+    public const UInt32 LEN_JTAG_DR = 10;
     /// <summary>
     /// 旁路指令
     /// </summary>
@@ -89,6 +184,10 @@ public static class JtagCmd
 
 
 
+    /// <summary>
+    /// The length of CMD_JTAG_XXXX_XXXX
+    /// </summary>
+    public const UInt32 LEN_CMD_JTAG = 4;
     /// <summary>
     /// 设定JTAG默认状态为TEST_LOGIC_RESET态 (JTAG复位)
     /// </summary>
@@ -122,18 +221,56 @@ class Jtag
     readonly int port;
     readonly string address;
     private IPEndPoint ep;
-    private UDPServer server;
 
-    public Jtag(string address, int port, UDPServer udpServer, int outTime = 2000)
+    public Jtag(string address, int port, int outTime = 2000)
     {
         this.address = address;
         this.port = port;
         this.ep = new IPEndPoint(IPAddress.Parse(address), port);
-        this.server = udpServer;
         this.timeout = outTime;
     }
 
-    public async ValueTask<Result<bool>> RunCommand(uint devAddr, uint cmd, uint exRet)
+    public async ValueTask<Result<uint>> ReadFIFO(uint devAddr)
+    {
+        var ret = false;
+        var opts = new SendAddrPackOptions();
+
+        opts.burstType = BurstType.FixedBurst;
+        opts.burstLength = 4;
+        opts.commandID = 0;
+        opts.address = devAddr;
+
+        // Read Jtag State Register
+        ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts));
+        if (!ret) throw new Exception("Send Address Package Failed!");
+
+        // Wait for Ack
+        if (!MsgBus.IsRunning)
+            throw new Exception("Message Bus not Working!");
+
+        var data = await MsgBus.UDPServer.FindDataAsync(address);
+        if (!data.HasValue)
+            throw new Exception("Get None after Time out!");
+
+        var recvData = data.Value;
+        if (recvData.addr != address || recvData.port != port)
+            throw new Exception("Receive Data From Wrong Board!");
+
+        var retPack = RecvDataPackage.FromBytes(recvData.data);
+        if (!retPack.IsSuccessful)
+            throw new Exception("Not RecvDataPackage!", retPack.Error);
+
+        if (retPack.Value.Options.data is null)
+            throw new Exception($"Data is Null, package: {retPack.Value.Options.ToString()}");
+
+        var retPackLen = retPack.Value.Options.data.Length;
+        if (retPackLen != 4)
+            throw new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes");
+
+        return (uint)(NumberProcessor.BytesToNumber(retPack.Value.Options.data));
+    }
+
+    public async ValueTask<Result<RecvDataPackage>> RunCommand(uint devAddr, uint command)
     {
         var ret = false;
         var opts = new SendAddrPackOptions();
@@ -148,7 +285,7 @@ class Jtag
         ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts));
         if (!ret) throw new Exception("Send 1st Address Package Failed!");
         ret = await UDPClientPool.SendDataPackAsync(ep,
-                new SendDataPackage(NumberProcessor.NumberToBytes(cmd, 4).Value));
+                new SendDataPackage(NumberProcessor.NumberToBytes(command, 4).Value));
         if (!ret) throw new Exception("Send Data Package Failed!");
 
         // Read Jtag State Register
@@ -170,41 +307,88 @@ class Jtag
 
         var retPack = RecvDataPackage.FromBytes(recvData.data);
         if (!retPack.IsSuccessful)
-            throw new Exception("Not Current RecvDataPackage!", retPack.Error);
+            throw new Exception("Not RecvDataPackage!", retPack.Error);
+
+
+        return retPack;
+    }
+
+    public async ValueTask<Result<bool>> RunCommand(uint devAddr, uint command, uint result, uint resultMask = 0xFF_FF_FF_FF)
+    {
+        var ret = false;
+        var retPack = await RunCommand(devAddr, command);
+
+        if (retPack.Value.Options.data is null)
+            throw new Exception($"Data is Null, package: {retPack.Value.Options.ToString()}");
 
         var retPackLen = retPack.Value.Options.data.Length;
-        if (retPackLen != 3)
-            throw new Exception($"RecvDataPackage BodyData Length not Equal to 3: Total {retPackLen} bytes");
+        if (retPackLen != 4)
+            throw new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes");
 
-        if (NumberProcessor.BytesToNumber(retPack.Value.Options.data).Value == exRet)
+        if (NumberProcessor.BitsCheck(
+                NumberProcessor.BytesToNumber(retPack.Value.Options.data).Value, result, resultMask))
             ret = true;
 
         return ret;
     }
 
+
     public async ValueTask<Result<bool>> ClearAllRegisters()
     {
-        return await RunCommand(JtagAddr.STATE, 0xFF_FF_FF_FF, 0x00_00_00_00);
+        return await RunCommand(JtagAddr.STATE, 0xFF_FF_FF_FF, 0x01_02_02_02, JtagState.ALL_REG);
     }
 
     public async ValueTask<Result<bool>> ClearWriteDataReg()
     {
-        return await RunCommand(JtagAddr.STATE, 0x00_00_11_00, 0x00_00_00_00);
+        return await RunCommand(JtagAddr.STATE, 0x00_00_11_00, 0x01_00_02_00, JtagState.WRITE_DATA_FIFO | JtagState.CMD_EXEC_FINISH);
     }
 
     public async ValueTask<Result<bool>> RunTest()
     {
-        return await RunCommand(JtagAddr.WRITE_CMD, 0x10_00_00_00, 0x01_00_00_00);
+        return await RunCommand(
+                JtagAddr.WRITE_CMD,
+                NumberProcessor.MultiBitsToNumber(JtagCmd.CMD_JTAG_RUN_TEST, JtagCmd.LEN_CMD_JTAG, 0, 28).Value,
+                0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
     }
 
-    public async ValueTask<Result<bool>> WriteIDCode()
+    public async ValueTask<Result<bool>> DRIDcode()
     {
-        return await RunCommand(JtagAddr.WRITE_DATA, 0b1010000011, 0x01_00_00_00);
+        return await RunCommand(
+                JtagAddr.WRITE_DATA,
+                NumberProcessor.MultiBitsToNumber(0, 22, JtagCmd.JTAG_DR_IDCODE, JtagCmd.LEN_JTAG_DR).Value, 0, 0);
     }
 
     public async ValueTask<Result<bool>> LoadIR()
     {
-        return await RunCommand(JtagAddr.WRITE_CMD, 0x20_00_00_0A, 0x01_00_00_00);
+        return await RunCommand(
+                JtagAddr.WRITE_CMD,
+                NumberProcessor.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_IR, JtagCmd.LEN_CMD_JTAG, 10, 28).Value,
+                0x01_00_00_00);
+    }
+
+    public async ValueTask<Result<uint>> LoadDRCareo()
+    {
+        var ret = await RunCommand(
+                JtagAddr.WRITE_CMD,
+                NumberProcessor.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREO, JtagCmd.LEN_CMD_JTAG, 32, 28).Value,
+                0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
+
+        if (ret.Value)
+            return await ReadFIFO(JtagAddr.READ_DATA);
+        else
+            throw new Exception("LoadDRCareo Failed!");
+    }
+
+    public async ValueTask<Result<uint>> ReadIDCode()
+    {
+        var ret = false;
+
+        ret = (await ClearAllRegisters()).Value;
+        ret = (await RunTest()).Value;
+        ret = (await DRIDcode()).Value;
+        ret = (await LoadIR()).Value;
+        ret = (await ClearWriteDataReg()).Value;
+        return (await LoadDRCareo()).Value;
     }
 
 }
diff --git a/server/src/Router.cs b/server/src/Router.cs
index d8532a9..2b7ea41 100644
--- a/server/src/Router.cs
+++ b/server/src/Router.cs
@@ -1,10 +1,15 @@
 using System.Net;
 using Common;
-using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 
 namespace Router
 {
+    struct Response
+    {
+        public bool IsSuccess;
+        public object? Data;
+    }
+
     /// <summary>
     /// Web API 
     /// </summary>
@@ -21,8 +26,8 @@ namespace Router
         {
             var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
             var ret = await UDPClientPool.SendStringAsync(endPoint, [text]);
-            if (ret) { return Results.Json(true); }
-            else { return Results.Json(false); }
+            if (ret) { return Results.Json(new Response() { IsSuccess = true }); }
+            else { return Results.Json(new Response() { IsSuccess = false }); }
         }
 
 
@@ -46,8 +51,8 @@ namespace Router
 
             var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
             var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new WebProtocol.SendAddrPackage(opts));
-            if (ret) { return Results.Json(true); }
-            else { return Results.Json(false); }
+            if (ret) { return Results.Json(new Response() { IsSuccess = true }); }
+            else { return Results.Json(new Response() { IsSuccess = false }); }
         }
 
         public static async ValueTask<IResult> SendDataPackage(string address, int port, string data)
@@ -55,12 +60,46 @@ namespace Router
             var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
             var ret = await UDPClientPool.SendDataPackAsync(endPoint,
                 new WebProtocol.SendDataPackage(NumberProcessor.StringToBytes(data).Value));
-            if (ret) { return Results.Json(true); }
-            else { return Results.Json(false); }
+            if (ret) { return Results.Json(new Response() { IsSuccess = true }); }
+            else { return Results.Json(new Response() { IsSuccess = false }); }
         }
 
-        public static async ValueTask<IResult> GetDeviceIDCode(string address, int port)
+        public class Jtag
         {
+            /// <summary>
+            /// 执行一个Jtag命令
+            /// </summary>
+            /// <param name="address"> 设备地址 </param>
+            /// <param name="port"> 设备端口 </param>
+            /// <param name="hexDevAddr"> 16进制设备目的地址(Jtag) </param>
+            /// <param name="hexCmd"> 16进制命令 </param>
+            /// <param name="hexExRet"> 16进制预期结果 </param>
+            /// <returns> Response </returns>
+            public static async ValueTask<IResult> RunCommand(string address, int port, string hexDevAddr, string hexCmd, string hexExRet)
+            {
+                var jtagCtrl = new JtagController.Jtag(address, port);
+                var ret = await jtagCtrl.RunCommand(Convert.ToUInt32(hexDevAddr, 16), Convert.ToUInt32(hexCmd, 16), Convert.ToUInt32(hexExRet, 16));
+
+
+                if (ret.IsSuccessful) { return Results.Json(new Response() { IsSuccess = true }); }
+                else { return Results.Json(new Response() { IsSuccess = false, Data = ret.Error }); }
+            }
+
+
+            /// <summary>
+            /// 获取Jtag ID Code
+            /// </summary>
+            /// <param name="address"> 设备地址 </param>
+            /// <param name="port"> 设备端口 </param>
+            /// <returns> Response </returns>
+            public static async ValueTask<IResult> GetDeviceIDCode(string address, int port)
+            {
+                var jtagCtrl = new JtagController.Jtag(address, port);
+                var ret = await jtagCtrl.ReadIDCode();
+
+                if (ret.IsSuccessful) { return Results.Json(new Response() { IsSuccess = true, Data = ret.Value }); }
+                else { return Results.Json(new Response() { IsSuccess = false, Data = ret.Error }); }
+            }
 
 
         }
diff --git a/server/src/WebProtocol.cs b/server/src/WebProtocol.cs
index a224724..4c48312 100644
--- a/server/src/WebProtocol.cs
+++ b/server/src/WebProtocol.cs
@@ -49,19 +49,43 @@ namespace WebProtocol
         /// <example> 0x10_00_00_00 </example>
         public UInt32 address;
 
+        /// <summary>
+        /// Convert to Json String
+        /// </summary>
+        /// <returns> Json String </returns>
         public override string ToString()
         {
             return JsonConvert.SerializeObject(this);
         }
     }
 
+    /// <summary> Package Options which to receive from boards </summary>
     public struct RecvPackOptions
     {
+        /// <summary>
+        /// Task ID
+        /// </summary>
         public byte commandID;
+        /// <summary>
+        /// Whether is succeed to finish command
+        /// </summary>
         public bool isSuccess;
-        public byte[] data;
+        /// <summary>
+        /// Return Data
+        /// </summary>
+        public byte[]? data;
+
+        /// <summary>
+        /// Convert to Json String
+        /// </summary>
+        /// <returns> Json String </returns>
+        public override string ToString()
+        {
+            return JsonConvert.SerializeObject(this);
+        }
     }
 
+    /// <summary> Package which send address to write </summary>
     public struct SendAddrPackage
     {
         readonly byte sign = (byte)PackSign.SendAddr;
@@ -227,6 +251,7 @@ namespace WebProtocol
             RecvPackOptions opts;
             opts.commandID = commandID;
             opts.isSuccess = Convert.ToBoolean((resp >> 1) == 0b01 ? true : false);
+            opts.data = null;
 
             return opts;
         }