diff --git a/project.cfg b/project.cfg index ec8fc15..dda609e 100644 --- a/project.cfg +++ b/project.cfg @@ -21,7 +21,7 @@ device = xc6slx9 package = tqg144 speedgrade = -2 toplevel = top_generic -xst_opts = -vlgincdir rtl/util -keep_hierarchy yes +xst_opts = -vlgincdir rtl/util files_verilog = rtl/util/conv.vh rtl/toplevel/top_generic.v rtl/core/nco_q15.v @@ -57,9 +57,10 @@ files_verilog = rtl/util/conv.vh rtl/serv/servile.v rtl/serv/serving_ram.v rtl/serv/serving.v + rtl/arch/spartan-6/jtag_if.v rtl/wb/wb_gpio.v rtl/wb/wb_gpio_banks.v - rtl/arch/spartan-6/jtag_if.v + rtl/wb/jtag_wb_bridge.v rtl/core/mcu.v files_con = boards/mimas_v1/constraints.ucf files_other = rtl/util/rc_alpha_q15.vh @@ -74,11 +75,13 @@ device = xc6slx9 package = tqg144 speedgrade = -2 toplevel = top_jtag -xst_opts = -vlgincdir rtl/util +xst_opts = -vlgincdir rtl/util -keep_hierarchy yes files_other = files_con = boards/mimas_v1/constraints.ucf files_verilog = rtl/arch/spartan-6/jtag_if.v rtl/arch/spartan-6/clk_gen.v + rtl/wb/jtag_wb_bridge.v + rtl/wb/wb_gpio.v rtl/toplevel/top_jtag.v [target.svftest] @@ -87,4 +90,16 @@ runtime = all toplevel = tb_svf files_verilog = sim/tb/tb_svf.v sim/overrides/jtag_if.v + rtl/core/cdc_strobed.v files_other = sim/other/test.svf + +[target.tools] +toolchain = make +output_files = tools/test +buildroot = tools +files_makefile = tools/Makefile +files_other = tools/digilent_jtag.cpp + tools/digilent_jtag.hpp + tools/argparse.cpp + tools/argparse.hpp + tools/test.cpp diff --git a/rtl/core/mcu.v b/rtl/core/mcu.v index 55f1872..973f7b9 100644 --- a/rtl/core/mcu.v +++ b/rtl/core/mcu.v @@ -153,6 +153,7 @@ module mcu #( .sim(sim) ) mem ( .i_clk(i_clk), + .i_rst(i_rst), .i_waddr(sram_waddr), .i_wdata(sram_wdata), .i_wen(sram_wen), @@ -180,175 +181,6 @@ module mcu #( endmodule -module jtag_mem_protocol #( - parameter aw = 8, - parameter chain = 1 -)( - input wire i_clk, - input wire i_rst, - input wire [7:0] i_mem_rdata, - output reg o_mem_wen, - output reg [7:0] o_mem_wdata, - output reg [aw-1:0] o_mem_waddr, - output reg [aw-1:0] o_mem_raddr, - output reg o_core_reset -); - localparam FRAME_BITS = 42; - - // Frame format (LSB-first on JTAG shift stream): - // [0] : core reset - // [1] : write (1=write, 0=read) - // [33:2] : address - // [41:34] : data - wire [31:0] cmd_addr_full; - wire cmd_reset; - wire cmd_write; - wire [7:0] cmd_data; - - // JTAG interface wires - wire jtag_tck; - wire jtag_tdi; - wire jtag_drck; - wire jtag_capture; - wire jtag_shift; - wire jtag_update; - wire jtag_runtest; - wire jtag_reset; - wire jtag_sel; - - // JTAG clock-domain regs - reg [FRAME_BITS-1:0] shift_q; - reg [FRAME_BITS-1:0] cmd_jtag_q; - reg update_toggle_jtag_q; - reg [FRAME_BITS-1:0] resp_meta_q; - reg [FRAME_BITS-1:0] resp_sync_q; - - // Core clock-domain CDC regs - reg update_toggle_meta_q; - reg update_toggle_sync_q; - reg update_toggle_sync_d_q; - reg [FRAME_BITS-1:0] cmd_meta_q; - reg [FRAME_BITS-1:0] cmd_sync_q; - - // Core clock-domain protocol state - reg [FRAME_BITS-1:0] resp_core_q; - reg read_pending_q; - reg [31:0] read_addr_full_q; - - assign cmd_reset = cmd_sync_q[0]; - assign cmd_write = cmd_sync_q[1]; - assign cmd_addr_full = cmd_sync_q[33:2]; - assign cmd_data = cmd_sync_q[41:34]; - - jtag_if #( - .chain(chain) - ) jtag ( - .i_tdo(shift_q[0]), - .o_tck(jtag_tck), - .o_tdi(jtag_tdi), - .o_drck(jtag_drck), - .o_capture(jtag_capture), - .o_shift(jtag_shift), - .o_update(jtag_update), - .o_runtest(jtag_runtest), - .o_reset(jtag_reset), - .o_sel(jtag_sel) - ); - - // JTAG domain: shift register and update event capture. - always @(posedge jtag_drck or posedge jtag_reset or posedge i_rst) begin - if (jtag_reset || i_rst) begin - shift_q <= {FRAME_BITS{1'b0}}; - resp_meta_q <= {FRAME_BITS{1'b0}}; - resp_sync_q <= {FRAME_BITS{1'b0}}; - end else begin - resp_meta_q <= resp_core_q; - resp_sync_q <= resp_meta_q; - - if (jtag_sel && jtag_capture) begin - shift_q <= resp_sync_q; - end else if (jtag_sel && jtag_shift) begin - shift_q <= {jtag_tdi, shift_q[FRAME_BITS-1:1]}; - end - end - end - - always @(posedge jtag_update or posedge jtag_reset or posedge i_rst) begin - if (jtag_reset || i_rst) begin - cmd_jtag_q <= {FRAME_BITS{1'b0}}; - update_toggle_jtag_q <= 1'b0; - end else if (jtag_sel) begin - cmd_jtag_q <= shift_q; - update_toggle_jtag_q <= ~update_toggle_jtag_q; - end - end - - // Core domain: receive command and execute against RAM port-B. - always @(posedge i_clk or posedge i_rst) begin - if (i_rst) begin - update_toggle_meta_q <= 1'b0; - update_toggle_sync_q <= 1'b0; - update_toggle_sync_d_q <= 1'b0; - cmd_meta_q <= {FRAME_BITS{1'b0}}; - cmd_sync_q <= {FRAME_BITS{1'b0}}; - o_mem_wen <= 1'b0; - o_mem_wdata <= 8'h00; - o_mem_waddr <= {aw{1'b0}}; - o_mem_raddr <= {aw{1'b0}}; - o_core_reset <= 1'b0; - resp_core_q <= {FRAME_BITS{1'b0}}; - read_pending_q <= 1'b0; - read_addr_full_q <= 32'h0000_0000; - end else begin - // Defaults are one-cycle strobes for write/reset. - o_mem_wen <= 1'b0; - o_core_reset <= 1'b0; - - update_toggle_meta_q <= update_toggle_jtag_q; - update_toggle_sync_q <= update_toggle_meta_q; - update_toggle_sync_d_q <= update_toggle_sync_q; - cmd_meta_q <= cmd_jtag_q; - cmd_sync_q <= cmd_meta_q; - - // Complete pending read (port-B read is synchronous). - if (read_pending_q) begin - read_pending_q <= 1'b0; - resp_core_q[0] <= 1'b0; - resp_core_q[1] <= 1'b0; - resp_core_q[33:2] <= read_addr_full_q; - resp_core_q[41:34] <= i_mem_rdata; - end - - // New JTAG command arrived. - if (update_toggle_sync_q ^ update_toggle_sync_d_q) begin - if (cmd_reset) begin - o_core_reset <= 1'b1; - end - - if (cmd_write) begin - o_mem_waddr <= cmd_addr_full[aw-1:0]; - o_mem_wdata <= cmd_data; - o_mem_wen <= 1'b1; - resp_core_q[0] <= cmd_reset; - resp_core_q[1] <= 1'b1; - resp_core_q[33:2] <= cmd_addr_full; - resp_core_q[41:34] <= cmd_data; - end else begin - o_mem_raddr <= cmd_addr_full[aw-1:0]; - read_pending_q <= 1'b1; - read_addr_full_q <= cmd_addr_full; - end - end - end - end - - // Keep lint quiet for currently-unused JTAG outputs. - wire _unused_tck; - wire _unused_runtest; - assign _unused_tck = jtag_tck; - assign _unused_runtest = jtag_runtest; -endmodule - module memory #( parameter memfile = "", parameter depth = 256, @@ -356,6 +188,7 @@ module memory #( localparam aw = `CLOG2(depth) )( input wire i_clk, + input wire i_rst, input wire [aw-1:0] i_waddr, input wire [7:0] i_wdata, input wire i_wen, @@ -364,38 +197,61 @@ module memory #( output wire o_core_reset ); // The actual memory - reg [7:0]mem [0:depth-1]; - - // Second interface - wire wen_b; - wire [7:0] wdata_b; - wire [aw-1:0] waddr_b; - reg [7:0] rdata_b; - wire [aw-1:0] raddr_b; - jtag_mem_protocol #( - .aw(aw), - .chain(1) - ) jtag_mem ( + reg [7:0] mem [0:depth-1]; + wire [aw-1:0] mem_adr; + assign mem_adr = (i_wen==1'b1) ? i_waddr : + i_raddr; + + // Second port wishbone + wire [31:0] wb_adr; + wire [31:0] wb_dat; + reg [31:0] wb_rdt; + wire [3:0] wb_sel; + wire wb_cyc; + wire wb_we; + wire wb_stb; + reg wb_ack; + reg wb_req_d; + wire cmd_reset; + // Driven by JTAG + jtag_wb_bridge #( + .chain(1), + .byte_aligned(1) + ) jtag_wb ( .i_clk(i_clk), - .i_rst(1'b0), - .i_mem_rdata(rdata_b), - .o_mem_wen(wen_b), - .o_mem_wdata(wdata_b), - .o_mem_waddr(waddr_b), - .o_mem_raddr(raddr_b), - .o_core_reset(o_core_reset) + .i_rst(i_rst), + + .o_wb_adr(wb_adr), + .o_wb_dat(wb_dat), + .o_wb_sel(wb_sel), + .o_wb_we(wb_we), + .o_wb_cyc(wb_cyc), + .o_wb_stb(wb_stb), + .i_wb_rdt(wb_rdt), + .i_wb_ack(wb_ack), + .o_cmd_reset(cmd_reset) ); + assign o_core_reset = cmd_reset; + // Read/Write always @(posedge i_clk) begin - // main interface - if (i_wen) - mem[i_waddr] <= i_wdata; - o_rdata <= mem[i_raddr]; - // second interface - if (wen_b) - mem[waddr_b] <= wdata_b; - rdata_b <= mem[raddr_b]; + if (i_rst) begin + wb_req_d <= 1'b0; + wb_ack <= 1'b0; + wb_rdt <= 32'h00000000; + o_rdata <= 32'h00000000; + end else begin + if (i_wen) + mem[mem_adr] <= i_wdata; + o_rdata <= mem[mem_adr]; + + wb_req_d <= wb_stb && wb_cyc; + wb_ack <= wb_req_d; + if (wb_we && wb_stb && wb_cyc) + mem[wb_adr[aw-1:0]] <= wb_dat[7:0]; + wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]}; + end end // Preload memory @@ -411,4 +267,6 @@ module memory #( end end + + endmodule diff --git a/rtl/toplevel/top_jtag.v b/rtl/toplevel/top_jtag.v index 9a730e1..87512b9 100644 --- a/rtl/toplevel/top_jtag.v +++ b/rtl/toplevel/top_jtag.v @@ -12,120 +12,64 @@ module top_jtag( wire clk_100; wire clk_15; assign clk_100 = aclk; - clk_gen clocking( + clk_gen clk( .clk_in(clk_100), .clk_out_15(clk_15) ); - localparam integer JTAG_DATA_BITS = 8; - wire [JTAG_DATA_BITS-1:0] jtag_data; + wire i_rst; + assign i_rst = !aresetn; - jtag_write_reg #( - .DATA_BITS(JTAG_DATA_BITS), - .CHAIN(1) - ) jtag_writer ( - .clk_i(clk_15), - .rst_n_i(aresetn), - .data_o(jtag_data) + wire [31:0] wb_adr; + wire [31:0] wb_dat; + wire [31:0] wb_rdt; + wire [3:0] wb_sel; + wire wb_cyc; + wire wb_we; + wire wb_stb; + wire wb_ack; + wire cmd_reset; + + jtag_wb_bridge #( + .chain(1) + ) jtag_wb ( + .i_clk(clk_15), + .i_rst(!aresetn), + + .o_wb_adr(wb_adr), + .o_wb_dat(wb_dat), + .o_wb_sel(wb_sel), + .o_wb_we(wb_we), + .o_wb_cyc(wb_cyc), + .o_wb_stb(wb_stb), + .i_wb_rdt(wb_rdt), + .i_wb_ack(wb_ack), + .o_cmd_reset(cmd_reset) ); - assign LED = jtag_data[7:0]; - assign led_green = jtag_data[0]; - assign led_red = jtag_data[7]; - assign r2r = jtag_data[5:0]; + wire [31:0] gpio; + wire [31:0] gpio_in; + assign gpio_in = 32'h0; -endmodule - -module jtag_write_reg #( - parameter integer DATA_BITS = 8, - parameter integer CHAIN = 1 -)( - input wire clk_i, - input wire rst_n_i, - output reg [DATA_BITS-1:0] data_o -); - wire jtag_tck; - wire jtag_tdi; - wire jtag_drck; - wire jtag_capture; - wire jtag_shift; - wire jtag_update; - wire jtag_runtest; - wire jtag_reset; - wire jtag_sel; - reg [DATA_BITS-1:0] shift_q; - reg [DATA_BITS-1:0] data_jtag_q; - reg update_toggle_jtag_q; - reg update_toggle_meta_q; - reg update_toggle_sync_q; - reg update_toggle_sync_d_q; - reg [DATA_BITS-1:0] data_meta_q; - reg [DATA_BITS-1:0] data_sync_q; - - jtag_if #( - .chain(CHAIN) - ) jtag ( - .i_tdo(shift_q[0]), - .o_tck(jtag_tck), - .o_tdi(jtag_tdi), - .o_drck(jtag_drck), - .o_capture(jtag_capture), - .o_shift(jtag_shift), - .o_update(jtag_update), - .o_runtest(jtag_runtest), - .o_reset(jtag_reset), - .o_sel(jtag_sel) + wb_gpio #( + .address(32'h00000000) + ) u_wb_gpio ( + .i_wb_clk(clk_15), + .i_wb_rst(i_rst | cmd_reset), + .i_wb_adr(wb_adr), + .i_wb_dat(wb_dat), + .i_wb_sel(wb_sel), + .i_wb_we(wb_we), + .i_wb_stb(wb_stb & wb_cyc), + .i_gpio(gpio_in), + .o_wb_rdt(wb_rdt), + .o_wb_ack(wb_ack), + .o_gpio(gpio) ); - always @(posedge jtag_drck or posedge jtag_reset or negedge rst_n_i) begin - if (!rst_n_i || jtag_reset) begin - shift_q <= {DATA_BITS{1'b0}}; - end else if (jtag_sel && jtag_capture) begin - shift_q <= data_jtag_q; - end else if (jtag_sel && jtag_shift) begin - if (DATA_BITS == 1) begin - shift_q <= jtag_tdi; - end else begin - shift_q <= {jtag_tdi, shift_q[DATA_BITS-1:1]}; - end - end - end + assign LED = gpio[7:0]; + assign r2r = gpio[13:8]; + assign led_green = gpio[30]; + assign led_red = gpio[31]; - always @(posedge jtag_update or posedge jtag_reset or negedge rst_n_i) begin - if (!rst_n_i || jtag_reset) begin - data_jtag_q <= {DATA_BITS{1'b0}}; - update_toggle_jtag_q <= 1'b0; - end else if (jtag_sel) begin - data_jtag_q <= shift_q; - update_toggle_jtag_q <= ~update_toggle_jtag_q; - end - end - - // CDC into clk_i domain: toggle synchronize + stable data sampling. - always @(posedge clk_i or negedge rst_n_i) begin - if (!rst_n_i) begin - update_toggle_meta_q <= 1'b0; - update_toggle_sync_q <= 1'b0; - update_toggle_sync_d_q <= 1'b0; - data_meta_q <= {DATA_BITS{1'b0}}; - data_sync_q <= {DATA_BITS{1'b0}}; - data_o <= {DATA_BITS{1'b0}}; - end else begin - update_toggle_meta_q <= update_toggle_jtag_q; - update_toggle_sync_q <= update_toggle_meta_q; - update_toggle_sync_d_q <= update_toggle_sync_q; - data_meta_q <= data_jtag_q; - data_sync_q <= data_meta_q; - - if (update_toggle_sync_q ^ update_toggle_sync_d_q) begin - data_o <= data_sync_q; - end - end - end - - // Not used for this simple write-register, but kept for completeness. - wire _unused_tck; - wire _unused_runtest; - assign _unused_tck = jtag_tck; - assign _unused_runtest = jtag_runtest; endmodule diff --git a/rtl/wb/jtag_wb_bridge.v b/rtl/wb/jtag_wb_bridge.v new file mode 100644 index 0000000..1240379 --- /dev/null +++ b/rtl/wb/jtag_wb_bridge.v @@ -0,0 +1,196 @@ +`timescale 1ns/1ps + +module jtag_wb_bridge #( + parameter integer chain = 1, + // 0: Use cmd_addr[1:0] to select byte lane on 32-bit WB data bus. + // 1: Always use lane 0 (LSB), for byte-wide memories that return data in [7:0]. + parameter integer byte_aligned = 0 +)( + input wire i_clk, + input wire i_rst, + + output wire [31:0] o_wb_adr, + output wire [31:0] o_wb_dat, + output wire [3:0] o_wb_sel, + output wire o_wb_we, + output wire o_wb_cyc, + output wire o_wb_stb, + input wire [31:0] i_wb_rdt, + input wire i_wb_ack, + + output wire o_cmd_reset +); + // JTAG interface wires + wire jtag_tck; + wire jtag_tdi; + wire jtag_drck; + wire jtag_capture; + wire jtag_shift; + wire jtag_update; + wire jtag_runtest; + wire jtag_reset; + wire jtag_sel; + + reg [41:0] jtag_q; + wire [41:0] jtag_data_in; + wire jtag_async_reset; + + jtag_if #( + .chain(chain) + ) jtag ( + .i_tdo(jtag_q[0]), + .o_tck(jtag_tck), + .o_tdi(jtag_tdi), + .o_drck(jtag_drck), + .o_capture(jtag_capture), + .o_shift(jtag_shift), + .o_update(jtag_update), + .o_runtest(jtag_runtest), + .o_reset(jtag_reset), + .o_sel(jtag_sel) + ); + + assign jtag_async_reset = jtag_reset || i_rst; + + // JTAG shift register behavior + always @(posedge jtag_drck or posedge jtag_async_reset) begin + if (jtag_async_reset) begin + jtag_q <= 42'b0; + end else if (jtag_sel && jtag_capture) begin + jtag_q <= jtag_data_in; + end else if (jtag_sel && jtag_shift) begin + jtag_q <= {jtag_tdi, jtag_q[41:1]}; + end + end + + // ----------------------------------------------------------------------------- + // JTAG -> i_clk crossing using toggle request/ack handshake. + // Command packet format: [41]=we, [40]=reset, [39:8]=addr, [7:0]=wdata + // ----------------------------------------------------------------------------- + reg [41:0] j_cmd_hold; + reg j_req_tgl; + reg j_ack_sync_1; + reg j_ack_sync_2; + + reg s_ack_tgl; + reg s_req_sync_1; + reg s_req_sync_2; + reg s_req_sync_3; + reg [41:0] s_cmd_sync_1; + reg [41:0] s_cmd_sync_2; + + always @(posedge jtag_drck or posedge jtag_async_reset) begin + if (jtag_async_reset) begin + j_ack_sync_1 <= 1'b0; + j_ack_sync_2 <= 1'b0; + end else begin + j_ack_sync_1 <= s_ack_tgl; + j_ack_sync_2 <= j_ack_sync_1; + end + end + + always @(posedge jtag_update or posedge jtag_async_reset) begin + if (jtag_async_reset) begin + j_cmd_hold <= 42'b0; + j_req_tgl <= 1'b0; + end else if (jtag_sel && (j_ack_sync_2 == j_req_tgl)) begin + j_cmd_hold <= jtag_q; + j_req_tgl <= ~j_req_tgl; + end + end + + // ----------------------------------------------------------------------------- + // Wishbone classic single-request master (1 outstanding transaction max). + // ----------------------------------------------------------------------------- + reg wb_busy; + reg [31:0] wb_adr_r; + reg [31:0] wb_dat_r; + reg [3:0] wb_sel_r; + reg wb_we_r; + reg cmd_reset_pulse_r; + reg [31:0] resp_addr_r; + reg [7:0] resp_data_r; + + wire req_pulse; + wire [7:0] cmd_wdata; + wire [31:0] cmd_addr; + wire cmd_reset; + wire cmd_we; + wire [1:0] req_lane; + wire [1:0] resp_lane; + + assign req_pulse = s_req_sync_2 ^ s_req_sync_3; + assign cmd_wdata = s_cmd_sync_2[7:0]; + assign cmd_addr = s_cmd_sync_2[39:8]; + assign cmd_reset = s_cmd_sync_2[40]; + assign cmd_we = s_cmd_sync_2[41]; + assign req_lane = byte_aligned ? 2'b00 : cmd_addr[1:0]; + assign resp_lane = byte_aligned ? 2'b00 : wb_adr_r[1:0]; + + assign o_wb_adr = wb_adr_r; + assign o_wb_dat = wb_dat_r; + assign o_wb_sel = wb_sel_r; + assign o_wb_we = wb_we_r; + assign o_wb_cyc = wb_busy; + assign o_wb_stb = wb_busy; + assign o_cmd_reset = cmd_reset_pulse_r; + + always @(posedge i_clk) begin + if (i_rst) begin + s_ack_tgl <= 1'b0; + s_req_sync_1 <= 1'b0; + s_req_sync_2 <= 1'b0; + s_req_sync_3 <= 1'b0; + s_cmd_sync_1 <= 42'b0; + s_cmd_sync_2 <= 42'b0; + wb_busy <= 1'b0; + wb_adr_r <= 32'b0; + wb_dat_r <= 32'b0; + wb_sel_r <= 4'b0000; + wb_we_r <= 1'b0; + cmd_reset_pulse_r <= 1'b0; + resp_addr_r <= 32'b0; + resp_data_r <= 8'b0; + end else begin + s_req_sync_1 <= j_req_tgl; + s_req_sync_2 <= s_req_sync_1; + s_req_sync_3 <= s_req_sync_2; + s_cmd_sync_1 <= j_cmd_hold; + s_cmd_sync_2 <= s_cmd_sync_1; + cmd_reset_pulse_r <= 1'b0; + + if (req_pulse && !wb_busy) begin + wb_busy <= 1'b1; + wb_we_r <= cmd_we; + wb_adr_r <= cmd_addr; + + case (req_lane) + 2'b00: begin wb_sel_r <= 4'b0001; wb_dat_r <= {24'b0, cmd_wdata}; end + 2'b01: begin wb_sel_r <= 4'b0010; wb_dat_r <= {16'b0, cmd_wdata, 8'b0}; end + 2'b10: begin wb_sel_r <= 4'b0100; wb_dat_r <= {8'b0, cmd_wdata, 16'b0}; end + default: begin wb_sel_r <= 4'b1000; wb_dat_r <= {cmd_wdata, 24'b0}; end + endcase + + cmd_reset_pulse_r <= cmd_reset; + end + + if (wb_busy && i_wb_ack) begin + wb_busy <= 1'b0; + wb_we_r <= 1'b0; + resp_addr_r <= wb_adr_r; + + case (resp_lane) + 2'b00: resp_data_r <= i_wb_rdt[7:0]; + 2'b01: resp_data_r <= i_wb_rdt[15:8]; + 2'b10: resp_data_r <= i_wb_rdt[23:16]; + default: resp_data_r <= i_wb_rdt[31:24]; + endcase + + s_ack_tgl <= s_req_sync_2; + end + end + end + + assign jtag_data_in = {2'b00, resp_addr_r, resp_data_r}; + +endmodule diff --git a/sim/other/test.svf b/sim/other/test.svf index 90808c7..6a3d507 100644 --- a/sim/other/test.svf +++ b/sim/other/test.svf @@ -5,21 +5,6 @@ STATE RESET; STATE IDLE; SIR 6 TDI (02); -SDR 8 TDI (07); -SDR 8 TDI (4C); -SDR 8 TDI (43); -SDR 8 TDI (D1); -SDR 8 TDI (97); -SDR 8 TDI (6B); -SDR 8 TDI (3D); -SDR 8 TDI (E4); -SDR 8 TDI (20); -SDR 8 TDI (3A); -SDR 8 TDI (8E); -SDR 8 TDI (1D); -SDR 8 TDI (9D); -SDR 8 TDI (E4); +SDR 42 TDI (3A987654321); -RUNTEST 16 TCK; -STATE RESET; STATE IDLE; diff --git a/sim/tb/tb_svf.v b/sim/tb/tb_svf.v index 001c5d6..bed2b5b 100644 --- a/sim/tb/tb_svf.v +++ b/sim/tb/tb_svf.v @@ -1,24 +1,28 @@ `timescale 1ns/1ps module tb_svf(); + reg clk; + reg resetn; + initial clk <= 1'b0; + initial resetn <= 1'b0; + always #33.33 clk <= !clk; + initial #40 resetn <= 1'b1; + wire [7:0] led_out; - wire [31:0] update_count; jtag_byte_sink #( .SVF_FILE("sim/other/test.svf"), - .TCK_HALF_PERIOD_NS(50) + .TCK_HALF_PERIOD_NS(500) ) dut ( - .o_led(led_out), - .o_update_count(update_count) + .i_clk(clk), + .i_rst(!resetn), + .o_led(led_out) ); initial begin - // Optional waveform dump. $dumpfile("out.vcd"); $dumpvars; - - // Timeout for large SVF playback. - #20_000; + #200_000; $finish; end endmodule @@ -27,9 +31,14 @@ module jtag_byte_sink #( parameter [8*256-1:0] SVF_FILE = "", parameter integer TCK_HALF_PERIOD_NS = 50 )( - output reg [7:0] o_led, - output reg [31:0] o_update_count + input wire i_clk, + input wire i_rst, + output reg [7:0] o_led ); + + initial o_led <= 0; + + // JTAG interface wires wire jtag_tck; wire jtag_tdi; wire jtag_drck; @@ -39,10 +48,10 @@ module jtag_byte_sink #( wire jtag_runtest; wire jtag_reset; wire jtag_sel; - wire jtag_tdo; - reg [7:0] shift_reg; - assign jtag_tdo = shift_reg[0]; + reg [41:0] jtag_q; + reg [41:0] jtag_data; + wire jtag_async_reset; jtag_if #( .chain(1), @@ -50,7 +59,7 @@ module jtag_byte_sink #( .TCK_HALF_PERIOD_NS(TCK_HALF_PERIOD_NS), .USER_IR_OPCODE(32'h0000_0002) ) jtag ( - .i_tdo(jtag_tdo), + .i_tdo(jtag_q[0]), .o_tck(jtag_tck), .o_tdi(jtag_tdi), .o_drck(jtag_drck), @@ -62,29 +71,36 @@ module jtag_byte_sink #( .o_sel(jtag_sel) ); - always @(posedge jtag_drck or posedge jtag_reset) begin - if (jtag_reset) begin - shift_reg <= 8'h00; + + assign jtag_async_reset = jtag_reset || i_rst; + + always @(posedge jtag_drck or posedge jtag_async_reset) begin + if (jtag_async_reset) begin + jtag_q <= 0; end else if (jtag_sel && jtag_capture) begin - shift_reg <= o_led; + jtag_q <= jtag_data; end else if (jtag_sel && jtag_shift) begin - shift_reg <= {jtag_tdi, shift_reg[7:1]}; + jtag_q <= {jtag_tdi, jtag_q[41:1]}; end end - always @(posedge jtag_update or posedge jtag_reset) begin - if (jtag_reset) begin - o_led <= 8'h00; - o_update_count <= 32'd0; + always @(posedge jtag_update or posedge jtag_async_reset) begin + if (jtag_async_reset) begin + jtag_data <= 0; end else if (jtag_sel) begin - o_led <= shift_reg; - o_update_count <= o_update_count + 1'b1; + jtag_data <= jtag_q; end end - // Keep lint happy for currently unused outputs. - wire _unused_tck; - wire _unused_runtest; - assign _unused_tck = jtag_tck; - assign _unused_runtest = jtag_runtest; + wire [41:0] j_data; + wire j_data_update; + cdc_strobed #(42) j_data_cdc ( + .i_clk_a(i_clk), + .i_clk_b(i_clk), + .i_data(jtag_data), + .i_strobe(jtag_update), + .o_data(j_data), + .o_strobe(j_data_update) + ); + endmodule diff --git a/tools/Makefile b/tools/Makefile index 6e30c9e..c09970c 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -4,7 +4,7 @@ CC := $(TOOLCHAIN_PREFIX)g++ TARGET := test SRCS_C := -SRCS_CPP:= test.cpp digilent_jtag.cpp +SRCS_CPP:= test.cpp digilent_jtag.cpp argparse.cpp OBJS := $(SRCS_C:.c=.o) $(SRCS_CPP:.cpp=.o) ADEPT_LIBDIR := /opt/packages/digilent.adept.runtime_2.27.9-x86_64/lib64 @@ -29,4 +29,4 @@ $(TARGET): $(OBJS) $(CC) $(CFLAGS) -c -o $@ $< clean: - rm -f $(TARGET) $(OBJS) \ No newline at end of file + rm -f $(TARGET) $(OBJS) diff --git a/tools/argparse.cpp b/tools/argparse.cpp new file mode 100644 index 0000000..02ef1a7 --- /dev/null +++ b/tools/argparse.cpp @@ -0,0 +1,158 @@ +#include "argparse.hpp" + +#include +#include +#include +#include +#include + +ArgParser::ArgParser(std::string program_name) + : program_name_(std::move(program_name)) {} + +void ArgParser::addString(const std::string &name, + const std::string &default_value, + const std::string &help, + bool required) { + order_.push_back(name); + meta_[name] = {OptionType::kString, help, required}; + string_values_[name] = default_value; + provided_[name] = false; +} + +void ArgParser::addInt(const std::string &name, + int default_value, + const std::string &help, + bool required) { + order_.push_back(name); + meta_[name] = {OptionType::kInt, help, required}; + int_values_[name] = default_value; + provided_[name] = false; +} + +bool ArgParser::parse(int argc, char **argv, std::string *error) { + for (int i = 1; i < argc; ++i) { + std::string token(argv[i]); + if (token == "--help" || token == "-h") { + if (error) { + *error = "help"; + } + return false; + } + + if (token.rfind("--", 0) != 0) { + if (error) { + *error = "Unexpected positional argument: " + token; + } + return false; + } + + std::string key; + std::string value; + size_t eq = token.find('='); + if (eq == std::string::npos) { + key = token.substr(2); + if (i + 1 >= argc) { + if (error) { + *error = "Missing value for --" + key; + } + return false; + } + value = argv[++i]; + } else { + key = token.substr(2, eq - 2); + value = token.substr(eq + 1); + } + + auto m = meta_.find(key); + if (m == meta_.end()) { + if (error) { + *error = "Unknown option: --" + key; + } + return false; + } + + if (m->second.type == OptionType::kString) { + string_values_[key] = value; + provided_[key] = true; + } else { + errno = 0; + char *endp = nullptr; + long parsed = std::strtol(value.c_str(), &endp, 0); + if (errno != 0 || endp == value.c_str() || *endp != '\0' || + parsed < INT_MIN || parsed > INT_MAX) { + if (error) { + *error = "Invalid integer for --" + key + ": " + value; + } + return false; + } + int_values_[key] = static_cast(parsed); + provided_[key] = true; + } + } + + for (const auto &key : order_) { + auto m = meta_.find(key); + if (m != meta_.end() && m->second.required && !has(key)) { + if (error) { + *error = "Missing required option: --" + key; + } + return false; + } + } + + return true; +} + +bool ArgParser::has(const std::string &name) const { + auto p = provided_.find(name); + return p != provided_.end() && p->second; +} + +std::string ArgParser::getString(const std::string &name) const { + auto it = string_values_.find(name); + if (it == string_values_.end()) { + return std::string(); + } + return it->second; +} + +int ArgParser::getInt(const std::string &name) const { + auto it = int_values_.find(name); + if (it == int_values_.end()) { + return 0; + } + return it->second; +} + +std::string ArgParser::helpText() const { + std::ostringstream oss; + oss << "Usage: " << program_name_ << " [options]\n\n"; + oss << "Options:\n"; + oss << " -h, --help Show this help\n"; + for (const auto &key : order_) { + auto m = meta_.find(key); + if (m == meta_.end()) { + continue; + } + + oss << " --" << key << " "; + if (m->second.required) { + oss << " (required)"; + } + oss << "\n"; + oss << " " << m->second.help; + if (m->second.type == OptionType::kString) { + auto s = string_values_.find(key); + if (s != string_values_.end()) { + oss << " [default: '" << s->second << "']"; + } + } else { + auto iv = int_values_.find(key); + if (iv != int_values_.end()) { + oss << " [default: " << iv->second << "]"; + } + } + oss << "\n"; + } + return oss.str(); +} diff --git a/tools/argparse.hpp b/tools/argparse.hpp new file mode 100644 index 0000000..098cbb7 --- /dev/null +++ b/tools/argparse.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include + +class ArgParser { +public: + struct StringOption { + std::string name; + std::string default_value; + std::string help; + bool required; + }; + + struct IntOption { + std::string name; + int default_value; + std::string help; + bool required; + }; + + explicit ArgParser(std::string program_name); + + void addString(const std::string &name, + const std::string &default_value, + const std::string &help, + bool required = false); + + void addInt(const std::string &name, + int default_value, + const std::string &help, + bool required = false); + + bool parse(int argc, char **argv, std::string *error); + + bool has(const std::string &name) const; + std::string getString(const std::string &name) const; + int getInt(const std::string &name) const; + + std::string helpText() const; + +private: + enum class OptionType { + kString, + kInt + }; + + struct OptionMeta { + OptionType type; + std::string help; + bool required; + }; + + std::string program_name_; + std::vector order_; + std::unordered_map meta_; + + std::unordered_map string_values_; + std::unordered_map int_values_; + std::unordered_map provided_; +}; diff --git a/tools/test.cpp b/tools/test.cpp index 27f48c1..72b363e 100644 --- a/tools/test.cpp +++ b/tools/test.cpp @@ -1,19 +1,90 @@ #include "digilent_jtag.hpp" +#include "argparse.hpp" + +#include +#include +#include + +void write(DigilentJtag &jtag, uint32_t addr, uint8_t data, bool reset){ + uint8_t d[6], din[6]; + d[0] = data; + d[1] = (uint8_t)addr; + d[2] = (uint8_t)(addr>>8); + d[3] = (uint8_t)(addr>>16); + d[4] = (uint8_t)(addr>>24); + d[5] = ((reset) ? 1 : 0) | 0x2; // + + jtag.shiftData(d, din, 42); +} + +uint8_t read(DigilentJtag &jtag, uint32_t addr, bool reset){ + uint8_t d[6], din[6]; + d[0] = 0xff; + d[1] = (uint8_t)addr; + d[2] = (uint8_t)(addr>>8); + d[3] = (uint8_t)(addr>>16); + d[4] = (uint8_t)(addr>>24); + d[5] = ((reset) ? 1 : 0); // + + jtag.shiftData(d, din, 42); + // Read back + jtag.shiftData(d, din, 42); + + return din[0]; +} int main(int argc, char** argv){ + ArgParser parser(argc > 0 ? argv[0] : "test"); + parser.addString("write", "", "file to write"); + + std::string parse_error; + if (!parser.parse(argc, argv, &parse_error)) { + if (parse_error == "help") { + std::printf("%s", parser.helpText().c_str()); + return 0; + } + std::printf("Argument error: %s\n\n", parse_error.c_str()); + std::printf("%s", parser.helpText().c_str()); + return -1; + } + + const std::string arg_write = parser.getString("write"); DigilentJtag jtag; if(!jtag.open()){ printf("Could not open programmer\r\n"); return -1; } - jtag.setChain(1); - - uint8_t data_out = 0xAB; - uint8_t data_in; - jtag.shiftData(&data_out, &data_in, 8); + + // Start reset + read(jtag, 0, true); + + if(arg_write!=""){ + uint32_t addr = 0; + uint8_t buf[32]; + int nr; + FILE* f = fopen(arg_write.c_str(), "rb"); + if(!f){ + goto end; + } + + do{ + nr = fread(buf, 1, 32, f); + for(int i=0; i<32; i++){ + write(jtag, addr, buf[i], true); + addr++; + } + }while(nr>0); + + fclose(f); + } + + +end: + // End reset + read(jtag, 0, false); jtag.close(); return 0; -} \ No newline at end of file +}