]> git.the-white-hart.net Git - vhdl/commitdiff
Add JTAG USERn registers and test to utility lib
authorRyan <>
Mon, 29 Sep 2025 01:27:49 +0000 (20:27 -0500)
committerRyan <>
Mon, 29 Sep 2025 01:27:49 +0000 (20:27 -0500)
libraries/utility/jtag_reg.vhd [new file with mode: 0644]
libraries/utility/tests/nexys2_test_jtag.vhd [new file with mode: 0644]
libraries/utility/tests/program.sh [new file with mode: 0755]
projects/nexys2_host_controller/host/build.sh
projects/nexys2_host_controller/host/jtag.c [new file with mode: 0644]

diff --git a/libraries/utility/jtag_reg.vhd b/libraries/utility/jtag_reg.vhd
new file mode 100644 (file)
index 0000000..69e3da4
--- /dev/null
@@ -0,0 +1,81 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity jtag_reg is
+       generic (N: natural := 8);
+       port (
+               -- Device signals
+               clk_i:   in  std_logic;
+               upd_o:   out std_logic;
+               dat_i:   in  std_logic_vector(N-1 downto 0);
+               dat_o:   out std_logic_vector(N-1 downto 0);
+
+               -- JTAG signals
+               reset:   in  std_logic;
+               capture: in  std_logic;
+               shift:   in  std_logic;
+               update:  in  std_logic;
+               sel:     in  std_logic;
+               drck:    in  std_logic;
+               tdi:     in  std_logic;
+               tdo:     out std_logic
+       );
+end jtag_reg;
+
+
+architecture behavioral of jtag_reg is
+
+       signal update_cross_reg: std_logic_vector(3 downto 0);
+
+       signal shift_reg:  std_logic_vector(N-1 downto 0);
+       signal outbuf_reg: std_logic_vector(N-1 downto 0);
+
+begin
+
+       process (reset, capture, shift, update, sel, drck, tdi, shift_reg)
+       begin
+               if reset = '1' then
+                       -- Don't need to reset shift_reg, it gets loaded on CAPTURE
+                       null;
+               elsif sel = '1' then
+                       if rising_edge(drck) and sel = '1' then
+                               if capture = '1' then
+                                       shift_reg <= dat_i;
+                               elsif shift = '1' then
+                                       shift_reg <= tdi & shift_reg(N-1 downto 1);
+                               end if;
+                       end if;
+               end if;
+       end process;
+       tdo <= shift_reg(0);
+
+
+       -- Output register, cross clock domains from DRCK to clk_i
+       -- This assumes:
+       -- * JTAG clock is slower than clk_i
+       -- * shift_reg will be stable while UPDATE is asserted
+       -- * sel will be stable while UPDATE is asserted
+       process (clk_i)
+       begin
+               if rising_edge(clk_i) then
+                       if reset = '1' then
+                               -- Probably not necessary
+                               update_cross_reg <= (others => '0');
+                       else
+                               update_cross_reg <= update_cross_reg(2 downto 0) & (update and sel);
+
+                               if update_cross_reg(1) = '1' then
+                                       outbuf_reg <= shift_reg;
+                               end if;
+                       end if;
+               end if;
+       end process;
+       dat_o <= outbuf_reg;
+
+       -- Extra bits of shift register are for generating single-cycle update
+       -- signal within clk_i domain
+       upd_o <= '1' when update_cross_reg(3 downto 2) = "01" else '0';
+
+end behavioral;
diff --git a/libraries/utility/tests/nexys2_test_jtag.vhd b/libraries/utility/tests/nexys2_test_jtag.vhd
new file mode 100644 (file)
index 0000000..f4d90c7
--- /dev/null
@@ -0,0 +1,176 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+library work;
+
+library nexys2_lib;
+
+
+entity nexys2_test_jtag is
+       port (
+               clk_50: in  std_logic;
+               seg:    out std_logic_vector(6 downto 0);
+               dp:     out std_logic;
+               an:     out std_logic_vector(3 downto 0);
+               Led:    out std_logic_vector(7 downto 0);
+               sw:     in  std_logic_vector(7 downto 0)
+       );
+end nexys2_test_jtag;
+
+
+architecture behavioral of nexys2_test_jtag is
+
+       signal jtag_capture:    std_logic;
+       signal jtag_drck1:      std_logic;
+       signal jtag_drck2:      std_logic;
+       signal jtag_reset:      std_logic;
+       signal jtag_sel1:       std_logic;
+       signal jtag_sel2:       std_logic;
+       signal jtag_shift:      std_logic;
+       signal jtag_tdi:        std_logic;
+       signal jtag_update:     std_logic;
+       signal jtag_tdo1:       std_logic;
+       signal jtag_tdo2:       std_logic;
+
+       signal clk_div_reg:     std_logic_vector(15 downto 0);
+       signal seg_0:           std_logic_vector(6 downto 0);
+       signal seg_1:           std_logic_vector(6 downto 0);
+       signal seg_2:           std_logic_vector(6 downto 0);
+       signal seg_3:           std_logic_vector(6 downto 0);
+
+       signal user1:           std_logic_vector(3 downto 0);
+       signal user1_reg:       std_logic_vector(3 downto 0);
+       signal user1_update:    std_logic;
+       signal user1_count_reg: std_logic_vector(7 downto 0);
+
+       signal user2:           std_logic_vector(3 downto 0);
+       signal user2_reg:       std_logic_vector(3 downto 0);
+       signal user2_update:    std_logic;
+       signal user2_count_reg: std_logic_vector(7 downto 0);
+
+begin
+
+       e_bscan: bscan_spartan3
+               port map (
+                       reset   => jtag_reset,
+                       capture => jtag_capture,
+                       shift   => jtag_shift,
+                       update  => jtag_update,
+
+                       tdi     => jtag_tdi,
+
+                       drck1   => jtag_drck1,
+                       sel1    => jtag_sel1,
+                       tdo1    => jtag_tdo1,
+
+                       drck2   => jtag_drck2,
+                       sel2    => jtag_sel2,
+                       tdo2    => jtag_tdo2
+               );
+
+
+       e_user1: entity work.jtag_reg
+               generic map (N => 4)
+               port map (
+                       clk_i   => clk_50,
+                       upd_o   => user1_update,
+                       dat_i   => sw(7 downto 4),
+                       dat_o   => user1,
+
+                       reset   => jtag_reset,
+                       capture => jtag_capture,
+                       shift   => jtag_shift,
+                       update  => jtag_update,
+                       sel     => jtag_sel1,
+                       drck    => jtag_drck1,
+                       tdi     => jtag_tdi,
+                       tdo     => jtag_tdo1
+               );
+
+
+       e_user2: entity work.jtag_reg
+               generic map (N => 4)
+               port map (
+                       clk_i   => clk_50,
+                       upd_o   => user2_update,
+                       dat_i   => sw(3 downto 0),
+                       dat_o   => user2,
+
+                       reset   => jtag_reset,
+                       capture => jtag_capture,
+                       shift   => jtag_shift,
+                       update  => jtag_update,
+                       sel     => jtag_sel2,
+                       drck    => jtag_drck2,
+                       tdi     => jtag_tdi,
+                       tdo     => jtag_tdo2
+               );
+
+
+       process (clk_50)
+       begin
+               if rising_edge(clk_50) then
+                       clk_div_reg <= std_logic_vector(unsigned(clk_div_reg) + 1);
+
+                       if user1_update = '1' then
+                               user1_reg <= user1;
+                               user1_count_reg <= std_logic_vector(unsigned(user1_count_reg) + 1);
+                       end if;
+
+                       if user2_update = '1' then
+                               user2_reg <= user2;
+                               user2_count_reg <= std_logic_vector(unsigned(user2_count_reg) + 1);
+                       end if;
+               end if;
+       end process;
+       Led(7 downto 4) <= user1_reg;
+       Led(3 downto 0) <= user2_reg;
+
+
+       e_seven_seg_hex_0: entity nexys2_lib.seven_seg_hex
+               port map (
+                       data_in => user2_count_reg(3 downto 0),
+                       seg_out => seg_0
+               );
+
+
+       e_seven_seg_hex_1: entity nexys2_lib.seven_seg_hex
+               port map (
+                       data_in => user2_count_reg(7 downto 4),
+                       seg_out => seg_1
+               );
+
+
+       e_seven_seg_hex_2: entity nexys2_lib.seven_seg_hex
+               port map (
+                       data_in => user1_count_reg(3 downto 0),
+                       seg_out => seg_2
+               );
+
+
+       e_seven_seg_hex_3: entity nexys2_lib.seven_seg_hex
+               port map (
+                       data_in => user1_count_reg(7 downto 4),
+                       seg_out => seg_3
+               );
+
+
+       e_seven_seg_mux: entity nexys2_lib.seven_seg_mux
+               port map (
+                       clk_in   => clk_div_reg(15),
+                       clk_en   => '1',
+                       seg_0_in => seg_0,
+                       seg_1_in => seg_1,
+                       seg_2_in => seg_2,
+                       seg_3_in => seg_3,
+                       dps_in   => "1011",
+                       seg_out  => seg,
+                       dp_out   => dp,
+                       an_out   => an
+               );
+
+end behavioral;
diff --git a/libraries/utility/tests/program.sh b/libraries/utility/tests/program.sh
new file mode 100755 (executable)
index 0000000..d19c11c
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f nexys2_test_jtag.bit prog
+
index dcc8e1ea85e880237a12b23fdc201b19bc727632..a7e16c6942841e4f0461cd9a5902f78171c2376c 100755 (executable)
@@ -9,3 +9,5 @@ gcc -o epp_read  epp_read.c  ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr
 gcc -o epp_write epp_write.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr
 gcc -o digdude digdude.c fileformat.c memctrl.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr
 
+gcc -o jtag jtag.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldmgr -ldjtg
+
diff --git a/projects/nexys2_host_controller/host/jtag.c b/projects/nexys2_host_controller/host/jtag.c
new file mode 100644 (file)
index 0000000..c42f544
--- /dev/null
@@ -0,0 +1,326 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+// Digilent SDK
+#include <dpcdecl.h>
+#include <djtg.h>
+#include <dmgr.h>
+
+#include "ec.h"
+
+#define MAX_ID_CODES 16
+#define MAX_REG_WIDTH 0x1000
+#define SENTINEL 0xdeadbeefcafefeed
+
+// Debug
+#include <unistd.h>
+
+
+// -----------------------------------------------------------------------------
+
+
+uint8_t tms_shift_dr[] = {0x01};
+const uint32_t tms_shift_dr_bits = 3;
+
+uint8_t tms_shift_ir[] = {0x03};
+const uint32_t tms_shift_ir_bits = 4;
+
+uint8_t tms_shift_to_idle[] = {0x03};
+const uint32_t tms_shift_to_idle_bits = 3;
+
+uint64_t window = 0;
+
+
+uint64_t field_read(size_t num_bits)
+{
+       assert(num_bits <= 64);
+       uint64_t mask = num_bits == 64 ? ~0ull : (1ull << num_bits) - 1;
+       return window & mask;
+}
+
+
+void field_write(size_t num_bits, uint64_t value)
+{
+       assert(num_bits <= 64);
+       uint64_t mask = num_bits == 64 ? ~0ull : (1ull << num_bits) - 1;
+       window = (window & ~mask) | (value & mask);
+}
+
+
+bool tap_reset(HIF hif)
+{
+       return DjtgClockTck(hif, true, false, 5, false);
+}
+
+
+bool tap_reset_to_idle(HIF hif)
+{
+       return DjtgClockTck(hif, false, false, 1, false);
+}
+
+
+bool tap_idle_to_dr(HIF hif)
+{
+       //return DjtgPutTmsBits(hif, false, tms_shift_dr, NULL, tms_shift_dr_bits, false);
+       EC_FALSE(DjtgClockTck(hif, true, false, 1, false));
+       EC_FALSE(DjtgClockTck(hif, false, false, 1, false));
+       EC_FALSE(DjtgClockTck(hif, false, false, 1, false));
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool tap_idle_to_ir(HIF hif)
+{
+       //return DjtgPutTmsBits(hif, false, tms_shift_ir, NULL, tms_shift_ir_bits, false);
+       EC_FALSE(DjtgClockTck(hif, true, false, 2, false));
+       EC_FALSE(DjtgClockTck(hif, false, false, 1, false));
+       EC_FALSE(DjtgClockTck(hif, false, false, 1, false));
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool tap_reg_to_idle(HIF hif)
+{
+       //return DjtgPutTmsBits(hif, false, tms_shift_to_idle, NULL, tms_shift_to_idle_bits, false);
+       EC_FALSE(DjtgClockTck(hif, true, false, 2, false));
+       EC_FALSE(DjtgClockTck(hif, false, false, 1, false));
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool tap_reg_shift(HIF hif, size_t num_bits, bool final)
+{
+       assert(hif != hifInvalid);
+       int tms, tdi, tdo, tck;
+
+       for (size_t i = 0; i < num_bits; i++)
+       {
+               EC_FALSE(DjtgGetTmsTdiTdoTck(hif, &tms, &tdi, &tdo, &tck));
+               tdi = window & 1;
+               if (final)
+               {
+                       EC_FALSE(DjtgClockTck(hif, i == num_bits-1, tdi, 1, false));
+               }
+               else
+               {
+                       EC_FALSE(DjtgClockTck(hif, false, tdi, 1, false));
+               }
+               window >>= 1;
+               if (tdo) window |= 0x8000000000000000;
+       }
+       if (final)
+       {
+               EC_FALSE(DjtgClockTck(hif, true,  false, 1, false));
+               EC_FALSE(DjtgClockTck(hif, false, false, 1, false));
+       }
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool tap_reg_get_len(HIF hif, uint32_t *len_out)
+{
+       assert(hif != hifInvalid);
+       assert(len_out != NULL);
+
+       uint32_t len = 0;
+
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+
+       for (len = 0; len < MAX_REG_WIDTH; len++)
+       {
+               if (field_read(64) == SENTINEL) break;
+               EC_FALSE(tap_reg_shift(hif, 1, false));
+       }
+       EC_NE(field_read(64), SENTINEL);
+
+       *len_out = len;
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool scan(HIF hif)
+{
+       assert(hif != hifInvalid);
+
+       uint32_t ir_width = 0;
+       uint32_t bypass_width = 0;
+
+       // Assume IDCODE is selected on reset and scan
+       printf("IDCODEs:\n");
+       EC_FALSE(tap_reset(hif));
+       EC_FALSE(tap_reset_to_idle(hif));
+       EC_FALSE(tap_idle_to_dr(hif));
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+       for (size_t i = 0; i < MAX_ID_CODES; i++)
+       {
+               uint32_t idcode = field_read(32);
+               printf("  0x%08x\n", idcode);
+               EC_FALSE(tap_reg_shift(hif, 32, false));
+               if (field_read(64) == SENTINEL) break;
+       }
+       EC_NE(field_read(64), SENTINEL);
+
+       // Select IR and find length
+       EC_FALSE(tap_reset(hif));
+       EC_FALSE(tap_reset_to_idle(hif));
+       EC_FALSE(tap_idle_to_ir(hif));
+       EC_FALSE(tap_reg_get_len(hif, &ir_width));
+       printf("JTAG chain total IR width: %u\n", ir_width);
+
+       // Select BYPASS and find length
+       //                         TMS    TDI
+       EC_FALSE(DjtgClockTck(hif, false, true, ir_width, false));
+       EC_FALSE(tap_reg_to_idle(hif));
+       EC_FALSE(tap_idle_to_dr(hif));
+       EC_FALSE(tap_reg_get_len(hif, &bypass_width));
+       printf("JTAG chain total BYPASS width: %u\n", bypass_width);
+
+       // Reset
+       EC_FALSE(tap_reset(hif));
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+// -----------------------------------------------------------------------------
+
+
+int main(int argc, char **argv)
+{
+       (void)argc;
+       (void)argv;
+
+       // Requested settings
+       char *device = "Nexys2";
+       int32_t port = 0;
+
+       // Port handle and properties
+       HIF hif = hifInvalid;
+       char version[cchVersionMax];
+       int32_t port_count;
+       uint32_t speed;
+
+       // -------------------------------------------------------------
+
+       EC_FALSE(DjtgGetVersion(version));
+       printf("DJTG version: %s\n", version);
+
+       EC_FALSE(DmgrOpen(&hif, device));
+       printf("Opened device \"%s\"\n", device);
+
+       EC_FALSE(DjtgGetPortCount(hif, &port_count));
+       printf("Port count: %" PRIi32 "\n", port_count);
+       for (int32_t i = 0; i < port_count; i++)
+       {
+               uint32_t port_properties  = 0;
+               EC_FALSE(DjtgGetPortProperties(hif, i, &port_properties));
+               printf("Port %" PRIi32 " properties: 0x%08" PRIx32 "\n", i, port_properties);
+               printf("  %c JtgSetSpeed\n",    port_properties & dprpJtgSetSpeed    ? 'y' : 'n');
+               printf("  %c JtgSetPinState\n", port_properties & dprpJtgSetPinState ? 'y' : 'n');
+               printf("  %c JtgWait\n",        port_properties & dprpJtgWait        ? 'y' : 'n');
+               printf("  %c JtgBatch\n",       port_properties & dprpJtgBatch       ? 'y' : 'n');
+               printf("  %c JtgSetAuxReset\n", port_properties & dprpJtgSetAuxReset ? 'y' : 'n');
+               printf("  %c JtgSetGetGpio\n",  port_properties & dprpJtgSetGetGpio  ? 'y' : 'n');
+
+               if (port_properties & dprpJtgBatch)
+               {
+                       uint32_t batch_properties = 0;
+                       EC_FALSE(DjtgGetBatchProperties(hif, i, &batch_properties));
+                       printf("    Batch properties:\n");
+                       printf("    %c WaitUs\n",      batch_properties & djbpWaitUs      ? 'y' : 'n');
+                       printf("    %c SetAuxReset\n", batch_properties & djbpSetAuxReset ? 'y' : 'n');
+                       printf("    %c SetGetGpio\n",  batch_properties & djbpSetGetGpio  ? 'y' : 'n');
+               }
+       }
+
+       if (port >= port_count)
+       {
+               fprintf(stderr, "Requested port out of range\n");
+               EC_FAIL;
+       }
+
+       EC_FALSE(DjtgEnableEx(hif, port));
+       printf("Opened JTAG port %" PRIi32 "\n", port);
+
+       EC_FALSE(DjtgGetSpeed(hif, &speed));
+       printf("Port speed: %" PRIi32 " Hz\n", speed);
+
+       // -------------------------------------------------------------
+
+       // Perform a scan to see what's on the chain
+       EC_FALSE(scan(hif));
+       printf("\n\n");
+
+       // Select IR
+       EC_FALSE(tap_reset(hif));
+       EC_FALSE(tap_reset_to_idle(hif));
+       EC_FALSE(tap_idle_to_ir(hif));
+
+       // Insert sentinel
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+
+       // Write first instruction
+       field_write(8, 0xff);
+       EC_FALSE(tap_reg_shift(hif, 7, false));
+
+       // Write second instruction
+       field_write(6, 0x03);
+       EC_FALSE(tap_reg_shift(hif, 6, true));
+
+       // Select DR
+       EC_FALSE(tap_idle_to_dr(hif));
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));           // Shift out sentinel
+       EC_FALSE(tap_reg_shift(hif, 1, false));            // Shift out ROM BYPASS
+       printf("USER1: 0x%1" PRIx64 "\n", field_read(4));  // Read USER1
+       field_write(4, 0x3);                               // Write USER1
+       EC_FALSE(tap_reg_shift(hif, 4, true));             // Shift out USER1
+       EC_NE(field_read(64), SENTINEL);                   // Verify sentinel
+
+       // -------------------------------------------------------------
+       // Close JTAG session
+
+       if (hif != hifInvalid)
+       {
+               DjtgDisable(hif);
+               DmgrClose(hif);
+       }
+       return 0;
+EC_CLEANUP_BEGIN
+       if (hif != hifInvalid)
+       {
+               DjtgDisable(hif);
+               DmgrClose(hif);
+       }
+       ec_print();
+       return 1;
+EC_CLEANUP_END
+}