spi_engine: Add additional synchronization FIFO's to axi_spi_engine

Add additional synchronization FIFOs to several interfaces of the
axi_spi_engine module, to prevent metastability and timing issues in
case when the system clock and the SPI clock are asynchronous.
main
Istvan Csomortani 2019-01-17 12:21:51 +00:00 committed by István Csomortáni
parent 91801bfe0d
commit 40fbb37d6f
4 changed files with 150 additions and 32 deletions

View File

@ -5,6 +5,7 @@
LIBRARY_NAME := axi_spi_engine
GENERIC_DEPS += axi_spi_engine_constr.xdc
GENERIC_DEPS += axi_spi_engine.v
XILINX_DEPS += ../../common/ad_rst.v

View File

@ -120,8 +120,8 @@ module axi_spi_engine #(
output offload0_sdo_wr_en,
output [(DATA_WIDTH-1):0] offload0_sdo_wr_data,
output reg offload0_mem_reset,
output reg offload0_enable,
output offload0_mem_reset,
output offload0_enable,
input offload0_enabled
);
@ -278,18 +278,22 @@ module axi_spi_engine #(
end
end
reg offload0_enable_reg;
reg offload0_mem_reset_reg;
wire offload0_enabled_s;
// the software reset should reset all the registers
always @(posedge clk) begin
if (up_sw_resetn == 1'b0) begin
up_irq_mask <= 'h00;
offload0_enable <= 1'b0;
offload0_mem_reset <= 1'b0;
offload0_enable_reg <= 1'b0;
offload0_mem_reset_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 <= up_wdata_s[0];
8'h42: offload0_mem_reset <= up_wdata_s[0];
8'h40: offload0_enable_reg <= up_wdata_s[0];
8'h42: offload0_mem_reset_reg <= up_wdata_s[0];
endcase
end
end
@ -319,8 +323,8 @@ module axi_spi_engine #(
8'h36: up_rdata_ff <= sdi_fifo_level;
8'h3a: up_rdata_ff <= sdi_fifo_out_data;
8'h3c: up_rdata_ff <= sdi_fifo_out_data; /* PEEK register */
8'h40: up_rdata_ff <= {offload0_enable};
8'h41: up_rdata_ff <= {offload0_enabled};
8'h40: up_rdata_ff <= {offload0_enable_reg};
8'h41: up_rdata_ff <= {offload0_enabled_s};
default: up_rdata_ff <= 'h00;
endcase
end
@ -431,6 +435,9 @@ module axi_spi_engine #(
.m_axis_level(sdi_fifo_level)
);
generate if (ASYNC_SPI_CLK) begin
// synchronization FIFO for the SYNC interface
util_axis_fifo #(
.DATA_WIDTH(8),
.ASYNC_CLK(ASYNC_SPI_CLK),
@ -451,10 +458,99 @@ module axi_spi_engine #(
.m_axis_level()
);
// synchronization FIFO for the offload command interface
wire up_offload0_cmd_wr_en_s;
wire [15:0] up_offload0_cmd_wr_data_s;
util_axis_fifo #(
.DATA_WIDTH(16),
.ASYNC_CLK(ASYNC_SPI_CLK),
.ADDRESS_WIDTH(SYNC_FIFO_ADDRESS_WIDTH),
.S_AXIS_REGISTERED(0)
) i_offload_cmd_fifo (
.s_axis_aclk(clk),
.s_axis_aresetn(up_sw_resetn),
.s_axis_ready(),
.s_axis_valid(up_offload0_cmd_wr_en_s),
.s_axis_data(up_offload0_cmd_wr_data_s),
.s_axis_empty(),
.m_axis_aclk(spi_clk),
.m_axis_aresetn(spi_resetn),
.m_axis_ready(1'b1),
.m_axis_valid(offload0_cmd_wr_en),
.m_axis_data(offload0_cmd_wr_data),
.m_axis_level()
);
assign up_offload0_cmd_wr_en_s = up_wreq_s == 1'b1 && up_waddr_s == 8'h44;
assign up_offload0_cmd_wr_data_s = up_wdata_s[15:0];
// synchronization FIFO for the offload SDO interface
wire up_offload0_sdo_wr_en_s;
wire [DATA_WIDTH-1:0] up_offload0_sdo_wr_data_s;
util_axis_fifo #(
.DATA_WIDTH(DATA_WIDTH),
.ASYNC_CLK(ASYNC_SPI_CLK),
.ADDRESS_WIDTH(SYNC_FIFO_ADDRESS_WIDTH),
.S_AXIS_REGISTERED(0)
) i_offload_sdo_fifo (
.s_axis_aclk(clk),
.s_axis_aresetn(up_sw_resetn),
.s_axis_ready(),
.s_axis_valid(up_offload0_sdo_wr_en_s),
.s_axis_data(up_offload0_sdo_wr_data_s),
.s_axis_empty(),
.m_axis_aclk(spi_clk),
.m_axis_aresetn(spi_resetn),
.m_axis_ready(1'b1),
.m_axis_valid(offload0_sdo_wr_en),
.m_axis_data(offload0_sdo_wr_data),
.m_axis_level()
);
assign up_offload0_sdo_wr_en_s = up_wreq_s == 1'b1 && up_waddr_s == 8'h45;
assign up_offload0_sdo_wr_data_s = up_wdata_s[DATA_WIDTH-1:0];
end else begin /* ASYNC_SPI_CLK == 0 */
assign sync_fifo_valid = sync_valid;
assign sync_fifo_data = sync_data;
assign offload0_cmd_wr_en = up_wreq_s == 1'b1 && up_waddr_s == 8'h44;
assign offload0_cmd_wr_data = up_wdata_s[15:0];
assign offload0_sdo_wr_en = up_wreq_s == 1'b1 && up_waddr_s == 8'h45;
assign offload0_sdo_wr_data = up_wdata_s[7:0];
assign offload0_sdo_wr_data = up_wdata_s[DATA_WIDTH-1:0];
end
endgenerate
sync_bits #(
.NUM_OF_BITS (1),
.ASYNC_CLK (ASYNC_SPI_CLK)
) i_offload_enable_sync (
.in (offload0_enable_reg),
.out_resetn (spi_resetn),
.out_clk (spi_clk),
.out (offload0_enable));
sync_bits #(
.NUM_OF_BITS (1),
.ASYNC_CLK (ASYNC_SPI_CLK)
) i_offload_enabled_sync (
.in (offload0_enabled),
.out_resetn (up_sw_resetn),
.out_clk (clk),
.out (offload0_enabled_s));
sync_bits #(
.NUM_OF_BITS (1),
.ASYNC_CLK (ASYNC_SPI_CLK)
) i_offload_mem_reset_sync (
.in (offload0_mem_reset_reg),
.out_resetn (spi_resetn),
.out_clk (spi_clk),
.out (offload0_mem_reset));
endmodule

View File

@ -0,0 +1,20 @@
set_property ASYNC_REG TRUE \
[get_cells -quiet -hierarchical *cdc_sync_stage1_reg*] \
[get_cells -quiet -hierarchical *cdc_sync_stage2_reg*]
set_false_path -quiet \
-from [get_cells -quiet -hierarchical -filter {NAME =~ *cdc_sync_stage0_reg* && IS_SEQUENTIAL}] \
-to [get_cells -quiet -hierarchical -filter {NAME =~ *cdc_sync_stage1_reg* && IS_SEQUENTIAL}]
set_false_path -quiet \
-from [get_cells -quiet -hierarchical -filter {NAME =~ *offload0_enable_reg* && IS_SEQUENTIAL}] \
-to [get_cells -quiet -hierarchical -filter {NAME =~ *cdc_sync_stage1_reg* && IS_SEQUENTIAL}]
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 \
-from [get_cells -quiet -hierarchical -filter {NAME =~ *offload0_mem_reset_reg* && IS_SEQUENTIAL}] \
-to [get_cells -quiet -hierarchical -filter {NAME =~ *cdc_sync_stage1_reg* && IS_SEQUENTIAL}]

View File

@ -5,9 +5,10 @@ source $ad_hdl_dir/library/scripts/adi_ip.tcl
adi_ip_create axi_spi_engine
adi_ip_files axi_spi_engine [list \
"axi_spi_engine.v" \
"$ad_hdl_dir/library/common/up_axi.v" \
"$ad_hdl_dir/library/common/ad_rst.v" \
"axi_spi_engine_constr.xdc" \
"axi_spi_engine.v" \
]
adi_ip_properties axi_spi_engine