]> git.the-white-hart.net Git - vhdl/commitdiff
Add JTAG debugging to CPU0
authorRyan <>
Tue, 30 Sep 2025 07:41:22 +0000 (02:41 -0500)
committerRyan <>
Tue, 30 Sep 2025 07:41:22 +0000 (02:41 -0500)
projects/cpu_0/nexys2_speed.vhd
projects/nexys2_host_controller/host/jtag.c

index e639457160043443ac0440b3aeeaad0024c96e4c..68e9575552cf54fc3e09f63005558fc92ff9bbd7 100644 (file)
@@ -2,6 +2,9 @@ library ieee;
 use ieee.std_logic_1164.all;
 use ieee.std_logic_misc.all;
 
+library unisim;
+use unisim.vcomponents.all;
+
 library utility;
 library nexys2_lib;
 library ps2;
@@ -170,48 +173,124 @@ architecture behavioral of nexys2_speed is
        signal ints:              std_logic_vector(15 downto 0);
 
        -- Debug signals
+       signal jtag_reset:        std_logic;
+       signal jtag_capture:      std_logic;
+       signal jtag_shift:        std_logic;
+       signal jtag_update:       std_logic;
+       signal jtag_tdi:          std_logic;
+       signal jtag_drck1:        std_logic;
+       signal jtag_sel1:         std_logic;
+       signal jtag_tdo1:         std_logic;
+       signal jtag_drck2:        std_logic;
+       signal jtag_sel2:         std_logic;
+       signal jtag_tdo2:         std_logic;
+
+       signal debug_to_host:     std_logic_vector(137 downto 0);
+       signal debug_from_host:   std_logic_vector(137 downto 0);
+
+       signal deb_halt:          std_logic;
        signal deb_wait:          std_logic;
        signal deb_pc:            std_logic_vector(31 downto 0);
        signal deb_ins:           std_logic_vector(7 downto 0);
        signal deb_t:             std_logic_vector(31 downto 0);
        signal deb_n:             std_logic_vector(31 downto 0);
        signal deb_r:             std_logic_vector(31 downto 0);
-       signal debug_i:           std_logic_vector(63 downto 0);
-       signal debug_o:           std_logic_vector(63 downto 0);
 
 begin
 
+       ----------------------------------------------------------------------------
+       -- JTAG debugging
+
+       e_jtag: 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
+               );
+
+
+       -- Single stepping and CPU state reading
+       e_user1: entity utility.jtag_reg
+               generic map (N => 138)
+               port map (
+                       clk_i   => d_clk,
+                       upd_o   => open,
+                       dat_i   => debug_to_host,
+                       dat_o   => debug_from_host,
+
+                       reset   => jtag_reset,
+                       capture => jtag_capture,
+                       shift   => jtag_shift,
+                       update  => jtag_update,
+                       sel     => jtag_sel1,
+                       drck    => jtag_drck1,
+                       tdi     => jtag_tdi,
+                       tdo     => jtag_tdo1
+               );
+
+       deb_halt <= debug_from_host(137);
+       deb_wait <= debug_from_host(136);
+       debug_to_host <= deb_halt & deb_wait & deb_pc & deb_ins & deb_t & deb_n & deb_r;
+
+
+       -- Only here to not break the JTAG chain when USER2 is selected
+       e_user2: entity utility.jtag_reg
+               generic map (N => 1)
+               port map (
+                       clk_i   => d_clk,
+                       upd_o   => open,
+                       dat_i   => (others => '0'),
+                       dat_o   => open,
+
+                       reset   => jtag_reset,
+                       capture => jtag_capture,
+                       shift   => jtag_shift,
+                       update  => jtag_update,
+                       sel     => jtag_sel2,
+                       drck    => jtag_drck2,
+                       tdi     => jtag_tdi,
+                       tdo     => jtag_tdo2
+               );
+
+
+       ----------------------------------------------------------------------------
+
        e_cpu: entity work.cpu
                port map (
-                       rst_i => d_rst,
-                       clk_i => d_clk,
+                       rst_i  => d_rst,
+                       clk_i  => d_clk,
 
-                       cyc_o => cyc,
-                       stb_o => stb,
-                       we_o  => we,
-                       ack_i => ack,
-                       adr_o => adr,
-                       dat_o => dat_mosi,
-                       dat_i => dat_miso,
+                       cyc_o  => cyc,
+                       stb_o  => stb,
+                       we_o   => we,
+                       ack_i  => ack,
+                       adr_o  => adr,
+                       dat_o  => dat_mosi,
+                       dat_i  => dat_miso,
 
-                       int_i => int_cpu,
-                       vec_i => int_vec,
+                       int_i  => int_cpu,
+                       vec_i  => int_vec,
 
-                       halt_i => sw(7),
+                       halt_i => deb_halt,
                        step_i => deb_wait,
-                       pc_o  => open,--deb_pc,
-                       ins_o => open,--deb_ins,
-                       t_o   => open,--deb_t,
-                       n_o   => open,--deb_n,
-                       r_o   => open--deb_r
+                       pc_o   => deb_pc,
+                       ins_o  => deb_ins,
+                       t_o    => deb_t,
+                       n_o    => deb_n,
+                       r_o    => deb_r
                );
 
-       deb_wait <= debug_i(0);
-       debug_o <= (others => '0');
-       --with debug_i(2 downto 1) select debug_o <=
-       --      x"000000" & deb_ins & deb_pc when "00",
-       --      deb_n & deb_t                when "01",
-       --      x"00000000" & deb_r          when others;
 
        int_vec(7 downto 4) <= (others => '0');
        ints(0) <= '0';
@@ -416,8 +495,8 @@ begin
                        -- Signals to the internal device
                        d_rst_o      => d_rst,
                        d_flags_o    => host_flags,
-                       debug_i      => debug_o,
-                       debug_o      => debug_i,
+                       debug_i      => (others => '0'),
+                       debug_o      => open,
 
                        -- Internal access to control registers
                        d_cyc_i      => host_cyc,
index c42f5449bc21dcc363d5eecd5f206818bef4a931..c9ebad0f171ee4c9ada962176f1170f1826787c1 100644 (file)
@@ -124,6 +124,7 @@ bool tap_reg_shift(HIF hif, size_t num_bits, bool final)
        }
        if (final)
        {
+               // Return to IDLE
                EC_FALSE(DjtgClockTck(hif, true,  false, 1, false));
                EC_FALSE(DjtgClockTck(hif, false, false, 1, false));
        }
@@ -212,6 +213,272 @@ EC_CLEANUP_END
 // -----------------------------------------------------------------------------
 
 
+bool cpu_state(HIF hif)
+{
+       assert(hif != hifInvalid);
+
+       uint64_t halt, wait, pc, ins, t, n, r;
+       const char *mnem;
+
+       // 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));
+
+       // Select BYPASS in ROM
+       field_write(8, 0xff);
+       EC_FALSE(tap_reg_shift(hif, 8, false));
+
+       // Select USER1 in FPGA
+       field_write(6, 0x02);
+       EC_FALSE(tap_reg_shift(hif, 6, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       // Select DR
+       EC_FALSE(tap_idle_to_dr(hif));
+
+       // Insert sentinel
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+
+       // Shift out ROM's BYPASS
+       EC_FALSE(tap_reg_shift(hif, 1, false));
+
+       // FPGA.USER1.R
+       r = field_read(32);
+       EC_FALSE(tap_reg_shift(hif, 32, false));
+
+       // FPGA.USER1.N
+       n = field_read(32);
+       EC_FALSE(tap_reg_shift(hif, 32, false));
+
+       // FPGA.USER1.T
+       t = field_read(32);
+       EC_FALSE(tap_reg_shift(hif, 32, false));
+
+       // FPGA.USER1.INS
+       ins = field_read(8);
+       EC_FALSE(tap_reg_shift(hif, 8, false));
+
+       // FPGA.USER1.PC
+       pc = field_read(32);
+       EC_FALSE(tap_reg_shift(hif, 32, false));
+
+       // FPGA.USER1.WAIT
+       wait = field_read(1);
+       EC_FALSE(tap_reg_shift(hif, 1, false));
+
+       // FPGA.USER1.HALT
+       halt = field_read(1);
+       EC_FALSE(tap_reg_shift(hif, 1, true));
+
+       switch (ins)
+       {
+       case 0x00: mnem = "NOP";  break;
+       case 0x01: mnem = "#8";   break;
+       case 0x02: mnem = "#32";  break;
+       case 0x03: mnem = "@8";   break;
+       case 0x04: mnem = "@32";  break;
+       case 0x05: mnem = "!8";   break;
+       case 0x06: mnem = "!32";  break;
+       case 0x07: mnem = "R@";   break;
+       case 0x08: mnem = ">R";   break;
+       case 0x09: mnem = "R>";   break;
+       case 0x0a: mnem = "CALL"; break;
+       case 0x0b: mnem = ";";    break;
+       case 0x0c: mnem = "JMP";  break;
+       case 0x0d: mnem = "JZ";   break;
+       case 0x0e: mnem = "JN";   break;
+       case 0x0f: mnem = "JP";   break;
+       case 0x10: mnem = "+";    break;
+       case 0x11: mnem = "-";    break;
+       case 0x12: mnem = "&";    break;
+       case 0x13: mnem = "|";    break;
+       case 0x14: mnem = "^";    break;
+       case 0x15: mnem = "~";    break;
+       case 0x16: mnem = "DUP";  break;
+       case 0x17: mnem = "DROP"; break;
+       case 0x18: mnem = "SWAP"; break;
+       case 0x19: mnem = "IEN";  break;
+       case 0x1a: mnem = "IDIS"; break;
+       case 0x1b: mnem = "LSR";  break;
+       case 0x1c: mnem = "ASR";  break;
+       case 0x1d: mnem = "SHL";  break;
+       default:   mnem = "???";  break;
+       }
+       printf("halt:%"    PRIu64 " "
+              "wait:%"    PRIu64 " "
+              "PC:0x%08"  PRIx64 " "
+              "INS:0x%02" PRIx64 " "
+              "T:0x%08"   PRIx64 " "
+              "N:0x%08"   PRIx64 " "
+              "R:0x%08"   PRIx64 " "
+              "%s\n",
+              halt, wait, pc, ins, t, n, r, mnem);
+
+       EC_NE(field_read(64), SENTINEL);
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool cpu_halt(HIF hif)
+{
+       assert(hif != hifInvalid);
+
+       // 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));
+
+       // Select BYPASS in ROM and USER1 in FPGA
+       field_write(14, 0x02ff);
+       EC_FALSE(tap_reg_shift(hif, 14, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       // Select DR
+       EC_FALSE(tap_idle_to_dr(hif));
+
+       // Insert sentinel
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+
+       // Shift out ROM's BYPASS and all of USER1 up to MSB (HALT bit)
+       EC_FALSE(tap_reg_shift(hif, 137, false));
+
+       // FPGA.USER1.WAIT
+       field_write(1, 0);
+       EC_FALSE(tap_reg_shift(hif, 1, false));
+
+       // FPGA.USER1.HALT
+       field_write(1, 1);
+       EC_FALSE(tap_reg_shift(hif, 1, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool cpu_run(HIF hif)
+{
+       assert(hif != hifInvalid);
+
+       // 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));
+
+       // Select BYPASS in ROM and USER1 in FPGA
+       field_write(14, 0x02ff);
+       EC_FALSE(tap_reg_shift(hif, 14, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       // Select DR
+       EC_FALSE(tap_idle_to_dr(hif));
+
+       // Insert sentinel
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+
+       // Shift out ROM's BYPASS and all of USER1 up to MSB (HALT bit)
+       EC_FALSE(tap_reg_shift(hif, 138, false));
+
+       // FPGA.USER1.HALT
+       field_write(1, 0);
+       EC_FALSE(tap_reg_shift(hif, 1, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool cpu_step(HIF hif)
+{
+       assert(hif != hifInvalid);
+
+       // 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));
+
+       // Select BYPASS in ROM and USER1 in FPGA
+       field_write(14, 0x02ff);
+       EC_FALSE(tap_reg_shift(hif, 14, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       // Select DR
+       EC_FALSE(tap_idle_to_dr(hif));
+
+       // Insert sentinel
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+
+       // Shift out ROM's BYPASS and all of USER1 up to MSB (HALT bit)
+       EC_FALSE(tap_reg_shift(hif, 137, false));
+
+       // FPGA.USER1.WAIT
+       field_write(1, 1);
+       EC_FALSE(tap_reg_shift(hif, 1, false));
+
+       // FPGA.USER1.HALT
+       field_write(1, 1);
+       EC_FALSE(tap_reg_shift(hif, 1, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       // Select DR again
+       EC_FALSE(tap_idle_to_dr(hif));
+
+       // Insert sentinel
+       field_write(64, SENTINEL);
+       EC_FALSE(tap_reg_shift(hif, 64, false));
+
+       // Shift out ROM's BYPASS and all of USER1 up to MSB (HALT bit)
+       EC_FALSE(tap_reg_shift(hif, 137, false));
+
+       // FPGA.USER1.WAIT
+       field_write(1, 0);
+       EC_FALSE(tap_reg_shift(hif, 1, false));
+
+       // FPGA.USER1.HALT
+       field_write(1, 1);
+       EC_FALSE(tap_reg_shift(hif, 1, true));
+       EC_NE(field_read(64), SENTINEL);
+
+       return true;
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+// -----------------------------------------------------------------------------
+
+
 int main(int argc, char **argv)
 {
        (void)argc;
@@ -275,35 +542,16 @@ int main(int argc, char **argv)
        // -------------------------------------------------------------
 
        // Perform a scan to see what's on the chain
-       EC_FALSE(scan(hif));
+       //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
+       EC_FALSE(cpu_halt(hif));
+       EC_FALSE(cpu_state(hif));
+       for (size_t i = 0; i < 20; i++)
+       {
+               EC_FALSE(cpu_step(hif));
+               EC_FALSE(cpu_state(hif));
+       }
 
        // -------------------------------------------------------------
        // Close JTAG session