From 971a8db4e903ee8ccf488a45fc7c3ab0d4efdb3f Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Sun, 11 May 2025 08:09:02 +0200 Subject: [PATCH] spiOverJtag: introduce a new spiOverJtag (v2) core able to work with complex JTAG chain --- spiOverJtag/Makefile | 2 +- spiOverJtag/build.py | 3 + spiOverJtag/spiOverJtag_core.v | 250 ++++++++++++++++++++++++++ spiOverJtag/xilinx_spiOverJtag.v | 290 +++++++++++++++++-------------- 4 files changed, 409 insertions(+), 136 deletions(-) create mode 100644 spiOverJtag/spiOverJtag_core.v diff --git a/spiOverJtag/Makefile b/spiOverJtag/Makefile index d1dec0d..d599332 100644 --- a/spiOverJtag/Makefile +++ b/spiOverJtag/Makefile @@ -40,7 +40,7 @@ tmp_efinix_%/efinix_spiOverJtag.bit : efinix_spiOverJtag.v $(XILINX_BIT_FILES) : spiOverJtag_%.bit.gz : tmp_%/spiOverJtag.bit gzip -9 -c $< > $@ -tmp_%/spiOverJtag.bit : xilinx_spiOverJtag.v +tmp_%/spiOverJtag.bit : xilinx_spiOverJtag.v spiOverJtag_core.v ./build.py $* $(ALTERA_BIT_FILES): spiOverJtag_%.rbf.gz: tmp_%/spiOverJtag.rbf diff --git a/spiOverJtag/build.py b/spiOverJtag/build.py index 534a350..e5352fb 100755 --- a/spiOverJtag/build.py +++ b/spiOverJtag/build.py @@ -223,6 +223,9 @@ else: 'file_type': 'SDC'}) tool_options = {'device': full_part, 'family':family} +files.append({'name': currDir + 'spiOverJtag_core.v', + 'file_type': 'verilogSource'}) + parameters[family.lower().replace(' ', '')]= { 'datatype': 'int', 'paramtype': 'vlogdefine', diff --git a/spiOverJtag/spiOverJtag_core.v b/spiOverJtag/spiOverJtag_core.v new file mode 100644 index 0000000..45c78ed --- /dev/null +++ b/spiOverJtag/spiOverJtag_core.v @@ -0,0 +1,250 @@ +`default_nettype none +/* + * JTAG: rising edge: sampling + * falling esge: update + * SPI: + */ + +module spiOverJtag_core ( + /* JTAG state/controls */ + input wire sel, + input wire capture, + input wire update, + input wire shift, + input wire drck, + input wire tdi, + output wire tdo, + + /* JTAG endpoint to version */ + input wire ver_sel, + input wire ver_cap, + input wire ver_shift, + input wire ver_drck, + input wire ver_tdi, + output wire ver_tdo, + /* phys */ + output reg csn, + output wire sck, + output reg sdi_dq0, + input wire sdo_dq1, + output wire wpn_dq2, + output wire hldn_dq3, + + /* debug signals */ + output wire [ 6:0] dbg_header1, + output wire [13:0] dbg_header, + output wire [ 3:0] dbg_hdr_cnt, + output wire [ 2:0] dbg_jtag_state, + output wire dbg_rst, + output wire dbg_clk, + output wire dbg_start_header, + + output wire dbg_ver_start, + output wire dbg_ver_rst, + output wire dbg_ver_state, + output wire [15:0] dbg_ver_cnt, + output wire [39:0] dbg_ver_shft +); + +/* no global reset at start time: + * reset system at capture time (before shift) + * and at update time (after shift) + */ +wire rst = ((capture | update) & sel); + +/* + * if the FPGAs executing this code is somewhere in a complex + * JTAG chain first bit isn't necessary for him + * Fortunately dummy bits sent are equal to '0' -> we sent a 'start bit' + */ +wire start_header = (tdi & shift & sel); + +localparam hdr_len = 16; +reg [hdr_len-1:0] header; /* number of bits to receive / send in XFER state */ +reg [hdr_len-1:0] header_d; +/* Primary header with mode and length LSB + * 6:5: mode (00: normal, 01: no header2, 10: infinite loop) + * 4:0: Byte length LSB + */ +reg [ 6:0] header1; +reg [ 6:0] header1_d; +wire [ 6:0] header1_next = {tdi, header1[6:1]}; +wire [ 1:0] mode = header1[1:0]; +/* Secondary header with extended length */ +wire [hdr_len-1:0] header_next = {tdi, header[hdr_len-1:1]}; +reg [ 3:0] hdr_cnt; /* counter of bit received in RECV_HEADERx states */ +reg [ 3:0] hdr_cnt_d; + +/* ---------------- */ +/* FSM */ +/* ---------------- */ + +localparam IDLE = 3'b000, + RECV_HEADER1 = 3'b001, + RECV_HEADER2 = 3'b010, + XFER = 3'b011, + WAIT_END = 3'b100; + +reg [2:0] jtag_state, jtag_state_d; + +/* + * 1. receives 8bits ( + */ +always @(*) begin + jtag_state_d = jtag_state; + hdr_cnt_d = hdr_cnt; + header_d = header; + header1_d = header1; + case (jtag_state) + IDLE: begin /* nothing: wait for the 'start bit' */ + hdr_cnt_d = 6; + if (start_header) begin + jtag_state_d = RECV_HEADER1; + end + end + RECV_HEADER1: begin /* first header with 1:0 : mode, 6:2: XFER length (LSB) */ + hdr_cnt_d = hdr_cnt - 1'b1; + header1_d = header1_next; + if (hdr_cnt == 0) begin + if (header1_next[1:0] == 2'b00) begin + hdr_cnt_d = 7; + header_d = {header1_next[6:2], 3'b000, 8'd0}; + jtag_state_d = RECV_HEADER2; + end else begin + header_d = {8'b0, header1_next[6:2], 3'b000}; + jtag_state_d = XFER; + end + end + end + + RECV_HEADER2: begin /* fill a counter with 16bits (number of bits to pass to the flash) */ + hdr_cnt_d = hdr_cnt - 1'b1; + header_d = header_next; + if (hdr_cnt == 0) begin + jtag_state_d = XFER; + end + end + XFER: begin + header_d = header - 1; + if (header == 1 && mode != 2'b10) + jtag_state_d = WAIT_END; + end + WAIT_END: begin /* move to this state when header bits have been transfered to the SPI flash */ + // /* nothing to do: rst will move automagically state in IDLE */ + end + default: begin + jtag_state_d = IDLE; + end + endcase +end + +always @(posedge drck) begin + header <= header_d; + header1 <= header1_d; + hdr_cnt <= hdr_cnt_d; +end + +always @(posedge drck or posedge rst) begin + if (rst) begin + jtag_state <= IDLE; + end else begin + jtag_state <= jtag_state_d; + end +end + +/* JTAG <-> phy SPI */ +always @(posedge drck or posedge rst) begin + if (rst) begin + sdi_dq0 <= 1'b0; + csn <= 1'b1; + end else begin + sdi_dq0 <= tdi; + csn <= ~(jtag_state == XFER); + end +end + +assign sck = ~drck; +assign tdo = sdo_dq1; +assign wpn_dq2 = 1'b1; +assign hldn_dq3 = 1'b1; + +/* ------------- */ +/* Version */ +/* ------------- */ +/* no global reset at start time: reset system at capture time (before shift) */ +wire ver_rst = (ver_cap & ver_sel); + +/* start bit */ +wire ver_start = (ver_tdi & ver_shift & ver_sel); + +localparam VER_VALUE = 40'h30_30_2E_32_30; // 02.00 +reg [ 6:0] ver_cnt, ver_cnt_d; +reg [39:0] ver_shft, ver_shft_d; + +reg [2:0] ver_state, ver_state_d; +always @(*) begin + ver_state_d = ver_state; + ver_cnt_d = ver_cnt; + ver_shft_d = ver_shft; + case (ver_state) + IDLE: begin /* nothing: wait for the 'start bit' */ + ver_cnt_d = 6; + if (ver_start) begin + ver_state_d = RECV_HEADER1; + end + end + RECV_HEADER1: begin + ver_cnt_d = ver_cnt - 1'b1; + if (ver_cnt == 0) begin + ver_state_d = XFER; + ver_cnt_d = 39; + ver_shft_d = VER_VALUE; + end + end + XFER: begin + ver_cnt_d = ver_cnt - 1; + ver_shft_d = {1'b1, ver_shft[39:1]}; + if (ver_cnt == 0) + ver_state_d = WAIT_END; + end + WAIT_END: begin /* move to this state when header bits have been transfered to the SPI flash */ + // /* nothing to do: rst will move automagically state in IDLE */ + end + default: begin + ver_state_d = IDLE; + end + endcase +end + +always @(posedge ver_drck) begin + ver_cnt <= ver_cnt_d; + ver_shft <= ver_shft_d; +end + +always @(posedge ver_drck or posedge ver_rst) begin + if (ver_rst) + ver_state <= IDLE; + else + ver_state <= ver_state_d; +end + +assign ver_tdo = ver_shft[0]; + +/* --------- */ +/* debug */ +/* --------- */ +assign dbg_header1 = header1; +assign dbg_header = header; +assign dbg_hdr_cnt = hdr_cnt; +assign dbg_jtag_state = jtag_state; +assign dbg_rst = rst; +assign dbg_clk = ~drck; +assign dbg_start_header = start_header; + +assign dbg_ver_start = ver_start; +assign dbg_ver_state = ver_state; +assign dbg_ver_cnt = ver_cnt; +assign dbg_ver_shft = ver_shft; +assign dbg_ver_rst = ver_rst; + +endmodule diff --git a/spiOverJtag/xilinx_spiOverJtag.v b/spiOverJtag/xilinx_spiOverJtag.v index 95baf68..4b168ff 100644 --- a/spiOverJtag/xilinx_spiOverJtag.v +++ b/spiOverJtag/xilinx_spiOverJtag.v @@ -1,85 +1,83 @@ +`default_nettype none module spiOverJtag ( +`ifndef xilinxultrascale + output wire csn, + `ifdef spartan6 - output sck, - output csn, - output sdi_dq0, - input sdo_dq1, - output wpn_dq2, - output hldn_dq3 -`define QSPI -`elsif spartan3e - output sck, - output csn, - output sdi_dq0, - input sdo_dq1 -`elsif virtex6 - output csn, - output sdi_dq0 -`elsif xilinxultrascale -`else - // Xilinx 7 but not ultrascale - output csn, - output sdi_dq0, - input sdo_dq1, - output wpn_dq2, - output hldn_dq3 -`define QSPI + output wire sck, `endif +`ifdef spartan3e + output wire sck, +`endif + output wire sdi_dq0, + input wire sdo_dq1, + output wire wpn_dq2, + output wire hldn_dq3 +`endif // xilinxultrascale `ifdef secondaryflash - output sdi_sec_dq0, - input sdo_sec_dq1, - output wpn_sec_dq2, - output hldn_sec_dq3, - output csn_sec + output wire sdi_sec_dq0, + input wire sdo_sec_dq1, + output wire wpn_sec_dq2, + output wire hldn_sec_dq3, + output wire csn_sec `endif // secondaryflash ); - wire capture, drck, sel, update; - wire runtest; - wire tdi; - reg fsm_csn; + wire capture, drck, sel, update, shift; + wire tdi, tdo; + wire spi_clk; -`ifdef QSPI - assign wpn_dq2 = 1'b1; - assign hldn_dq3 = 1'b1; +`ifndef spartan3e + /* Version Interface. */ + wire ver_sel, ver_cap, ver_shift, ver_drck, ver_tdi, ver_tdo; + + spiOverJtag_core spiOverJtag_core_prim ( + /* JTAG state/controls */ + .sel(sel), + .capture(capture), + .update(update), + .shift(shift), + .drck(drck), + .tdi(tdi), + .tdo(tdo), + + /* JTAG endpoint to version */ + .ver_sel(ver_sel), + .ver_cap(ver_cap), + .ver_shift(ver_shift), + .ver_drck(ver_drck), + .ver_tdi(ver_tdi), + .ver_tdo(ver_tdo), + + /* phys */ + .csn(csn), + .sck(spi_clk), + .sdi_dq0(sdi_dq0), + .sdo_dq1(sdo_dq1), + .wpn_dq2(wpn_dq2), + .hldn_dq3(hldn_dq3) + ); `endif - // jtag -> spi flash - assign sdi_dq0 = tdi; -`ifdef virtex6 - wire di; - wire tdo = (sel) ? di : tdi; -`else - wire tdo = (sel) ? sdo_dq1 : tdi; -`endif - assign csn = fsm_csn; - - wire tmp_cap_s = capture && sel; - wire tmp_up_s = update && sel; - - always @(posedge drck, posedge runtest) begin - if (runtest) begin - fsm_csn <= 1'b1; - end else begin - if (tmp_cap_s) begin - fsm_csn <= 1'b0; - end else if (tmp_up_s) begin - fsm_csn <= 1'b1; - end else begin - fsm_csn <= fsm_csn; - end - end - end +`ifdef spartan6 + assign sck = spi_clk; +`else // !spartan6 +`ifdef spartan3e + assign sck = spi_clk; +`else // !spartan6 && !spartan3e `ifdef xilinxultrascale - assign sck = drck; + assign sck = drck; wire [3:0] di; - assign wpn_dq2 = 1'b1; - assign hldn_dq3 = 1'b1; assign sdo_dq1 = di[1]; wire [3:0] do = {hldn_dq3, wpn_dq2, 1'b0, sdi_dq0}; wire [3:0] dts = 4'b0010; + // secondary BSCANE3 signals + wire sel_sec, spi_clk_sec; + + wire sck = (sel_sec) ? spi_clk_sec : spi_clk; + STARTUPE3 #( .PROG_USR("FALSE"), // Activate program event security feature. Requires encrypted bitstreams. .SIM_CCLK_FREQ(0.0) // Set the Configuration Clock Frequency (ns) for simulation. @@ -102,53 +100,57 @@ module spiOverJtag .USRDONEO (1'b1), // 1-bit input: User DONE pin output control. .USRDONETS(1'b1) // 1-bit input: User DONE 3-state enable output. ); -`elsif spartan3e - assign sck = drck; - assign runtest = tmp_up_s; -`elsif spartan6 - assign sck = drck; -`elsif virtex6 - STARTUP_VIRTEX6 #( - .PROG_USR("FALSE") - ) startup_virtex6_inst ( - .CFGCLK(), // unused - .CFGMCLK(), // unused - .CLK(1'b0), // unused - .DINSPI(di), // data from SPI flash - .EOS(), - .GSR(1'b0), // unused - .GTS(1'b0), // unused - .KEYCLEARB(1'b0), // not used - .PACK(1'b1), // tied low for 'safe' operations - .PREQ(), // unused - .TCKSPI(), // echo of CCLK from TCK pin - .USRCCLKO (drck), // user FPGA -> CCLK pin - .USRCCLKTS(1'b0), // drive CCLK not in high-Z - .USRDONEO (1'b1), // why both USRDONE are high? - .USRDONETS(1'b1) // ?? - ); -`else +`else // !spartan6 && !spartan3e && !xilinxultrascale STARTUPE2 #( .PROG_USR("FALSE"), // Activate program event security feature. Requires encrypted bitstreams. .SIM_CCLK_FREQ(0.0) // Set the Configuration Clock Frequency(ns) for simulation. ) startupe2_inst ( - .CFGCLK (), // 1-bit output: Configuration main clock output - .CFGMCLK (), // 1-bit output: Configuration internal oscillator clock output - .EOS (), // 1-bit output: Active high output signal indicating the End Of Startup. - .PREQ (), // 1-bit output: PROGRAM request to fabric output - .CLK (1'b0), // 1-bit input: User start-up clock input - .GSR (1'b0), // 1-bit input: Global Set/Reset input (GSR cannot be used for the port name) - .GTS (1'b0), // 1-bit input: Global 3-state input (GTS cannot be used for the port name) - .KEYCLEARB(1'b0), // 1-bit input: Clear AES Decrypter Key input from Battery-Backed RAM (BBRAM) - .PACK (1'b1), // 1-bit input: PROGRAM acknowledge input - .USRCCLKO (drck), // 1-bit input: User CCLK input - .USRCCLKTS(1'b0), // 1-bit input: User CCLK 3-state enable input - .USRDONEO (1'b1), // 1-bit input: User DONE pin output control - .USRDONETS(1'b1) // 1-bit input: User DONE 3-state enable output + .CFGCLK (), // 1-bit output: Configuration main clock output + .CFGMCLK (), // 1-bit output: Configuration internal oscillator clock output + .EOS (), // 1-bit output: Active high output signal indicating the End Of Startup. + .PREQ (), // 1-bit output: PROGRAM request to fabric output + .CLK (1'b0), // 1-bit input: User start-up clock input + .GSR (1'b0), // 1-bit input: Global Set/Reset input (GSR cannot be used for the port name) + .GTS (1'b0), // 1-bit input: Global 3-state input (GTS cannot be used for the port name) + .KEYCLEARB(1'b0), // 1-bit input: Clear AES Decrypter Key input from Battery-Backed RAM (BBRAM) + .PACK (1'b1), // 1-bit input: PROGRAM acknowledge input + .USRCCLKO (spi_clk), // 1-bit input: User CCLK input + .USRCCLKTS(1'b0), // 1-bit input: User CCLK 3-state enable input + .USRDONEO (1'b1), // 1-bit input: User DONE pin output control + .USRDONETS(1'b1) // 1-bit input: User DONE 3-state enable output ); `endif +`endif +`endif `ifdef spartan3e + wire runtest; + reg fsm_csn; + assign wpn_dq2 = 1'b1; + assign hldn_dq3 = 1'b1; + // jtag -> spi flash + assign sdi_dq0 = tdi; + wire tdo = (sel) ? sdo_dq1 : tdi; + assign csn = fsm_csn; + + wire tmp_cap_s = capture && sel; + wire tmp_up_s = update && sel; + assign runtest = tmp_up_s; + + always @(posedge drck, posedge runtest) begin + if (runtest) begin + fsm_csn <= 1'b1; + end else begin + if (tmp_cap_s) begin + fsm_csn <= 1'b0; + end else if (tmp_up_s) begin + fsm_csn <= 1'b1; + end else begin + fsm_csn <= fsm_csn; + end + end + end + BSCAN_SPARTAN3 bscane2_inst ( .CAPTURE(capture), // 1-bit output: CAPTURE output from TAP controller. .DRCK1 (drck), // 1-bit output: Gated TCK output. When SEL @@ -167,9 +169,7 @@ module spiOverJtag .TDO2 () // 1-bit input: USER2 function ); `else -`ifdef virtex6 - BSCAN_VIRTEX6 #( -`elsif spartan6 +`ifdef spartan6 BSCAN_SPARTAN6 #( `else BSCANE2 #( @@ -181,10 +181,10 @@ module spiOverJtag // is asserted, DRCK toggles when // CAPTURE or SHIFT are asserted. .RESET (), // 1-bit output: Reset output for TAP controller. - .RUNTEST(runtest), // 1-bit output: Output asserted when TAP + .RUNTEST(), // 1-bit output: Output asserted when TAP // controller is in Run Test/Idle state. .SEL (sel), // 1-bit output: USER instruction active output. - .SHIFT (), // 1-bit output: SHIFT output from TAP controller. + .SHIFT (shift), // 1-bit output: SHIFT output from TAP controller. .TCK (), // 1-bit output: Test Clock output. // Fabric connection to TAP Clock pin. .TDI (tdi), // 1-bit output: Test Data Input (TDI) output @@ -195,38 +195,58 @@ module spiOverJtag .TDO (tdo) // 1-bit input: Test Data Output (TDO) input // for USER function. ); + +/* BSCAN for Version Interface. */ +`ifdef spartan6 + BSCAN_SPARTAN6 #( +`else + BSCANE2 #( +`endif + .JTAG_CHAIN(4) // Value for USER command. + ) bscane2_version ( + .CAPTURE(ver_cap), + .DRCK (ver_drck), + .RESET (), + .RUNTEST(), + .SEL (ver_sel), + .SHIFT (ver_shift), + .TCK (), + .TDI (ver_tdi), + .TMS (), + .UPDATE (), + .TDO (ver_tdo) + ); `endif `ifdef secondaryflash - reg fsm_csn_sec; - wire tdo_sec; - // secondary BSCANE3 signals - wire sel_sec, drck_sec; + wire drck_sec; - wire sck = (sel_sec) ? drck_sec : drck; + spiOverJtag_core spiOverJtag_core_sec ( + /* JTAG state/controls */ + .sel(sel_sec), + .capture(capture), + .update(update), + .shift(shift), + .drck(drck_sec), + .tdi(tdi), + .tdo(tdo_sec), - assign wpn_sec_dq2 = 1'b1; - assign hldn_sec_dq3 = 1'b1; - assign sdi_sec_dq0 = tdi; - assign tdo_sec = (sel_sec) ? sdo_sec_dq1 : tdi; - assign csn_sec = fsm_csn_sec; + /* JTAG endpoint to version (Unused) */ + .ver_sel(1'b0), + .ver_cap(1'b0), + .ver_shift(1'b0), + .ver_drck(1'b0), + .ver_tdi(1'b0), + .ver_tdo(), - wire tmp_cap_sec_s = capture && sel_sec; - wire tmp_up_sec_s = update && sel_sec; - - always @(posedge drck_sec, posedge runtest) begin - if (runtest) begin - fsm_csn_sec <= 1'b1; - end else begin - if (tmp_cap_sec_s) begin - fsm_csn_sec <= 1'b0; - end else if (tmp_up_sec_s) begin - fsm_csn_sec <= 1'b1; - end else begin - fsm_csn_sec <= fsm_csn_sec; - end - end - end + /* phys */ + .csn(csn_sec), + .sck(spi_clk_sec), + .sdi_dq0(sdi_sec_dq0), + .sdo_dq1(sdo_sec_dq1), + .wpn_dq2(wpn_sec_dq2), + .hldn_dq3(hldn_sec_dq3) + ); BSCANE2 #( .JTAG_CHAIN(2) // Value for USER command.