Skip to content

Commit

Permalink
SPI Engine: decouple SDO data handshake from control
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
LBFFilho committed Nov 6, 2024
1 parent 88ed47a commit 1840af0
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 59 deletions.
21 changes: 18 additions & 3 deletions docs/regmap/adi_regmap_spi_engine.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion library/spi_engine/axi_spi_engine/axi_spi_engine.v
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions library/spi_engine/axi_spi_engine/axi_spi_engine_constr.ttcl
Original file line number Diff line number Diff line change
Expand Up @@ -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}]

Expand Down
1 change: 1 addition & 0 deletions library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -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"} \
Expand Down
1 change: 1 addition & 0 deletions library/spi_engine/interfaces/interfaces_ip.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 10 additions & 12 deletions library/spi_engine/spi_engine_execution/spi_engine_execution.v
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ module spi_engine_execution #(

reg sdo_enabled = 1'b0;
reg sdi_enabled = 1'b0;
wire sdo_enabled_io;
wire sdi_enabled_io;

wire [2:0] inst = cmd[14:12];
wire [2:0] inst_d1 = cmd_d1[14:12];
Expand All @@ -163,7 +165,7 @@ module spi_engine_execution #(

wire end_of_sdi_latch;

wire sample_sdo;
wire sdo_io_ready;

(* direct_enable = "yes" *) wire cs_gen;

Expand Down Expand Up @@ -192,7 +194,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),
Expand All @@ -201,8 +202,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)
Expand All @@ -215,6 +214,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)
Expand Down Expand Up @@ -386,9 +387,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
Expand All @@ -407,14 +408,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
input sdi_data_ready,
Expand All @@ -69,8 +69,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,
Expand All @@ -81,36 +80,36 @@ module spi_engine_execution_shiftreg #(

reg [7:0] sdi_counter = 8'b0;
reg [(DATA_WIDTH-1):0] data_sdo_shift = 'h0;
reg [(DATA_WIDTH-1):0] aligned_sdo_data, sdo_data_d;
reg [(DATA_WIDTH-1):0] aligned_sdo_data, sdo_data_reg;
reg data_sdo_v;
wire sdo_toshiftreg;
wire last_sdi_bit;
reg [SDI_DELAY+1:0] trigger_rx_d = {(SDI_DELAY+2){1'b0}};
wire trigger_rx_s;
wire [2:0] current_instr = current_cmd[14:12];

always @(posedge clk) begin
// 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

// 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;
aligned_sdo_data <= sdo_data_reg << left_aligned;
end
end

Expand All @@ -130,7 +129,6 @@ module spi_engine_execution_shiftreg #(
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
Expand Down
41 changes: 18 additions & 23 deletions library/spi_engine/spi_engine_offload/spi_engine_offload.v
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -278,34 +281,14 @@ 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;
end
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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"} \
Expand Down

0 comments on commit 1840af0

Please sign in to comment.