From 4f982aa4bad0b24ab66f99835c2b04ed37a68cbc Mon Sep 17 00:00:00 2001 From: Laez Barbosa Date: Fri, 6 Sep 2024 11:50:37 -0300 Subject: [PATCH] SPI Engine: decouple SDO data handshake from control Switched SDO source control to software (memory-mapped register). SDO data can now be clocked in independently from the offload trigger. This allows lower latencies for executing transfers, since the data can be obtained from the DMA before the trigger. It also better separates the command path from the data path Signed-off-by: Laez Barbosa --- docs/regmap/adi_regmap_spi_engine.txt | 21 +++++++-- .../axi_spi_engine/axi_spi_engine.v | 16 ++++++- .../axi_spi_engine/axi_spi_engine_constr.ttcl | 3 ++ .../axi_spi_engine/axi_spi_engine_ip.tcl | 1 + .../spi_engine/interfaces/interfaces_ip.tcl | 1 + .../spi_engine_execution.v | 22 ++++----- .../spi_engine_execution_shiftreg.v | 46 +++++++++---------- .../spi_engine_offload/spi_engine_offload.v | 41 ++++++++--------- .../spi_engine_offload_ip.tcl | 1 + 9 files changed, 89 insertions(+), 63 deletions(-) diff --git a/docs/regmap/adi_regmap_spi_engine.txt b/docs/regmap/adi_regmap_spi_engine.txt index 46833de568..23e7f17fa1 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.01. +Version of the peripheral. Follows semantic versioning. Current version 1.04.00. ENDREG FIELD @@ -19,13 +19,13 @@ RO ENDFIELD FIELD -[15:8] 0x00000003 +[15:8] 0x00000004 VERSION_MINOR RO ENDFIELD FIELD -[7:0] 0x00000001 +[7:0] 0x00000000 VERSION_PATCH RO ENDFIELD @@ -447,6 +447,21 @@ ENDFIELD ############################################################################################ ############################################################################################ +REG +0x43 +OFFLOAD0_SDO_SRC_SEL +ENDREG + +FIELD +[31:0] 0x00000000 +OFFLOAD0_SDO_SRC_SEL +RW +Selects data source for SDO offload. 0=SDO memory, 1=SDO stream (DMA). +ENDFIELD + +############################################################################################ +############################################################################################ + REG 0x44 OFFLOAD0_CDM_FIFO 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 2c0a9f37c9..fd276eba47 100644 --- a/library/spi_engine/axi_spi_engine/axi_spi_engine.v +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine.v @@ -123,6 +123,7 @@ module axi_spi_engine #( output offload0_sdo_wr_en, output [(DATA_WIDTH-1):0] offload0_sdo_wr_data, + output offload0_sdo_src_sel, output offload0_mem_reset, output offload0_enable, @@ -133,7 +134,7 @@ module axi_spi_engine #( input [7:0] offload_sync_data ); - localparam PCORE_VERSION = 'h010301; + localparam PCORE_VERSION = 'h010400; localparam S_AXI = 0; localparam UP_FIFO = 1; @@ -298,6 +299,7 @@ module axi_spi_engine #( reg offload0_enable_reg; reg offload0_mem_reset_reg; + reg offload0_sdo_src_sel_reg; wire offload0_enabled_s; // the software reset should reset all the registers @@ -306,12 +308,14 @@ module axi_spi_engine #( up_irq_mask <= 'h00; offload0_enable_reg <= 1'b0; offload0_mem_reset_reg <= 1'b0; + offload0_sdo_src_sel_reg <= 1'b0; end else begin if (up_wreq_s) begin case (up_waddr_s) 8'h20: up_irq_mask <= up_wdata_s; 8'h40: offload0_enable_reg <= up_wdata_s[0]; 8'h42: offload0_mem_reset_reg <= up_wdata_s[0]; + 8'h43: offload0_sdo_src_sel_reg <= up_wdata_s[0]; endcase end end @@ -362,6 +366,7 @@ module axi_spi_engine #( 8'h3c: up_rdata_ff <= sdi_fifo_out_data; /* PEEK register */ 8'h40: up_rdata_ff <= {offload0_enable_reg}; 8'h41: up_rdata_ff <= {offload0_enabled_s}; + 8'h43: up_rdata_ff <= {offload0_sdo_src_sel_reg}; 8'h80: up_rdata_ff <= CFG_INFO_0; 8'h81: up_rdata_ff <= CFG_INFO_1; 8'h82: up_rdata_ff <= CFG_INFO_2; @@ -649,6 +654,15 @@ module axi_spi_engine #( .out_clk (spi_clk), .out_bits (offload0_mem_reset)); + sync_bits #( + .NUM_OF_BITS (1), + .ASYNC_CLK (ASYNC_SPI_CLK) + ) i_offload_sdo_src_sel_sync ( + .in_bits (offload0_sdo_src_sel_reg), + .out_resetn (spi_resetn), + .out_clk (spi_clk), + .out_bits (offload0_sdo_src_sel)); + sync_bits #( .NUM_OF_BITS (3), .ASYNC_CLK (ASYNC_SPI_CLK) diff --git a/library/spi_engine/axi_spi_engine/axi_spi_engine_constr.ttcl b/library/spi_engine/axi_spi_engine/axi_spi_engine_constr.ttcl index 3170323468..1f2de49275 100644 --- a/library/spi_engine/axi_spi_engine/axi_spi_engine_constr.ttcl +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine_constr.ttcl @@ -26,6 +26,9 @@ set_false_path -quiet \ set_false_path -quiet \ -to [get_cells -quiet -hierarchical -filter {NAME =~ *i_offload_enabled_sync/cdc_sync_stage1_reg* && IS_SEQUENTIAL}] +set_false_path -quiet \ + -to [get_cells -quiet -hierarchical -filter {NAME =~ *i_offload_sdo_src_sel_sync/cdc_sync_stage1_reg* && IS_SEQUENTIAL}] + set_false_path -quiet \ -to [get_cells -quiet -hierarchical -filter {NAME =~ *i_offload_mem_reset_sync/cdc_sync_stage1_reg* && IS_SEQUENTIAL}] diff --git a/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl b/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl index b775c377c7..c8252389a7 100644 --- a/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl @@ -57,6 +57,7 @@ adi_add_bus "spi_engine_offload_ctrl0" "master" \ { "offload0_cmd_wr_data" "cmd_wr_data"} \ { "offload0_sdo_wr_en" "sdo_wr_en"} \ { "offload0_sdo_wr_data" "sdo_wr_data"} \ + { "offload0_sdo_src_sel" "sdo_src_sel"} \ { "offload0_enable" "enable"} \ { "offload0_enabled" "enabled"} \ { "offload0_mem_reset" "mem_reset"} \ diff --git a/library/spi_engine/interfaces/interfaces_ip.tcl b/library/spi_engine/interfaces/interfaces_ip.tcl index fa1cc602e8..8626dc48f8 100644 --- a/library/spi_engine/interfaces/interfaces_ip.tcl +++ b/library/spi_engine/interfaces/interfaces_ip.tcl @@ -39,6 +39,7 @@ adi_if_ports output 1 cmd_wr_en adi_if_ports output 16 cmd_wr_data adi_if_ports output 1 sdo_wr_en adi_if_ports output -1 sdo_wr_data +adi_if_ports output 1 sdo_src_sel adi_if_ports output 1 mem_reset adi_if_ports output 1 enable adi_if_ports input 1 enabled 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 4dd61f6732..9c579d7aad 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution.v +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution.v @@ -132,6 +132,8 @@ module spi_engine_execution #( reg sdo_enabled = 1'b0; reg sdi_enabled = 1'b0; + wire sdo_enabled_io; + wire sdi_enabled_io; wire sdo_int_s; @@ -165,7 +167,7 @@ module spi_engine_execution #( wire end_of_sdi_latch; - wire sample_sdo; + wire sdo_io_ready; (* direct_enable = "yes" *) wire cs_gen; @@ -194,7 +196,6 @@ module spi_engine_execution #( .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), @@ -203,8 +204,6 @@ module spi_engine_execution #( .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) @@ -217,6 +216,8 @@ module spi_engine_execution #( sdi_enabled <= cmd[9]; end end + assign sdo_enabled_io = (exec_transfer_cmd) ? cmd[8] : sdo_enabled; + assign sdi_enabled_io = (exec_transfer_cmd) ? cmd[9] : sdi_enabled; always @(posedge clk) begin if (cmd_ready & cmd_valid) @@ -388,9 +389,9 @@ module spi_engine_execution #( assign sync = cmd_d1[7:0]; assign io_ready1 = (sdi_data_valid == 1'b0 || sdi_data_ready == 1'b1) && - (sdo_enabled == 1'b0 || last_transfer == 1'b1 || sdo_io_ready == 1'b1); + (sdo_enabled_io == 1'b0 || 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); + (sdo_enabled_io == 1'b0 || last_transfer == 1'b1 || sdo_io_ready == 1'b1); always @(posedge clk) begin if (idle == 1'b1) begin @@ -409,14 +410,11 @@ module spi_engine_execution #( wait_for_io <= 1'b0; end else begin if (exec_transfer_cmd == 1'b1) begin - wait_for_io <= 1'b1; - transfer_active <= 1'b0; + wait_for_io <= !io_ready1; + transfer_active <= io_ready1; end else if (wait_for_io == 1'b1 && io_ready1 == 1'b1) begin wait_for_io <= 1'b0; - if (last_transfer == 1'b0) - transfer_active <= 1'b1; - else - transfer_active <= 1'b0; + transfer_active <= !last_transfer; end else if (transfer_active == 1'b1 && end_of_word == 1'b1) begin if (last_transfer == 1'b1 || io_ready2 == 1'b0) transfer_active <= 1'b0; diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v b/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v index 2298a20861..fe52996f5c 100644 --- a/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution_shiftreg.v @@ -55,7 +55,7 @@ module spi_engine_execution_shiftreg #( // spi data input [(DATA_WIDTH-1):0] sdo_data, input sdo_data_valid, - output reg sdo_data_ready, + output sdo_data_ready, output [(NUM_OF_SDI * DATA_WIDTH-1):0] sdi_data, output reg sdi_data_valid, @@ -70,8 +70,7 @@ module spi_engine_execution_shiftreg #( input [ 7:0] word_length, // timing from main fsm - input sample_sdo, - output reg sdo_io_ready, + output sdo_io_ready, input transfer_active, input trigger_tx, input trigger_rx, @@ -83,19 +82,26 @@ module spi_engine_execution_shiftreg #( 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 + reg [(DATA_WIDTH-1):0] aligned_sdo_data, sdo_data_reg; + reg data_sdo_v; + wire sdo_toshiftreg; + wire last_sdi_bit; + wire trigger_rx_s; + wire [2:0] current_instr = current_cmd[14:12]; + + // sdo data handshake + assign sdo_data_ready = (!data_sdo_v) || sdo_toshiftreg; + assign sdo_io_ready = data_sdo_v; + 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; + data_sdo_v <= 1'b0; + end else begin + if (sdo_data_ready && sdo_data_valid) begin + data_sdo_v <= 1'b1; + sdo_data_reg <= sdo_data; + end else if (sdo_toshiftreg) begin + data_sdo_v <= 1'b0; + end end end @@ -103,16 +109,8 @@ module spi_engine_execution_shiftreg #( 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; + aligned_sdo_data <= sdo_data_reg << left_aligned; end end diff --git a/library/spi_engine/spi_engine_offload/spi_engine_offload.v b/library/spi_engine/spi_engine_offload/spi_engine_offload.v index d1d294037a..ed7ffd6da7 100644 --- a/library/spi_engine/spi_engine_offload/spi_engine_offload.v +++ b/library/spi_engine/spi_engine_offload/spi_engine_offload.v @@ -52,6 +52,7 @@ module spi_engine_offload #( input ctrl_sdo_wr_en, input [(DATA_WIDTH-1):0] ctrl_sdo_wr_data, + input ctrl_sdo_src_sel, input ctrl_enable, output ctrl_enabled, @@ -95,7 +96,7 @@ module spi_engine_offload #( localparam SDO_SOURCE_MEM = 1'b0; reg spi_active = 1'b0; - reg sdo_source_select = SDO_SOURCE_MEM; + wire sdo_source_select; reg [CMD_MEM_ADDRESS_WIDTH-1:0] ctrl_cmd_wr_addr = 'h00; reg [CMD_MEM_ADDRESS_WIDTH-1:0] spi_cmd_rd_addr = 'h00; @@ -111,10 +112,12 @@ module spi_engine_offload #( wire [CMD_MEM_ADDRESS_WIDTH-1:0] spi_cmd_rd_addr_next; wire spi_enable; wire trigger_posedge; + reg sdo_mem_valid; + assign sdo_source_select = ctrl_sdo_src_sel; assign cmd_valid = spi_active; assign sdo_data_valid = (sdo_source_select == SDO_SOURCE_STREAM) ? - s_axis_sdo_valid : spi_active; + s_axis_sdo_valid : (spi_active && sdo_mem_valid); assign s_axis_sdo_ready = (sdo_source_select == SDO_SOURCE_STREAM) ? sdo_data_ready : 1'b0; assign offload_sdi_valid = sdi_data_valid; @@ -278,7 +281,7 @@ module spi_engine_offload #( if (!spi_active) begin // start offload when we have a valid trigger, offload is enabled and // the DMA is enabled - if (trigger_posedge && spi_enable && (offload_sdi_ready || (SDO_STREAMING && s_axis_sdo_valid))) + if (trigger_posedge && spi_enable) spi_active <= 1'b1; end else if (cmd_ready && (spi_cmd_rd_addr_next == ctrl_cmd_wr_addr)) begin spi_active <= 1'b0; @@ -286,26 +289,6 @@ module spi_engine_offload #( end end - always @(posedge spi_clk ) begin - if (!spi_resetn) begin - sdo_source_select <= SDO_SOURCE_MEM; - end else begin - if (SDO_STREAMING) begin - if (sdo_source_select == SDO_SOURCE_MEM) begin - // switch to streaming sdo after we're done with reading the sdo memory - if (sdo_data_valid && sdo_data_ready && (spi_sdo_rd_addr+1 == ctrl_sdo_wr_addr)|| (ctrl_sdo_wr_addr==0 && spi_active) ) begin - sdo_source_select <= SDO_SOURCE_STREAM; - end - end else begin - // switch back to sdo memory after last command accepted - if (cmd_ready && (spi_cmd_rd_addr_next == ctrl_cmd_wr_addr)) begin - sdo_source_select <= SDO_SOURCE_MEM; - end - end - end - end - end - always @(posedge spi_clk) begin if (!cmd_valid) begin spi_cmd_rd_addr <= 'h00; @@ -322,6 +305,18 @@ module spi_engine_offload #( end end + always @(posedge spi_clk ) begin + if (!spi_resetn) begin + sdo_mem_valid <= 1'b0; + end else begin + if (!spi_active && trigger_posedge && spi_enable) begin + sdo_mem_valid <= (ctrl_sdo_wr_addr != 'h00); // if ctrl_sdo_wr_addr is 0, mem is empty + end else if (sdo_data_ready && spi_active && sdo_mem_valid && (spi_sdo_rd_addr + 1'b1 == ctrl_sdo_wr_addr)) begin + sdo_mem_valid <= 1'b0; + end + end + end + always @(posedge ctrl_clk) begin if (ctrl_mem_reset) ctrl_cmd_wr_addr <= 'h00; diff --git a/library/spi_engine/spi_engine_offload/spi_engine_offload_ip.tcl b/library/spi_engine/spi_engine_offload/spi_engine_offload_ip.tcl index 0b1170a243..5a6aabf756 100644 --- a/library/spi_engine/spi_engine_offload/spi_engine_offload_ip.tcl +++ b/library/spi_engine/spi_engine_offload/spi_engine_offload_ip.tcl @@ -54,6 +54,7 @@ adi_add_bus "spi_engine_offload_ctrl" "slave" \ { "ctrl_cmd_wr_data" "cmd_wr_data"} \ { "ctrl_sdo_wr_en" "sdo_wr_en"} \ { "ctrl_sdo_wr_data" "sdo_wr_data"} \ + { "ctrl_sdo_src_sel" "sdo_src_sel"} \ { "ctrl_enable" "enable"} \ { "ctrl_enabled" "enabled"} \ { "ctrl_mem_reset" "mem_reset"} \