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 <lars@metafoo.de>
main
Lars-Peter Clausen 2015-04-01 12:12:11 +02:00
parent a5b452cc27
commit e6b58e8a20
19 changed files with 1760 additions and 0 deletions

View File

@ -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

View File

@ -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
####################################################################################
####################################################################################

View File

@ -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

View File

@ -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]

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<spirit:busDefinition xmlns:xilinx="http://www.xilinx.com"
xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<spirit:vendor>analog.com</spirit:vendor>
<spirit:library>interface</spirit:library>
<spirit:name>spi_engine_ctrl</spirit:name>
<spirit:version>1.0</spirit:version>
<spirit:directConnection>false</spirit:directConnection>
<spirit:isAddressable>false</spirit:isAddressable>
<spirit:maxMasters>1</spirit:maxMasters>
<spirit:maxSlaves>1</spirit:maxSlaves>
</spirit:busDefinition>

View File

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<spirit:abstractionDefinition xmlns:xilinx="http://www.xilinx.com"
xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<spirit:vendor>analog.com</spirit:vendor>
<spirit:library>interface</spirit:library>
<spirit:name>spi_engine_ctrl_rtl</spirit:name>
<spirit:version>1.0</spirit:version>
<spirit:busType spirit:vendor="analog.com"
spirit:library="interface"
spirit:name="spi_engine_ctrl"
spirit:version="1.0"/>
<spirit:ports>
<spirit:port>
<spirit:logicalName>CMD_READY</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>CMD_VALID</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>CMD_DATA</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>16</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>16</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDO_READY</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDO_VALID</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDO_DATA</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDI_READY</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>out</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDI_VALID</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>out</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDI_DATA</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
<spirit:direction>out</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SYNC_READY</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>out</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SYNC_VALID</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>out</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SYNC_DATA</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
<spirit:direction>out</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
</spirit:ports>
</spirit:abstractionDefinition>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<spirit:busDefinition xmlns:xilinx="http://www.xilinx.com"
xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<spirit:vendor>analog.com</spirit:vendor>
<spirit:library>interface</spirit:library>
<spirit:name>spi_engine_offload_ctrl</spirit:name>
<spirit:version>1.0</spirit:version>
<spirit:directConnection>false</spirit:directConnection>
<spirit:isAddressable>false</spirit:isAddressable>
<spirit:maxMasters>1</spirit:maxMasters>
<spirit:maxSlaves>1</spirit:maxSlaves>
</spirit:busDefinition>

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<spirit:abstractionDefinition xmlns:xilinx="http://www.xilinx.com"
xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<spirit:vendor>analog.com</spirit:vendor>
<spirit:library>interface</spirit:library>
<spirit:name>spi_engine_offload_ctrl_rtl</spirit:name>
<spirit:version>1.0</spirit:version>
<spirit:busType spirit:vendor="analog.com"
spirit:library="interface"
spirit:name="spi_engine_offload_ctrl"
spirit:version="1.0"/>
<spirit:ports>
<spirit:port>
<spirit:logicalName>CMD_WR_EN</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>CMD_WR_DATA</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>16</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>16</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDO_WR_EN</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDO_WR_DATA</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>8</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>ENABLE</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>MEM_RESET</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>ENABLED</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
</spirit:ports>
</spirit:abstractionDefinition>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<spirit:busDefinition xmlns:xilinx="http://www.xilinx.com"
xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<spirit:vendor>analog.com</spirit:vendor>
<spirit:library>interface</spirit:library>
<spirit:name>spi_master</spirit:name>
<spirit:version>1.0</spirit:version>
<spirit:directConnection>false</spirit:directConnection>
<spirit:isAddressable>false</spirit:isAddressable>
<spirit:maxMasters>1</spirit:maxMasters>
<spirit:maxSlaves>1</spirit:maxSlaves>
</spirit:busDefinition>

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<spirit:abstractionDefinition xmlns:xilinx="http://www.xilinx.com"
xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<spirit:vendor>analog.com</spirit:vendor>
<spirit:library>interface</spirit:library>
<spirit:name>spi_master_rtl</spirit:name>
<spirit:version>1.0</spirit:version>
<spirit:busType spirit:vendor="analog.com"
spirit:library="interface"
spirit:name="spi_master"
spirit:version="1.0"/>
<spirit:ports>
<spirit:port>
<spirit:logicalName>SCLK</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:presence>required</spirit:presence>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDI</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onMaster>
<spirit:onSlave>
<spirit:width>1</spirit:width>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDO</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>SDO_T</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>THREE_WIRE</spirit:logicalName>
<spirit:wire>
<spirit:onMaster>
<spirit:width>1</spirit:width>
</spirit:onMaster>
<spirit:onSlave>
<spirit:width>1</spirit:width>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
<spirit:port>
<spirit:logicalName>CS</spirit:logicalName>
<spirit:wire>
<spirit:onSlave>
<spirit:direction>in</spirit:direction>
</spirit:onSlave>
</spirit:wire>
</spirit:port>
</spirit:ports>
</spirit:abstractionDefinition>

View File

@ -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
####################################################################################
####################################################################################

View File

@ -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

View File

@ -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]

View File

@ -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
####################################################################################
####################################################################################

View File

@ -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

View File

@ -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]

View File

@ -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
####################################################################################
####################################################################################

View File

@ -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

View File

@ -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]