From: Ryan <> Date: Tue, 30 Sep 2025 07:41:22 +0000 (-0500) Subject: Add JTAG debugging to CPU0 X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=20bde7a4f69c917f68919c5a1d7dba8ae3833a13;p=vhdl Add JTAG debugging to CPU0 --- diff --git a/projects/cpu_0/nexys2_speed.vhd b/projects/cpu_0/nexys2_speed.vhd index e639457..68e9575 100644 --- a/projects/cpu_0/nexys2_speed.vhd +++ b/projects/cpu_0/nexys2_speed.vhd @@ -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, diff --git a/projects/nexys2_host_controller/host/jtag.c b/projects/nexys2_host_controller/host/jtag.c index c42f544..c9ebad0 100644 --- a/projects/nexys2_host_controller/host/jtag.c +++ b/projects/nexys2_host_controller/host/jtag.c @@ -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