From e6b58e8a20fbcf8aade27a951aa32de0e1ce1ef4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 1 Apr 2015 12:12:11 +0200 Subject: [PATCH] Add SPI Engine framework SPI Engine is a highly flexible and powerful SPI controller framework. It consist out of multiple sub-modules which communicate over well defined interfaces. This allows a high degree of flexibility and re-usability while at the same time staying highly customizable and easily extensible. Currently included are four components: * SPI Engine execution module: The excution module is responsible for handling the low-level physical interface SPI logic. * SPI Engine AXI interface module: The AXI interface module allows memory mapped acccess to a SPI bus control stream and can be used to implement a software driver that controls the SPI bus. * SPI Engine offload module: The offload module allows to store a predefined SPI Engine command and data stream which will be send out when a external trigger signal is asserted. * SPI Engine interconnect module: The interconnect module allows to combine multiple control streams into a single stream giving multiple control modules access to a execution module. For more information see: http://wiki.analog.com/resources/fpga/peripherals/spi_engine Signed-off-by: Lars-Peter Clausen --- library/Makefile | 8 + library/spi_engine/axi_spi_engine/Makefile | 49 +++ .../axi_spi_engine/axi_spi_engine.v | 335 ++++++++++++++++++ .../axi_spi_engine/axi_spi_engine_ip.tcl | 62 ++++ .../spi_engine/interfaces/spi_engine_ctrl.xml | 13 + .../interfaces/spi_engine_ctrl_rtl.xml | 189 ++++++++++ .../interfaces/spi_engine_offload_ctrl.xml | 13 + .../spi_engine_offload_ctrl_rtl.xml | 113 ++++++ library/spi_engine/interfaces/spi_master.xml | 13 + .../spi_engine/interfaces/spi_master_rtl.xml | 85 +++++ .../spi_engine/spi_engine_execution/Makefile | 42 +++ .../spi_engine_execution.v | 313 ++++++++++++++++ .../spi_engine_execution_ip.tcl | 46 +++ .../spi_engine_interconnect/Makefile | 42 +++ .../spi_engine_interconnect.v | 107 ++++++ .../spi_engine_interconnect_ip.tcl | 54 +++ .../spi_engine/spi_engine_offload/Makefile | 42 +++ .../spi_engine_offload/spi_engine_offload.v | 178 ++++++++++ .../spi_engine_offload_ip.tcl | 56 +++ 19 files changed, 1760 insertions(+) create mode 100644 library/spi_engine/axi_spi_engine/Makefile create mode 100644 library/spi_engine/axi_spi_engine/axi_spi_engine.v create mode 100644 library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl create mode 100644 library/spi_engine/interfaces/spi_engine_ctrl.xml create mode 100644 library/spi_engine/interfaces/spi_engine_ctrl_rtl.xml create mode 100644 library/spi_engine/interfaces/spi_engine_offload_ctrl.xml create mode 100644 library/spi_engine/interfaces/spi_engine_offload_ctrl_rtl.xml create mode 100644 library/spi_engine/interfaces/spi_master.xml create mode 100644 library/spi_engine/interfaces/spi_master_rtl.xml create mode 100644 library/spi_engine/spi_engine_execution/Makefile create mode 100644 library/spi_engine/spi_engine_execution/spi_engine_execution.v create mode 100644 library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl create mode 100644 library/spi_engine/spi_engine_interconnect/Makefile create mode 100644 library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v create mode 100644 library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl create mode 100644 library/spi_engine/spi_engine_offload/Makefile create mode 100644 library/spi_engine/spi_engine_offload/spi_engine_offload.v create mode 100644 library/spi_engine/spi_engine_offload/spi_engine_offload_ip.tcl diff --git a/library/Makefile b/library/Makefile index 92c973333..77e255891 100644 --- a/library/Makefile +++ b/library/Makefile @@ -41,6 +41,10 @@ clean: make -C axi_spdif_tx clean make -C controllerperipheralhdladi_pcore clean make -C cordic_demod clean + make -C spi_engine/axi_spi_engine clean + make -C spi_engine/spi_engine_execution clean + make -C spi_engine/spi_engine_interconnect clean + make -C spi_engine/spi_engine_offload clean make -C util_adc_pack clean make -C util_adcfifo clean make -C util_axis_fifo clean @@ -94,6 +98,10 @@ lib: -make -C axi_spdif_tx -make -C controllerperipheralhdladi_pcore -make -C cordic_demod + -make -C spi_engine/axi_spi_engine + -make -C spi_engine/spi_engine_execution + -make -C spi_engine/spi_engine_interconnect + -make -C spi_engine/spi_engine_offload -make -C util_adc_pack -make -C util_adcfifo -make -C util_axis_fifo diff --git a/library/spi_engine/axi_spi_engine/Makefile b/library/spi_engine/axi_spi_engine/Makefile new file mode 100644 index 000000000..def1365cf --- /dev/null +++ b/library/spi_engine/axi_spi_engine/Makefile @@ -0,0 +1,49 @@ +#################################################################################### +#################################################################################### +## Copyright 2011(c) Analog Devices, Inc. +## Auto-generated, do not modify! +#################################################################################### +#################################################################################### + +M_DEPS := axi_spi_engine_ip.tcl +M_DEPS += ../../scripts/adi_env.tcl +M_DEPS += ../../scripts/adi_ip.tcl +M_DEPS += axi_spi_engine.v +M_DEPS += ../../common/sync_bits.v +M_DEPS += ../../common/sync_gray.v +M_DEPS += ../../common/up_axi.v +M_DEPS += ../../common/ad_rst.v +M_DEPS += ../../util_axis_fifo/util_axis_fifo.xpr + +M_VIVADO := vivado -mode batch -source + +M_FLIST := *.cache +M_FLIST += *.data +M_FLIST += *.xpr +M_FLIST += *.log +M_FLIST += component.xml +M_FLIST += *.jou +M_FLIST += xgui +M_FLIST += .Xil + + + +.PHONY: all dep clean clean-all +all: dep axi_spi_engine.xpr + + +clean:clean-all + + +clean-all: + rm -rf $(M_FLIST) + + +axi_spi_engine.xpr: $(M_DEPS) + rm -rf $(M_FLIST) + $(M_VIVADO) axi_spi_engine_ip.tcl >> axi_spi_engine_ip.log 2>&1 + +dep: + make -C ../../util_axis_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 new file mode 100644 index 000000000..8db1a93a9 --- /dev/null +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine.v @@ -0,0 +1,335 @@ + +module axi_spi_engine ( + // Slave AXI interface + input s_axi_aclk, + input s_axi_aresetn, + + input s_axi_awvalid, + input [31:0] s_axi_awaddr, + output s_axi_awready, + input [2:0] s_axi_awprot, + input s_axi_wvalid, + input [31:0] s_axi_wdata, + input [ 3:0] s_axi_wstrb, + output s_axi_wready, + output s_axi_bvalid, + output [ 1:0] s_axi_bresp, + input s_axi_bready, + input s_axi_arvalid, + input [31:0] s_axi_araddr, + output s_axi_arready, + input [2:0] s_axi_arprot, + output s_axi_rvalid, + input s_axi_rready, + output [ 1:0] s_axi_rresp, + output [31:0] s_axi_rdata, + + output reg irq, + + + // SPI signals + input spi_clk, + + output spi_resetn, + + input cmd_ready, + output cmd_valid, + output [15:0] cmd_data, + + input sdo_data_ready, + output sdo_data_valid, + output [7:0] sdo_data, + + output sdi_data_ready, + input sdi_data_valid, + input [7:0] sdi_data, + + output sync_ready, + input sync_valid, + input [7:0] sync_data, + + // Offload ctrl signals + output offload0_cmd_wr_en, + output [15:0] offload0_cmd_wr_data, + + output offload0_sdo_wr_en, + output [7:0] offload0_sdo_wr_data, + + output reg offload0_mem_reset, + output reg offload0_enable, + input offload0_enabled +); + +parameter CMD_FIFO_ADDRESS_WIDTH = 4; +parameter SDO_FIFO_ADDRESS_WIDTH = 5; +parameter SDI_FIFO_ADDRESS_WIDTH = 5; + +parameter ASYNC_SPI_CLK = 0; + +parameter NUM_OFFLOAD = 0; + +parameter OFFLOAD0_CMD_MEM_ADDR_WIDTH = 4; +parameter OFFLOAD0_SDO_MEM_ADDR_WIDTH = 4; + +parameter PCORE_ID = 'h00; +localparam PCORE_VERSION = 'h010061; + +wire [CMD_FIFO_ADDRESS_WIDTH:0] cmd_fifo_room; +wire cmd_fifo_almost_empty; + +wire [15:0] cmd_fifo_in_data; +wire cmd_fifo_in_ready; +wire cmd_fifo_in_valid; + +wire [SDO_FIFO_ADDRESS_WIDTH:0] sdo_fifo_room; +wire sdo_fifo_almost_empty; + +wire [7:0] sdo_fifo_in_data; +wire sdo_fifo_in_ready; +wire sdo_fifo_in_valid; + +wire [SDI_FIFO_ADDRESS_WIDTH:0] sdi_fifo_level; +wire sdi_fifo_almost_full; + +wire [7:0] sdi_fifo_out_data; +wire sdi_fifo_out_ready; +wire sdi_fifo_out_valid; + +reg up_reset = 1'b1; +wire up_resetn = ~up_reset; + +reg [31:0] up_rdata = 'd0; +reg up_wack = 1'b0; +reg up_rack = 1'b0; +wire up_wreq; +wire up_rreq; +wire [31:0] up_wdata; +wire [ 7:0] up_waddr; +wire [ 7:0] up_raddr; + +// Scratch register +reg [31:0] up_scratch = 'h00; + +reg [7:0] sync_id = 'h00; +reg sync_id_pending = 1'b0; + +up_axi #( + .PCORE_ADDR_WIDTH (8) +) i_up_axi ( + .up_rstn(s_axi_aresetn), + .up_clk(s_axi_aclk), + .up_axi_awvalid(s_axi_awvalid), + .up_axi_awaddr(s_axi_awaddr), + .up_axi_awready(s_axi_awready), + .up_axi_wvalid(s_axi_wvalid), + .up_axi_wdata(s_axi_wdata), + .up_axi_wstrb(s_axi_wstrb), + .up_axi_wready(s_axi_wready), + .up_axi_bvalid(s_axi_bvalid), + .up_axi_bresp(s_axi_bresp), + .up_axi_bready(s_axi_bready), + .up_axi_arvalid(s_axi_arvalid), + .up_axi_araddr(s_axi_araddr), + .up_axi_arready(s_axi_arready), + .up_axi_rvalid(s_axi_rvalid), + .up_axi_rresp(s_axi_rresp), + .up_axi_rdata(s_axi_rdata), + .up_axi_rready(s_axi_rready), + .up_wreq(up_wreq), + .up_waddr(up_waddr), + .up_wdata(up_wdata), + .up_wack(up_wack), + .up_rreq(up_rreq), + .up_raddr(up_raddr), + .up_rdata(up_rdata), + .up_rack(up_rack) +); + +// IRQ handling +reg [3:0] up_irq_mask = 'h0; +wire [3:0] up_irq_source; +wire [3:0] up_irq_pending; + +assign up_irq_source = { + sync_id_pending, + sdi_fifo_almost_full, + sdo_fifo_almost_empty, + cmd_fifo_almost_empty +}; + +assign up_irq_pending = up_irq_mask & up_irq_source; + +always @(posedge s_axi_aclk) begin + if (s_axi_aresetn == 1'b0) + irq <= 1'b0; + else + irq <= |up_irq_pending; +end + +always @(posedge s_axi_aclk) begin + if (s_axi_aresetn == 1'b0) begin + up_wack <= 1'b0; + up_scratch <= 'h00; + up_reset <= 1'b1; + up_irq_mask <= 'h00; + offload0_enable <= 1'b0; + offload0_mem_reset <= 1'b0; + end else begin + up_wack <= up_wreq; + offload0_mem_reset <= 1'b0; + if (up_wreq) begin + case (up_waddr) + 8'h02: up_scratch <= up_wdata; + 8'h10: up_reset <= up_wdata; + 8'h20: up_irq_mask <= up_wdata; + 8'h40: offload0_enable <= up_wdata[0]; + 8'h42: offload0_mem_reset <= up_wdata[0]; + endcase + end + end +end + +always @(posedge s_axi_aclk) begin + if (s_axi_aresetn == 1'b0) begin + up_rack <= 'd0; + end else begin + up_rack <= up_rreq; + end +end + +always @(posedge s_axi_aclk) begin + case (up_raddr) + 8'h00: up_rdata <= PCORE_VERSION; + 8'h01: up_rdata <= PCORE_ID; + 8'h02: up_rdata <= up_scratch; + 8'h10: up_rdata <= up_reset; + 8'h20: up_rdata <= up_irq_mask; + 8'h21: up_rdata <= up_irq_pending; + 8'h22: up_rdata <= up_irq_source; + 8'h30: up_rdata <= sync_id; + 8'h34: up_rdata <= cmd_fifo_room; + 8'h35: up_rdata <= sdo_fifo_room; + 8'h36: up_rdata <= sdi_fifo_level; + 8'h3a: up_rdata <= sdi_fifo_out_data; + 8'h3c: up_rdata <= sdi_fifo_out_data; /* PEEK register */ + 8'h40: up_rdata <= {offload0_enable}; + 8'h41: up_rdata <= {offload0_enabled}; + default: up_rdata <= 'h00; + endcase +end + +always @(posedge s_axi_aclk) begin + if (up_resetn == 1'b0) begin + sync_id <= 'h00; + sync_id_pending <= 1'b0; + end else begin + if (sync_valid == 1'b1) begin + sync_id <= sync_data; + sync_id_pending <= 1'b1; + end else if (up_wreq == 1'b1 && up_waddr == 8'h21 && up_wdata[3] == 1'b1) begin + sync_id_pending <= 1'b0; + end + end +end + +assign sync_ready = 1'b1; + +generate if (ASYNC_SPI_CLK) begin + +wire spi_reset; +ad_rst i_spi_resetn ( + .preset(up_reset), + .clk(spi_clk), + .rst(spi_reset) +); +assign spi_resetn = ~spi_reset; +end else begin +assign spi_resetn = ~up_reset; +end +endgenerate + +/* Evaluates to true if FIFO level/room is 3/4 or above */ +`define axi_spi_engine_check_watermark(x, n) \ + (x[n] == 1'b1 || x[n-1:n-2] == 2'b11) + +assign cmd_fifo_in_valid = up_wreq == 1'b1 && up_waddr == 8'h38; +assign cmd_fifo_in_data = up_wdata[15:0]; +assign cmd_fifo_almost_empty = + `axi_spi_engine_check_watermark(cmd_fifo_room, CMD_FIFO_ADDRESS_WIDTH); + +util_axis_fifo #( + .C_DATA_WIDTH(16), + .C_CLKS_ASYNC(ASYNC_SPI_CLK), + .C_ADDRESS_WIDTH(CMD_FIFO_ADDRESS_WIDTH), + .C_S_AXIS_REGISTERED(0) +) i_cmd_fifo ( + .s_axis_aclk(s_axi_aclk), + .s_axis_aresetn(up_resetn), + .s_axis_ready(cmd_fifo_in_ready), + .s_axis_valid(cmd_fifo_in_valid), + .s_axis_data(cmd_fifo_in_data), + .s_axis_room(cmd_fifo_room), + + .m_axis_aclk(spi_clk), + .m_axis_aresetn(spi_resetn), + .m_axis_ready(cmd_ready), + .m_axis_valid(cmd_valid), + .m_axis_data(cmd_data) +); + +assign sdo_fifo_in_valid = up_wreq == 1'b1 && up_waddr == 8'h39; +assign sdo_fifo_in_data = up_wdata[7:0]; +assign sdo_fifo_almost_empty = + `axi_spi_engine_check_watermark(sdo_fifo_room, SDO_FIFO_ADDRESS_WIDTH); + +util_axis_fifo #( + .C_DATA_WIDTH(8), + .C_CLKS_ASYNC(ASYNC_SPI_CLK), + .C_ADDRESS_WIDTH(SDO_FIFO_ADDRESS_WIDTH), + .C_S_AXIS_REGISTERED(0) +) i_sdo_fifo ( + .s_axis_aclk(s_axi_aclk), + .s_axis_aresetn(up_resetn), + .s_axis_ready(sdo_fifo_in_ready), + .s_axis_valid(sdo_fifo_in_valid), + .s_axis_data(sdo_fifo_in_data), + .s_axis_room(sdo_fifo_room), + + .m_axis_aclk(spi_clk), + .m_axis_aresetn(spi_resetn), + .m_axis_ready(sdo_data_ready), + .m_axis_valid(sdo_data_valid), + .m_axis_data(sdo_data) +); + +assign sdi_fifo_out_ready = up_rreq == 1'b1 && up_raddr == 8'h3a; +assign sdi_fifo_almost_full = + `axi_spi_engine_check_watermark(sdi_fifo_level, SDI_FIFO_ADDRESS_WIDTH); + +util_axis_fifo #( + .C_DATA_WIDTH(8), + .C_CLKS_ASYNC(ASYNC_SPI_CLK), + .C_ADDRESS_WIDTH(SDI_FIFO_ADDRESS_WIDTH), + .C_S_AXIS_REGISTERED(0) +) i_sdi_fifo ( + .s_axis_aclk(spi_clk), + .s_axis_aresetn(spi_resetn), + .s_axis_ready(sdi_data_ready), + .s_axis_valid(sdi_data_valid), + .s_axis_data(sdi_data), + + .m_axis_aclk(s_axi_aclk), + .m_axis_aresetn(up_resetn), + .m_axis_ready(sdi_fifo_out_ready), + .m_axis_valid(sdi_fifo_out_valid), + .m_axis_data(sdi_fifo_out_data), + .m_axis_level(sdi_fifo_level) +); + +assign offload0_cmd_wr_en = up_wreq == 1'b1 && up_waddr == 8'h44; +assign offload0_cmd_wr_data = up_wdata[15:0]; + +assign offload0_sdo_wr_en = up_wreq == 1'b1 && up_waddr == 8'h45; +assign offload0_sdo_wr_data = up_wdata[7:0]; + +endmodule 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 new file mode 100644 index 000000000..881aacb5f --- /dev/null +++ b/library/spi_engine/axi_spi_engine/axi_spi_engine_ip.tcl @@ -0,0 +1,62 @@ +# ip + +source ../../scripts/adi_env.tcl +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/sync_bits.v" \ + "$ad_hdl_dir/library/common/sync_gray.v" \ + "$ad_hdl_dir/library/common/up_axi.v" \ + "$ad_hdl_dir/library/common/ad_rst.v" \ +] + +adi_ip_properties axi_spi_engine +# Remove auto inferred interfaces +ipx::remove_bus_interface offload0_mem_signal_reset [ipx::current_core] +ipx::remove_bus_interface spi_signal_clock [ipx::current_core] +ipx::remove_bus_interface spi_signal_reset [ipx::current_core] + +adi_ip_add_core_dependencies { \ + analog.com:user:util_axis_fifo:1.0 \ +} + +set_property physical_name {s_axi_aclk} [ipx::get_port_map CLK \ + [ipx::get_bus_interface s_axi_signal_clock [ipx::current_core]]] + +adi_add_bus "spi_engine_ctrl" "master" \ + "analog.com:interface:spi_engine_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_ctrl:1.0" \ + { + {"cmd_ready" "CMD_READY"} \ + {"cmd_valid" "CMD_VALID"} \ + {"cmd_data" "CMD_DATA"} \ + {"sdo_data_ready" "SDO_READY"} \ + {"sdo_data_valid" "SDO_VALID"} \ + {"sdo_data" "SDO_DATA"} \ + {"sdi_data_ready" "SDI_READY"} \ + {"sdi_data_valid" "SDI_VALID"} \ + {"sdi_data" "SDI_DATA"} \ + {"sync_ready" "SYNC_READY"} \ + {"sync_valid" "SYNC_VALID"} \ + {"sync_data" "SYNC_DATA"} \ + } +adi_add_bus_clock "spi_clk" "spi_engine_ctrl" "spi_resetn" "master" + +adi_add_bus "spi_engine_offload_ctrl0" "master" \ + "analog.com:interface:spi_engine_offload_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_offload_ctrl:1.0" \ + { \ + { "offload0_cmd_wr_en" "CMD_WR_EN"} \ + { "offload0_cmd_wr_data" "CMD_WR_DATA"} \ + { "offload0_sdo_wr_en" "SDO_WR_EN"} \ + { "offload0_sdo_wr_data" "SDO_WR_DATA"} \ + { "offload0_enable" "ENABLE"} \ + { "offload0_enabled" "ENABLED"} \ + { "offload0_mem_reset" "MEM_RESET"} \ + } + +adi_add_bus_clock "s_axi_aclk" "spi_engine_offload_ctrl0:s_axi" "s_axi_aresetn" + +ipx::save_core [ipx::current_core] diff --git a/library/spi_engine/interfaces/spi_engine_ctrl.xml b/library/spi_engine/interfaces/spi_engine_ctrl.xml new file mode 100644 index 000000000..0ccbf8def --- /dev/null +++ b/library/spi_engine/interfaces/spi_engine_ctrl.xml @@ -0,0 +1,13 @@ + + + analog.com + interface + spi_engine_ctrl + 1.0 + false + false + 1 + 1 + diff --git a/library/spi_engine/interfaces/spi_engine_ctrl_rtl.xml b/library/spi_engine/interfaces/spi_engine_ctrl_rtl.xml new file mode 100644 index 000000000..2ba4f27f6 --- /dev/null +++ b/library/spi_engine/interfaces/spi_engine_ctrl_rtl.xml @@ -0,0 +1,189 @@ + + + analog.com + interface + spi_engine_ctrl_rtl + 1.0 + + + + CMD_READY + + + required + 1 + in + + + required + 1 + + + + + CMD_VALID + + + required + 1 + + + required + 1 + in + + + + + CMD_DATA + + + required + 16 + + + required + 16 + in + + + + + SDO_READY + + + required + 1 + in + + + required + 1 + + + + + SDO_VALID + + + required + 1 + + + required + 1 + in + + + + + SDO_DATA + + + required + 8 + + + required + 8 + in + + + + + SDI_READY + + + required + 1 + out + + + required + 1 + in + + + + + SDI_VALID + + + required + 1 + in + + + required + 1 + out + + + + + SDI_DATA + + + required + 8 + in + + + required + 8 + out + + + + + SYNC_READY + + + required + 1 + out + + + required + 1 + in + + + + + SYNC_VALID + + + required + 1 + in + + + required + 1 + out + + + + + SYNC_DATA + + + required + 8 + in + + + required + 8 + out + + + + + diff --git a/library/spi_engine/interfaces/spi_engine_offload_ctrl.xml b/library/spi_engine/interfaces/spi_engine_offload_ctrl.xml new file mode 100644 index 000000000..5a8a05eaf --- /dev/null +++ b/library/spi_engine/interfaces/spi_engine_offload_ctrl.xml @@ -0,0 +1,13 @@ + + + analog.com + interface + spi_engine_offload_ctrl + 1.0 + false + false + 1 + 1 + diff --git a/library/spi_engine/interfaces/spi_engine_offload_ctrl_rtl.xml b/library/spi_engine/interfaces/spi_engine_offload_ctrl_rtl.xml new file mode 100644 index 000000000..06b262339 --- /dev/null +++ b/library/spi_engine/interfaces/spi_engine_offload_ctrl_rtl.xml @@ -0,0 +1,113 @@ + + + analog.com + interface + spi_engine_offload_ctrl_rtl + 1.0 + + + + CMD_WR_EN + + + required + 1 + + + required + 1 + in + + + + + CMD_WR_DATA + + + required + 16 + + + required + 16 + in + + + + + SDO_WR_EN + + + required + 1 + + + required + 1 + in + + + + + SDO_WR_DATA + + + required + 8 + + + required + 8 + in + + + + + ENABLE + + + required + 1 + + + required + 1 + in + + + + + MEM_RESET + + + required + 1 + + + required + 1 + in + + + + + ENABLED + + + required + 1 + in + + + required + 1 + + + + + diff --git a/library/spi_engine/interfaces/spi_master.xml b/library/spi_engine/interfaces/spi_master.xml new file mode 100644 index 000000000..525c27cac --- /dev/null +++ b/library/spi_engine/interfaces/spi_master.xml @@ -0,0 +1,13 @@ + + + analog.com + interface + spi_master + 1.0 + false + false + 1 + 1 + diff --git a/library/spi_engine/interfaces/spi_master_rtl.xml b/library/spi_engine/interfaces/spi_master_rtl.xml new file mode 100644 index 000000000..44c5bc17f --- /dev/null +++ b/library/spi_engine/interfaces/spi_master_rtl.xml @@ -0,0 +1,85 @@ + + + analog.com + interface + spi_master_rtl + 1.0 + + + + SCLK + + + required + 1 + + + required + 1 + in + + + + + SDI + + + 1 + in + + + 1 + + + + + SDO + + + 1 + + + 1 + in + + + + + SDO_T + + + 1 + + + 1 + in + + + + + THREE_WIRE + + + 1 + + + 1 + in + + + + + CS + + + in + + + + + diff --git a/library/spi_engine/spi_engine_execution/Makefile b/library/spi_engine/spi_engine_execution/Makefile new file mode 100644 index 000000000..f3af6ed7d --- /dev/null +++ b/library/spi_engine/spi_engine_execution/Makefile @@ -0,0 +1,42 @@ +#################################################################################### +#################################################################################### +## Copyright 2011(c) Analog Devices, Inc. +## Auto-generated, do not modify! +#################################################################################### +#################################################################################### + +M_DEPS := spi_engine_execution_ip.tcl +M_DEPS += ../../scripts/adi_env.tcl +M_DEPS += ../../scripts/adi_ip.tcl +M_DEPS += spi_engine_execution.v + +M_VIVADO := vivado -mode batch -source + +M_FLIST := *.cache +M_FLIST += *.data +M_FLIST += *.xpr +M_FLIST += *.log +M_FLIST += component.xml +M_FLIST += *.jou +M_FLIST += xgui +M_FLIST += .Xil + + + +.PHONY: all clean clean-all +all: spi_engine_execution.xpr + + +clean:clean-all + + +clean-all: + rm -rf $(M_FLIST) + + +spi_engine_execution.xpr: $(M_DEPS) + rm -rf $(M_FLIST) + $(M_VIVADO) spi_engine_execution_ip.tcl >> spi_engine_execution_ip.log 2>&1 + +#################################################################################### +#################################################################################### diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution.v b/library/spi_engine/spi_engine_execution/spi_engine_execution.v new file mode 100644 index 000000000..2593adbad --- /dev/null +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution.v @@ -0,0 +1,313 @@ + +module spi_engine_execution ( + input clk, + input resetn, + + output reg active, + + output cmd_ready, + input cmd_valid, + input [15:0] cmd, + + input sdo_data_valid, + output reg sdo_data_ready, + input [7:0] sdo_data, + + input sdi_data_ready, + output reg sdi_data_valid, + output [7:0] sdi_data, + + input sync_ready, + output reg sync_valid, + output [7:0] sync, + + output reg sclk, + output sdo, + output reg sdo_t, + input sdi, + output reg [NUM_CS-1:0] cs, + output reg three_wire +); + +parameter NUM_CS = 1; +parameter DEFAULT_SPI_CFG = 0; +parameter DEFAULT_CLK_DIV = 0; + +localparam CMD_TRANSFER = 2'b00; +localparam CMD_CHIPSELECT = 2'b01; +localparam CMD_WRITE = 2'b10; +localparam CMD_MISC = 2'b11; + +localparam MISC_SYNC = 1'b0; +localparam MISC_SLEEP = 1'b1; + +localparam REG_CLK_DIV = 1'b0; +localparam REG_CONFIG = 1'b1; + +reg idle; + +reg [7:0] clk_div_counter = 'h00; +reg [7:0] clk_div_counter_next = 'h00; +reg clk_div_last; + +reg [11:0] counter = 'h00; + +wire [7:0] sleep_counter = counter[11:4]; +wire [1:0] cs_sleep_counter = counter[5:4]; +wire [2:0] cs_sleep_counter2 = counter[6:4]; +wire [2:0] bit_counter = counter[3:1]; +wire [7:0] transfer_counter = counter[11:4]; +wire ntx_rx = counter[0]; + +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; +wire end_of_word; + +assign first_bit = bit_counter == 'h0; +assign last_bit = bit_counter == 'h7; +assign end_of_word = last_bit == 1'b1 && ntx_rx == 1'b1 && clk_div_last == 1'b1; + +reg [15:0] cmd_d1; + +reg cpha = DEFAULT_SPI_CFG[0]; +reg cpol = DEFAULT_SPI_CFG[1]; +reg [7:0] clk_div = DEFAULT_CLK_DIV; + +wire sdo_enabled = cmd_d1[8]; +wire sdi_enabled = cmd_d1[9]; + +reg [8:0] data_shift = 'h0; + +wire [1:0] inst = cmd[13:12]; +wire [1:0] inst_d1 = cmd_d1[13:12]; + +wire exec_cmd = cmd_ready && cmd_valid; +wire exec_transfer_cmd = exec_cmd && inst == CMD_TRANSFER; +wire exec_write_cmd = exec_cmd && inst == CMD_WRITE; +wire exec_chipselect_cmd = exec_cmd && inst == CMD_CHIPSELECT; +wire exec_misc_cmd = exec_cmd && inst == CMD_MISC; +wire exec_sync_cmd = exec_misc_cmd && cmd[8] == MISC_SYNC; + +assign cmd_ready = idle; + +always @(posedge clk) begin + if (cmd_ready) + cmd_d1 <= cmd; +end + +always @(posedge clk) begin + if (resetn == 1'b0) begin + active <= 1'b0; + end else begin + if (exec_cmd == 1'b1) + active <= 1'b1; + else if (sync_ready == 1'b1 && sync_valid == 1'b1) + active <= 1'b0; + end +end + +always @(posedge clk) begin + if (resetn == 1'b0) begin + cpha <= DEFAULT_SPI_CFG[0]; + cpol <= DEFAULT_SPI_CFG[1]; + three_wire <= DEFAULT_SPI_CFG[2]; + clk_div <= DEFAULT_CLK_DIV; + end else if (exec_write_cmd == 1'b1) begin + if (cmd[8] == REG_CONFIG) begin + cpha <= cmd[0]; + cpol <= cmd[1]; + three_wire <= cmd[2]; + end else if (cmd[8] == REG_CLK_DIV) begin + clk_div <= cmd[7:0]; + end + end +end + +always @(posedge clk) begin + if ((clk_div_last == 1'b0 && idle == 1'b0 && wait_for_io == 1'b0 && + clk_div_counter == 'h01) || clk_div == 'h00) + clk_div_last <= 1'b1; + else + clk_div_last <= 1'b0; +end + +always @(posedge clk) begin + if (clk_div_last == 1'b1 || idle == 1'b1 || wait_for_io == 1'b1) begin + clk_div_counter <= clk_div; + trigger <= 1'b1; + end else begin + clk_div_counter <= clk_div_counter - 1'b1; + trigger <= 1'b0; + end +end + +wire trigger_tx = trigger == 1'b1 && ntx_rx == 1'b0; +wire trigger_rx = trigger == 1'b1 && ntx_rx == 1'b1; + +wire sleep_counter_compare = sleep_counter == cmd_d1[7:0] && clk_div_last == 1'b1; +wire cs_sleep_counter_compare = cs_sleep_counter == cmd_d1[9:8] && clk_div_last == 1'b1; +wire 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) + counter <= 'h00; + else if (clk_div_last == 1'b1 && wait_for_io == 1'b0) + counter <= counter + (transfer_active ? 'h1 : 'h10); +end + +always @(posedge clk) begin + if (resetn == 1'b0) begin + idle <= 1'b1; + end else begin + if (exec_transfer_cmd || exec_chipselect_cmd || exec_misc_cmd) begin + idle <= 1'b0; + end else begin + case (inst_d1) + CMD_TRANSFER: begin + if (transfer_active == 1'b0 && wait_for_io == 1'b0) + idle <= 1'b1; + end + CMD_CHIPSELECT: begin + if (cs_sleep_counter_compare2) + idle <= 1'b1; + end + CMD_MISC: begin + case (cmd_d1[8]) + MISC_SLEEP: begin + if (sleep_counter_compare) + idle <= 1'b1; + end + MISC_SYNC: begin + if (sync_ready) + idle <= 1'b1; + end + endcase + end + endcase + end + end +end + +always @(posedge clk) begin + if (resetn == 1'b0) begin + cs <= 'hff; + end else if (inst_d1 == CMD_CHIPSELECT && cs_sleep_counter_compare == 1'b1) begin + cs <= cmd_d1[NUM_CS-1:0]; + end +end + +always @(posedge clk) begin + if (resetn == 1'b0) begin + sync_valid <= 1'b0; + end else begin + if (exec_sync_cmd == 1'b1) begin + sync_valid <= 1'b1; + end else if (sync_ready == 1'b1) begin + sync_valid <= 1'b0; + end + end +end + +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 + +always @(posedge clk) begin + if (resetn == 1'b0) + sdi_data_valid <= 1'b0; + else if (sdi_enabled == 1'b1 && last_bit == 1'b1 && trigger_rx == 1'b1 && + transfer_active == 1'b1) + sdi_data_valid <= 1'b1; + else if (sdi_data_ready == 1'b1) + sdi_data_valid <= 1'b0; +end + +wire 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); +wire io_ready2 = (sdi_enabled == 1'b0 || sdi_data_ready == 1'b1) && + (sdo_enabled == 1'b0 || last_transfer == 1'b1 || sdo_data_valid == 1'b1); + +always @(posedge clk) begin + if (idle == 1'b1) begin + last_transfer <= 1'b0; + end else if (trigger_tx == 1'b1 && transfer_active == 1'b1) begin + if (transfer_counter == cmd_d1[7:0]) + last_transfer <= 1'b1; + else + last_transfer <= 1'b0; + end +end + +always @(posedge clk) begin + if (resetn == 1'b0) begin + transfer_active <= 1'b0; + wait_for_io <= 1'b0; + end else begin + if (exec_transfer_cmd == 1'b1) begin + wait_for_io <= 1'b1; + transfer_active <= 1'b0; + 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; + 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; + if (io_ready2 == 1'b0) + wait_for_io <= 1'b1; + end + end +end + +always @(posedge clk) begin + if (transfer_active == 1'b1 || wait_for_io == 1'b1) + begin + sdo_t <= ~sdo_enabled; + end else begin + sdo_t <= 1'b1; + end +end + +always @(posedge clk) begin + if (transfer_active == 1'b1 && trigger_tx == 1'b1) begin + if (first_bit == 1'b1) + data_shift[8:1] <= sdo_data; + else + data_shift[8:1] <= data_shift[7:0]; + end +end + +assign sdo = data_shift[8]; +assign sdi_data = data_shift[7:0]; + +always @(posedge clk) begin + if (trigger_rx == 1'b1) begin + data_shift[0] <= sdi; + end +end + +always @(posedge clk) begin + if (transfer_active == 1'b1) begin + sclk <= cpol ^ cpha ^ ntx_rx; + end else begin + sclk <= cpol; + end +end + +endmodule diff --git a/library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl b/library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl new file mode 100644 index 000000000..d7a51e00b --- /dev/null +++ b/library/spi_engine/spi_engine_execution/spi_engine_execution_ip.tcl @@ -0,0 +1,46 @@ + +source ../../scripts/adi_env.tcl +source $ad_hdl_dir/library/scripts/adi_ip.tcl + +adi_ip_create spi_engine_execution +adi_ip_files spi_engine_execution [list \ + "spi_engine_execution.v" \ +] + +adi_ip_properties_lite spi_engine_execution +# Remove all inferred interfaces +ipx::remove_all_bus_interface [ipx::current_core] + +adi_add_bus "ctrl" "slave" \ + "analog.com:interface:spi_engine_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_ctrl:1.0" \ + { + {"cmd_ready" "CMD_READY"} \ + {"cmd_valid" "CMD_VALID"} \ + {"cmd" "CMD_DATA"} \ + {"sdo_data_ready" "SDO_READY"} \ + {"sdo_data_valid" "SDO_VALID"} \ + {"sdo_data" "SDO_DATA"} \ + {"sdi_data_ready" "SDI_READY"} \ + {"sdi_data_valid" "SDI_VALID"} \ + {"sdi_data" "SDI_DATA"} \ + {"sync_ready" "SYNC_READY"} \ + {"sync_valid" "SYNC_VALID"} \ + {"sync" "SYNC_DATA"} \ + } +adi_add_bus_clock "clk" "ctrl" "resetn" + +adi_add_bus "spi" "master" \ + "analog.com:interface:spi_master_rtl:1.0" \ + "analog.com:interface:spi_master:1.0" \ + { + {"sclk" "SCLK"} \ + {"sdi" "SDI"} \ + {"sdo" "SDO"} \ + {"sdo_t" "SDO_T"} \ + {"three_wire" "THREE_WIRE"} \ + {"cs" "CS"} \ + } +adi_add_bus_clock "clk" "spi" "resetn" + +ipx::save_core [ipx::current_core] diff --git a/library/spi_engine/spi_engine_interconnect/Makefile b/library/spi_engine/spi_engine_interconnect/Makefile new file mode 100644 index 000000000..eb521ec54 --- /dev/null +++ b/library/spi_engine/spi_engine_interconnect/Makefile @@ -0,0 +1,42 @@ +#################################################################################### +#################################################################################### +## Copyright 2011(c) Analog Devices, Inc. +## Auto-generated, do not modify! +#################################################################################### +#################################################################################### + +M_DEPS := spi_engine_interconnect_ip.tcl +M_DEPS += ../../scripts/adi_env.tcl +M_DEPS += ../../scripts/adi_ip.tcl +M_DEPS += spi_engine_interconnect.v + +M_VIVADO := vivado -mode batch -source + +M_FLIST := *.cache +M_FLIST += *.data +M_FLIST += *.xpr +M_FLIST += *.log +M_FLIST += component.xml +M_FLIST += *.jou +M_FLIST += xgui +M_FLIST += .Xil + + + +.PHONY: all clean clean-all +all: spi_engine_interconnect.xpr + + +clean:clean-all + + +clean-all: + rm -rf $(M_FLIST) + + +spi_engine_interconnect.xpr: $(M_DEPS) + rm -rf $(M_FLIST) + $(M_VIVADO) spi_engine_interconnect_ip.tcl >> spi_engine_interconnect_ip.log 2>&1 + +#################################################################################### +#################################################################################### diff --git a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v new file mode 100644 index 000000000..463073955 --- /dev/null +++ b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect.v @@ -0,0 +1,107 @@ + + +module spi_engine_interconnect ( + input clk, + input resetn, + + output m_cmd_valid, + input m_cmd_ready, + output [15:0] m_cmd_data, + + output m_sdo_valid, + input m_sdo_ready, + output [7:0] m_sdo_data, + + input m_sdi_valid, + output m_sdi_ready, + input [7:0] m_sdi_data, + + input m_sync_valid, + output m_sync_ready, + input [7:0] m_sync, + + + input s0_cmd_valid, + output s0_cmd_ready, + input [15:0] s0_cmd_data, + + input s0_sdo_valid, + output s0_sdo_ready, + input [7:0] s0_sdo_data, + + output s0_sdi_valid, + input s0_sdi_ready, + output [7:0] s0_sdi_data, + + output s0_sync_valid, + input s0_sync_ready, + output [7:0] s0_sync, + + + input s1_cmd_valid, + output s1_cmd_ready, + input [15:0] s1_cmd_data, + + input s1_sdo_valid, + output s1_sdo_ready, + input [7:0] s1_sdo_data, + + output s1_sdi_valid, + input s1_sdi_ready, + output [7:0] s1_sdi_data, + + output s1_sync_valid, + input s1_sync_ready, + output [7:0] s1_sync +); + +reg s_active = 1'b0; + +reg idle = 1'b1; + +`define spi_engine_interconnect_mux(s0, s1) (idle == 1'b1 ? 1'b0 : (s_active == 1'b0 ? s0 : s1)) + +assign m_cmd_data = s_active == 1'b0 ? s0_cmd_data : s1_cmd_data; +assign m_cmd_valid = `spi_engine_interconnect_mux(s0_cmd_valid, s1_cmd_valid); +assign s0_cmd_ready = `spi_engine_interconnect_mux(m_cmd_ready, 1'b0); +assign s1_cmd_ready = `spi_engine_interconnect_mux(1'b0, m_cmd_ready); + +assign m_sdo_data = s_active == 1'b0 ? s0_sdo_data : s1_sdo_data; +assign m_sdo_valid = `spi_engine_interconnect_mux(s0_sdo_valid, s1_sdo_valid); +assign s0_sdo_ready = `spi_engine_interconnect_mux(m_sdo_ready, 1'b0); +assign s1_sdo_ready = `spi_engine_interconnect_mux(1'b0, m_sdo_ready); + +assign s0_sdi_data = m_sdi_data; +assign s1_sdi_data = m_sdi_data; +assign m_sdi_ready = `spi_engine_interconnect_mux(s0_sdi_ready, s1_sdi_ready); +assign s0_sdi_valid = `spi_engine_interconnect_mux(m_sdi_valid, 1'b0); +assign s1_sdi_valid = `spi_engine_interconnect_mux(1'b0, m_sdi_valid); + +assign s0_sync = m_sync; +assign s1_sync = m_sync; +assign m_sync_ready = `spi_engine_interconnect_mux(s0_sync_ready, s1_sync_ready); +assign s0_sync_valid = `spi_engine_interconnect_mux(m_sync_valid, 1'b0); +assign s1_sync_valid = `spi_engine_interconnect_mux(1'b0, m_sync_valid); + +always @(posedge clk) begin + if (idle == 1'b1) begin + if (s0_cmd_valid) + s_active <= 1'b0; + else if (s1_cmd_valid) + s_active <= 1'b1; + end +end + +always @(posedge clk) begin + if (resetn == 1'b0) begin + idle = 1'b1; + end else begin + if (m_sync_valid == 1'b1 && m_sync_ready == 1'b1) begin + idle <= 1'b1; + end else if (s0_cmd_valid == 1'b1 || s1_cmd_valid == 1'b1) begin + idle <= 1'b0; + end + end +end + +endmodule diff --git a/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl new file mode 100644 index 000000000..9b1521b00 --- /dev/null +++ b/library/spi_engine/spi_engine_interconnect/spi_engine_interconnect_ip.tcl @@ -0,0 +1,54 @@ + +source ../../scripts/adi_env.tcl +source $ad_hdl_dir/library/scripts/adi_ip.tcl + +adi_ip_create spi_engine_interconnect +adi_ip_files spi_engine_interconnect [list \ + "spi_engine_interconnect.v" \ +] + +adi_ip_properties_lite spi_engine_interconnect +# Remove all inferred interfaces +ipx::remove_all_bus_interface [ipx::current_core] + +adi_add_bus "m_ctrl" "master" \ + "analog.com:interface:spi_engine_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_ctrl:1.0" \ + { \ + {"m_cmd_ready" "CMD_READY"} \ + {"m_cmd_valid" "CMD_VALID"} \ + {"m_cmd_data" "CMD_DATA"} \ + {"m_sdo_ready" "SDO_READY"} \ + {"m_sdo_valid" "SDO_VALID"} \ + {"m_sdo_data" "SDO_DATA"} \ + {"m_sdi_ready" "SDI_READY"} \ + {"m_sdi_valid" "SDI_VALID"} \ + {"m_sdi_data" "SDI_DATA"} \ + {"m_sync_ready" "SYNC_READY"} \ + {"m_sync_valid" "SYNC_VALID"} \ + {"m_sync" "SYNC_DATA"} \ + } +adi_add_bus_clock "clk" "m_ctrl" "resetn" + +foreach prefix [list "s0" "s1"] { + adi_add_bus [format "%s_ctrl" $prefix] "slave" \ + "analog.com:interface:spi_engine_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_ctrl:1.0" \ + [list \ + [list [format "%s_cmd_ready" $prefix] "CMD_READY"] \ + [list [format "%s_cmd_valid" $prefix] "CMD_VALID"] \ + [list [format "%s_cmd_data" $prefix] "CMD_DATA"] \ + [list [format "%s_sdo_ready" $prefix] "SDO_READY"] \ + [list [format "%s_sdo_valid" $prefix] "SDO_VALID"] \ + [list [format "%s_sdo_data" $prefix] "SDO_DATA"] \ + [list [format "%s_sdi_ready" $prefix] "SDI_READY"] \ + [list [format "%s_sdi_valid" $prefix] "SDI_VALID"] \ + [list [format "%s_sdi_data" $prefix] "SDI_DATA"] \ + [list [format "%s_sync_ready" $prefix] "SYNC_READY"] \ + [list [format "%s_sync_valid" $prefix] "SYNC_VALID"] \ + [list [format "%s_sync" $prefix] "SYNC_DATA"] \ + ] + adi_add_bus_clock "clk" [format "%s_ctrl" $prefix] "resetn" +} + +ipx::save_core [ipx::current_core] diff --git a/library/spi_engine/spi_engine_offload/Makefile b/library/spi_engine/spi_engine_offload/Makefile new file mode 100644 index 000000000..f48f54660 --- /dev/null +++ b/library/spi_engine/spi_engine_offload/Makefile @@ -0,0 +1,42 @@ +#################################################################################### +#################################################################################### +## Copyright 2011(c) Analog Devices, Inc. +## Auto-generated, do not modify! +#################################################################################### +#################################################################################### + +M_DEPS := spi_engine_offload_ip.tcl +M_DEPS += ../../scripts/adi_env.tcl +M_DEPS += ../../scripts/adi_ip.tcl +M_DEPS += spi_engine_offload.v + +M_VIVADO := vivado -mode batch -source + +M_FLIST := *.cache +M_FLIST += *.data +M_FLIST += *.xpr +M_FLIST += *.log +M_FLIST += component.xml +M_FLIST += *.jou +M_FLIST += xgui +M_FLIST += .Xil + + + +.PHONY: all clean clean-all +all: spi_engine_offload.xpr + + +clean:clean-all + + +clean-all: + rm -rf $(M_FLIST) + + +spi_engine_offload.xpr: $(M_DEPS) + rm -rf $(M_FLIST) + $(M_VIVADO) spi_engine_offload_ip.tcl >> spi_engine_offload_ip.log 2>&1 + +#################################################################################### +#################################################################################### diff --git a/library/spi_engine/spi_engine_offload/spi_engine_offload.v b/library/spi_engine/spi_engine_offload/spi_engine_offload.v new file mode 100644 index 000000000..8662c46e8 --- /dev/null +++ b/library/spi_engine/spi_engine_offload/spi_engine_offload.v @@ -0,0 +1,178 @@ + +module spi_engine_offload ( + input ctrl_clk, + + input ctrl_cmd_wr_en, + input [15:0] ctrl_cmd_wr_data, + + input ctrl_sdo_wr_en, + input [7:0] ctrl_sdo_wr_data, + + input ctrl_enable, + output ctrl_enabled, + input ctrl_mem_reset, + + input spi_clk, + input spi_resetn, + + input trigger, + + output cmd_valid, + input cmd_ready, + output [15:0] cmd, + + output sdo_data_valid, + input sdo_data_ready, + output [7:0] sdo_data, + + input sdi_data_valid, + output sdi_data_ready, + input [7:0] sdi_data, + + input sync_valid, + output sync_ready, + input [7:0] sync_data, + + output offload_sdi_valid, + input offload_sdi_ready, + output [7:0] offload_sdi_data +); + +parameter SPI_CLK_ASYNC = 0; +parameter CMD_MEM_ADDR_WIDTH = 4; +parameter SDO_MEM_ADDR_WIDTH = 4; + +reg spi_active = 1'b0; + +reg [CMD_MEM_ADDR_WIDTH-1:0] ctrl_cmd_wr_addr = 'h00; +reg [CMD_MEM_ADDR_WIDTH-1:0] spi_cmd_rd_addr = 'h00; +reg [SDO_MEM_ADDR_WIDTH-1:0] ctrl_sdo_wr_addr = 'h00; +reg [SDO_MEM_ADDR_WIDTH-1:0] spi_sdo_rd_addr = 'h00; + +reg [15:0] cmd_mem[0:2**CMD_MEM_ADDR_WIDTH-1]; +reg [7:0] sdo_mem[0:2**SDO_MEM_ADDR_WIDTH-1]; + +wire [CMD_MEM_ADDR_WIDTH-1:0] spi_cmd_rd_addr_next; +wire spi_enable; + +assign cmd_valid = spi_active; +assign sdo_data_valid = spi_active; +assign sync_ready = 1'b1; + +assign offload_sdi_valid = sdi_data_valid; +assign sdi_data_ready = offload_sdi_ready; +assign offload_sdi_data = sdi_data; + +assign cmd = cmd_mem[spi_cmd_rd_addr]; +assign sdo_data = sdo_mem[spi_sdo_rd_addr]; + +generate if (SPI_CLK_ASYNC) begin + +/* + * The synchronization circuit takes care that there are no glitches on the + * ctrl_enabled signal. ctrl_do_enable is asserted whenever ctrl_enable is + * asserted, but only deasserted once the signal has been synchronized back from + * the SPI domain. This makes sure that we can't end up in a state where the + * enable signal in the SPI domain is asserted, but neither enable nor enabled + * is asserted in the control domain. + */ + +reg ctrl_do_enable = 1'b0; +wire ctrl_is_enabled; +reg spi_enabled = 1'b0; + +always @(posedge ctrl_clk) begin + if (ctrl_enable == 1'b1) begin + ctrl_do_enable <= 1'b1; + end else if (ctrl_is_enabled == 1'b1) begin + ctrl_do_enable <= 1'b0; + end +end + +assign ctrl_enabled = ctrl_is_enabled | ctrl_do_enable; + +always @(posedge spi_clk) begin + spi_enabled <= spi_enable | spi_active; +end + +sync_bits # ( + .NUM_BITS(1), + .CLK_ASYNC(1) +) i_sync_enable ( + .in(ctrl_do_enable), + .out_clk(spi_clk), + .out_resetn(1'b1), + .out(spi_enable) +); + +sync_bits # ( + .NUM_BITS(1), + .CLK_ASYNC(1) +) i_sync_enabled ( + .in(spi_enabled), + .out_clk(ctrl_clk), + .out_resetn(1'b1), + .out(ctrl_is_enabled) +); + +end else begin +assign spi_enable = ctrl_enable; +assign ctrl_enabled = spi_enable | spi_active; +end endgenerate + +assign spi_cmd_rd_addr_next = spi_cmd_rd_addr + 1; + +always @(posedge spi_clk) begin + if (spi_resetn == 1'b0) begin + spi_active <= 1'b0; + end else begin + if (spi_active == 1'b0) begin + if (trigger == 1'b1 && spi_enable == 1'b1) + spi_active <= 1'b1; + end else if (cmd_ready == 1'b1 && spi_cmd_rd_addr_next == ctrl_cmd_wr_addr) begin + spi_active <= 1'b0; + end + end +end + +always @(posedge spi_clk) begin + if (cmd_valid == 1'b0) begin + spi_cmd_rd_addr <= 'h00; + end else if (cmd_ready == 1'b1) begin + spi_cmd_rd_addr <= spi_cmd_rd_addr_next; + end +end + +always @(posedge spi_clk) begin + if (spi_active == 1'b0) begin + spi_sdo_rd_addr <= 'h00; + end else if (sdo_data_ready == 1'b1) begin + spi_sdo_rd_addr <= spi_sdo_rd_addr + 1'b1; + end +end + +always @(posedge ctrl_clk) begin + if (ctrl_mem_reset == 1'b1) + ctrl_cmd_wr_addr <= 'h00; + else if (ctrl_cmd_wr_en == 1'b1) + ctrl_cmd_wr_addr <= ctrl_cmd_wr_addr + 1'b1; +end + +always @(posedge ctrl_clk) begin + if (ctrl_cmd_wr_en == 1'b1) + cmd_mem[ctrl_cmd_wr_addr] <= ctrl_cmd_wr_data; +end + +always @(posedge ctrl_clk) begin + if (ctrl_mem_reset == 1'b1) + ctrl_sdo_wr_addr <= 'h00; + else if (ctrl_sdo_wr_en == 1'b1) + ctrl_sdo_wr_addr <= ctrl_sdo_wr_addr + 1'b1; +end + +always @(posedge ctrl_clk) begin + if (ctrl_sdo_wr_en == 1'b1) + sdo_mem[ctrl_sdo_wr_addr] <= ctrl_sdo_wr_data; +end + +endmodule 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 new file mode 100644 index 000000000..87d54e817 --- /dev/null +++ b/library/spi_engine/spi_engine_offload/spi_engine_offload_ip.tcl @@ -0,0 +1,56 @@ +source ../../scripts/adi_env.tcl +source $ad_hdl_dir/library/scripts/adi_ip.tcl + +adi_ip_create spi_engine_offload +adi_ip_files spi_engine_offload [list \ + "spi_engine_offload.v" \ +] + +adi_ip_properties_lite spi_engine_offload +# Remove all inferred interfaces +ipx::remove_all_bus_interface [ipx::current_core] + +adi_add_bus "spi_engine_ctrl" "master" \ + "analog.com:interface:spi_engine_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_ctrl:1.0" \ + { + {"cmd_ready" "CMD_READY"} \ + {"cmd_valid" "CMD_VALID"} \ + {"cmd" "CMD_DATA"} \ + {"sdo_data_ready" "SDO_READY"} \ + {"sdo_data_valid" "SDO_VALID"} \ + {"sdo_data" "SDO_DATA"} \ + {"sdi_data_ready" "SDI_READY"} \ + {"sdi_data_valid" "SDI_VALID"} \ + {"sdi_data" "SDI_DATA"} \ + {"sync_ready" "SYNC_READY"} \ + {"sync_valid" "SYNC_VALID"} \ + {"sync_data" "SYNC_DATA"} \ + } + +adi_add_bus "spi_engine_offload_ctrl" "slave" \ + "analog.com:interface:spi_engine_offload_ctrl_rtl:1.0" \ + "analog.com:interface:spi_engine_offload_ctrl:1.0" \ + { \ + { "ctrl_cmd_wr_en" "CMD_WR_EN"} \ + { "ctrl_cmd_wr_data" "CMD_WR_DATA"} \ + { "ctrl_sdo_wr_en" "SDO_WR_EN"} \ + { "ctrl_sdo_wr_data" "SDO_WR_DATA"} \ + { "ctrl_enable" "ENABLE"} \ + { "ctrl_enabled" "ENABLED"} \ + { "ctrl_mem_reset" "MEM_RESET"} \ + } + +adi_add_bus "offload_sdi" "master" \ + "xilinx.com:interface:axis_rtl:1.0" \ + "xilinx.com:interface:axis:1.0" \ + { \ + {"offload_sdi_valid" "TVALID"} \ + {"offload_sdi_ready" "TREADY"} \ + {"offload_sdi_data" "TDATA"} \ + } + +adi_add_bus_clock "spi_clk" "spi_engine_ctrl:offload_sdi" "spi_resetn" +adi_add_bus_clock "ctrl_clk" "spi_engine_offload_ctrl" + +ipx::save_core [ipx::current_core]