From ab10bd136ed9eff74011f9ebb9e2642bcedafc32 Mon Sep 17 00:00:00 2001 From: Istvan Csomortani Date: Wed, 22 Jul 2020 13:45:27 +0100 Subject: [PATCH] spi_engine_execution: Add echoed SCLK support There are boards (e.g. AD4630-24) which take the SCLK and echo back to the FPGA through a level shifter - doing this removes the effect of round-trip timing delays from the level shifter. This is commonly done whenever isolators are used since they are very slow. By setting the ECHO_SCLK parameter to 1, the IP will use the incoming echoed SCLK clock to latch the SDI line(s). The sdi_data_valid is still synchronous to the SPI clock, and it's generated after the last valid SDI latch. The designer's responsibility is to time the SDI shift registers in order to respect the design requirements. --- library/spi_engine/scripts/spi_engine.tcl | 10 +- .../spi_engine_execution.v | 188 +++++++++++++++--- .../spi_engine_execution_ip.tcl | 24 +++ 3 files changed, 196 insertions(+), 26 deletions(-) diff --git a/library/spi_engine/scripts/spi_engine.tcl b/library/spi_engine/scripts/spi_engine.tcl index 2c2667582..79315b39b 100644 --- a/library/spi_engine/scripts/spi_engine.tcl +++ b/library/spi_engine/scripts/spi_engine.tcl @@ -1,4 +1,4 @@ -proc spi_engine_create {{name "hier_spi_engine"} {data_width 32} {async_spi_clk 1} {num_cs 1} {num_sdi 1} {sdi_delay 2}} { +proc spi_engine_create {{name "spi_engine"} {data_width 32} {async_spi_clk 1} {num_cs 1} {num_sdi 1} {sdi_delay 0} {echo_sclk 0}} { create_bd_cell -type hier $name current_bd_instance /$name @@ -6,6 +6,9 @@ proc spi_engine_create {{name "hier_spi_engine"} {data_width 32} {async_spi_clk if {$async_spi_clk == 1} { create_bd_pin -dir I -type clk spi_clk } + if {$echo_sclk == 1} { + create_bd_pin -dir I -type clk echo_sclk + } create_bd_pin -dir I -type clk clk create_bd_pin -dir I -type rst resetn create_bd_pin -dir I trigger @@ -19,6 +22,7 @@ proc spi_engine_create {{name "hier_spi_engine"} {data_width 32} {async_spi_clk ad_ip_parameter execution CONFIG.NUM_OF_SDI $num_sdi ad_ip_parameter execution CONFIG.SDO_DEFAULT 1 ad_ip_parameter execution CONFIG.SDI_DELAY $sdi_delay + ad_ip_parameter execution CONFIG.ECHO_SCLK $echo_sclk ad_ip_instance axi_spi_engine axi_regmap ad_ip_parameter axi_regmap CONFIG.DATA_WIDTH $data_width @@ -60,6 +64,10 @@ proc spi_engine_create {{name "hier_spi_engine"} {data_width 32} {async_spi_clk ad_connect clk interconnect/clk } + if {$echo_sclk == 1} { + ad_connect echo_sclk execution/echo_sclk + } + ad_connect axi_regmap/spi_resetn offload/spi_resetn ad_connect axi_regmap/spi_resetn execution/resetn ad_connect axi_regmap/spi_resetn interconnect/resetn diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution.v b/library/spi_engine/spi_engine_execution/spi_engine_execution.v index b31675e03..342c44e29 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution.v +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution.v @@ -43,6 +43,7 @@ module spi_engine_execution #( parameter DATA_WIDTH = 8, // Valid data widths values are 8/16/24/32 parameter NUM_OF_SDI = 1, parameter [0:0] SDO_DEFAULT = 1'b0, + parameter ECHO_SCLK = 0, parameter [1:0] SDI_DELAY = 2'b00) ( input clk, @@ -67,6 +68,7 @@ module spi_engine_execution #( output reg sync_valid, output [7:0] sync, + input echo_sclk, output reg sclk, output reg sdo, output reg sdo_t, @@ -107,7 +109,6 @@ reg [(BIT_COUNTER_WIDTH+8):0] counter = 'h00; wire [7:0] sleep_counter = counter[(BIT_COUNTER_WIDTH+8):(BIT_COUNTER_WIDTH+1)]; wire [1:0] cs_sleep_counter = counter[(BIT_COUNTER_WIDTH+2):(BIT_COUNTER_WIDTH+1)]; -wire [2:0] cs_sleep_counter2 = counter[(BIT_COUNTER_WIDTH+3):(BIT_COUNTER_WIDTH+1)]; wire [(BIT_COUNTER_WIDTH-1):0] bit_counter = counter[BIT_COUNTER_WIDTH:1]; wire [7:0] transfer_counter = counter[(BIT_COUNTER_WIDTH+8):(BIT_COUNTER_WIDTH+1)]; wire ntx_rx = counter[0]; @@ -127,8 +128,6 @@ wire end_of_word; reg [7:0] sdi_counter = 8'b0; assign first_bit = ((bit_counter == 'h0) || (bit_counter == word_length)); -assign last_bit = bit_counter == word_length - 1; -assign end_of_word = last_bit == 1'b1 && ntx_rx == 1'b1 && clk_div_last == 1'b1; reg [15:0] cmd_d1; @@ -159,13 +158,13 @@ wire trigger_rx; wire sleep_counter_compare; wire cs_sleep_counter_compare; -wire cs_sleep_counter_compare2; wire io_ready1; wire io_ready2; wire trigger_rx_s; wire last_sdi_bit; +wire end_of_sdi_latch; assign cmd_ready = idle; @@ -240,7 +239,6 @@ assign trigger_rx = trigger == 1'b1 && ntx_rx == 1'b1; assign sleep_counter_compare = sleep_counter == cmd_d1[7:0] && clk_div_last == 1'b1; assign cs_sleep_counter_compare = cs_sleep_counter == cmd_d1[9:8] && clk_div_last == 1'b1; -assign cs_sleep_counter_compare2 = cs_sleep_counter2 == {cmd_d1[9:8],1'b1} && clk_div_last == 1'b1; always @(posedge clk) begin if (idle == 1'b1) begin @@ -263,11 +261,11 @@ always @(posedge clk) begin end else begin case (inst_d1) CMD_TRANSFER: begin - if (transfer_active == 1'b0 && wait_for_io == 1'b0) + if (transfer_active == 1'b0 && wait_for_io == 1'b0 && end_of_sdi_latch == 1'b1) idle <= 1'b1; end CMD_CHIPSELECT: begin - if (cs_sleep_counter_compare2) + if (cs_sleep_counter_compare) idle <= 1'b1; end CMD_MISC: begin @@ -318,15 +316,6 @@ always @(posedge clk) begin sdo_data_ready <= 1'b0; end -always @(posedge clk) begin - if (resetn == 1'b0) - sdi_data_valid <= 1'b0; - else if (sdi_enabled == 1'b1 && last_sdi_bit == 1'b1 && trigger_rx_s == 1'b1) - sdi_data_valid <= 1'b1; - else if (sdi_data_ready == 1'b1) - sdi_data_valid <= 1'b0; -end - assign io_ready1 = (sdi_data_valid == 1'b0 || sdi_data_ready == 1'b1) && (sdo_enabled == 1'b0 || last_transfer == 1'b1 || sdo_data_valid == 1'b1); assign io_ready2 = (sdi_enabled == 1'b0 || sdi_data_ready == 1'b1) && @@ -404,10 +393,142 @@ assign trigger_rx_s = trigger_rx_d[SDI_DELAY+1]; // Load the serial data into SDI shift register(s), then link it to the output // register of the module +// NOTE: ECHO_SCLK mode can be used when the SCLK line is looped back to the FPGA +// through an other level shifter, in order to remove the round-trip timing delays +// introduced by the level shifters. This can improve the timing significantly +// on higher SCLK rates. Devices like ad4630 have an echod SCLK, which can be +// used to latch the MISO lines, improving the overall timing margin of the +// interface. wire cs_active_s = (inst_d1 == CMD_CHIPSELECT) & ~(&cmd_d1[NUM_OF_CS-1:0]); genvar i; + +// NOTE: SPI configuration (CPOL/PHA) is only hardware configurable at this point generate +if (ECHO_SCLK == 1) begin : g_echo_sclk_miso_latch + + reg [7:0] sdi_counter_d = 8'b0; + reg [7:0] sdi_transfer_counter = 8'b0; + reg [7:0] num_of_transfers = 8'b0; + reg [(NUM_OF_SDI * DATA_WIDTH)-1:0] sdi_data_latch = {(NUM_OF_SDI * DATA_WIDTH){1'b0}}; + + if ((DEFAULT_SPI_CFG[1:0] == 2'b01) || (DEFAULT_SPI_CFG[1:0] == 2'b10)) begin : g_echo_miso_nshift_reg + + // MISO shift register runs on negative echo_sclk + for (i=0; i