New scan controller Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
diff --git a/configure.py b/configure.py index ebb2106..637ca8f 100755 --- a/configure.py +++ b/configure.py
@@ -290,13 +290,14 @@ .slow_clk (io_out[10]), .set_clk_div (io_in[11]), - .scan_clk (clk[0]), + .scan_clk_in (clk[0]), + .scan_clk_out (clk[NUM_MACROS]), .scan_data_out (data[0]), .scan_data_in (data[NUM_MACROS]), .scan_select (scan[0]), .scan_latch_en (latch[0]), - .la_scan_clk (la_data_in[0]), + .la_scan_clk_in (la_data_in[0]), .la_scan_data_in (la_data_in[1]), .la_scan_data_out (la_data_out[0]), .la_scan_select (la_data_in[2]),
diff --git a/verilog/rtl/scan_controller/properties.v b/verilog/rtl/scan_controller/properties.v index c104f6a..5825c57 100644 --- a/verilog/rtl/scan_controller/properties.v +++ b/verilog/rtl/scan_controller/properties.v
@@ -1,22 +1,24 @@ always @(*) begin if(driver_sel == 2'b00) begin // external driver - assert(outputs[0] == scan_data_in); - assert(scan_clk == ext_scan_clk); + assert(outputs[0] == scan_clk_in); + assert(outputs[1] == scan_data_in); + assert(scan_clk_out == inputs[0]); assert(scan_data_out == inputs[1]); assert(scan_select == inputs[2]); assert(scan_latch_en == inputs[3]); end else if(driver_sel == 2'b10) begin // external driver assert(la_scan_data_out == scan_data_in); - assert(scan_clk == la_scan_clk); + assert(scan_clk_out == la_scan_clk); assert(scan_data_out == la_scan_data_in); assert(scan_select == la_scan_select); assert(scan_latch_en == la_scan_latch_en); end else if(driver_sel == 2'b01) begin // internal driver assert(int_scan_data_out == scan_data_out); - assert(scan_clk == int_scan_clk); + assert(scan_clk__out == int_scan_clk_out); assert(scan_data_in == int_scan_data_in); + assert(scan_clk_in == int_scan_clk_in); assert(scan_select == int_scan_select); assert(scan_latch_en == int_scan_latch_en); end
diff --git a/verilog/rtl/scan_controller/scan_controller.v b/verilog/rtl/scan_controller/scan_controller.v index 1591991..1b37af3 100644 --- a/verilog/rtl/scan_controller/scan_controller.v +++ b/verilog/rtl/scan_controller/scan_controller.v
@@ -1,226 +1,450 @@ +`timescale 1ns / 100ps `default_nettype none -module scan_controller ( +module scan_controller #( + parameter integer NUM_DESIGNS = 8, + parameter integer NUM_IOS = 8, + + // auto-set + parameter integer PL = NUM_IOS - 1 +)( input wire clk, input wire reset, input wire [8:0] active_select, // which design is connected to the inputs and outputs - input wire [7:0] inputs, // inputs to the design (or external scan chain) + input wire [PL:0] inputs, // inputs to the design (or external scan chain) input wire set_clk_div, // set clock divider. See module below - output wire [7:0] outputs, // outputs from the design (or external scan chain) + output wire [PL:0] outputs, // outputs from the design (or external scan chain) output wire ready, // debug output that goes high once per refresh output wire slow_clk, // debug clock divider output - output wire scan_clk, // scan chain interface for the tiny designs, from perspective of this module - output wire scan_data_out, // see diagrams below for how the scan chain works + output reg scan_clk_out, // scan chain interface for the tiny designs, from perspective of this module + output reg scan_data_out, // see diagrams below for how the scan chain works + input wire scan_clk_in, // feedback clock after having done the whole round input wire scan_data_in, // will be driven by internal driver, external gpio pins, or Caravel logic analyser - output wire scan_select, // external scan chain driver muxes with ins/outs, eg microcontroller outside the ASIC - output wire scan_latch_en, + output reg scan_select, // external scan chain driver muxes with ins/outs, eg microcontroller outside the ASIC + output reg scan_latch_en, - input wire la_scan_clk, // logic analyser scan chain driver, driven by firmware running on Caravel's VexRisc - input wire la_scan_data_in, // signal names from perspective of this module + input wire la_scan_clk_in, // logic analyser scan chain driver, driven by firmware running on Caravel's VexRisc + input wire la_scan_data_in, // signal names from perspective of this module toward scan chain output wire la_scan_data_out, input wire la_scan_select, input wire la_scan_latch_en, - input wire [1:0] driver_sel, // 00 = external, 01 = internal, 10 = logic analyser + input wire [1:0] driver_sel, // 00 = external, 01 = logic analyzer, 1x = internal output wire [`MPRJ_IO_PADS-1:0] oeb // caravel harness needs output enable bar set low to enable outputs - ); +); + // Signals + // ------- + + // Reset + reg [2:0] rst_shift; + wire rst_i; + + // Muxing + // _in / _out are from the perspecitve of this module toward the scan + // chain. So all _in are FROM the scan chain (and either input to this + // module from there, or output from this module to LA / External) + + wire ext_scan_clk_in; + wire ext_scan_data_in; + wire ext_scan_clk_out; + wire ext_scan_data_out; + wire ext_scan_select; + wire ext_scan_latch_en; + + wire int_scan_clk_in; + wire int_scan_data_in; + reg int_scan_clk_out; + reg int_scan_data_out; + reg int_scan_select; + reg int_scan_latch_en; + + // FSM + localparam [3:0] + ST_IDLE = 0, // Idle + ST_IN_LOAD = 1, // Capture input + ST_IN_SHIFT_LO = 2, // Shift input to design: lo-clk + ST_IN_SHIFT_HI = 3, // Shift input to design: hi-clk + ST_IN_LATCH_WAIT = 4, // Wait before latching + ST_IN_LATCH = 5, // Latch + ST_OUT_LOAD_PRE = 6, // Prepare load + ST_OUT_LOAD = 7, // Clock once to load + ST_OUT_LOAD_POST = 8, // Wait for load to be done + ST_OUT_LOAD_CLR = 9, // Restore chain to shift mode + ST_OUT_SHIFT_LO = 10, // Shift output to us: lo-clk + ST_OUT_SHIFT_HI = 11, // Shift output to us: hi-clk + ST_OUT_CAP_WAIT = 12, // Wait for capture + ST_OUT_CAP = 13; // Capture to out local register + + reg active; + + reg [3:0] state; + reg [3:0] state_nxt; + + // Scan progress + reg [$clog2(NUM_IOS)-1:0] bit_cnt; + wire bit_last; + + reg [8:0] proj_cnt; + wire proj_sel; + wire proj_done; + + // Auto IO + reg [PL:0] aio_input_sync; + reg [PL:0] aio_input_reg; + reg [PL:0] aio_input_shift; + + reg [PL:0] aio_output_shift; + reg [PL:0] aio_output_reg; + + wire aio_input_sh; + wire aio_input_ld; + wire aio_output_cap; + + // Wait state config + reg [7:0] ws_cnt; + wire ws_cnt_done; + wire ws_cnt_run; + + // Wait state config + reg [2:0] ws_set_sync; + reg ws_set_now; + reg [7:0] ws_cfg; + + // Clock divider + wire slow_clk_ena; + + + // Misc + // ---- + + // Generate internal ties for the IO blocks assign oeb = {`MPRJ_IO_PADS{1'b0}}; - parameter NUM_DESIGNS = 8; - parameter NUM_IOS = 8; - - localparam START = 0; - localparam LOAD = 1; - localparam READ = 2; - localparam CAPTURE_STATE = 3; - localparam LATCH = 4; - - // scan chain muxing - // signal names in perspective of this module - wire ext_scan_clk = inputs[0]; - wire ext_scan_data_in = inputs[1]; - wire ext_scan_data_out = scan_data_in; - wire ext_scan_select = inputs[2]; - wire ext_scan_latch_en = inputs[3]; - - assign scan_clk = driver_sel == 2'b00 ? ext_scan_clk : driver_sel == 2'b01 ? int_scan_clk : la_scan_clk; - assign scan_data_out = driver_sel == 2'b00 ? ext_scan_data_in : driver_sel == 2'b01 ? int_scan_data_out : la_scan_data_in; - assign scan_select = driver_sel == 2'b00 ? ext_scan_select : driver_sel == 2'b01 ? int_scan_select : la_scan_select; - assign scan_latch_en = driver_sel == 2'b00 ? ext_scan_latch_en : driver_sel == 2'b01 ? int_scan_latch_en : la_scan_latch_en; - - wire int_scan_data_in = scan_data_in; - assign la_scan_data_out = scan_data_in; - assign outputs = driver_sel == 2'b01 ? outputs_r : {7'b0, ext_scan_data_out}; - `ifdef FORMAL `include "properties.v" `endif - // reg - reg [8:0] current_design; - reg [2:0] state; - reg [3:0] num_io; - reg scan_clk_r; - reg scan_select_out_r; + // Generate our own reset, with de-assertion + // synchronized to clock + always @(posedge clk or posedge reset) + if (reset) + rst_shift <= 3'b111; + else + rst_shift <= { rst_shift[1:0], 1'b0 }; - reg [7:0] inputs_r; - reg [7:0] outputs_r; - reg [7:0] output_buf; - - // wires - wire [8:0] active_select_rev = NUM_DESIGNS - 1 - active_select; - assign ready = state == START; - wire int_scan_latch_en = state == LATCH; - wire int_scan_clk = scan_clk_r; - wire int_scan_data_out = (state == LOAD && current_design == active_select_rev ) ? inputs_r[NUM_IOS-1-num_io] : 0; - wire int_scan_select = scan_select_out_r; - - // clock divider - clk_divider clk_divider ( - .clk (clk), - .set (set_clk_div), - .reset (reset), - .divider (inputs), - .slow_clk (slow_clk) - ); - wire [7:0] inputs_and_clk = set_clk_div ? { inputs[7:1], slow_clk } : inputs; - - /* - - LOAD - - ┌──┐ ┌──┐ ┌──┐ ┌──┐ - clk : ┘ └──┘ └──┘ └──┘ └────────────── - ┐ - scan en: └─────────────────────────────────── - ┐ ┌─────┐ - latch : └───────────────────────┘ └───── - ┐ ┌─────┐ ┌─────┐ - data o : └─────┘ └─────┘ └─────────── - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - data i : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + assign rst_i = rst_shift[2]; - READ + // Scan chain and IO muxing + // ------------------------ - ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ - clk : ┘ └──┘ └──┘ └──┘ └──┘ └──┘ └── - ┐ ┌─────┐ - scan en: └─────┘ └─────────────────────── - ┐ - latch : └─────────────────────────────────── - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - data o : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - ┐ ┌─────┐ ┌─────┐ - data i : └───────────┘ └─────┘ └───── + // To ensure we get something working, the scan chain can be driven + // three different ways. Automatic, from caravel LA and fully + // externally (see driver_sel input) - */ + // External interface + assign ext_scan_clk_out = inputs[0]; + assign ext_scan_data_out = inputs[1]; + assign ext_scan_select = inputs[2]; + assign ext_scan_latch_en = inputs[3]; - // FSM, only run it if driver_sel is set to internal - always @(posedge clk) begin - if(reset) begin - current_design <= 0; - state <= START; - inputs_r <= 0; - outputs_r <= 0; - scan_clk_r <= 0; - num_io <= 0; - output_buf <= 0; - end else if (driver_sel == 2'b01) begin - case(state) - START: begin - state <= LOAD; - inputs_r <= inputs_and_clk; - outputs_r <= output_buf; - current_design <= 0; - scan_select_out_r <= 0; - end + assign ext_scan_clk_in = scan_clk_in; + assign ext_scan_data_in = scan_data_in; - LOAD: begin - scan_clk_r <= ~scan_clk_r; - if(scan_clk_r) begin - num_io <= num_io + 1; + // Internal interface + assign int_scan_clk_in = scan_clk_in; + assign int_scan_data_in = scan_data_in; - if(num_io == NUM_IOS - 1) begin - num_io <= 0; - current_design <= current_design + 1; - - if(current_design == NUM_DESIGNS - 1) - state <= LATCH; - end + // LA interface + assign la_scan_data_out = scan_data_in; - end - - end - LATCH: begin - state <= READ; - current_design <= 0; - scan_select_out_r <= 1; - end - - READ: begin - scan_select_out_r <= 0; - scan_clk_r <= ~scan_clk_r; - if(scan_clk_r) begin - num_io <= num_io + 1; - if(current_design == active_select_rev) - output_buf[NUM_IOS-1-num_io] <= int_scan_data_in; - - if(num_io == NUM_IOS - 1) begin - num_io <= 0; - current_design <= current_design + 1; - - - if(current_design == NUM_DESIGNS - 1) begin - state <= START; - end - end - end - end - endcase - end - end - -endmodule - -module clk_divider ( - input clk, - input reset, - input set, - input [DIV_WIDTH-1:0] divider, - output slow_clk - ); - - // fastest useful clock period must be < max refresh rate: 750Hz - // 10M with 14bit divider (min) gives ~610Hz - // 10M with 22bit divider (max) gives ~2.4Hz - localparam MIN_WIDTH = 13; - localparam DIV_WIDTH = 8; - - reg [MIN_WIDTH+8:0] counter; - reg [DIV_WIDTH-1:0] compare; - reg last_set; - - assign slow_clk = counter[MIN_WIDTH + compare]; - - always @(posedge clk) begin - if(reset) begin - counter <= 0; - compare <= 0; - last_set <= 0; - end - else begin - // update divider on positive edge of set - if(set && !last_set) begin - compare <= divider; + // Mux toward scan-schain + always @(*) + begin + casez (driver_sel) + // External + 2'b00: begin + scan_clk_out = ext_scan_clk_out; + scan_data_out = ext_scan_data_out; + scan_select = ext_scan_select; + scan_latch_en = ext_scan_latch_en; end - counter <= counter + 1'b1; - last_set <= set; - end + + // Caravel LA + 2'b01: begin + scan_clk_out = la_scan_clk_in; + scan_data_out = la_scan_data_in; + scan_select = la_scan_select; + scan_latch_en = la_scan_latch_en; + end + + // Internal + 2'b1z: begin + scan_clk_out = int_scan_clk_out; + scan_data_out = int_scan_data_out; + scan_select = int_scan_select; + scan_latch_en = int_scan_latch_en; + end + endcase end -endmodule + // Synchronizer for inputs + always @(posedge clk) + begin + aio_input_sync <= inputs; + aio_input_reg <= driver_sel[1] ? aio_input_sync : 0; + end + + // Mux for outputs + assign outputs = driver_sel[1] ? aio_output_reg : {6'b0, ext_scan_data_in, ext_scan_clk_in}; + + + // FSM & control + // ------------- + + // Check if we're active + always @(posedge clk or posedge rst_i) + if (rst_i) + active <= 1'b0; + else + active <= driver_sel[1]; + + // State register + always @(posedge clk or posedge rst_i) + if (rst_i) + state <= ST_IDLE; + else + state <= state_nxt; + + // State transitions + always @(*) + begin + // Defaults + state_nxt = state; + + // Transitions + case (state) + ST_IDLE: + if (active) + state_nxt = ST_IN_LOAD; + + ST_IN_LOAD: + state_nxt = ST_IN_SHIFT_LO; + + ST_IN_SHIFT_LO: + state_nxt = ST_IN_SHIFT_HI; + + ST_IN_SHIFT_HI: + state_nxt = (proj_sel & bit_last) ? ST_IN_LATCH_WAIT : ST_IN_SHIFT_LO; + + ST_IN_LATCH_WAIT: + if (ws_cnt_done) + state_nxt = ST_IN_LATCH; + + ST_IN_LATCH: + state_nxt = ST_OUT_LOAD_PRE; + + ST_OUT_LOAD_PRE: + if (ws_cnt_done) + state_nxt = ST_OUT_LOAD; + + ST_OUT_LOAD: + state_nxt = ST_OUT_LOAD_POST; + + ST_OUT_LOAD_POST: + if (ws_cnt_done) + state_nxt = ST_OUT_LOAD_CLR; + + ST_OUT_LOAD_CLR: + if (ws_cnt_done) + state_nxt = ST_OUT_SHIFT_LO; + + ST_OUT_SHIFT_LO: + state_nxt = ST_OUT_SHIFT_HI; + + ST_OUT_SHIFT_HI: + state_nxt = (proj_done & bit_last) ? ST_OUT_CAP_WAIT : ST_OUT_SHIFT_LO; + + ST_OUT_CAP_WAIT: + if (ws_cnt_done) + state_nxt = ST_OUT_CAP; + + ST_OUT_CAP: + state_nxt = ST_IDLE; + endcase + end + + + // Scan progress tracking + // ---------------------- + + // Keep track of IO number + always @(posedge clk) + if (state == ST_IDLE) + bit_cnt <= 0; + else if ((state == ST_IN_SHIFT_HI) | (state == ST_OUT_SHIFT_HI)) + bit_cnt <= bit_last ? 0 : (bit_cnt + 1); + + assign bit_last = (bit_cnt == (NUM_IOS - 1)); + + // Keep track of project number + always @(posedge clk) + if (state == ST_IDLE) + proj_cnt <= 0; + else if (((state == ST_IN_SHIFT_HI) | (state == ST_OUT_SHIFT_HI)) & bit_last) + proj_cnt <= proj_cnt + 1; + + assign proj_sel = (proj_cnt == active_select); + assign proj_done = (proj_cnt == NUM_DESIGNS); + + + // Scan chain control + // ------------------ + + always @(posedge clk) + begin + int_scan_clk_out <= (state == ST_IN_SHIFT_HI) | (state == ST_OUT_LOAD) | (state == ST_OUT_SHIFT_HI); + int_scan_data_out <= aio_input_shift[PL]; + int_scan_select <= (state == ST_OUT_LOAD_PRE) | (state == ST_OUT_LOAD) | (state == ST_OUT_LOAD_POST); + int_scan_latch_en <= (state == ST_IN_LATCH); + end + + + // Shift registers + // --------------- + + // Local control from FSM + assign aio_input_sh = (state == ST_IN_SHIFT_HI); + assign aio_input_ld = (state == ST_IN_LOAD); + assign aio_output_cap = (state == ST_OUT_CAP); + + // Input + always @(posedge clk) + if (aio_input_ld) + aio_input_shift <= slow_clk_ena ? { aio_input_reg[PL-1:1], slow_clk } : aio_input_reg; + else if (aio_input_sh) + aio_input_shift <= { aio_input_shift[PL-1:0], 1'b0 }; + + // Output + always @(posedge int_scan_clk_in) + aio_output_shift <= { aio_output_shift[PL-1:0], int_scan_data_in }; + + always @(posedge clk) + if (aio_output_cap) + aio_output_reg <= aio_output_shift; + + + // Wait state counter + // ------------------ + + always @(posedge clk) + if (~ws_cnt_run | ws_cnt_done) + ws_cnt <= 0; + else + ws_cnt <= ws_cnt + 1; + + assign ws_cnt_done = (ws_cnt == ws_cfg); + + assign ws_cnt_run = ( + (state == ST_IN_LATCH_WAIT) | + (state == ST_OUT_LOAD_PRE) | + (state == ST_OUT_LOAD_POST) | + (state == ST_OUT_LOAD_CLR) | + (state == ST_OUT_CAP_WAIT) + ); + + + // Wait state config + // ----------------- + + // This dictates how many wait cycle we insert in various state + // of the load process. We have a sane default, but also allow + // override externally. + + always @(posedge clk or posedge rst_i) + if (rst_i) + ws_set_sync <= 3'b000; + else + ws_set_sync <= { ws_set_sync[1:0], (driver_sel == 2'b11) }; + + always @(posedge clk) + ws_set_now <= ~ws_set_sync[2] & ws_set_sync[1]; + + always @(posedge clk or posedge rst_i) + if (rst_i) + ws_cfg <= 8'd10; + else if (ws_set_now) + ws_cfg <= inputs; + + + // Clock Divider + // ------------- + + clk_divider clk_divider_I ( + .clk (clk), + .ce (aio_input_ld), + .set (set_clk_div), + .reset (rst_i), + .divider (inputs), + .active (slow_clk_ena), + .slow_clk (slow_clk) + ); + +endmodule // scan_controller + + +module clk_divider #( + parameter integer DIV_WIDTH = 8 +)( + input wire clk, + input wire ce, + input wire reset, + input wire set, + input wire [DIV_WIDTH-1:0] divider, + output wire active, + output reg slow_clk +); + + reg [DIV_WIDTH-1:0] counter; + reg [DIV_WIDTH-1:0] compare; + wire match; + + reg [2:0] set_sync; + reg set_now; + + // Detect rising edge (with synchronizer) + always @(posedge clk) + begin + set_sync <= { set_sync[1:0], set }; + set_now <= ~set_sync[2] & set_sync[1]; + end + + assign active = set_sync[2]; + + // Latch divider + always @(posedge clk) + if (set_now) + compare <= divider; + + // Compare + assign match = (counter == compare); + + // Counter + always @(posedge clk or posedge reset) + if (reset) + counter <= 0; + else if (ce) + counter <= match ? 0 : (counter + 1); + + // Clock gen + always @(posedge clk or posedge reset) + if (reset) + slow_clk <= 1'b0; + else if (ce) + slow_clk <= slow_clk ^ match; + +endmodule // clk_divider
diff --git a/verilog/rtl/user_project_wrapper.v b/verilog/rtl/user_project_wrapper.v index 7fcce1c..2674df0 100644 --- a/verilog/rtl/user_project_wrapper.v +++ b/verilog/rtl/user_project_wrapper.v
@@ -96,13 +96,14 @@ .slow_clk (io_out[10]), .set_clk_div (io_in[11]), - .scan_clk (clk[0]), + .scan_clk_out (clk[0]), + .scan_clk_in (clk[NUM_MACROS]), .scan_data_out (data[0]), .scan_data_in (data[NUM_MACROS]), .scan_select (scan[0]), .scan_latch_en (latch[0]), - .la_scan_clk (la_data_in[0]), + .la_scan_clk_in (la_data_in[0]), .la_scan_data_in (la_data_in[1]), .la_scan_data_out (la_data_out[0]), .la_scan_select (la_data_in[2]),