diff --git a/docs/regmap/adi_regmap_spi_engine.txt b/docs/regmap/adi_regmap_spi_engine.txt index 681c37d45d..908b04bf33 100644 --- a/docs/regmap/adi_regmap_spi_engine.txt +++ b/docs/regmap/adi_regmap_spi_engine.txt @@ -9,7 +9,7 @@ ENDTITLE REG 0x00 VERSION -Version of the peripheral. Follows semantic versioning. Current version 1.03.00. +Version of the peripheral. Follows semantic versioning. Current version 1.03.01. ENDREG FIELD @@ -25,7 +25,7 @@ RO ENDFIELD FIELD -[7:0] 0x00000000 +[7:0] 0x00000001 VERSION_PATCH RO ENDFIELD diff --git a/library/spi_engine/axi_spi_engine/axi_spi_engine.v b/library/spi_engine/axi_spi_engine/axi_spi_engine.v index f91e00eb26..2c0a9f37c9 100644 --- a/library/spi_engine/axi_spi_engine/axi_spi_engine.v +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine.v @@ -133,7 +133,7 @@ module axi_spi_engine #( input [7:0] offload_sync_data ); - localparam PCORE_VERSION = 'h010300; + localparam PCORE_VERSION = 'h010301; localparam S_AXI = 0; localparam UP_FIFO = 1; diff --git a/library/spi_engine/spi_engine_execution/Makefile b/library/spi_engine/spi_engine_execution/Makefile index 30f20717be..dd5ed79796 100644 --- a/library/spi_engine/spi_engine_execution/Makefile +++ b/library/spi_engine/spi_engine_execution/Makefile @@ -7,6 +7,7 @@ LIBRARY_NAME := spi_engine_execution GENERIC_DEPS += spi_engine_execution.v +GENERIC_DEPS += spi_engine_execution_shiftreg.v XILINX_DEPS += spi_engine_execution_constr.ttcl XILINX_DEPS += spi_engine_execution_ip.tcl 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 e759da3ea4..4dd61f6732 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution.v +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution.v @@ -56,11 +56,11 @@ module spi_engine_execution #( input [15:0] cmd, input sdo_data_valid, - output reg sdo_data_ready, + output sdo_data_ready, input [(DATA_WIDTH-1):0] sdo_data, input sdi_data_ready, - output reg sdi_data_valid, + output sdi_data_valid, output [(NUM_OF_SDI * DATA_WIDTH)-1:0] sdi_data, input sync_ready, @@ -96,7 +96,6 @@ module spi_engine_execution #( localparam BIT_COUNTER_CLEAR = {{8{1'b1}}, {BIT_COUNTER_WIDTH{1'b0}}, 1'b1}; reg sclk_int = 1'b0; - wire sdo_int_s; reg sdo_t_int = 1'b0; reg idle; @@ -105,27 +104,20 @@ module spi_engine_execution #( reg [7:0] clk_div_counter_next = 'h00; reg clk_div_last; - 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 [(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]; + reg [7:0] sleep_counter; + reg [(BIT_COUNTER_WIDTH-1):0] bit_counter; + reg [7:0] transfer_counter; + reg ntx_rx; + reg sleep_counter_increment; reg trigger = 1'b0; reg trigger_next = 1'b0; reg wait_for_io = 1'b0; reg transfer_active = 1'b0; - wire last_bit; - wire first_bit; reg last_transfer; reg [7:0] word_length = DATA_WIDTH; reg [7:0] left_aligned = 8'b0; - wire end_of_word; - - reg [7:0] sdi_counter = 8'b0; assign first_bit = ((bit_counter == 'h0) || (bit_counter == word_length)); @@ -141,9 +133,11 @@ module spi_engine_execution #( reg sdo_enabled = 1'b0; reg sdi_enabled = 1'b0; - reg [(DATA_WIDTH-1):0] data_sdo_shift = 'h0; + wire sdo_int_s; - reg [SDI_DELAY+1:0] trigger_rx_d = {(SDI_DELAY+2){1'b0}}; + wire last_bit; + wire first_bit; + wire end_of_word; wire [2:0] inst = cmd[14:12]; wire [2:0] inst_d1 = cmd_d1[14:12]; @@ -159,6 +153,7 @@ module spi_engine_execution #( wire trigger_tx; wire trigger_rx; + wire [1:0] cs_sleep_counter = sleep_counter[1:0]; wire sleep_counter_compare; wire cs_sleep_counter_compare; wire cs_sleep_early_exit; @@ -167,13 +162,49 @@ module spi_engine_execution #( wire io_ready1; wire io_ready2; - wire trigger_rx_s; - wire last_sdi_bit; wire end_of_sdi_latch; + wire sample_sdo; + (* direct_enable = "yes" *) wire cs_gen; + spi_engine_execution_shiftreg #( + .DEFAULT_SPI_CFG(DEFAULT_SPI_CFG), + .DATA_WIDTH(DATA_WIDTH), + .NUM_OF_SDI(NUM_OF_SDI), + .SDI_DELAY(SDI_DELAY), + .ECHO_SCLK(ECHO_SCLK), + .CMD_TRANSFER(CMD_TRANSFER) + ) shiftreg ( + .clk(clk), + .resetn(resetn), + .sdi(sdi), + .sdo_int(sdo_int_s), + .echo_sclk(echo_sclk), + .sdo_data(sdo_data), + .sdo_data_valid(sdo_data_valid), + .sdo_data_ready(sdo_data_ready), + .sdi_data(sdi_data), + .sdi_data_valid(sdi_data_valid), + .sdi_data_ready(sdi_data_ready), + .sdo_enabled(sdo_enabled), + .sdi_enabled(sdi_enabled), + .current_cmd(cmd_d1), + .sdo_idle_state(sdo_idle_state), + .left_aligned(left_aligned), + .word_length(word_length), + .sample_sdo(sample_sdo), + .sdo_io_ready(sdo_io_ready), + .transfer_active(transfer_active), + .trigger_tx(trigger_tx), + .trigger_rx(trigger_rx), + .first_bit(first_bit), + .cs_activate(cs_activate), + .end_of_sdi_latch(end_of_sdi_latch)); + + assign sample_sdo = sdo_data_valid && ((trigger_tx && last_bit) || (wait_for_io || exec_transfer_cmd)); + assign cs_gen = inst_d1 == CMD_CHIPSELECT && ((cs_sleep_counter_compare == 1'b1) || cs_sleep_early_exit) && (cs_sleep_repeat == 1'b0) @@ -269,12 +300,24 @@ module spi_engine_execution #( always @(posedge clk) begin if (idle == 1'b1 || (cs_sleep_counter_compare && !cs_sleep_repeat && inst_d1 == CMD_CHIPSELECT)) begin - counter <= 'h00; + bit_counter <= 'h0; + transfer_counter <= 'h0; + sleep_counter <= 'h0; + ntx_rx <= 1'b0; + sleep_counter_increment <= 1'b0; end else if (clk_div_last == 1'b1 && wait_for_io == 1'b0) begin if (bit_counter == word_length && transfer_active) begin - counter <= (counter & BIT_COUNTER_CLEAR) + 'h1 + BIT_COUNTER_CARRY; + bit_counter <= 'h0; + transfer_counter <= transfer_counter + 1; + ntx_rx <= ~ntx_rx; end else begin - counter <= counter + (transfer_active ? 'h1 : (2**BIT_COUNTER_WIDTH)); + if (transfer_active) begin + bit_counter <= bit_counter + ntx_rx; + ntx_rx <= ~ntx_rx; + end else begin + sleep_counter_increment <= ~sleep_counter_increment; + sleep_counter <= sleep_counter + sleep_counter_increment; + end end end end @@ -344,17 +387,8 @@ module spi_engine_execution #( assign sync = cmd_d1[7:0]; - always @(posedge clk) begin - if (resetn == 1'b0) - sdo_data_ready <= 1'b0; - else if (sdo_enabled == 1'b1 && first_bit == 1'b1 && trigger_tx == 1'b1 && transfer_active == 1'b1) - sdo_data_ready <= 1'b1; - else if (sdo_data_valid == 1'b1) - sdo_data_ready <= 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); + (sdo_enabled == 1'b0 || last_transfer == 1'b1 || sdo_io_ready == 1'b1); assign io_ready2 = (sdi_enabled == 1'b0 || sdi_data_ready == 1'b1) && (sdo_enabled == 1'b0 || last_transfer == 1'b1 || sdo_data_valid == 1'b1); @@ -401,42 +435,6 @@ module spi_engine_execution #( end end - // Load the SDO parallel data into the SDO shift register. In case of a custom - // data width, additional bit shifting must done at load. - always @(posedge clk) begin - if (!sdo_enabled || (inst_d1 != CMD_TRANSFER)) begin - data_sdo_shift <= {DATA_WIDTH{sdo_idle_state}}; - end else if (transfer_active == 1'b1 && trigger_tx == 1'b1) begin - if (first_bit == 1'b1) - data_sdo_shift <= sdo_data << left_aligned; - else - data_sdo_shift <= {data_sdo_shift[(DATA_WIDTH-2):0], 1'b0}; - end - end - - assign sdo_int_s = data_sdo_shift[DATA_WIDTH-1]; - - // In case of an interface with high clock rate (SCLK > 50MHz), the latch of - // the SDI line can be delayed with 1, 2 or 3 SPI core clock cycle. - // Taking the fact that in high SCLK frequencies the pre-scaler most likely will - // be set to 0, to reduce the core clock's speed, this delay will mean that SDI will - // be latched at one of the next consecutive SCLK edge. - - always @(posedge clk) begin - trigger_rx_d <= {trigger_rx_d, trigger_rx}; - end - - 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. - always @(posedge clk) begin if (!resetn) begin // set cs_activate during reset for a cycle to clear shift reg cs_activate <= 1; @@ -445,175 +443,6 @@ module spi_engine_execution #( end end - 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 +// +// OR +// +// 2. An ADI specific BSD license, which can be found in the top level directory +// of this repository (LICENSE_ADIBSD), and also on-line at: +// https://github.com/analogdevicesinc/hdl/blob/main/LICENSE_ADIBSD +// This will allow to generate bit files and not release the source code, +// as long as it attaches to an ADI device. +// +// *************************************************************************** +// *************************************************************************** + +`timescale 1ns/100ps + +module spi_engine_execution_shiftreg #( + + parameter DEFAULT_SPI_CFG = 0, + parameter DATA_WIDTH = 8, + parameter NUM_OF_SDI = 1, + parameter [1:0] SDI_DELAY = 2'b00, + parameter ECHO_SCLK = 0, + parameter [2:0]CMD_TRANSFER = 3'b000 +) ( + input clk, + input resetn, + + // spi io + input [NUM_OF_SDI-1:0] sdi, + output sdo_int, + input echo_sclk, + + // spi data + input [(DATA_WIDTH-1):0] sdo_data, + input sdo_data_valid, + output reg sdo_data_ready, + + output [(NUM_OF_SDI * DATA_WIDTH-1):0] sdi_data, + output reg sdi_data_valid, + input sdi_data_ready, + + // cfg and status + input sdo_enabled, + input sdi_enabled, + input [15:0] current_cmd, + input sdo_idle_state, + input [ 7:0] left_aligned, + input [ 7:0] word_length, + + // timing from main fsm + input sample_sdo, + output reg sdo_io_ready, + input transfer_active, + input trigger_tx, + input trigger_rx, + input first_bit, + input cs_activate, + output end_of_sdi_latch +); + + reg [ 7:0] sdi_counter = 8'b0; + reg [(DATA_WIDTH-1):0] data_sdo_shift = 'h0; + reg [ SDI_DELAY+1:0] trigger_rx_d = {(SDI_DELAY+2){1'b0}}; + reg [(DATA_WIDTH-1):0] aligned_sdo_data, sdo_data_d; + + wire trigger_rx_s; + wire [2:0] current_instr = current_cmd[14:12]; + wire last_sdi_bit; + + always @(posedge clk) begin + if (resetn == 1'b0) begin + sdo_data_ready <= 1'b0; + end else if (sdo_toshiftreg) begin + sdo_data_ready <= 1'b1; + end else if (sdo_data_valid == 1'b1) begin + sdo_data_ready <= 1'b0; + end + end + + // pipelined shifter for sdo_data + always @(posedge clk ) begin + if (resetn == 1'b0) begin + aligned_sdo_data <= 0; + sdo_io_ready <= 1'b0; + end else begin + if (transfer_active == 1'b1 && trigger_tx == 1'b1) begin + sdo_io_ready <= 1'b0; + end + if (sample_sdo) begin + sdo_data_d <= sdo_data; + sdo_io_ready <= 1'b1; + end + aligned_sdo_data <= sdo_data_d << left_aligned; + end + end + + // Load the SDO parallel data into the SDO shift register. In case of a custom + // data width, additional bit shifting must done at load. + always @(posedge clk) begin + if (!sdo_enabled || (current_instr != CMD_TRANSFER)) begin + data_sdo_shift <= {DATA_WIDTH{sdo_idle_state}}; + end else if (transfer_active == 1'b1 && trigger_tx == 1'b1) begin + if (first_bit == 1'b1) begin + data_sdo_shift <= aligned_sdo_data; + end else begin + data_sdo_shift <= {data_sdo_shift[(DATA_WIDTH-2):0], 1'b0}; + end + end + end + assign sdo_int = data_sdo_shift[DATA_WIDTH-1]; + assign sdo_toshiftreg = (transfer_active && trigger_tx && first_bit && sdo_enabled); + + // In case of an interface with high clock rate (SCLK > 50MHz), the latch of + // the SDI line can be delayed with 1, 2 or 3 SPI core clock cycle. + // Taking the fact that in high SCLK frequencies the pre-scaler most likely will + // be set to 0, to reduce the core clock's speed, this delay will mean that SDI will + // be latched at one of the next consecutive SCLK edge. + always @(posedge clk) begin + trigger_rx_d <= {trigger_rx_d, trigger_rx}; + end + 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. + + genvar i; + // NOTE: SPI configuration (CPOL/PHA) is only hardware configurable at this point, unless ECHO_SCLK=0 + 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