diff --git a/corundum/Makefile b/corundum/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..83dbfd387a7ba12eb30615cd1179d21ddaab07c1 --- /dev/null +++ b/corundum/Makefile @@ -0,0 +1,44 @@ +CXXFLAGS = -Wall + +VERILATOR = verilator +VFLAGS = +1364-2005ext+v \ + -Wno-WIDTH -Wno-PINMISSING -Wno-LITENDIAN -Wno-IMPLICIT -Wno-SELRANGE \ + -Wno-CASEINCOMPLETE -Wno-UNSIGNED + +SRCS = corundum_verilator.cpp + +all: corundum_verilator + +obj_dir/Vinterface.cpp: rtl/interface.v + $(VERILATOR) $(VFLAGS) --cc \ + -y rtl \ + -y lib/axi/rtl \ + -y lib/eth/lib/axis/rtl/ \ + -y ./lib/pcie/rtl \ + rtl/interface.v --exe $(SRCS) + +obj_dir/Vinterface: obj_dir/Vinterface.cpp $(SRCS) + $(MAKE) -C obj_dir -f Vinterface.mk + +corundum_verilator: obj_dir/Vinterface + cp $< $@ + +#obj_dir/%.o: CXXFLAGS += -Wno-all +#obj_dir/%.o: CPPFLAGS += -I /usr/share/verilator/include/ +# +#obj_dir/verilated.o: /usr/share/verilator/include/verilated.cpp +# $(CXX) $(CXXFLAGS) -c $< -o $@ +# +#obj_dir/verilated_vcd_c.o: /usr/share/verilator/include/verilated_vcd_c.cpp +# $(CXX) $(CXXFLAGS) -c $< -o $@ +# +#corundum_verilator: corundum_verilator.o obj_dir/verilated.o \ +# obj_dir/verilated_vcd_c.o obj_dir/Vinterface.o \ +# obj_dir/Vinterface__Syms.o obj_dir/Vinterface__Trace.o \ +# obj_dir/Vinterface__Trace__Slow.o +# $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + rm -rf obj_dir corundum_verilator *.o + +.PHONY: all clean diff --git a/corundum/README.md b/corundum/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5b68a738a7d2d078b997f3336bad6e91124e1898 --- /dev/null +++ b/corundum/README.md @@ -0,0 +1 @@ +Commit `d0c9a83752cde7715787aa31a5bb951fc808e0cc` from https://github.com/ucsdsysnet/corundum. diff --git a/corundum/corundum_verilator.cpp b/corundum/corundum_verilator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f271dde23695b8cc71db21a8b92410521187e500 --- /dev/null +++ b/corundum/corundum_verilator.cpp @@ -0,0 +1,22 @@ +#include + +#include "Vinterface.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +static uint64_t main_time = 0; + +double sc_time_stamp() +{ + return main_time; +} + +int main(int argc, char *argv[]) +{ + Verilated::commandArgs(argc, argv); + Vinterface *top = new Vinterface; + + top->final(); + delete top; + return 0; +} diff --git a/corundum/lib/axi/.gitignore b/corundum/lib/axi/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..964df4d4afc9fbf5e32167a837ffa1359c80d062 --- /dev/null +++ b/corundum/lib/axi/.gitignore @@ -0,0 +1,6 @@ +*~ +*.lxt +*.pyc +*.vvp +*.kate-swp + diff --git a/corundum/lib/axi/AUTHORS b/corundum/lib/axi/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..7dab2b3a51633a1dd67fd71ceb1c637931f27b85 --- /dev/null +++ b/corundum/lib/axi/AUTHORS @@ -0,0 +1 @@ +Alex Forencich diff --git a/corundum/lib/axi/COPYING b/corundum/lib/axi/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..6923387c7569f15368fefc084c0f3c46e18e3622 --- /dev/null +++ b/corundum/lib/axi/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/corundum/lib/axi/README b/corundum/lib/axi/README new file mode 120000 index 0000000000000000000000000000000000000000..42061c01a1c70097d1e4579f29a5adf40abdec95 --- /dev/null +++ b/corundum/lib/axi/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/corundum/lib/axi/README.md b/corundum/lib/axi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cde70e70ad4b513ce4dc0bd056856af1babc488b --- /dev/null +++ b/corundum/lib/axi/README.md @@ -0,0 +1,413 @@ +# Verilog AXI Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/axi/start + +GitHub repository: https://github.com/alexforencich/verilog-axi + +## Introduction + +Collection of AXI4 and AXI4 lite bus components. Most components are fully +parametrizable in interface widths. Includes full MyHDL testbench with +intelligent bus cosimulation endpoints. + +## Documentation + +### axi_adapter module + +AXI width adapter module with parametrizable data and address interface widths. +Supports INCR burst types and narrow bursts. Wrapper for axi_adapter_rd and axi_adapter_wr. + +### axi_adapter_rd module + +AXI width adapter module with parametrizable data and address interface widths. +Supports INCR burst types and narrow bursts. + +### axi_adapter_wr module + +AXI width adapter module with parametrizable data and address interface widths. +Supports INCR burst types and narrow bursts. + +### axi_axil_adapter module + +AXI to AXI lite converter and width adapter module with parametrizable data +and address interface widths. Supports INCR burst types and narrow bursts. +Wrapper for axi_axil_adapter_rd and axi_axil_adapter_wr. + +### axi_axil_adapter_rd module + +AXI to AXI lite converter and width adapter module with parametrizable data +and address interface widths. Supports INCR burst types and narrow bursts. + +### axi_axil_adapter_wr module + +AXI to AXI lite converter and width adapter module with parametrizable data +and address interface widths. Supports INCR burst types and narrow bursts. + +### axi_cdma module + +AXI to AXI DMA engine with parametrizable data and address interface widths. +Generates full-width INCR bursts only, with parametrizable maximum burst +length. Supports unaligned transfers, which can be disabled via parameter +to save on resource consumption. + +### axi_cdma_desc_mux module + +Descriptor multiplexer/demultiplexer for AXI CDMA module. Enables sharing the +AXI CDMA module between multiple request sources, interleaving requests and +distributing responses. + +### axi_crossbar module + +AXI nonblocking crossbar interconnect with parametrizable data and address +interface widths and master and slave interface counts. Supports all burst +types. Fully nonblocking with completely separate read and write paths; ID +based transaction ordering protection logic; and per-port address decode, +admission control, and decode error handling. Wrapper for axi_crossbar_rd and +axi_crossbar_wr. + +### axi_crossbar_addr module + +Address decode and admission control module for AXI nonblocking crossbar interconnect. + +### axi_crossbar_rd module + +AXI nonblocking crossbar interconnect with parametrizable data and address +interface widths and master and slave interface counts. Read interface only. +Supports all burst types. Fully nonblocking with completely separate read and +write paths; ID based transaction ordering protection logic; and per-port +address decode, admission control, and decode error handling. + +### axi_crossbar_wr module + +AXI nonblocking crossbar interconnect with parametrizable data and address +interface widths and master and slave interface counts. Write interface only. +Supports all burst types. Fully nonblocking with completely separate read and +write paths; ID based transaction ordering protection logic; and per-port +address decode, admission control, and decode error handling. + +### axi_dma module + +AXI to AXI stream DMA engine with parametrizable data and address interface +widths. Generates full-width INCR bursts only, with parametrizable maximum +burst length. Supports unaligned transfers, which can be disabled via +parameter to save on resource consumption. Wrapper for axi_dma_rd and +axi_dma_wr. + +### axi_dma_desc_mux module + +Descriptor multiplexer/demultiplexer for AXI DMA module. Enables sharing the +AXI DMA module between multiple request sources, interleaving requests and +distributing responses. + +### axi_dma_rd module + +AXI to AXI stream DMA engine with parametrizable data and address interface +widths. Generates full-width INCR bursts only, with parametrizable maximum +burst length. Supports unaligned transfers, which can be disabled via +parameter to save on resource consumption. + +### axi_dma_wr module + +AXI stream to AXI DMA engine with parametrizable data and address interface +widths. Generates full-width INCR bursts only, with parametrizable maximum +burst length. Supports unaligned transfers, which can be disabled via +parameter to save on resource consumption. + +### axi_fifo module + +AXI FIFO with parametrizable data and address interface widths. Supports all +burst types. Optionally can delay the address channel until either the write +data is completely shifted into the FIFO or the read data FIFO has enough +capacity to fit the whole burst. Wrapper for axi_fifo_rd and axi_fifo_wr. + +### axi_fifo_rd module + +AXI FIFO with parametrizable data and address interface widths. AR and R +channels only. Supports all burst types. Optionally can delay the address +channel until either the read data FIFO is empty or has enough capacity to fit +the whole burst. + +### axi_fifo_wr module + +AXI FIFO with parametrizable data and address interface widths. WR, W, and B +channels only. Supports all burst types. Optionally can delay the address +channel until the write data is shifted completely into the write data FIFO, +or the current burst completely fills the write data FIFO. + +### axi_interconnect module + +AXI shared interconnect with parametrizable data and address interface +widths and master and slave interface counts. Supports all burst types. +Small in area, but does not support concurrent operations. + +### axi_ram module + +AXI RAM with parametrizable data and address interface widths. Supports FIXED +and INCR burst types as well as narrow bursts. + +### axi_register module + +AXI register with parametrizable data and address interface widths. Supports +all burst types. Inserts simple buffers or skid buffers into all channels. +Channel register types can be individually changed or bypassed. Wrapper for +axi_register_rd and axi_register_wr. + +### axi_register_rd module + +AXI register with parametrizable data and address interface widths. AR and R +channels only. Supports all burst types. Inserts simple buffers or skid +buffers into all channels. Channel register types can be individually changed +or bypassed. + +### axi_register_wr module + +AXI register with parametrizable data and address interface widths. WR, W, +and B channels only. Supports all burst types. Inserts simple buffers or +skid buffers into all channels. Channel register types can be individually +changed or bypassed. + +### axil_adapter module + +AXI lite width adapter module with parametrizable data and address interface +widths. Wrapper for axi_adapter_rd and axi_adapter_wr. + +### axil_adapter_rd module + +AXI lite width adapter module with parametrizable data and address interface +widths. + +### axil_adapter_wr module + +AXI lite width adapter module with parametrizable data and address interface +widths. + +### axil_cdc module + +AXI lite clock domain crossing module with parametrizable data and address +interface widths. Wrapper for axi_cdc_rd and axi_cdc_wr. + +### axil_cdc_rd module + +AXI lite clock domain crossing module with parametrizable data and address +interface widths. + +### axil_cdc_wr module + +AXI lite clock domain crossing module with parametrizable data and address +interface widths. + +### axil_interconnect module + +AXI lite shared interconnect with parametrizable data and address interface +widths and master and slave interface counts. Small in area, but does not +support concurrent operations. + +### axil_ram module + +AXI lite RAM with parametrizable data and address interface widths. + +### axil_register module + +AXI lite register with parametrizable data and address interface widths. +Inserts skid buffers into all channels. Channel registers can be individually +bypassed. Wrapper for axil_register_rd and axil_register_wr. + +### axil_register_rd module + +AXI lite register with parametrizable data and address interface widths. AR +and R channels only. Inserts simple buffers into all channels. Channel +registers can be individually bypassed. + +### axil_register_wr module + +AXI lite register with parametrizable data and address interface widths. WR, +W, and B channels only. Inserts simple buffers into all channels. Channel +registers can be individually bypassed. + +### Common signals + + awid : Write address ID + awaddr : Write address + awlen : Write burst length + awsize : Write burst size + awburst : Write burst type + awlock : Write locking + awcache : Write cache handling + awprot : Write protection level + awqos : Write QoS setting + awregion : Write region + awuser : Write user sideband signal + awvalid : Write address valid + awready : Write address ready (from slave) + wdata : Write data + wstrb : Write data strobe (byte select) + wlast : Write data last transfer in burst + wuser : Write data user sideband signal + wvalid : Write data valid + wready : Write data ready (from slave) + bid : Write response ID + bresp : Write response + buser : Write response user sideband signal + bvalid : Write response valid + bready : Write response ready (from master) + arid : Read address ID + araddr : Read address + arlen : Read burst length + arsize : Read burst size + arburst : Read burst type + arlock : Read locking + arcache : Read cache handling + arprot : Read protection level + arqos : Read QoS setting + arregion : Read region + aruser : Read user sideband signal + arvalid : Read address valid + arready : Read address ready (from slave) + rid : Read data ID + rdata : Read data + rresp : Read response + rlast : Read data last transfer in burst + ruser : Read data user sideband signal + rvalid : Read response valid + rready : Read response ready (from master) + +### Common parameters + + ADDR_WIDTH : width of awaddr and araddr signals + DATA_WIDTH : width of wdata and rdata signals + STRB_WIDTH : width of wstrb signal + ID_WIDTH : width of *id signals + AWUSER_ENABLE : enable awuser signal + AWUSER_WIDTH : width of awuser signal + WUSER_ENABLE : enable wuser signal + WUSER_WIDTH : width of wuser signal + BUSER_ENABLE : enable buser signal + BUSER_WIDTH : width of buser signal + ARUSER_ENABLE : enable aruser signal + ARUSER_WIDTH : width of aruser signal + RUSER_ENABLE : enable ruser signal + RUSER_WIDTH : width of ruser signal + +### Source Files + + rtl/arbiter.v : Parametrizable arbiter + rtl/axi_adapter.v : AXI lite width converter + rtl/axi_adapter_rd.v : AXI lite width converter (read) + rtl/axi_adapter_wr.v : AXI lite width converter (write) + rtl/axi_axil_adapter.v : AXI to AXI lite converter + rtl/axi_axil_adapter_rd.v : AXI to AXI lite converter (read) + rtl/axi_axil_adapter_wr.v : AXI to AXI lite converter (write) + rtl/axi_cdma.v : AXI central DMA engine + rtl/axi_cdma_desc_mux.v : AXI CDMA descriptor mux + rtl/axi_crossbar.v : AXI nonblocking crossbar interconnect + rtl/axi_crossbar_addr.v : AXI crossbar address module + rtl/axi_crossbar_rd.v : AXI crossbar read module + rtl/axi_crossbar_wr.v : AXI crossbar write module + rtl/axi_dma.v : AXI DMA engine + rtl/axi_dma_desc_mux.v : AXI DMA descriptor mux + rtl/axi_dma_rd.v : AXI DMA engine (read) + rtl/axi_dma_wr.v : AXI DMA engine (write) + rtl/axi_fifo.v : AXI FIFO + rtl/axi_fifo_rd.v : AXI FIFO (read) + rtl/axi_fifo_wr.v : AXI FIFO (write) + rtl/axi_interconnect.v : AXI shared interconnect + rtl/axi_ram.v : AXI RAM + rtl/axi_register.v : AXI register + rtl/axi_register_rd.v : AXI register (read) + rtl/axi_register_wr.v : AXI register (write) + rtl/axil_adapter.v : AXI lite width converter + rtl/axil_adapter_rd.v : AXI lite width converter (read) + rtl/axil_adapter_wr.v : AXI lite width converter (write) + rtl/axil_cdc.v : AXI lite CDC + rtl/axil_cdc_rd.v : AXI lite CDC (read) + rtl/axil_cdc_wr.v : AXI lite CDC (write) + rtl/axil_interconnect.v : AXI lite shared interconnect + rtl/axil_ram.v : AXI lite RAM + rtl/axil_register.v : AXI lite register + rtl/axil_register_rd.v : AXI lite register (read) + rtl/axil_register_wr.v : AXI lite register (write) + rtl/priority_encoder.v : Parametrizable priority encoder + +### AXI4-Lite Interface Example + +Write + + ___ ___ ___ ___ ___ + clk ___/ \___/ \___/ \___/ \___/ \___ + _______ + awid XXXX_ID____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + awaddr XXXX_ADDR__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + awlen XXXX_00____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + awsize XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + awburst XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + awprot XXXX_PROT__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + awvalid ___/ \_______________________________ + ___________ _______________________ + awready \_______/ + _______________ + wdata XXXX_DATA__________XXXXXXXXXXXXXXXXXXXXXXXX + _______________ + wstrb XXXX_STRB__________XXXXXXXXXXXXXXXXXXXXXXXX + _______________ + wvalid ___/ \_______________________ + _______ + wready ___________/ \_______________________ + _______ + bid XXXXXXXXXXXXXXXXXXXXXXXXXXXX_ID____XXXXXXXX + _______ + bresp XXXXXXXXXXXXXXXXXXXXXXXXXXXX_RESP__XXXXXXXX + _______ + bvalid ___________________________/ \_______ + ___________________________________________ + bready + + +Read + + ___ ___ ___ ___ ___ + clk ___/ \___/ \___/ \___/ \___/ \___ + _______ + arid XXXX_ID____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + araddr XXXX_ADDR__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + arlen XXXX_00____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + arsize XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + arburst XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + arprot XXXX_PROT__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + _______ + arvalid ___/ \_______________________________ + ___________________________________________ + arready + _______ + rid XXXXXXXXXXXXXXXXXXXXXXXXXXXX_ID____XXXXXXXX + _______ + rdata XXXXXXXXXXXXXXXXXXXXXXXXXXXX_DATA__XXXXXXXX + _______ + rresp XXXXXXXXXXXXXXXXXXXXXXXXXXXX_RESP__XXXXXXXX + _______ + rvalid ___________________________/ \_______ + ___________________________________________ + rready + + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/axi.py : MyHDL AXI4 master and memory BFM + tb/axil.py : MyHDL AXI4 lite master and memory BFM diff --git a/corundum/lib/axi/rtl/arbiter.v b/corundum/lib/axi/rtl/arbiter.v new file mode 100644 index 0000000000000000000000000000000000000000..8b0443fdbae43c2afc42c074cc93be1603c69964 --- /dev/null +++ b/corundum/lib/axi/rtl/arbiter.v @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Arbiter module + */ +module arbiter # +( + parameter PORTS = 4, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter TYPE = "PRIORITY", + // block type: "NONE", "REQUEST", "ACKNOWLEDGE" + parameter BLOCK = "NONE", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire clk, + input wire rst, + + input wire [PORTS-1:0] request, + input wire [PORTS-1:0] acknowledge, + + output wire [PORTS-1:0] grant, + output wire grant_valid, + output wire [$clog2(PORTS)-1:0] grant_encoded +); + +reg [PORTS-1:0] grant_reg = 0, grant_next; +reg grant_valid_reg = 0, grant_valid_next; +reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next; + +assign grant_valid = grant_valid_reg; +assign grant = grant_reg; +assign grant_encoded = grant_encoded_reg; + +wire request_valid; +wire [$clog2(PORTS)-1:0] request_index; +wire [PORTS-1:0] request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_inst ( + .input_unencoded(request), + .output_valid(request_valid), + .output_encoded(request_index), + .output_unencoded(request_mask) +); + +reg [PORTS-1:0] mask_reg = 0, mask_next; + +wire masked_request_valid; +wire [$clog2(PORTS)-1:0] masked_request_index; +wire [PORTS-1:0] masked_request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_masked ( + .input_unencoded(request & mask_reg), + .output_valid(masked_request_valid), + .output_encoded(masked_request_index), + .output_unencoded(masked_request_mask) +); + +always @* begin + grant_next = 0; + grant_valid_next = 0; + grant_encoded_next = 0; + mask_next = mask_reg; + + if (BLOCK == "REQUEST" && grant_reg & request) begin + // granted request still asserted; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (BLOCK == "ACKNOWLEDGE" && grant_valid && !(grant_reg & acknowledge)) begin + // granted request not yet acknowledged; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (request_valid) begin + if (TYPE == "PRIORITY") begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + end else if (TYPE == "ROUND_ROBIN") begin + if (masked_request_valid) begin + grant_valid_next = 1; + grant_next = masked_request_mask; + grant_encoded_next = masked_request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index); + end else begin + mask_next = {PORTS{1'b1}} << (masked_request_index + 1); + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - request_index); + end else begin + mask_next = {PORTS{1'b1}} << (request_index + 1); + end + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + grant_reg <= 0; + grant_valid_reg <= 0; + grant_encoded_reg <= 0; + mask_reg <= 0; + end else begin + grant_reg <= grant_next; + grant_valid_reg <= grant_valid_next; + grant_encoded_reg <= grant_encoded_next; + mask_reg <= mask_next; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_adapter.v b/corundum/lib/axi/rtl/axi_adapter.v new file mode 100644 index 0000000000000000000000000000000000000000..e945f36997caa1e5830d0c245da8873749322929 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_adapter.v @@ -0,0 +1,320 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 width adapter + */ +module axi_adapter # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) interface data bus in bits + parameter S_DATA_WIDTH = 32, + // Width of input (slave) interface wstrb (width of data bus in words) + parameter S_STRB_WIDTH = (S_DATA_WIDTH/8), + // Width of output (master) interface data bus in bits + parameter M_DATA_WIDTH = 32, + // Width of output (master) interface wstrb (width of data bus in words) + parameter M_STRB_WIDTH = (M_DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // When adapting to a wider bus, re-pack full-width burst instead of passing through narrow burst if possible + parameter CONVERT_BURST = 1, + // When adapting to a wider bus, re-pack all bursts instead of passing through narrow burst if possible + parameter CONVERT_NARROW_BURST = 0, + // Forward ID through adapter + parameter FORWARD_ID = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [S_DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [S_DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [M_DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [M_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +axi_adapter_wr #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_STRB_WIDTH(S_STRB_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_STRB_WIDTH(M_STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .CONVERT_BURST(CONVERT_BURST), + .CONVERT_NARROW_BURST(CONVERT_NARROW_BURST), + .FORWARD_ID(FORWARD_ID) +) +axi_adapter_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awqos(s_axi_awqos), + .s_axi_awregion(s_axi_awregion), + .s_axi_awuser(s_axi_awuser), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wuser(s_axi_wuser), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_buser(s_axi_buser), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awqos(m_axi_awqos), + .m_axi_awregion(m_axi_awregion), + .m_axi_awuser(m_axi_awuser), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wuser(m_axi_wuser), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_buser(m_axi_buser), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready) +); + +axi_adapter_rd #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_STRB_WIDTH(S_STRB_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_STRB_WIDTH(M_STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .CONVERT_BURST(CONVERT_BURST), + .CONVERT_NARROW_BURST(CONVERT_NARROW_BURST), + .FORWARD_ID(FORWARD_ID) +) +axi_adapter_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arqos(s_axi_arqos), + .s_axi_arregion(s_axi_arregion), + .s_axi_aruser(s_axi_aruser), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_ruser(s_axi_ruser), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + + /* + * AXI master interface + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arqos(m_axi_arqos), + .m_axi_arregion(m_axi_arregion), + .m_axi_aruser(m_axi_aruser), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_ruser(m_axi_ruser), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axi_adapter_rd.v b/corundum/lib/axi/rtl/axi_adapter_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..8957a7a9a5dccfb3fc2b0595ec1c4ebae748ad38 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_adapter_rd.v @@ -0,0 +1,689 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 width adapter + */ +module axi_adapter_rd # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) interface data bus in bits + parameter S_DATA_WIDTH = 32, + // Width of input (slave) interface wstrb (width of data bus in words) + parameter S_STRB_WIDTH = (S_DATA_WIDTH/8), + // Width of output (master) interface data bus in bits + parameter M_DATA_WIDTH = 32, + // Width of output (master) interface wstrb (width of data bus in words) + parameter M_STRB_WIDTH = (M_DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // When adapting to a wider bus, re-pack full-width burst instead of passing through narrow burst if possible + parameter CONVERT_BURST = 1, + // When adapting to a wider bus, re-pack all bursts instead of passing through narrow burst if possible + parameter CONVERT_NARROW_BURST = 0, + // Forward ID through adapter + parameter FORWARD_ID = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [S_DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [M_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +parameter S_ADDR_BIT_OFFSET = $clog2(S_STRB_WIDTH); +parameter M_ADDR_BIT_OFFSET = $clog2(M_STRB_WIDTH); +parameter S_WORD_WIDTH = S_STRB_WIDTH; +parameter M_WORD_WIDTH = M_STRB_WIDTH; +parameter S_WORD_SIZE = S_DATA_WIDTH/S_WORD_WIDTH; +parameter M_WORD_SIZE = M_DATA_WIDTH/M_WORD_WIDTH; +parameter S_BURST_SIZE = $clog2(S_STRB_WIDTH); +parameter M_BURST_SIZE = $clog2(M_STRB_WIDTH); + +// output bus is wider +parameter EXPAND = M_STRB_WIDTH > S_STRB_WIDTH; +parameter DATA_WIDTH = EXPAND ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter STRB_WIDTH = EXPAND ? M_STRB_WIDTH : S_STRB_WIDTH; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND ? (M_STRB_WIDTH / S_STRB_WIDTH) : (S_STRB_WIDTH / M_STRB_WIDTH); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_STRB_WIDTH = STRB_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (S_WORD_SIZE * S_STRB_WIDTH != S_DATA_WIDTH) begin + $error("Error: AXI slave interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_WORD_SIZE * M_STRB_WIDTH != M_DATA_WIDTH) begin + $error("Error: AXI master interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_WORD_SIZE != M_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(S_WORD_WIDTH) != S_WORD_WIDTH) begin + $error("Error: AXI slave interface word width must be even power of two (instance %m)"); + $finish; + end + + if (2**$clog2(M_WORD_WIDTH) != M_WORD_WIDTH) begin + $error("Error: AXI master interface word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_DATA = 2'd1, + STATE_DATA_READ = 2'd2, + STATE_DATA_SPLIT = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [ID_WIDTH-1:0] id_reg = {ID_WIDTH{1'b0}}, id_next; +reg [ADDR_WIDTH-1:0] addr_reg = {ADDR_WIDTH{1'b0}}, addr_next; +reg [DATA_WIDTH-1:0] data_reg = {DATA_WIDTH{1'b0}}, data_next; +reg [1:0] resp_reg = 2'd0, resp_next; +reg [RUSER_WIDTH-1:0] ruser_reg = {RUSER_WIDTH{1'b0}}, ruser_next; +reg [7:0] burst_reg = 8'd0, burst_next; +reg [2:0] burst_size_reg = 3'd0, burst_size_next; +reg [7:0] master_burst_reg = 8'd0, master_burst_next; +reg [2:0] master_burst_size_reg = 3'd0, master_burst_size_next; + +reg s_axi_arready_reg = 1'b0, s_axi_arready_next; + +reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}, m_axi_arid_next; +reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}, m_axi_araddr_next; +reg [7:0] m_axi_arlen_reg = 8'd0, m_axi_arlen_next; +reg [2:0] m_axi_arsize_reg = 3'd0, m_axi_arsize_next; +reg [1:0] m_axi_arburst_reg = 2'd0, m_axi_arburst_next; +reg m_axi_arlock_reg = 1'b0, m_axi_arlock_next; +reg [3:0] m_axi_arcache_reg = 4'd0, m_axi_arcache_next; +reg [2:0] m_axi_arprot_reg = 3'd0, m_axi_arprot_next; +reg [3:0] m_axi_arqos_reg = 4'd0, m_axi_arqos_next; +reg [3:0] m_axi_arregion_reg = 4'd0, m_axi_arregion_next; +reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}, m_axi_aruser_next; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; +reg m_axi_rready_reg = 1'b0, m_axi_rready_next; + +// internal datapath +reg [ID_WIDTH-1:0] s_axi_rid_int; +reg [S_DATA_WIDTH-1:0] s_axi_rdata_int; +reg [1:0] s_axi_rresp_int; +reg s_axi_rlast_int; +reg [RUSER_WIDTH-1:0] s_axi_ruser_int; +reg s_axi_rvalid_int; +reg s_axi_rready_int_reg = 1'b0; +wire s_axi_rready_int_early; + +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_arid = FORWARD_ID ? m_axi_arid_reg : {ID_WIDTH{1'b0}}; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = m_axi_arsize_reg; +assign m_axi_arburst = m_axi_arburst_reg; +assign m_axi_arlock = m_axi_arlock_reg; +assign m_axi_arcache = m_axi_arcache_reg; +assign m_axi_arprot = m_axi_arprot_reg; +assign m_axi_arqos = m_axi_arqos_reg; +assign m_axi_arregion = m_axi_arregion_reg; +assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +always @* begin + state_next = STATE_IDLE; + + id_next = id_reg; + addr_next = addr_reg; + data_next = data_reg; + resp_next = resp_reg; + ruser_next = ruser_reg; + burst_next = burst_reg; + burst_size_next = burst_size_reg; + master_burst_next = master_burst_reg; + master_burst_size_next = master_burst_size_reg; + + s_axi_arready_next = 1'b0; + m_axi_arid_next = m_axi_arid_reg; + m_axi_araddr_next = m_axi_araddr_reg; + m_axi_arlen_next = m_axi_arlen_reg; + m_axi_arsize_next = m_axi_arsize_reg; + m_axi_arburst_next = m_axi_arburst_reg; + m_axi_arlock_next = m_axi_arlock_reg; + m_axi_arcache_next = m_axi_arcache_reg; + m_axi_arprot_next = m_axi_arprot_reg; + m_axi_arqos_next = m_axi_arqos_reg; + m_axi_arregion_next = m_axi_arregion_reg; + m_axi_aruser_next = m_axi_aruser_reg; + m_axi_arvalid_next = m_axi_arvalid_reg && !m_axi_arready; + m_axi_rready_next = 1'b0; + + if (SEGMENT_COUNT == 1) begin + // master output is same width; direct transfer with no splitting/merging + s_axi_rid_int = id_reg; + s_axi_rdata_int = m_axi_rdata; + s_axi_rresp_int = m_axi_rresp; + s_axi_rlast_int = m_axi_rlast; + s_axi_ruser_int = m_axi_ruser; + s_axi_rvalid_int = 0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_arready_next = !m_axi_arvalid; + + if (s_axi_arready && s_axi_arvalid) begin + s_axi_arready_next = 1'b0; + id_next = s_axi_arid; + m_axi_arid_next = s_axi_arid; + m_axi_araddr_next = s_axi_araddr; + m_axi_arlen_next = s_axi_arlen; + m_axi_arsize_next = s_axi_arsize; + m_axi_arburst_next = s_axi_arburst; + m_axi_arlock_next = s_axi_arlock; + m_axi_arcache_next = s_axi_arcache; + m_axi_arprot_next = s_axi_arprot; + m_axi_arqos_next = s_axi_arqos; + m_axi_arregion_next = s_axi_arregion; + m_axi_aruser_next = s_axi_aruser; + m_axi_arvalid_next = 1'b1; + m_axi_rready_next = s_axi_rready_int_early; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + // data state; transfer read data + m_axi_rready_next = s_axi_rready_int_early; + + if (m_axi_rready && m_axi_rvalid) begin + s_axi_rid_int = id_reg; + s_axi_rdata_int = m_axi_rdata; + s_axi_rresp_int = m_axi_rresp; + s_axi_rlast_int = m_axi_rlast; + s_axi_ruser_int = m_axi_ruser; + s_axi_rvalid_int = 1'b1; + if (m_axi_rlast) begin + // last data word, return to idle + m_axi_rready_next = 1'b0; + s_axi_arready_next = !m_axi_arvalid; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + endcase + end else if (EXPAND) begin + // master output is wider; split reads + s_axi_rid_int = id_reg; + s_axi_rdata_int = m_axi_rdata; + s_axi_rresp_int = m_axi_rresp; + s_axi_rlast_int = m_axi_rlast; + s_axi_ruser_int = m_axi_ruser; + s_axi_rvalid_int = 0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_arready_next = !m_axi_arvalid; + + if (s_axi_arready && s_axi_arvalid) begin + s_axi_arready_next = 1'b0; + id_next = s_axi_arid; + m_axi_arid_next = s_axi_arid; + m_axi_araddr_next = s_axi_araddr; + addr_next = s_axi_araddr; + burst_next = s_axi_arlen; + burst_size_next = s_axi_arsize; + if (CONVERT_BURST && s_axi_arcache[1] && (CONVERT_NARROW_BURST || s_axi_arsize == S_BURST_SIZE)) begin + // split reads + // require CONVERT_BURST and arcache[1] set + master_burst_size_next = M_BURST_SIZE; + if (CONVERT_NARROW_BURST) begin + m_axi_arlen_next = (({{S_ADDR_BIT_OFFSET+1{1'b0}}, s_axi_arlen} << s_axi_arsize) + s_axi_araddr[M_ADDR_BIT_OFFSET-1:0]) >> M_BURST_SIZE; + end else begin + m_axi_arlen_next = ({1'b0, s_axi_arlen} + s_axi_araddr[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET]) >> $clog2(SEGMENT_COUNT); + end + m_axi_arsize_next = M_BURST_SIZE; + state_next = STATE_DATA_READ; + end else begin + // output narrow burst + master_burst_size_next = s_axi_arsize; + m_axi_arlen_next = s_axi_arlen; + m_axi_arsize_next = s_axi_arsize; + state_next = STATE_DATA; + end + m_axi_arburst_next = s_axi_arburst; + m_axi_arlock_next = s_axi_arlock; + m_axi_arcache_next = s_axi_arcache; + m_axi_arprot_next = s_axi_arprot; + m_axi_arqos_next = s_axi_arqos; + m_axi_arregion_next = s_axi_arregion; + m_axi_aruser_next = s_axi_aruser; + m_axi_arvalid_next = 1'b1; + m_axi_rready_next = s_axi_rready_int_early; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + m_axi_rready_next = s_axi_rready_int_early; + + if (m_axi_rready && m_axi_rvalid) begin + s_axi_rid_int = id_reg; + s_axi_rdata_int = m_axi_rdata >> (addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET] * S_DATA_WIDTH); + s_axi_rresp_int = m_axi_rresp; + s_axi_rlast_int = m_axi_rlast; + s_axi_ruser_int = m_axi_ruser; + s_axi_rvalid_int = 1'b1; + addr_next = addr_reg + (1 << burst_size_reg); + if (m_axi_rlast) begin + m_axi_rready_next = 1'b0; + s_axi_arready_next = !m_axi_arvalid; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + STATE_DATA_READ: begin + m_axi_rready_next = s_axi_rready_int_early; + + if (m_axi_rready && m_axi_rvalid) begin + s_axi_rid_int = id_reg; + data_next = m_axi_rdata; + resp_next = m_axi_rresp; + ruser_next = m_axi_ruser; + s_axi_rdata_int = m_axi_rdata >> (addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET] * S_DATA_WIDTH); + s_axi_rresp_int = m_axi_rresp; + s_axi_rlast_int = 1'b0; + s_axi_ruser_int = m_axi_ruser; + s_axi_rvalid_int = 1'b1; + burst_next = burst_reg - 1; + addr_next = addr_reg + (1 << burst_size_reg); + if (burst_reg == 0) begin + m_axi_rready_next = 1'b0; + s_axi_arready_next = !m_axi_arvalid; + s_axi_rlast_int = 1'b1; + state_next = STATE_IDLE; + end else if (addr_next[master_burst_size_reg] != addr_reg[master_burst_size_reg]) begin + state_next = STATE_DATA_READ; + end else begin + m_axi_rready_next = 1'b0; + state_next = STATE_DATA_SPLIT; + end + end else begin + state_next = STATE_DATA_READ; + end + end + STATE_DATA_SPLIT: begin + m_axi_rready_next = 1'b0; + + if (s_axi_rready_int_reg) begin + s_axi_rid_int = id_reg; + s_axi_rdata_int = data_reg >> (addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET] * S_DATA_WIDTH); + s_axi_rresp_int = resp_reg; + s_axi_rlast_int = 1'b0; + s_axi_ruser_int = ruser_reg; + s_axi_rvalid_int = 1'b1; + burst_next = burst_reg - 1; + addr_next = addr_reg + (1 << burst_size_reg); + if (burst_reg == 0) begin + s_axi_arready_next = !m_axi_arvalid; + s_axi_rlast_int = 1'b1; + state_next = STATE_IDLE; + end else if (addr_next[master_burst_size_reg] != addr_reg[master_burst_size_reg]) begin + m_axi_rready_next = s_axi_rready_int_early; + state_next = STATE_DATA_READ; + end else begin + state_next = STATE_DATA_SPLIT; + end + end else begin + state_next = STATE_DATA_SPLIT; + end + end + endcase + end else begin + // master output is narrower; merge reads and possibly split burst + s_axi_rid_int = id_reg; + s_axi_rdata_int = data_reg; + s_axi_rresp_int = resp_reg; + s_axi_rlast_int = 1'b0; + s_axi_ruser_int = m_axi_ruser; + s_axi_rvalid_int = 0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_arready_next = !m_axi_arvalid; + + resp_next = 2'd0; + + if (s_axi_arready && s_axi_arvalid) begin + s_axi_arready_next = 1'b0; + id_next = s_axi_arid; + m_axi_arid_next = s_axi_arid; + m_axi_araddr_next = s_axi_araddr; + addr_next = s_axi_araddr; + burst_next = s_axi_arlen; + burst_size_next = s_axi_arsize; + if (s_axi_arsize > M_BURST_SIZE) begin + // need to adjust burst size + if ({s_axi_arlen, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-s_axi_arsize) > 255) begin + // limit burst length to max + master_burst_next = 8'd255; + end else begin + master_burst_next = {s_axi_arlen, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-s_axi_arsize); + end + master_burst_size_next = M_BURST_SIZE; + m_axi_arlen_next = master_burst_next; + m_axi_arsize_next = master_burst_size_next; + end else begin + // pass through narrow (enough) burst + master_burst_next = s_axi_arlen; + master_burst_size_next = s_axi_arsize; + m_axi_arlen_next = s_axi_arlen; + m_axi_arsize_next = s_axi_arsize; + end + m_axi_arburst_next = s_axi_arburst; + m_axi_arlock_next = s_axi_arlock; + m_axi_arcache_next = s_axi_arcache; + m_axi_arprot_next = s_axi_arprot; + m_axi_arqos_next = s_axi_arqos; + m_axi_arregion_next = s_axi_arregion; + m_axi_aruser_next = s_axi_aruser; + m_axi_arvalid_next = 1'b1; + m_axi_rready_next = 1'b0; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + m_axi_rready_next = s_axi_rready_int_early && !m_axi_arvalid; + + if (m_axi_rready && m_axi_rvalid) begin + data_next[addr_reg[S_ADDR_BIT_OFFSET-1:M_ADDR_BIT_OFFSET]*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = m_axi_rdata; + if (m_axi_rresp) begin + resp_next = m_axi_rresp; + end + s_axi_rid_int = id_reg; + s_axi_rdata_int = data_next; + s_axi_rresp_int = resp_next; + s_axi_rlast_int = 1'b0; + s_axi_ruser_int = m_axi_ruser; + s_axi_rvalid_int = 1'b0; + master_burst_next = master_burst_reg - 1; + addr_next = addr_reg + (1 << master_burst_size_reg); + if (addr_next[burst_size_reg] != addr_reg[burst_size_reg]) begin + data_next = {DATA_WIDTH{1'b0}}; + burst_next = burst_reg - 1; + s_axi_rvalid_int = 1'b1; + end + if (master_burst_reg == 0) begin + if (burst_reg == 0) begin + m_axi_rready_next = 1'b0; + s_axi_rlast_int = 1'b1; + s_axi_rvalid_int = 1'b1; + s_axi_arready_next = !m_axi_arvalid; + state_next = STATE_IDLE; + end else begin + // start new burst + m_axi_araddr_next = addr_next; + if (burst_size_reg > M_BURST_SIZE) begin + // need to adjust burst size + if ({burst_next, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-burst_size_reg) > 255) begin + // limit burst length to max + master_burst_next = 8'd255; + end else begin + master_burst_next = {burst_next, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-burst_size_reg); + end + master_burst_size_next = M_BURST_SIZE; + m_axi_arlen_next = master_burst_next; + m_axi_arsize_next = master_burst_size_next; + end else begin + // pass through narrow (enough) burst + master_burst_next = burst_next; + master_burst_size_next = burst_size_reg; + m_axi_arlen_next = burst_next; + m_axi_arsize_next = burst_size_reg; + end + m_axi_arvalid_next = 1'b1; + m_axi_rready_next = 1'b0; + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axi_arready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + m_axi_rready_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axi_arready_reg <= s_axi_arready_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + end + + id_reg <= id_next; + addr_reg <= addr_next; + data_reg <= data_next; + resp_reg <= resp_next; + ruser_reg <= ruser_next; + burst_reg <= burst_next; + burst_size_reg <= burst_size_next; + master_burst_reg <= master_burst_next; + master_burst_size_reg <= master_burst_size_next; + + m_axi_arid_reg <= m_axi_arid_next; + m_axi_araddr_reg <= m_axi_araddr_next; + m_axi_arlen_reg <= m_axi_arlen_next; + m_axi_arsize_reg <= m_axi_arsize_next; + m_axi_arburst_reg <= m_axi_arburst_next; + m_axi_arlock_reg <= m_axi_arlock_next; + m_axi_arcache_reg <= m_axi_arcache_next; + m_axi_arprot_reg <= m_axi_arprot_next; + m_axi_arqos_reg <= m_axi_arqos_next; + m_axi_arregion_reg <= m_axi_arregion_next; + m_axi_aruser_reg <= m_axi_aruser_next; +end + +// output datapath logic +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [S_DATA_WIDTH-1:0] s_axi_rdata_reg = {S_DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'd0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = 1'b0; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [S_DATA_WIDTH-1:0] temp_s_axi_rdata_reg = {S_DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_rresp_reg = 2'd0; +reg temp_s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg = 1'b0; +reg temp_s_axi_rvalid_reg = 1'b0, temp_s_axi_rvalid_next; + +// datapath control +reg store_axi_r_int_to_output; +reg store_axi_r_int_to_temp; +reg store_axi_r_temp_to_output; + +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign s_axi_rready_int_early = s_axi_rready | (~temp_s_axi_rvalid_reg & (~s_axi_rvalid_reg | ~s_axi_rvalid_int)); + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; + + store_axi_r_int_to_output = 1'b0; + store_axi_r_int_to_temp = 1'b0; + store_axi_r_temp_to_output = 1'b0; + + if (s_axi_rready_int_reg) begin + // input is ready + if (s_axi_rready | ~s_axi_rvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axi_rvalid_next = s_axi_rvalid_int; + store_axi_r_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_rvalid_next = s_axi_rvalid_int; + store_axi_r_int_to_temp = 1'b1; + end + end else if (s_axi_rready) begin + // input is not ready, but output is ready + s_axi_rvalid_next = temp_s_axi_rvalid_reg; + temp_s_axi_rvalid_next = 1'b0; + store_axi_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_rvalid_reg <= 1'b0; + s_axi_rready_int_reg <= 1'b0; + temp_s_axi_rvalid_reg <= 1'b0; + end else begin + s_axi_rvalid_reg <= s_axi_rvalid_next; + s_axi_rready_int_reg <= s_axi_rready_int_early; + temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_int_to_output) begin + s_axi_rid_reg <= s_axi_rid_int; + s_axi_rdata_reg <= s_axi_rdata_int; + s_axi_rresp_reg <= s_axi_rresp_int; + s_axi_rlast_reg <= s_axi_rlast_int; + s_axi_ruser_reg <= s_axi_ruser_int; + end else if (store_axi_r_temp_to_output) begin + s_axi_rid_reg <= temp_s_axi_rid_reg; + s_axi_rdata_reg <= temp_s_axi_rdata_reg; + s_axi_rresp_reg <= temp_s_axi_rresp_reg; + s_axi_rlast_reg <= temp_s_axi_rlast_reg; + s_axi_ruser_reg <= temp_s_axi_ruser_reg; + end + + if (store_axi_r_int_to_temp) begin + temp_s_axi_rid_reg <= s_axi_rid_int; + temp_s_axi_rdata_reg <= s_axi_rdata_int; + temp_s_axi_rresp_reg <= s_axi_rresp_int; + temp_s_axi_rlast_reg <= s_axi_rlast_int; + temp_s_axi_ruser_reg <= s_axi_ruser_int; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_adapter_wr.v b/corundum/lib/axi/rtl/axi_adapter_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..47b7210bf16903f743f00f61f825611fce389c01 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_adapter_wr.v @@ -0,0 +1,780 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 width adapter + */ +module axi_adapter_wr # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) interface data bus in bits + parameter S_DATA_WIDTH = 32, + // Width of input (slave) interface wstrb (width of data bus in words) + parameter S_STRB_WIDTH = (S_DATA_WIDTH/8), + // Width of output (master) interface data bus in bits + parameter M_DATA_WIDTH = 32, + // Width of output (master) interface wstrb (width of data bus in words) + parameter M_STRB_WIDTH = (M_DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // When adapting to a wider bus, re-pack full-width burst instead of passing through narrow burst if possible + parameter CONVERT_BURST = 1, + // When adapting to a wider bus, re-pack all bursts instead of passing through narrow burst if possible + parameter CONVERT_NARROW_BURST = 0, + // Forward ID through adapter + parameter FORWARD_ID = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [S_DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [M_DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready +); + +parameter S_ADDR_BIT_OFFSET = $clog2(S_STRB_WIDTH); +parameter M_ADDR_BIT_OFFSET = $clog2(M_STRB_WIDTH); +parameter S_WORD_WIDTH = S_STRB_WIDTH; +parameter M_WORD_WIDTH = M_STRB_WIDTH; +parameter S_WORD_SIZE = S_DATA_WIDTH/S_WORD_WIDTH; +parameter M_WORD_SIZE = M_DATA_WIDTH/M_WORD_WIDTH; +parameter S_BURST_SIZE = $clog2(S_STRB_WIDTH); +parameter M_BURST_SIZE = $clog2(M_STRB_WIDTH); + +// output bus is wider +parameter EXPAND = M_STRB_WIDTH > S_STRB_WIDTH; +parameter DATA_WIDTH = EXPAND ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter STRB_WIDTH = EXPAND ? M_STRB_WIDTH : S_STRB_WIDTH; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND ? (M_STRB_WIDTH / S_STRB_WIDTH) : (S_STRB_WIDTH / M_STRB_WIDTH); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_STRB_WIDTH = STRB_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (S_WORD_SIZE * S_STRB_WIDTH != S_DATA_WIDTH) begin + $error("Error: AXI slave interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_WORD_SIZE * M_STRB_WIDTH != M_DATA_WIDTH) begin + $error("Error: AXI master interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_WORD_SIZE != M_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(S_WORD_WIDTH) != S_WORD_WIDTH) begin + $error("Error: AXI slave interface word width must be even power of two (instance %m)"); + $finish; + end + + if (2**$clog2(M_WORD_WIDTH) != M_WORD_WIDTH) begin + $error("Error: AXI master interface word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_DATA = 2'd1, + STATE_DATA_2 = 2'd2, + STATE_RESP = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [ID_WIDTH-1:0] id_reg = {ID_WIDTH{1'b0}}, id_next; +reg [ADDR_WIDTH-1:0] addr_reg = {ADDR_WIDTH{1'b0}}, addr_next; +reg [DATA_WIDTH-1:0] data_reg = {DATA_WIDTH{1'b0}}, data_next; +reg [STRB_WIDTH-1:0] strb_reg = {STRB_WIDTH{1'b0}}, strb_next; +reg [WUSER_WIDTH-1:0] wuser_reg = {WUSER_WIDTH{1'b0}}, wuser_next; +reg [7:0] burst_reg = 8'd0, burst_next; +reg [2:0] burst_size_reg = 3'd0, burst_size_next; +reg [7:0] master_burst_reg = 8'd0, master_burst_next; +reg [2:0] master_burst_size_reg = 3'd0, master_burst_size_next; +reg burst_active_reg = 1'b0, burst_active_next; +reg first_transfer_reg = 1'b0, first_transfer_next; + +reg s_axi_awready_reg = 1'b0, s_axi_awready_next; +reg s_axi_wready_reg = 1'b0, s_axi_wready_next; +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}, s_axi_bid_next; +reg [1:0] s_axi_bresp_reg = 2'd0, s_axi_bresp_next; +reg [BUSER_WIDTH-1:0] s_axi_buser_reg = {BUSER_WIDTH{1'b0}}, s_axi_buser_next; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}, m_axi_awid_next; +reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}, m_axi_awaddr_next; +reg [7:0] m_axi_awlen_reg = 8'd0, m_axi_awlen_next; +reg [2:0] m_axi_awsize_reg = 3'd0, m_axi_awsize_next; +reg [1:0] m_axi_awburst_reg = 2'd0, m_axi_awburst_next; +reg m_axi_awlock_reg = 1'b0, m_axi_awlock_next; +reg [3:0] m_axi_awcache_reg = 4'd0, m_axi_awcache_next; +reg [2:0] m_axi_awprot_reg = 3'd0, m_axi_awprot_next; +reg [3:0] m_axi_awqos_reg = 4'd0, m_axi_awqos_next; +reg [3:0] m_axi_awregion_reg = 4'd0, m_axi_awregion_next; +reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}, m_axi_awuser_next; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; +reg m_axi_bready_reg = 1'b0, m_axi_bready_next; + +// internal datapath +reg [M_DATA_WIDTH-1:0] m_axi_wdata_int; +reg [M_STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wlast_int; +reg [WUSER_WIDTH-1:0] m_axi_wuser_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +wire m_axi_wready_int_early; + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = s_axi_wready_reg; +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_buser = BUSER_ENABLE ? s_axi_buser_reg : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +assign m_axi_awid = FORWARD_ID ? m_axi_awid_reg : {ID_WIDTH{1'b0}}; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = m_axi_awsize_reg; +assign m_axi_awburst = m_axi_awburst_reg; +assign m_axi_awlock = m_axi_awlock_reg; +assign m_axi_awcache = m_axi_awcache_reg; +assign m_axi_awprot = m_axi_awprot_reg; +assign m_axi_awqos = m_axi_awqos_reg; +assign m_axi_awregion = m_axi_awregion_reg; +assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign m_axi_awvalid = m_axi_awvalid_reg; +assign m_axi_bready = m_axi_bready_reg; + +integer i; + +always @* begin + state_next = STATE_IDLE; + + id_next = id_reg; + addr_next = addr_reg; + data_next = data_reg; + strb_next = strb_reg; + wuser_next = wuser_reg; + burst_next = burst_reg; + burst_size_next = burst_size_reg; + master_burst_next = master_burst_reg; + master_burst_size_next = master_burst_size_reg; + burst_active_next = burst_active_reg; + first_transfer_next = first_transfer_reg; + + s_axi_awready_next = 1'b0; + s_axi_wready_next = 1'b0; + s_axi_bid_next = s_axi_bid_reg; + s_axi_bresp_next = s_axi_bresp_reg; + s_axi_buser_next = s_axi_buser_reg; + s_axi_bvalid_next = s_axi_bvalid_reg && !s_axi_bready; + m_axi_awid_next = m_axi_awid_reg; + m_axi_awaddr_next = m_axi_awaddr_reg; + m_axi_awlen_next = m_axi_awlen_reg; + m_axi_awsize_next = m_axi_awsize_reg; + m_axi_awburst_next = m_axi_awburst_reg; + m_axi_awlock_next = m_axi_awlock_reg; + m_axi_awcache_next = m_axi_awcache_reg; + m_axi_awprot_next = m_axi_awprot_reg; + m_axi_awqos_next = m_axi_awqos_reg; + m_axi_awregion_next = m_axi_awregion_reg; + m_axi_awuser_next = m_axi_awuser_reg; + m_axi_awvalid_next = m_axi_awvalid_reg && !m_axi_awready; + m_axi_bready_next = 1'b0; + + if (SEGMENT_COUNT == 1) begin + // master output is same width; direct transfer with no splitting/merging + m_axi_wdata_int = s_axi_wdata; + m_axi_wstrb_int = s_axi_wstrb; + m_axi_wlast_int = s_axi_wlast; + m_axi_wuser_int = s_axi_wuser; + m_axi_wvalid_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_awready_next = !m_axi_awvalid; + + if (s_axi_awready && s_axi_awvalid) begin + s_axi_awready_next = 1'b0; + id_next = s_axi_awid; + m_axi_awid_next = s_axi_awid; + m_axi_awaddr_next = s_axi_awaddr; + m_axi_awlen_next = s_axi_awlen; + m_axi_awsize_next = s_axi_awsize; + m_axi_awburst_next = s_axi_awburst; + m_axi_awlock_next = s_axi_awlock; + m_axi_awcache_next = s_axi_awcache; + m_axi_awprot_next = s_axi_awprot; + m_axi_awqos_next = s_axi_awqos; + m_axi_awregion_next = s_axi_awregion; + m_axi_awuser_next = s_axi_awuser; + m_axi_awvalid_next = 1'b1; + s_axi_wready_next = m_axi_wready_int_early; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + // data state; transfer write data + s_axi_wready_next = m_axi_wready_int_early; + + if (s_axi_wready && s_axi_wvalid) begin + m_axi_wdata_int = s_axi_wdata; + m_axi_wstrb_int = s_axi_wstrb; + m_axi_wlast_int = s_axi_wlast; + m_axi_wuser_int = s_axi_wuser; + m_axi_wvalid_int = 1'b1; + if (s_axi_wlast) begin + // last data word, wait for response + s_axi_wready_next = 1'b0; + m_axi_bready_next = !s_axi_bvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + STATE_RESP: begin + // resp state; transfer write response + m_axi_bready_next = !s_axi_bvalid; + + if (m_axi_bready && m_axi_bvalid) begin + m_axi_bready_next = 1'b0; + s_axi_bid_next = id_reg; + s_axi_bresp_next = m_axi_bresp; + s_axi_buser_next = m_axi_buser; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = !m_axi_awvalid; + state_next = STATE_IDLE; + end else begin + state_next = STATE_RESP; + end + end + endcase + end else if (EXPAND) begin + // master output is wider; merge writes + m_axi_wdata_int = {(M_WORD_WIDTH/S_WORD_WIDTH){s_axi_wdata}}; + m_axi_wstrb_int = s_axi_wstrb; + m_axi_wlast_int = s_axi_wlast; + m_axi_wuser_int = s_axi_wuser; + m_axi_wvalid_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_awready_next = !m_axi_awvalid; + + data_next = {DATA_WIDTH{1'b0}}; + strb_next = {STRB_WIDTH{1'b0}}; + + if (s_axi_awready && s_axi_awvalid) begin + s_axi_awready_next = 1'b0; + id_next = s_axi_awid; + m_axi_awid_next = s_axi_awid; + m_axi_awaddr_next = s_axi_awaddr; + addr_next = s_axi_awaddr; + burst_next = s_axi_awlen; + burst_size_next = s_axi_awsize; + if (CONVERT_BURST && s_axi_awcache[1] && (CONVERT_NARROW_BURST || s_axi_awsize == S_BURST_SIZE)) begin + // merge writes + // require CONVERT_BURST and awcache[1] set + master_burst_size_next = M_BURST_SIZE; + if (CONVERT_NARROW_BURST) begin + m_axi_awlen_next = (({{S_ADDR_BIT_OFFSET+1{1'b0}}, s_axi_awlen} << s_axi_awsize) + s_axi_awaddr[M_ADDR_BIT_OFFSET-1:0]) >> M_BURST_SIZE; + end else begin + m_axi_awlen_next = ({1'b0, s_axi_awlen} + s_axi_awaddr[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET]) >> $clog2(SEGMENT_COUNT); + end + m_axi_awsize_next = M_BURST_SIZE; + state_next = STATE_DATA_2; + end else begin + // output narrow burst + master_burst_size_next = s_axi_awsize; + m_axi_awlen_next = s_axi_awlen; + m_axi_awsize_next = s_axi_awsize; + state_next = STATE_DATA; + end + m_axi_awburst_next = s_axi_awburst; + m_axi_awlock_next = s_axi_awlock; + m_axi_awcache_next = s_axi_awcache; + m_axi_awprot_next = s_axi_awprot; + m_axi_awqos_next = s_axi_awqos; + m_axi_awregion_next = s_axi_awregion; + m_axi_awuser_next = s_axi_awuser; + m_axi_awvalid_next = 1'b1; + s_axi_wready_next = m_axi_wready_int_early; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + // data state; transfer write data + s_axi_wready_next = m_axi_wready_int_early; + + if (s_axi_wready && s_axi_wvalid) begin + m_axi_wdata_int = {(M_WORD_WIDTH/S_WORD_WIDTH){s_axi_wdata}}; + m_axi_wstrb_int = s_axi_wstrb << (addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET] * S_STRB_WIDTH); + m_axi_wlast_int = s_axi_wlast; + m_axi_wuser_int = s_axi_wuser; + m_axi_wvalid_int = 1'b1; + addr_next = addr_reg + (1 << burst_size_reg); + if (s_axi_wlast) begin + s_axi_wready_next = 1'b0; + m_axi_bready_next = !s_axi_bvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + STATE_DATA_2: begin + s_axi_wready_next = m_axi_wready_int_early; + + if (s_axi_wready && s_axi_wvalid) begin + if (CONVERT_NARROW_BURST) begin + for (i = 0; i < S_WORD_WIDTH; i = i + 1) begin + if (s_axi_wstrb[i]) begin + data_next[addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET]*SEGMENT_DATA_WIDTH+i*M_WORD_SIZE +: M_WORD_SIZE] = s_axi_wdata[i*M_WORD_SIZE +: M_WORD_SIZE]; + strb_next[addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET]*SEGMENT_STRB_WIDTH+i] = 1'b1; + end + end + end else begin + data_next[addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET]*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = s_axi_wdata; + strb_next[addr_reg[M_ADDR_BIT_OFFSET-1:S_ADDR_BIT_OFFSET]*SEGMENT_STRB_WIDTH +: SEGMENT_STRB_WIDTH] = s_axi_wstrb; + end + m_axi_wdata_int = data_next; + m_axi_wstrb_int = strb_next; + m_axi_wlast_int = s_axi_wlast; + m_axi_wuser_int = s_axi_wuser; + burst_next = burst_reg - 1; + addr_next = addr_reg + (1 << burst_size_reg); + if (addr_next[master_burst_size_reg] != addr_reg[master_burst_size_reg]) begin + data_next = {DATA_WIDTH{1'b0}}; + strb_next = {STRB_WIDTH{1'b0}}; + m_axi_wvalid_int = 1'b1; + end + if (burst_reg == 0) begin + m_axi_wvalid_int = 1'b1; + s_axi_wready_next = 1'b0; + m_axi_bready_next = !s_axi_bvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA_2; + end + end else begin + state_next = STATE_DATA_2; + end + end + STATE_RESP: begin + // resp state; transfer write response + m_axi_bready_next = !s_axi_bvalid; + + if (m_axi_bready && m_axi_bvalid) begin + m_axi_bready_next = 1'b0; + s_axi_bid_next = id_reg; + s_axi_bresp_next = m_axi_bresp; + s_axi_buser_next = m_axi_buser; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = !m_axi_awvalid; + state_next = STATE_IDLE; + end else begin + state_next = STATE_RESP; + end + end + endcase + end else begin + // master output is narrower; split writes, and possibly split burst + m_axi_wdata_int = data_reg; + m_axi_wstrb_int = strb_reg; + m_axi_wlast_int = 1'b0; + m_axi_wuser_int = wuser_reg; + m_axi_wvalid_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_awready_next = !m_axi_awvalid; + + first_transfer_next = 1'b1; + + if (s_axi_awready && s_axi_awvalid) begin + s_axi_awready_next = 1'b0; + id_next = s_axi_awid; + m_axi_awid_next = s_axi_awid; + m_axi_awaddr_next = s_axi_awaddr; + addr_next = s_axi_awaddr; + burst_next = s_axi_awlen; + burst_size_next = s_axi_awsize; + burst_active_next = 1'b1; + if (s_axi_awsize > M_BURST_SIZE) begin + // need to adjust burst size + if ({s_axi_awlen, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-s_axi_awsize) > 255) begin + // limit burst length to max + master_burst_next = 8'd255; + end else begin + master_burst_next = {s_axi_awlen, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-s_axi_awsize); + end + master_burst_size_next = M_BURST_SIZE; + m_axi_awlen_next = master_burst_next; + m_axi_awsize_next = master_burst_size_next; + end else begin + // pass through narrow (enough) burst + master_burst_next = s_axi_awlen; + master_burst_size_next = s_axi_awsize; + m_axi_awlen_next = s_axi_awlen; + m_axi_awsize_next = s_axi_awsize; + end + m_axi_awburst_next = s_axi_awburst; + m_axi_awlock_next = s_axi_awlock; + m_axi_awcache_next = s_axi_awcache; + m_axi_awprot_next = s_axi_awprot; + m_axi_awqos_next = s_axi_awqos; + m_axi_awregion_next = s_axi_awregion; + m_axi_awuser_next = s_axi_awuser; + m_axi_awvalid_next = 1'b1; + s_axi_wready_next = m_axi_wready_int_early; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + s_axi_wready_next = m_axi_wready_int_early; + + if (s_axi_wready && s_axi_wvalid) begin + data_next = s_axi_wdata; + strb_next = s_axi_wstrb; + wuser_next = s_axi_wuser; + m_axi_wdata_int = s_axi_wdata >> (addr_reg[S_ADDR_BIT_OFFSET-1:M_ADDR_BIT_OFFSET] * M_DATA_WIDTH); + m_axi_wstrb_int = s_axi_wstrb >> (addr_reg[S_ADDR_BIT_OFFSET-1:M_ADDR_BIT_OFFSET] * M_STRB_WIDTH); + m_axi_wlast_int = 1'b0; + m_axi_wuser_int = s_axi_wuser; + m_axi_wvalid_int = 1'b1; + burst_next = burst_reg - 1; + burst_active_next = burst_reg != 0; + master_burst_next = master_burst_reg - 1; + addr_next = addr_reg + (1 << master_burst_size_reg); + if (master_burst_reg == 0) begin + s_axi_wready_next = 1'b0; + m_axi_bready_next = !s_axi_bvalid && !s_axi_awvalid; + m_axi_wlast_int = 1'b1; + state_next = STATE_RESP; + end else if (addr_next[burst_size_reg] != addr_reg[burst_size_reg]) begin + state_next = STATE_DATA; + end else begin + s_axi_wready_next = 1'b0; + state_next = STATE_DATA_2; + end + end else begin + state_next = STATE_DATA; + end + end + STATE_DATA_2: begin + s_axi_wready_next = 1'b0; + + if (m_axi_wready_int_reg) begin + m_axi_wdata_int = data_reg >> (addr_reg[S_ADDR_BIT_OFFSET-1:M_ADDR_BIT_OFFSET] * M_DATA_WIDTH); + m_axi_wstrb_int = strb_reg >> (addr_reg[S_ADDR_BIT_OFFSET-1:M_ADDR_BIT_OFFSET] * M_STRB_WIDTH); + m_axi_wlast_int = 1'b0; + m_axi_wuser_int = wuser_reg; + m_axi_wvalid_int = 1'b1; + master_burst_next = master_burst_reg - 1; + addr_next = addr_reg + (1 << master_burst_size_reg); + if (master_burst_reg == 0) begin + // burst on master interface finished; transfer response + s_axi_wready_next = 1'b0; + m_axi_bready_next = !s_axi_bvalid && !m_axi_awvalid; + m_axi_wlast_int = 1'b1; + state_next = STATE_RESP; + end else if (addr_next[burst_size_reg] != addr_reg[burst_size_reg]) begin + state_next = STATE_DATA; + end else begin + s_axi_wready_next = 1'b0; + state_next = STATE_DATA_2; + end + end else begin + state_next = STATE_DATA_2; + end + end + STATE_RESP: begin + // resp state; transfer write response + m_axi_bready_next = !s_axi_bvalid && !m_axi_awvalid; + + if (m_axi_bready && m_axi_bvalid) begin + first_transfer_next = 1'b0; + m_axi_bready_next = 1'b0; + s_axi_bid_next = id_reg; + if (first_transfer_reg || m_axi_bresp != 0) begin + s_axi_bresp_next = m_axi_bresp; + end + if (burst_active_reg) begin + // burst on slave interface still active; start new burst + m_axi_awaddr_next = addr_reg; + if (burst_size_reg > M_BURST_SIZE) begin + // need to adjust burst size + if ({burst_reg, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-burst_size_reg) > 255) begin + // limit burst length to max + master_burst_next = 8'd255; + end else begin + master_burst_next = {burst_reg, {S_BURST_SIZE-M_BURST_SIZE{1'b1}}} >> (S_BURST_SIZE-burst_size_reg); + end + master_burst_size_next = M_BURST_SIZE; + m_axi_awlen_next = master_burst_next; + m_axi_awsize_next = master_burst_size_next; + end else begin + // pass through narrow (enough) burst + master_burst_next = burst_reg; + master_burst_size_next = burst_size_reg; + m_axi_awlen_next = burst_reg; + m_axi_awsize_next = burst_size_reg; + end + m_axi_awvalid_next = 1'b1; + state_next = STATE_DATA; + end else begin + // burst on slave interface finished; return to idle + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = !m_axi_awvalid; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_RESP; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axi_awready_reg <= 1'b0; + s_axi_wready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + m_axi_bready_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axi_awready_reg <= s_axi_awready_next; + s_axi_wready_reg <= s_axi_wready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + m_axi_awvalid_reg <= m_axi_awvalid_next; + m_axi_bready_reg <= m_axi_bready_next; + end + + id_reg <= id_next; + addr_reg <= addr_next; + data_reg <= data_next; + strb_reg <= strb_next; + wuser_reg <= wuser_next; + burst_reg <= burst_next; + burst_size_reg <= burst_size_next; + master_burst_reg <= master_burst_next; + master_burst_size_reg <= master_burst_size_next; + burst_active_reg <= burst_active_next; + first_transfer_reg <= first_transfer_next; + + s_axi_bid_reg <= s_axi_bid_next; + s_axi_bresp_reg <= s_axi_bresp_next; + s_axi_buser_reg <= s_axi_buser_next; + m_axi_awid_reg <= m_axi_awid_next; + m_axi_awaddr_reg <= m_axi_awaddr_next; + m_axi_awlen_reg <= m_axi_awlen_next; + m_axi_awsize_reg <= m_axi_awsize_next; + m_axi_awburst_reg <= m_axi_awburst_next; + m_axi_awlock_reg <= m_axi_awlock_next; + m_axi_awcache_reg <= m_axi_awcache_next; + m_axi_awprot_reg <= m_axi_awprot_next; + m_axi_awqos_reg <= m_axi_awqos_next; + m_axi_awregion_reg <= m_axi_awregion_next; + m_axi_awuser_reg <= m_axi_awuser_next; +end + +// output datapath logic +reg [M_DATA_WIDTH-1:0] m_axi_wdata_reg = {M_DATA_WIDTH{1'b0}}; +reg [M_STRB_WIDTH-1:0] m_axi_wstrb_reg = {M_STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = 1'b0; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [M_DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {M_DATA_WIDTH{1'b0}}; +reg [M_STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {M_STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = 1'b0; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wlast = m_axi_wlast_reg; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = m_axi_wready | (~temp_m_axi_wvalid_reg & (~m_axi_wvalid_reg | ~m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (m_axi_wready | ~m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + m_axi_wuser_reg <= m_axi_wuser_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + m_axi_wuser_reg <= temp_m_axi_wuser_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + temp_m_axi_wuser_reg <= m_axi_wuser_int; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_axil_adapter.v b/corundum/lib/axi/rtl/axi_axil_adapter.v new file mode 100644 index 0000000000000000000000000000000000000000..184c5d77e73f5caa083f5eb0398938babeae1933 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_axil_adapter.v @@ -0,0 +1,219 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 to AXI4-Lite adapter + */ +module axi_axil_adapter # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) AXI interface data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of input (slave) AXI interface wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Width of output (master) AXI lite interface data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of output (master) AXI lite interface wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // When adapting to a wider bus, re-pack full-width burst instead of passing through narrow burst if possible + parameter CONVERT_BURST = 1, + // When adapting to a wider bus, re-pack all bursts instead of passing through narrow burst if possible + parameter CONVERT_NARROW_BURST = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [AXI_ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [AXI_DATA_WIDTH-1:0] s_axi_wdata, + input wire [AXI_STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [AXI_ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [AXI_ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [AXI_ID_WIDTH-1:0] s_axi_rid, + output wire [AXI_DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [AXIL_DATA_WIDTH-1:0] m_axil_wdata, + output wire [AXIL_STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready, + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [AXIL_DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + + +axi_axil_adapter_wr #( + .ADDR_WIDTH(ADDR_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH), + .CONVERT_BURST(CONVERT_BURST), + .CONVERT_NARROW_BURST(CONVERT_NARROW_BURST) +) +axi_axil_adapter_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + + /* + * AXI lite master interface + */ + .m_axil_awaddr(m_axil_awaddr), + .m_axil_awprot(m_axil_awprot), + .m_axil_awvalid(m_axil_awvalid), + .m_axil_awready(m_axil_awready), + .m_axil_wdata(m_axil_wdata), + .m_axil_wstrb(m_axil_wstrb), + .m_axil_wvalid(m_axil_wvalid), + .m_axil_wready(m_axil_wready), + .m_axil_bresp(m_axil_bresp), + .m_axil_bvalid(m_axil_bvalid), + .m_axil_bready(m_axil_bready) +); + +axi_axil_adapter_rd #( + .ADDR_WIDTH(ADDR_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH), + .CONVERT_BURST(CONVERT_BURST), + .CONVERT_NARROW_BURST(CONVERT_NARROW_BURST) +) +axi_axil_adapter_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + + /* + * AXI lite master interface + */ + .m_axil_araddr(m_axil_araddr), + .m_axil_arprot(m_axil_arprot), + .m_axil_arvalid(m_axil_arvalid), + .m_axil_arready(m_axil_arready), + .m_axil_rdata(m_axil_rdata), + .m_axil_rresp(m_axil_rresp), + .m_axil_rvalid(m_axil_rvalid), + .m_axil_rready(m_axil_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axi_axil_adapter_rd.v b/corundum/lib/axi/rtl/axi_axil_adapter_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..becf96211709284b9f62f11b6f4e6a82ef254410 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_axil_adapter_rd.v @@ -0,0 +1,503 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 to AXI4-Lite adapter (read) + */ +module axi_axil_adapter_rd # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) AXI interface data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of input (slave) AXI interface wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Width of output (master) AXI lite interface data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of output (master) AXI lite interface wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // When adapting to a wider bus, re-pack full-width burst instead of passing through narrow burst if possible + parameter CONVERT_BURST = 1, + // When adapting to a wider bus, re-pack all bursts instead of passing through narrow burst if possible + parameter CONVERT_NARROW_BURST = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [AXI_ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [AXI_ID_WIDTH-1:0] s_axi_rid, + output wire [AXI_DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [AXIL_DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + +parameter AXI_ADDR_BIT_OFFSET = $clog2(AXI_STRB_WIDTH); +parameter AXIL_ADDR_BIT_OFFSET = $clog2(AXIL_STRB_WIDTH); +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXIL_WORD_WIDTH = AXIL_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXIL_WORD_SIZE = AXIL_DATA_WIDTH/AXIL_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXIL_BURST_SIZE = $clog2(AXIL_STRB_WIDTH); + +// output bus is wider +parameter EXPAND = AXIL_STRB_WIDTH > AXI_STRB_WIDTH; +parameter DATA_WIDTH = EXPAND ? AXIL_DATA_WIDTH : AXI_DATA_WIDTH; +parameter STRB_WIDTH = EXPAND ? AXIL_STRB_WIDTH : AXI_STRB_WIDTH; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND ? (AXIL_STRB_WIDTH / AXI_STRB_WIDTH) : (AXI_STRB_WIDTH / AXIL_STRB_WIDTH); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_STRB_WIDTH = STRB_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (AXI_WORD_SIZE * AXI_STRB_WIDTH != AXI_DATA_WIDTH) begin + $error("Error: AXI slave interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXIL_WORD_SIZE * AXIL_STRB_WIDTH != AXIL_DATA_WIDTH) begin + $error("Error: AXI lite master interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXI_WORD_SIZE != AXIL_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(AXI_WORD_WIDTH) != AXI_WORD_WIDTH) begin + $error("Error: AXI slave interface word width must be even power of two (instance %m)"); + $finish; + end + + if (2**$clog2(AXIL_WORD_WIDTH) != AXIL_WORD_WIDTH) begin + $error("Error: AXI lite master interface word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_DATA = 2'd1, + STATE_DATA_READ = 2'd2, + STATE_DATA_SPLIT = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [AXI_ID_WIDTH-1:0] id_reg = {AXI_ID_WIDTH{1'b0}}, id_next; +reg [ADDR_WIDTH-1:0] addr_reg = {ADDR_WIDTH{1'b0}}, addr_next; +reg [DATA_WIDTH-1:0] data_reg = {DATA_WIDTH{1'b0}}, data_next; +reg [1:0] resp_reg = 2'd0, resp_next; +reg [7:0] burst_reg = 8'd0, burst_next; +reg [2:0] burst_size_reg = 3'd0, burst_size_next; +reg [7:0] master_burst_reg = 8'd0, master_burst_next; +reg [2:0] master_burst_size_reg = 3'd0, master_burst_size_next; + +reg s_axi_arready_reg = 1'b0, s_axi_arready_next; +reg [AXI_ID_WIDTH-1:0] s_axi_rid_reg = {AXI_ID_WIDTH{1'b0}}, s_axi_rid_next; +reg [AXI_DATA_WIDTH-1:0] s_axi_rdata_reg = {AXI_DATA_WIDTH{1'b0}}, s_axi_rdata_next; +reg [1:0] s_axi_rresp_reg = 2'd0, s_axi_rresp_next; +reg s_axi_rlast_reg = 1'b0, s_axi_rlast_next; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ADDR_WIDTH-1:0] m_axil_araddr_reg = {ADDR_WIDTH{1'b0}}, m_axil_araddr_next; +reg [2:0] m_axil_arprot_reg = 3'd0, m_axil_arprot_next; +reg m_axil_arvalid_reg = 1'b0, m_axil_arvalid_next; +reg m_axil_rready_reg = 1'b0, m_axil_rready_next; + +assign s_axi_arready = s_axi_arready_reg; +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_rvalid = s_axi_rvalid_reg; + +assign m_axil_araddr = m_axil_araddr_reg; +assign m_axil_arprot = m_axil_arprot_reg; +assign m_axil_arvalid = m_axil_arvalid_reg; +assign m_axil_rready = m_axil_rready_reg; + +always @* begin + state_next = STATE_IDLE; + + id_next = id_reg; + addr_next = addr_reg; + data_next = data_reg; + resp_next = resp_reg; + burst_next = burst_reg; + burst_size_next = burst_size_reg; + master_burst_next = master_burst_reg; + master_burst_size_next = master_burst_size_reg; + + s_axi_arready_next = 1'b0; + s_axi_rid_next = s_axi_rid_reg; + s_axi_rdata_next = s_axi_rdata_reg; + s_axi_rresp_next = s_axi_rresp_reg; + s_axi_rlast_next = s_axi_rlast_reg; + s_axi_rvalid_next = s_axi_rvalid_reg && !s_axi_rready; + m_axil_araddr_next = m_axil_araddr_reg; + m_axil_arprot_next = m_axil_arprot_reg; + m_axil_arvalid_next = m_axil_arvalid_reg && !m_axil_arready; + m_axil_rready_next = 1'b0; + + if (SEGMENT_COUNT == 1) begin + // master output is same width; direct transfer with no splitting/merging + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_arready_next = !m_axil_arvalid; + + if (s_axi_arready && s_axi_arvalid) begin + s_axi_arready_next = 1'b0; + id_next = s_axi_arid; + m_axil_araddr_next = s_axi_araddr; + addr_next = s_axi_araddr; + burst_next = s_axi_arlen; + burst_size_next = s_axi_arsize; + m_axil_arprot_next = s_axi_arprot; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + // data state; transfer read data + m_axil_rready_next = !s_axi_rvalid && !m_axil_arvalid; + + if (m_axil_rready && m_axil_rvalid) begin + s_axi_rid_next = id_reg; + s_axi_rdata_next = m_axil_rdata; + s_axi_rresp_next = m_axil_rresp; + s_axi_rlast_next = 1'b0; + s_axi_rvalid_next = 1'b1; + burst_next = burst_reg - 1; + addr_next = addr_reg + (1 << burst_size_reg); + if (burst_reg == 0) begin + // last data word, return to idle + m_axil_rready_next = 1'b0; + s_axi_rlast_next = 1'b1; + s_axi_arready_next = !m_axil_arvalid; + state_next = STATE_IDLE; + end else begin + // start new AXI lite read + m_axil_araddr_next = addr_next; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + endcase + end else if (EXPAND) begin + // master output is wider; split reads + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_arready_next = !m_axil_arvalid; + + if (s_axi_arready && s_axi_arvalid) begin + s_axi_arready_next = 1'b0; + id_next = s_axi_arid; + m_axil_araddr_next = s_axi_araddr; + addr_next = s_axi_araddr; + burst_next = s_axi_arlen; + burst_size_next = s_axi_arsize; + if (CONVERT_BURST && s_axi_arcache[1] && (CONVERT_NARROW_BURST || s_axi_arsize == AXI_BURST_SIZE)) begin + // split reads + // require CONVERT_BURST and arcache[1] set + master_burst_size_next = AXIL_BURST_SIZE; + state_next = STATE_DATA_READ; + end else begin + // output narrow burst + master_burst_size_next = s_axi_arsize; + state_next = STATE_DATA; + end + m_axil_arprot_next = s_axi_arprot; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + m_axil_rready_next = !s_axi_rvalid && !m_axil_arvalid; + + if (m_axil_rready && m_axil_rvalid) begin + s_axi_rid_next = id_reg; + s_axi_rdata_next = m_axil_rdata >> (addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET] * AXI_DATA_WIDTH); + s_axi_rresp_next = m_axil_rresp; + s_axi_rlast_next = 1'b0; + s_axi_rvalid_next = 1'b1; + burst_next = burst_reg - 1; + addr_next = addr_reg + (1 << burst_size_reg); + if (burst_reg == 0) begin + // last data word, return to idle + m_axil_rready_next = 1'b0; + s_axi_rlast_next = 1'b1; + s_axi_arready_next = !m_axil_arvalid; + state_next = STATE_IDLE; + end else begin + // start new AXI lite read + m_axil_araddr_next = addr_next; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + STATE_DATA_READ: begin + m_axil_rready_next = !s_axi_rvalid && !m_axil_arvalid; + + if (m_axil_rready && m_axil_rvalid) begin + s_axi_rid_next = id_reg; + data_next = m_axil_rdata; + resp_next = m_axil_rresp; + s_axi_rdata_next = m_axil_rdata >> (addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET] * AXI_DATA_WIDTH); + s_axi_rresp_next = m_axil_rresp; + s_axi_rlast_next = 1'b0; + s_axi_rvalid_next = 1'b1; + burst_next = burst_reg - 1; + addr_next = addr_reg + (1 << burst_size_reg); + if (burst_reg == 0) begin + m_axil_rready_next = 1'b0; + s_axi_arready_next = !m_axil_arvalid; + s_axi_rlast_next = 1'b1; + state_next = STATE_IDLE; + end else if (addr_next[master_burst_size_reg] != addr_reg[master_burst_size_reg]) begin + // start new AXI lite read + m_axil_araddr_next = addr_next; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA_READ; + end else begin + m_axil_rready_next = 1'b0; + state_next = STATE_DATA_SPLIT; + end + end else begin + state_next = STATE_DATA_READ; + end + end + STATE_DATA_SPLIT: begin + m_axil_rready_next = 1'b0; + + if (s_axi_rready || !s_axi_rvalid) begin + s_axi_rid_next = id_reg; + s_axi_rdata_next = data_reg >> (addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET] * AXI_DATA_WIDTH); + s_axi_rresp_next = resp_reg; + s_axi_rlast_next = 1'b0; + s_axi_rvalid_next = 1'b1; + burst_next = burst_reg - 1; + addr_next = addr_reg + (1 << burst_size_reg); + if (burst_reg == 0) begin + s_axi_arready_next = !m_axil_arvalid; + s_axi_rlast_next = 1'b1; + state_next = STATE_IDLE; + end else if (addr_next[master_burst_size_reg] != addr_reg[master_burst_size_reg]) begin + // start new AXI lite read + m_axil_araddr_next = addr_next; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA_READ; + end else begin + state_next = STATE_DATA_SPLIT; + end + end else begin + state_next = STATE_DATA_SPLIT; + end + end + endcase + end else begin + // master output is narrower; merge reads and possibly split burst + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_arready_next = !m_axil_arvalid; + + resp_next = 2'd0; + + if (s_axi_arready && s_axi_arvalid) begin + s_axi_arready_next = 1'b0; + id_next = s_axi_arid; + m_axil_araddr_next = s_axi_araddr; + addr_next = s_axi_araddr; + burst_next = s_axi_arlen; + burst_size_next = s_axi_arsize; + if (s_axi_arsize > AXIL_BURST_SIZE) begin + // need to adjust burst size + if ({s_axi_arlen, {AXI_BURST_SIZE-AXIL_BURST_SIZE{1'b1}}} >> (AXI_BURST_SIZE-s_axi_arsize) > 255) begin + // limit burst length to max + master_burst_next = 8'd255; + end else begin + master_burst_next = {s_axi_arlen, {AXI_BURST_SIZE-AXIL_BURST_SIZE{1'b1}}} >> (AXI_BURST_SIZE-s_axi_arsize); + end + master_burst_size_next = AXIL_BURST_SIZE; + end else begin + // pass through narrow (enough) burst + master_burst_next = s_axi_arlen; + master_burst_size_next = s_axi_arsize; + end + m_axil_arprot_next = s_axi_arprot; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + m_axil_rready_next = !s_axi_rvalid && !m_axil_arvalid; + + if (m_axil_rready && m_axil_rvalid) begin + data_next[addr_reg[AXI_ADDR_BIT_OFFSET-1:AXIL_ADDR_BIT_OFFSET]*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = m_axil_rdata; + if (m_axil_rresp) begin + resp_next = m_axil_rresp; + end + s_axi_rid_next = id_reg; + s_axi_rdata_next = data_next; + s_axi_rresp_next = resp_next; + s_axi_rlast_next = 1'b0; + s_axi_rvalid_next = 1'b0; + master_burst_next = master_burst_reg - 1; + addr_next = addr_reg + (1 << master_burst_size_reg); + if (addr_next[burst_size_reg] != addr_reg[burst_size_reg]) begin + data_next = {DATA_WIDTH{1'b0}}; + burst_next = burst_reg - 1; + s_axi_rvalid_next = 1'b1; + end + if (master_burst_reg == 0) begin + if (burst_reg == 0) begin + m_axil_rready_next = 1'b0; + s_axi_rlast_next = 1'b1; + s_axi_rvalid_next = 1'b1; + s_axi_arready_next = !m_axil_arvalid; + state_next = STATE_IDLE; + end else begin + // start new burst + m_axil_araddr_next = addr_next; + if (burst_size_reg > AXIL_BURST_SIZE) begin + // need to adjust burst size + if ({burst_next, {AXI_BURST_SIZE-AXIL_BURST_SIZE{1'b1}}} >> (AXI_BURST_SIZE-burst_size_reg) > 255) begin + // limit burst length to max + master_burst_next = 8'd255; + end else begin + master_burst_next = {burst_next, {AXI_BURST_SIZE-AXIL_BURST_SIZE{1'b1}}} >> (AXI_BURST_SIZE-burst_size_reg); + end + master_burst_size_next = AXIL_BURST_SIZE; + end else begin + // pass through narrow (enough) burst + master_burst_next = burst_next; + master_burst_size_next = burst_size_reg; + end + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA; + end + end else begin + m_axil_araddr_next = addr_next; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = 1'b0; + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axi_arready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + m_axil_arvalid_reg <= 1'b0; + m_axil_rready_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axi_arready_reg <= s_axi_arready_next; + s_axi_rvalid_reg <= s_axi_rvalid_next; + m_axil_arvalid_reg <= m_axil_arvalid_next; + m_axil_rready_reg <= m_axil_rready_next; + end + + id_reg <= id_next; + addr_reg <= addr_next; + data_reg <= data_next; + resp_reg <= resp_next; + burst_reg <= burst_next; + burst_size_reg <= burst_size_next; + master_burst_reg <= master_burst_next; + master_burst_size_reg <= master_burst_size_next; + + s_axi_rid_reg <= s_axi_rid_next; + s_axi_rdata_reg <= s_axi_rdata_next; + s_axi_rresp_reg <= s_axi_rresp_next; + s_axi_rlast_reg <= s_axi_rlast_next; + m_axil_araddr_reg <= m_axil_araddr_next; + m_axil_arprot_reg <= m_axil_arprot_next; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_axil_adapter_wr.v b/corundum/lib/axi/rtl/axi_axil_adapter_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..5a878944a3187f46c6720b4d81ae3f4f91c8bcbc --- /dev/null +++ b/corundum/lib/axi/rtl/axi_axil_adapter_wr.v @@ -0,0 +1,557 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 to AXI4-Lite adapter (write) + */ +module axi_axil_adapter_wr # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) AXI interface data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of input (slave) AXI interface wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Width of output (master) AXI lite interface data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of output (master) AXI lite interface wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // When adapting to a wider bus, re-pack full-width burst instead of passing through narrow burst if possible + parameter CONVERT_BURST = 1, + // When adapting to a wider bus, re-pack all bursts instead of passing through narrow burst if possible + parameter CONVERT_NARROW_BURST = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [AXI_ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [AXI_DATA_WIDTH-1:0] s_axi_wdata, + input wire [AXI_STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [AXI_ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire s_axi_bvalid, + input wire s_axi_bready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [AXIL_DATA_WIDTH-1:0] m_axil_wdata, + output wire [AXIL_STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready +); + +parameter AXI_ADDR_BIT_OFFSET = $clog2(AXI_STRB_WIDTH); +parameter AXIL_ADDR_BIT_OFFSET = $clog2(AXIL_STRB_WIDTH); +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXIL_WORD_WIDTH = AXIL_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXIL_WORD_SIZE = AXIL_DATA_WIDTH/AXIL_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXIL_BURST_SIZE = $clog2(AXIL_STRB_WIDTH); + +// output bus is wider +parameter EXPAND = AXIL_STRB_WIDTH > AXI_STRB_WIDTH; +parameter DATA_WIDTH = EXPAND ? AXIL_DATA_WIDTH : AXI_DATA_WIDTH; +parameter STRB_WIDTH = EXPAND ? AXIL_STRB_WIDTH : AXI_STRB_WIDTH; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND ? (AXIL_STRB_WIDTH / AXI_STRB_WIDTH) : (AXI_STRB_WIDTH / AXIL_STRB_WIDTH); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_STRB_WIDTH = STRB_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (AXI_WORD_SIZE * AXI_STRB_WIDTH != AXI_DATA_WIDTH) begin + $error("Error: AXI slave interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXIL_WORD_SIZE * AXIL_STRB_WIDTH != AXIL_DATA_WIDTH) begin + $error("Error: AXI lite master interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXI_WORD_SIZE != AXIL_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(AXI_WORD_WIDTH) != AXI_WORD_WIDTH) begin + $error("Error: AXI slave interface word width must be even power of two (instance %m)"); + $finish; + end + + if (2**$clog2(AXIL_WORD_WIDTH) != AXIL_WORD_WIDTH) begin + $error("Error: AXI lite master interface word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_DATA = 2'd1, + STATE_DATA_2 = 2'd2, + STATE_RESP = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [AXI_ID_WIDTH-1:0] id_reg = {AXI_ID_WIDTH{1'b0}}, id_next; +reg [ADDR_WIDTH-1:0] addr_reg = {ADDR_WIDTH{1'b0}}, addr_next; +reg [DATA_WIDTH-1:0] data_reg = {DATA_WIDTH{1'b0}}, data_next; +reg [STRB_WIDTH-1:0] strb_reg = {STRB_WIDTH{1'b0}}, strb_next; +reg [7:0] burst_reg = 8'd0, burst_next; +reg [2:0] burst_size_reg = 3'd0, burst_size_next; +reg [2:0] master_burst_size_reg = 3'd0, master_burst_size_next; +reg burst_active_reg = 1'b0, burst_active_next; +reg convert_burst_reg = 1'b0, convert_burst_next; +reg first_transfer_reg = 1'b0, first_transfer_next; +reg last_segment_reg = 1'b0, last_segment_next; + +reg s_axi_awready_reg = 1'b0, s_axi_awready_next; +reg s_axi_wready_reg = 1'b0, s_axi_wready_next; +reg [AXI_ID_WIDTH-1:0] s_axi_bid_reg = {AXI_ID_WIDTH{1'b0}}, s_axi_bid_next; +reg [1:0] s_axi_bresp_reg = 2'd0, s_axi_bresp_next; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +reg [ADDR_WIDTH-1:0] m_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}, m_axil_awaddr_next; +reg [2:0] m_axil_awprot_reg = 3'd0, m_axil_awprot_next; +reg m_axil_awvalid_reg = 1'b0, m_axil_awvalid_next; +reg [AXIL_DATA_WIDTH-1:0] m_axil_wdata_reg = {AXIL_DATA_WIDTH{1'b0}}, m_axil_wdata_next; +reg [AXIL_STRB_WIDTH-1:0] m_axil_wstrb_reg = {AXIL_STRB_WIDTH{1'b0}}, m_axil_wstrb_next; +reg m_axil_wvalid_reg = 1'b0, m_axil_wvalid_next; +reg m_axil_bready_reg = 1'b0, m_axil_bready_next; + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = s_axi_wready_reg; +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_bvalid = s_axi_bvalid_reg; + +assign m_axil_awaddr = m_axil_awaddr_reg; +//assign m_axil_awlen = m_axil_awlen_reg; +//assign m_axil_awsize = m_axil_awsize_reg; +//assign m_axil_awburst = m_axil_awburst_reg; +assign m_axil_awprot = m_axil_awprot_reg; +assign m_axil_awvalid = m_axil_awvalid_reg; +assign m_axil_wdata = m_axil_wdata_reg; +assign m_axil_wstrb = m_axil_wstrb_reg; +assign m_axil_wvalid = m_axil_wvalid_reg; +assign m_axil_bready = m_axil_bready_reg; + +integer i; + +always @* begin + state_next = STATE_IDLE; + + id_next = id_reg; + addr_next = addr_reg; + data_next = data_reg; + strb_next = strb_reg; + burst_next = burst_reg; + burst_size_next = burst_size_reg; + master_burst_size_next = master_burst_size_reg; + burst_active_next = burst_active_reg; + convert_burst_next = convert_burst_reg; + first_transfer_next = first_transfer_reg; + last_segment_next = last_segment_reg; + + s_axi_awready_next = 1'b0; + s_axi_wready_next = 1'b0; + s_axi_bid_next = s_axi_bid_reg; + s_axi_bresp_next = s_axi_bresp_reg; + s_axi_bvalid_next = s_axi_bvalid_reg && !s_axi_bready; + m_axil_awaddr_next = m_axil_awaddr_reg; + m_axil_awprot_next = m_axil_awprot_reg; + m_axil_awvalid_next = m_axil_awvalid_reg && !m_axil_awready; + m_axil_wdata_next = m_axil_wdata_reg; + m_axil_wstrb_next = m_axil_wstrb_reg; + m_axil_wvalid_next = m_axil_wvalid_reg && !m_axil_wready; + m_axil_bready_next = 1'b0; + + if (SEGMENT_COUNT == 1) begin + // master output is same width; direct transfer with no splitting/merging + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_awready_next = !m_axil_awvalid; + first_transfer_next = 1'b1; + + if (s_axi_awready && s_axi_awvalid) begin + s_axi_awready_next = 1'b0; + id_next = s_axi_awid; + m_axil_awaddr_next = s_axi_awaddr; + addr_next = s_axi_awaddr; + burst_next = s_axi_awlen; + burst_size_next = s_axi_awsize; + burst_active_next = 1'b1; + m_axil_awprot_next = s_axi_awprot; + m_axil_awvalid_next = 1'b1; + s_axi_wready_next = !m_axil_wvalid; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + // data state; transfer write data + s_axi_wready_next = !m_axil_wvalid; + + if (s_axi_wready && s_axi_wvalid) begin + m_axil_wdata_next = s_axi_wdata; + m_axil_wstrb_next = s_axi_wstrb; + m_axil_wvalid_next = 1'b1; + burst_next = burst_reg - 1; + burst_active_next = burst_reg != 0; + addr_next = addr_reg + (1 << burst_size_reg); + s_axi_wready_next = 1'b0; + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA; + end + end + STATE_RESP: begin + // resp state; transfer write response + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + + if (m_axil_bready && m_axil_bvalid) begin + m_axil_bready_next = 1'b0; + s_axi_bid_next = id_reg; + first_transfer_next = 1'b0; + if (first_transfer_reg || m_axil_bresp != 0) begin + s_axi_bresp_next = m_axil_bresp; + end + if (burst_active_reg) begin + // burst on slave interface still active; start new AXI lite write + m_axil_awaddr_next = addr_reg; + m_axil_awvalid_next = 1'b1; + s_axi_wready_next = !m_axil_wvalid; + state_next = STATE_DATA; + end else begin + // burst on slave interface finished; return to idle + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = !m_axil_awvalid; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_RESP; + end + end + endcase + end else if (EXPAND) begin + // master output is wider; merge writes + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_awready_next = !m_axil_awvalid; + + first_transfer_next = 1'b1; + + data_next = {DATA_WIDTH{1'b0}}; + strb_next = {STRB_WIDTH{1'b0}}; + + if (s_axi_awready && s_axi_awvalid) begin + s_axi_awready_next = 1'b0; + id_next = s_axi_awid; + m_axil_awaddr_next = s_axi_awaddr; + addr_next = s_axi_awaddr; + burst_next = s_axi_awlen; + burst_size_next = s_axi_awsize; + if (CONVERT_BURST && s_axi_awcache[1] && (CONVERT_NARROW_BURST || s_axi_awsize == AXI_BURST_SIZE)) begin + // merge writes + // require CONVERT_BURST and awcache[1] set + convert_burst_next = 1'b1; + master_burst_size_next = AXIL_BURST_SIZE; + state_next = STATE_DATA_2; + end else begin + // output narrow burst + convert_burst_next = 1'b0; + master_burst_size_next = s_axi_awsize; + state_next = STATE_DATA; + end + m_axil_awprot_next = s_axi_awprot; + m_axil_awvalid_next = 1'b1; + s_axi_wready_next = !m_axil_wvalid; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + // data state; transfer write data + s_axi_wready_next = !m_axil_wvalid || m_axil_wready; + + if (s_axi_wready && s_axi_wvalid) begin + m_axil_wdata_next = {(AXIL_WORD_WIDTH/AXI_WORD_WIDTH){s_axi_wdata}}; + m_axil_wstrb_next = s_axi_wstrb << (addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET] * AXI_STRB_WIDTH); + m_axil_wvalid_next = 1'b1; + burst_next = burst_reg - 1; + burst_active_next = burst_reg != 0; + addr_next = addr_reg + (1 << burst_size_reg); + s_axi_wready_next = 1'b0; + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA; + end + end + STATE_DATA_2: begin + s_axi_wready_next = !m_axil_wvalid; + + if (s_axi_wready && s_axi_wvalid) begin + if (CONVERT_NARROW_BURST) begin + for (i = 0; i < AXI_WORD_WIDTH; i = i + 1) begin + if (s_axi_wstrb[i]) begin + data_next[addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET]*SEGMENT_DATA_WIDTH+i*AXIL_WORD_SIZE +: AXIL_WORD_SIZE] = s_axi_wdata[i*AXIL_WORD_SIZE +: AXIL_WORD_SIZE]; + strb_next[addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET]*SEGMENT_STRB_WIDTH+i] = 1'b1; + end + end + end else begin + data_next[addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET]*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = s_axi_wdata; + strb_next[addr_reg[AXIL_ADDR_BIT_OFFSET-1:AXI_ADDR_BIT_OFFSET]*SEGMENT_STRB_WIDTH +: SEGMENT_STRB_WIDTH] = s_axi_wstrb; + end + m_axil_wdata_next = data_next; + m_axil_wstrb_next = strb_next; + burst_next = burst_reg - 1; + burst_active_next = burst_reg != 0; + addr_next = addr_reg + (1 << burst_size_reg); + if (burst_reg == 0 || addr_next[master_burst_size_reg] != addr_reg[master_burst_size_reg]) begin + data_next = {DATA_WIDTH{1'b0}}; + strb_next = {STRB_WIDTH{1'b0}}; + m_axil_wvalid_next = 1'b1; + s_axi_wready_next = 1'b0; + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA_2; + end + end else begin + state_next = STATE_DATA_2; + end + end + STATE_RESP: begin + // resp state; transfer write response + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + + if (m_axil_bready && m_axil_bvalid) begin + m_axil_bready_next = 1'b0; + s_axi_bid_next = id_reg; + first_transfer_next = 1'b0; + if (first_transfer_reg || m_axil_bresp != 0) begin + s_axi_bresp_next = m_axil_bresp; + end + if (burst_active_reg) begin + // burst on slave interface still active; start new AXI lite write + m_axil_awaddr_next = addr_reg; + m_axil_awvalid_next = 1'b1; + s_axi_wready_next = !m_axil_wvalid || m_axil_wready; + if (convert_burst_reg) begin + state_next = STATE_DATA_2; + end else begin + state_next = STATE_DATA; + end + end else begin + // burst on slave interface finished; return to idle + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = !m_axil_awvalid; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_RESP; + end + end + endcase + end else begin + // master output is narrower; split writes, and possibly split burst + case (state_reg) + STATE_IDLE: begin + // idle state; wait for new burst + s_axi_awready_next = !m_axil_awvalid; + + first_transfer_next = 1'b1; + + if (s_axi_awready && s_axi_awvalid) begin + s_axi_awready_next = 1'b0; + id_next = s_axi_awid; + m_axil_awaddr_next = s_axi_awaddr; + addr_next = s_axi_awaddr; + burst_next = s_axi_awlen; + burst_size_next = s_axi_awsize; + burst_active_next = 1'b1; + if (s_axi_awsize > AXIL_BURST_SIZE) begin + // need to adjust burst size + master_burst_size_next = AXIL_BURST_SIZE; + end else begin + // pass through narrow (enough) burst + master_burst_size_next = s_axi_awsize; + end + m_axil_awprot_next = s_axi_awprot; + m_axil_awvalid_next = 1'b1; + s_axi_wready_next = !m_axil_wvalid; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + s_axi_wready_next = !m_axil_wvalid; + + if (s_axi_wready && s_axi_wvalid) begin + data_next = s_axi_wdata; + strb_next = s_axi_wstrb; + m_axil_wdata_next = s_axi_wdata >> (addr_reg[AXI_ADDR_BIT_OFFSET-1:AXIL_ADDR_BIT_OFFSET] * AXIL_DATA_WIDTH); + m_axil_wstrb_next = s_axi_wstrb >> (addr_reg[AXI_ADDR_BIT_OFFSET-1:AXIL_ADDR_BIT_OFFSET] * AXIL_STRB_WIDTH); + m_axil_wvalid_next = 1'b1; + burst_next = burst_reg - 1; + burst_active_next = burst_reg != 0; + addr_next = addr_reg + (1 << master_burst_size_reg); + last_segment_next = addr_next[burst_size_reg] != addr_reg[burst_size_reg]; + s_axi_wready_next = 1'b0; + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA; + end + end + STATE_DATA_2: begin + s_axi_wready_next = 1'b0; + + if (!m_axil_wvalid || m_axil_wready) begin + m_axil_wdata_next = data_reg >> (addr_reg[AXI_ADDR_BIT_OFFSET-1:AXIL_ADDR_BIT_OFFSET] * AXIL_DATA_WIDTH); + m_axil_wstrb_next = strb_reg >> (addr_reg[AXI_ADDR_BIT_OFFSET-1:AXIL_ADDR_BIT_OFFSET] * AXIL_STRB_WIDTH); + m_axil_wvalid_next = 1'b1; + addr_next = addr_reg + (1 << master_burst_size_reg); + last_segment_next = addr_next[burst_size_reg] != addr_reg[burst_size_reg]; + s_axi_wready_next = 1'b0; + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA_2; + end + end + STATE_RESP: begin + // resp state; transfer write response + m_axil_bready_next = !s_axi_bvalid && !m_axil_awvalid; + + if (m_axil_bready && m_axil_bvalid) begin + first_transfer_next = 1'b0; + m_axil_bready_next = 1'b0; + s_axi_bid_next = id_reg; + if (first_transfer_reg || m_axil_bresp != 0) begin + s_axi_bresp_next = m_axil_bresp; + end + if (burst_active_reg || !last_segment_reg) begin + // burst on slave interface still active; start new burst + m_axil_awaddr_next = addr_reg; + m_axil_awvalid_next = 1'b1; + if (last_segment_reg) begin + s_axi_wready_next = !m_axil_wvalid; + state_next = STATE_DATA; + end else begin + s_axi_wready_next = 1'b0; + state_next = STATE_DATA_2; + end + end else begin + // burst on slave interface finished; return to idle + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = !m_axil_awvalid; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_RESP; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axi_awready_reg <= 1'b0; + s_axi_wready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + m_axil_awvalid_reg <= 1'b0; + m_axil_wvalid_reg <= 1'b0; + m_axil_bready_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axi_awready_reg <= s_axi_awready_next; + s_axi_wready_reg <= s_axi_wready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + m_axil_awvalid_reg <= m_axil_awvalid_next; + m_axil_wvalid_reg <= m_axil_wvalid_next; + m_axil_bready_reg <= m_axil_bready_next; + end + + id_reg <= id_next; + addr_reg <= addr_next; + data_reg <= data_next; + strb_reg <= strb_next; + burst_reg <= burst_next; + burst_size_reg <= burst_size_next; + master_burst_size_reg <= master_burst_size_next; + burst_active_reg <= burst_active_next; + convert_burst_reg <= convert_burst_next; + first_transfer_reg <= first_transfer_next; + last_segment_reg <= last_segment_next; + + s_axi_bid_reg <= s_axi_bid_next; + s_axi_bresp_reg <= s_axi_bresp_next; + m_axil_awaddr_reg <= m_axil_awaddr_next; + m_axil_awprot_reg <= m_axil_awprot_next; + m_axil_wdata_reg <= m_axil_wdata_next; + m_axil_wstrb_reg <= m_axil_wstrb_next; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_cdma.v b/corundum/lib/axi/rtl/axi_cdma.v new file mode 100644 index 0000000000000000000000000000000000000000..527fd1ab2c7fbfa56b3eb99364c068673f780a38 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_cdma.v @@ -0,0 +1,737 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 Central DMA + */ +module axi_cdma # +( + // Width of data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of address bus in bits + parameter AXI_ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 16, + // Width of length field + parameter LEN_WIDTH = 20, + // Width of tag field + parameter TAG_WIDTH = 8, + // Enable support for unaligned transfers + parameter ENABLE_UNALIGNED = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI descriptor input + */ + input wire [AXI_ADDR_WIDTH-1:0] s_axis_desc_read_addr, + input wire [AXI_ADDR_WIDTH-1:0] s_axis_desc_write_addr, + input wire [LEN_WIDTH-1:0] s_axis_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_desc_tag, + input wire s_axis_desc_valid, + output wire s_axis_desc_ready, + + /* + * AXI descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_desc_status_tag, + output wire m_axis_desc_status_valid, + + /* + * AXI write master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + + /* + * AXI read master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready, + + /* + * Configuration + */ + input wire enable +); + +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXI_MAX_BURST_SIZE = AXI_MAX_BURST_LEN << AXI_BURST_SIZE; + +parameter OFFSET_WIDTH = AXI_STRB_WIDTH > 1 ? $clog2(AXI_STRB_WIDTH) : 1; +parameter OFFSET_MASK = AXI_STRB_WIDTH > 1 ? {OFFSET_WIDTH{1'b1}} : 0; +parameter ADDR_MASK = {AXI_ADDR_WIDTH{1'b1}} << $clog2(AXI_STRB_WIDTH); +parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - AXI_BURST_SIZE + 1; + +parameter STATUS_FIFO_ADDR_WIDTH = 5; + +// bus width assertions +initial begin + if (AXI_WORD_SIZE * AXI_STRB_WIDTH != AXI_DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (2**$clog2(AXI_WORD_WIDTH) != AXI_WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end + + if (AXI_MAX_BURST_LEN < 1 || AXI_MAX_BURST_LEN > 256) begin + $error("Error: AXI_MAX_BURST_LEN must be between 1 and 256 (instance %m)"); + $finish; + end +end + +localparam [1:0] + READ_STATE_IDLE = 2'd0, + READ_STATE_START = 2'd1, + READ_STATE_REQ = 2'd2; + +reg [1:0] read_state_reg = READ_STATE_IDLE, read_state_next; + +localparam [0:0] + AXI_STATE_IDLE = 1'd0, + AXI_STATE_WRITE = 1'd1; + +reg [0:0] axi_state_reg = AXI_STATE_IDLE, axi_state_next; + +// datapath control signals +reg transfer_in_save; +reg axi_cmd_ready; +reg status_fifo_we; + +reg [AXI_ADDR_WIDTH-1:0] read_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, read_addr_next; +reg [AXI_ADDR_WIDTH-1:0] write_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, write_addr_next; +reg [LEN_WIDTH-1:0] op_word_count_reg = {LEN_WIDTH{1'b0}}, op_word_count_next; +reg [LEN_WIDTH-1:0] tr_word_count_reg = {LEN_WIDTH{1'b0}}, tr_word_count_next; +reg [LEN_WIDTH-1:0] axi_word_count_reg = {LEN_WIDTH{1'b0}}, axi_word_count_next; + +reg [AXI_ADDR_WIDTH-1:0] axi_cmd_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, axi_cmd_addr_next; +reg [OFFSET_WIDTH-1:0] axi_cmd_offset_reg = {OFFSET_WIDTH{1'b0}}, axi_cmd_offset_next; +reg [OFFSET_WIDTH-1:0] axi_cmd_first_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, axi_cmd_first_cycle_offset_next; +reg [OFFSET_WIDTH-1:0] axi_cmd_last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, axi_cmd_last_cycle_offset_next; +reg [CYCLE_COUNT_WIDTH-1:0] axi_cmd_input_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, axi_cmd_input_cycle_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] axi_cmd_output_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, axi_cmd_output_cycle_count_next; +reg axi_cmd_bubble_cycle_reg = 1'b0, axi_cmd_bubble_cycle_next; +reg axi_cmd_last_transfer_reg = 1'b0, axi_cmd_last_transfer_next; +reg [TAG_WIDTH-1:0] axi_cmd_tag_reg = {TAG_WIDTH{1'b0}}, axi_cmd_tag_next; +reg axi_cmd_valid_reg = 1'b0, axi_cmd_valid_next; + +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; +reg [OFFSET_WIDTH-1:0] first_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, first_cycle_offset_next; +reg [OFFSET_WIDTH-1:0] last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, last_cycle_offset_next; +reg [CYCLE_COUNT_WIDTH-1:0] input_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, input_cycle_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] output_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, output_cycle_count_next; +reg input_active_reg = 1'b0, input_active_next; +reg output_active_reg = 1'b0, output_active_next; +reg bubble_cycle_reg = 1'b0, bubble_cycle_next; +reg first_input_cycle_reg = 1'b0, first_input_cycle_next; +reg first_output_cycle_reg = 1'b0, first_output_cycle_next; +reg output_last_cycle_reg = 1'b0, output_last_cycle_next; +reg last_transfer_reg = 1'b0, last_transfer_next; + +reg [TAG_WIDTH-1:0] tag_reg = {TAG_WIDTH{1'b0}}, tag_next; + +reg [STATUS_FIFO_ADDR_WIDTH+1-1:0] status_fifo_wr_ptr_reg = 0, status_fifo_wr_ptr_next; +reg [STATUS_FIFO_ADDR_WIDTH+1-1:0] status_fifo_rd_ptr_reg = 0, status_fifo_rd_ptr_next; +reg [TAG_WIDTH-1:0] status_fifo_tag[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg status_fifo_last[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg [TAG_WIDTH-1:0] status_fifo_wr_tag; +reg status_fifo_wr_last; + +reg s_axis_desc_ready_reg = 1'b0, s_axis_desc_ready_next; + +reg [TAG_WIDTH-1:0] m_axis_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_desc_status_tag_next; +reg m_axis_desc_status_valid_reg = 1'b0, m_axis_desc_status_valid_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axi_araddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_araddr_next; +reg [7:0] m_axi_arlen_reg = 8'd0, m_axi_arlen_next; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; +reg m_axi_rready_reg = 1'b0, m_axi_rready_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axi_awaddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_awaddr_next; +reg [7:0] m_axi_awlen_reg = 8'd0, m_axi_awlen_next; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; +reg m_axi_bready_reg = 1'b0, m_axi_bready_next; + +reg [AXI_DATA_WIDTH-1:0] save_axi_rdata_reg = {AXI_DATA_WIDTH{1'b0}}; + +wire [AXI_DATA_WIDTH-1:0] shift_axi_rdata = {m_axi_rdata, save_axi_rdata_reg} >> ((AXI_STRB_WIDTH-offset_reg)*AXI_WORD_SIZE); + +// internal datapath +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_int; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wlast_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +wire m_axi_wready_int_early; + +assign s_axis_desc_ready = s_axis_desc_ready_reg; + +assign m_axis_desc_status_tag = m_axis_desc_status_tag_reg; +assign m_axis_desc_status_valid = m_axis_desc_status_valid_reg; + +assign m_axi_arid = {AXI_ID_WIDTH{1'b0}}; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = AXI_BURST_SIZE; +assign m_axi_arburst = 2'b01; +assign m_axi_arlock = 1'b0; +assign m_axi_arcache = 4'b0011; +assign m_axi_arprot = 3'b010; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +assign m_axi_awid = {AXI_ID_WIDTH{1'b0}}; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = AXI_BURST_SIZE; +assign m_axi_awburst = 2'b01; +assign m_axi_awlock = 1'b0; +assign m_axi_awcache = 4'b0011; +assign m_axi_awprot = 3'b010; +assign m_axi_awvalid = m_axi_awvalid_reg; +assign m_axi_bready = m_axi_bready_reg; + +wire [AXI_ADDR_WIDTH-1:0] read_addr_plus_max_burst = read_addr_reg + AXI_MAX_BURST_SIZE; +wire [AXI_ADDR_WIDTH-1:0] read_addr_plus_op_count = read_addr_reg + op_word_count_reg; +wire [AXI_ADDR_WIDTH-1:0] read_addr_plus_axi_count = read_addr_reg + axi_word_count_reg; + +wire [AXI_ADDR_WIDTH-1:0] write_addr_plus_max_burst = write_addr_reg + AXI_MAX_BURST_SIZE; +wire [AXI_ADDR_WIDTH-1:0] write_addr_plus_op_count = write_addr_reg + op_word_count_reg; +wire [AXI_ADDR_WIDTH-1:0] write_addr_plus_axi_count = write_addr_reg + axi_word_count_reg; + +always @* begin + read_state_next = READ_STATE_IDLE; + + s_axis_desc_ready_next = 1'b0; + + m_axi_araddr_next = m_axi_araddr_reg; + m_axi_arlen_next = m_axi_arlen_reg; + m_axi_arvalid_next = m_axi_arvalid_reg && !m_axi_arready; + + read_addr_next = read_addr_reg; + write_addr_next = write_addr_reg; + op_word_count_next = op_word_count_reg; + tr_word_count_next = tr_word_count_reg; + axi_word_count_next = axi_word_count_reg; + + axi_cmd_addr_next = axi_cmd_addr_reg; + axi_cmd_offset_next = axi_cmd_offset_reg; + axi_cmd_first_cycle_offset_next = axi_cmd_first_cycle_offset_reg; + axi_cmd_last_cycle_offset_next = axi_cmd_last_cycle_offset_reg; + axi_cmd_input_cycle_count_next = axi_cmd_input_cycle_count_reg; + axi_cmd_output_cycle_count_next = axi_cmd_output_cycle_count_reg; + axi_cmd_bubble_cycle_next = axi_cmd_bubble_cycle_reg; + axi_cmd_last_transfer_next = axi_cmd_last_transfer_reg; + axi_cmd_tag_next = axi_cmd_tag_reg; + axi_cmd_valid_next = axi_cmd_valid_reg && !axi_cmd_ready; + + case (read_state_reg) + READ_STATE_IDLE: begin + // idle state - load new descriptor to start operation + s_axis_desc_ready_next = !axi_cmd_valid_reg && enable; + + if (s_axis_desc_ready && s_axis_desc_valid) begin + if (ENABLE_UNALIGNED) begin + read_addr_next = s_axis_desc_read_addr; + write_addr_next = s_axis_desc_write_addr; + end else begin + read_addr_next = s_axis_desc_read_addr & ADDR_MASK; + write_addr_next = s_axis_desc_write_addr & ADDR_MASK; + end + axi_cmd_tag_next = s_axis_desc_tag; + op_word_count_next = s_axis_desc_len; + + s_axis_desc_ready_next = 1'b0; + read_state_next = READ_STATE_START; + end else begin + read_state_next = READ_STATE_IDLE; + end + end + READ_STATE_START: begin + // start state - compute write length + if (!axi_cmd_valid_reg) begin + if (op_word_count_reg <= AXI_MAX_BURST_SIZE - (write_addr_reg & OFFSET_MASK)) begin + // packet smaller than max burst size + if (write_addr_reg[12] != write_addr_plus_op_count[12]) begin + // crosses 4k boundary + axi_word_count_next = 13'h1000 - write_addr_reg[11:0]; + end else begin + // does not cross 4k boundary + axi_word_count_next = op_word_count_reg; + end + end else begin + // packet larger than max burst size + if (write_addr_reg[12] != write_addr_plus_max_burst[12]) begin + // crosses 4k boundary + axi_word_count_next = 13'h1000 - write_addr_reg[11:0]; + end else begin + // does not cross 4k boundary + axi_word_count_next = AXI_MAX_BURST_SIZE - (write_addr_reg & OFFSET_MASK); + end + end + + write_addr_next = write_addr_reg + axi_word_count_next; + op_word_count_next = op_word_count_reg - axi_word_count_next; + + axi_cmd_addr_next = write_addr_reg; + if (ENABLE_UNALIGNED) begin + axi_cmd_input_cycle_count_next = (axi_word_count_next + (read_addr_reg & OFFSET_MASK) - 1) >> AXI_BURST_SIZE; + axi_cmd_output_cycle_count_next = (axi_word_count_next + (write_addr_reg & OFFSET_MASK) - 1) >> AXI_BURST_SIZE; + axi_cmd_offset_next = (write_addr_reg & OFFSET_MASK) - (read_addr_reg & OFFSET_MASK); + axi_cmd_bubble_cycle_next = (read_addr_reg & OFFSET_MASK) > (write_addr_reg & OFFSET_MASK); + axi_cmd_first_cycle_offset_next = write_addr_reg & OFFSET_MASK; + axi_cmd_last_cycle_offset_next = axi_cmd_first_cycle_offset_next + axi_word_count_next & OFFSET_MASK; + end else begin + axi_cmd_input_cycle_count_next = (axi_word_count_next - 1) >> AXI_BURST_SIZE; + axi_cmd_output_cycle_count_next = (axi_word_count_next - 1) >> AXI_BURST_SIZE; + axi_cmd_offset_next = 0; + axi_cmd_bubble_cycle_next = 0; + axi_cmd_first_cycle_offset_next = 0; + axi_cmd_last_cycle_offset_next = axi_word_count_next & OFFSET_MASK; + end + axi_cmd_last_transfer_next = op_word_count_next == 0; + axi_cmd_valid_next = 1'b1; + + read_state_next = READ_STATE_REQ; + end else begin + read_state_next = READ_STATE_START; + end + end + READ_STATE_REQ: begin + // request state - issue AXI read requests + if (!m_axi_arvalid) begin + if (axi_word_count_reg <= AXI_MAX_BURST_SIZE - (read_addr_reg & OFFSET_MASK)) begin + // packet smaller than max burst size + if (read_addr_reg[12] != read_addr_plus_axi_count[12]) begin + // crosses 4k boundary + tr_word_count_next = 13'h1000 - read_addr_reg[11:0]; + end else begin + // does not cross 4k boundary + tr_word_count_next = axi_word_count_reg; + end + end else begin + // packet larger than max burst size + if (read_addr_reg[12] != read_addr_plus_max_burst[12]) begin + // crosses 4k boundary + tr_word_count_next = 13'h1000 - read_addr_reg[11:0]; + end else begin + // does not cross 4k boundary + tr_word_count_next = AXI_MAX_BURST_SIZE - (read_addr_reg & OFFSET_MASK); + end + end + + m_axi_araddr_next = read_addr_reg; + if (ENABLE_UNALIGNED) begin + m_axi_arlen_next = (tr_word_count_next + (read_addr_reg & OFFSET_MASK) - 1) >> AXI_BURST_SIZE; + end else begin + m_axi_arlen_next = (tr_word_count_next - 1) >> AXI_BURST_SIZE; + end + m_axi_arvalid_next = 1'b1; + + read_addr_next = read_addr_reg + tr_word_count_next; + axi_word_count_next = axi_word_count_reg - tr_word_count_next; + + if (axi_word_count_next > 0) begin + read_state_next = READ_STATE_REQ; + end else if (op_word_count_next > 0) begin + read_state_next = READ_STATE_START; + end else begin + s_axis_desc_ready_next = !axi_cmd_valid_reg && enable; + read_state_next = READ_STATE_IDLE; + end + end else begin + read_state_next = READ_STATE_REQ; + end + end + endcase +end + +always @* begin + axi_state_next = AXI_STATE_IDLE; + + m_axis_desc_status_tag_next = m_axis_desc_status_tag_reg; + m_axis_desc_status_valid_next = 1'b0; + + m_axi_awaddr_next = m_axi_awaddr_reg; + m_axi_awlen_next = m_axi_awlen_reg; + m_axi_awvalid_next = m_axi_awvalid_reg && !m_axi_awready; + m_axi_wdata_int = shift_axi_rdata; + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b0}}; + m_axi_wlast_int = 1'b0; + m_axi_wvalid_int = 1'b0; + m_axi_bready_next = 1'b0; + + m_axi_rready_next = 1'b0; + + transfer_in_save = 1'b0; + axi_cmd_ready = 1'b0; + status_fifo_we = 1'b0; + + offset_next = offset_reg; + first_cycle_offset_next = first_cycle_offset_reg; + last_cycle_offset_next = last_cycle_offset_reg; + input_cycle_count_next = input_cycle_count_reg; + output_cycle_count_next = output_cycle_count_reg; + input_active_next = input_active_reg; + output_active_next = output_active_reg; + bubble_cycle_next = bubble_cycle_reg; + first_input_cycle_next = first_input_cycle_reg; + first_output_cycle_next = first_output_cycle_reg; + output_last_cycle_next = output_last_cycle_reg; + last_transfer_next = last_transfer_reg; + + tag_next = tag_reg; + + status_fifo_rd_ptr_next = status_fifo_rd_ptr_reg; + + status_fifo_wr_tag = tag_reg; + status_fifo_wr_last = 1'b0; + + case (axi_state_reg) + AXI_STATE_IDLE: begin + // idle state - load new descriptor to start operation + m_axi_rready_next = 1'b0; + + // store transfer parameters + if (ENABLE_UNALIGNED) begin + offset_next = axi_cmd_offset_reg; + first_cycle_offset_next = axi_cmd_first_cycle_offset_reg; + end else begin + offset_next = 0; + first_cycle_offset_next = 0; + end + last_cycle_offset_next = axi_cmd_last_cycle_offset_reg; + input_cycle_count_next = axi_cmd_input_cycle_count_reg; + output_cycle_count_next = axi_cmd_output_cycle_count_reg; + bubble_cycle_next = axi_cmd_bubble_cycle_reg; + last_transfer_next = axi_cmd_last_transfer_reg; + tag_next = axi_cmd_tag_reg; + + output_last_cycle_next = output_cycle_count_next == 0; + input_active_next = 1'b1; + output_active_next = 1'b1; + first_input_cycle_next = 1'b1; + first_output_cycle_next = 1'b1; + + if (!m_axi_awvalid && axi_cmd_valid_reg) begin + axi_cmd_ready = 1'b1; + + m_axi_awaddr_next = axi_cmd_addr_reg; + m_axi_awlen_next = axi_cmd_output_cycle_count_reg; + m_axi_awvalid_next = 1'b1; + + m_axi_rready_next = m_axi_wready_int_early; + axi_state_next = AXI_STATE_WRITE; + end + end + AXI_STATE_WRITE: begin + // handle AXI read data + m_axi_rready_next = m_axi_wready_int_early && input_active_reg; + + if (m_axi_wready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + // transfer in AXI read data + transfer_in_save = m_axi_rready && m_axi_rvalid; + + if (ENABLE_UNALIGNED && first_input_cycle_reg && bubble_cycle_reg) begin + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + bubble_cycle_next = 1'b0; + first_input_cycle_next = 1'b0; + + m_axi_rready_next = m_axi_wready_int_early && input_active_next; + axi_state_next = AXI_STATE_WRITE; + end else begin + // update counters + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + if (output_active_reg) begin + output_cycle_count_next = output_cycle_count_reg - 1; + output_active_next = output_cycle_count_reg > 0; + end + output_last_cycle_next = output_cycle_count_next == 0; + bubble_cycle_next = 1'b0; + first_input_cycle_next = 1'b0; + first_output_cycle_next = 1'b0; + + // pass through read data + m_axi_wdata_int = shift_axi_rdata; + if (first_output_cycle_reg) begin + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b1}} << first_cycle_offset_reg; + end else begin + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b1}}; + end + m_axi_wvalid_int = 1'b1; + + if (output_last_cycle_reg) begin + // no more data to transfer, finish operation + if (last_cycle_offset_reg > 0) begin + m_axi_wstrb_int = m_axi_wstrb_int & {AXI_STRB_WIDTH{1'b1}} >> (AXI_STRB_WIDTH - last_cycle_offset_reg); + end + m_axi_wlast_int = 1'b1; + + status_fifo_we = 1'b1; + status_fifo_wr_tag = tag_reg; + status_fifo_wr_last = last_transfer_reg; + + m_axi_rready_next = 1'b0; + axi_state_next = AXI_STATE_IDLE; + end else begin + // more cycles in AXI transfer + axi_state_next = AXI_STATE_WRITE; + end + end + end else begin + axi_state_next = AXI_STATE_WRITE; + end + end + endcase + + if (status_fifo_rd_ptr_reg != status_fifo_wr_ptr_reg) begin + // status FIFO not empty + if (m_axi_bready && m_axi_bvalid) begin + // got write completion, pop and return status + m_axis_desc_status_tag_next = status_fifo_tag[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + m_axis_desc_status_valid_next = status_fifo_last[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + status_fifo_rd_ptr_next = status_fifo_rd_ptr_reg + 1; + m_axi_bready_next = 1'b0; + end else begin + // wait for write completion + m_axi_bready_next = 1'b1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + read_state_reg <= READ_STATE_IDLE; + axi_state_reg <= AXI_STATE_IDLE; + axi_cmd_valid_reg <= 1'b0; + s_axis_desc_ready_reg <= 1'b0; + m_axis_desc_status_valid_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + m_axi_bready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + m_axi_rready_reg <= 1'b0; + + status_fifo_wr_ptr_reg <= 0; + status_fifo_rd_ptr_reg <= 0; + end else begin + read_state_reg <= read_state_next; + axi_state_reg <= axi_state_next; + axi_cmd_valid_reg <= axi_cmd_valid_next; + s_axis_desc_ready_reg <= s_axis_desc_ready_next; + m_axis_desc_status_valid_reg <= m_axis_desc_status_valid_next; + m_axi_awvalid_reg <= m_axi_awvalid_next; + m_axi_bready_reg <= m_axi_bready_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + + if (status_fifo_we) begin + status_fifo_wr_ptr_reg <= status_fifo_wr_ptr_reg + 1; + end + status_fifo_rd_ptr_reg <= status_fifo_rd_ptr_next; + end + + m_axis_desc_status_tag_reg <= m_axis_desc_status_tag_next; + + m_axi_awaddr_reg <= m_axi_awaddr_next; + m_axi_awlen_reg <= m_axi_awlen_next; + m_axi_araddr_reg <= m_axi_araddr_next; + m_axi_arlen_reg <= m_axi_arlen_next; + + read_addr_reg <= read_addr_next; + write_addr_reg <= write_addr_next; + op_word_count_reg <= op_word_count_next; + tr_word_count_reg <= tr_word_count_next; + axi_word_count_reg <= axi_word_count_next; + + axi_cmd_addr_reg <= axi_cmd_addr_next; + axi_cmd_offset_reg <= axi_cmd_offset_next; + axi_cmd_first_cycle_offset_reg <= axi_cmd_first_cycle_offset_next; + axi_cmd_last_cycle_offset_reg <= axi_cmd_last_cycle_offset_next; + axi_cmd_input_cycle_count_reg <= axi_cmd_input_cycle_count_next; + axi_cmd_output_cycle_count_reg <= axi_cmd_output_cycle_count_next; + axi_cmd_bubble_cycle_reg <= axi_cmd_bubble_cycle_next; + axi_cmd_last_transfer_reg <= axi_cmd_last_transfer_next; + axi_cmd_tag_reg <= axi_cmd_tag_next; + axi_cmd_valid_reg <= axi_cmd_valid_next; + + offset_reg <= offset_next; + first_cycle_offset_reg <= first_cycle_offset_next; + last_cycle_offset_reg <= last_cycle_offset_next; + input_cycle_count_reg <= input_cycle_count_next; + output_cycle_count_reg <= output_cycle_count_next; + input_active_reg <= input_active_next; + output_active_reg <= output_active_next; + bubble_cycle_reg <= bubble_cycle_next; + first_input_cycle_reg <= first_input_cycle_next; + first_output_cycle_reg <= first_output_cycle_next; + output_last_cycle_reg <= output_last_cycle_next; + last_transfer_reg <= last_transfer_next; + + tag_reg <= tag_next; + + if (transfer_in_save) begin + save_axi_rdata_reg <= m_axi_rdata; + end + + if (status_fifo_we) begin + status_fifo_tag[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_tag; + status_fifo_last[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_last; + status_fifo_wr_ptr_reg <= status_fifo_wr_ptr_reg + 1; + end +end + +// output datapath logic +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [AXI_DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wvalid = m_axi_wvalid_reg; +assign m_axi_wlast = m_axi_wlast_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = m_axi_wready || (!temp_m_axi_wvalid_reg && (!m_axi_wvalid_reg || !m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (m_axi_wready || !m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_cdma_desc_mux.v b/corundum/lib/axi/rtl/axi_cdma_desc_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..7f5406114b375453504214e3faaf8fa88c385ab4 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_cdma_desc_mux.v @@ -0,0 +1,255 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI CDMA descriptor mux + */ +module axi_cdma_desc_mux # +( + // Number of ports + parameter PORTS = 2, + // AXI address width + parameter AXI_ADDR_WIDTH = 16, + // Length field width + parameter LEN_WIDTH = 20, + // Input tag field width + parameter S_TAG_WIDTH = 8, + // Output tag field width (towards CDMA module) + // Additional bits required for response routing + parameter M_TAG_WIDTH = S_TAG_WIDTH+$clog2(PORTS), + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Descriptor output (to AXI CDMA core) + */ + output wire [AXI_ADDR_WIDTH-1:0] m_axis_desc_read_addr, + output wire [AXI_ADDR_WIDTH-1:0] m_axis_desc_write_addr, + output wire [LEN_WIDTH-1:0] m_axis_desc_len, + output wire [M_TAG_WIDTH-1:0] m_axis_desc_tag, + output wire m_axis_desc_valid, + input wire m_axis_desc_ready, + + /* + * Descriptor status input (from AXI CDMA core) + */ + input wire [M_TAG_WIDTH-1:0] s_axis_desc_status_tag, + input wire s_axis_desc_status_valid, + + /* + * Descriptor input + */ + input wire [PORTS*AXI_ADDR_WIDTH-1:0] s_axis_desc_read_addr, + input wire [PORTS*AXI_ADDR_WIDTH-1:0] s_axis_desc_write_addr, + input wire [PORTS*LEN_WIDTH-1:0] s_axis_desc_len, + input wire [PORTS*S_TAG_WIDTH-1:0] s_axis_desc_tag, + input wire [PORTS-1:0] s_axis_desc_valid, + output wire [PORTS-1:0] s_axis_desc_ready, + + /* + * Descriptor status output + */ + output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_desc_status_tag, + output wire [PORTS-1:0] m_axis_desc_status_valid +); + +parameter CL_PORTS = $clog2(PORTS); + +// check configuration +initial begin + if (M_TAG_WIDTH < S_TAG_WIDTH+$clog2(PORTS)) begin + $error("Error: M_TAG_WIDTH must be at least $clog2(PORTS) larger than S_TAG_WIDTH (instance %m)"); + $finish; + end +end + +// descriptor mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_read_addr_int; +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_write_addr_int; +reg [LEN_WIDTH-1:0] m_axis_desc_len_int; +reg [M_TAG_WIDTH-1:0] m_axis_desc_tag_int; +reg m_axis_desc_valid_int; +reg m_axis_desc_ready_int_reg = 1'b0; +wire m_axis_desc_ready_int_early; + +assign s_axis_desc_ready = (m_axis_desc_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [AXI_ADDR_WIDTH-1:0] current_s_desc_read_addr = s_axis_desc_read_addr[grant_encoded*AXI_ADDR_WIDTH +: AXI_ADDR_WIDTH]; +wire [AXI_ADDR_WIDTH-1:0] current_s_desc_write_addr = s_axis_desc_write_addr[grant_encoded*AXI_ADDR_WIDTH +: AXI_ADDR_WIDTH]; +wire [LEN_WIDTH-1:0] current_s_desc_len = s_axis_desc_len[grant_encoded*LEN_WIDTH +: LEN_WIDTH]; +wire [S_TAG_WIDTH-1:0] current_s_desc_tag = s_axis_desc_tag[grant_encoded*S_TAG_WIDTH +: S_TAG_WIDTH]; +wire current_s_desc_valid = s_axis_desc_valid[grant_encoded]; +wire current_s_desc_ready = s_axis_desc_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_desc_valid & ~grant; +assign acknowledge = grant & s_axis_desc_valid & s_axis_desc_ready; + +always @* begin + m_axis_desc_read_addr_int = current_s_desc_read_addr; + m_axis_desc_write_addr_int = current_s_desc_write_addr; + m_axis_desc_len_int = current_s_desc_len; + m_axis_desc_tag_int = {grant_encoded, current_s_desc_tag}; + m_axis_desc_valid_int = current_s_desc_valid && m_axis_desc_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_read_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_write_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] m_axis_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] m_axis_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg m_axis_desc_valid_reg = 1'b0, m_axis_desc_valid_next; + +reg [AXI_ADDR_WIDTH-1:0] temp_m_axis_desc_read_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [AXI_ADDR_WIDTH-1:0] temp_m_axis_desc_write_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] temp_m_axis_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] temp_m_axis_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg temp_m_axis_desc_valid_reg = 1'b0, temp_m_axis_desc_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_desc_read_addr = m_axis_desc_read_addr_reg; +assign m_axis_desc_write_addr = m_axis_desc_write_addr_reg; +assign m_axis_desc_len = m_axis_desc_len_reg; +assign m_axis_desc_tag = m_axis_desc_tag_reg; +assign m_axis_desc_valid = m_axis_desc_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_desc_ready_int_early = m_axis_desc_ready || (!temp_m_axis_desc_valid_reg && (!m_axis_desc_valid_reg || !m_axis_desc_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_desc_valid_next = m_axis_desc_valid_reg; + temp_m_axis_desc_valid_next = temp_m_axis_desc_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_desc_ready_int_reg) begin + // input is ready + if (m_axis_desc_ready || !m_axis_desc_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_desc_valid_next = m_axis_desc_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_desc_valid_next = m_axis_desc_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_desc_ready) begin + // input is not ready, but output is ready + m_axis_desc_valid_next = temp_m_axis_desc_valid_reg; + temp_m_axis_desc_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_desc_valid_reg <= 1'b0; + m_axis_desc_ready_int_reg <= 1'b0; + temp_m_axis_desc_valid_reg <= 1'b0; + end else begin + m_axis_desc_valid_reg <= m_axis_desc_valid_next; + m_axis_desc_ready_int_reg <= m_axis_desc_ready_int_early; + temp_m_axis_desc_valid_reg <= temp_m_axis_desc_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_desc_read_addr_reg <= m_axis_desc_read_addr_int; + m_axis_desc_write_addr_reg <= m_axis_desc_write_addr_int; + m_axis_desc_len_reg <= m_axis_desc_len_int; + m_axis_desc_tag_reg <= m_axis_desc_tag_int; + end else if (store_axis_temp_to_output) begin + m_axis_desc_read_addr_reg <= temp_m_axis_desc_read_addr_reg; + m_axis_desc_write_addr_reg <= temp_m_axis_desc_write_addr_reg; + m_axis_desc_len_reg <= temp_m_axis_desc_len_reg; + m_axis_desc_tag_reg <= temp_m_axis_desc_tag_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_desc_read_addr_reg <= m_axis_desc_read_addr_int; + temp_m_axis_desc_write_addr_reg <= m_axis_desc_write_addr_int; + temp_m_axis_desc_len_reg <= m_axis_desc_len_int; + temp_m_axis_desc_tag_reg <= m_axis_desc_tag_int; + end +end + +// descriptor status demux +reg [S_TAG_WIDTH-1:0] m_axis_desc_status_tag_reg = {S_TAG_WIDTH{1'b0}}; +reg [PORTS-1:0] m_axis_desc_status_valid_reg = {PORTS{1'b0}}; + +assign m_axis_desc_status_tag = {PORTS{m_axis_desc_status_tag_reg}}; +assign m_axis_desc_status_valid = m_axis_desc_status_valid_reg; + +always @(posedge clk) begin + if (rst) begin + m_axis_desc_status_valid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_desc_status_valid_reg <= s_axis_desc_status_valid << (PORTS > 1 ? s_axis_desc_status_tag[S_TAG_WIDTH+CL_PORTS-1:S_TAG_WIDTH] : 0); + end + + m_axis_desc_status_tag_reg <= s_axis_desc_status_tag; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_crossbar.v b/corundum/lib/axi/rtl/axi_crossbar.v new file mode 100644 index 0000000000000000000000000000000000000000..04af4163659ad09a0db83aeca9f72d15eee0a744 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_crossbar.v @@ -0,0 +1,387 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 crossbar + */ +module axi_crossbar # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Input ID field width (from AXI masters) + parameter S_ID_WIDTH = 8, + // Output ID field width (towards AXI slaves) + // Additional bits required for response routing + parameter M_ID_WIDTH = S_ID_WIDTH+$clog2(S_COUNT), + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Number of concurrent unique IDs for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_THREADS = {S_COUNT{32'd2}}, + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd4}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AW_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_W_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_B_REG_TYPE = {S_COUNT{2'd1}}, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AR_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_R_REG_TYPE = {S_COUNT{2'd2}}, + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AW_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_W_REG_TYPE = {M_COUNT{2'd2}}, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_B_REG_TYPE = {M_COUNT{2'd0}}, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AR_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_R_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_awid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [S_COUNT*8-1:0] s_axi_awlen, + input wire [S_COUNT*3-1:0] s_axi_awsize, + input wire [S_COUNT*2-1:0] s_axi_awburst, + input wire [S_COUNT-1:0] s_axi_awlock, + input wire [S_COUNT*4-1:0] s_axi_awcache, + input wire [S_COUNT*3-1:0] s_axi_awprot, + input wire [S_COUNT*4-1:0] s_axi_awqos, + input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser, + input wire [S_COUNT-1:0] s_axi_awvalid, + output wire [S_COUNT-1:0] s_axi_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb, + input wire [S_COUNT-1:0] s_axi_wlast, + input wire [S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser, + input wire [S_COUNT-1:0] s_axi_wvalid, + output wire [S_COUNT-1:0] s_axi_wready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_bid, + output wire [S_COUNT*2-1:0] s_axi_bresp, + output wire [S_COUNT*BUSER_WIDTH-1:0] s_axi_buser, + output wire [S_COUNT-1:0] s_axi_bvalid, + input wire [S_COUNT-1:0] s_axi_bready, + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_arid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr, + input wire [S_COUNT*8-1:0] s_axi_arlen, + input wire [S_COUNT*3-1:0] s_axi_arsize, + input wire [S_COUNT*2-1:0] s_axi_arburst, + input wire [S_COUNT-1:0] s_axi_arlock, + input wire [S_COUNT*4-1:0] s_axi_arcache, + input wire [S_COUNT*3-1:0] s_axi_arprot, + input wire [S_COUNT*4-1:0] s_axi_arqos, + input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser, + input wire [S_COUNT-1:0] s_axi_arvalid, + output wire [S_COUNT-1:0] s_axi_arready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_rid, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axi_rdata, + output wire [S_COUNT*2-1:0] s_axi_rresp, + output wire [S_COUNT-1:0] s_axi_rlast, + output wire [S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser, + output wire [S_COUNT-1:0] s_axi_rvalid, + input wire [S_COUNT-1:0] s_axi_rready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_awid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [M_COUNT*8-1:0] m_axi_awlen, + output wire [M_COUNT*3-1:0] m_axi_awsize, + output wire [M_COUNT*2-1:0] m_axi_awburst, + output wire [M_COUNT-1:0] m_axi_awlock, + output wire [M_COUNT*4-1:0] m_axi_awcache, + output wire [M_COUNT*3-1:0] m_axi_awprot, + output wire [M_COUNT*4-1:0] m_axi_awqos, + output wire [M_COUNT*4-1:0] m_axi_awregion, + output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser, + output wire [M_COUNT-1:0] m_axi_awvalid, + input wire [M_COUNT-1:0] m_axi_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb, + output wire [M_COUNT-1:0] m_axi_wlast, + output wire [M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser, + output wire [M_COUNT-1:0] m_axi_wvalid, + input wire [M_COUNT-1:0] m_axi_wready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_bid, + input wire [M_COUNT*2-1:0] m_axi_bresp, + input wire [M_COUNT*BUSER_WIDTH-1:0] m_axi_buser, + input wire [M_COUNT-1:0] m_axi_bvalid, + output wire [M_COUNT-1:0] m_axi_bready, + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_arid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr, + output wire [M_COUNT*8-1:0] m_axi_arlen, + output wire [M_COUNT*3-1:0] m_axi_arsize, + output wire [M_COUNT*2-1:0] m_axi_arburst, + output wire [M_COUNT-1:0] m_axi_arlock, + output wire [M_COUNT*4-1:0] m_axi_arcache, + output wire [M_COUNT*3-1:0] m_axi_arprot, + output wire [M_COUNT*4-1:0] m_axi_arqos, + output wire [M_COUNT*4-1:0] m_axi_arregion, + output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser, + output wire [M_COUNT-1:0] m_axi_arvalid, + input wire [M_COUNT-1:0] m_axi_arready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_rid, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axi_rdata, + input wire [M_COUNT*2-1:0] m_axi_rresp, + input wire [M_COUNT-1:0] m_axi_rlast, + input wire [M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser, + input wire [M_COUNT-1:0] m_axi_rvalid, + output wire [M_COUNT-1:0] m_axi_rready +); + +axi_crossbar_wr #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ID_WIDTH(S_ID_WIDTH), + .M_ID_WIDTH(M_ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .S_THREADS(S_THREADS), + .S_ACCEPT(S_ACCEPT), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT_WRITE), + .M_ISSUE(M_ISSUE), + .M_SECURE(M_SECURE), + .S_AW_REG_TYPE(S_AW_REG_TYPE), + .S_W_REG_TYPE (S_W_REG_TYPE), + .S_B_REG_TYPE (S_B_REG_TYPE) +) +axi_crossbar_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interfaces + */ + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awqos(s_axi_awqos), + .s_axi_awuser(s_axi_awuser), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wuser(s_axi_wuser), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_buser(s_axi_buser), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + + /* + * AXI master interfaces + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awqos(m_axi_awqos), + .m_axi_awregion(m_axi_awregion), + .m_axi_awuser(m_axi_awuser), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wuser(m_axi_wuser), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_buser(m_axi_buser), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready) +); + +axi_crossbar_rd #( + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .S_ID_WIDTH(S_ID_WIDTH), + .M_ID_WIDTH(M_ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .S_THREADS(S_THREADS), + .S_ACCEPT(S_ACCEPT), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT_READ), + .M_ISSUE(M_ISSUE), + .M_SECURE(M_SECURE), + .S_AR_REG_TYPE(S_AR_REG_TYPE), + .S_R_REG_TYPE (S_R_REG_TYPE) +) +axi_crossbar_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interfaces + */ + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arqos(s_axi_arqos), + .s_axi_aruser(s_axi_aruser), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_ruser(s_axi_ruser), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + + /* + * AXI master interfaces + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arqos(m_axi_arqos), + .m_axi_arregion(m_axi_arregion), + .m_axi_aruser(m_axi_aruser), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_ruser(m_axi_ruser), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axi_crossbar_addr.v b/corundum/lib/axi/rtl/axi_crossbar_addr.v new file mode 100644 index 0000000000000000000000000000000000000000..361fb289feef780859a4e7beffc3f18f74f6273e --- /dev/null +++ b/corundum/lib/axi/rtl/axi_crossbar_addr.v @@ -0,0 +1,373 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 crossbar address decode and admission control + */ +module axi_crossbar_addr # +( + // Slave interface index + parameter S = 0, + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // ID field width + parameter ID_WIDTH = 8, + // Number of concurrent unique IDs + parameter S_THREADS = 32'd2, + // Number of concurrent operations + parameter S_ACCEPT = 32'd16, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Enable write command output + parameter WC_OUTPUT = 0 +) +( + input wire clk, + input wire rst, + + /* + * Address input + */ + input wire [ID_WIDTH-1:0] s_axi_aid, + input wire [ADDR_WIDTH-1:0] s_axi_aaddr, + input wire [2:0] s_axi_aprot, + input wire [3:0] s_axi_aqos, + input wire s_axi_avalid, + output wire s_axi_aready, + + /* + * Address output + */ + output wire [3:0] m_axi_aregion, + output wire [$clog2(M_COUNT)-1:0] m_select, + output wire m_axi_avalid, + input wire m_axi_aready, + + /* + * Write command output + */ + output wire [$clog2(M_COUNT)-1:0] m_wc_select, + output wire m_wc_decerr, + output wire m_wc_valid, + input wire m_wc_ready, + + /* + * Reply command output + */ + output wire m_rc_decerr, + output wire m_rc_valid, + input wire m_rc_ready, + + /* + * Completion input + */ + input wire [ID_WIDTH-1:0] s_cpl_id, + input wire s_cpl_valid +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +parameter S_INT_THREADS = S_THREADS > S_ACCEPT ? S_ACCEPT : S_THREADS; +parameter CL_S_INT_THREADS = $clog2(S_INT_THREADS); +parameter CL_S_ACCEPT = $clog2(S_ACCEPT); + +// default address computation +function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy); + integer i; + reg [ADDR_WIDTH-1:0] base; + begin + calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}}; + base = 0; + for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment + base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align + calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base; + end + end + end +endfunction + +parameter M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); + +integer i, j; + +// check configuration +initial begin + if (S_ACCEPT < 1) begin + $error("Error: need at least 1 accept (instance %m)"); + $finish; + end + + if (S_THREADS < 1) begin + $error("Error: need at least 1 thread (instance %m)"); + $finish; + end + + if (S_THREADS > S_ACCEPT) begin + $warning("Warning: requested thread count larger than accept count; limiting thread count to accept count (instance %m)"); + end + + if (M_REGIONS < 1) begin + $error("Error: need at least 1 region (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: address width out of range (instance %m)"); + $finish; + end + end + + $display("Addressing configuration for axi_crossbar_addr instance %m"); + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + $display("%2d (%2d): %x / %02d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin + if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin + $display("Overlapping regions:"); + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + $display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))); + $error("Error: address ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_DECODE = 3'd1; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg s_axi_aready_reg = 0, s_axi_aready_next; + +reg [3:0] m_axi_aregion_reg = 4'd0, m_axi_aregion_next; +reg [CL_M_COUNT-1:0] m_select_reg = 0, m_select_next; +reg m_axi_avalid_reg = 1'b0, m_axi_avalid_next; +reg m_decerr_reg = 1'b0, m_decerr_next; +reg m_wc_valid_reg = 1'b0, m_wc_valid_next; +reg m_rc_valid_reg = 1'b0, m_rc_valid_next; + +assign s_axi_aready = s_axi_aready_reg; + +assign m_axi_aregion = m_axi_aregion_reg; +assign m_select = m_select_reg; +assign m_axi_avalid = m_axi_avalid_reg; + +assign m_wc_select = m_select_reg; +assign m_wc_decerr = m_decerr_reg; +assign m_wc_valid = m_wc_valid_reg; + +assign m_rc_decerr = m_decerr_reg; +assign m_rc_valid = m_rc_valid_reg; + +reg match; +reg trans_start; +reg trans_complete; + +reg [$clog2(S_ACCEPT+1)-1:0] trans_count_reg = 0; +wire trans_limit = trans_count_reg >= S_ACCEPT && !trans_complete; + +// transfer ID thread tracking +reg [ID_WIDTH-1:0] thread_id_reg[S_INT_THREADS-1:0]; +reg [CL_M_COUNT-1:0] thread_m_reg[S_INT_THREADS-1:0]; +reg [3:0] thread_region_reg[S_INT_THREADS-1:0]; +reg [$clog2(S_ACCEPT+1)-1:0] thread_count_reg[S_INT_THREADS-1:0]; + +wire [S_INT_THREADS-1:0] thread_active; +wire [S_INT_THREADS-1:0] thread_match; +wire [S_INT_THREADS-1:0] thread_match_dest; +wire [S_INT_THREADS-1:0] thread_cpl_match; +wire [S_INT_THREADS-1:0] thread_trans_start; +wire [S_INT_THREADS-1:0] thread_trans_complete; + +generate + genvar n; + + for (n = 0; n < S_INT_THREADS; n = n + 1) begin + initial begin + thread_count_reg[n] <= 0; + end + + assign thread_active[n] = thread_count_reg[n] != 0; + assign thread_match[n] = thread_active[n] && thread_id_reg[n] == s_axi_aid; + assign thread_match_dest[n] = thread_match[n] && thread_m_reg[n] == m_select_next && (M_REGIONS < 2 || thread_region_reg[n] == m_axi_aregion_next); + assign thread_cpl_match[n] = thread_active[n] && thread_id_reg[n] == s_cpl_id; + assign thread_trans_start[n] = (thread_match[n] || (!thread_active[n] && !thread_match && !(thread_trans_start & ({S_INT_THREADS{1'b1}} >> (S_INT_THREADS-n))))) && trans_start; + assign thread_trans_complete[n] = thread_cpl_match[n] && trans_complete; + + always @(posedge clk) begin + if (rst) begin + thread_count_reg[n] <= 0; + end else begin + if (thread_trans_start[n] && !thread_trans_complete[n]) begin + thread_count_reg[n] <= thread_count_reg[n] + 1; + end else if (!thread_trans_start[n] && thread_trans_complete[n]) begin + thread_count_reg[n] <= thread_count_reg[n] - 1; + end + end + + if (thread_trans_start[n]) begin + thread_id_reg[n] <= s_axi_aid; + thread_m_reg[n] <= m_select_next; + thread_region_reg[n] <= m_axi_aregion_next; + end + end + end +endgenerate + +always @* begin + state_next = STATE_IDLE; + + match = 1'b0; + trans_start = 1'b0; + trans_complete = 1'b0; + + s_axi_aready_next = 1'b0; + + m_axi_aregion_next = m_axi_aregion_reg; + m_select_next = m_select_reg; + m_select_next = m_select_reg; + m_axi_avalid_next = m_axi_avalid_reg && !m_axi_aready; + m_decerr_next = m_decerr_reg; + m_wc_valid_next = m_wc_valid_reg && !m_wc_ready; + m_rc_valid_next = m_rc_valid_reg && !m_rc_ready; + + case (state_reg) + STATE_IDLE: begin + // idle state, store values + s_axi_aready_next = 1'b0; + + if (s_axi_avalid && !s_axi_aready) begin + match = 1'b0; + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = 0; j < M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !s_axi_aprot[1]) && (M_CONNECT & (1 << (S+i*S_COUNT))) && (s_axi_aaddr >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin + m_select_next = i; + m_axi_aregion_next = j; + match = 1'b1; + end + end + end + + if (match) begin + // address decode successful + if (!trans_limit && (thread_match_dest || (!(&thread_active) && !thread_match))) begin + // transaction limit not reached + m_axi_avalid_next = 1'b1; + m_decerr_next = 1'b0; + m_wc_valid_next = WC_OUTPUT; + m_rc_valid_next = 1'b0; + trans_start = 1'b1; + state_next = STATE_DECODE; + end else begin + // transaction limit reached; block in idle + state_next = STATE_IDLE; + end + end else begin + // decode error + m_axi_avalid_next = 1'b0; + m_decerr_next = 1'b1; + m_wc_valid_next = WC_OUTPUT; + m_rc_valid_next = 1'b1; + state_next = STATE_DECODE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_DECODE: begin + if (!m_axi_avalid_next && (!m_wc_valid_next || !WC_OUTPUT) && !m_rc_valid_next) begin + s_axi_aready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DECODE; + end + end + endcase + + // manage completions + trans_complete = s_cpl_valid; +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axi_aready_reg <= 1'b0; + m_axi_avalid_reg <= 1'b0; + m_wc_valid_reg <= 1'b0; + m_rc_valid_reg <= 1'b0; + + trans_count_reg <= 0; + end else begin + state_reg <= state_next; + s_axi_aready_reg <= s_axi_aready_next; + m_axi_avalid_reg <= m_axi_avalid_next; + m_wc_valid_reg <= m_wc_valid_next; + m_rc_valid_reg <= m_rc_valid_next; + + if (trans_start && !trans_complete) begin + trans_count_reg <= trans_count_reg + 1; + end else if (!trans_start && trans_complete) begin + trans_count_reg <= trans_count_reg - 1; + end + end + + m_axi_aregion_reg <= m_axi_aregion_next; + m_select_reg <= m_select_next; + m_decerr_reg <= m_decerr_next; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_crossbar_rd.v b/corundum/lib/axi/rtl/axi_crossbar_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..846caaebf5253af1340f0d89ab700b5965957b14 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_crossbar_rd.v @@ -0,0 +1,563 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 crossbar (read) + */ +module axi_crossbar_rd # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Input ID field width (from AXI masters) + parameter S_ID_WIDTH = 8, + // Output ID field width (towards AXI slaves) + // Additional bits required for response routing + parameter M_ID_WIDTH = S_ID_WIDTH+$clog2(S_COUNT), + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Number of concurrent unique IDs for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_THREADS = {S_COUNT{32'd2}}, + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd4}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AR channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AR_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface R channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_R_REG_TYPE = {S_COUNT{2'd2}}, + // Master interface AR channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AR_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface R channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_R_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_arid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr, + input wire [S_COUNT*8-1:0] s_axi_arlen, + input wire [S_COUNT*3-1:0] s_axi_arsize, + input wire [S_COUNT*2-1:0] s_axi_arburst, + input wire [S_COUNT-1:0] s_axi_arlock, + input wire [S_COUNT*4-1:0] s_axi_arcache, + input wire [S_COUNT*3-1:0] s_axi_arprot, + input wire [S_COUNT*4-1:0] s_axi_arqos, + input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser, + input wire [S_COUNT-1:0] s_axi_arvalid, + output wire [S_COUNT-1:0] s_axi_arready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_rid, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axi_rdata, + output wire [S_COUNT*2-1:0] s_axi_rresp, + output wire [S_COUNT-1:0] s_axi_rlast, + output wire [S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser, + output wire [S_COUNT-1:0] s_axi_rvalid, + input wire [S_COUNT-1:0] s_axi_rready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_arid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr, + output wire [M_COUNT*8-1:0] m_axi_arlen, + output wire [M_COUNT*3-1:0] m_axi_arsize, + output wire [M_COUNT*2-1:0] m_axi_arburst, + output wire [M_COUNT-1:0] m_axi_arlock, + output wire [M_COUNT*4-1:0] m_axi_arcache, + output wire [M_COUNT*3-1:0] m_axi_arprot, + output wire [M_COUNT*4-1:0] m_axi_arqos, + output wire [M_COUNT*4-1:0] m_axi_arregion, + output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser, + output wire [M_COUNT-1:0] m_axi_arvalid, + input wire [M_COUNT-1:0] m_axi_arready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_rid, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axi_rdata, + input wire [M_COUNT*2-1:0] m_axi_rresp, + input wire [M_COUNT-1:0] m_axi_rlast, + input wire [M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser, + input wire [M_COUNT-1:0] m_axi_rvalid, + output wire [M_COUNT-1:0] m_axi_rready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); +parameter M_COUNT_P1 = M_COUNT+1; +parameter CL_M_COUNT_P1 = $clog2(M_COUNT_P1); + +integer i; + +// check configuration +initial begin + if (M_ID_WIDTH < S_ID_WIDTH+$clog2(S_COUNT)) begin + $error("Error: M_ID_WIDTH must be at least $clog2(S_COUNT) larger than S_ID_WIDTH (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: value out of range (instance %m)"); + $finish; + end + end +end + +wire [S_COUNT*S_ID_WIDTH-1:0] int_s_axi_arid; +wire [S_COUNT*ADDR_WIDTH-1:0] int_s_axi_araddr; +wire [S_COUNT*8-1:0] int_s_axi_arlen; +wire [S_COUNT*3-1:0] int_s_axi_arsize; +wire [S_COUNT*2-1:0] int_s_axi_arburst; +wire [S_COUNT-1:0] int_s_axi_arlock; +wire [S_COUNT*4-1:0] int_s_axi_arcache; +wire [S_COUNT*3-1:0] int_s_axi_arprot; +wire [S_COUNT*4-1:0] int_s_axi_arqos; +wire [S_COUNT*4-1:0] int_s_axi_arregion; +wire [S_COUNT*ARUSER_WIDTH-1:0] int_s_axi_aruser; +wire [S_COUNT-1:0] int_s_axi_arvalid; +wire [S_COUNT-1:0] int_s_axi_arready; + +wire [S_COUNT*M_COUNT-1:0] int_axi_arvalid; +wire [M_COUNT*S_COUNT-1:0] int_axi_arready; + +wire [M_COUNT*M_ID_WIDTH-1:0] int_m_axi_rid; +wire [M_COUNT*DATA_WIDTH-1:0] int_m_axi_rdata; +wire [M_COUNT*2-1:0] int_m_axi_rresp; +wire [M_COUNT-1:0] int_m_axi_rlast; +wire [M_COUNT*RUSER_WIDTH-1:0] int_m_axi_ruser; +wire [M_COUNT-1:0] int_m_axi_rvalid; +wire [M_COUNT-1:0] int_m_axi_rready; + +wire [M_COUNT*S_COUNT-1:0] int_axi_rvalid; +wire [S_COUNT*M_COUNT-1:0] int_axi_rready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + // address decode and admission control + wire [CL_M_COUNT-1:0] a_select; + + wire m_axi_avalid; + wire m_axi_aready; + + wire m_rc_decerr; + wire m_rc_valid; + wire m_rc_ready; + + wire [S_ID_WIDTH-1:0] s_cpl_id; + wire s_cpl_valid; + + axi_crossbar_addr #( + .S(m), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .ADDR_WIDTH(ADDR_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .S_THREADS(S_THREADS[m*32 +: 32]), + .S_ACCEPT(S_ACCEPT[m*32 +: 32]), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT), + .M_SECURE(M_SECURE), + .WC_OUTPUT(0) + ) + addr_inst ( + .clk(clk), + .rst(rst), + + /* + * Address input + */ + .s_axi_aid(int_s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_aaddr(int_s_axi_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_aprot(int_s_axi_arprot[m*3 +: 3]), + .s_axi_aqos(int_s_axi_arqos[m*4 +: 4]), + .s_axi_avalid(int_s_axi_arvalid[m]), + .s_axi_aready(int_s_axi_arready[m]), + + /* + * Address output + */ + .m_axi_aregion(int_s_axi_arregion[m*4 +: 4]), + .m_select(a_select), + .m_axi_avalid(m_axi_avalid), + .m_axi_aready(m_axi_aready), + + /* + * Write command output + */ + .m_wc_select(), + .m_wc_decerr(), + .m_wc_valid(), + .m_wc_ready(1'b1), + + /* + * Response command output + */ + .m_rc_decerr(m_rc_decerr), + .m_rc_valid(m_rc_valid), + .m_rc_ready(m_rc_ready), + + /* + * Completion input + */ + .s_cpl_id(s_cpl_id), + .s_cpl_valid(s_cpl_valid) + ); + + assign int_axi_arvalid[m*M_COUNT +: M_COUNT] = m_axi_avalid << a_select; + assign m_axi_aready = int_axi_arready[a_select*S_COUNT+m]; + + // decode error handling + reg [S_ID_WIDTH-1:0] decerr_m_axi_rid_reg = {S_ID_WIDTH{1'b0}}, decerr_m_axi_rid_next; + reg decerr_m_axi_rlast_reg = 1'b0, decerr_m_axi_rlast_next; + reg decerr_m_axi_rvalid_reg = 1'b0, decerr_m_axi_rvalid_next; + wire decerr_m_axi_rready; + + reg [7:0] decerr_len_reg = 8'd0, decerr_len_next; + + assign m_rc_ready = !decerr_m_axi_rvalid_reg; + + always @* begin + decerr_len_next = decerr_len_reg; + decerr_m_axi_rid_next = decerr_m_axi_rid_reg; + decerr_m_axi_rlast_next = decerr_m_axi_rlast_reg; + decerr_m_axi_rvalid_next = decerr_m_axi_rvalid_reg; + + if (decerr_m_axi_rvalid_reg) begin + if (decerr_m_axi_rready) begin + if (decerr_len_reg > 0) begin + decerr_len_next = decerr_len_reg-1; + decerr_m_axi_rlast_next = (decerr_len_next == 0); + decerr_m_axi_rvalid_next = 1'b1; + end else begin + decerr_m_axi_rvalid_next = 1'b0; + end + end + end else if (m_rc_valid && m_rc_ready) begin + decerr_len_next = int_s_axi_arlen[m*8 +: 8]; + decerr_m_axi_rid_next = int_s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]; + decerr_m_axi_rlast_next = (decerr_len_next == 0); + decerr_m_axi_rvalid_next = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + decerr_m_axi_rvalid_reg <= 1'b0; + end else begin + decerr_m_axi_rvalid_reg <= decerr_m_axi_rvalid_next; + end + + decerr_m_axi_rid_reg <= decerr_m_axi_rid_next; + decerr_m_axi_rlast_reg <= decerr_m_axi_rlast_next; + decerr_len_reg <= decerr_len_next; + end + + // read response arbitration + wire [M_COUNT_P1-1:0] r_request; + wire [M_COUNT_P1-1:0] r_acknowledge; + wire [M_COUNT_P1-1:0] r_grant; + wire r_grant_valid; + wire [CL_M_COUNT_P1-1:0] r_grant_encoded; + + arbiter #( + .PORTS(M_COUNT_P1), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") + ) + r_arb_inst ( + .clk(clk), + .rst(rst), + .request(r_request), + .acknowledge(r_acknowledge), + .grant(r_grant), + .grant_valid(r_grant_valid), + .grant_encoded(r_grant_encoded) + ); + + // read response mux + wire [S_ID_WIDTH-1:0] m_axi_rid_mux = {decerr_m_axi_rid_reg, int_m_axi_rid} >> r_grant_encoded*M_ID_WIDTH; + wire [DATA_WIDTH-1:0] m_axi_rdata_mux = {{DATA_WIDTH{1'b0}}, int_m_axi_rdata} >> r_grant_encoded*DATA_WIDTH; + wire [1:0] m_axi_rresp_mux = {2'b11, int_m_axi_rresp} >> r_grant_encoded*2; + wire m_axi_rlast_mux = {decerr_m_axi_rlast_reg, int_m_axi_rlast} >> r_grant_encoded; + wire [RUSER_WIDTH-1:0] m_axi_ruser_mux = {{RUSER_WIDTH{1'b0}}, int_m_axi_ruser} >> r_grant_encoded*RUSER_WIDTH; + wire m_axi_rvalid_mux = ({decerr_m_axi_rvalid_reg, int_m_axi_rvalid} >> r_grant_encoded) & r_grant_valid; + wire m_axi_rready_mux; + + assign int_axi_rready[m*M_COUNT +: M_COUNT] = (r_grant_valid && m_axi_rready_mux) << r_grant_encoded; + assign decerr_m_axi_rready = (r_grant_valid && m_axi_rready_mux) && (r_grant_encoded == M_COUNT_P1-1); + + for (n = 0; n < M_COUNT; n = n + 1) begin + assign r_request[n] = int_axi_rvalid[n*S_COUNT+m] && !r_grant[n]; + assign r_acknowledge[n] = r_grant[n] && int_axi_rvalid[n*S_COUNT+m] && m_axi_rlast_mux && m_axi_rready_mux; + end + + assign r_request[M_COUNT_P1-1] = decerr_m_axi_rvalid_reg && !r_grant[M_COUNT_P1-1]; + assign r_acknowledge[M_COUNT_P1-1] = r_grant[M_COUNT_P1-1] && decerr_m_axi_rvalid_reg && decerr_m_axi_rlast_reg && m_axi_rready_mux; + + assign s_cpl_id = m_axi_rid_mux; + assign s_cpl_valid = m_axi_rvalid_mux && m_axi_rready_mux && m_axi_rlast_mux; + + // S side register + axi_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .AR_REG_TYPE(S_AR_REG_TYPE[m*2 +: 2]), + .R_REG_TYPE(S_R_REG_TYPE[m*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_arid(s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_araddr(s_axi_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_arlen(s_axi_arlen[m*8 +: 8]), + .s_axi_arsize(s_axi_arsize[m*3 +: 3]), + .s_axi_arburst(s_axi_arburst[m*2 +: 2]), + .s_axi_arlock(s_axi_arlock[m]), + .s_axi_arcache(s_axi_arcache[m*4 +: 4]), + .s_axi_arprot(s_axi_arprot[m*3 +: 3]), + .s_axi_arqos(s_axi_arqos[m*4 +: 4]), + .s_axi_arregion(4'd0), + .s_axi_aruser(s_axi_aruser[m*ARUSER_WIDTH +: ARUSER_WIDTH]), + .s_axi_arvalid(s_axi_arvalid[m]), + .s_axi_arready(s_axi_arready[m]), + .s_axi_rid(s_axi_rid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_rdata(s_axi_rdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axi_rresp(s_axi_rresp[m*2 +: 2]), + .s_axi_rlast(s_axi_rlast[m]), + .s_axi_ruser(s_axi_ruser[m*RUSER_WIDTH +: RUSER_WIDTH]), + .s_axi_rvalid(s_axi_rvalid[m]), + .s_axi_rready(s_axi_rready[m]), + .m_axi_arid(int_s_axi_arid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .m_axi_araddr(int_s_axi_araddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_arlen(int_s_axi_arlen[m*8 +: 8]), + .m_axi_arsize(int_s_axi_arsize[m*3 +: 3]), + .m_axi_arburst(int_s_axi_arburst[m*2 +: 2]), + .m_axi_arlock(int_s_axi_arlock[m]), + .m_axi_arcache(int_s_axi_arcache[m*4 +: 4]), + .m_axi_arprot(int_s_axi_arprot[m*3 +: 3]), + .m_axi_arqos(int_s_axi_arqos[m*4 +: 4]), + .m_axi_arregion(), + .m_axi_aruser(int_s_axi_aruser[m*ARUSER_WIDTH +: ARUSER_WIDTH]), + .m_axi_arvalid(int_s_axi_arvalid[m]), + .m_axi_arready(int_s_axi_arready[m]), + .m_axi_rid(m_axi_rid_mux), + .m_axi_rdata(m_axi_rdata_mux), + .m_axi_rresp(m_axi_rresp_mux), + .m_axi_rlast(m_axi_rlast_mux), + .m_axi_ruser(m_axi_ruser_mux), + .m_axi_rvalid(m_axi_rvalid_mux), + .m_axi_rready(m_axi_rready_mux) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + // in-flight transaction count + wire trans_start; + wire trans_complete; + reg [$clog2(M_ISSUE[n*32 +: 32]+1)-1:0] trans_count_reg = 0; + + wire trans_limit = trans_count_reg >= M_ISSUE[n*32 +: 32] && !trans_complete; + + always @(posedge clk) begin + if (rst) begin + trans_count_reg <= 0; + end else begin + if (trans_start && !trans_complete) begin + trans_count_reg <= trans_count_reg + 1; + end else if (!trans_start && trans_complete) begin + trans_count_reg <= trans_count_reg - 1; + end + end + end + + // address arbitration + wire [S_COUNT-1:0] a_request; + wire [S_COUNT-1:0] a_acknowledge; + wire [S_COUNT-1:0] a_grant; + wire a_grant_valid; + wire [CL_S_COUNT-1:0] a_grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") + ) + a_arb_inst ( + .clk(clk), + .rst(rst), + .request(a_request), + .acknowledge(a_acknowledge), + .grant(a_grant), + .grant_valid(a_grant_valid), + .grant_encoded(a_grant_encoded) + ); + + // address mux + wire [M_ID_WIDTH-1:0] s_axi_arid_mux = int_s_axi_arid[a_grant_encoded*S_ID_WIDTH +: S_ID_WIDTH] | (a_grant_encoded << S_ID_WIDTH); + wire [ADDR_WIDTH-1:0] s_axi_araddr_mux = int_s_axi_araddr[a_grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [7:0] s_axi_arlen_mux = int_s_axi_arlen[a_grant_encoded*8 +: 8]; + wire [2:0] s_axi_arsize_mux = int_s_axi_arsize[a_grant_encoded*3 +: 3]; + wire [1:0] s_axi_arburst_mux = int_s_axi_arburst[a_grant_encoded*2 +: 2]; + wire s_axi_arlock_mux = int_s_axi_arlock[a_grant_encoded]; + wire [3:0] s_axi_arcache_mux = int_s_axi_arcache[a_grant_encoded*4 +: 4]; + wire [2:0] s_axi_arprot_mux = int_s_axi_arprot[a_grant_encoded*3 +: 3]; + wire [3:0] s_axi_arqos_mux = int_s_axi_arqos[a_grant_encoded*4 +: 4]; + wire [3:0] s_axi_arregion_mux = int_s_axi_arregion[a_grant_encoded*4 +: 4]; + wire [ARUSER_WIDTH-1:0] s_axi_aruser_mux = int_s_axi_aruser[a_grant_encoded*ARUSER_WIDTH +: ARUSER_WIDTH]; + wire s_axi_arvalid_mux = int_axi_arvalid[a_grant_encoded*M_COUNT+n] && a_grant_valid; + wire s_axi_arready_mux; + + assign int_axi_arready[n*S_COUNT +: S_COUNT] = (a_grant_valid && s_axi_arready_mux) << a_grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign a_request[m] = int_axi_arvalid[m*M_COUNT+n] && !a_grant[m] && !trans_limit; + assign a_acknowledge[m] = a_grant[m] && int_axi_arvalid[m*M_COUNT+n] && s_axi_arready_mux; + end + + assign trans_start = s_axi_arvalid_mux && s_axi_arready_mux && a_grant_valid; + + // read response forwarding + wire [CL_S_COUNT-1:0] r_select = m_axi_rid[n*M_ID_WIDTH +: M_ID_WIDTH] >> S_ID_WIDTH; + + assign int_axi_rvalid[n*S_COUNT +: S_COUNT] = int_m_axi_rvalid[n] << r_select; + assign int_m_axi_rready[n] = int_axi_rready[r_select*M_COUNT+n]; + + assign trans_complete = int_m_axi_rvalid[n] && int_m_axi_rready[n] && int_m_axi_rlast[n]; + + // M side register + axi_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(M_ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .AR_REG_TYPE(M_AR_REG_TYPE[n*2 +: 2]), + .R_REG_TYPE(M_R_REG_TYPE[n*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_arid(s_axi_arid_mux), + .s_axi_araddr(s_axi_araddr_mux), + .s_axi_arlen(s_axi_arlen_mux), + .s_axi_arsize(s_axi_arsize_mux), + .s_axi_arburst(s_axi_arburst_mux), + .s_axi_arlock(s_axi_arlock_mux), + .s_axi_arcache(s_axi_arcache_mux), + .s_axi_arprot(s_axi_arprot_mux), + .s_axi_arqos(s_axi_arqos_mux), + .s_axi_arregion(s_axi_arregion_mux), + .s_axi_aruser(s_axi_aruser_mux), + .s_axi_arvalid(s_axi_arvalid_mux), + .s_axi_arready(s_axi_arready_mux), + .s_axi_rid(int_m_axi_rid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .s_axi_rdata(int_m_axi_rdata[n*DATA_WIDTH +: DATA_WIDTH]), + .s_axi_rresp(int_m_axi_rresp[n*2 +: 2]), + .s_axi_rlast(int_m_axi_rlast[n]), + .s_axi_ruser(int_m_axi_ruser[n*RUSER_WIDTH +: RUSER_WIDTH]), + .s_axi_rvalid(int_m_axi_rvalid[n]), + .s_axi_rready(int_m_axi_rready[n]), + .m_axi_arid(m_axi_arid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_araddr(m_axi_araddr[n*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_arlen(m_axi_arlen[n*8 +: 8]), + .m_axi_arsize(m_axi_arsize[n*3 +: 3]), + .m_axi_arburst(m_axi_arburst[n*2 +: 2]), + .m_axi_arlock(m_axi_arlock[n]), + .m_axi_arcache(m_axi_arcache[n*4 +: 4]), + .m_axi_arprot(m_axi_arprot[n*3 +: 3]), + .m_axi_arqos(m_axi_arqos[n*4 +: 4]), + .m_axi_arregion(m_axi_arregion[n*4 +: 4]), + .m_axi_aruser(m_axi_aruser[n*ARUSER_WIDTH +: ARUSER_WIDTH]), + .m_axi_arvalid(m_axi_arvalid[n]), + .m_axi_arready(m_axi_arready[n]), + .m_axi_rid(m_axi_rid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_rdata(m_axi_rdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axi_rresp(m_axi_rresp[n*2 +: 2]), + .m_axi_rlast(m_axi_rlast[n]), + .m_axi_ruser(m_axi_ruser[n*RUSER_WIDTH +: RUSER_WIDTH]), + .m_axi_rvalid(m_axi_rvalid[n]), + .m_axi_rready(m_axi_rready[n]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/corundum/lib/axi/rtl/axi_crossbar_wr.v b/corundum/lib/axi/rtl/axi_crossbar_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..19492b2ea79b4a0e99d52727456b3583cc0d9f90 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_crossbar_wr.v @@ -0,0 +1,672 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 crossbar (write) + */ +module axi_crossbar_wr # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Input ID field width (from AXI masters) + parameter S_ID_WIDTH = 8, + // Output ID field width (towards AXI slaves) + // Additional bits required for response routing + parameter M_ID_WIDTH = S_ID_WIDTH+$clog2(S_COUNT), + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Number of concurrent unique IDs for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_THREADS = {S_COUNT{32'd2}}, + // Number of concurrent operations for each slave interface + // S_COUNT concatenated fields of 32 bits + parameter S_ACCEPT = {S_COUNT{32'd16}}, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Number of concurrent operations for each master interface + // M_COUNT concatenated fields of 32 bits + parameter M_ISSUE = {M_COUNT{32'd4}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}}, + // Slave interface AW channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_AW_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface W channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_W_REG_TYPE = {S_COUNT{2'd0}}, + // Slave interface B channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_B_REG_TYPE = {S_COUNT{2'd1}}, + // Master interface AW channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_AW_REG_TYPE = {M_COUNT{2'd1}}, + // Master interface W channel register type (output) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_W_REG_TYPE = {M_COUNT{2'd2}}, + // Master interface B channel register type (input) + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_B_REG_TYPE = {M_COUNT{2'd0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_awid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [S_COUNT*8-1:0] s_axi_awlen, + input wire [S_COUNT*3-1:0] s_axi_awsize, + input wire [S_COUNT*2-1:0] s_axi_awburst, + input wire [S_COUNT-1:0] s_axi_awlock, + input wire [S_COUNT*4-1:0] s_axi_awcache, + input wire [S_COUNT*3-1:0] s_axi_awprot, + input wire [S_COUNT*4-1:0] s_axi_awqos, + input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser, + input wire [S_COUNT-1:0] s_axi_awvalid, + output wire [S_COUNT-1:0] s_axi_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb, + input wire [S_COUNT-1:0] s_axi_wlast, + input wire [S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser, + input wire [S_COUNT-1:0] s_axi_wvalid, + output wire [S_COUNT-1:0] s_axi_wready, + output wire [S_COUNT*S_ID_WIDTH-1:0] s_axi_bid, + output wire [S_COUNT*2-1:0] s_axi_bresp, + output wire [S_COUNT*BUSER_WIDTH-1:0] s_axi_buser, + output wire [S_COUNT-1:0] s_axi_bvalid, + input wire [S_COUNT-1:0] s_axi_bready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_awid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [M_COUNT*8-1:0] m_axi_awlen, + output wire [M_COUNT*3-1:0] m_axi_awsize, + output wire [M_COUNT*2-1:0] m_axi_awburst, + output wire [M_COUNT-1:0] m_axi_awlock, + output wire [M_COUNT*4-1:0] m_axi_awcache, + output wire [M_COUNT*3-1:0] m_axi_awprot, + output wire [M_COUNT*4-1:0] m_axi_awqos, + output wire [M_COUNT*4-1:0] m_axi_awregion, + output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser, + output wire [M_COUNT-1:0] m_axi_awvalid, + input wire [M_COUNT-1:0] m_axi_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb, + output wire [M_COUNT-1:0] m_axi_wlast, + output wire [M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser, + output wire [M_COUNT-1:0] m_axi_wvalid, + input wire [M_COUNT-1:0] m_axi_wready, + input wire [M_COUNT*M_ID_WIDTH-1:0] m_axi_bid, + input wire [M_COUNT*2-1:0] m_axi_bresp, + input wire [M_COUNT*BUSER_WIDTH-1:0] m_axi_buser, + input wire [M_COUNT-1:0] m_axi_bvalid, + output wire [M_COUNT-1:0] m_axi_bready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); +parameter M_COUNT_P1 = M_COUNT+1; +parameter CL_M_COUNT_P1 = $clog2(M_COUNT_P1); + +integer i; + +// check configuration +initial begin + if (M_ID_WIDTH < S_ID_WIDTH+$clog2(S_COUNT)) begin + $error("Error: M_ID_WIDTH must be at least $clog2(S_COUNT) larger than S_ID_WIDTH (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: value out of range (instance %m)"); + $finish; + end + end +end + +wire [S_COUNT*S_ID_WIDTH-1:0] int_s_axi_awid; +wire [S_COUNT*ADDR_WIDTH-1:0] int_s_axi_awaddr; +wire [S_COUNT*8-1:0] int_s_axi_awlen; +wire [S_COUNT*3-1:0] int_s_axi_awsize; +wire [S_COUNT*2-1:0] int_s_axi_awburst; +wire [S_COUNT-1:0] int_s_axi_awlock; +wire [S_COUNT*4-1:0] int_s_axi_awcache; +wire [S_COUNT*3-1:0] int_s_axi_awprot; +wire [S_COUNT*4-1:0] int_s_axi_awqos; +wire [S_COUNT*4-1:0] int_s_axi_awregion; +wire [S_COUNT*AWUSER_WIDTH-1:0] int_s_axi_awuser; +wire [S_COUNT-1:0] int_s_axi_awvalid; +wire [S_COUNT-1:0] int_s_axi_awready; + +wire [S_COUNT*M_COUNT-1:0] int_axi_awvalid; +wire [M_COUNT*S_COUNT-1:0] int_axi_awready; + +wire [S_COUNT*DATA_WIDTH-1:0] int_s_axi_wdata; +wire [S_COUNT*STRB_WIDTH-1:0] int_s_axi_wstrb; +wire [S_COUNT-1:0] int_s_axi_wlast; +wire [S_COUNT*WUSER_WIDTH-1:0] int_s_axi_wuser; +wire [S_COUNT-1:0] int_s_axi_wvalid; +wire [S_COUNT-1:0] int_s_axi_wready; + +wire [S_COUNT*M_COUNT-1:0] int_axi_wvalid; +wire [M_COUNT*S_COUNT-1:0] int_axi_wready; + +wire [M_COUNT*M_ID_WIDTH-1:0] int_m_axi_bid; +wire [M_COUNT*2-1:0] int_m_axi_bresp; +wire [M_COUNT*BUSER_WIDTH-1:0] int_m_axi_buser; +wire [M_COUNT-1:0] int_m_axi_bvalid; +wire [M_COUNT-1:0] int_m_axi_bready; + +wire [M_COUNT*S_COUNT-1:0] int_axi_bvalid; +wire [S_COUNT*M_COUNT-1:0] int_axi_bready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + // address decode and admission control + wire [CL_M_COUNT-1:0] a_select; + + wire m_axi_avalid; + wire m_axi_aready; + + wire [CL_M_COUNT-1:0] m_wc_select; + wire m_wc_decerr; + wire m_wc_valid; + wire m_wc_ready; + + wire m_rc_decerr; + wire m_rc_valid; + wire m_rc_ready; + + wire [S_ID_WIDTH-1:0] s_cpl_id; + wire s_cpl_valid; + + axi_crossbar_addr #( + .S(m), + .S_COUNT(S_COUNT), + .M_COUNT(M_COUNT), + .ADDR_WIDTH(ADDR_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .S_THREADS(S_THREADS[m*32 +: 32]), + .S_ACCEPT(S_ACCEPT[m*32 +: 32]), + .M_REGIONS(M_REGIONS), + .M_BASE_ADDR(M_BASE_ADDR), + .M_ADDR_WIDTH(M_ADDR_WIDTH), + .M_CONNECT(M_CONNECT), + .M_SECURE(M_SECURE), + .WC_OUTPUT(1) + ) + addr_inst ( + .clk(clk), + .rst(rst), + + /* + * Address input + */ + .s_axi_aid(int_s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_aaddr(int_s_axi_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_aprot(int_s_axi_awprot[m*3 +: 3]), + .s_axi_aqos(int_s_axi_awqos[m*4 +: 4]), + .s_axi_avalid(int_s_axi_awvalid[m]), + .s_axi_aready(int_s_axi_awready[m]), + + /* + * Address output + */ + .m_axi_aregion(int_s_axi_awregion[m*4 +: 4]), + .m_select(a_select), + .m_axi_avalid(m_axi_avalid), + .m_axi_aready(m_axi_aready), + + /* + * Write command output + */ + .m_wc_select(m_wc_select), + .m_wc_decerr(m_wc_decerr), + .m_wc_valid(m_wc_valid), + .m_wc_ready(m_wc_ready), + + /* + * Response command output + */ + .m_rc_decerr(m_rc_decerr), + .m_rc_valid(m_rc_valid), + .m_rc_ready(m_rc_ready), + + /* + * Completion input + */ + .s_cpl_id(s_cpl_id), + .s_cpl_valid(s_cpl_valid) + ); + + assign int_axi_awvalid[m*M_COUNT +: M_COUNT] = m_axi_avalid << a_select; + assign m_axi_aready = int_axi_awready[a_select*S_COUNT+m]; + + // write command handling + reg [CL_M_COUNT-1:0] w_select_reg = 0, w_select_next; + reg w_drop_reg = 1'b0, w_drop_next; + reg w_select_valid_reg = 1'b0, w_select_valid_next; + + assign m_wc_ready = !w_select_valid_reg; + + always @* begin + w_select_next = w_select_reg; + w_drop_next = w_drop_reg && !(int_s_axi_wvalid[m] && int_s_axi_wready[m] && int_s_axi_wlast[m]); + w_select_valid_next = w_select_valid_reg && !(int_s_axi_wvalid[m] && int_s_axi_wready[m] && int_s_axi_wlast[m]); + + if (m_wc_valid && !w_select_valid_reg) begin + w_select_next = m_wc_select; + w_drop_next = m_wc_decerr; + w_select_valid_next = m_wc_valid; + end + end + + always @(posedge clk) begin + if (rst) begin + w_select_valid_reg <= 1'b0; + end else begin + w_select_valid_reg <= w_select_valid_next; + end + + w_select_reg <= w_select_next; + w_drop_reg <= w_drop_next; + end + + // write data forwarding + assign int_axi_wvalid[m*M_COUNT +: M_COUNT] = (int_s_axi_wvalid[m] && w_select_valid_reg && !w_drop_reg) << w_select_reg; + assign int_s_axi_wready[m] = int_axi_wready[w_select_reg*S_COUNT+m] || w_drop_reg; + + // decode error handling + reg [S_ID_WIDTH-1:0] decerr_m_axi_bid_reg = {S_ID_WIDTH{1'b0}}, decerr_m_axi_bid_next; + reg decerr_m_axi_bvalid_reg = 1'b0, decerr_m_axi_bvalid_next; + wire decerr_m_axi_bready; + + assign m_rc_ready = !decerr_m_axi_bvalid_reg; + + always @* begin + decerr_m_axi_bid_next = decerr_m_axi_bid_reg; + decerr_m_axi_bvalid_next = decerr_m_axi_bvalid_reg; + + if (decerr_m_axi_bvalid_reg) begin + if (decerr_m_axi_bready) begin + decerr_m_axi_bvalid_next = 1'b0; + end + end else if (m_rc_valid && m_rc_ready) begin + decerr_m_axi_bid_next = int_s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]; + decerr_m_axi_bvalid_next = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + decerr_m_axi_bvalid_reg <= 1'b0; + end else begin + decerr_m_axi_bvalid_reg <= decerr_m_axi_bvalid_next; + end + + decerr_m_axi_bid_reg <= decerr_m_axi_bid_next; + end + + // write response arbitration + wire [M_COUNT_P1-1:0] b_request; + wire [M_COUNT_P1-1:0] b_acknowledge; + wire [M_COUNT_P1-1:0] b_grant; + wire b_grant_valid; + wire [CL_M_COUNT_P1-1:0] b_grant_encoded; + + arbiter #( + .PORTS(M_COUNT_P1), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") + ) + b_arb_inst ( + .clk(clk), + .rst(rst), + .request(b_request), + .acknowledge(b_acknowledge), + .grant(b_grant), + .grant_valid(b_grant_valid), + .grant_encoded(b_grant_encoded) + ); + + // write response mux + wire [S_ID_WIDTH-1:0] m_axi_bid_mux = {decerr_m_axi_bid_reg, int_m_axi_bid} >> b_grant_encoded*M_ID_WIDTH; + wire [1:0] m_axi_bresp_mux = {2'b11, int_m_axi_bresp} >> b_grant_encoded*2; + wire [BUSER_WIDTH-1:0] m_axi_buser_mux = {{BUSER_WIDTH{1'b0}}, int_m_axi_buser} >> b_grant_encoded*BUSER_WIDTH; + wire m_axi_bvalid_mux = ({decerr_m_axi_bvalid_reg, int_m_axi_bvalid} >> b_grant_encoded) & b_grant_valid; + wire m_axi_bready_mux; + + assign int_axi_bready[m*M_COUNT +: M_COUNT] = (b_grant_valid && m_axi_bready_mux) << b_grant_encoded; + assign decerr_m_axi_bready = (b_grant_valid && m_axi_bready_mux) && (b_grant_encoded == M_COUNT_P1-1); + + for (n = 0; n < M_COUNT; n = n + 1) begin + assign b_request[n] = int_axi_bvalid[n*S_COUNT+m] && !b_grant[n]; + assign b_acknowledge[n] = b_grant[n] && int_axi_bvalid[n*S_COUNT+m] && m_axi_bready_mux; + end + + assign b_request[M_COUNT_P1-1] = decerr_m_axi_bvalid_reg && !b_grant[M_COUNT_P1-1]; + assign b_acknowledge[M_COUNT_P1-1] = b_grant[M_COUNT_P1-1] && decerr_m_axi_bvalid_reg && m_axi_bready_mux; + + assign s_cpl_id = m_axi_bid_mux; + assign s_cpl_valid = m_axi_bvalid_mux && m_axi_bready_mux; + + // S side register + axi_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(S_ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .AW_REG_TYPE(S_AW_REG_TYPE[m*2 +: 2]), + .W_REG_TYPE(S_W_REG_TYPE[m*2 +: 2]), + .B_REG_TYPE(S_B_REG_TYPE[m*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_awid(s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_awaddr(s_axi_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .s_axi_awlen(s_axi_awlen[m*8 +: 8]), + .s_axi_awsize(s_axi_awsize[m*3 +: 3]), + .s_axi_awburst(s_axi_awburst[m*2 +: 2]), + .s_axi_awlock(s_axi_awlock[m]), + .s_axi_awcache(s_axi_awcache[m*4 +: 4]), + .s_axi_awprot(s_axi_awprot[m*3 +: 3]), + .s_axi_awqos(s_axi_awqos[m*4 +: 4]), + .s_axi_awregion(4'd0), + .s_axi_awuser(s_axi_awuser[m*AWUSER_WIDTH +: AWUSER_WIDTH]), + .s_axi_awvalid(s_axi_awvalid[m]), + .s_axi_awready(s_axi_awready[m]), + .s_axi_wdata(s_axi_wdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axi_wstrb(s_axi_wstrb[m*STRB_WIDTH +: STRB_WIDTH]), + .s_axi_wlast(s_axi_wlast[m]), + .s_axi_wuser(s_axi_wuser[m*WUSER_WIDTH +: WUSER_WIDTH]), + .s_axi_wvalid(s_axi_wvalid[m]), + .s_axi_wready(s_axi_wready[m]), + .s_axi_bid(s_axi_bid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .s_axi_bresp(s_axi_bresp[m*2 +: 2]), + .s_axi_buser(s_axi_buser[m*BUSER_WIDTH +: BUSER_WIDTH]), + .s_axi_bvalid(s_axi_bvalid[m]), + .s_axi_bready(s_axi_bready[m]), + .m_axi_awid(int_s_axi_awid[m*S_ID_WIDTH +: S_ID_WIDTH]), + .m_axi_awaddr(int_s_axi_awaddr[m*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_awlen(int_s_axi_awlen[m*8 +: 8]), + .m_axi_awsize(int_s_axi_awsize[m*3 +: 3]), + .m_axi_awburst(int_s_axi_awburst[m*2 +: 2]), + .m_axi_awlock(int_s_axi_awlock[m]), + .m_axi_awcache(int_s_axi_awcache[m*4 +: 4]), + .m_axi_awprot(int_s_axi_awprot[m*3 +: 3]), + .m_axi_awqos(int_s_axi_awqos[m*4 +: 4]), + .m_axi_awregion(), + .m_axi_awuser(int_s_axi_awuser[m*AWUSER_WIDTH +: AWUSER_WIDTH]), + .m_axi_awvalid(int_s_axi_awvalid[m]), + .m_axi_awready(int_s_axi_awready[m]), + .m_axi_wdata(int_s_axi_wdata[m*DATA_WIDTH +: DATA_WIDTH]), + .m_axi_wstrb(int_s_axi_wstrb[m*STRB_WIDTH +: STRB_WIDTH]), + .m_axi_wlast(int_s_axi_wlast[m]), + .m_axi_wuser(int_s_axi_wuser[m*WUSER_WIDTH +: WUSER_WIDTH]), + .m_axi_wvalid(int_s_axi_wvalid[m]), + .m_axi_wready(int_s_axi_wready[m]), + .m_axi_bid(m_axi_bid_mux), + .m_axi_bresp(m_axi_bresp_mux), + .m_axi_buser(m_axi_buser_mux), + .m_axi_bvalid(m_axi_bvalid_mux), + .m_axi_bready(m_axi_bready_mux) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + // in-flight transaction count + wire trans_start; + wire trans_complete; + reg [$clog2(M_ISSUE[n*32 +: 32]+1)-1:0] trans_count_reg = 0; + + wire trans_limit = trans_count_reg >= M_ISSUE[n*32 +: 32] && !trans_complete; + + always @(posedge clk) begin + if (rst) begin + trans_count_reg <= 0; + end else begin + if (trans_start && !trans_complete) begin + trans_count_reg <= trans_count_reg + 1; + end else if (!trans_start && trans_complete) begin + trans_count_reg <= trans_count_reg - 1; + end + end + end + + // address arbitration + reg [CL_S_COUNT-1:0] w_select_reg = 0, w_select_next; + reg w_select_valid_reg = 1'b0, w_select_valid_next; + reg w_select_new_reg = 1'b0, w_select_new_next; + + wire [S_COUNT-1:0] a_request; + wire [S_COUNT-1:0] a_acknowledge; + wire [S_COUNT-1:0] a_grant; + wire a_grant_valid; + wire [CL_S_COUNT-1:0] a_grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") + ) + a_arb_inst ( + .clk(clk), + .rst(rst), + .request(a_request), + .acknowledge(a_acknowledge), + .grant(a_grant), + .grant_valid(a_grant_valid), + .grant_encoded(a_grant_encoded) + ); + + // address mux + wire [M_ID_WIDTH-1:0] s_axi_awid_mux = int_s_axi_awid[a_grant_encoded*S_ID_WIDTH +: S_ID_WIDTH] | (a_grant_encoded << S_ID_WIDTH); + wire [ADDR_WIDTH-1:0] s_axi_awaddr_mux = int_s_axi_awaddr[a_grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [7:0] s_axi_awlen_mux = int_s_axi_awlen[a_grant_encoded*8 +: 8]; + wire [2:0] s_axi_awsize_mux = int_s_axi_awsize[a_grant_encoded*3 +: 3]; + wire [1:0] s_axi_awburst_mux = int_s_axi_awburst[a_grant_encoded*2 +: 2]; + wire s_axi_awlock_mux = int_s_axi_awlock[a_grant_encoded]; + wire [3:0] s_axi_awcache_mux = int_s_axi_awcache[a_grant_encoded*4 +: 4]; + wire [2:0] s_axi_awprot_mux = int_s_axi_awprot[a_grant_encoded*3 +: 3]; + wire [3:0] s_axi_awqos_mux = int_s_axi_awqos[a_grant_encoded*4 +: 4]; + wire [3:0] s_axi_awregion_mux = int_s_axi_awregion[a_grant_encoded*4 +: 4]; + wire [AWUSER_WIDTH-1:0] s_axi_awuser_mux = int_s_axi_awuser[a_grant_encoded*AWUSER_WIDTH +: AWUSER_WIDTH]; + wire s_axi_awvalid_mux = int_axi_awvalid[a_grant_encoded*M_COUNT+n] && a_grant_valid; + wire s_axi_awready_mux; + + assign int_axi_awready[n*S_COUNT +: S_COUNT] = (a_grant_valid && s_axi_awready_mux) << a_grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign a_request[m] = int_axi_awvalid[m*M_COUNT+n] && !a_grant[m] && !trans_limit && !w_select_valid_next; + assign a_acknowledge[m] = a_grant[m] && int_axi_awvalid[m*M_COUNT+n] && s_axi_awready_mux; + end + + assign trans_start = s_axi_awvalid_mux && s_axi_awready_mux && a_grant_valid; + + // write data mux + wire [DATA_WIDTH-1:0] s_axi_wdata_mux = int_s_axi_wdata[w_select_reg*DATA_WIDTH +: DATA_WIDTH]; + wire [STRB_WIDTH-1:0] s_axi_wstrb_mux = int_s_axi_wstrb[w_select_reg*STRB_WIDTH +: STRB_WIDTH]; + wire s_axi_wlast_mux = int_s_axi_wlast[w_select_reg]; + wire [WUSER_WIDTH-1:0] s_axi_wuser_mux = int_s_axi_wuser[w_select_reg*WUSER_WIDTH +: WUSER_WIDTH]; + wire s_axi_wvalid_mux = int_axi_wvalid[w_select_reg*M_COUNT+n] && w_select_valid_reg; + wire s_axi_wready_mux; + + assign int_axi_wready[n*S_COUNT +: S_COUNT] = (w_select_valid_reg && s_axi_wready_mux) << w_select_reg; + + // write data routing + always @* begin + w_select_next = w_select_reg; + w_select_valid_next = w_select_valid_reg && !(s_axi_wvalid_mux && s_axi_wready_mux && s_axi_wlast_mux); + w_select_new_next = w_select_new_reg || !a_grant_valid || a_acknowledge; + + if (a_grant_valid && !w_select_valid_reg && w_select_new_reg) begin + w_select_next = a_grant_encoded; + w_select_valid_next = a_grant_valid; + w_select_new_next = 1'b0; + end + end + + always @(posedge clk) begin + if (rst) begin + w_select_valid_reg <= 1'b0; + w_select_new_reg <= 1'b1; + end else begin + w_select_valid_reg <= w_select_valid_next; + w_select_new_reg <= w_select_new_next; + end + + w_select_reg <= w_select_next; + end + + // write response forwarding + wire [CL_S_COUNT-1:0] b_select = m_axi_bid[n*M_ID_WIDTH +: M_ID_WIDTH] >> S_ID_WIDTH; + + assign int_axi_bvalid[n*S_COUNT +: S_COUNT] = int_m_axi_bvalid[n] << b_select; + assign int_m_axi_bready[n] = int_axi_bready[b_select*M_COUNT+n]; + + assign trans_complete = int_m_axi_bvalid[n] && int_m_axi_bready[n]; + + // M side register + axi_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(M_ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .AW_REG_TYPE(M_AW_REG_TYPE[n*2 +: 2]), + .W_REG_TYPE(M_W_REG_TYPE[n*2 +: 2]), + .B_REG_TYPE(M_B_REG_TYPE[n*2 +: 2]) + ) + reg_inst ( + .clk(clk), + .rst(rst), + .s_axi_awid(s_axi_awid_mux), + .s_axi_awaddr(s_axi_awaddr_mux), + .s_axi_awlen(s_axi_awlen_mux), + .s_axi_awsize(s_axi_awsize_mux), + .s_axi_awburst(s_axi_awburst_mux), + .s_axi_awlock(s_axi_awlock_mux), + .s_axi_awcache(s_axi_awcache_mux), + .s_axi_awprot(s_axi_awprot_mux), + .s_axi_awqos(s_axi_awqos_mux), + .s_axi_awregion(s_axi_awregion_mux), + .s_axi_awuser(s_axi_awuser_mux), + .s_axi_awvalid(s_axi_awvalid_mux), + .s_axi_awready(s_axi_awready_mux), + .s_axi_wdata(s_axi_wdata_mux), + .s_axi_wstrb(s_axi_wstrb_mux), + .s_axi_wlast(s_axi_wlast_mux), + .s_axi_wuser(s_axi_wuser_mux), + .s_axi_wvalid(s_axi_wvalid_mux), + .s_axi_wready(s_axi_wready_mux), + .s_axi_bid(int_m_axi_bid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .s_axi_bresp(int_m_axi_bresp[n*2 +: 2]), + .s_axi_buser(int_m_axi_buser[n*BUSER_WIDTH +: BUSER_WIDTH]), + .s_axi_bvalid(int_m_axi_bvalid[n]), + .s_axi_bready(int_m_axi_bready[n]), + .m_axi_awid(m_axi_awid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_awaddr(m_axi_awaddr[n*ADDR_WIDTH +: ADDR_WIDTH]), + .m_axi_awlen(m_axi_awlen[n*8 +: 8]), + .m_axi_awsize(m_axi_awsize[n*3 +: 3]), + .m_axi_awburst(m_axi_awburst[n*2 +: 2]), + .m_axi_awlock(m_axi_awlock[n]), + .m_axi_awcache(m_axi_awcache[n*4 +: 4]), + .m_axi_awprot(m_axi_awprot[n*3 +: 3]), + .m_axi_awqos(m_axi_awqos[n*4 +: 4]), + .m_axi_awregion(m_axi_awregion[n*4 +: 4]), + .m_axi_awuser(m_axi_awuser[n*AWUSER_WIDTH +: AWUSER_WIDTH]), + .m_axi_awvalid(m_axi_awvalid[n]), + .m_axi_awready(m_axi_awready[n]), + .m_axi_wdata(m_axi_wdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axi_wstrb(m_axi_wstrb[n*STRB_WIDTH +: STRB_WIDTH]), + .m_axi_wlast(m_axi_wlast[n]), + .m_axi_wuser(m_axi_wuser[n*WUSER_WIDTH +: WUSER_WIDTH]), + .m_axi_wvalid(m_axi_wvalid[n]), + .m_axi_wready(m_axi_wready[n]), + .m_axi_bid(m_axi_bid[n*M_ID_WIDTH +: M_ID_WIDTH]), + .m_axi_bresp(m_axi_bresp[n*2 +: 2]), + .m_axi_buser(m_axi_buser[n*BUSER_WIDTH +: BUSER_WIDTH]), + .m_axi_bvalid(m_axi_bvalid[n]), + .m_axi_bready(m_axi_bready[n]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/corundum/lib/axi/rtl/axi_dma.v b/corundum/lib/axi/rtl/axi_dma.v new file mode 100644 index 0000000000000000000000000000000000000000..b4a3f3ac48c776b7bee3564ec202c9c0053e58d7 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_dma.v @@ -0,0 +1,353 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 DMA + */ +module axi_dma # +( + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 16, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 16, + // Width of AXI stream interfaces in bits + parameter AXIS_DATA_WIDTH = AXI_DATA_WIDTH, + // Use AXI stream tkeep signal + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + // Use AXI stream tlast signal + parameter AXIS_LAST_ENABLE = 1, + // Propagate AXI stream tid signal + parameter AXIS_ID_ENABLE = 0, + // AXI stream tid signal width + parameter AXIS_ID_WIDTH = 8, + // Propagate AXI stream tdest signal + parameter AXIS_DEST_ENABLE = 0, + // AXI stream tdest signal width + parameter AXIS_DEST_WIDTH = 8, + // Propagate AXI stream tuser signal + parameter AXIS_USER_ENABLE = 1, + // AXI stream tuser signal width + parameter AXIS_USER_WIDTH = 1, + // Width of length field + parameter LEN_WIDTH = 20, + // Width of tag field + parameter TAG_WIDTH = 8, + // Enable support for scatter/gather DMA + // (multiple descriptors per AXI stream frame) + parameter ENABLE_SG = 0, + // Enable support for unaligned transfers + parameter ENABLE_UNALIGNED = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI read descriptor input + */ + input wire [AXI_ADDR_WIDTH-1:0] s_axis_read_desc_addr, + input wire [LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire [AXIS_ID_WIDTH-1:0] s_axis_read_desc_id, + input wire [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_dest, + input wire [AXIS_USER_WIDTH-1:0] s_axis_read_desc_user, + input wire s_axis_read_desc_valid, + output wire s_axis_read_desc_ready, + + /* + * AXI read descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire m_axis_read_desc_status_valid, + + /* + * AXI stream read data output + */ + output wire [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep, + output wire m_axis_read_data_tvalid, + input wire m_axis_read_data_tready, + output wire m_axis_read_data_tlast, + output wire [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid, + output wire [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest, + output wire [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser, + + /* + * AXI write descriptor input + */ + input wire [AXI_ADDR_WIDTH-1:0] s_axis_write_desc_addr, + input wire [LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire s_axis_write_desc_valid, + output wire s_axis_write_desc_ready, + + /* + * AXI write descriptor status output + */ + output wire [LEN_WIDTH-1:0] m_axis_write_desc_status_len, + output wire [TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id, + output wire [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest, + output wire [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user, + output wire m_axis_write_desc_status_valid, + + /* + * AXI stream write data input + */ + input wire [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tkeep, + input wire s_axis_write_data_tvalid, + output wire s_axis_write_data_tready, + input wire s_axis_write_data_tlast, + input wire [AXIS_ID_WIDTH-1:0] s_axis_write_data_tid, + input wire [AXIS_DEST_WIDTH-1:0] s_axis_write_data_tdest, + input wire [AXIS_USER_WIDTH-1:0] s_axis_write_data_tuser, + + /* + * AXI master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready, + + /* + * Configuration + */ + input wire read_enable, + input wire write_enable, + input wire write_abort +); + +axi_dma_rd #( + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN), + .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH), + .AXIS_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .AXIS_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .AXIS_LAST_ENABLE(AXIS_LAST_ENABLE), + .AXIS_ID_ENABLE(AXIS_ID_ENABLE), + .AXIS_ID_WIDTH(AXIS_ID_WIDTH), + .AXIS_DEST_ENABLE(AXIS_DEST_ENABLE), + .AXIS_DEST_WIDTH(AXIS_DEST_WIDTH), + .AXIS_USER_ENABLE(AXIS_USER_ENABLE), + .AXIS_USER_WIDTH(AXIS_USER_WIDTH), + .LEN_WIDTH(LEN_WIDTH), + .TAG_WIDTH(TAG_WIDTH), + .ENABLE_SG(ENABLE_SG), + .ENABLE_UNALIGNED(ENABLE_UNALIGNED) +) +axi_dma_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI read descriptor input + */ + .s_axis_read_desc_addr(s_axis_read_desc_addr), + .s_axis_read_desc_len(s_axis_read_desc_len), + .s_axis_read_desc_tag(s_axis_read_desc_tag), + .s_axis_read_desc_id(s_axis_read_desc_id), + .s_axis_read_desc_dest(s_axis_read_desc_dest), + .s_axis_read_desc_user(s_axis_read_desc_user), + .s_axis_read_desc_valid(s_axis_read_desc_valid), + .s_axis_read_desc_ready(s_axis_read_desc_ready), + + /* + * AXI read descriptor status output + */ + .m_axis_read_desc_status_tag(m_axis_read_desc_status_tag), + .m_axis_read_desc_status_valid(m_axis_read_desc_status_valid), + + /* + * AXI stream read data output + */ + .m_axis_read_data_tdata(m_axis_read_data_tdata), + .m_axis_read_data_tkeep(m_axis_read_data_tkeep), + .m_axis_read_data_tvalid(m_axis_read_data_tvalid), + .m_axis_read_data_tready(m_axis_read_data_tready), + .m_axis_read_data_tlast(m_axis_read_data_tlast), + .m_axis_read_data_tid(m_axis_read_data_tid), + .m_axis_read_data_tdest(m_axis_read_data_tdest), + .m_axis_read_data_tuser(m_axis_read_data_tuser), + + /* + * AXI master interface + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready), + + /* + * Configuration + */ + .enable(read_enable) +); + +axi_dma_wr #( + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN), + .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH), + .AXIS_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .AXIS_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .AXIS_LAST_ENABLE(AXIS_LAST_ENABLE), + .AXIS_ID_ENABLE(AXIS_ID_ENABLE), + .AXIS_ID_WIDTH(AXIS_ID_WIDTH), + .AXIS_DEST_ENABLE(AXIS_DEST_ENABLE), + .AXIS_DEST_WIDTH(AXIS_DEST_WIDTH), + .AXIS_USER_ENABLE(AXIS_USER_ENABLE), + .AXIS_USER_WIDTH(AXIS_USER_WIDTH), + .LEN_WIDTH(LEN_WIDTH), + .TAG_WIDTH(TAG_WIDTH), + .ENABLE_SG(ENABLE_SG), + .ENABLE_UNALIGNED(ENABLE_UNALIGNED) +) +axi_dma_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI write descriptor input + */ + .s_axis_write_desc_addr(s_axis_write_desc_addr), + .s_axis_write_desc_len(s_axis_write_desc_len), + .s_axis_write_desc_tag(s_axis_write_desc_tag), + .s_axis_write_desc_valid(s_axis_write_desc_valid), + .s_axis_write_desc_ready(s_axis_write_desc_ready), + + /* + * AXI write descriptor status output + */ + .m_axis_write_desc_status_len(m_axis_write_desc_status_len), + .m_axis_write_desc_status_tag(m_axis_write_desc_status_tag), + .m_axis_write_desc_status_id(m_axis_write_desc_status_id), + .m_axis_write_desc_status_dest(m_axis_write_desc_status_dest), + .m_axis_write_desc_status_user(m_axis_write_desc_status_user), + .m_axis_write_desc_status_valid(m_axis_write_desc_status_valid), + + /* + * AXI stream write data input + */ + .s_axis_write_data_tdata(s_axis_write_data_tdata), + .s_axis_write_data_tkeep(s_axis_write_data_tkeep), + .s_axis_write_data_tvalid(s_axis_write_data_tvalid), + .s_axis_write_data_tready(s_axis_write_data_tready), + .s_axis_write_data_tlast(s_axis_write_data_tlast), + .s_axis_write_data_tid(s_axis_write_data_tid), + .s_axis_write_data_tdest(s_axis_write_data_tdest), + .s_axis_write_data_tuser(s_axis_write_data_tuser), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready), + + /* + * Configuration + */ + .enable(write_enable), + .abort(write_abort) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axi_dma_desc_mux.v b/corundum/lib/axi/rtl/axi_dma_desc_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..71008bf2ce86c0e9ec04de97b1bfc50f74bebf92 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_dma_desc_mux.v @@ -0,0 +1,309 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI DMA descriptor mux + */ +module axi_dma_desc_mux # +( + // Number of ports + parameter PORTS = 2, + // AXI address width + parameter AXI_ADDR_WIDTH = 16, + // Propagate AXI stream tid signal + parameter AXIS_ID_ENABLE = 0, + // AXI stream tid signal width + parameter AXIS_ID_WIDTH = 8, + // Propagate AXI stream tdest signal + parameter AXIS_DEST_ENABLE = 0, + // AXI stream tdest signal width + parameter AXIS_DEST_WIDTH = 8, + // Propagate AXI stream tuser signal + parameter AXIS_USER_ENABLE = 1, + // AXI stream tuser signal width + parameter AXIS_USER_WIDTH = 1, + // Length field width + parameter LEN_WIDTH = 20, + // Input tag field width + parameter S_TAG_WIDTH = 8, + // Output tag field width (towards CDMA module) + // Additional bits required for response routing + parameter M_TAG_WIDTH = S_TAG_WIDTH+$clog2(PORTS), + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Descriptor output (to AXI DMA core) + */ + output wire [AXI_ADDR_WIDTH-1:0] m_axis_desc_addr, + output wire [LEN_WIDTH-1:0] m_axis_desc_len, + output wire [M_TAG_WIDTH-1:0] m_axis_desc_tag, + output wire [AXIS_ID_WIDTH-1:0] m_axis_desc_id, + output wire [AXIS_DEST_WIDTH-1:0] m_axis_desc_dest, + output wire [AXIS_USER_WIDTH-1:0] m_axis_desc_user, + output wire m_axis_desc_valid, + input wire m_axis_desc_ready, + + /* + * Descriptor status input (from AXI DMA core) + */ + input wire [LEN_WIDTH-1:0] s_axis_desc_status_len, + input wire [M_TAG_WIDTH-1:0] s_axis_desc_status_tag, + input wire [AXIS_ID_WIDTH-1:0] s_axis_desc_status_id, + input wire [AXIS_DEST_WIDTH-1:0] s_axis_desc_status_dest, + input wire [AXIS_USER_WIDTH-1:0] s_axis_desc_status_user, + input wire s_axis_desc_status_valid, + + /* + * Descriptor input + */ + input wire [PORTS*AXI_ADDR_WIDTH-1:0] s_axis_desc_addr, + input wire [PORTS*LEN_WIDTH-1:0] s_axis_desc_len, + input wire [PORTS*S_TAG_WIDTH-1:0] s_axis_desc_tag, + input wire [PORTS*AXIS_ID_WIDTH-1:0] s_axis_desc_id, + input wire [PORTS*AXIS_DEST_WIDTH-1:0] s_axis_desc_dest, + input wire [PORTS*AXIS_USER_WIDTH-1:0] s_axis_desc_user, + input wire [PORTS-1:0] s_axis_desc_valid, + output wire [PORTS-1:0] s_axis_desc_ready, + + /* + * Descriptor status output + */ + output wire [PORTS*LEN_WIDTH-1:0] m_axis_desc_status_len, + output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_desc_status_tag, + output wire [PORTS*AXIS_ID_WIDTH-1:0] m_axis_desc_status_id, + output wire [PORTS*AXIS_DEST_WIDTH-1:0] m_axis_desc_status_dest, + output wire [PORTS*AXIS_USER_WIDTH-1:0] m_axis_desc_status_user, + output wire [PORTS-1:0] m_axis_desc_status_valid +); + +parameter CL_PORTS = $clog2(PORTS); + +// check configuration +initial begin + if (M_TAG_WIDTH < S_TAG_WIDTH+$clog2(PORTS)) begin + $error("Error: M_TAG_WIDTH must be at least $clog2(PORTS) larger than S_TAG_WIDTH (instance %m)"); + $finish; + end +end + +// descriptor mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_addr_int; +reg [LEN_WIDTH-1:0] m_axis_desc_len_int; +reg [M_TAG_WIDTH-1:0] m_axis_desc_tag_int; +reg [AXIS_ID_WIDTH-1:0] m_axis_desc_id_int; +reg [AXIS_DEST_WIDTH-1:0] m_axis_desc_dest_int; +reg [AXIS_USER_WIDTH-1:0] m_axis_desc_user_int; +reg m_axis_desc_valid_int; +reg m_axis_desc_ready_int_reg = 1'b0; +wire m_axis_desc_ready_int_early; + +assign s_axis_desc_ready = (m_axis_desc_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [AXI_ADDR_WIDTH-1:0] current_s_desc_addr = s_axis_desc_addr[grant_encoded*AXI_ADDR_WIDTH +: AXI_ADDR_WIDTH]; +wire [LEN_WIDTH-1:0] current_s_desc_len = s_axis_desc_len[grant_encoded*LEN_WIDTH +: LEN_WIDTH]; +wire [S_TAG_WIDTH-1:0] current_s_desc_tag = s_axis_desc_tag[grant_encoded*S_TAG_WIDTH +: S_TAG_WIDTH]; +wire [AXIS_ID_WIDTH-1:0] current_s_desc_id = s_axis_desc_id[grant_encoded*AXIS_ID_WIDTH +: AXIS_ID_WIDTH]; +wire [AXIS_DEST_WIDTH-1:0] current_s_desc_dest = s_axis_desc_dest[grant_encoded*AXIS_DEST_WIDTH +: AXIS_DEST_WIDTH]; +wire [AXIS_USER_WIDTH-1:0] current_s_desc_user = s_axis_desc_user[grant_encoded*AXIS_USER_WIDTH +: AXIS_USER_WIDTH]; +wire current_s_desc_valid = s_axis_desc_valid[grant_encoded]; +wire current_s_desc_ready = s_axis_desc_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_desc_valid & ~grant; +assign acknowledge = grant & s_axis_desc_valid & s_axis_desc_ready; + +always @* begin + m_axis_desc_addr_int = current_s_desc_addr; + m_axis_desc_len_int = current_s_desc_len; + m_axis_desc_tag_int = {grant_encoded, current_s_desc_tag}; + m_axis_desc_id_int = current_s_desc_id; + m_axis_desc_dest_int = current_s_desc_dest; + m_axis_desc_user_int = current_s_desc_user; + m_axis_desc_valid_int = current_s_desc_valid && m_axis_desc_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] m_axis_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] m_axis_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg [AXIS_ID_WIDTH-1:0] m_axis_desc_id_reg = {AXIS_ID_WIDTH{1'b0}}; +reg [AXIS_DEST_WIDTH-1:0] m_axis_desc_dest_reg = {AXIS_DEST_WIDTH{1'b0}}; +reg [AXIS_USER_WIDTH-1:0] m_axis_desc_user_reg = {AXIS_USER_WIDTH{1'b0}}; +reg m_axis_desc_valid_reg = 1'b0, m_axis_desc_valid_next; + +reg [AXI_ADDR_WIDTH-1:0] temp_m_axis_desc_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] temp_m_axis_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] temp_m_axis_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg [AXIS_ID_WIDTH-1:0] temp_m_axis_desc_id_reg = {AXIS_ID_WIDTH{1'b0}}; +reg [AXIS_DEST_WIDTH-1:0] temp_m_axis_desc_dest_reg = {AXIS_DEST_WIDTH{1'b0}}; +reg [AXIS_USER_WIDTH-1:0] temp_m_axis_desc_user_reg = {AXIS_USER_WIDTH{1'b0}}; +reg temp_m_axis_desc_valid_reg = 1'b0, temp_m_axis_desc_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_desc_addr = m_axis_desc_addr_reg; +assign m_axis_desc_len = m_axis_desc_len_reg; +assign m_axis_desc_tag = m_axis_desc_tag_reg; +assign m_axis_desc_id = AXIS_ID_ENABLE ? m_axis_desc_id_reg : {AXIS_ID_WIDTH{1'b0}}; +assign m_axis_desc_dest = AXIS_DEST_ENABLE ? m_axis_desc_dest_reg : {AXIS_DEST_WIDTH{1'b0}}; +assign m_axis_desc_user = AXIS_USER_ENABLE ? m_axis_desc_user_reg : {AXIS_USER_WIDTH{1'b0}}; +assign m_axis_desc_valid = m_axis_desc_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_desc_ready_int_early = m_axis_desc_ready || (!temp_m_axis_desc_valid_reg && (!m_axis_desc_valid_reg || !m_axis_desc_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_desc_valid_next = m_axis_desc_valid_reg; + temp_m_axis_desc_valid_next = temp_m_axis_desc_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_desc_ready_int_reg) begin + // input is ready + if (m_axis_desc_ready || !m_axis_desc_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_desc_valid_next = m_axis_desc_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_desc_valid_next = m_axis_desc_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_desc_ready) begin + // input is not ready, but output is ready + m_axis_desc_valid_next = temp_m_axis_desc_valid_reg; + temp_m_axis_desc_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_desc_valid_reg <= 1'b0; + m_axis_desc_ready_int_reg <= 1'b0; + temp_m_axis_desc_valid_reg <= 1'b0; + end else begin + m_axis_desc_valid_reg <= m_axis_desc_valid_next; + m_axis_desc_ready_int_reg <= m_axis_desc_ready_int_early; + temp_m_axis_desc_valid_reg <= temp_m_axis_desc_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_desc_addr_reg <= m_axis_desc_addr_int; + m_axis_desc_len_reg <= m_axis_desc_len_int; + m_axis_desc_tag_reg <= m_axis_desc_tag_int; + m_axis_desc_id_reg <= m_axis_desc_id_int; + m_axis_desc_dest_reg <= m_axis_desc_dest_int; + m_axis_desc_user_reg <= m_axis_desc_user_int; + end else if (store_axis_temp_to_output) begin + m_axis_desc_addr_reg <= temp_m_axis_desc_addr_reg; + m_axis_desc_len_reg <= temp_m_axis_desc_len_reg; + m_axis_desc_tag_reg <= temp_m_axis_desc_tag_reg; + m_axis_desc_id_reg <= temp_m_axis_desc_id_reg; + m_axis_desc_dest_reg <= temp_m_axis_desc_dest_reg; + m_axis_desc_user_reg <= temp_m_axis_desc_user_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_desc_addr_reg <= m_axis_desc_addr_int; + temp_m_axis_desc_len_reg <= m_axis_desc_len_int; + temp_m_axis_desc_tag_reg <= m_axis_desc_tag_int; + temp_m_axis_desc_id_reg <= m_axis_desc_id_int; + temp_m_axis_desc_dest_reg <= m_axis_desc_dest_int; + temp_m_axis_desc_user_reg <= m_axis_desc_user_int; + end +end + +// descriptor status demux +reg [LEN_WIDTH-1:0] m_axis_desc_status_len_reg = {LEN_WIDTH{1'b0}}; +reg [S_TAG_WIDTH-1:0] m_axis_desc_status_tag_reg = {S_TAG_WIDTH{1'b0}}; +reg [AXIS_ID_WIDTH-1:0] m_axis_desc_status_id_reg = {AXIS_ID_WIDTH{1'b0}}; +reg [AXIS_DEST_WIDTH-1:0] m_axis_desc_status_dest_reg = {AXIS_DEST_WIDTH{1'b0}}; +reg [AXIS_USER_WIDTH-1:0] m_axis_desc_status_user_reg = {AXIS_USER_WIDTH{1'b0}}; +reg [PORTS-1:0] m_axis_desc_status_valid_reg = {PORTS{1'b0}}; + +assign m_axis_desc_status_len = {PORTS{m_axis_desc_status_len_reg}}; +assign m_axis_desc_status_tag = {PORTS{m_axis_desc_status_tag_reg}}; +assign m_axis_desc_status_id = AXIS_ID_ENABLE ? {PORTS{m_axis_desc_status_id_reg}} : {AXIS_ID_WIDTH*PORTS{1'b0}}; +assign m_axis_desc_status_dest = AXIS_DEST_ENABLE ? {PORTS{m_axis_desc_status_dest_reg}} : {AXIS_DEST_WIDTH*PORTS{1'b0}}; +assign m_axis_desc_status_user = AXIS_USER_ENABLE ? {PORTS{m_axis_desc_status_user_reg}} : {AXIS_USER_WIDTH*PORTS{1'b0}}; +assign m_axis_desc_status_valid = m_axis_desc_status_valid_reg; + +always @(posedge clk) begin + if (rst) begin + m_axis_desc_status_valid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_desc_status_valid_reg <= s_axis_desc_status_valid << (PORTS > 1 ? s_axis_desc_status_tag[S_TAG_WIDTH+CL_PORTS-1:S_TAG_WIDTH] : 0); + end + + m_axis_desc_status_len_reg <= s_axis_desc_status_len; + m_axis_desc_status_tag_reg <= s_axis_desc_status_tag; + m_axis_desc_status_id_reg <= s_axis_desc_status_id; + m_axis_desc_status_dest_reg <= s_axis_desc_status_dest; + m_axis_desc_status_user_reg <= s_axis_desc_status_user; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_dma_rd.v b/corundum/lib/axi/rtl/axi_dma_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..13379ef523cec99afdeff2371e4ea2b8ef02fdaa --- /dev/null +++ b/corundum/lib/axi/rtl/axi_dma_rd.v @@ -0,0 +1,671 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 DMA + */ +module axi_dma_rd # +( + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 16, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 16, + // Width of AXI stream interfaces in bits + parameter AXIS_DATA_WIDTH = AXI_DATA_WIDTH, + // Use AXI stream tkeep signal + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + // Use AXI stream tlast signal + parameter AXIS_LAST_ENABLE = 1, + // Propagate AXI stream tid signal + parameter AXIS_ID_ENABLE = 0, + // AXI stream tid signal width + parameter AXIS_ID_WIDTH = 8, + // Propagate AXI stream tdest signal + parameter AXIS_DEST_ENABLE = 0, + // AXI stream tdest signal width + parameter AXIS_DEST_WIDTH = 8, + // Propagate AXI stream tuser signal + parameter AXIS_USER_ENABLE = 1, + // AXI stream tuser signal width + parameter AXIS_USER_WIDTH = 1, + // Width of length field + parameter LEN_WIDTH = 20, + // Width of tag field + parameter TAG_WIDTH = 8, + // Enable support for scatter/gather DMA + // (multiple descriptors per AXI stream frame) + parameter ENABLE_SG = 0, + // Enable support for unaligned transfers + parameter ENABLE_UNALIGNED = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI read descriptor input + */ + input wire [AXI_ADDR_WIDTH-1:0] s_axis_read_desc_addr, + input wire [LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire [AXIS_ID_WIDTH-1:0] s_axis_read_desc_id, + input wire [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_dest, + input wire [AXIS_USER_WIDTH-1:0] s_axis_read_desc_user, + input wire s_axis_read_desc_valid, + output wire s_axis_read_desc_ready, + + /* + * AXI read descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire m_axis_read_desc_status_valid, + + /* + * AXI stream read data output + */ + output wire [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep, + output wire m_axis_read_data_tvalid, + input wire m_axis_read_data_tready, + output wire m_axis_read_data_tlast, + output wire [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid, + output wire [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest, + output wire [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser, + + /* + * AXI master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready, + + /* + * Configuration + */ + input wire enable +); + +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXI_MAX_BURST_SIZE = AXI_MAX_BURST_LEN << AXI_BURST_SIZE; + +parameter AXIS_KEEP_WIDTH_INT = AXIS_KEEP_ENABLE ? AXIS_KEEP_WIDTH : 1; +parameter AXIS_WORD_WIDTH = AXIS_KEEP_WIDTH_INT; +parameter AXIS_WORD_SIZE = AXIS_DATA_WIDTH/AXIS_WORD_WIDTH; + +parameter OFFSET_WIDTH = AXI_STRB_WIDTH > 1 ? $clog2(AXI_STRB_WIDTH) : 1; +parameter OFFSET_MASK = AXI_STRB_WIDTH > 1 ? {OFFSET_WIDTH{1'b1}} : 0; +parameter ADDR_MASK = {AXI_ADDR_WIDTH{1'b1}} << $clog2(AXI_STRB_WIDTH); +parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - AXI_BURST_SIZE + 1; + +// bus width assertions +initial begin + if (AXI_WORD_SIZE * AXI_STRB_WIDTH != AXI_DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXIS_WORD_SIZE * AXIS_KEEP_WIDTH_INT != AXIS_DATA_WIDTH) begin + $error("Error: AXI stream data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXI_WORD_SIZE != AXIS_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(AXI_WORD_WIDTH) != AXI_WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end + + if (AXI_DATA_WIDTH != AXIS_DATA_WIDTH) begin + $error("Error: AXI interface width must match AXI stream interface width (instance %m)"); + $finish; + end + + if (AXI_MAX_BURST_LEN < 1 || AXI_MAX_BURST_LEN > 256) begin + $error("Error: AXI_MAX_BURST_LEN must be between 1 and 256 (instance %m)"); + $finish; + end + + if (ENABLE_SG) begin + $error("Error: scatter/gather is not yet implemented (instance %m)"); + $finish; + end +end + +localparam [0:0] + AXI_STATE_IDLE = 1'd0, + AXI_STATE_START = 1'd1; + +reg [0:0] axi_state_reg = AXI_STATE_IDLE, axi_state_next; + +localparam [0:0] + AXIS_STATE_IDLE = 1'd0, + AXIS_STATE_READ = 1'd1; + +reg [0:0] axis_state_reg = AXIS_STATE_IDLE, axis_state_next; + +// datapath control signals +reg transfer_in_save; +reg axis_cmd_ready; + +reg [AXI_ADDR_WIDTH-1:0] addr_reg = {AXI_ADDR_WIDTH{1'b0}}, addr_next; +reg [LEN_WIDTH-1:0] op_word_count_reg = {LEN_WIDTH{1'b0}}, op_word_count_next; +reg [LEN_WIDTH-1:0] tr_word_count_reg = {LEN_WIDTH{1'b0}}, tr_word_count_next; + +reg [OFFSET_WIDTH-1:0] axis_cmd_offset_reg = {OFFSET_WIDTH{1'b0}}, axis_cmd_offset_next; +reg [OFFSET_WIDTH-1:0] axis_cmd_last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, axis_cmd_last_cycle_offset_next; +reg [CYCLE_COUNT_WIDTH-1:0] axis_cmd_input_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, axis_cmd_input_cycle_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] axis_cmd_output_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, axis_cmd_output_cycle_count_next; +reg axis_cmd_bubble_cycle_reg = 1'b0, axis_cmd_bubble_cycle_next; +reg [TAG_WIDTH-1:0] axis_cmd_tag_reg = {TAG_WIDTH{1'b0}}, axis_cmd_tag_next; +reg [AXIS_ID_WIDTH-1:0] axis_cmd_axis_id_reg = {AXIS_ID_WIDTH{1'b0}}, axis_cmd_axis_id_next; +reg [AXIS_DEST_WIDTH-1:0] axis_cmd_axis_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, axis_cmd_axis_dest_next; +reg [AXIS_USER_WIDTH-1:0] axis_cmd_axis_user_reg = {AXIS_USER_WIDTH{1'b0}}, axis_cmd_axis_user_next; +reg axis_cmd_valid_reg = 1'b0, axis_cmd_valid_next; + +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; +reg [OFFSET_WIDTH-1:0] last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, last_cycle_offset_next; +reg [CYCLE_COUNT_WIDTH-1:0] input_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, input_cycle_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] output_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, output_cycle_count_next; +reg input_active_reg = 1'b0, input_active_next; +reg output_active_reg = 1'b0, output_active_next; +reg bubble_cycle_reg = 1'b0, bubble_cycle_next; +reg first_cycle_reg = 1'b0, first_cycle_next; +reg output_last_cycle_reg = 1'b0, output_last_cycle_next; + +reg [TAG_WIDTH-1:0] tag_reg = {TAG_WIDTH{1'b0}}, tag_next; +reg [AXIS_ID_WIDTH-1:0] axis_id_reg = {AXIS_ID_WIDTH{1'b0}}, axis_id_next; +reg [AXIS_DEST_WIDTH-1:0] axis_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, axis_dest_next; +reg [AXIS_USER_WIDTH-1:0] axis_user_reg = {AXIS_USER_WIDTH{1'b0}}, axis_user_next; + +reg s_axis_read_desc_ready_reg = 1'b0, s_axis_read_desc_ready_next; + +reg [TAG_WIDTH-1:0] m_axis_read_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_read_desc_status_tag_next; +reg m_axis_read_desc_status_valid_reg = 1'b0, m_axis_read_desc_status_valid_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axi_araddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_araddr_next; +reg [7:0] m_axi_arlen_reg = 8'd0, m_axi_arlen_next; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; +reg m_axi_rready_reg = 1'b0, m_axi_rready_next; + +reg [AXI_DATA_WIDTH-1:0] save_axi_rdata_reg = {AXI_DATA_WIDTH{1'b0}}; + +wire [AXI_DATA_WIDTH-1:0] shift_axi_rdata = {m_axi_rdata, save_axi_rdata_reg} >> ((AXI_STRB_WIDTH-offset_reg)*AXI_WORD_SIZE); + +// internal datapath +reg [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata_int; +reg [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep_int; +reg m_axis_read_data_tvalid_int; +reg m_axis_read_data_tready_int_reg = 1'b0; +reg m_axis_read_data_tlast_int; +reg [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid_int; +reg [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest_int; +reg [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser_int; +wire m_axis_read_data_tready_int_early; + +assign s_axis_read_desc_ready = s_axis_read_desc_ready_reg; + +assign m_axis_read_desc_status_tag = m_axis_read_desc_status_tag_reg; +assign m_axis_read_desc_status_valid = m_axis_read_desc_status_valid_reg; + +assign m_axi_arid = {AXI_ID_WIDTH{1'b0}}; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = AXI_BURST_SIZE; +assign m_axi_arburst = 2'b01; +assign m_axi_arlock = 1'b0; +assign m_axi_arcache = 4'b0011; +assign m_axi_arprot = 3'b010; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +wire [AXI_ADDR_WIDTH-1:0] addr_plus_max_burst = addr_reg + AXI_MAX_BURST_SIZE; +wire [AXI_ADDR_WIDTH-1:0] addr_plus_count = addr_reg + op_word_count_reg; + +always @* begin + axi_state_next = AXI_STATE_IDLE; + + s_axis_read_desc_ready_next = 1'b0; + + m_axi_araddr_next = m_axi_araddr_reg; + m_axi_arlen_next = m_axi_arlen_reg; + m_axi_arvalid_next = m_axi_arvalid_reg && !m_axi_arready; + + addr_next = addr_reg; + op_word_count_next = op_word_count_reg; + tr_word_count_next = tr_word_count_reg; + + axis_cmd_offset_next = axis_cmd_offset_reg; + axis_cmd_last_cycle_offset_next = axis_cmd_last_cycle_offset_reg; + axis_cmd_input_cycle_count_next = axis_cmd_input_cycle_count_reg; + axis_cmd_output_cycle_count_next = axis_cmd_output_cycle_count_reg; + axis_cmd_bubble_cycle_next = axis_cmd_bubble_cycle_reg; + axis_cmd_tag_next = axis_cmd_tag_reg; + axis_cmd_axis_id_next = axis_cmd_axis_id_reg; + axis_cmd_axis_dest_next = axis_cmd_axis_dest_reg; + axis_cmd_axis_user_next = axis_cmd_axis_user_reg; + axis_cmd_valid_next = axis_cmd_valid_reg && !axis_cmd_ready; + + case (axi_state_reg) + AXI_STATE_IDLE: begin + // idle state - load new descriptor to start operation + s_axis_read_desc_ready_next = !axis_cmd_valid_reg && enable; + + if (s_axis_read_desc_ready && s_axis_read_desc_valid) begin + if (ENABLE_UNALIGNED) begin + addr_next = s_axis_read_desc_addr; + axis_cmd_offset_next = AXI_STRB_WIDTH > 1 ? AXI_STRB_WIDTH - (s_axis_read_desc_addr & OFFSET_MASK) : 0; + axis_cmd_bubble_cycle_next = axis_cmd_offset_next > 0; + axis_cmd_last_cycle_offset_next = s_axis_read_desc_len & OFFSET_MASK; + end else begin + addr_next = s_axis_read_desc_addr & ADDR_MASK; + axis_cmd_offset_next = 0; + axis_cmd_bubble_cycle_next = 1'b0; + axis_cmd_last_cycle_offset_next = s_axis_read_desc_len & OFFSET_MASK; + end + axis_cmd_tag_next = s_axis_read_desc_tag; + op_word_count_next = s_axis_read_desc_len; + + axis_cmd_axis_id_next = s_axis_read_desc_id; + axis_cmd_axis_dest_next = s_axis_read_desc_dest; + axis_cmd_axis_user_next = s_axis_read_desc_user; + + if (ENABLE_UNALIGNED) begin + axis_cmd_input_cycle_count_next = (op_word_count_next + (s_axis_read_desc_addr & OFFSET_MASK) - 1) >> AXI_BURST_SIZE; + end else begin + axis_cmd_input_cycle_count_next = (op_word_count_next - 1) >> AXI_BURST_SIZE; + end + axis_cmd_output_cycle_count_next = (op_word_count_next - 1) >> AXI_BURST_SIZE; + + axis_cmd_valid_next = 1'b1; + + s_axis_read_desc_ready_next = 1'b0; + axi_state_next = AXI_STATE_START; + end else begin + axi_state_next = AXI_STATE_IDLE; + end + end + AXI_STATE_START: begin + // start state - initiate new AXI transfer + if (!m_axi_arvalid) begin + if (op_word_count_reg <= AXI_MAX_BURST_SIZE - (addr_reg & OFFSET_MASK) || AXI_MAX_BURST_SIZE >= 4096) begin + // packet smaller than max burst size + if (addr_reg[12] != addr_plus_count[12]) begin + // crosses 4k boundary + tr_word_count_next = 13'h1000 - addr_reg[11:0]; + end else begin + // does not cross 4k boundary + tr_word_count_next = op_word_count_reg; + end + end else begin + // packet larger than max burst size + if (addr_reg[12] != addr_plus_max_burst[12]) begin + // crosses 4k boundary + tr_word_count_next = 13'h1000 - addr_reg[11:0]; + end else begin + // does not cross 4k boundary + tr_word_count_next = AXI_MAX_BURST_SIZE - (addr_reg & OFFSET_MASK); + end + end + + m_axi_araddr_next = addr_reg; + if (ENABLE_UNALIGNED) begin + m_axi_arlen_next = (tr_word_count_next + (addr_reg & OFFSET_MASK) - 1) >> AXI_BURST_SIZE; + end else begin + m_axi_arlen_next = (tr_word_count_next - 1) >> AXI_BURST_SIZE; + end + m_axi_arvalid_next = 1'b1; + + addr_next = addr_reg + tr_word_count_next; + op_word_count_next = op_word_count_reg - tr_word_count_next; + + if (op_word_count_next > 0) begin + axi_state_next = AXI_STATE_START; + end else begin + s_axis_read_desc_ready_next = !axis_cmd_valid_reg && enable; + axi_state_next = AXI_STATE_IDLE; + end + end else begin + axi_state_next = AXI_STATE_START; + end + end + endcase +end + +always @* begin + axis_state_next = AXIS_STATE_IDLE; + + m_axis_read_desc_status_tag_next = m_axis_read_desc_status_tag_reg; + m_axis_read_desc_status_valid_next = 1'b0; + + m_axis_read_data_tdata_int = shift_axi_rdata; + m_axis_read_data_tkeep_int = {AXIS_KEEP_WIDTH{1'b1}}; + m_axis_read_data_tlast_int = 1'b0; + m_axis_read_data_tvalid_int = 1'b0; + m_axis_read_data_tid_int = axis_id_reg; + m_axis_read_data_tdest_int = axis_dest_reg; + m_axis_read_data_tuser_int = axis_user_reg; + + m_axi_rready_next = 1'b0; + + transfer_in_save = 1'b0; + axis_cmd_ready = 1'b0; + + offset_next = offset_reg; + last_cycle_offset_next = last_cycle_offset_reg; + input_cycle_count_next = input_cycle_count_reg; + output_cycle_count_next = output_cycle_count_reg; + input_active_next = input_active_reg; + output_active_next = output_active_reg; + bubble_cycle_next = bubble_cycle_reg; + first_cycle_next = first_cycle_reg; + output_last_cycle_next = output_last_cycle_reg; + + tag_next = tag_reg; + axis_id_next = axis_id_reg; + axis_dest_next = axis_dest_reg; + axis_user_next = axis_user_reg; + + case (axis_state_reg) + AXIS_STATE_IDLE: begin + // idle state - load new descriptor to start operation + m_axi_rready_next = 1'b0; + + // store transfer parameters + if (ENABLE_UNALIGNED) begin + offset_next = axis_cmd_offset_reg; + end else begin + offset_next = 0; + end + last_cycle_offset_next = axis_cmd_last_cycle_offset_reg; + input_cycle_count_next = axis_cmd_input_cycle_count_reg; + output_cycle_count_next = axis_cmd_output_cycle_count_reg; + bubble_cycle_next = axis_cmd_bubble_cycle_reg; + tag_next = axis_cmd_tag_reg; + axis_id_next = axis_cmd_axis_id_reg; + axis_dest_next = axis_cmd_axis_dest_reg; + axis_user_next = axis_cmd_axis_user_reg; + + output_last_cycle_next = output_cycle_count_next == 0; + input_active_next = 1'b1; + output_active_next = 1'b1; + first_cycle_next = 1'b1; + + if (axis_cmd_valid_reg) begin + axis_cmd_ready = 1'b1; + m_axi_rready_next = m_axis_read_data_tready_int_early; + axis_state_next = AXIS_STATE_READ; + end + end + AXIS_STATE_READ: begin + // handle AXI read data + m_axi_rready_next = m_axis_read_data_tready_int_early && input_active_reg; + + if (m_axis_read_data_tready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + // transfer in AXI read data + transfer_in_save = m_axi_rready && m_axi_rvalid; + + if (ENABLE_UNALIGNED && first_cycle_reg && bubble_cycle_reg) begin + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + bubble_cycle_next = 1'b0; + first_cycle_next = 1'b0; + + m_axi_rready_next = m_axis_read_data_tready_int_early && input_active_next; + axis_state_next = AXIS_STATE_READ; + end else begin + // update counters + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + if (output_active_reg) begin + output_cycle_count_next = output_cycle_count_reg - 1; + output_active_next = output_cycle_count_reg > 0; + end + output_last_cycle_next = output_cycle_count_next == 0; + bubble_cycle_next = 1'b0; + first_cycle_next = 1'b0; + + // pass through read data + m_axis_read_data_tdata_int = shift_axi_rdata; + m_axis_read_data_tkeep_int = {AXIS_KEEP_WIDTH_INT{1'b1}}; + m_axis_read_data_tvalid_int = 1'b1; + + if (output_last_cycle_reg) begin + // no more data to transfer, finish operation + if (last_cycle_offset_reg > 0) begin + m_axis_read_data_tkeep_int = {AXIS_KEEP_WIDTH_INT{1'b1}} >> (AXIS_KEEP_WIDTH_INT - last_cycle_offset_reg); + end + m_axis_read_data_tlast_int = 1'b1; + + m_axis_read_desc_status_tag_next = tag_reg; + m_axis_read_desc_status_valid_next = 1'b1; + + m_axi_rready_next = 1'b0; + axis_state_next = AXIS_STATE_IDLE; + end else begin + // more cycles in AXI transfer + m_axi_rready_next = m_axis_read_data_tready_int_early && input_active_next; + axis_state_next = AXIS_STATE_READ; + end + end + end else begin + axis_state_next = AXIS_STATE_READ; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + axi_state_reg <= AXI_STATE_IDLE; + axis_state_reg <= AXIS_STATE_IDLE; + axis_cmd_valid_reg <= 1'b0; + s_axis_read_desc_ready_reg <= 1'b0; + m_axis_read_desc_status_valid_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + m_axi_rready_reg <= 1'b0; + end else begin + axi_state_reg <= axi_state_next; + axis_state_reg <= axis_state_next; + axis_cmd_valid_reg <= axis_cmd_valid_next; + s_axis_read_desc_ready_reg <= s_axis_read_desc_ready_next; + m_axis_read_desc_status_valid_reg <= m_axis_read_desc_status_valid_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + end + + m_axis_read_desc_status_tag_reg <= m_axis_read_desc_status_tag_next; + + m_axi_araddr_reg <= m_axi_araddr_next; + m_axi_arlen_reg <= m_axi_arlen_next; + + addr_reg <= addr_next; + op_word_count_reg <= op_word_count_next; + tr_word_count_reg <= tr_word_count_next; + + axis_cmd_offset_reg <= axis_cmd_offset_next; + axis_cmd_last_cycle_offset_reg <= axis_cmd_last_cycle_offset_next; + axis_cmd_input_cycle_count_reg <= axis_cmd_input_cycle_count_next; + axis_cmd_output_cycle_count_reg <= axis_cmd_output_cycle_count_next; + axis_cmd_bubble_cycle_reg <= axis_cmd_bubble_cycle_next; + axis_cmd_tag_reg <= axis_cmd_tag_next; + axis_cmd_axis_id_reg <= axis_cmd_axis_id_next; + axis_cmd_axis_dest_reg <= axis_cmd_axis_dest_next; + axis_cmd_axis_user_reg <= axis_cmd_axis_user_next; + axis_cmd_valid_reg <= axis_cmd_valid_next; + + offset_reg <= offset_next; + last_cycle_offset_reg <= last_cycle_offset_next; + input_cycle_count_reg <= input_cycle_count_next; + output_cycle_count_reg <= output_cycle_count_next; + input_active_reg <= input_active_next; + output_active_reg <= output_active_next; + bubble_cycle_reg <= bubble_cycle_next; + first_cycle_reg <= first_cycle_next; + output_last_cycle_reg <= output_last_cycle_next; + + tag_reg <= tag_next; + axis_id_reg <= axis_id_next; + axis_dest_reg <= axis_dest_next; + axis_user_reg <= axis_user_next; + + if (transfer_in_save) begin + save_axi_rdata_reg <= m_axi_rdata; + end +end + +// output datapath logic +reg [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; +reg [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; +reg m_axis_read_data_tvalid_reg = 1'b0, m_axis_read_data_tvalid_next; +reg m_axis_read_data_tlast_reg = 1'b0; +reg [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid_reg = {AXIS_ID_WIDTH{1'b0}}; +reg [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest_reg = {AXIS_DEST_WIDTH{1'b0}}; +reg [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser_reg = {AXIS_USER_WIDTH{1'b0}}; + +reg [AXIS_DATA_WIDTH-1:0] temp_m_axis_read_data_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; +reg [AXIS_KEEP_WIDTH-1:0] temp_m_axis_read_data_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_read_data_tvalid_reg = 1'b0, temp_m_axis_read_data_tvalid_next; +reg temp_m_axis_read_data_tlast_reg = 1'b0; +reg [AXIS_ID_WIDTH-1:0] temp_m_axis_read_data_tid_reg = {AXIS_ID_WIDTH{1'b0}}; +reg [AXIS_DEST_WIDTH-1:0] temp_m_axis_read_data_tdest_reg = {AXIS_DEST_WIDTH{1'b0}}; +reg [AXIS_USER_WIDTH-1:0] temp_m_axis_read_data_tuser_reg = {AXIS_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_read_data_tdata = m_axis_read_data_tdata_reg; +assign m_axis_read_data_tkeep = AXIS_KEEP_ENABLE ? m_axis_read_data_tkeep_reg : {AXIS_KEEP_WIDTH{1'b1}}; +assign m_axis_read_data_tvalid = m_axis_read_data_tvalid_reg; +assign m_axis_read_data_tlast = AXIS_LAST_ENABLE ? m_axis_read_data_tlast_reg : 1'b1; +assign m_axis_read_data_tid = AXIS_ID_ENABLE ? m_axis_read_data_tid_reg : {AXIS_ID_WIDTH{1'b0}}; +assign m_axis_read_data_tdest = AXIS_DEST_ENABLE ? m_axis_read_data_tdest_reg : {AXIS_DEST_WIDTH{1'b0}}; +assign m_axis_read_data_tuser = AXIS_USER_ENABLE ? m_axis_read_data_tuser_reg : {AXIS_USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_read_data_tready_int_early = m_axis_read_data_tready || (!temp_m_axis_read_data_tvalid_reg && (!m_axis_read_data_tvalid_reg || !m_axis_read_data_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_reg; + temp_m_axis_read_data_tvalid_next = temp_m_axis_read_data_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_read_data_tready_int_reg) begin + // input is ready + if (m_axis_read_data_tready || !m_axis_read_data_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_read_data_tready) begin + // input is not ready, but output is ready + m_axis_read_data_tvalid_next = temp_m_axis_read_data_tvalid_reg; + temp_m_axis_read_data_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_read_data_tvalid_reg <= 1'b0; + m_axis_read_data_tready_int_reg <= 1'b0; + temp_m_axis_read_data_tvalid_reg <= 1'b0; + end else begin + m_axis_read_data_tvalid_reg <= m_axis_read_data_tvalid_next; + m_axis_read_data_tready_int_reg <= m_axis_read_data_tready_int_early; + temp_m_axis_read_data_tvalid_reg <= temp_m_axis_read_data_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_read_data_tdata_reg <= m_axis_read_data_tdata_int; + m_axis_read_data_tkeep_reg <= m_axis_read_data_tkeep_int; + m_axis_read_data_tlast_reg <= m_axis_read_data_tlast_int; + m_axis_read_data_tid_reg <= m_axis_read_data_tid_int; + m_axis_read_data_tdest_reg <= m_axis_read_data_tdest_int; + m_axis_read_data_tuser_reg <= m_axis_read_data_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_read_data_tdata_reg <= temp_m_axis_read_data_tdata_reg; + m_axis_read_data_tkeep_reg <= temp_m_axis_read_data_tkeep_reg; + m_axis_read_data_tlast_reg <= temp_m_axis_read_data_tlast_reg; + m_axis_read_data_tid_reg <= temp_m_axis_read_data_tid_reg; + m_axis_read_data_tdest_reg <= temp_m_axis_read_data_tdest_reg; + m_axis_read_data_tuser_reg <= temp_m_axis_read_data_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_read_data_tdata_reg <= m_axis_read_data_tdata_int; + temp_m_axis_read_data_tkeep_reg <= m_axis_read_data_tkeep_int; + temp_m_axis_read_data_tlast_reg <= m_axis_read_data_tlast_int; + temp_m_axis_read_data_tid_reg <= m_axis_read_data_tid_int; + temp_m_axis_read_data_tdest_reg <= m_axis_read_data_tdest_int; + temp_m_axis_read_data_tuser_reg <= m_axis_read_data_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_dma_wr.v b/corundum/lib/axi/rtl/axi_dma_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..5dc25a5c71d99378dd2412220b6e68bf19229abe --- /dev/null +++ b/corundum/lib/axi/rtl/axi_dma_wr.v @@ -0,0 +1,899 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 DMA + */ +module axi_dma_wr # +( + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 16, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 16, + // Width of AXI stream interfaces in bits + parameter AXIS_DATA_WIDTH = AXI_DATA_WIDTH, + // Use AXI stream tkeep signal + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + // Use AXI stream tlast signal + parameter AXIS_LAST_ENABLE = 1, + // Propagate AXI stream tid signal + parameter AXIS_ID_ENABLE = 0, + // AXI stream tid signal width + parameter AXIS_ID_WIDTH = 8, + // Propagate AXI stream tdest signal + parameter AXIS_DEST_ENABLE = 0, + // AXI stream tdest signal width + parameter AXIS_DEST_WIDTH = 8, + // Propagate AXI stream tuser signal + parameter AXIS_USER_ENABLE = 1, + // AXI stream tuser signal width + parameter AXIS_USER_WIDTH = 1, + // Width of length field + parameter LEN_WIDTH = 20, + // Width of tag field + parameter TAG_WIDTH = 8, + // Enable support for scatter/gather DMA + // (multiple descriptors per AXI stream frame) + parameter ENABLE_SG = 0, + // Enable support for unaligned transfers + parameter ENABLE_UNALIGNED = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI write descriptor input + */ + input wire [AXI_ADDR_WIDTH-1:0] s_axis_write_desc_addr, + input wire [LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire s_axis_write_desc_valid, + output wire s_axis_write_desc_ready, + + /* + * AXI write descriptor status output + */ + output wire [LEN_WIDTH-1:0] m_axis_write_desc_status_len, + output wire [TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id, + output wire [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest, + output wire [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user, + output wire m_axis_write_desc_status_valid, + + /* + * AXI stream write data input + */ + input wire [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tkeep, + input wire s_axis_write_data_tvalid, + output wire s_axis_write_data_tready, + input wire s_axis_write_data_tlast, + input wire [AXIS_ID_WIDTH-1:0] s_axis_write_data_tid, + input wire [AXIS_DEST_WIDTH-1:0] s_axis_write_data_tdest, + input wire [AXIS_USER_WIDTH-1:0] s_axis_write_data_tuser, + + /* + * AXI master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + + /* + * Configuration + */ + input wire enable, + input wire abort +); + +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXI_MAX_BURST_SIZE = AXI_MAX_BURST_LEN << AXI_BURST_SIZE; + +parameter AXIS_KEEP_WIDTH_INT = AXIS_KEEP_ENABLE ? AXIS_KEEP_WIDTH : 1; +parameter AXIS_WORD_WIDTH = AXIS_KEEP_WIDTH_INT; +parameter AXIS_WORD_SIZE = AXIS_DATA_WIDTH/AXIS_WORD_WIDTH; + +parameter OFFSET_WIDTH = AXI_STRB_WIDTH > 1 ? $clog2(AXI_STRB_WIDTH) : 1; +parameter OFFSET_MASK = AXI_STRB_WIDTH > 1 ? {OFFSET_WIDTH{1'b1}} : 0; +parameter ADDR_MASK = {AXI_ADDR_WIDTH{1'b1}} << $clog2(AXI_STRB_WIDTH); +parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - AXI_BURST_SIZE + 1; + +parameter STATUS_FIFO_ADDR_WIDTH = 5; + +// bus width assertions +initial begin + if (AXI_WORD_SIZE * AXI_STRB_WIDTH != AXI_DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXIS_WORD_SIZE * AXIS_KEEP_WIDTH_INT != AXIS_DATA_WIDTH) begin + $error("Error: AXI stream data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXI_WORD_SIZE != AXIS_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(AXI_WORD_WIDTH) != AXI_WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end + + if (AXI_DATA_WIDTH != AXIS_DATA_WIDTH) begin + $error("Error: AXI interface width must match AXI stream interface width (instance %m)"); + $finish; + end + + if (AXI_MAX_BURST_LEN < 1 || AXI_MAX_BURST_LEN > 256) begin + $error("Error: AXI_MAX_BURST_LEN must be between 1 and 256 (instance %m)"); + $finish; + end + + if (ENABLE_SG) begin + $error("Error: scatter/gather is not yet implemented (instance %m)"); + $finish; + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_START = 3'd1, + STATE_WRITE = 3'd2, + STATE_FINISH_BURST = 3'd3, + STATE_DROP_DATA = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg transfer_in_save; +reg flush_save; +reg status_fifo_we; + +integer i; +reg [OFFSET_WIDTH:0] cycle_size; + +reg [AXI_ADDR_WIDTH-1:0] addr_reg = {AXI_ADDR_WIDTH{1'b0}}, addr_next; +reg [LEN_WIDTH-1:0] op_word_count_reg = {LEN_WIDTH{1'b0}}, op_word_count_next; +reg [LEN_WIDTH-1:0] tr_word_count_reg = {LEN_WIDTH{1'b0}}, tr_word_count_next; + +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; +reg [AXI_STRB_WIDTH-1:0] strb_offset_mask_reg = {AXI_STRB_WIDTH{1'b1}}, strb_offset_mask_next; +reg zero_offset_reg = 1'b1, zero_offset_next; +reg [OFFSET_WIDTH-1:0] last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, last_cycle_offset_next; +reg [LEN_WIDTH-1:0] length_reg = {LEN_WIDTH{1'b0}}, length_next; +reg [CYCLE_COUNT_WIDTH-1:0] input_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, input_cycle_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] output_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, output_cycle_count_next; +reg input_active_reg = 1'b0, input_active_next; +reg first_cycle_reg = 1'b0, first_cycle_next; +reg input_last_cycle_reg = 1'b0, input_last_cycle_next; +reg output_last_cycle_reg = 1'b0, output_last_cycle_next; +reg last_transfer_reg = 1'b0, last_transfer_next; + +reg [TAG_WIDTH-1:0] tag_reg = {TAG_WIDTH{1'b0}}, tag_next; +reg [AXIS_ID_WIDTH-1:0] axis_id_reg = {AXIS_ID_WIDTH{1'b0}}, axis_id_next; +reg [AXIS_DEST_WIDTH-1:0] axis_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, axis_dest_next; +reg [AXIS_USER_WIDTH-1:0] axis_user_reg = {AXIS_USER_WIDTH{1'b0}}, axis_user_next; + +reg [STATUS_FIFO_ADDR_WIDTH+1-1:0] status_fifo_wr_ptr_reg = 0, status_fifo_wr_ptr_next; +reg [STATUS_FIFO_ADDR_WIDTH+1-1:0] status_fifo_rd_ptr_reg = 0, status_fifo_rd_ptr_next; +reg [LEN_WIDTH-1:0] status_fifo_len[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg [TAG_WIDTH-1:0] status_fifo_tag[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg [AXIS_ID_WIDTH-1:0] status_fifo_id[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg [AXIS_DEST_WIDTH-1:0] status_fifo_dest[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg [AXIS_USER_WIDTH-1:0] status_fifo_user[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg status_fifo_last[(2**STATUS_FIFO_ADDR_WIDTH)-1:0]; +reg [LEN_WIDTH-1:0] status_fifo_wr_len; +reg [TAG_WIDTH-1:0] status_fifo_wr_tag; +reg [AXIS_ID_WIDTH-1:0] status_fifo_wr_id; +reg [AXIS_DEST_WIDTH-1:0] status_fifo_wr_dest; +reg [AXIS_USER_WIDTH-1:0] status_fifo_wr_user; +reg status_fifo_wr_last; + +reg s_axis_write_desc_ready_reg = 1'b0, s_axis_write_desc_ready_next; + +reg [LEN_WIDTH-1:0] m_axis_write_desc_status_len_reg = {LEN_WIDTH{1'b0}}, m_axis_write_desc_status_len_next; +reg [TAG_WIDTH-1:0] m_axis_write_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_write_desc_status_tag_next; +reg [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id_reg = {AXIS_ID_WIDTH{1'b0}}, m_axis_write_desc_status_id_next; +reg [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, m_axis_write_desc_status_dest_next; +reg [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user_reg = {AXIS_USER_WIDTH{1'b0}}, m_axis_write_desc_status_user_next; +reg m_axis_write_desc_status_valid_reg = 1'b0, m_axis_write_desc_status_valid_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axi_awaddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_awaddr_next; +reg [7:0] m_axi_awlen_reg = 8'd0, m_axi_awlen_next; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; +reg m_axi_bready_reg = 1'b0, m_axi_bready_next; + +reg s_axis_write_data_tready_reg = 1'b0, s_axis_write_data_tready_next; + +reg [AXIS_DATA_WIDTH-1:0] save_axis_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; +reg [AXIS_KEEP_WIDTH_INT-1:0] save_axis_tkeep_reg = {AXIS_KEEP_WIDTH_INT{1'b0}}; +reg save_axis_tlast_reg = 1'b0; + +reg [AXIS_DATA_WIDTH-1:0] shift_axis_tdata; +reg [AXIS_KEEP_WIDTH_INT-1:0] shift_axis_tkeep; +reg shift_axis_tvalid; +reg shift_axis_tlast; +reg shift_axis_input_tready; +reg shift_axis_extra_cycle_reg = 1'b0; + +// internal datapath +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_int; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wlast_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +wire m_axi_wready_int_early; + +assign s_axis_write_desc_ready = s_axis_write_desc_ready_reg; + +assign m_axis_write_desc_status_len = m_axis_write_desc_status_len_reg; +assign m_axis_write_desc_status_tag = m_axis_write_desc_status_tag_reg; +assign m_axis_write_desc_status_id = m_axis_write_desc_status_id_reg; +assign m_axis_write_desc_status_dest = m_axis_write_desc_status_dest_reg; +assign m_axis_write_desc_status_user = m_axis_write_desc_status_user_reg; +assign m_axis_write_desc_status_valid = m_axis_write_desc_status_valid_reg; + +assign s_axis_write_data_tready = s_axis_write_data_tready_reg; + +assign m_axi_awid = {AXI_ID_WIDTH{1'b0}}; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = AXI_BURST_SIZE; +assign m_axi_awburst = 2'b01; +assign m_axi_awlock = 1'b0; +assign m_axi_awcache = 4'b0011; +assign m_axi_awprot = 3'b010; +assign m_axi_awvalid = m_axi_awvalid_reg; +assign m_axi_bready = m_axi_bready_reg; + +wire [AXI_ADDR_WIDTH-1:0] addr_plus_max_burst = addr_reg + AXI_MAX_BURST_SIZE; +wire [AXI_ADDR_WIDTH-1:0] addr_plus_count = addr_reg + op_word_count_reg; + +always @* begin + if (!ENABLE_UNALIGNED || zero_offset_reg) begin + // passthrough if no overlap + shift_axis_tdata = s_axis_write_data_tdata; + shift_axis_tkeep = s_axis_write_data_tkeep; + shift_axis_tvalid = s_axis_write_data_tvalid; + shift_axis_tlast = AXIS_LAST_ENABLE && s_axis_write_data_tlast; + shift_axis_input_tready = 1'b1; + end else if (!AXIS_LAST_ENABLE) begin + shift_axis_tdata = {s_axis_write_data_tdata, save_axis_tdata_reg} >> ((AXIS_KEEP_WIDTH_INT-offset_reg)*AXIS_WORD_SIZE); + shift_axis_tkeep = {s_axis_write_data_tkeep, save_axis_tkeep_reg} >> (AXIS_KEEP_WIDTH_INT-offset_reg); + shift_axis_tvalid = s_axis_write_data_tvalid; + shift_axis_tlast = 1'b0; + shift_axis_input_tready = 1'b1; + end else if (shift_axis_extra_cycle_reg) begin + shift_axis_tdata = {s_axis_write_data_tdata, save_axis_tdata_reg} >> ((AXIS_KEEP_WIDTH_INT-offset_reg)*AXIS_WORD_SIZE); + shift_axis_tkeep = {{AXIS_KEEP_WIDTH_INT{1'b0}}, save_axis_tkeep_reg} >> (AXIS_KEEP_WIDTH_INT-offset_reg); + shift_axis_tvalid = 1'b1; + shift_axis_tlast = save_axis_tlast_reg; + shift_axis_input_tready = flush_save; + end else begin + shift_axis_tdata = {s_axis_write_data_tdata, save_axis_tdata_reg} >> ((AXIS_KEEP_WIDTH_INT-offset_reg)*AXIS_WORD_SIZE); + shift_axis_tkeep = {s_axis_write_data_tkeep, save_axis_tkeep_reg} >> (AXIS_KEEP_WIDTH_INT-offset_reg); + shift_axis_tvalid = s_axis_write_data_tvalid; + shift_axis_tlast = (s_axis_write_data_tlast && ((s_axis_write_data_tkeep & ({AXIS_KEEP_WIDTH_INT{1'b1}} << (AXIS_KEEP_WIDTH_INT-offset_reg))) == 0)); + shift_axis_input_tready = 1'b1; + end +end + +always @* begin + state_next = STATE_IDLE; + + s_axis_write_desc_ready_next = 1'b0; + + m_axis_write_desc_status_len_next = m_axis_write_desc_status_len_reg; + m_axis_write_desc_status_tag_next = m_axis_write_desc_status_tag_reg; + m_axis_write_desc_status_id_next = m_axis_write_desc_status_id_reg; + m_axis_write_desc_status_dest_next = m_axis_write_desc_status_dest_reg; + m_axis_write_desc_status_user_next = m_axis_write_desc_status_user_reg; + m_axis_write_desc_status_valid_next = 1'b0; + + s_axis_write_data_tready_next = 1'b0; + + m_axi_awaddr_next = m_axi_awaddr_reg; + m_axi_awlen_next = m_axi_awlen_reg; + m_axi_awvalid_next = m_axi_awvalid_reg && !m_axi_awready; + m_axi_wdata_int = shift_axis_tdata; + m_axi_wstrb_int = shift_axis_tkeep; + m_axi_wlast_int = 1'b0; + m_axi_wvalid_int = 1'b0; + m_axi_bready_next = 1'b0; + + transfer_in_save = 1'b0; + flush_save = 1'b0; + status_fifo_we = 1'b0; + + cycle_size = AXIS_KEEP_WIDTH_INT; + + addr_next = addr_reg; + offset_next = offset_reg; + strb_offset_mask_next = strb_offset_mask_reg; + zero_offset_next = zero_offset_reg; + last_cycle_offset_next = last_cycle_offset_reg; + length_next = length_reg; + op_word_count_next = op_word_count_reg; + tr_word_count_next = tr_word_count_reg; + input_cycle_count_next = input_cycle_count_reg; + output_cycle_count_next = output_cycle_count_reg; + input_active_next = input_active_reg; + first_cycle_next = first_cycle_reg; + input_last_cycle_next = input_last_cycle_reg; + output_last_cycle_next = output_last_cycle_reg; + last_transfer_next = last_transfer_reg; + + status_fifo_rd_ptr_next = status_fifo_rd_ptr_reg; + + tag_next = tag_reg; + axis_id_next = axis_id_reg; + axis_dest_next = axis_dest_reg; + axis_user_next = axis_user_reg; + + status_fifo_wr_len = length_reg; + status_fifo_wr_tag = tag_reg; + status_fifo_wr_id = axis_id_reg; + status_fifo_wr_dest = axis_dest_reg; + status_fifo_wr_user = axis_user_reg; + status_fifo_wr_last = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - load new descriptor to start operation + flush_save = 1'b1; + s_axis_write_desc_ready_next = enable; + + if (ENABLE_UNALIGNED) begin + addr_next = s_axis_write_desc_addr; + offset_next = s_axis_write_desc_addr & OFFSET_MASK; + strb_offset_mask_next = {AXI_STRB_WIDTH{1'b1}} << (s_axis_write_desc_addr & OFFSET_MASK); + zero_offset_next = (s_axis_write_desc_addr & OFFSET_MASK) == 0; + last_cycle_offset_next = offset_next + (s_axis_write_desc_len & OFFSET_MASK); + end else begin + addr_next = s_axis_write_desc_addr & ADDR_MASK; + offset_next = 0; + strb_offset_mask_next = {AXI_STRB_WIDTH{1'b1}}; + zero_offset_next = 1'b1; + last_cycle_offset_next = offset_next + (s_axis_write_desc_len & OFFSET_MASK); + end + tag_next = s_axis_write_desc_tag; + op_word_count_next = s_axis_write_desc_len; + first_cycle_next = 1'b1; + length_next = 0; + + if (s_axis_write_desc_ready && s_axis_write_desc_valid) begin + s_axis_write_desc_ready_next = 1'b0; + state_next = STATE_START; + end else begin + state_next = STATE_IDLE; + end + end + STATE_START: begin + // start state - initiate new AXI transfer + if (op_word_count_reg <= AXI_MAX_BURST_SIZE - (addr_reg & OFFSET_MASK) || AXI_MAX_BURST_SIZE >= 4096) begin + // packet smaller than max burst size + if (addr_reg[12] != addr_plus_count[12]) begin + // crosses 4k boundary + tr_word_count_next = 13'h1000 - addr_reg[11:0]; + end else begin + // does not cross 4k boundary + tr_word_count_next = op_word_count_reg; + end + end else begin + // packet larger than max burst size + if (addr_reg[12] != addr_plus_max_burst[12]) begin + // crosses 4k boundary + tr_word_count_next = 13'h1000 - addr_reg[11:0]; + end else begin + // does not cross 4k boundary + tr_word_count_next = AXI_MAX_BURST_SIZE - (addr_reg & OFFSET_MASK); + end + end + + input_cycle_count_next = (tr_word_count_next - 1) >> $clog2(AXIS_KEEP_WIDTH_INT); + input_last_cycle_next = input_cycle_count_next == 0; + if (ENABLE_UNALIGNED) begin + output_cycle_count_next = (tr_word_count_next + (addr_reg & OFFSET_MASK) - 1) >> AXI_BURST_SIZE; + end else begin + output_cycle_count_next = (tr_word_count_next - 1) >> AXI_BURST_SIZE; + end + output_last_cycle_next = output_cycle_count_next == 0; + last_transfer_next = tr_word_count_next == op_word_count_reg; + input_active_next = 1'b1; + + if (ENABLE_UNALIGNED) begin + if (!first_cycle_reg && last_transfer_next) begin + if (offset_reg >= last_cycle_offset_reg && last_cycle_offset_reg > 0) begin + // last cycle will be served by stored partial cycle + input_active_next = input_cycle_count_next > 0; + input_cycle_count_next = input_cycle_count_next - 1; + end + end + end + + if (!m_axi_awvalid_reg) begin + m_axi_awaddr_next = addr_reg; + m_axi_awlen_next = output_cycle_count_next; + m_axi_awvalid_next = s_axis_write_data_tvalid || !first_cycle_reg; + + if (m_axi_awvalid_next) begin + addr_next = addr_reg + tr_word_count_next; + op_word_count_next = op_word_count_reg - tr_word_count_next; + + s_axis_write_data_tready_next = m_axi_wready_int_early && input_active_next; + state_next = STATE_WRITE; + end else begin + state_next = STATE_START; + end + end else begin + state_next = STATE_START; + end + end + STATE_WRITE: begin + s_axis_write_data_tready_next = m_axi_wready_int_early && (last_transfer_reg || input_active_reg) && shift_axis_input_tready; + + if (m_axi_wready_int_reg && ((s_axis_write_data_tready && shift_axis_tvalid) || (!input_active_reg && !last_transfer_reg) || !shift_axis_input_tready)) begin + if (s_axis_write_data_tready && s_axis_write_data_tvalid) begin + transfer_in_save = 1'b1; + + axis_id_next = s_axis_write_data_tid; + axis_dest_next = s_axis_write_data_tdest; + axis_user_next = s_axis_write_data_tuser; + end + + // update counters + if (first_cycle_reg) begin + length_next = length_reg + (AXIS_KEEP_WIDTH_INT - offset_reg); + end else begin + length_next = length_reg + AXIS_KEEP_WIDTH_INT; + end + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + input_last_cycle_next = input_cycle_count_next == 0; + output_cycle_count_next = output_cycle_count_reg - 1; + output_last_cycle_next = output_cycle_count_next == 0; + first_cycle_next = 1'b0; + strb_offset_mask_next = {AXI_STRB_WIDTH{1'b1}}; + + m_axi_wdata_int = shift_axis_tdata; + m_axi_wstrb_int = strb_offset_mask_reg; + m_axi_wvalid_int = 1'b1; + + if (AXIS_LAST_ENABLE && s_axis_write_data_tlast) begin + // end of input frame + input_active_next = 1'b0; + s_axis_write_data_tready_next = 1'b0; + end + + if (AXIS_LAST_ENABLE && shift_axis_tlast) begin + // end of data packet + + if (AXIS_KEEP_ENABLE) begin + cycle_size = AXIS_KEEP_WIDTH_INT; + for (i = AXIS_KEEP_WIDTH_INT-1; i >= 0; i = i - 1) begin + if (~shift_axis_tkeep & strb_offset_mask_reg & (1 << i)) begin + cycle_size = i; + end + end + end else begin + cycle_size = AXIS_KEEP_WIDTH_INT; + end + + if (output_last_cycle_reg) begin + m_axi_wlast_int = 1'b1; + + // no more data to transfer, finish operation + if (last_transfer_reg && last_cycle_offset_reg > 0) begin + if (AXIS_KEEP_ENABLE && !(shift_axis_tkeep & ~({AXI_STRB_WIDTH{1'b1}} >> (AXI_STRB_WIDTH - last_cycle_offset_reg)))) begin + m_axi_wstrb_int = strb_offset_mask_reg & shift_axis_tkeep; + if (first_cycle_reg) begin + length_next = length_reg + (cycle_size - offset_reg); + end else begin + length_next = length_reg + cycle_size; + end + end else begin + m_axi_wstrb_int = strb_offset_mask_reg & {AXI_STRB_WIDTH{1'b1}} >> (AXI_STRB_WIDTH - last_cycle_offset_reg); + if (first_cycle_reg) begin + length_next = length_reg + (last_cycle_offset_reg - offset_reg); + end else begin + length_next = length_reg + last_cycle_offset_reg; + end + end + end else begin + if (AXIS_KEEP_ENABLE) begin + m_axi_wstrb_int = strb_offset_mask_reg & shift_axis_tkeep; + if (first_cycle_reg) begin + length_next = length_reg + (cycle_size - offset_reg); + end else begin + length_next = length_reg + cycle_size; + end + end + end + + // enqueue status FIFO entry for write completion + status_fifo_we = 1'b1; + status_fifo_wr_len = length_next; + status_fifo_wr_tag = tag_reg; + status_fifo_wr_id = axis_id_next; + status_fifo_wr_dest = axis_dest_next; + status_fifo_wr_user = axis_user_next; + status_fifo_wr_last = 1'b1; + + s_axis_write_data_tready_next = 1'b0; + s_axis_write_desc_ready_next = enable; + state_next = STATE_IDLE; + end else begin + // more cycles left in burst, finish burst + if (AXIS_KEEP_ENABLE) begin + m_axi_wstrb_int = strb_offset_mask_reg & shift_axis_tkeep; + if (first_cycle_reg) begin + length_next = length_reg + (cycle_size - offset_reg); + end else begin + length_next = length_reg + cycle_size; + end + end + + // enqueue status FIFO entry for write completion + status_fifo_we = 1'b1; + status_fifo_wr_len = length_next; + status_fifo_wr_tag = tag_reg; + status_fifo_wr_id = axis_id_next; + status_fifo_wr_dest = axis_dest_next; + status_fifo_wr_user = axis_user_next; + status_fifo_wr_last = 1'b1; + + s_axis_write_data_tready_next = 1'b0; + state_next = STATE_FINISH_BURST; + end + + end else if (output_last_cycle_reg) begin + m_axi_wlast_int = 1'b1; + + if (op_word_count_reg > 0) begin + // current AXI transfer complete, but there is more data to transfer + // enqueue status FIFO entry for write completion + status_fifo_we = 1'b1; + status_fifo_wr_len = length_next; + status_fifo_wr_tag = tag_reg; + status_fifo_wr_id = axis_id_next; + status_fifo_wr_dest = axis_dest_next; + status_fifo_wr_user = axis_user_next; + status_fifo_wr_last = 1'b0; + + s_axis_write_data_tready_next = 1'b0; + state_next = STATE_START; + end else begin + // no more data to transfer, finish operation + if (last_cycle_offset_reg > 0) begin + m_axi_wstrb_int = strb_offset_mask_reg & {AXI_STRB_WIDTH{1'b1}} >> (AXI_STRB_WIDTH - last_cycle_offset_reg); + if (first_cycle_reg) begin + length_next = length_reg + (last_cycle_offset_reg - offset_reg); + end else begin + length_next = length_reg + last_cycle_offset_reg; + end + end + + // enqueue status FIFO entry for write completion + status_fifo_we = 1'b1; + status_fifo_wr_len = length_next; + status_fifo_wr_tag = tag_reg; + status_fifo_wr_id = axis_id_next; + status_fifo_wr_dest = axis_dest_next; + status_fifo_wr_user = axis_user_next; + status_fifo_wr_last = 1'b1; + + if (AXIS_LAST_ENABLE) begin + // not at the end of packet; drop remainder + s_axis_write_data_tready_next = shift_axis_input_tready; + state_next = STATE_DROP_DATA; + end else begin + // no framing; return to idle + s_axis_write_data_tready_next = 1'b0; + s_axis_write_desc_ready_next = enable; + state_next = STATE_IDLE; + end + end + end else begin + s_axis_write_data_tready_next = m_axi_wready_int_early && (last_transfer_reg || input_active_next) && shift_axis_input_tready; + state_next = STATE_WRITE; + end + end else begin + state_next = STATE_WRITE; + end + end + STATE_FINISH_BURST: begin + // finish current AXI burst + + if (m_axi_wready_int_reg) begin + // update counters + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + input_last_cycle_next = input_cycle_count_next == 0; + output_cycle_count_next = output_cycle_count_reg - 1; + output_last_cycle_next = output_cycle_count_next == 0; + + m_axi_wdata_int = {AXI_DATA_WIDTH{1'b0}}; + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b0}}; + m_axi_wvalid_int = 1'b1; + + if (output_last_cycle_reg) begin + // no more data to transfer, finish operation + m_axi_wlast_int = 1'b1; + + s_axis_write_data_tready_next = 1'b0; + s_axis_write_desc_ready_next = enable; + state_next = STATE_IDLE; + end else begin + // more cycles in AXI transfer + state_next = STATE_FINISH_BURST; + end + end else begin + state_next = STATE_FINISH_BURST; + end + end + STATE_DROP_DATA: begin + // drop excess AXI stream data + s_axis_write_data_tready_next = shift_axis_input_tready; + + if (shift_axis_tvalid) begin + if (s_axis_write_data_tready && s_axis_write_data_tvalid) begin + transfer_in_save = 1'b1; + end + + if (shift_axis_tlast) begin + s_axis_write_data_tready_next = 1'b0; + s_axis_write_desc_ready_next = enable; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DROP_DATA; + end + end else begin + state_next = STATE_DROP_DATA; + end + end + endcase + + if (status_fifo_rd_ptr_reg != status_fifo_wr_ptr_reg) begin + // status FIFO not empty + if (m_axi_bready && m_axi_bvalid) begin + // got write completion, pop and return status + m_axis_write_desc_status_len_next = status_fifo_len[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + m_axis_write_desc_status_tag_next = status_fifo_tag[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + m_axis_write_desc_status_id_next = status_fifo_id[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + m_axis_write_desc_status_dest_next = status_fifo_dest[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + m_axis_write_desc_status_user_next = status_fifo_user[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + m_axis_write_desc_status_valid_next = status_fifo_last[status_fifo_rd_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]]; + status_fifo_rd_ptr_next = status_fifo_rd_ptr_reg + 1; + m_axi_bready_next = 1'b0; + end else begin + // wait for write completion + m_axi_bready_next = 1'b1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axis_write_desc_ready_reg <= 1'b0; + m_axis_write_desc_status_valid_reg <= 1'b0; + s_axis_write_data_tready_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + m_axi_bready_reg <= 1'b0; + save_axis_tlast_reg <= 1'b0; + shift_axis_extra_cycle_reg <= 1'b0; + + status_fifo_wr_ptr_reg <= 0; + status_fifo_rd_ptr_reg <= 0; + end else begin + state_reg <= state_next; + s_axis_write_desc_ready_reg <= s_axis_write_desc_ready_next; + m_axis_write_desc_status_valid_reg <= m_axis_write_desc_status_valid_next; + s_axis_write_data_tready_reg <= s_axis_write_data_tready_next; + m_axi_awvalid_reg <= m_axi_awvalid_next; + m_axi_bready_reg <= m_axi_bready_next; + + // datapath + if (flush_save) begin + save_axis_tlast_reg <= 1'b0; + shift_axis_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_axis_tlast_reg <= s_axis_write_data_tlast; + shift_axis_extra_cycle_reg <= s_axis_write_data_tlast & ((s_axis_write_data_tkeep >> (AXIS_KEEP_WIDTH_INT-offset_reg)) != 0); + end + + if (status_fifo_we) begin + status_fifo_wr_ptr_reg <= status_fifo_wr_ptr_reg + 1; + end + status_fifo_rd_ptr_reg <= status_fifo_rd_ptr_next; + end + + m_axis_write_desc_status_len_reg <= m_axis_write_desc_status_len_next; + m_axis_write_desc_status_tag_reg <= m_axis_write_desc_status_tag_next; + m_axis_write_desc_status_id_reg <= m_axis_write_desc_status_id_next; + m_axis_write_desc_status_dest_reg <= m_axis_write_desc_status_dest_next; + m_axis_write_desc_status_user_reg <= m_axis_write_desc_status_user_next; + + m_axi_awaddr_reg <= m_axi_awaddr_next; + m_axi_awlen_reg <= m_axi_awlen_next; + + addr_reg <= addr_next; + offset_reg <= offset_next; + strb_offset_mask_reg <= strb_offset_mask_next; + zero_offset_reg <= zero_offset_next; + last_cycle_offset_reg <= last_cycle_offset_next; + length_reg <= length_next; + op_word_count_reg <= op_word_count_next; + tr_word_count_reg <= tr_word_count_next; + input_cycle_count_reg <= input_cycle_count_next; + output_cycle_count_reg <= output_cycle_count_next; + input_active_reg <= input_active_next; + first_cycle_reg <= first_cycle_next; + input_last_cycle_reg <= input_last_cycle_next; + output_last_cycle_reg <= output_last_cycle_next; + last_transfer_reg <= last_transfer_next; + + tag_reg <= tag_next; + axis_id_reg <= axis_id_next; + axis_dest_reg <= axis_dest_next; + axis_user_reg <= axis_user_next; + + if (flush_save) begin + save_axis_tkeep_reg <= {AXIS_KEEP_WIDTH_INT{1'b0}}; + end else if (transfer_in_save) begin + save_axis_tdata_reg <= s_axis_write_data_tdata; + save_axis_tkeep_reg <= AXIS_KEEP_ENABLE ? s_axis_write_data_tkeep : {AXIS_KEEP_WIDTH_INT{1'b1}}; + end + + if (status_fifo_we) begin + status_fifo_len[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_len; + status_fifo_tag[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_tag; + status_fifo_id[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_id; + status_fifo_dest[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_dest; + status_fifo_user[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_user; + status_fifo_last[status_fifo_wr_ptr_reg[STATUS_FIFO_ADDR_WIDTH-1:0]] <= status_fifo_wr_last; + status_fifo_wr_ptr_reg <= status_fifo_wr_ptr_reg + 1; + end +end + +// output datapath logic +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [AXI_DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wvalid = m_axi_wvalid_reg; +assign m_axi_wlast = m_axi_wlast_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = m_axi_wready || (!temp_m_axi_wvalid_reg && (!m_axi_wvalid_reg || !m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (m_axi_wready || !m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_dp_ram.v b/corundum/lib/axi/rtl/axi_dp_ram.v new file mode 100644 index 0000000000000000000000000000000000000000..e76a2dc8003b13c654886e0b0fdb8bee210a9d90 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_dp_ram.v @@ -0,0 +1,424 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 dual port RAM + */ +module axi_dp_ram # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Extra pipeline register on output port A + parameter A_PIPELINE_OUTPUT = 0, + // Extra pipeline register on output port B + parameter B_PIPELINE_OUTPUT = 0, + // Interleave read and write burst cycles on port A + parameter A_INTERLEAVE = 0, + // Interleave read and write burst cycles on port B + parameter B_INTERLEAVE = 0 +) +( + input wire a_clk, + input wire a_rst, + + input wire b_clk, + input wire b_rst, + + input wire [ID_WIDTH-1:0] s_axi_a_awid, + input wire [ADDR_WIDTH-1:0] s_axi_a_awaddr, + input wire [7:0] s_axi_a_awlen, + input wire [2:0] s_axi_a_awsize, + input wire [1:0] s_axi_a_awburst, + input wire s_axi_a_awlock, + input wire [3:0] s_axi_a_awcache, + input wire [2:0] s_axi_a_awprot, + input wire s_axi_a_awvalid, + output wire s_axi_a_awready, + input wire [DATA_WIDTH-1:0] s_axi_a_wdata, + input wire [STRB_WIDTH-1:0] s_axi_a_wstrb, + input wire s_axi_a_wlast, + input wire s_axi_a_wvalid, + output wire s_axi_a_wready, + output wire [ID_WIDTH-1:0] s_axi_a_bid, + output wire [1:0] s_axi_a_bresp, + output wire s_axi_a_bvalid, + input wire s_axi_a_bready, + input wire [ID_WIDTH-1:0] s_axi_a_arid, + input wire [ADDR_WIDTH-1:0] s_axi_a_araddr, + input wire [7:0] s_axi_a_arlen, + input wire [2:0] s_axi_a_arsize, + input wire [1:0] s_axi_a_arburst, + input wire s_axi_a_arlock, + input wire [3:0] s_axi_a_arcache, + input wire [2:0] s_axi_a_arprot, + input wire s_axi_a_arvalid, + output wire s_axi_a_arready, + output wire [ID_WIDTH-1:0] s_axi_a_rid, + output wire [DATA_WIDTH-1:0] s_axi_a_rdata, + output wire [1:0] s_axi_a_rresp, + output wire s_axi_a_rlast, + output wire s_axi_a_rvalid, + input wire s_axi_a_rready, + + input wire [ID_WIDTH-1:0] s_axi_b_awid, + input wire [ADDR_WIDTH-1:0] s_axi_b_awaddr, + input wire [7:0] s_axi_b_awlen, + input wire [2:0] s_axi_b_awsize, + input wire [1:0] s_axi_b_awburst, + input wire s_axi_b_awlock, + input wire [3:0] s_axi_b_awcache, + input wire [2:0] s_axi_b_awprot, + input wire s_axi_b_awvalid, + output wire s_axi_b_awready, + input wire [DATA_WIDTH-1:0] s_axi_b_wdata, + input wire [STRB_WIDTH-1:0] s_axi_b_wstrb, + input wire s_axi_b_wlast, + input wire s_axi_b_wvalid, + output wire s_axi_b_wready, + output wire [ID_WIDTH-1:0] s_axi_b_bid, + output wire [1:0] s_axi_b_bresp, + output wire s_axi_b_bvalid, + input wire s_axi_b_bready, + input wire [ID_WIDTH-1:0] s_axi_b_arid, + input wire [ADDR_WIDTH-1:0] s_axi_b_araddr, + input wire [7:0] s_axi_b_arlen, + input wire [2:0] s_axi_b_arsize, + input wire [1:0] s_axi_b_arburst, + input wire s_axi_b_arlock, + input wire [3:0] s_axi_b_arcache, + input wire [2:0] s_axi_b_arprot, + input wire s_axi_b_arvalid, + output wire s_axi_b_arready, + output wire [ID_WIDTH-1:0] s_axi_b_rid, + output wire [DATA_WIDTH-1:0] s_axi_b_rdata, + output wire [1:0] s_axi_b_rresp, + output wire s_axi_b_rlast, + output wire s_axi_b_rvalid, + input wire s_axi_b_rready +); + +parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); +parameter WORD_WIDTH = STRB_WIDTH; +parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH; + +// bus width assertions +initial begin + if (WORD_SIZE * STRB_WIDTH != DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (2**$clog2(WORD_WIDTH) != WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end +end + +wire [ID_WIDTH-1:0] ram_a_cmd_id; +wire [ADDR_WIDTH-1:0] ram_a_cmd_addr; +wire [DATA_WIDTH-1:0] ram_a_cmd_wr_data; +wire [STRB_WIDTH-1:0] ram_a_cmd_wr_strb; +wire ram_a_cmd_wr_en; +wire ram_a_cmd_rd_en; +wire ram_a_cmd_last; +reg ram_a_cmd_ready_reg = 1'b1; +reg [ID_WIDTH-1:0] ram_a_rd_resp_id_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] ram_a_rd_resp_data_reg = {DATA_WIDTH{1'b0}}; +reg ram_a_rd_resp_last_reg = 1'b0; +reg ram_a_rd_resp_valid_reg = 1'b0; +wire ram_a_rd_resp_ready; + +wire [ID_WIDTH-1:0] ram_b_cmd_id; +wire [ADDR_WIDTH-1:0] ram_b_cmd_addr; +wire [DATA_WIDTH-1:0] ram_b_cmd_wr_data; +wire [STRB_WIDTH-1:0] ram_b_cmd_wr_strb; +wire ram_b_cmd_wr_en; +wire ram_b_cmd_rd_en; +wire ram_b_cmd_last; +reg ram_b_cmd_ready_reg = 1'b1; +reg [ID_WIDTH-1:0] ram_b_rd_resp_id_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] ram_b_rd_resp_data_reg = {DATA_WIDTH{1'b0}}; +reg ram_b_rd_resp_last_reg = 1'b0; +reg ram_b_rd_resp_valid_reg = 1'b0; +wire ram_b_rd_resp_ready; + +axi_ram_wr_rd_if #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(0), + .WUSER_ENABLE(0), + .BUSER_ENABLE(0), + .ARUSER_ENABLE(0), + .RUSER_ENABLE(0), + .PIPELINE_OUTPUT(A_PIPELINE_OUTPUT), + .INTERLEAVE(A_INTERLEAVE) +) +a_if ( + .clk(a_clk), + .rst(a_rst), + + /* + * AXI slave interface + */ + .s_axi_awid(s_axi_a_awid), + .s_axi_awaddr(s_axi_a_awaddr), + .s_axi_awlen(s_axi_a_awlen), + .s_axi_awsize(s_axi_a_awsize), + .s_axi_awburst(s_axi_a_awburst), + .s_axi_awlock(s_axi_a_awlock), + .s_axi_awcache(s_axi_a_awcache), + .s_axi_awprot(s_axi_a_awprot), + .s_axi_awqos(4'd0), + .s_axi_awregion(4'd0), + .s_axi_awuser(0), + .s_axi_awvalid(s_axi_a_awvalid), + .s_axi_awready(s_axi_a_awready), + .s_axi_wdata(s_axi_a_wdata), + .s_axi_wstrb(s_axi_a_wstrb), + .s_axi_wlast(s_axi_a_wlast), + .s_axi_wuser(0), + .s_axi_wvalid(s_axi_a_wvalid), + .s_axi_wready(s_axi_a_wready), + .s_axi_bid(s_axi_a_bid), + .s_axi_bresp(s_axi_a_bresp), + .s_axi_buser(), + .s_axi_bvalid(s_axi_a_bvalid), + .s_axi_bready(s_axi_a_bready), + .s_axi_arid(s_axi_a_arid), + .s_axi_araddr(s_axi_a_araddr), + .s_axi_arlen(s_axi_a_arlen), + .s_axi_arsize(s_axi_a_arsize), + .s_axi_arburst(s_axi_a_arburst), + .s_axi_arlock(s_axi_a_arlock), + .s_axi_arcache(s_axi_a_arcache), + .s_axi_arprot(s_axi_a_arprot), + .s_axi_arqos(4'd0), + .s_axi_arregion(4'd0), + .s_axi_aruser(0), + .s_axi_arvalid(s_axi_a_arvalid), + .s_axi_arready(s_axi_a_arready), + .s_axi_rid(s_axi_a_rid), + .s_axi_rdata(s_axi_a_rdata), + .s_axi_rresp(s_axi_a_rresp), + .s_axi_rlast(s_axi_a_rlast), + .s_axi_ruser(), + .s_axi_rvalid(s_axi_a_rvalid), + .s_axi_rready(s_axi_a_rready), + + /* + * RAM interface + */ + .ram_cmd_id(ram_a_cmd_id), + .ram_cmd_addr(ram_a_cmd_addr), + .ram_cmd_lock(), + .ram_cmd_cache(), + .ram_cmd_prot(), + .ram_cmd_qos(), + .ram_cmd_region(), + .ram_cmd_auser(), + .ram_cmd_wr_data(ram_a_cmd_wr_data), + .ram_cmd_wr_strb(ram_a_cmd_wr_strb), + .ram_cmd_wr_user(), + .ram_cmd_wr_en(ram_a_cmd_wr_en), + .ram_cmd_rd_en(ram_a_cmd_rd_en), + .ram_cmd_last(ram_a_cmd_last), + .ram_cmd_ready(ram_a_cmd_ready_reg), + .ram_rd_resp_id(ram_a_rd_resp_id_reg), + .ram_rd_resp_data(ram_a_rd_resp_data_reg), + .ram_rd_resp_last(ram_a_rd_resp_last_reg), + .ram_rd_resp_user(0), + .ram_rd_resp_valid(ram_a_rd_resp_valid_reg), + .ram_rd_resp_ready(ram_a_rd_resp_ready) +); + +axi_ram_wr_rd_if #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(0), + .WUSER_ENABLE(0), + .BUSER_ENABLE(0), + .ARUSER_ENABLE(0), + .RUSER_ENABLE(0), + .PIPELINE_OUTPUT(B_PIPELINE_OUTPUT), + .INTERLEAVE(B_INTERLEAVE) +) +b_if ( + .clk(b_clk), + .rst(b_rst), + + /* + * AXI slave interface + */ + .s_axi_awid(s_axi_b_awid), + .s_axi_awaddr(s_axi_b_awaddr), + .s_axi_awlen(s_axi_b_awlen), + .s_axi_awsize(s_axi_b_awsize), + .s_axi_awburst(s_axi_b_awburst), + .s_axi_awlock(s_axi_b_awlock), + .s_axi_awcache(s_axi_b_awcache), + .s_axi_awprot(s_axi_b_awprot), + .s_axi_awqos(4'd0), + .s_axi_awregion(4'd0), + .s_axi_awuser(0), + .s_axi_awvalid(s_axi_b_awvalid), + .s_axi_awready(s_axi_b_awready), + .s_axi_wdata(s_axi_b_wdata), + .s_axi_wstrb(s_axi_b_wstrb), + .s_axi_wlast(s_axi_b_wlast), + .s_axi_wuser(0), + .s_axi_wvalid(s_axi_b_wvalid), + .s_axi_wready(s_axi_b_wready), + .s_axi_bid(s_axi_b_bid), + .s_axi_bresp(s_axi_b_bresp), + .s_axi_buser(), + .s_axi_bvalid(s_axi_b_bvalid), + .s_axi_bready(s_axi_b_bready), + .s_axi_arid(s_axi_b_arid), + .s_axi_araddr(s_axi_b_araddr), + .s_axi_arlen(s_axi_b_arlen), + .s_axi_arsize(s_axi_b_arsize), + .s_axi_arburst(s_axi_b_arburst), + .s_axi_arlock(s_axi_b_arlock), + .s_axi_arcache(s_axi_b_arcache), + .s_axi_arprot(s_axi_b_arprot), + .s_axi_arqos(4'd0), + .s_axi_arregion(4'd0), + .s_axi_aruser(0), + .s_axi_arvalid(s_axi_b_arvalid), + .s_axi_arready(s_axi_b_arready), + .s_axi_rid(s_axi_b_rid), + .s_axi_rdata(s_axi_b_rdata), + .s_axi_rresp(s_axi_b_rresp), + .s_axi_rlast(s_axi_b_rlast), + .s_axi_ruser(), + .s_axi_rvalid(s_axi_b_rvalid), + .s_axi_rready(s_axi_b_rready), + + /* + * RAM interface + */ + .ram_cmd_id(ram_b_cmd_id), + .ram_cmd_addr(ram_b_cmd_addr), + .ram_cmd_lock(), + .ram_cmd_cache(), + .ram_cmd_prot(), + .ram_cmd_qos(), + .ram_cmd_region(), + .ram_cmd_auser(), + .ram_cmd_wr_data(ram_b_cmd_wr_data), + .ram_cmd_wr_strb(ram_b_cmd_wr_strb), + .ram_cmd_wr_user(), + .ram_cmd_wr_en(ram_b_cmd_wr_en), + .ram_cmd_rd_en(ram_b_cmd_rd_en), + .ram_cmd_last(ram_b_cmd_last), + .ram_cmd_ready(ram_b_cmd_ready_reg), + .ram_rd_resp_id(ram_b_rd_resp_id_reg), + .ram_rd_resp_data(ram_b_rd_resp_data_reg), + .ram_rd_resp_last(ram_b_rd_resp_last_reg), + .ram_rd_resp_user(0), + .ram_rd_resp_valid(ram_b_rd_resp_valid_reg), + .ram_rd_resp_ready(ram_b_rd_resp_ready) +); + +// (* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH-1:0] mem[(2**VALID_ADDR_WIDTH)-1:0]; + +wire [VALID_ADDR_WIDTH-1:0] addr_a_valid = ram_a_cmd_addr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] addr_b_valid = ram_b_cmd_addr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); + +integer i, j; + +initial begin + // two nested loops for smaller number of iterations per loop + // workaround for synthesizer complaints about large loop counts + for (i = 0; i < 2**ADDR_WIDTH; i = i + 2**(ADDR_WIDTH/2)) begin + for (j = i; j < i + 2**(ADDR_WIDTH/2); j = j + 1) begin + mem[j] = 0; + end + end +end + +always @(posedge a_clk) begin + ram_a_rd_resp_valid_reg <= ram_a_rd_resp_valid_reg && !ram_a_rd_resp_ready; + ram_a_cmd_ready_reg <= !ram_a_rd_resp_valid_reg || ram_a_rd_resp_ready; + + if (ram_a_cmd_ready_reg && ram_a_cmd_rd_en) begin + ram_a_rd_resp_id_reg <= ram_a_cmd_id; + ram_a_rd_resp_data_reg <= mem[addr_a_valid]; + ram_a_rd_resp_last_reg <= ram_a_cmd_last; + ram_a_rd_resp_valid_reg <= 1'b1; + ram_a_cmd_ready_reg <= ram_a_rd_resp_ready; + end else if (ram_a_cmd_ready_reg && ram_a_cmd_wr_en) begin + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (ram_a_cmd_wr_strb[i]) begin + mem[addr_a_valid][WORD_SIZE*i +: WORD_SIZE] <= ram_a_cmd_wr_data[WORD_SIZE*i +: WORD_SIZE]; + end + end + end + + if (a_rst) begin + ram_a_cmd_ready_reg <= 1'b1; + ram_a_rd_resp_valid_reg <= 1'b0; + end +end + +always @(posedge b_clk) begin + ram_b_rd_resp_valid_reg <= ram_b_rd_resp_valid_reg && !ram_b_rd_resp_ready; + ram_b_cmd_ready_reg <= !ram_b_rd_resp_valid_reg || ram_b_rd_resp_ready; + + if (ram_b_cmd_ready_reg && ram_b_cmd_rd_en) begin + ram_b_rd_resp_id_reg <= ram_b_cmd_id; + ram_b_rd_resp_data_reg <= mem[addr_b_valid]; + ram_b_rd_resp_last_reg <= ram_b_cmd_last; + ram_b_rd_resp_valid_reg <= 1'b1; + ram_b_cmd_ready_reg <= ram_b_rd_resp_ready; + end else if (ram_b_cmd_ready_reg && ram_b_cmd_wr_en) begin + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (ram_b_cmd_wr_strb[i]) begin + mem[addr_b_valid][WORD_SIZE*i +: WORD_SIZE] <= ram_b_cmd_wr_data[WORD_SIZE*i +: WORD_SIZE]; + end + end + end + + if (b_rst) begin + ram_a_cmd_ready_reg <= 1'b1; + ram_b_rd_resp_valid_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_fifo.v b/corundum/lib/axi/rtl/axi_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..0e18f5fb660f54ef5b1c4109a9efd4f02e82aa34 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_fifo.v @@ -0,0 +1,312 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 FIFO + */ +module axi_fifo # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Write data FIFO depth (cycles) + parameter WRITE_FIFO_DEPTH = 32, + // Read data FIFO depth (cycles) + parameter READ_FIFO_DEPTH = 32, + // Hold write address until write data in FIFO, if possible + parameter WRITE_FIFO_DELAY = 0, + // Hold read address until space available in FIFO for data, if possible + parameter READ_FIFO_DELAY = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [DATA_WIDTH-1:0] m_axi_wdata, + output wire [STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +axi_fifo_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .FIFO_DEPTH(WRITE_FIFO_DEPTH), + .FIFO_DELAY(WRITE_FIFO_DELAY) +) +axi_fifo_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awqos(s_axi_awqos), + .s_axi_awregion(s_axi_awregion), + .s_axi_awuser(s_axi_awuser), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wuser(s_axi_wuser), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_buser(s_axi_buser), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awqos(m_axi_awqos), + .m_axi_awregion(m_axi_awregion), + .m_axi_awuser(m_axi_awuser), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wuser(m_axi_wuser), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_buser(m_axi_buser), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready) +); + +axi_fifo_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .FIFO_DEPTH(READ_FIFO_DEPTH), + .FIFO_DELAY(READ_FIFO_DELAY) +) +axi_fifo_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arqos(s_axi_arqos), + .s_axi_arregion(s_axi_arregion), + .s_axi_aruser(s_axi_aruser), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_ruser(s_axi_ruser), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + + /* + * AXI master interface + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arqos(m_axi_arqos), + .m_axi_arregion(m_axi_arregion), + .m_axi_aruser(m_axi_aruser), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_ruser(m_axi_ruser), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axi_fifo_rd.v b/corundum/lib/axi/rtl/axi_fifo_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..ed1a29191ba0e24459f7e8660daa6064daaee9d6 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_fifo_rd.v @@ -0,0 +1,409 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 FIFO (read) + */ +module axi_fifo_rd # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Read data FIFO depth (cycles) + parameter FIFO_DEPTH = 32, + // Hold read address until space available in FIFO for data, if possible + parameter FIFO_DELAY = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +parameter LAST_OFFSET = DATA_WIDTH; +parameter ID_OFFSET = LAST_OFFSET + 1; +parameter RESP_OFFSET = ID_OFFSET + ID_WIDTH; +parameter RUSER_OFFSET = RESP_OFFSET + 2; +parameter RWIDTH = RUSER_OFFSET + (RUSER_ENABLE ? RUSER_WIDTH : 0); + +parameter FIFO_ADDR_WIDTH = $clog2(FIFO_DEPTH); + +reg [FIFO_ADDR_WIDTH:0] wr_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [FIFO_ADDR_WIDTH:0] wr_addr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [FIFO_ADDR_WIDTH:0] rd_addr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; + +reg [RWIDTH-1:0] mem[(2**FIFO_ADDR_WIDTH)-1:0]; +reg [RWIDTH-1:0] mem_read_data_reg; +reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next; + +wire [RWIDTH-1:0] m_axi_r; + +reg [RWIDTH-1:0] s_axi_r_reg; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +// full when first MSB different but rest same +wire full = ((wr_ptr_reg[FIFO_ADDR_WIDTH] != rd_ptr_reg[FIFO_ADDR_WIDTH]) && + (wr_ptr_reg[FIFO_ADDR_WIDTH-1:0] == rd_ptr_reg[FIFO_ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr_reg == rd_ptr_reg; + +// control signals +reg write; +reg read; +reg store_output; + +assign m_axi_rready = !full; + +generate + assign m_axi_r[DATA_WIDTH-1:0] = m_axi_rdata; + assign m_axi_r[LAST_OFFSET] = m_axi_rlast; + assign m_axi_r[ID_OFFSET +: ID_WIDTH] = m_axi_rid; + assign m_axi_r[RESP_OFFSET +: 2] = m_axi_rresp; + if (RUSER_ENABLE) assign m_axi_r[RUSER_OFFSET +: RUSER_WIDTH] = m_axi_ruser; +endgenerate + +generate + +if (FIFO_DELAY) begin + // store AR channel value until there is enough space to store R channel burst in FIFO or FIFO is empty + + localparam COUNT_WIDTH = (FIFO_ADDR_WIDTH > 8 ? FIFO_ADDR_WIDTH : 8) + 1; + + localparam [1:0] + STATE_IDLE = 1'd0, + STATE_WAIT = 1'd1; + + reg [1:0] state_reg = STATE_IDLE, state_next; + + reg [COUNT_WIDTH-1:0] count_reg = 0, count_next; + + reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}, m_axi_arid_next; + reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}, m_axi_araddr_next; + reg [7:0] m_axi_arlen_reg = 8'd0, m_axi_arlen_next; + reg [2:0] m_axi_arsize_reg = 3'd0, m_axi_arsize_next; + reg [1:0] m_axi_arburst_reg = 2'd0, m_axi_arburst_next; + reg m_axi_arlock_reg = 1'b0, m_axi_arlock_next; + reg [3:0] m_axi_arcache_reg = 4'd0, m_axi_arcache_next; + reg [2:0] m_axi_arprot_reg = 3'd0, m_axi_arprot_next; + reg [3:0] m_axi_arqos_reg = 4'd0, m_axi_arqos_next; + reg [3:0] m_axi_arregion_reg = 4'd0, m_axi_arregion_next; + reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}, m_axi_aruser_next; + reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; + + reg s_axi_arready_reg = 1'b0, s_axi_arready_next; + + assign m_axi_arid = m_axi_arid_reg; + assign m_axi_araddr = m_axi_araddr_reg; + assign m_axi_arlen = m_axi_arlen_reg; + assign m_axi_arsize = m_axi_arsize_reg; + assign m_axi_arburst = m_axi_arburst_reg; + assign m_axi_arlock = m_axi_arlock_reg; + assign m_axi_arcache = m_axi_arcache_reg; + assign m_axi_arprot = m_axi_arprot_reg; + assign m_axi_arqos = m_axi_arqos_reg; + assign m_axi_arregion = m_axi_arregion_reg; + assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; + assign m_axi_arvalid = m_axi_arvalid_reg; + + assign s_axi_arready = s_axi_arready_reg; + + always @* begin + state_next = STATE_IDLE; + + count_next = count_reg; + + m_axi_arid_next = m_axi_arid_reg; + m_axi_araddr_next = m_axi_araddr_reg; + m_axi_arlen_next = m_axi_arlen_reg; + m_axi_arsize_next = m_axi_arsize_reg; + m_axi_arburst_next = m_axi_arburst_reg; + m_axi_arlock_next = m_axi_arlock_reg; + m_axi_arcache_next = m_axi_arcache_reg; + m_axi_arprot_next = m_axi_arprot_reg; + m_axi_arqos_next = m_axi_arqos_reg; + m_axi_arregion_next = m_axi_arregion_reg; + m_axi_aruser_next = m_axi_aruser_reg; + m_axi_arvalid_next = m_axi_arvalid_reg && !m_axi_arready; + s_axi_arready_next = s_axi_arready_reg; + + case (state_reg) + STATE_IDLE: begin + s_axi_arready_next = !m_axi_arvalid; + + if (s_axi_arready & s_axi_arvalid) begin + s_axi_arready_next = 1'b0; + + m_axi_arid_next = s_axi_arid; + m_axi_araddr_next = s_axi_araddr; + m_axi_arlen_next = s_axi_arlen; + m_axi_arsize_next = s_axi_arsize; + m_axi_arburst_next = s_axi_arburst; + m_axi_arlock_next = s_axi_arlock; + m_axi_arcache_next = s_axi_arcache; + m_axi_arprot_next = s_axi_arprot; + m_axi_arqos_next = s_axi_arqos; + m_axi_arregion_next = s_axi_arregion; + m_axi_aruser_next = s_axi_aruser; + + if (count_reg == 0 || count_reg + m_axi_arlen_next + 1 <= 2**FIFO_ADDR_WIDTH) begin + count_next = count_reg + m_axi_arlen_next + 1; + m_axi_arvalid_next = 1'b1; + s_axi_arready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + s_axi_arready_next = 1'b0; + state_next = STATE_WAIT; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WAIT: begin + s_axi_arready_next = 1'b0; + + if (count_reg == 0 || count_reg + m_axi_arlen_reg + 1 <= 2**FIFO_ADDR_WIDTH) begin + count_next = count_reg + m_axi_arlen_reg + 1; + m_axi_arvalid_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT; + end + end + endcase + + if (s_axi_rready && s_axi_rvalid) begin + count_next = count_next - 1; + end + end + + always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + m_axi_arvalid_reg <= 1'b0; + s_axi_arready_reg <= 1'b0; + end else begin + state_reg <= state_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + s_axi_arready_reg <= s_axi_arready_next; + end + + count_reg <= count_next; + + m_axi_arid_reg <= m_axi_arid_next; + m_axi_araddr_reg <= m_axi_araddr_next; + m_axi_arlen_reg <= m_axi_arlen_next; + m_axi_arsize_reg <= m_axi_arsize_next; + m_axi_arburst_reg <= m_axi_arburst_next; + m_axi_arlock_reg <= m_axi_arlock_next; + m_axi_arcache_reg <= m_axi_arcache_next; + m_axi_arprot_reg <= m_axi_arprot_next; + m_axi_arqos_reg <= m_axi_arqos_next; + m_axi_arregion_reg <= m_axi_arregion_next; + m_axi_aruser_reg <= m_axi_aruser_next; + end +end else begin + // bypass AR channel + assign m_axi_arid = s_axi_arid; + assign m_axi_araddr = s_axi_araddr; + assign m_axi_arlen = s_axi_arlen; + assign m_axi_arsize = s_axi_arsize; + assign m_axi_arburst = s_axi_arburst; + assign m_axi_arlock = s_axi_arlock; + assign m_axi_arcache = s_axi_arcache; + assign m_axi_arprot = s_axi_arprot; + assign m_axi_arqos = s_axi_arqos; + assign m_axi_arregion = s_axi_arregion; + assign m_axi_aruser = ARUSER_ENABLE ? s_axi_aruser : {ARUSER_WIDTH{1'b0}}; + assign m_axi_arvalid = s_axi_arvalid; + assign s_axi_arready = m_axi_arready; +end + +endgenerate + +assign s_axi_rvalid = s_axi_rvalid_reg; + +assign s_axi_rdata = s_axi_r_reg[DATA_WIDTH-1:0]; +assign s_axi_rlast = s_axi_r_reg[LAST_OFFSET]; +assign s_axi_rid = s_axi_r_reg[ID_OFFSET +: ID_WIDTH]; +assign s_axi_rresp = s_axi_r_reg[RESP_OFFSET +: 2]; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_r_reg[RUSER_OFFSET +: RUSER_WIDTH] : {RUSER_WIDTH{1'b0}}; + +// Write logic +always @* begin + write = 1'b0; + + wr_ptr_next = wr_ptr_reg; + + if (m_axi_rvalid) begin + // input data valid + if (!full) begin + // not full, perform write + write = 1'b1; + wr_ptr_next = wr_ptr_reg + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + wr_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + wr_ptr_reg <= wr_ptr_next; + end + + wr_addr_reg <= wr_ptr_next; + + if (write) begin + mem[wr_addr_reg[FIFO_ADDR_WIDTH-1:0]] <= m_axi_r; + end +end + +// Read logic +always @* begin + read = 1'b0; + + rd_ptr_next = rd_ptr_reg; + + mem_read_data_valid_next = mem_read_data_valid_reg; + + if (store_output || !mem_read_data_valid_reg) begin + // output data not valid OR currently being transferred + if (!empty) begin + // not empty, perform read + read = 1'b1; + mem_read_data_valid_next = 1'b1; + rd_ptr_next = rd_ptr_reg + 1; + end else begin + // empty, invalidate + mem_read_data_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + rd_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + mem_read_data_valid_reg <= 1'b0; + end else begin + rd_ptr_reg <= rd_ptr_next; + mem_read_data_valid_reg <= mem_read_data_valid_next; + end + + rd_addr_reg <= rd_ptr_next; + + if (read) begin + mem_read_data_reg <= mem[rd_addr_reg[FIFO_ADDR_WIDTH-1:0]]; + end +end + +// Output register +always @* begin + store_output = 1'b0; + + s_axi_rvalid_next = s_axi_rvalid_reg; + + if (s_axi_rready || !s_axi_rvalid) begin + store_output = 1'b1; + s_axi_rvalid_next = mem_read_data_valid_reg; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_rvalid_reg <= 1'b0; + end else begin + s_axi_rvalid_reg <= s_axi_rvalid_next; + end + + if (store_output) begin + s_axi_r_reg <= mem_read_data_reg; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_fifo_wr.v b/corundum/lib/axi/rtl/axi_fifo_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..c4035094db82ba3ba644eda33cb5cb703b3a94a3 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_fifo_wr.v @@ -0,0 +1,450 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 FIFO (write) + */ +module axi_fifo_wr # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Write data FIFO depth (cycles) + parameter FIFO_DEPTH = 32, + // Hold write address until write data in FIFO, if possible + parameter FIFO_DELAY = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [DATA_WIDTH-1:0] m_axi_wdata, + output wire [STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready +); + +parameter STRB_OFFSET = DATA_WIDTH; +parameter LAST_OFFSET = STRB_OFFSET + STRB_WIDTH; +parameter WUSER_OFFSET = LAST_OFFSET + 1; +parameter WWIDTH = WUSER_OFFSET + (WUSER_ENABLE ? WUSER_WIDTH : 0); + +parameter FIFO_ADDR_WIDTH = $clog2(FIFO_DEPTH); + +reg [FIFO_ADDR_WIDTH:0] wr_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [FIFO_ADDR_WIDTH:0] wr_addr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [FIFO_ADDR_WIDTH:0] rd_addr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; + +reg [WWIDTH-1:0] mem[(2**FIFO_ADDR_WIDTH)-1:0]; +reg [WWIDTH-1:0] mem_read_data_reg; +reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next; + +wire [WWIDTH-1:0] s_axi_w; + +reg [WWIDTH-1:0] m_axi_w_reg; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +// full when first MSB different but rest same +wire full = ((wr_ptr_reg[FIFO_ADDR_WIDTH] != rd_ptr_reg[FIFO_ADDR_WIDTH]) && + (wr_ptr_reg[FIFO_ADDR_WIDTH-1:0] == rd_ptr_reg[FIFO_ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr_reg == rd_ptr_reg; + +wire hold; + +// control signals +reg write; +reg read; +reg store_output; + +assign s_axi_wready = !full && !hold; + +generate + assign s_axi_w[DATA_WIDTH-1:0] = s_axi_wdata; + assign s_axi_w[STRB_OFFSET +: STRB_WIDTH] = s_axi_wstrb; + assign s_axi_w[LAST_OFFSET] = s_axi_wlast; + if (WUSER_ENABLE) assign s_axi_w[WUSER_OFFSET +: WUSER_WIDTH] = s_axi_wuser; +endgenerate + +generate + +if (FIFO_DELAY) begin + // store AW channel value until W channel burst is stored in FIFO or FIFO is full + + localparam [1:0] + STATE_IDLE = 2'd0, + STATE_TRANSFER_IN = 2'd1, + STATE_TRANSFER_OUT = 2'd2; + + reg [1:0] state_reg = STATE_IDLE, state_next; + + reg hold_reg = 1'b1, hold_next; + reg [8:0] count_reg = 9'd0, count_next; + + reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}, m_axi_awid_next; + reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}, m_axi_awaddr_next; + reg [7:0] m_axi_awlen_reg = 8'd0, m_axi_awlen_next; + reg [2:0] m_axi_awsize_reg = 3'd0, m_axi_awsize_next; + reg [1:0] m_axi_awburst_reg = 2'd0, m_axi_awburst_next; + reg m_axi_awlock_reg = 1'b0, m_axi_awlock_next; + reg [3:0] m_axi_awcache_reg = 4'd0, m_axi_awcache_next; + reg [2:0] m_axi_awprot_reg = 3'd0, m_axi_awprot_next; + reg [3:0] m_axi_awqos_reg = 4'd0, m_axi_awqos_next; + reg [3:0] m_axi_awregion_reg = 4'd0, m_axi_awregion_next; + reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}, m_axi_awuser_next; + reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + + reg s_axi_awready_reg = 1'b0, s_axi_awready_next; + + assign m_axi_awid = m_axi_awid_reg; + assign m_axi_awaddr = m_axi_awaddr_reg; + assign m_axi_awlen = m_axi_awlen_reg; + assign m_axi_awsize = m_axi_awsize_reg; + assign m_axi_awburst = m_axi_awburst_reg; + assign m_axi_awlock = m_axi_awlock_reg; + assign m_axi_awcache = m_axi_awcache_reg; + assign m_axi_awprot = m_axi_awprot_reg; + assign m_axi_awqos = m_axi_awqos_reg; + assign m_axi_awregion = m_axi_awregion_reg; + assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; + assign m_axi_awvalid = m_axi_awvalid_reg; + + assign s_axi_awready = s_axi_awready_reg; + + assign hold = hold_reg; + + always @* begin + state_next = STATE_IDLE; + + hold_next = hold_reg; + count_next = count_reg; + + m_axi_awid_next = m_axi_awid_reg; + m_axi_awaddr_next = m_axi_awaddr_reg; + m_axi_awlen_next = m_axi_awlen_reg; + m_axi_awsize_next = m_axi_awsize_reg; + m_axi_awburst_next = m_axi_awburst_reg; + m_axi_awlock_next = m_axi_awlock_reg; + m_axi_awcache_next = m_axi_awcache_reg; + m_axi_awprot_next = m_axi_awprot_reg; + m_axi_awqos_next = m_axi_awqos_reg; + m_axi_awregion_next = m_axi_awregion_reg; + m_axi_awuser_next = m_axi_awuser_reg; + m_axi_awvalid_next = m_axi_awvalid_reg && !m_axi_awready; + s_axi_awready_next = s_axi_awready_reg; + + case (state_reg) + STATE_IDLE: begin + s_axi_awready_next = !m_axi_awvalid; + hold_next = 1'b1; + + if (s_axi_awready & s_axi_awvalid) begin + s_axi_awready_next = 1'b0; + + m_axi_awid_next = s_axi_awid; + m_axi_awaddr_next = s_axi_awaddr; + m_axi_awlen_next = s_axi_awlen; + m_axi_awsize_next = s_axi_awsize; + m_axi_awburst_next = s_axi_awburst; + m_axi_awlock_next = s_axi_awlock; + m_axi_awcache_next = s_axi_awcache; + m_axi_awprot_next = s_axi_awprot; + m_axi_awqos_next = s_axi_awqos; + m_axi_awregion_next = s_axi_awregion; + m_axi_awuser_next = s_axi_awuser; + + hold_next = 1'b0; + count_next = 0; + state_next = STATE_TRANSFER_IN; + end else begin + state_next = STATE_IDLE; + end + end + STATE_TRANSFER_IN: begin + s_axi_awready_next = 1'b0; + hold_next = 1'b0; + + if (s_axi_wready & s_axi_wvalid) begin + count_next = count_reg + 1; + if (count_next == 2**FIFO_ADDR_WIDTH) begin + m_axi_awvalid_next = 1'b1; + state_next = STATE_TRANSFER_OUT; + end else if (count_reg == m_axi_awlen) begin + m_axi_awvalid_next = 1'b1; + hold_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRANSFER_IN; + end + end else begin + state_next = STATE_TRANSFER_IN; + end + end + STATE_TRANSFER_OUT: begin + s_axi_awready_next = 1'b0; + hold_next = 1'b0; + + if (s_axi_wready & s_axi_wvalid) begin + count_next = count_reg + 1; + if (count_reg == m_axi_awlen) begin + hold_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRANSFER_OUT; + end + end else begin + state_next = STATE_TRANSFER_OUT; + end + end + endcase + end + + always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + hold_reg <= 1'b1; + m_axi_awvalid_reg <= 1'b0; + s_axi_awready_reg <= 1'b0; + end else begin + state_reg <= state_next; + hold_reg <= hold_next; + m_axi_awvalid_reg <= m_axi_awvalid_next; + s_axi_awready_reg <= s_axi_awready_next; + end + + count_reg <= count_next; + + m_axi_awid_reg <= m_axi_awid_next; + m_axi_awaddr_reg <= m_axi_awaddr_next; + m_axi_awlen_reg <= m_axi_awlen_next; + m_axi_awsize_reg <= m_axi_awsize_next; + m_axi_awburst_reg <= m_axi_awburst_next; + m_axi_awlock_reg <= m_axi_awlock_next; + m_axi_awcache_reg <= m_axi_awcache_next; + m_axi_awprot_reg <= m_axi_awprot_next; + m_axi_awqos_reg <= m_axi_awqos_next; + m_axi_awregion_reg <= m_axi_awregion_next; + m_axi_awuser_reg <= m_axi_awuser_next; + end +end else begin + // bypass AW channel + assign m_axi_awid = s_axi_awid; + assign m_axi_awaddr = s_axi_awaddr; + assign m_axi_awlen = s_axi_awlen; + assign m_axi_awsize = s_axi_awsize; + assign m_axi_awburst = s_axi_awburst; + assign m_axi_awlock = s_axi_awlock; + assign m_axi_awcache = s_axi_awcache; + assign m_axi_awprot = s_axi_awprot; + assign m_axi_awqos = s_axi_awqos; + assign m_axi_awregion = s_axi_awregion; + assign m_axi_awuser = AWUSER_ENABLE ? s_axi_awuser : {AWUSER_WIDTH{1'b0}}; + assign m_axi_awvalid = s_axi_awvalid; + assign s_axi_awready = m_axi_awready; + + assign hold = 1'b0; +end + +endgenerate + +// bypass B channel +assign s_axi_bid = m_axi_bid; +assign s_axi_bresp = m_axi_bresp; +assign s_axi_buser = BUSER_ENABLE ? m_axi_buser : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = m_axi_bvalid; +assign m_axi_bready = s_axi_bready; + +assign m_axi_wvalid = m_axi_wvalid_reg; + +assign m_axi_wdata = m_axi_w_reg[DATA_WIDTH-1:0]; +assign m_axi_wstrb = m_axi_w_reg[STRB_OFFSET +: STRB_WIDTH]; +assign m_axi_wlast = m_axi_w_reg[LAST_OFFSET]; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_w_reg[WUSER_OFFSET +: WUSER_WIDTH] : {WUSER_WIDTH{1'b0}}; + +// Write logic +always @* begin + write = 1'b0; + + wr_ptr_next = wr_ptr_reg; + + if (s_axi_wvalid) begin + // input data valid + if (!full && !hold) begin + // not full, perform write + write = 1'b1; + wr_ptr_next = wr_ptr_reg + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + wr_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + wr_ptr_reg <= wr_ptr_next; + end + + wr_addr_reg <= wr_ptr_next; + + if (write) begin + mem[wr_addr_reg[FIFO_ADDR_WIDTH-1:0]] <= s_axi_w; + end +end + +// Read logic +always @* begin + read = 1'b0; + + rd_ptr_next = rd_ptr_reg; + + mem_read_data_valid_next = mem_read_data_valid_reg; + + if (store_output || !mem_read_data_valid_reg) begin + // output data not valid OR currently being transferred + if (!empty) begin + // not empty, perform read + read = 1'b1; + mem_read_data_valid_next = 1'b1; + rd_ptr_next = rd_ptr_reg + 1; + end else begin + // empty, invalidate + mem_read_data_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + rd_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + mem_read_data_valid_reg <= 1'b0; + end else begin + rd_ptr_reg <= rd_ptr_next; + mem_read_data_valid_reg <= mem_read_data_valid_next; + end + + rd_addr_reg <= rd_ptr_next; + + if (read) begin + mem_read_data_reg <= mem[rd_addr_reg[FIFO_ADDR_WIDTH-1:0]]; + end +end + +// Output register +always @* begin + store_output = 1'b0; + + m_axi_wvalid_next = m_axi_wvalid_reg; + + if (m_axi_wready || !m_axi_wvalid) begin + store_output = 1'b1; + m_axi_wvalid_next = mem_read_data_valid_reg; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + end + + if (store_output) begin + m_axi_w_reg <= mem_read_data_reg; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_interconnect.v b/corundum/lib/axi/rtl/axi_interconnect.v new file mode 100644 index 0000000000000000000000000000000000000000..dc9fd64381a019925bb4accb70ca3b46cdb68ea0 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_interconnect.v @@ -0,0 +1,941 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 interconnect + */ +module axi_interconnect # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Propagate ID field + parameter FORWARD_ID = 0, + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interfaces + */ + input wire [S_COUNT*ID_WIDTH-1:0] s_axi_awid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [S_COUNT*8-1:0] s_axi_awlen, + input wire [S_COUNT*3-1:0] s_axi_awsize, + input wire [S_COUNT*2-1:0] s_axi_awburst, + input wire [S_COUNT-1:0] s_axi_awlock, + input wire [S_COUNT*4-1:0] s_axi_awcache, + input wire [S_COUNT*3-1:0] s_axi_awprot, + input wire [S_COUNT*4-1:0] s_axi_awqos, + input wire [S_COUNT*AWUSER_WIDTH-1:0] s_axi_awuser, + input wire [S_COUNT-1:0] s_axi_awvalid, + output wire [S_COUNT-1:0] s_axi_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axi_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axi_wstrb, + input wire [S_COUNT-1:0] s_axi_wlast, + input wire [S_COUNT*WUSER_WIDTH-1:0] s_axi_wuser, + input wire [S_COUNT-1:0] s_axi_wvalid, + output wire [S_COUNT-1:0] s_axi_wready, + output wire [S_COUNT*ID_WIDTH-1:0] s_axi_bid, + output wire [S_COUNT*2-1:0] s_axi_bresp, + output wire [S_COUNT*BUSER_WIDTH-1:0] s_axi_buser, + output wire [S_COUNT-1:0] s_axi_bvalid, + input wire [S_COUNT-1:0] s_axi_bready, + input wire [S_COUNT*ID_WIDTH-1:0] s_axi_arid, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axi_araddr, + input wire [S_COUNT*8-1:0] s_axi_arlen, + input wire [S_COUNT*3-1:0] s_axi_arsize, + input wire [S_COUNT*2-1:0] s_axi_arburst, + input wire [S_COUNT-1:0] s_axi_arlock, + input wire [S_COUNT*4-1:0] s_axi_arcache, + input wire [S_COUNT*3-1:0] s_axi_arprot, + input wire [S_COUNT*4-1:0] s_axi_arqos, + input wire [S_COUNT*ARUSER_WIDTH-1:0] s_axi_aruser, + input wire [S_COUNT-1:0] s_axi_arvalid, + output wire [S_COUNT-1:0] s_axi_arready, + output wire [S_COUNT*ID_WIDTH-1:0] s_axi_rid, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axi_rdata, + output wire [S_COUNT*2-1:0] s_axi_rresp, + output wire [S_COUNT-1:0] s_axi_rlast, + output wire [S_COUNT*RUSER_WIDTH-1:0] s_axi_ruser, + output wire [S_COUNT-1:0] s_axi_rvalid, + input wire [S_COUNT-1:0] s_axi_rready, + + /* + * AXI master interfaces + */ + output wire [M_COUNT*ID_WIDTH-1:0] m_axi_awid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [M_COUNT*8-1:0] m_axi_awlen, + output wire [M_COUNT*3-1:0] m_axi_awsize, + output wire [M_COUNT*2-1:0] m_axi_awburst, + output wire [M_COUNT-1:0] m_axi_awlock, + output wire [M_COUNT*4-1:0] m_axi_awcache, + output wire [M_COUNT*3-1:0] m_axi_awprot, + output wire [M_COUNT*4-1:0] m_axi_awqos, + output wire [M_COUNT*4-1:0] m_axi_awregion, + output wire [M_COUNT*AWUSER_WIDTH-1:0] m_axi_awuser, + output wire [M_COUNT-1:0] m_axi_awvalid, + input wire [M_COUNT-1:0] m_axi_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axi_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axi_wstrb, + output wire [M_COUNT-1:0] m_axi_wlast, + output wire [M_COUNT*WUSER_WIDTH-1:0] m_axi_wuser, + output wire [M_COUNT-1:0] m_axi_wvalid, + input wire [M_COUNT-1:0] m_axi_wready, + input wire [M_COUNT*ID_WIDTH-1:0] m_axi_bid, + input wire [M_COUNT*2-1:0] m_axi_bresp, + input wire [M_COUNT*BUSER_WIDTH-1:0] m_axi_buser, + input wire [M_COUNT-1:0] m_axi_bvalid, + output wire [M_COUNT-1:0] m_axi_bready, + output wire [M_COUNT*ID_WIDTH-1:0] m_axi_arid, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axi_araddr, + output wire [M_COUNT*8-1:0] m_axi_arlen, + output wire [M_COUNT*3-1:0] m_axi_arsize, + output wire [M_COUNT*2-1:0] m_axi_arburst, + output wire [M_COUNT-1:0] m_axi_arlock, + output wire [M_COUNT*4-1:0] m_axi_arcache, + output wire [M_COUNT*3-1:0] m_axi_arprot, + output wire [M_COUNT*4-1:0] m_axi_arqos, + output wire [M_COUNT*4-1:0] m_axi_arregion, + output wire [M_COUNT*ARUSER_WIDTH-1:0] m_axi_aruser, + output wire [M_COUNT-1:0] m_axi_arvalid, + input wire [M_COUNT-1:0] m_axi_arready, + input wire [M_COUNT*ID_WIDTH-1:0] m_axi_rid, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axi_rdata, + input wire [M_COUNT*2-1:0] m_axi_rresp, + input wire [M_COUNT-1:0] m_axi_rlast, + input wire [M_COUNT*RUSER_WIDTH-1:0] m_axi_ruser, + input wire [M_COUNT-1:0] m_axi_rvalid, + output wire [M_COUNT-1:0] m_axi_rready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +parameter AUSER_WIDTH = AWUSER_WIDTH > ARUSER_WIDTH ? AWUSER_WIDTH : ARUSER_WIDTH; + +// default address computation +function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy); + integer i; + reg [ADDR_WIDTH-1:0] base; + begin + calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}}; + base = 0; + for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment + base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align + calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base; + end + end + end +endfunction + +parameter M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); + +integer i, j; + +// check configuration +initial begin + if (M_REGIONS < 1 || M_REGIONS > 16) begin + $error("Error: M_REGIONS must be between 1 and 16 (instance %m)"); + $finish; + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 12 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: address width out of range (instance %m)"); + $finish; + end + end + + $display("Addressing configuration for axi_interconnect instance %m"); + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin + if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin + $display("Overlapping regions:"); + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + $display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))); + $error("Error: address ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_DECODE = 3'd1, + STATE_WRITE = 3'd2, + STATE_WRITE_RESP = 3'd3, + STATE_WRITE_DROP = 3'd4, + STATE_READ = 3'd5, + STATE_READ_DROP = 3'd6, + STATE_WAIT_IDLE = 3'd7; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg match; + +reg [CL_M_COUNT-1:0] m_select_reg = 2'd0, m_select_next; +reg [ID_WIDTH-1:0] axi_id_reg = {ID_WIDTH{1'b0}}, axi_id_next; +reg [ADDR_WIDTH-1:0] axi_addr_reg = {ADDR_WIDTH{1'b0}}, axi_addr_next; +reg axi_addr_valid_reg = 1'b0, axi_addr_valid_next; +reg [7:0] axi_len_reg = 8'd0, axi_len_next; +reg [2:0] axi_size_reg = 3'd0, axi_size_next; +reg [1:0] axi_burst_reg = 2'd0, axi_burst_next; +reg axi_lock_reg = 1'b0, axi_lock_next; +reg [3:0] axi_cache_reg = 4'd0, axi_cache_next; +reg [2:0] axi_prot_reg = 3'b000, axi_prot_next; +reg [3:0] axi_qos_reg = 4'd0, axi_qos_next; +reg [3:0] axi_region_reg = 4'd0, axi_region_next; +reg [AUSER_WIDTH-1:0] axi_auser_reg = {AUSER_WIDTH{1'b0}}, axi_auser_next; +reg [1:0] axi_bresp_reg = 2'b00, axi_bresp_next; +reg [BUSER_WIDTH-1:0] axi_buser_reg = {BUSER_WIDTH{1'b0}}, axi_buser_next; + +reg [S_COUNT-1:0] s_axi_awready_reg = 0, s_axi_awready_next; +reg [S_COUNT-1:0] s_axi_wready_reg = 0, s_axi_wready_next; +reg [S_COUNT-1:0] s_axi_bvalid_reg = 0, s_axi_bvalid_next; +reg [S_COUNT-1:0] s_axi_arready_reg = 0, s_axi_arready_next; + +reg [M_COUNT-1:0] m_axi_awvalid_reg = 0, m_axi_awvalid_next; +reg [M_COUNT-1:0] m_axi_bready_reg = 0, m_axi_bready_next; +reg [M_COUNT-1:0] m_axi_arvalid_reg = 0, m_axi_arvalid_next; +reg [M_COUNT-1:0] m_axi_rready_reg = 0, m_axi_rready_next; + +// internal datapath +reg [ID_WIDTH-1:0] s_axi_rid_int; +reg [DATA_WIDTH-1:0] s_axi_rdata_int; +reg [1:0] s_axi_rresp_int; +reg s_axi_rlast_int; +reg [RUSER_WIDTH-1:0] s_axi_ruser_int; +reg s_axi_rvalid_int; +reg s_axi_rready_int_reg = 1'b0; +wire s_axi_rready_int_early; + +reg [DATA_WIDTH-1:0] m_axi_wdata_int; +reg [STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wlast_int; +reg [WUSER_WIDTH-1:0] m_axi_wuser_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +wire m_axi_wready_int_early; + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = s_axi_wready_reg; +assign s_axi_bid = {S_COUNT{axi_id_reg}}; +assign s_axi_bresp = {S_COUNT{axi_bresp_reg}}; +assign s_axi_buser = {S_COUNT{BUSER_ENABLE ? axi_buser_reg : {BUSER_WIDTH{1'b0}}}}; +assign s_axi_bvalid = s_axi_bvalid_reg; +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_awid = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; +assign m_axi_awaddr = {M_COUNT{axi_addr_reg}}; +assign m_axi_awlen = {M_COUNT{axi_len_reg}}; +assign m_axi_awsize = {M_COUNT{axi_size_reg}}; +assign m_axi_awburst = {M_COUNT{axi_burst_reg}}; +assign m_axi_awlock = {M_COUNT{axi_lock_reg}}; +assign m_axi_awcache = {M_COUNT{axi_cache_reg}}; +assign m_axi_awprot = {M_COUNT{axi_prot_reg}}; +assign m_axi_awqos = {M_COUNT{axi_qos_reg}}; +assign m_axi_awregion = {M_COUNT{axi_region_reg}}; +assign m_axi_awuser = {M_COUNT{AWUSER_ENABLE ? axi_auser_reg[AWUSER_WIDTH-1:0] : {AWUSER_WIDTH{1'b0}}}}; +assign m_axi_awvalid = m_axi_awvalid_reg; +assign m_axi_bready = m_axi_bready_reg; +assign m_axi_arid = {M_COUNT{FORWARD_ID ? axi_id_reg : {ID_WIDTH{1'b0}}}}; +assign m_axi_araddr = {M_COUNT{axi_addr_reg}}; +assign m_axi_arlen = {M_COUNT{axi_len_reg}}; +assign m_axi_arsize = {M_COUNT{axi_size_reg}}; +assign m_axi_arburst = {M_COUNT{axi_burst_reg}}; +assign m_axi_arlock = {M_COUNT{axi_lock_reg}}; +assign m_axi_arcache = {M_COUNT{axi_cache_reg}}; +assign m_axi_arprot = {M_COUNT{axi_prot_reg}}; +assign m_axi_arqos = {M_COUNT{axi_qos_reg}}; +assign m_axi_arregion = {M_COUNT{axi_region_reg}}; +assign m_axi_aruser = {M_COUNT{ARUSER_ENABLE ? axi_auser_reg[ARUSER_WIDTH-1:0] : {ARUSER_WIDTH{1'b0}}}}; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +// slave side mux +wire [(CL_S_COUNT > 0 ? CL_S_COUNT-1 : 0):0] s_select; + +wire [ID_WIDTH-1:0] current_s_axi_awid = s_axi_awid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_s_axi_awaddr = s_axi_awaddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_s_axi_awlen = s_axi_awlen[s_select*8 +: 8]; +wire [2:0] current_s_axi_awsize = s_axi_awsize[s_select*3 +: 3]; +wire [1:0] current_s_axi_awburst = s_axi_awburst[s_select*2 +: 2]; +wire current_s_axi_awlock = s_axi_awlock[s_select]; +wire [3:0] current_s_axi_awcache = s_axi_awcache[s_select*4 +: 4]; +wire [2:0] current_s_axi_awprot = s_axi_awprot[s_select*3 +: 3]; +wire [3:0] current_s_axi_awqos = s_axi_awqos[s_select*4 +: 4]; +wire [AWUSER_WIDTH-1:0] current_s_axi_awuser = s_axi_awuser[s_select*AWUSER_WIDTH +: AWUSER_WIDTH]; +wire current_s_axi_awvalid = s_axi_awvalid[s_select]; +wire current_s_axi_awready = s_axi_awready[s_select]; +wire [DATA_WIDTH-1:0] current_s_axi_wdata = s_axi_wdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_s_axi_wstrb = s_axi_wstrb[s_select*STRB_WIDTH +: STRB_WIDTH]; +wire current_s_axi_wlast = s_axi_wlast[s_select]; +wire [WUSER_WIDTH-1:0] current_s_axi_wuser = s_axi_wuser[s_select*WUSER_WIDTH +: WUSER_WIDTH]; +wire current_s_axi_wvalid = s_axi_wvalid[s_select]; +wire current_s_axi_wready = s_axi_wready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_bid = s_axi_bid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [1:0] current_s_axi_bresp = s_axi_bresp[s_select*2 +: 2]; +wire [BUSER_WIDTH-1:0] current_s_axi_buser = s_axi_buser[s_select*BUSER_WIDTH +: BUSER_WIDTH]; +wire current_s_axi_bvalid = s_axi_bvalid[s_select]; +wire current_s_axi_bready = s_axi_bready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_arid = s_axi_arid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_s_axi_araddr = s_axi_araddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_s_axi_arlen = s_axi_arlen[s_select*8 +: 8]; +wire [2:0] current_s_axi_arsize = s_axi_arsize[s_select*3 +: 3]; +wire [1:0] current_s_axi_arburst = s_axi_arburst[s_select*2 +: 2]; +wire current_s_axi_arlock = s_axi_arlock[s_select]; +wire [3:0] current_s_axi_arcache = s_axi_arcache[s_select*4 +: 4]; +wire [2:0] current_s_axi_arprot = s_axi_arprot[s_select*3 +: 3]; +wire [3:0] current_s_axi_arqos = s_axi_arqos[s_select*4 +: 4]; +wire [ARUSER_WIDTH-1:0] current_s_axi_aruser = s_axi_aruser[s_select*ARUSER_WIDTH +: ARUSER_WIDTH]; +wire current_s_axi_arvalid = s_axi_arvalid[s_select]; +wire current_s_axi_arready = s_axi_arready[s_select]; +wire [ID_WIDTH-1:0] current_s_axi_rid = s_axi_rid[s_select*ID_WIDTH +: ID_WIDTH]; +wire [DATA_WIDTH-1:0] current_s_axi_rdata = s_axi_rdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_s_axi_rresp = s_axi_rresp[s_select*2 +: 2]; +wire current_s_axi_rlast = s_axi_rlast[s_select]; +wire [RUSER_WIDTH-1:0] current_s_axi_ruser = s_axi_ruser[s_select*RUSER_WIDTH +: RUSER_WIDTH]; +wire current_s_axi_rvalid = s_axi_rvalid[s_select]; +wire current_s_axi_rready = s_axi_rready[s_select]; + +// master side mux +wire [ID_WIDTH-1:0] current_m_axi_awid = m_axi_awid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_m_axi_awaddr = m_axi_awaddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_m_axi_awlen = m_axi_awlen[m_select_reg*8 +: 8]; +wire [2:0] current_m_axi_awsize = m_axi_awsize[m_select_reg*3 +: 3]; +wire [1:0] current_m_axi_awburst = m_axi_awburst[m_select_reg*2 +: 2]; +wire current_m_axi_awlock = m_axi_awlock[m_select_reg]; +wire [3:0] current_m_axi_awcache = m_axi_awcache[m_select_reg*4 +: 4]; +wire [2:0] current_m_axi_awprot = m_axi_awprot[m_select_reg*3 +: 3]; +wire [3:0] current_m_axi_awqos = m_axi_awqos[m_select_reg*4 +: 4]; +wire [3:0] current_m_axi_awregion = m_axi_awregion[m_select_reg*4 +: 4]; +wire [AWUSER_WIDTH-1:0] current_m_axi_awuser = m_axi_awuser[m_select_reg*AWUSER_WIDTH +: AWUSER_WIDTH]; +wire current_m_axi_awvalid = m_axi_awvalid[m_select_reg]; +wire current_m_axi_awready = m_axi_awready[m_select_reg]; +wire [DATA_WIDTH-1:0] current_m_axi_wdata = m_axi_wdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_m_axi_wstrb = m_axi_wstrb[m_select_reg*STRB_WIDTH +: STRB_WIDTH]; +wire current_m_axi_wlast = m_axi_wlast[m_select_reg]; +wire [WUSER_WIDTH-1:0] current_m_axi_wuser = m_axi_wuser[m_select_reg*WUSER_WIDTH +: WUSER_WIDTH]; +wire current_m_axi_wvalid = m_axi_wvalid[m_select_reg]; +wire current_m_axi_wready = m_axi_wready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_bid = m_axi_bid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [1:0] current_m_axi_bresp = m_axi_bresp[m_select_reg*2 +: 2]; +wire [BUSER_WIDTH-1:0] current_m_axi_buser = m_axi_buser[m_select_reg*BUSER_WIDTH +: BUSER_WIDTH]; +wire current_m_axi_bvalid = m_axi_bvalid[m_select_reg]; +wire current_m_axi_bready = m_axi_bready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_arid = m_axi_arid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [ADDR_WIDTH-1:0] current_m_axi_araddr = m_axi_araddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [7:0] current_m_axi_arlen = m_axi_arlen[m_select_reg*8 +: 8]; +wire [2:0] current_m_axi_arsize = m_axi_arsize[m_select_reg*3 +: 3]; +wire [1:0] current_m_axi_arburst = m_axi_arburst[m_select_reg*2 +: 2]; +wire current_m_axi_arlock = m_axi_arlock[m_select_reg]; +wire [3:0] current_m_axi_arcache = m_axi_arcache[m_select_reg*4 +: 4]; +wire [2:0] current_m_axi_arprot = m_axi_arprot[m_select_reg*3 +: 3]; +wire [3:0] current_m_axi_arqos = m_axi_arqos[m_select_reg*4 +: 4]; +wire [3:0] current_m_axi_arregion = m_axi_arregion[m_select_reg*4 +: 4]; +wire [ARUSER_WIDTH-1:0] current_m_axi_aruser = m_axi_aruser[m_select_reg*ARUSER_WIDTH +: ARUSER_WIDTH]; +wire current_m_axi_arvalid = m_axi_arvalid[m_select_reg]; +wire current_m_axi_arready = m_axi_arready[m_select_reg]; +wire [ID_WIDTH-1:0] current_m_axi_rid = m_axi_rid[m_select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DATA_WIDTH-1:0] current_m_axi_rdata = m_axi_rdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_m_axi_rresp = m_axi_rresp[m_select_reg*2 +: 2]; +wire current_m_axi_rlast = m_axi_rlast[m_select_reg]; +wire [RUSER_WIDTH-1:0] current_m_axi_ruser = m_axi_ruser[m_select_reg*RUSER_WIDTH +: RUSER_WIDTH]; +wire current_m_axi_rvalid = m_axi_rvalid[m_select_reg]; +wire current_m_axi_rready = m_axi_rready[m_select_reg]; + +// arbiter instance +wire [S_COUNT*2-1:0] request; +wire [S_COUNT*2-1:0] acknowledge; +wire [S_COUNT*2-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT:0] grant_encoded; + +wire read = grant_encoded[0]; +assign s_select = grant_encoded >> 1; + +arbiter #( + .PORTS(S_COUNT*2), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +genvar n; + +// request generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign request[2*n] = s_axi_awvalid[n]; + assign request[2*n+1] = s_axi_arvalid[n]; +end +endgenerate + +// acknowledge generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign acknowledge[2*n] = grant[2*n] && s_axi_bvalid[n] && s_axi_bready[n]; + assign acknowledge[2*n+1] = grant[2*n+1] && s_axi_rvalid[n] && s_axi_rready[n] && s_axi_rlast[n]; +end +endgenerate + +always @* begin + state_next = STATE_IDLE; + + match = 1'b0; + + m_select_next = m_select_reg; + axi_id_next = axi_id_reg; + axi_addr_next = axi_addr_reg; + axi_addr_valid_next = axi_addr_valid_reg; + axi_len_next = axi_len_reg; + axi_size_next = axi_size_reg; + axi_burst_next = axi_burst_reg; + axi_lock_next = axi_lock_reg; + axi_cache_next = axi_cache_reg; + axi_prot_next = axi_prot_reg; + axi_qos_next = axi_qos_reg; + axi_region_next = axi_region_reg; + axi_auser_next = axi_auser_reg; + axi_bresp_next = axi_bresp_reg; + axi_buser_next = axi_buser_reg; + + s_axi_awready_next = 0; + s_axi_wready_next = 0; + s_axi_bvalid_next = s_axi_bvalid_reg & ~s_axi_bready; + s_axi_arready_next = 0; + + m_axi_awvalid_next = m_axi_awvalid_reg & ~m_axi_awready; + m_axi_bready_next = 0; + m_axi_arvalid_next = m_axi_arvalid_reg & ~m_axi_arready; + m_axi_rready_next = 0; + + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = current_m_axi_rdata; + s_axi_rresp_int = current_m_axi_rresp; + s_axi_rlast_int = current_m_axi_rlast; + s_axi_ruser_int = current_m_axi_ruser; + s_axi_rvalid_int = 1'b0; + + m_axi_wdata_int = current_s_axi_wdata; + m_axi_wstrb_int = current_s_axi_wstrb; + m_axi_wlast_int = current_s_axi_wlast; + m_axi_wuser_int = current_s_axi_wuser; + m_axi_wvalid_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for arbitration + + if (grant_valid) begin + + axi_addr_valid_next = 1'b1; + + if (read) begin + // reading + axi_addr_next = current_s_axi_araddr; + axi_prot_next = current_s_axi_arprot; + axi_id_next = current_s_axi_arid; + axi_addr_next = current_s_axi_araddr; + axi_len_next = current_s_axi_arlen; + axi_size_next = current_s_axi_arsize; + axi_burst_next = current_s_axi_arburst; + axi_lock_next = current_s_axi_arlock; + axi_cache_next = current_s_axi_arcache; + axi_prot_next = current_s_axi_arprot; + axi_qos_next = current_s_axi_arqos; + axi_auser_next = current_s_axi_aruser; + s_axi_arready_next[s_select] = 1'b1; + end else begin + // writing + axi_addr_next = current_s_axi_awaddr; + axi_prot_next = current_s_axi_awprot; + axi_id_next = current_s_axi_awid; + axi_addr_next = current_s_axi_awaddr; + axi_len_next = current_s_axi_awlen; + axi_size_next = current_s_axi_awsize; + axi_burst_next = current_s_axi_awburst; + axi_lock_next = current_s_axi_awlock; + axi_cache_next = current_s_axi_awcache; + axi_prot_next = current_s_axi_awprot; + axi_qos_next = current_s_axi_awqos; + axi_auser_next = current_s_axi_awuser; + s_axi_awready_next[s_select] = 1'b1; + end + + state_next = STATE_DECODE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DECODE: begin + // decode state; determine master interface + + match = 1'b0; + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = 0; j < M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !axi_prot_reg[1]) && ((read ? M_CONNECT_READ : M_CONNECT_WRITE) & (1 << (s_select+i*S_COUNT))) && (axi_addr_reg >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin + m_select_next = i; + axi_region_next = j; + match = 1'b1; + end + end + end + + if (match) begin + if (read) begin + // reading + m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; + state_next = STATE_READ; + end else begin + // writing + s_axi_wready_next[s_select] = m_axi_wready_int_early; + state_next = STATE_WRITE; + end + end else begin + // no match; return decode error + if (read) begin + // reading + state_next = STATE_READ_DROP; + end else begin + // writing + axi_bresp_next = 2'b11; + s_axi_wready_next[s_select] = 1'b1; + state_next = STATE_WRITE_DROP; + end + end + end + STATE_WRITE: begin + // write state; store and forward write data + s_axi_wready_next[s_select] = m_axi_wready_int_early; + + if (axi_addr_valid_reg) begin + m_axi_awvalid_next[m_select_reg] = 1'b1; + end + axi_addr_valid_next = 1'b0; + + if (current_s_axi_wready && current_s_axi_wvalid) begin + m_axi_wdata_int = current_s_axi_wdata; + m_axi_wstrb_int = current_s_axi_wstrb; + m_axi_wlast_int = current_s_axi_wlast; + m_axi_wuser_int = current_s_axi_wuser; + m_axi_wvalid_int = 1'b1; + + if (current_s_axi_wlast) begin + s_axi_wready_next[s_select] = 1'b0; + m_axi_bready_next[m_select_reg] = 1'b1; + state_next = STATE_WRITE_RESP; + end else begin + state_next = STATE_WRITE; + end + end else begin + state_next = STATE_WRITE; + end + end + STATE_WRITE_RESP: begin + // write response state; store and forward write response + m_axi_bready_next[m_select_reg] = 1'b1; + + if (current_m_axi_bready && current_m_axi_bvalid) begin + m_axi_bready_next[m_select_reg] = 1'b0; + axi_bresp_next = current_m_axi_bresp; + s_axi_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_RESP; + end + end + STATE_WRITE_DROP: begin + // write drop state; drop write data + s_axi_wready_next[s_select] = 1'b1; + + axi_addr_valid_next = 1'b0; + + if (current_s_axi_wready && current_s_axi_wvalid) begin + s_axi_wready_next[s_select] = 1'b0; + s_axi_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_DROP; + end + end + STATE_READ: begin + // read state; store and forward read response + m_axi_rready_next[m_select_reg] = s_axi_rready_int_early; + + if (axi_addr_valid_reg) begin + m_axi_arvalid_next[m_select_reg] = 1'b1; + end + axi_addr_valid_next = 1'b0; + + if (current_m_axi_rready && current_m_axi_rvalid) begin + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = current_m_axi_rdata; + s_axi_rresp_int = current_m_axi_rresp; + s_axi_rlast_int = current_m_axi_rlast; + s_axi_ruser_int = current_m_axi_ruser; + s_axi_rvalid_int = 1'b1; + + if (current_m_axi_rlast) begin + m_axi_rready_next[m_select_reg] = 1'b0; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_READ; + end + end else begin + state_next = STATE_READ; + end + end + STATE_READ_DROP: begin + // read drop state; generate decode error read response + + s_axi_rid_int = axi_id_reg; + s_axi_rdata_int = {DATA_WIDTH{1'b0}}; + s_axi_rresp_int = 2'b11; + s_axi_rlast_int = axi_len_reg == 0; + s_axi_ruser_int = {RUSER_WIDTH{1'b0}}; + s_axi_rvalid_int = 1'b1; + + if (s_axi_rready_int_reg) begin + axi_len_next = axi_len_reg - 1; + if (axi_len_reg == 0) begin + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_READ_DROP; + end + end else begin + state_next = STATE_READ_DROP; + end + end + STATE_WAIT_IDLE: begin + // wait for idle state; wait untl grant valid is deasserted + + if (!grant_valid || acknowledge) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_IDLE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axi_awready_reg <= 0; + s_axi_wready_reg <= 0; + s_axi_bvalid_reg <= 0; + s_axi_arready_reg <= 0; + + m_axi_awvalid_reg <= 0; + m_axi_bready_reg <= 0; + m_axi_arvalid_reg <= 0; + m_axi_rready_reg <= 0; + end else begin + state_reg <= state_next; + + s_axi_awready_reg <= s_axi_awready_next; + s_axi_wready_reg <= s_axi_wready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + s_axi_arready_reg <= s_axi_arready_next; + + m_axi_awvalid_reg <= m_axi_awvalid_next; + m_axi_bready_reg <= m_axi_bready_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + end + + m_select_reg <= m_select_next; + axi_id_reg <= axi_id_next; + axi_addr_reg <= axi_addr_next; + axi_addr_valid_reg <= axi_addr_valid_next; + axi_len_reg <= axi_len_next; + axi_size_reg <= axi_size_next; + axi_burst_reg <= axi_burst_next; + axi_lock_reg <= axi_lock_next; + axi_cache_reg <= axi_cache_next; + axi_prot_reg <= axi_prot_next; + axi_qos_reg <= axi_qos_next; + axi_region_reg <= axi_region_next; + axi_auser_reg <= axi_auser_next; + axi_bresp_reg <= axi_bresp_next; + axi_buser_reg <= axi_buser_next; +end + +// output datapath logic (R channel) +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'd0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = 1'b0; +reg [S_COUNT-1:0] s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] temp_s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_rresp_reg = 2'd0; +reg temp_s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg = 1'b0; +reg temp_s_axi_rvalid_reg = 1'b0, temp_s_axi_rvalid_next; + +// datapath control +reg store_axi_r_int_to_output; +reg store_axi_r_int_to_temp; +reg store_axi_r_temp_to_output; + +assign s_axi_rid = {S_COUNT{s_axi_rid_reg}}; +assign s_axi_rdata = {S_COUNT{s_axi_rdata_reg}}; +assign s_axi_rresp = {S_COUNT{s_axi_rresp_reg}}; +assign s_axi_rlast = {S_COUNT{s_axi_rlast_reg}}; +assign s_axi_ruser = {S_COUNT{RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign s_axi_rready_int_early = current_s_axi_rready | (~temp_s_axi_rvalid_reg & (~current_s_axi_rvalid | ~s_axi_rvalid_int)); + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; + + store_axi_r_int_to_output = 1'b0; + store_axi_r_int_to_temp = 1'b0; + store_axi_r_temp_to_output = 1'b0; + + if (s_axi_rready_int_reg) begin + // input is ready + if (current_s_axi_rready | ~current_s_axi_rvalid) begin + // output is ready or currently not valid, transfer data to output + s_axi_rvalid_next[s_select] = s_axi_rvalid_int; + store_axi_r_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_rvalid_next = s_axi_rvalid_int; + store_axi_r_int_to_temp = 1'b1; + end + end else if (current_s_axi_rready) begin + // input is not ready, but output is ready + s_axi_rvalid_next[s_select] = temp_s_axi_rvalid_reg; + temp_s_axi_rvalid_next = 1'b0; + store_axi_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_rvalid_reg <= 1'b0; + s_axi_rready_int_reg <= 1'b0; + temp_s_axi_rvalid_reg <= 1'b0; + end else begin + s_axi_rvalid_reg <= s_axi_rvalid_next; + s_axi_rready_int_reg <= s_axi_rready_int_early; + temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_int_to_output) begin + s_axi_rid_reg <= s_axi_rid_int; + s_axi_rdata_reg <= s_axi_rdata_int; + s_axi_rresp_reg <= s_axi_rresp_int; + s_axi_rlast_reg <= s_axi_rlast_int; + s_axi_ruser_reg <= s_axi_ruser_int; + end else if (store_axi_r_temp_to_output) begin + s_axi_rid_reg <= temp_s_axi_rid_reg; + s_axi_rdata_reg <= temp_s_axi_rdata_reg; + s_axi_rresp_reg <= temp_s_axi_rresp_reg; + s_axi_rlast_reg <= temp_s_axi_rlast_reg; + s_axi_ruser_reg <= temp_s_axi_ruser_reg; + end + + if (store_axi_r_int_to_temp) begin + temp_s_axi_rid_reg <= s_axi_rid_int; + temp_s_axi_rdata_reg <= s_axi_rdata_int; + temp_s_axi_rresp_reg <= s_axi_rresp_int; + temp_s_axi_rlast_reg <= s_axi_rlast_int; + temp_s_axi_ruser_reg <= s_axi_ruser_int; + end +end + +// output datapath logic (W channel) +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = 1'b0; +reg [M_COUNT-1:0] m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = 1'b0; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = {M_COUNT{m_axi_wdata_reg}}; +assign m_axi_wstrb = {M_COUNT{m_axi_wstrb_reg}}; +assign m_axi_wlast = {M_COUNT{m_axi_wlast_reg}}; +assign m_axi_wuser = {M_COUNT{WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = current_m_axi_wready | (~temp_m_axi_wvalid_reg & (~current_m_axi_wvalid | ~m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (current_m_axi_wready | ~current_m_axi_wvalid) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next[m_select_reg] = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (current_m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next[m_select_reg] = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + m_axi_wuser_reg <= m_axi_wuser_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + m_axi_wuser_reg <= temp_m_axi_wuser_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + temp_m_axi_wuser_reg <= m_axi_wuser_int; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_ram.v b/corundum/lib/axi/rtl/axi_ram.v new file mode 100644 index 0000000000000000000000000000000000000000..6a1a25e214a73d99baffbc5cdda62d74c706427d --- /dev/null +++ b/corundum/lib/axi/rtl/axi_ram.v @@ -0,0 +1,370 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 RAM + */ +module axi_ram # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Extra pipeline register on output + parameter PIPELINE_OUTPUT = 0 +) +( + input wire clk, + input wire rst, + + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire s_axi_rvalid, + input wire s_axi_rready +); + +parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); +parameter WORD_WIDTH = STRB_WIDTH; +parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH; + +// bus width assertions +initial begin + if (WORD_SIZE * STRB_WIDTH != DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (2**$clog2(WORD_WIDTH) != WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [0:0] + READ_STATE_IDLE = 1'd0, + READ_STATE_BURST = 1'd1; + +reg [0:0] read_state_reg = READ_STATE_IDLE, read_state_next; + +localparam [1:0] + WRITE_STATE_IDLE = 2'd0, + WRITE_STATE_BURST = 2'd1, + WRITE_STATE_RESP = 2'd2; + +reg [1:0] write_state_reg = WRITE_STATE_IDLE, write_state_next; + +reg mem_wr_en; +reg mem_rd_en; + +reg [ID_WIDTH-1:0] read_id_reg = {ID_WIDTH{1'b0}}, read_id_next; +reg [ADDR_WIDTH-1:0] read_addr_reg = {ADDR_WIDTH{1'b0}}, read_addr_next; +reg [7:0] read_count_reg = 8'd0, read_count_next; +reg [2:0] read_size_reg = 3'd0, read_size_next; +reg [1:0] read_burst_reg = 2'd0, read_burst_next; +reg [ID_WIDTH-1:0] write_id_reg = {ID_WIDTH{1'b0}}, write_id_next; +reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next; +reg [7:0] write_count_reg = 8'd0, write_count_next; +reg [2:0] write_size_reg = 3'd0, write_size_next; +reg [1:0] write_burst_reg = 2'd0, write_burst_next; + +reg s_axi_awready_reg = 1'b0, s_axi_awready_next; +reg s_axi_wready_reg = 1'b0, s_axi_wready_next; +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}, s_axi_bid_next; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; +reg s_axi_arready_reg = 1'b0, s_axi_arready_next; +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}, s_axi_rid_next; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}, s_axi_rdata_next; +reg s_axi_rlast_reg = 1'b0, s_axi_rlast_next; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; +reg [ID_WIDTH-1:0] s_axi_rid_pipe_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_pipe_reg = {DATA_WIDTH{1'b0}}; +reg s_axi_rlast_pipe_reg = 1'b0; +reg s_axi_rvalid_pipe_reg = 1'b0; + +// (* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH-1:0] mem[(2**VALID_ADDR_WIDTH)-1:0]; + +wire [VALID_ADDR_WIDTH-1:0] s_axi_awaddr_valid = s_axi_awaddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] s_axi_araddr_valid = s_axi_araddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] read_addr_valid = read_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] write_addr_valid = write_addr_reg >> (ADDR_WIDTH - VALID_ADDR_WIDTH); + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = s_axi_wready_reg; +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = 2'b00; +assign s_axi_bvalid = s_axi_bvalid_reg; +assign s_axi_arready = s_axi_arready_reg; +assign s_axi_rid = PIPELINE_OUTPUT ? s_axi_rid_pipe_reg : s_axi_rid_reg; +assign s_axi_rdata = PIPELINE_OUTPUT ? s_axi_rdata_pipe_reg : s_axi_rdata_reg; +assign s_axi_rresp = 2'b00; +assign s_axi_rlast = PIPELINE_OUTPUT ? s_axi_rlast_pipe_reg : s_axi_rlast_reg; +assign s_axi_rvalid = PIPELINE_OUTPUT ? s_axi_rvalid_pipe_reg : s_axi_rvalid_reg; + +integer i, j; + +initial begin + // two nested loops for smaller number of iterations per loop + // workaround for synthesizer complaints about large loop counts + for (i = 0; i < 2**ADDR_WIDTH; i = i + 2**(ADDR_WIDTH/2)) begin + for (j = i; j < i + 2**(ADDR_WIDTH/2); j = j + 1) begin + mem[j] = 0; + end + end +end + +always @* begin + write_state_next = WRITE_STATE_IDLE; + + mem_wr_en = 1'b0; + + write_id_next = write_id_reg; + write_addr_next = write_addr_reg; + write_count_next = write_count_reg; + write_size_next = write_size_reg; + write_burst_next = write_burst_reg; + + s_axi_awready_next = 1'b0; + s_axi_wready_next = 1'b0; + s_axi_bid_next = s_axi_bid_reg; + s_axi_bvalid_next = s_axi_bvalid_reg && !s_axi_bready; + + case (write_state_reg) + WRITE_STATE_IDLE: begin + s_axi_awready_next = 1'b1; + + if (s_axi_awready && s_axi_awvalid) begin + write_id_next = s_axi_awid; + write_addr_next = s_axi_awaddr; + write_count_next = s_axi_awlen; + write_size_next = s_axi_awsize < $clog2(STRB_WIDTH) ? s_axi_awsize : $clog2(STRB_WIDTH); + write_burst_next = s_axi_awburst; + + s_axi_awready_next = 1'b0; + s_axi_wready_next = 1'b1; + write_state_next = WRITE_STATE_BURST; + end else begin + write_state_next = WRITE_STATE_IDLE; + end + end + WRITE_STATE_BURST: begin + s_axi_wready_next = 1'b1; + + if (s_axi_wready && s_axi_wvalid) begin + mem_wr_en = 1'b1; + if (write_burst_reg != 2'b00) begin + write_addr_next = write_addr_reg + (1 << write_size_reg); + end + write_count_next = write_count_reg - 1; + if (write_count_reg > 0) begin + write_state_next = WRITE_STATE_BURST; + end else begin + s_axi_wready_next = 1'b0; + if (s_axi_bready || !s_axi_bvalid) begin + s_axi_bid_next = write_id_reg; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = 1'b1; + write_state_next = WRITE_STATE_IDLE; + end else begin + write_state_next = WRITE_STATE_RESP; + end + end + end else begin + write_state_next = WRITE_STATE_BURST; + end + end + WRITE_STATE_RESP: begin + if (s_axi_bready || !s_axi_bvalid) begin + s_axi_bid_next = write_id_reg; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = 1'b1; + write_state_next = WRITE_STATE_IDLE; + end else begin + write_state_next = WRITE_STATE_RESP; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + write_state_reg <= WRITE_STATE_IDLE; + s_axi_awready_reg <= 1'b0; + s_axi_wready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + end else begin + write_state_reg <= write_state_next; + s_axi_awready_reg <= s_axi_awready_next; + s_axi_wready_reg <= s_axi_wready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + end + + write_id_reg <= write_id_next; + write_addr_reg <= write_addr_next; + write_count_reg <= write_count_next; + write_size_reg <= write_size_next; + write_burst_reg <= write_burst_next; + + s_axi_bid_reg <= s_axi_bid_next; + + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (mem_wr_en & s_axi_wstrb[i]) begin + mem[write_addr_valid][WORD_SIZE*i +: WORD_SIZE] <= s_axi_wdata[WORD_SIZE*i +: WORD_SIZE]; + end + end +end + +always @* begin + read_state_next = READ_STATE_IDLE; + + mem_rd_en = 1'b0; + + s_axi_rid_next = s_axi_rid_reg; + s_axi_rlast_next = s_axi_rlast_reg; + s_axi_rvalid_next = s_axi_rvalid_reg && !(s_axi_rready || (PIPELINE_OUTPUT && !s_axi_rvalid_pipe_reg)); + + read_id_next = read_id_reg; + read_addr_next = read_addr_reg; + read_count_next = read_count_reg; + read_size_next = read_size_reg; + read_burst_next = read_burst_reg; + + s_axi_arready_next = 1'b0; + + case (read_state_reg) + READ_STATE_IDLE: begin + s_axi_arready_next = 1'b1; + + if (s_axi_arready && s_axi_arvalid) begin + read_id_next = s_axi_arid; + read_addr_next = s_axi_araddr; + read_count_next = s_axi_arlen; + read_size_next = s_axi_arsize < $clog2(STRB_WIDTH) ? s_axi_arsize : $clog2(STRB_WIDTH); + read_burst_next = s_axi_arburst; + + s_axi_arready_next = 1'b0; + read_state_next = READ_STATE_BURST; + end else begin + read_state_next = READ_STATE_IDLE; + end + end + READ_STATE_BURST: begin + if (s_axi_rready || (PIPELINE_OUTPUT && !s_axi_rvalid_pipe_reg) || !s_axi_rvalid_reg) begin + mem_rd_en = 1'b1; + s_axi_rvalid_next = 1'b1; + s_axi_rid_next = read_id_reg; + s_axi_rlast_next = read_count_reg == 0; + if (read_burst_reg != 2'b00) begin + read_addr_next = read_addr_reg + (1 << read_size_reg); + end + read_count_next = read_count_reg - 1; + if (read_count_reg > 0) begin + read_state_next = READ_STATE_BURST; + end else begin + s_axi_arready_next = 1'b1; + read_state_next = READ_STATE_IDLE; + end + end else begin + read_state_next = READ_STATE_BURST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + read_state_reg <= READ_STATE_IDLE; + s_axi_arready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + s_axi_rvalid_pipe_reg <= 1'b0; + end else begin + read_state_reg <= read_state_next; + s_axi_arready_reg <= s_axi_arready_next; + s_axi_rvalid_reg <= s_axi_rvalid_next; + + if (!s_axi_rvalid_pipe_reg || s_axi_rready) begin + s_axi_rvalid_pipe_reg <= s_axi_rvalid_reg; + end + end + + read_id_reg <= read_id_next; + read_addr_reg <= read_addr_next; + read_count_reg <= read_count_next; + read_size_reg <= read_size_next; + read_burst_reg <= read_burst_next; + + s_axi_rid_reg <= s_axi_rid_next; + s_axi_rlast_reg <= s_axi_rlast_next; + + if (mem_rd_en) begin + s_axi_rdata_reg <= mem[read_addr_valid]; + end + + if (!s_axi_rvalid_pipe_reg || s_axi_rready) begin + s_axi_rid_pipe_reg <= s_axi_rid_reg; + s_axi_rdata_pipe_reg <= s_axi_rdata_reg; + s_axi_rlast_pipe_reg <= s_axi_rlast_reg; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_ram_rd_if.v b/corundum/lib/axi/rtl/axi_ram_rd_if.v new file mode 100644 index 0000000000000000000000000000000000000000..6b02a314f0e7c66ba8bfc654987ed018c6ee0796 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_ram_rd_if.v @@ -0,0 +1,270 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 RAM read interface + */ +module axi_ram_rd_if # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Extra pipeline register on output + parameter PIPELINE_OUTPUT = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * RAM interface + */ + output wire [ID_WIDTH-1:0] ram_rd_cmd_id, + output wire [ADDR_WIDTH-1:0] ram_rd_cmd_addr, + output wire ram_rd_cmd_lock, + output wire [3:0] ram_rd_cmd_cache, + output wire [2:0] ram_rd_cmd_prot, + output wire [3:0] ram_rd_cmd_qos, + output wire [3:0] ram_rd_cmd_region, + output wire [ARUSER_WIDTH-1:0] ram_rd_cmd_auser, + output wire ram_rd_cmd_en, + output wire ram_rd_cmd_last, + input wire ram_rd_cmd_ready, + input wire [ID_WIDTH-1:0] ram_rd_resp_id, + input wire [DATA_WIDTH-1:0] ram_rd_resp_data, + input wire ram_rd_resp_last, + input wire [RUSER_WIDTH-1:0] ram_rd_resp_user, + input wire ram_rd_resp_valid, + output wire ram_rd_resp_ready +); + +parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); +parameter WORD_WIDTH = STRB_WIDTH; +parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH; + +// bus width assertions +initial begin + if (WORD_SIZE * STRB_WIDTH != DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (2**$clog2(WORD_WIDTH) != WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [0:0] + STATE_IDLE = 1'd0, + STATE_BURST = 1'd1; + +reg [0:0] state_reg = STATE_IDLE, state_next; + +reg [ID_WIDTH-1:0] read_id_reg = {ID_WIDTH{1'b0}}, read_id_next; +reg [ADDR_WIDTH-1:0] read_addr_reg = {ADDR_WIDTH{1'b0}}, read_addr_next; +reg read_lock_reg = 1'b0, read_lock_next; +reg [3:0] read_cache_reg = 4'd0, read_cache_next; +reg [2:0] read_prot_reg = 3'd0, read_prot_next; +reg [3:0] read_qos_reg = 4'd0, read_qos_next; +reg [3:0] read_region_reg = 4'd0, read_region_next; +reg [ARUSER_WIDTH-1:0] read_aruser_reg = {ARUSER_WIDTH{1'b0}}, read_aruser_next; +reg read_addr_valid_reg = 1'b0, read_addr_valid_next; +reg read_last_reg = 1'b0, read_last_next; +reg [7:0] read_count_reg = 8'd0, read_count_next; +reg [2:0] read_size_reg = 3'd0, read_size_next; +reg [1:0] read_burst_reg = 2'd0, read_burst_next; + +reg s_axi_arready_reg = 1'b0, s_axi_arready_next; +reg [ID_WIDTH-1:0] s_axi_rid_pipe_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_pipe_reg = {DATA_WIDTH{1'b0}}; +reg s_axi_rlast_pipe_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_pipe_reg = {RUSER_WIDTH{1'b0}}; +reg s_axi_rvalid_pipe_reg = 1'b0; + +assign s_axi_arready = s_axi_arready_reg; +assign s_axi_rid = PIPELINE_OUTPUT ? s_axi_rid_pipe_reg : ram_rd_resp_id; +assign s_axi_rdata = PIPELINE_OUTPUT ? s_axi_rdata_pipe_reg : ram_rd_resp_data; +assign s_axi_rresp = 2'b00; +assign s_axi_rlast = PIPELINE_OUTPUT ? s_axi_rlast_pipe_reg : ram_rd_resp_last; +assign s_axi_ruser = PIPELINE_OUTPUT ? s_axi_ruser_pipe_reg : ram_rd_resp_user; +assign s_axi_rvalid = PIPELINE_OUTPUT ? s_axi_rvalid_pipe_reg : ram_rd_resp_valid; + +assign ram_rd_cmd_id = read_id_reg; +assign ram_rd_cmd_addr = read_addr_reg; +assign ram_rd_cmd_lock = read_lock_reg; +assign ram_rd_cmd_cache = read_cache_reg; +assign ram_rd_cmd_prot = read_prot_reg; +assign ram_rd_cmd_qos = read_qos_reg; +assign ram_rd_cmd_region = read_region_reg; +assign ram_rd_cmd_auser = ARUSER_ENABLE ? read_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign ram_rd_cmd_en = read_addr_valid_reg; +assign ram_rd_cmd_last = read_last_reg; + +assign ram_rd_resp_ready = s_axi_rready || (PIPELINE_OUTPUT && !s_axi_rvalid_pipe_reg); + +always @* begin + state_next = STATE_IDLE; + + read_id_next = read_id_reg; + read_addr_next = read_addr_reg; + read_lock_next = read_lock_reg; + read_cache_next = read_cache_reg; + read_prot_next = read_prot_reg; + read_qos_next = read_qos_reg; + read_region_next = read_region_reg; + read_aruser_next = read_aruser_reg; + read_addr_valid_next = read_addr_valid_reg && !ram_rd_cmd_ready; + read_last_next = read_last_reg; + read_count_next = read_count_reg; + read_size_next = read_size_reg; + read_burst_next = read_burst_reg; + + s_axi_arready_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + s_axi_arready_next = 1'b1; + + if (s_axi_arready && s_axi_arvalid) begin + read_id_next = s_axi_arid; + read_addr_next = s_axi_araddr; + read_lock_next = s_axi_arlock; + read_cache_next = s_axi_arcache; + read_prot_next = s_axi_arprot; + read_qos_next = s_axi_arqos; + read_region_next = s_axi_arregion; + read_aruser_next = s_axi_aruser; + read_count_next = s_axi_arlen; + read_size_next = s_axi_arsize < $clog2(STRB_WIDTH) ? s_axi_arsize : $clog2(STRB_WIDTH); + read_burst_next = s_axi_arburst; + + s_axi_arready_next = 1'b0; + read_last_next = read_count_next == 0; + read_addr_valid_next = 1'b1; + state_next = STATE_BURST; + end else begin + state_next = STATE_IDLE; + end + end + STATE_BURST: begin + if (ram_rd_cmd_ready && ram_rd_cmd_en) begin + if (read_burst_reg != 2'b00) begin + read_addr_next = read_addr_reg + (1 << read_size_reg); + end + read_count_next = read_count_reg - 1; + read_last_next = read_count_next == 0; + if (read_count_reg > 0) begin + read_addr_valid_next = 1'b1; + state_next = STATE_BURST; + end else begin + s_axi_arready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_BURST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + read_addr_valid_reg <= 1'b0; + s_axi_arready_reg <= 1'b0; + s_axi_rvalid_pipe_reg <= 1'b0; + end else begin + state_reg <= state_next; + read_addr_valid_reg <= read_addr_valid_next; + s_axi_arready_reg <= s_axi_arready_next; + + if (!s_axi_rvalid_pipe_reg || s_axi_rready) begin + s_axi_rvalid_pipe_reg <= ram_rd_resp_valid; + end + end + + read_id_reg <= read_id_next; + read_addr_reg <= read_addr_next; + read_lock_reg <= read_lock_next; + read_cache_reg <= read_cache_next; + read_prot_reg <= read_prot_next; + read_qos_reg <= read_qos_next; + read_region_reg <= read_region_next; + read_aruser_reg <= read_aruser_next; + read_last_reg <= read_last_next; + read_count_reg <= read_count_next; + read_size_reg <= read_size_next; + read_burst_reg <= read_burst_next; + + if (!s_axi_rvalid_pipe_reg || s_axi_rready) begin + s_axi_rid_pipe_reg <= ram_rd_resp_id; + s_axi_rdata_pipe_reg <= ram_rd_resp_data; + s_axi_rlast_pipe_reg <= ram_rd_resp_last; + s_axi_ruser_pipe_reg <= ram_rd_resp_user; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_ram_wr_if.v b/corundum/lib/axi/rtl/axi_ram_wr_if.v new file mode 100644 index 0000000000000000000000000000000000000000..f3be8549b9516e73f8484006c36bbb3c4f48298c --- /dev/null +++ b/corundum/lib/axi/rtl/axi_ram_wr_if.v @@ -0,0 +1,286 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 RAM write interface + */ +module axi_ram_wr_if # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + + /* + * RAM interface + */ + output wire [ID_WIDTH-1:0] ram_wr_cmd_id, + output wire [ADDR_WIDTH-1:0] ram_wr_cmd_addr, + output wire ram_wr_cmd_lock, + output wire [3:0] ram_wr_cmd_cache, + output wire [2:0] ram_wr_cmd_prot, + output wire [3:0] ram_wr_cmd_qos, + output wire [3:0] ram_wr_cmd_region, + output wire [AWUSER_WIDTH-1:0] ram_wr_cmd_auser, + output wire [DATA_WIDTH-1:0] ram_wr_cmd_data, + output wire [STRB_WIDTH-1:0] ram_wr_cmd_strb, + output wire [WUSER_WIDTH-1:0] ram_wr_cmd_user, + output wire ram_wr_cmd_en, + output wire ram_wr_cmd_last, + input wire ram_wr_cmd_ready +); + +parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); +parameter WORD_WIDTH = STRB_WIDTH; +parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH; + +// bus width assertions +initial begin + if (WORD_SIZE * STRB_WIDTH != DATA_WIDTH) begin + $error("Error: AXI data width not evenly divisble (instance %m)"); + $finish; + end + + if (2**$clog2(WORD_WIDTH) != WORD_WIDTH) begin + $error("Error: AXI word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_BURST = 2'd1, + STATE_RESP = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [ID_WIDTH-1:0] write_id_reg = {ID_WIDTH{1'b0}}, write_id_next; +reg [ADDR_WIDTH-1:0] write_addr_reg = {ADDR_WIDTH{1'b0}}, write_addr_next; +reg write_lock_reg = 1'b0, write_lock_next; +reg [3:0] write_cache_reg = 4'd0, write_cache_next; +reg [2:0] write_prot_reg = 3'd0, write_prot_next; +reg [3:0] write_qos_reg = 4'd0, write_qos_next; +reg [3:0] write_region_reg = 4'd0, write_region_next; +reg [AWUSER_WIDTH-1:0] write_awuser_reg = {AWUSER_WIDTH{1'b0}}, write_awuser_next; +reg write_addr_valid_reg = 1'b0, write_addr_valid_next; +reg write_last_reg = 1'b0, write_last_next; +reg [7:0] write_count_reg = 8'd0, write_count_next; +reg [2:0] write_size_reg = 3'd0, write_size_next; +reg [1:0] write_burst_reg = 2'd0, write_burst_next; + +reg s_axi_awready_reg = 1'b0, s_axi_awready_next; +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}, s_axi_bid_next; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +assign s_axi_awready = s_axi_awready_reg; +assign s_axi_wready = write_addr_valid_reg && ram_wr_cmd_ready; +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = 2'b00; +assign s_axi_buser = {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +assign ram_wr_cmd_id = write_id_reg; +assign ram_wr_cmd_addr = write_addr_reg; +assign ram_wr_cmd_lock = write_lock_reg; +assign ram_wr_cmd_cache = write_cache_reg; +assign ram_wr_cmd_prot = write_prot_reg; +assign ram_wr_cmd_qos = write_qos_reg; +assign ram_wr_cmd_region = write_region_reg; +assign ram_wr_cmd_auser = AWUSER_ENABLE ? write_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign ram_wr_cmd_data = s_axi_wdata; +assign ram_wr_cmd_strb = s_axi_wstrb; +assign ram_wr_cmd_user = WUSER_ENABLE ? s_axi_wuser : {WUSER_WIDTH{1'b0}}; +assign ram_wr_cmd_en = write_addr_valid_reg && s_axi_wvalid; +assign ram_wr_cmd_last = write_last_reg; + +always @* begin + state_next = STATE_IDLE; + + write_id_next = write_id_reg; + write_addr_next = write_addr_reg; + write_lock_next = write_lock_reg; + write_cache_next = write_cache_reg; + write_prot_next = write_prot_reg; + write_qos_next = write_qos_reg; + write_region_next = write_region_reg; + write_awuser_next = write_awuser_reg; + write_addr_valid_next = write_addr_valid_reg; + write_last_next = write_last_reg; + write_count_next = write_count_reg; + write_size_next = write_size_reg; + write_burst_next = write_burst_reg; + + s_axi_awready_next = 1'b0; + s_axi_bid_next = s_axi_bid_reg; + s_axi_bvalid_next = s_axi_bvalid_reg && !s_axi_bready; + + case (state_reg) + STATE_IDLE: begin + s_axi_awready_next = 1'b1; + + if (s_axi_awready && s_axi_awvalid) begin + write_id_next = s_axi_awid; + write_addr_next = s_axi_awaddr; + write_lock_next = s_axi_awlock; + write_cache_next = s_axi_awcache; + write_prot_next = s_axi_awprot; + write_qos_next = s_axi_awqos; + write_region_next = s_axi_awregion; + write_awuser_next = s_axi_awuser; + write_count_next = s_axi_awlen; + write_size_next = s_axi_awsize < $clog2(STRB_WIDTH) ? s_axi_awsize : $clog2(STRB_WIDTH); + write_burst_next = s_axi_awburst; + + write_addr_valid_next = 1'b1; + s_axi_awready_next = 1'b0; + if (s_axi_awlen > 0) begin + write_last_next = 1'b0; + end else begin + write_last_next = 1'b1; + end + state_next = STATE_BURST; + end else begin + state_next = STATE_IDLE; + end + end + STATE_BURST: begin + if (s_axi_wready && s_axi_wvalid) begin + if (write_burst_reg != 2'b00) begin + write_addr_next = write_addr_reg + (1 << write_size_reg); + end + write_count_next = write_count_reg - 1; + write_last_next = write_count_next == 0; + if (write_count_reg > 0) begin + write_addr_valid_next = 1'b1; + state_next = STATE_BURST; + end else begin + write_addr_valid_next = 1'b0; + if (s_axi_bready || !s_axi_bvalid) begin + s_axi_bid_next = write_id_reg; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_RESP; + end + end + end else begin + state_next = STATE_BURST; + end + end + STATE_RESP: begin + if (s_axi_bready || !s_axi_bvalid) begin + s_axi_bid_next = write_id_reg; + s_axi_bvalid_next = 1'b1; + s_axi_awready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_RESP; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + write_addr_valid_reg <= 1'b0; + s_axi_awready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + end else begin + state_reg <= state_next; + write_addr_valid_reg <= write_addr_valid_next; + s_axi_awready_reg <= s_axi_awready_next; + s_axi_bvalid_reg <= s_axi_bvalid_next; + end + + write_id_reg <= write_id_next; + write_addr_reg <= write_addr_next; + write_lock_reg <= write_lock_next; + write_cache_reg <= write_cache_next; + write_prot_reg <= write_prot_next; + write_qos_reg <= write_qos_next; + write_region_reg <= write_region_next; + write_awuser_reg <= write_awuser_next; + write_last_reg <= write_last_next; + write_count_reg <= write_count_next; + write_size_reg <= write_size_next; + write_burst_reg <= write_burst_next; + + s_axi_bid_reg <= s_axi_bid_next; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_ram_wr_rd_if.v b/corundum/lib/axi/rtl/axi_ram_wr_rd_if.v new file mode 100644 index 0000000000000000000000000000000000000000..8924023eff4397f53b8f3cffec34f03274ced0da --- /dev/null +++ b/corundum/lib/axi/rtl/axi_ram_wr_rd_if.v @@ -0,0 +1,333 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 RAM read/write interface + */ +module axi_ram_wr_rd_if # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // Width of auser output + parameter AUSER_WIDTH = (ARUSER_ENABLE && (!AWUSER_ENABLE || ARUSER_WIDTH > AWUSER_WIDTH)) ? ARUSER_WIDTH : AWUSER_WIDTH, + // Extra pipeline register on output + parameter PIPELINE_OUTPUT = 0, + // Interleave read and write burst cycles + parameter INTERLEAVE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * RAM interface + */ + output wire [ID_WIDTH-1:0] ram_cmd_id, + output wire [ADDR_WIDTH-1:0] ram_cmd_addr, + output wire ram_cmd_lock, + output wire [3:0] ram_cmd_cache, + output wire [2:0] ram_cmd_prot, + output wire [3:0] ram_cmd_qos, + output wire [3:0] ram_cmd_region, + output wire [AUSER_WIDTH-1:0] ram_cmd_auser, + output wire [DATA_WIDTH-1:0] ram_cmd_wr_data, + output wire [STRB_WIDTH-1:0] ram_cmd_wr_strb, + output wire [WUSER_WIDTH-1:0] ram_cmd_wr_user, + output wire ram_cmd_wr_en, + output wire ram_cmd_rd_en, + output wire ram_cmd_last, + input wire ram_cmd_ready, + input wire [ID_WIDTH-1:0] ram_rd_resp_id, + input wire [DATA_WIDTH-1:0] ram_rd_resp_data, + input wire ram_rd_resp_last, + input wire [RUSER_WIDTH-1:0] ram_rd_resp_user, + input wire ram_rd_resp_valid, + output wire ram_rd_resp_ready +); + + +wire [ID_WIDTH-1:0] ram_wr_cmd_id; +wire [ADDR_WIDTH-1:0] ram_wr_cmd_addr; +wire ram_wr_cmd_lock; +wire [3:0] ram_wr_cmd_cache; +wire [2:0] ram_wr_cmd_prot; +wire [3:0] ram_wr_cmd_qos; +wire [3:0] ram_wr_cmd_region; +wire [AWUSER_WIDTH-1:0] ram_wr_cmd_auser; + +wire [ID_WIDTH-1:0] ram_rd_cmd_id; +wire [ADDR_WIDTH-1:0] ram_rd_cmd_addr; +wire ram_rd_cmd_lock; +wire [3:0] ram_rd_cmd_cache; +wire [2:0] ram_rd_cmd_prot; +wire [3:0] ram_rd_cmd_qos; +wire [3:0] ram_rd_cmd_region; +wire [AWUSER_WIDTH-1:0] ram_rd_cmd_auser; + +axi_ram_wr_if #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH) +) +axi_ram_wr_if_inst ( + .clk(clk), + .rst(rst), + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awqos(s_axi_awqos), + .s_axi_awregion(s_axi_awregion), + .s_axi_awuser(s_axi_awuser), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wuser(s_axi_wuser), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_buser(s_axi_buser), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + .ram_wr_cmd_id(ram_wr_cmd_id), + .ram_wr_cmd_addr(ram_wr_cmd_addr), + .ram_wr_cmd_lock(ram_wr_cmd_lock), + .ram_wr_cmd_cache(ram_wr_cmd_cache), + .ram_wr_cmd_prot(ram_wr_cmd_prot), + .ram_wr_cmd_qos(ram_wr_cmd_qos), + .ram_wr_cmd_region(ram_wr_cmd_region), + .ram_wr_cmd_auser(ram_wr_cmd_auser), + .ram_wr_cmd_data(ram_cmd_wr_data), + .ram_wr_cmd_strb(ram_cmd_wr_strb), + .ram_wr_cmd_user(ram_cmd_wr_user), + .ram_wr_cmd_en(ram_wr_cmd_en), + .ram_wr_cmd_last(ram_wr_cmd_last), + .ram_wr_cmd_ready(ram_wr_cmd_ready) +); + +axi_ram_rd_if #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .PIPELINE_OUTPUT(PIPELINE_OUTPUT) +) +axi_ram_rd_if_inst ( + .clk(clk), + .rst(rst), + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arqos(s_axi_arqos), + .s_axi_arregion(s_axi_arregion), + .s_axi_aruser(s_axi_aruser), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_ruser(s_axi_ruser), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + .ram_rd_cmd_id(ram_rd_cmd_id), + .ram_rd_cmd_addr(ram_rd_cmd_addr), + .ram_rd_cmd_lock(ram_rd_cmd_lock), + .ram_rd_cmd_cache(ram_rd_cmd_cache), + .ram_rd_cmd_prot(ram_rd_cmd_prot), + .ram_rd_cmd_qos(ram_rd_cmd_qos), + .ram_rd_cmd_region(ram_rd_cmd_region), + .ram_rd_cmd_auser(ram_rd_cmd_auser), + .ram_rd_cmd_en(ram_rd_cmd_en), + .ram_rd_cmd_last(ram_rd_cmd_last), + .ram_rd_cmd_ready(ram_rd_cmd_ready), + .ram_rd_resp_id(ram_rd_resp_id), + .ram_rd_resp_data(ram_rd_resp_data), + .ram_rd_resp_last(ram_rd_resp_last), + .ram_rd_resp_user(ram_rd_resp_user), + .ram_rd_resp_valid(ram_rd_resp_valid), + .ram_rd_resp_ready(ram_rd_resp_ready) +); + +// arbitration +reg read_eligible; +reg write_eligible; + +reg write_en; +reg read_en; + +reg last_read_reg = 1'b0, last_read_next; +reg transaction_reg = 1'b0, transaction_next; + +assign ram_cmd_wr_en = write_en; +assign ram_cmd_rd_en = read_en; + +assign ram_cmd_id = ram_cmd_rd_en ? ram_rd_cmd_id : ram_wr_cmd_id; +assign ram_cmd_addr = ram_cmd_rd_en ? ram_rd_cmd_addr : ram_wr_cmd_addr; +assign ram_cmd_lock = ram_cmd_rd_en ? ram_rd_cmd_lock : ram_wr_cmd_lock; +assign ram_cmd_cache = ram_cmd_rd_en ? ram_rd_cmd_cache : ram_wr_cmd_cache; +assign ram_cmd_prot = ram_cmd_rd_en ? ram_rd_cmd_prot : ram_wr_cmd_prot; +assign ram_cmd_qos = ram_cmd_rd_en ? ram_rd_cmd_qos : ram_wr_cmd_qos; +assign ram_cmd_region = ram_cmd_rd_en ? ram_rd_cmd_region : ram_wr_cmd_region; +assign ram_cmd_auser = ram_cmd_rd_en ? ram_rd_cmd_auser : ram_wr_cmd_auser; +assign ram_cmd_last = ram_cmd_rd_en ? ram_rd_cmd_last : ram_wr_cmd_last; + +assign ram_wr_cmd_ready = ram_cmd_ready && write_en; +assign ram_rd_cmd_ready = ram_cmd_ready && read_en; + +always @* begin + write_en = 1'b0; + read_en = 1'b0; + + last_read_next = last_read_reg; + transaction_next = transaction_reg; + + write_eligible = ram_wr_cmd_en && ram_cmd_ready; + read_eligible = ram_rd_cmd_en && ram_cmd_ready; + + if (write_eligible && (!read_eligible || last_read_reg || (!INTERLEAVE && transaction_reg)) && (INTERLEAVE || !transaction_reg || !last_read_reg)) begin + last_read_next = 1'b0; + transaction_next = !ram_wr_cmd_last; + + write_en = 1'b1; + end else if (read_eligible && (INTERLEAVE || !transaction_reg || last_read_reg)) begin + last_read_next = 1'b1; + transaction_next = !ram_rd_cmd_last; + + read_en = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + last_read_reg <= 1'b0; + transaction_reg <= 1'b0; + end else begin + last_read_reg <= last_read_next; + transaction_reg <= transaction_next; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axi_register.v b/corundum/lib/axi/rtl/axi_register.v new file mode 100644 index 0000000000000000000000000000000000000000..040e5af41246821e7d4624a5f6b17abe1e72b2a7 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_register.v @@ -0,0 +1,320 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 register + */ +module axi_register # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // AW channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AW_REG_TYPE = 1, + // W channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter W_REG_TYPE = 2, + // B channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter B_REG_TYPE = 1, + // AR channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AR_REG_TYPE = 1, + // R channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter R_REG_TYPE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [DATA_WIDTH-1:0] m_axi_wdata, + output wire [STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +axi_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .AWUSER_ENABLE(AWUSER_ENABLE), + .AWUSER_WIDTH(AWUSER_WIDTH), + .WUSER_ENABLE(WUSER_ENABLE), + .WUSER_WIDTH(WUSER_WIDTH), + .BUSER_ENABLE(BUSER_ENABLE), + .BUSER_WIDTH(BUSER_WIDTH), + .AW_REG_TYPE(AW_REG_TYPE), + .W_REG_TYPE(W_REG_TYPE), + .B_REG_TYPE(B_REG_TYPE) +) +axi_register_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_awid(s_axi_awid), + .s_axi_awaddr(s_axi_awaddr), + .s_axi_awlen(s_axi_awlen), + .s_axi_awsize(s_axi_awsize), + .s_axi_awburst(s_axi_awburst), + .s_axi_awlock(s_axi_awlock), + .s_axi_awcache(s_axi_awcache), + .s_axi_awprot(s_axi_awprot), + .s_axi_awqos(s_axi_awqos), + .s_axi_awregion(s_axi_awregion), + .s_axi_awuser(s_axi_awuser), + .s_axi_awvalid(s_axi_awvalid), + .s_axi_awready(s_axi_awready), + .s_axi_wdata(s_axi_wdata), + .s_axi_wstrb(s_axi_wstrb), + .s_axi_wlast(s_axi_wlast), + .s_axi_wuser(s_axi_wuser), + .s_axi_wvalid(s_axi_wvalid), + .s_axi_wready(s_axi_wready), + .s_axi_bid(s_axi_bid), + .s_axi_bresp(s_axi_bresp), + .s_axi_buser(s_axi_buser), + .s_axi_bvalid(s_axi_bvalid), + .s_axi_bready(s_axi_bready), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awqos(m_axi_awqos), + .m_axi_awregion(m_axi_awregion), + .m_axi_awuser(m_axi_awuser), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wuser(m_axi_wuser), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_buser(m_axi_buser), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready) +); + +axi_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .ID_WIDTH(ID_WIDTH), + .ARUSER_ENABLE(ARUSER_ENABLE), + .ARUSER_WIDTH(ARUSER_WIDTH), + .RUSER_ENABLE(RUSER_ENABLE), + .RUSER_WIDTH(RUSER_WIDTH), + .AR_REG_TYPE(AR_REG_TYPE), + .R_REG_TYPE(R_REG_TYPE) +) +axi_register_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI slave interface + */ + .s_axi_arid(s_axi_arid), + .s_axi_araddr(s_axi_araddr), + .s_axi_arlen(s_axi_arlen), + .s_axi_arsize(s_axi_arsize), + .s_axi_arburst(s_axi_arburst), + .s_axi_arlock(s_axi_arlock), + .s_axi_arcache(s_axi_arcache), + .s_axi_arprot(s_axi_arprot), + .s_axi_arqos(s_axi_arqos), + .s_axi_arregion(s_axi_arregion), + .s_axi_aruser(s_axi_aruser), + .s_axi_arvalid(s_axi_arvalid), + .s_axi_arready(s_axi_arready), + .s_axi_rid(s_axi_rid), + .s_axi_rdata(s_axi_rdata), + .s_axi_rresp(s_axi_rresp), + .s_axi_rlast(s_axi_rlast), + .s_axi_ruser(s_axi_ruser), + .s_axi_rvalid(s_axi_rvalid), + .s_axi_rready(s_axi_rready), + + /* + * AXI master interface + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arqos(m_axi_arqos), + .m_axi_arregion(m_axi_arregion), + .m_axi_aruser(m_axi_aruser), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_ruser(m_axi_ruser), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axi_register_rd.v b/corundum/lib/axi/rtl/axi_register_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..56648b4eb232998ad478ac2f019bd10172c96732 --- /dev/null +++ b/corundum/lib/axi/rtl/axi_register_rd.v @@ -0,0 +1,526 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 register (read) + */ +module axi_register_rd # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate aruser signal + parameter ARUSER_ENABLE = 0, + // Width of aruser signal + parameter ARUSER_WIDTH = 1, + // Propagate ruser signal + parameter RUSER_ENABLE = 0, + // Width of ruser signal + parameter RUSER_WIDTH = 1, + // AR channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AR_REG_TYPE = 1, + // R channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter R_REG_TYPE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_arid, + input wire [ADDR_WIDTH-1:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire [3:0] s_axi_arregion, + input wire [ARUSER_WIDTH-1:0] s_axi_aruser, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [ID_WIDTH-1:0] s_axi_rid, + output wire [DATA_WIDTH-1:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire [RUSER_WIDTH-1:0] s_axi_ruser, + output wire s_axi_rvalid, + input wire s_axi_rready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_arid, + output wire [ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire [ARUSER_WIDTH-1:0] m_axi_aruser, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [ID_WIDTH-1:0] m_axi_rid, + input wire [DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire [RUSER_WIDTH-1:0] m_axi_ruser, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +generate + +// AR channel + +if (AR_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_arready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_arlen_reg = 8'd0; +reg [2:0] m_axi_arsize_reg = 3'd0; +reg [1:0] m_axi_arburst_reg = 2'd0; +reg m_axi_arlock_reg = 1'b0; +reg [3:0] m_axi_arcache_reg = 4'd0; +reg [2:0] m_axi_arprot_reg = 3'd0; +reg [3:0] m_axi_arqos_reg = 4'd0; +reg [3:0] m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; + +reg [ID_WIDTH-1:0] temp_m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] temp_m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] temp_m_axi_arlen_reg = 8'd0; +reg [2:0] temp_m_axi_arsize_reg = 3'd0; +reg [1:0] temp_m_axi_arburst_reg = 2'd0; +reg temp_m_axi_arlock_reg = 1'b0; +reg [3:0] temp_m_axi_arcache_reg = 4'd0; +reg [2:0] temp_m_axi_arprot_reg = 3'd0; +reg [3:0] temp_m_axi_arqos_reg = 4'd0; +reg [3:0] temp_m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] temp_m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg temp_m_axi_arvalid_reg = 1'b0, temp_m_axi_arvalid_next; + +// datapath control +reg store_axi_ar_input_to_output; +reg store_axi_ar_input_to_temp; +reg store_axi_ar_temp_to_output; + +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_arid = m_axi_arid_reg; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = m_axi_arsize_reg; +assign m_axi_arburst = m_axi_arburst_reg; +assign m_axi_arlock = m_axi_arlock_reg; +assign m_axi_arcache = m_axi_arcache_reg; +assign m_axi_arprot = m_axi_arprot_reg; +assign m_axi_arqos = m_axi_arqos_reg; +assign m_axi_arregion = m_axi_arregion_reg; +assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign m_axi_arvalid = m_axi_arvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_arready_early = m_axi_arready | (~temp_m_axi_arvalid_reg & (~m_axi_arvalid_reg | ~s_axi_arvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_arvalid_next = m_axi_arvalid_reg; + temp_m_axi_arvalid_next = temp_m_axi_arvalid_reg; + + store_axi_ar_input_to_output = 1'b0; + store_axi_ar_input_to_temp = 1'b0; + store_axi_ar_temp_to_output = 1'b0; + + if (s_axi_arready_reg) begin + // input is ready + if (m_axi_arready | ~m_axi_arvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_temp = 1'b1; + end + end else if (m_axi_arready) begin + // input is not ready, but output is ready + m_axi_arvalid_next = temp_m_axi_arvalid_reg; + temp_m_axi_arvalid_next = 1'b0; + store_axi_ar_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_arready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + temp_m_axi_arvalid_reg <= 1'b0; + end else begin + s_axi_arready_reg <= s_axi_arready_early; + m_axi_arvalid_reg <= m_axi_arvalid_next; + temp_m_axi_arvalid_reg <= temp_m_axi_arvalid_next; + end + + // datapath + if (store_axi_ar_input_to_output) begin + m_axi_arid_reg <= s_axi_arid; + m_axi_araddr_reg <= s_axi_araddr; + m_axi_arlen_reg <= s_axi_arlen; + m_axi_arsize_reg <= s_axi_arsize; + m_axi_arburst_reg <= s_axi_arburst; + m_axi_arlock_reg <= s_axi_arlock; + m_axi_arcache_reg <= s_axi_arcache; + m_axi_arprot_reg <= s_axi_arprot; + m_axi_arqos_reg <= s_axi_arqos; + m_axi_arregion_reg <= s_axi_arregion; + m_axi_aruser_reg <= s_axi_aruser; + end else if (store_axi_ar_temp_to_output) begin + m_axi_arid_reg <= temp_m_axi_arid_reg; + m_axi_araddr_reg <= temp_m_axi_araddr_reg; + m_axi_arlen_reg <= temp_m_axi_arlen_reg; + m_axi_arsize_reg <= temp_m_axi_arsize_reg; + m_axi_arburst_reg <= temp_m_axi_arburst_reg; + m_axi_arlock_reg <= temp_m_axi_arlock_reg; + m_axi_arcache_reg <= temp_m_axi_arcache_reg; + m_axi_arprot_reg <= temp_m_axi_arprot_reg; + m_axi_arqos_reg <= temp_m_axi_arqos_reg; + m_axi_arregion_reg <= temp_m_axi_arregion_reg; + m_axi_aruser_reg <= temp_m_axi_aruser_reg; + end + + if (store_axi_ar_input_to_temp) begin + temp_m_axi_arid_reg <= s_axi_arid; + temp_m_axi_araddr_reg <= s_axi_araddr; + temp_m_axi_arlen_reg <= s_axi_arlen; + temp_m_axi_arsize_reg <= s_axi_arsize; + temp_m_axi_arburst_reg <= s_axi_arburst; + temp_m_axi_arlock_reg <= s_axi_arlock; + temp_m_axi_arcache_reg <= s_axi_arcache; + temp_m_axi_arprot_reg <= s_axi_arprot; + temp_m_axi_arqos_reg <= s_axi_arqos; + temp_m_axi_arregion_reg <= s_axi_arregion; + temp_m_axi_aruser_reg <= s_axi_aruser; + end +end + +end else if (AR_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_arready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_arid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_arlen_reg = 8'd0; +reg [2:0] m_axi_arsize_reg = 3'd0; +reg [1:0] m_axi_arburst_reg = 2'd0; +reg m_axi_arlock_reg = 1'b0; +reg [3:0] m_axi_arcache_reg = 4'd0; +reg [2:0] m_axi_arprot_reg = 3'd0; +reg [3:0] m_axi_arqos_reg = 4'd0; +reg [3:0] m_axi_arregion_reg = 4'd0; +reg [ARUSER_WIDTH-1:0] m_axi_aruser_reg = {ARUSER_WIDTH{1'b0}}; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; + +// datapath control +reg store_axi_ar_input_to_output; + +assign s_axi_arready = s_axi_arready_reg; + +assign m_axi_arid = m_axi_arid_reg; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = m_axi_arsize_reg; +assign m_axi_arburst = m_axi_arburst_reg; +assign m_axi_arlock = m_axi_arlock_reg; +assign m_axi_arcache = m_axi_arcache_reg; +assign m_axi_arprot = m_axi_arprot_reg; +assign m_axi_arqos = m_axi_arqos_reg; +assign m_axi_arregion = m_axi_arregion_reg; +assign m_axi_aruser = ARUSER_ENABLE ? m_axi_aruser_reg : {ARUSER_WIDTH{1'b0}}; +assign m_axi_arvalid = m_axi_arvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_arready_early = !m_axi_arvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_arvalid_next = m_axi_arvalid_reg; + + store_axi_ar_input_to_output = 1'b0; + + if (s_axi_arready_reg) begin + m_axi_arvalid_next = s_axi_arvalid; + store_axi_ar_input_to_output = 1'b1; + end else if (m_axi_arready) begin + m_axi_arvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_arready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + end else begin + s_axi_arready_reg <= s_axi_arready_early; + m_axi_arvalid_reg <= m_axi_arvalid_next; + end + + // datapath + if (store_axi_ar_input_to_output) begin + m_axi_arid_reg <= s_axi_arid; + m_axi_araddr_reg <= s_axi_araddr; + m_axi_arlen_reg <= s_axi_arlen; + m_axi_arsize_reg <= s_axi_arsize; + m_axi_arburst_reg <= s_axi_arburst; + m_axi_arlock_reg <= s_axi_arlock; + m_axi_arcache_reg <= s_axi_arcache; + m_axi_arprot_reg <= s_axi_arprot; + m_axi_arqos_reg <= s_axi_arqos; + m_axi_arregion_reg <= s_axi_arregion; + m_axi_aruser_reg <= s_axi_aruser; + end +end + +end else begin + + // bypass AR channel + assign m_axi_arid = s_axi_arid; + assign m_axi_araddr = s_axi_araddr; + assign m_axi_arlen = s_axi_arlen; + assign m_axi_arsize = s_axi_arsize; + assign m_axi_arburst = s_axi_arburst; + assign m_axi_arlock = s_axi_arlock; + assign m_axi_arcache = s_axi_arcache; + assign m_axi_arprot = s_axi_arprot; + assign m_axi_arqos = s_axi_arqos; + assign m_axi_arregion = s_axi_arregion; + assign m_axi_aruser = ARUSER_ENABLE ? s_axi_aruser : {ARUSER_WIDTH{1'b0}}; + assign m_axi_arvalid = s_axi_arvalid; + assign s_axi_arready = m_axi_arready; + +end + +// R channel + +if (R_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axi_rready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'b0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] temp_s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_rresp_reg = 2'b0; +reg temp_s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] temp_s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg temp_s_axi_rvalid_reg = 1'b0, temp_s_axi_rvalid_next; + +// datapath control +reg store_axi_r_input_to_output; +reg store_axi_r_input_to_temp; +reg store_axi_r_temp_to_output; + +assign m_axi_rready = m_axi_rready_reg; + +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axi_rready_early = s_axi_rready | (~temp_s_axi_rvalid_reg & (~s_axi_rvalid_reg | ~m_axi_rvalid)); + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + temp_s_axi_rvalid_next = temp_s_axi_rvalid_reg; + + store_axi_r_input_to_output = 1'b0; + store_axi_r_input_to_temp = 1'b0; + store_axi_r_temp_to_output = 1'b0; + + if (m_axi_rready_reg) begin + // input is ready + if (s_axi_rready | ~s_axi_rvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_temp = 1'b1; + end + end else if (s_axi_rready) begin + // input is not ready, but output is ready + s_axi_rvalid_next = temp_s_axi_rvalid_reg; + temp_s_axi_rvalid_next = 1'b0; + store_axi_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_rready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + temp_s_axi_rvalid_reg <= 1'b0; + end else begin + m_axi_rready_reg <= m_axi_rready_early; + s_axi_rvalid_reg <= s_axi_rvalid_next; + temp_s_axi_rvalid_reg <= temp_s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_input_to_output) begin + s_axi_rid_reg <= m_axi_rid; + s_axi_rdata_reg <= m_axi_rdata; + s_axi_rresp_reg <= m_axi_rresp; + s_axi_rlast_reg <= m_axi_rlast; + s_axi_ruser_reg <= m_axi_ruser; + end else if (store_axi_r_temp_to_output) begin + s_axi_rid_reg <= temp_s_axi_rid_reg; + s_axi_rdata_reg <= temp_s_axi_rdata_reg; + s_axi_rresp_reg <= temp_s_axi_rresp_reg; + s_axi_rlast_reg <= temp_s_axi_rlast_reg; + s_axi_ruser_reg <= temp_s_axi_ruser_reg; + end + + if (store_axi_r_input_to_temp) begin + temp_s_axi_rid_reg <= m_axi_rid; + temp_s_axi_rdata_reg <= m_axi_rdata; + temp_s_axi_rresp_reg <= m_axi_rresp; + temp_s_axi_rlast_reg <= m_axi_rlast; + temp_s_axi_ruser_reg <= m_axi_ruser; + end +end + +end else if (R_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axi_rready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_rid_reg = {ID_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] s_axi_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axi_rresp_reg = 2'b0; +reg s_axi_rlast_reg = 1'b0; +reg [RUSER_WIDTH-1:0] s_axi_ruser_reg = {RUSER_WIDTH{1'b0}}; +reg s_axi_rvalid_reg = 1'b0, s_axi_rvalid_next; + +// datapath control +reg store_axi_r_input_to_output; + +assign m_axi_rready = m_axi_rready_reg; + +assign s_axi_rid = s_axi_rid_reg; +assign s_axi_rdata = s_axi_rdata_reg; +assign s_axi_rresp = s_axi_rresp_reg; +assign s_axi_rlast = s_axi_rlast_reg; +assign s_axi_ruser = RUSER_ENABLE ? s_axi_ruser_reg : {RUSER_WIDTH{1'b0}}; +assign s_axi_rvalid = s_axi_rvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axi_rready_early = !s_axi_rvalid_next; + +always @* begin + // transfer sink ready state to source + s_axi_rvalid_next = s_axi_rvalid_reg; + + store_axi_r_input_to_output = 1'b0; + + if (m_axi_rready_reg) begin + s_axi_rvalid_next = m_axi_rvalid; + store_axi_r_input_to_output = 1'b1; + end else if (s_axi_rready) begin + s_axi_rvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_rready_reg <= 1'b0; + s_axi_rvalid_reg <= 1'b0; + end else begin + m_axi_rready_reg <= m_axi_rready_early; + s_axi_rvalid_reg <= s_axi_rvalid_next; + end + + // datapath + if (store_axi_r_input_to_output) begin + s_axi_rid_reg <= m_axi_rid; + s_axi_rdata_reg <= m_axi_rdata; + s_axi_rresp_reg <= m_axi_rresp; + s_axi_rlast_reg <= m_axi_rlast; + s_axi_ruser_reg <= m_axi_ruser; + end +end + +end else begin + + // bypass R channel + assign s_axi_rid = m_axi_rid; + assign s_axi_rdata = m_axi_rdata; + assign s_axi_rresp = m_axi_rresp; + assign s_axi_rlast = m_axi_rlast; + assign s_axi_ruser = RUSER_ENABLE ? m_axi_ruser : {RUSER_WIDTH{1'b0}}; + assign s_axi_rvalid = m_axi_rvalid; + assign m_axi_rready = s_axi_rready; + +end + +endgenerate + +endmodule diff --git a/corundum/lib/axi/rtl/axi_register_wr.v b/corundum/lib/axi/rtl/axi_register_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..4d83a0abf43e0a2a619d51cad91fb2f5fdee65df --- /dev/null +++ b/corundum/lib/axi/rtl/axi_register_wr.v @@ -0,0 +1,687 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 register (write) + */ +module axi_register_wr # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Width of ID signal + parameter ID_WIDTH = 8, + // Propagate awuser signal + parameter AWUSER_ENABLE = 0, + // Width of awuser signal + parameter AWUSER_WIDTH = 1, + // Propagate wuser signal + parameter WUSER_ENABLE = 0, + // Width of wuser signal + parameter WUSER_WIDTH = 1, + // Propagate buser signal + parameter BUSER_ENABLE = 0, + // Width of buser signal + parameter BUSER_WIDTH = 1, + // AW channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter AW_REG_TYPE = 1, + // W channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter W_REG_TYPE = 2, + // B channel register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter B_REG_TYPE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI slave interface + */ + input wire [ID_WIDTH-1:0] s_axi_awid, + input wire [ADDR_WIDTH-1:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire [3:0] s_axi_awregion, + input wire [AWUSER_WIDTH-1:0] s_axi_awuser, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [DATA_WIDTH-1:0] s_axi_wdata, + input wire [STRB_WIDTH-1:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire [WUSER_WIDTH-1:0] s_axi_wuser, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [ID_WIDTH-1:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire [BUSER_WIDTH-1:0] s_axi_buser, + output wire s_axi_bvalid, + input wire s_axi_bready, + + /* + * AXI master interface + */ + output wire [ID_WIDTH-1:0] m_axi_awid, + output wire [ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire [AWUSER_WIDTH-1:0] m_axi_awuser, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [DATA_WIDTH-1:0] m_axi_wdata, + output wire [STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire [WUSER_WIDTH-1:0] m_axi_wuser, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire [BUSER_WIDTH-1:0] m_axi_buser, + input wire m_axi_bvalid, + output wire m_axi_bready +); + +generate + +// AW channel + +if (AW_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_awready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_awlen_reg = 8'd0; +reg [2:0] m_axi_awsize_reg = 3'd0; +reg [1:0] m_axi_awburst_reg = 2'd0; +reg m_axi_awlock_reg = 1'b0; +reg [3:0] m_axi_awcache_reg = 4'd0; +reg [2:0] m_axi_awprot_reg = 3'd0; +reg [3:0] m_axi_awqos_reg = 4'd0; +reg [3:0] m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + +reg [ID_WIDTH-1:0] temp_m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] temp_m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] temp_m_axi_awlen_reg = 8'd0; +reg [2:0] temp_m_axi_awsize_reg = 3'd0; +reg [1:0] temp_m_axi_awburst_reg = 2'd0; +reg temp_m_axi_awlock_reg = 1'b0; +reg [3:0] temp_m_axi_awcache_reg = 4'd0; +reg [2:0] temp_m_axi_awprot_reg = 3'd0; +reg [3:0] temp_m_axi_awqos_reg = 4'd0; +reg [3:0] temp_m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] temp_m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg temp_m_axi_awvalid_reg = 1'b0, temp_m_axi_awvalid_next; + +// datapath control +reg store_axi_aw_input_to_output; +reg store_axi_aw_input_to_temp; +reg store_axi_aw_temp_to_output; + +assign s_axi_awready = s_axi_awready_reg; + +assign m_axi_awid = m_axi_awid_reg; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = m_axi_awsize_reg; +assign m_axi_awburst = m_axi_awburst_reg; +assign m_axi_awlock = m_axi_awlock_reg; +assign m_axi_awcache = m_axi_awcache_reg; +assign m_axi_awprot = m_axi_awprot_reg; +assign m_axi_awqos = m_axi_awqos_reg; +assign m_axi_awregion = m_axi_awregion_reg; +assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign m_axi_awvalid = m_axi_awvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_awready_early = m_axi_awready | (~temp_m_axi_awvalid_reg & (~m_axi_awvalid_reg | ~s_axi_awvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_awvalid_next = m_axi_awvalid_reg; + temp_m_axi_awvalid_next = temp_m_axi_awvalid_reg; + + store_axi_aw_input_to_output = 1'b0; + store_axi_aw_input_to_temp = 1'b0; + store_axi_aw_temp_to_output = 1'b0; + + if (s_axi_awready_reg) begin + // input is ready + if (m_axi_awready | ~m_axi_awvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_temp = 1'b1; + end + end else if (m_axi_awready) begin + // input is not ready, but output is ready + m_axi_awvalid_next = temp_m_axi_awvalid_reg; + temp_m_axi_awvalid_next = 1'b0; + store_axi_aw_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_awready_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + temp_m_axi_awvalid_reg <= 1'b0; + end else begin + s_axi_awready_reg <= s_axi_awready_early; + m_axi_awvalid_reg <= m_axi_awvalid_next; + temp_m_axi_awvalid_reg <= temp_m_axi_awvalid_next; + end + + // datapath + if (store_axi_aw_input_to_output) begin + m_axi_awid_reg <= s_axi_awid; + m_axi_awaddr_reg <= s_axi_awaddr; + m_axi_awlen_reg <= s_axi_awlen; + m_axi_awsize_reg <= s_axi_awsize; + m_axi_awburst_reg <= s_axi_awburst; + m_axi_awlock_reg <= s_axi_awlock; + m_axi_awcache_reg <= s_axi_awcache; + m_axi_awprot_reg <= s_axi_awprot; + m_axi_awqos_reg <= s_axi_awqos; + m_axi_awregion_reg <= s_axi_awregion; + m_axi_awuser_reg <= s_axi_awuser; + end else if (store_axi_aw_temp_to_output) begin + m_axi_awid_reg <= temp_m_axi_awid_reg; + m_axi_awaddr_reg <= temp_m_axi_awaddr_reg; + m_axi_awlen_reg <= temp_m_axi_awlen_reg; + m_axi_awsize_reg <= temp_m_axi_awsize_reg; + m_axi_awburst_reg <= temp_m_axi_awburst_reg; + m_axi_awlock_reg <= temp_m_axi_awlock_reg; + m_axi_awcache_reg <= temp_m_axi_awcache_reg; + m_axi_awprot_reg <= temp_m_axi_awprot_reg; + m_axi_awqos_reg <= temp_m_axi_awqos_reg; + m_axi_awregion_reg <= temp_m_axi_awregion_reg; + m_axi_awuser_reg <= temp_m_axi_awuser_reg; + end + + if (store_axi_aw_input_to_temp) begin + temp_m_axi_awid_reg <= s_axi_awid; + temp_m_axi_awaddr_reg <= s_axi_awaddr; + temp_m_axi_awlen_reg <= s_axi_awlen; + temp_m_axi_awsize_reg <= s_axi_awsize; + temp_m_axi_awburst_reg <= s_axi_awburst; + temp_m_axi_awlock_reg <= s_axi_awlock; + temp_m_axi_awcache_reg <= s_axi_awcache; + temp_m_axi_awprot_reg <= s_axi_awprot; + temp_m_axi_awqos_reg <= s_axi_awqos; + temp_m_axi_awregion_reg <= s_axi_awregion; + temp_m_axi_awuser_reg <= s_axi_awuser; + end +end + +end else if (AW_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_awready_reg = 1'b0; + +reg [ID_WIDTH-1:0] m_axi_awid_reg = {ID_WIDTH{1'b0}}; +reg [ADDR_WIDTH-1:0] m_axi_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [7:0] m_axi_awlen_reg = 8'd0; +reg [2:0] m_axi_awsize_reg = 3'd0; +reg [1:0] m_axi_awburst_reg = 2'd0; +reg m_axi_awlock_reg = 1'b0; +reg [3:0] m_axi_awcache_reg = 4'd0; +reg [2:0] m_axi_awprot_reg = 3'd0; +reg [3:0] m_axi_awqos_reg = 4'd0; +reg [3:0] m_axi_awregion_reg = 4'd0; +reg [AWUSER_WIDTH-1:0] m_axi_awuser_reg = {AWUSER_WIDTH{1'b0}}; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + +// datapath control +reg store_axi_aw_input_to_output; + +assign s_axi_awready = s_axi_awready_reg; + +assign m_axi_awid = m_axi_awid_reg; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = m_axi_awsize_reg; +assign m_axi_awburst = m_axi_awburst_reg; +assign m_axi_awlock = m_axi_awlock_reg; +assign m_axi_awcache = m_axi_awcache_reg; +assign m_axi_awprot = m_axi_awprot_reg; +assign m_axi_awqos = m_axi_awqos_reg; +assign m_axi_awregion = m_axi_awregion_reg; +assign m_axi_awuser = AWUSER_ENABLE ? m_axi_awuser_reg : {AWUSER_WIDTH{1'b0}}; +assign m_axi_awvalid = m_axi_awvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_awready_eawly = !m_axi_awvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_awvalid_next = m_axi_awvalid_reg; + + store_axi_aw_input_to_output = 1'b0; + + if (s_axi_awready_reg) begin + m_axi_awvalid_next = s_axi_awvalid; + store_axi_aw_input_to_output = 1'b1; + end else if (m_axi_awready) begin + m_axi_awvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_awready_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + end else begin + s_axi_awready_reg <= s_axi_awready_eawly; + m_axi_awvalid_reg <= m_axi_awvalid_next; + end + + // datapath + if (store_axi_aw_input_to_output) begin + m_axi_awid_reg <= s_axi_awid; + m_axi_awaddr_reg <= s_axi_awaddr; + m_axi_awlen_reg <= s_axi_awlen; + m_axi_awsize_reg <= s_axi_awsize; + m_axi_awburst_reg <= s_axi_awburst; + m_axi_awlock_reg <= s_axi_awlock; + m_axi_awcache_reg <= s_axi_awcache; + m_axi_awprot_reg <= s_axi_awprot; + m_axi_awqos_reg <= s_axi_awqos; + m_axi_awregion_reg <= s_axi_awregion; + m_axi_awuser_reg <= s_axi_awuser; + end +end + +end else begin + + // bypass AW channel + assign m_axi_awid = s_axi_awid; + assign m_axi_awaddr = s_axi_awaddr; + assign m_axi_awlen = s_axi_awlen; + assign m_axi_awsize = s_axi_awsize; + assign m_axi_awburst = s_axi_awburst; + assign m_axi_awlock = s_axi_awlock; + assign m_axi_awcache = s_axi_awcache; + assign m_axi_awprot = s_axi_awprot; + assign m_axi_awqos = s_axi_awqos; + assign m_axi_awregion = s_axi_awregion; + assign m_axi_awuser = AWUSER_ENABLE ? s_axi_awuser : {AWUSER_WIDTH{1'b0}}; + assign m_axi_awvalid = s_axi_awvalid; + assign s_axi_awready = m_axi_awready; + +end + +// W channel + +if (W_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axi_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +reg [DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg temp_m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] temp_m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; + +// datapath control +reg store_axi_w_input_to_output; +reg store_axi_w_input_to_temp; +reg store_axi_w_temp_to_output; + +assign s_axi_wready = s_axi_wready_reg; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wlast = m_axi_wlast_reg; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axi_wready_early = m_axi_wready | (~temp_m_axi_wvalid_reg & (~m_axi_wvalid_reg | ~s_axi_wvalid)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_input_to_output = 1'b0; + store_axi_w_input_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (s_axi_wready_reg) begin + // input is ready + if (m_axi_wready | ~m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_wready_reg <= 1'b0; + m_axi_wvalid_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + s_axi_wready_reg <= s_axi_wready_early; + m_axi_wvalid_reg <= m_axi_wvalid_next; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_input_to_output) begin + m_axi_wdata_reg <= s_axi_wdata; + m_axi_wstrb_reg <= s_axi_wstrb; + m_axi_wlast_reg <= s_axi_wlast; + m_axi_wuser_reg <= s_axi_wuser; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + m_axi_wuser_reg <= temp_m_axi_wuser_reg; + end + + if (store_axi_w_input_to_temp) begin + temp_m_axi_wdata_reg <= s_axi_wdata; + temp_m_axi_wstrb_reg <= s_axi_wstrb; + temp_m_axi_wlast_reg <= s_axi_wlast; + temp_m_axi_wuser_reg <= s_axi_wuser; + end +end + +end else if (W_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axi_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axi_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axi_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axi_wlast_reg = 1'b0; +reg [WUSER_WIDTH-1:0] m_axi_wuser_reg = {WUSER_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; + +// datapath control +reg store_axi_w_input_to_output; + +assign s_axi_wready = s_axi_wready_reg; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wlast = m_axi_wlast_reg; +assign m_axi_wuser = WUSER_ENABLE ? m_axi_wuser_reg : {WUSER_WIDTH{1'b0}}; +assign m_axi_wvalid = m_axi_wvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axi_wready_ewly = !m_axi_wvalid_next; + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + + store_axi_w_input_to_output = 1'b0; + + if (s_axi_wready_reg) begin + m_axi_wvalid_next = s_axi_wvalid; + store_axi_w_input_to_output = 1'b1; + end else if (m_axi_wready) begin + m_axi_wvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axi_wready_reg <= 1'b0; + m_axi_wvalid_reg <= 1'b0; + end else begin + s_axi_wready_reg <= s_axi_wready_ewly; + m_axi_wvalid_reg <= m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_input_to_output) begin + m_axi_wdata_reg <= s_axi_wdata; + m_axi_wstrb_reg <= s_axi_wstrb; + m_axi_wlast_reg <= s_axi_wlast; + m_axi_wuser_reg <= s_axi_wuser; + end +end + +end else begin + + // bypass W channel + assign m_axi_wdata = s_axi_wdata; + assign m_axi_wstrb = s_axi_wstrb; + assign m_axi_wlast = s_axi_wlast; + assign m_axi_wuser = WUSER_ENABLE ? s_axi_wuser : {WUSER_WIDTH{1'b0}}; + assign m_axi_wvalid = s_axi_wvalid; + assign s_axi_wready = m_axi_wready; + +end + +// B channel + +if (B_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axi_bready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +reg [ID_WIDTH-1:0] temp_s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] temp_s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] temp_s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg temp_s_axi_bvalid_reg = 1'b0, temp_s_axi_bvalid_next; + +// datapath control +reg store_axi_b_input_to_output; +reg store_axi_b_input_to_temp; +reg store_axi_b_temp_to_output; + +assign m_axi_bready = m_axi_bready_reg; + +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_buser = BUSER_ENABLE ? s_axi_buser_reg : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axi_bready_early = s_axi_bready | (~temp_s_axi_bvalid_reg & (~s_axi_bvalid_reg | ~m_axi_bvalid)); + +always @* begin + // transfer sink ready state to source + s_axi_bvalid_next = s_axi_bvalid_reg; + temp_s_axi_bvalid_next = temp_s_axi_bvalid_reg; + + store_axi_b_input_to_output = 1'b0; + store_axi_b_input_to_temp = 1'b0; + store_axi_b_temp_to_output = 1'b0; + + if (m_axi_bready_reg) begin + // input is ready + if (s_axi_bready | ~s_axi_bvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_temp = 1'b1; + end + end else if (s_axi_bready) begin + // input is not ready, but output is ready + s_axi_bvalid_next = temp_s_axi_bvalid_reg; + temp_s_axi_bvalid_next = 1'b0; + store_axi_b_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_bready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + temp_s_axi_bvalid_reg <= 1'b0; + end else begin + m_axi_bready_reg <= m_axi_bready_early; + s_axi_bvalid_reg <= s_axi_bvalid_next; + temp_s_axi_bvalid_reg <= temp_s_axi_bvalid_next; + end + + // datapath + if (store_axi_b_input_to_output) begin + s_axi_bid_reg <= m_axi_bid; + s_axi_bresp_reg <= m_axi_bresp; + s_axi_buser_reg <= m_axi_buser; + end else if (store_axi_b_temp_to_output) begin + s_axi_bid_reg <= temp_s_axi_bid_reg; + s_axi_bresp_reg <= temp_s_axi_bresp_reg; + s_axi_buser_reg <= temp_s_axi_buser_reg; + end + + if (store_axi_b_input_to_temp) begin + temp_s_axi_bid_reg <= m_axi_bid; + temp_s_axi_bresp_reg <= m_axi_bresp; + temp_s_axi_buser_reg <= m_axi_buser; + end +end + +end else if (B_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axi_bready_reg = 1'b0; + +reg [ID_WIDTH-1:0] s_axi_bid_reg = {ID_WIDTH{1'b0}}; +reg [1:0] s_axi_bresp_reg = 2'b0; +reg [BUSER_WIDTH-1:0] s_axi_buser_reg = {BUSER_WIDTH{1'b0}}; +reg s_axi_bvalid_reg = 1'b0, s_axi_bvalid_next; + +// datapath control +reg store_axi_b_input_to_output; + +assign m_axi_bready = m_axi_bready_reg; + +assign s_axi_bid = s_axi_bid_reg; +assign s_axi_bresp = s_axi_bresp_reg; +assign s_axi_buser = BUSER_ENABLE ? s_axi_buser_reg : {BUSER_WIDTH{1'b0}}; +assign s_axi_bvalid = s_axi_bvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axi_bready_early = !s_axi_bvalid_next; + +always @* begin + // transfer sink ready state to source + s_axi_bvalid_next = s_axi_bvalid_reg; + + store_axi_b_input_to_output = 1'b0; + + if (m_axi_bready_reg) begin + s_axi_bvalid_next = m_axi_bvalid; + store_axi_b_input_to_output = 1'b1; + end else if (s_axi_bready) begin + s_axi_bvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_bready_reg <= 1'b0; + s_axi_bvalid_reg <= 1'b0; + end else begin + m_axi_bready_reg <= m_axi_bready_early; + s_axi_bvalid_reg <= s_axi_bvalid_next; + end + + // datapath + if (store_axi_b_input_to_output) begin + s_axi_bid_reg <= m_axi_bid; + s_axi_bresp_reg <= m_axi_bresp; + s_axi_buser_reg <= m_axi_buser; + end +end + +end else begin + + // bypass B channel + assign s_axi_bid = m_axi_bid; + assign s_axi_bresp = m_axi_bresp; + assign s_axi_buser = BUSER_ENABLE ? m_axi_buser : {BUSER_WIDTH{1'b0}}; + assign s_axi_bvalid = m_axi_bvalid; + assign m_axi_bready = s_axi_bready; + +end + +endgenerate + +endmodule diff --git a/corundum/lib/axi/rtl/axil_adapter.v b/corundum/lib/axi/rtl/axil_adapter.v new file mode 100644 index 0000000000000000000000000000000000000000..c77a6a3076a64352bd99ad475aa9d586d85c3a56 --- /dev/null +++ b/corundum/lib/axi/rtl/axil_adapter.v @@ -0,0 +1,174 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite width adapter + */ +module axil_adapter # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) interface data bus in bits + parameter S_DATA_WIDTH = 32, + // Width of input (slave) interface wstrb (width of data bus in words) + parameter S_STRB_WIDTH = (S_DATA_WIDTH/8), + // Width of output (master) interface data bus in bits + parameter M_DATA_WIDTH = 32, + // Width of output (master) interface wstrb (width of data bus in words) + parameter M_STRB_WIDTH = (M_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interface + */ + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [S_DATA_WIDTH-1:0] s_axil_wdata, + input wire [S_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [S_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [M_DATA_WIDTH-1:0] m_axil_wdata, + output wire [M_STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready, + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [M_DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + +axil_adapter_wr #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_STRB_WIDTH(S_STRB_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_STRB_WIDTH(M_STRB_WIDTH) +) +axil_adapter_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI lite slave interface + */ + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + + /* + * AXI lite master interface + */ + .m_axil_awaddr(m_axil_awaddr), + .m_axil_awprot(m_axil_awprot), + .m_axil_awvalid(m_axil_awvalid), + .m_axil_awready(m_axil_awready), + .m_axil_wdata(m_axil_wdata), + .m_axil_wstrb(m_axil_wstrb), + .m_axil_wvalid(m_axil_wvalid), + .m_axil_wready(m_axil_wready), + .m_axil_bresp(m_axil_bresp), + .m_axil_bvalid(m_axil_bvalid), + .m_axil_bready(m_axil_bready) +); + +axil_adapter_rd #( + .ADDR_WIDTH(ADDR_WIDTH), + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_STRB_WIDTH(S_STRB_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_STRB_WIDTH(M_STRB_WIDTH) +) +axil_adapter_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI lite slave interface + */ + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + + /* + * AXI lite master interface + */ + .m_axil_araddr(m_axil_araddr), + .m_axil_arprot(m_axil_arprot), + .m_axil_arvalid(m_axil_arvalid), + .m_axil_arready(m_axil_arready), + .m_axil_rdata(m_axil_rdata), + .m_axil_rresp(m_axil_rresp), + .m_axil_rvalid(m_axil_rvalid), + .m_axil_rready(m_axil_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axil_adapter_rd.v b/corundum/lib/axi/rtl/axil_adapter_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..e1941017403bb14328ed2e010360ba00ecb22fb0 --- /dev/null +++ b/corundum/lib/axi/rtl/axil_adapter_rd.v @@ -0,0 +1,268 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite width adapter (read) + */ +module axil_adapter_rd # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) interface data bus in bits + parameter S_DATA_WIDTH = 32, + // Width of input (slave) interface wstrb (width of data bus in words) + parameter S_STRB_WIDTH = (S_DATA_WIDTH/8), + // Width of output (master) interface data bus in bits + parameter M_DATA_WIDTH = 32, + // Width of output (master) interface wstrb (width of data bus in words) + parameter M_STRB_WIDTH = (M_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interface + */ + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [S_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [M_DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + +parameter S_ADDR_BIT_OFFSET = $clog2(S_STRB_WIDTH); +parameter M_ADDR_BIT_OFFSET = $clog2(M_STRB_WIDTH); +parameter S_WORD_WIDTH = S_STRB_WIDTH; +parameter M_WORD_WIDTH = M_STRB_WIDTH; +parameter S_WORD_SIZE = S_DATA_WIDTH/S_WORD_WIDTH; +parameter M_WORD_SIZE = M_DATA_WIDTH/M_WORD_WIDTH; + +// output bus is wider +parameter EXPAND = M_STRB_WIDTH > S_STRB_WIDTH; +parameter DATA_WIDTH = EXPAND ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter STRB_WIDTH = EXPAND ? M_STRB_WIDTH : S_STRB_WIDTH; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND ? (M_STRB_WIDTH / S_STRB_WIDTH) : (S_STRB_WIDTH / M_STRB_WIDTH); +parameter SEGMENT_COUNT_WIDTH = SEGMENT_COUNT == 1 ? 1 : $clog2(SEGMENT_COUNT); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_STRB_WIDTH = STRB_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (S_WORD_SIZE * S_STRB_WIDTH != S_DATA_WIDTH) begin + $error("Error: AXI slave interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_WORD_SIZE * M_STRB_WIDTH != M_DATA_WIDTH) begin + $error("Error: AXI master interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_WORD_SIZE != M_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(S_WORD_WIDTH) != S_WORD_WIDTH) begin + $error("Error: AXI slave interface word width must be even power of two (instance %m)"); + $finish; + end + + if (2**$clog2(M_WORD_WIDTH) != M_WORD_WIDTH) begin + $error("Error: AXI master interface word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [0:0] + STATE_IDLE = 1'd0, + STATE_DATA = 1'd1; + +reg [0:0] state_reg = STATE_IDLE, state_next; + +reg [SEGMENT_COUNT_WIDTH-1:0] current_segment_reg = 0, current_segment_next; + +reg s_axil_arready_reg = 1'b0, s_axil_arready_next; +reg [S_DATA_WIDTH-1:0] s_axil_rdata_reg = {S_DATA_WIDTH{1'b0}}, s_axil_rdata_next; +reg [1:0] s_axil_rresp_reg = 2'd0, s_axil_rresp_next; +reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next; + +reg [ADDR_WIDTH-1:0] m_axil_araddr_reg = {ADDR_WIDTH{1'b0}}, m_axil_araddr_next; +reg [2:0] m_axil_arprot_reg = 3'd0, m_axil_arprot_next; +reg m_axil_arvalid_reg = 1'b0, m_axil_arvalid_next; +reg m_axil_rready_reg = 1'b0, m_axil_rready_next; + +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = s_axil_rresp_reg; +assign s_axil_rvalid = s_axil_rvalid_reg; + +assign m_axil_araddr = m_axil_araddr_reg; +assign m_axil_arprot = m_axil_arprot_reg; +assign m_axil_arvalid = m_axil_arvalid_reg; +assign m_axil_rready = m_axil_rready_reg; + +always @* begin + state_next = STATE_IDLE; + + current_segment_next = current_segment_reg; + + s_axil_arready_next = 1'b0; + s_axil_rdata_next = s_axil_rdata_reg; + s_axil_rresp_next = s_axil_rresp_reg; + s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready; + m_axil_araddr_next = m_axil_araddr_reg; + m_axil_arprot_next = m_axil_arprot_reg; + m_axil_arvalid_next = m_axil_arvalid_reg && !m_axil_arready; + m_axil_rready_next = 1'b0; + + if (SEGMENT_COUNT == 1 || EXPAND) begin + // master output is same width or wider; single cycle direct transfer + case (state_reg) + STATE_IDLE: begin + s_axil_arready_next = !m_axil_arvalid; + + if (s_axil_arready && s_axil_arvalid) begin + s_axil_arready_next = 1'b0; + m_axil_araddr_next = s_axil_araddr; + m_axil_arprot_next = s_axil_arprot; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = !m_axil_rvalid; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + m_axil_rready_next = !s_axil_rvalid; + + if (m_axil_rready && m_axil_rvalid) begin + m_axil_rready_next = 1'b0; + if (M_WORD_WIDTH == S_WORD_WIDTH) begin + s_axil_rdata_next = m_axil_rdata; + end else begin + s_axil_rdata_next = m_axil_rdata >> (m_axil_araddr_reg[M_ADDR_BIT_OFFSET - 1:S_ADDR_BIT_OFFSET] * S_DATA_WIDTH); + end + s_axil_rresp_next = m_axil_rresp; + s_axil_rvalid_next = 1'b1; + s_axil_arready_next = !m_axil_arvalid; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DATA; + end + end + endcase + end else begin + // master output is narrower; may need several cycles + case (state_reg) + STATE_IDLE: begin + s_axil_arready_next = !m_axil_arvalid; + + current_segment_next = 0; + s_axil_rresp_next = 2'd0; + + if (s_axil_arready && s_axil_arvalid) begin + s_axil_arready_next = 1'b0; + m_axil_araddr_next = s_axil_araddr & ({ADDR_WIDTH{1'b1}} << S_ADDR_BIT_OFFSET); + m_axil_arprot_next = s_axil_arprot; + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = !m_axil_rvalid; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + m_axil_rready_next = !s_axil_rvalid; + + if (m_axil_rready && m_axil_rvalid) begin + m_axil_rready_next = 1'b0; + s_axil_rdata_next[current_segment_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = m_axil_rdata; + if (m_axil_rresp) begin + s_axil_rresp_next = m_axil_rresp; + end + if (current_segment_reg == SEGMENT_COUNT-1) begin + s_axil_rvalid_next = 1'b1; + s_axil_arready_next = !m_axil_arvalid; + state_next = STATE_IDLE; + end else begin + current_segment_next = current_segment_reg + 1; + m_axil_araddr_next = m_axil_araddr_reg + SEGMENT_STRB_WIDTH; + m_axil_arvalid_next = 1'b1; + state_next = STATE_DATA; + end + end else begin + state_next = STATE_DATA; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + m_axil_arvalid_reg <= 1'b0; + m_axil_rready_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + m_axil_arvalid_reg <= m_axil_arvalid_next; + m_axil_rready_reg <= m_axil_rready_next; + end + + current_segment_reg <= current_segment_next; + + s_axil_rdata_reg <= s_axil_rdata_next; + s_axil_rresp_reg <= s_axil_rresp_next; + m_axil_araddr_reg <= m_axil_araddr_next; + m_axil_arprot_reg <= m_axil_arprot_next; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axil_adapter_wr.v b/corundum/lib/axi/rtl/axil_adapter_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..ad2bfd138c87fa12c1b11c5c065c638c3111c7aa --- /dev/null +++ b/corundum/lib/axi/rtl/axil_adapter_wr.v @@ -0,0 +1,330 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite width adapter (write) + */ +module axil_adapter_wr # +( + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of input (slave) interface data bus in bits + parameter S_DATA_WIDTH = 32, + // Width of input (slave) interface wstrb (width of data bus in words) + parameter S_STRB_WIDTH = (S_DATA_WIDTH/8), + // Width of output (master) interface data bus in bits + parameter M_DATA_WIDTH = 32, + // Width of output (master) interface wstrb (width of data bus in words) + parameter M_STRB_WIDTH = (M_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interface + */ + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [S_DATA_WIDTH-1:0] s_axil_wdata, + input wire [S_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [M_DATA_WIDTH-1:0] m_axil_wdata, + output wire [M_STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready +); + +parameter S_ADDR_BIT_OFFSET = $clog2(S_STRB_WIDTH); +parameter M_ADDR_BIT_OFFSET = $clog2(M_STRB_WIDTH); +parameter S_WORD_WIDTH = S_STRB_WIDTH; +parameter M_WORD_WIDTH = M_STRB_WIDTH; +parameter S_WORD_SIZE = S_DATA_WIDTH/S_WORD_WIDTH; +parameter M_WORD_SIZE = M_DATA_WIDTH/M_WORD_WIDTH; + +// output bus is wider +parameter EXPAND = M_STRB_WIDTH > S_STRB_WIDTH; +parameter DATA_WIDTH = EXPAND ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter STRB_WIDTH = EXPAND ? M_STRB_WIDTH : S_STRB_WIDTH; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND ? (M_STRB_WIDTH / S_STRB_WIDTH) : (S_STRB_WIDTH / M_STRB_WIDTH); +parameter SEGMENT_COUNT_WIDTH = SEGMENT_COUNT == 1 ? 1 : $clog2(SEGMENT_COUNT); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_STRB_WIDTH = STRB_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (S_WORD_SIZE * S_STRB_WIDTH != S_DATA_WIDTH) begin + $error("Error: AXI slave interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_WORD_SIZE * M_STRB_WIDTH != M_DATA_WIDTH) begin + $error("Error: AXI master interface data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_WORD_SIZE != M_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(S_WORD_WIDTH) != S_WORD_WIDTH) begin + $error("Error: AXI slave interface word width must be even power of two (instance %m)"); + $finish; + end + + if (2**$clog2(M_WORD_WIDTH) != M_WORD_WIDTH) begin + $error("Error: AXI master interface word width must be even power of two (instance %m)"); + $finish; + end +end + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_DATA = 2'd1, + STATE_RESP = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [DATA_WIDTH-1:0] data_reg = {DATA_WIDTH{1'b0}}, data_next; +reg [STRB_WIDTH-1:0] strb_reg = {STRB_WIDTH{1'b0}}, strb_next; + +reg [SEGMENT_COUNT_WIDTH-1:0] current_segment_reg = 0, current_segment_next; + +reg s_axil_awready_reg = 1'b0, s_axil_awready_next; +reg s_axil_wready_reg = 1'b0, s_axil_wready_next; +reg [1:0] s_axil_bresp_reg = 2'd0, s_axil_bresp_next; +reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next; + +reg [ADDR_WIDTH-1:0] m_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}, m_axil_awaddr_next; +reg [2:0] m_axil_awprot_reg = 3'd0, m_axil_awprot_next; +reg m_axil_awvalid_reg = 1'b0, m_axil_awvalid_next; +reg [M_DATA_WIDTH-1:0] m_axil_wdata_reg = {M_DATA_WIDTH{1'b0}}, m_axil_wdata_next; +reg [M_STRB_WIDTH-1:0] m_axil_wstrb_reg = {M_STRB_WIDTH{1'b0}}, m_axil_wstrb_next; +reg m_axil_wvalid_reg = 1'b0, m_axil_wvalid_next; +reg m_axil_bready_reg = 1'b0, m_axil_bready_next; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = s_axil_bresp_reg; +assign s_axil_bvalid = s_axil_bvalid_reg; + +assign m_axil_awaddr = m_axil_awaddr_reg; +assign m_axil_awprot = m_axil_awprot_reg; +assign m_axil_awvalid = m_axil_awvalid_reg; +assign m_axil_wdata = m_axil_wdata_reg; +assign m_axil_wstrb = m_axil_wstrb_reg; +assign m_axil_wvalid = m_axil_wvalid_reg; +assign m_axil_bready = m_axil_bready_reg; + +always @* begin + state_next = STATE_IDLE; + + data_next = data_reg; + strb_next = strb_reg; + + current_segment_next = current_segment_reg; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bresp_next = s_axil_bresp_reg; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + m_axil_awaddr_next = m_axil_awaddr_reg; + m_axil_awprot_next = m_axil_awprot_reg; + m_axil_awvalid_next = m_axil_awvalid_reg && !m_axil_awready; + m_axil_wdata_next = m_axil_wdata_reg; + m_axil_wstrb_next = m_axil_wstrb_reg; + m_axil_wvalid_next = m_axil_wvalid_reg && !m_axil_wready; + m_axil_bready_next = 1'b0; + + if (SEGMENT_COUNT == 1 || EXPAND) begin + // master output is same width or wider; single cycle direct transfer + case (state_reg) + STATE_IDLE: begin + s_axil_awready_next = !m_axil_awvalid; + + if (s_axil_awready && s_axil_awvalid) begin + s_axil_awready_next = 1'b0; + m_axil_awaddr_next = s_axil_awaddr; + m_axil_awprot_next = s_axil_awprot; + m_axil_awvalid_next = 1'b1; + s_axil_wready_next = !m_axil_wvalid; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + s_axil_wready_next = !m_axil_wvalid; + + if (s_axil_wready && s_axil_wvalid) begin + s_axil_wready_next = 1'b0; + if (M_WORD_WIDTH == S_WORD_WIDTH) begin + m_axil_wdata_next = s_axil_wdata; + m_axil_wstrb_next = s_axil_wstrb; + end else begin + m_axil_wdata_next = {(M_WORD_WIDTH/S_WORD_WIDTH){s_axil_wdata}}; + m_axil_wstrb_next = s_axil_wstrb << (m_axil_awaddr_reg[M_ADDR_BIT_OFFSET - 1:S_ADDR_BIT_OFFSET] * S_STRB_WIDTH); + end + m_axil_wvalid_next = 1'b1; + m_axil_bready_next = !s_axil_bvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA; + end + end + STATE_RESP: begin + m_axil_bready_next = !s_axil_bvalid; + + if (m_axil_bready && m_axil_bvalid) begin + m_axil_bready_next = 1'b0; + s_axil_bresp_next = m_axil_bresp; + s_axil_bvalid_next = 1'b1; + s_axil_awready_next = !m_axil_awvalid; + state_next = STATE_IDLE; + end else begin + state_next = STATE_RESP; + end + end + endcase + end else begin + // master output is narrower; may need several cycles + case (state_reg) + STATE_IDLE: begin + s_axil_awready_next = !m_axil_awvalid; + + current_segment_next = 0; + s_axil_bresp_next = 2'd0; + + if (s_axil_awready && s_axil_awvalid) begin + s_axil_awready_next = 1'b0; + m_axil_awaddr_next = s_axil_awaddr & ({ADDR_WIDTH{1'b1}} << S_ADDR_BIT_OFFSET); + m_axil_awprot_next = s_axil_awprot; + m_axil_awvalid_next = 1'b1; + s_axil_wready_next = !m_axil_wvalid; + state_next = STATE_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DATA: begin + s_axil_wready_next = !m_axil_wvalid; + + if (s_axil_wready && s_axil_wvalid) begin + s_axil_wready_next = 1'b0; + data_next = s_axil_wdata; + strb_next = s_axil_wstrb; + m_axil_wdata_next = s_axil_wdata; + m_axil_wstrb_next = s_axil_wstrb; + m_axil_wvalid_next = 1'b1; + m_axil_bready_next = !s_axil_bvalid; + state_next = STATE_RESP; + end else begin + state_next = STATE_DATA; + end + end + STATE_RESP: begin + m_axil_bready_next = !s_axil_bvalid; + + if (m_axil_bready && m_axil_bvalid) begin + m_axil_bready_next = 1'b0; + if (m_axil_bresp != 0) begin + s_axil_bresp_next = m_axil_bresp; + end + if (current_segment_reg == SEGMENT_COUNT-1) begin + s_axil_bvalid_next = 1'b1; + s_axil_awready_next = !m_axil_awvalid; + state_next = STATE_IDLE; + end else begin + current_segment_next = current_segment_reg + 1; + m_axil_awaddr_next = m_axil_awaddr_reg + SEGMENT_STRB_WIDTH; + m_axil_awvalid_next = 1'b1; + m_axil_wdata_next = data_reg >> (current_segment_reg+1)*SEGMENT_DATA_WIDTH; + m_axil_wstrb_next = strb_reg >> (current_segment_reg+1)*SEGMENT_STRB_WIDTH; + m_axil_wvalid_next = 1'b1; + state_next = STATE_RESP; + end + end else begin + state_next = STATE_RESP; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + m_axil_awvalid_reg <= 1'b0; + m_axil_wvalid_reg <= 1'b0; + m_axil_bready_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + m_axil_awvalid_reg <= m_axil_awvalid_next; + m_axil_wvalid_reg <= m_axil_wvalid_next; + m_axil_bready_reg <= m_axil_bready_next; + end + + data_reg <= data_next; + strb_reg <= strb_next; + + current_segment_reg <= current_segment_next; + + s_axil_bresp_reg <= s_axil_bresp_next; + m_axil_awaddr_reg <= m_axil_awaddr_next; + m_axil_awprot_reg <= m_axil_awprot_next; + m_axil_wdata_reg <= m_axil_wdata_next; + m_axil_wstrb_reg <= m_axil_wstrb_next; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axil_cdc.v b/corundum/lib/axi/rtl/axil_cdc.v new file mode 100644 index 0000000000000000000000000000000000000000..f719d6c5f75aa18107b2cd2317c6cd7a5abee6de --- /dev/null +++ b/corundum/lib/axi/rtl/axil_cdc.v @@ -0,0 +1,169 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite clock domain crossing module + */ +module axil_cdc # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8) +) +( + /* + * AXI lite slave interface + */ + input wire s_clk, + input wire s_rst, + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [DATA_WIDTH-1:0] s_axil_wdata, + input wire [STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * AXI lite master interface + */ + input wire m_clk, + input wire m_rst, + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [DATA_WIDTH-1:0] m_axil_wdata, + output wire [STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready, + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + +axil_cdc_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH) +) +axil_cdc_wr_inst ( + /* + * AXI lite slave interface + */ + .s_clk(s_clk), + .s_rst(s_rst), + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + + /* + * AXI lite master interface + */ + .m_clk(m_clk), + .m_rst(m_rst), + .m_axil_awaddr(m_axil_awaddr), + .m_axil_awprot(m_axil_awprot), + .m_axil_awvalid(m_axil_awvalid), + .m_axil_awready(m_axil_awready), + .m_axil_wdata(m_axil_wdata), + .m_axil_wstrb(m_axil_wstrb), + .m_axil_wvalid(m_axil_wvalid), + .m_axil_wready(m_axil_wready), + .m_axil_bresp(m_axil_bresp), + .m_axil_bvalid(m_axil_bvalid), + .m_axil_bready(m_axil_bready) +); + +axil_cdc_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH) +) +axil_cdc_rd_inst ( + /* + * AXI lite slave interface + */ + .s_clk(s_clk), + .s_rst(s_rst), + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + + /* + * AXI lite master interface + */ + .m_clk(m_clk), + .m_rst(m_rst), + .m_axil_araddr(m_axil_araddr), + .m_axil_arprot(m_axil_arprot), + .m_axil_arvalid(m_axil_arvalid), + .m_axil_arready(m_axil_arready), + .m_axil_rdata(m_axil_rdata), + .m_axil_rresp(m_axil_rresp), + .m_axil_rvalid(m_axil_rvalid), + .m_axil_rready(m_axil_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axil_cdc_rd.v b/corundum/lib/axi/rtl/axil_cdc_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..a34aff2815cfbe65500d10c3a118c974e56903e6 --- /dev/null +++ b/corundum/lib/axi/rtl/axil_cdc_rd.v @@ -0,0 +1,204 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite clock domain crossing module (read) + */ +module axil_cdc_rd # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8) +) +( + /* + * AXI lite slave interface + */ + input wire s_clk, + input wire s_rst, + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * AXI lite master interface + */ + input wire m_clk, + input wire m_rst, + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + +reg [1:0] s_state_reg = 2'd0; +reg s_flag_reg = 1'b0; +(* srl_style = "register" *) +reg s_flag_sync_reg_1 = 1'b0; +(* srl_style = "register" *) +reg s_flag_sync_reg_2 = 1'b0; + +reg [1:0] m_state_reg = 2'd0; +reg m_flag_reg = 1'b0; +(* srl_style = "register" *) +reg m_flag_sync_reg_1 = 1'b0; +(* srl_style = "register" *) +reg m_flag_sync_reg_2 = 1'b0; + +reg [ADDR_WIDTH-1:0] s_axil_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] s_axil_arprot_reg = 3'd0; +reg s_axil_arvalid_reg = 1'b0; +reg [DATA_WIDTH-1:0] s_axil_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axil_rresp_reg = 2'b00; +reg s_axil_rvalid_reg = 1'b0; + +reg [ADDR_WIDTH-1:0] m_axil_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] m_axil_arprot_reg = 3'd0; +reg m_axil_arvalid_reg = 1'b0; +reg [DATA_WIDTH-1:0] m_axil_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] m_axil_rresp_reg = 2'b00; +reg m_axil_rvalid_reg = 1'b1; + +assign s_axil_arready = !s_axil_arvalid_reg && !s_axil_rvalid_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = s_axil_rresp_reg; +assign s_axil_rvalid = s_axil_rvalid_reg; + +assign m_axil_araddr = m_axil_araddr_reg; +assign m_axil_arprot = m_axil_arprot_reg; +assign m_axil_arvalid = m_axil_arvalid_reg; +assign m_axil_rready = !m_axil_rvalid_reg; + +// slave side +always @(posedge s_clk) begin + s_axil_rvalid_reg <= s_axil_rvalid_reg && !s_axil_rready; + + if (!s_axil_arvalid_reg && !s_axil_rvalid_reg) begin + s_axil_araddr_reg <= s_axil_araddr; + s_axil_arprot_reg <= s_axil_arprot; + s_axil_arvalid_reg <= s_axil_arvalid; + end + + case (s_state_reg) + 2'd0: begin + if (s_axil_arvalid_reg) begin + s_state_reg <= 2'd1; + s_flag_reg <= 1'b1; + end + end + 2'd1: begin + if (m_flag_sync_reg_2) begin + s_state_reg <= 2'd2; + s_flag_reg <= 1'b0; + s_axil_rdata_reg <= m_axil_rdata_reg; + s_axil_rresp_reg <= m_axil_rresp_reg; + s_axil_rvalid_reg <= 1'b1; + end + end + 2'd2: begin + if (!m_flag_sync_reg_2) begin + s_state_reg <= 2'd0; + s_axil_arvalid_reg <= 1'b0; + end + end + endcase + + if (s_rst) begin + s_state_reg <= 2'd0; + s_flag_reg <= 1'b0; + s_axil_arvalid_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + end +end + +// synchronization +always @(posedge s_clk) begin + m_flag_sync_reg_1 <= m_flag_reg; + m_flag_sync_reg_2 <= m_flag_sync_reg_1; +end + +always @(posedge m_clk) begin + s_flag_sync_reg_1 <= s_flag_reg; + s_flag_sync_reg_2 <= s_flag_sync_reg_1; +end + +// master side +always @(posedge m_clk) begin + m_axil_arvalid_reg <= m_axil_arvalid_reg && !m_axil_arready; + + if (!m_axil_rvalid_reg) begin + m_axil_rdata_reg <= m_axil_rdata; + m_axil_rresp_reg <= m_axil_rresp; + m_axil_rvalid_reg <= m_axil_rvalid; + end + + case (m_state_reg) + 2'd0: begin + if (s_flag_sync_reg_2) begin + m_state_reg <= 2'd1; + m_axil_araddr_reg <= s_axil_araddr_reg; + m_axil_arprot_reg <= s_axil_arprot_reg; + m_axil_arvalid_reg <= 1'b1; + m_axil_rvalid_reg <= 1'b0; + end + end + 2'd1: begin + if (m_axil_rvalid_reg) begin + m_flag_reg <= 1'b1; + m_state_reg <= 2'd2; + end + end + 2'd2: begin + if (!s_flag_sync_reg_2) begin + m_state_reg <= 2'd0; + m_flag_reg <= 1'b0; + end + end + endcase + + if (m_rst) begin + m_state_reg <= 2'd0; + m_flag_reg <= 1'b0; + m_axil_arvalid_reg <= 1'b0; + m_axil_rvalid_reg <= 1'b1; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axil_cdc_wr.v b/corundum/lib/axi/rtl/axil_cdc_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..4107d6ee2724059288881087b103f036e048492f --- /dev/null +++ b/corundum/lib/axi/rtl/axil_cdc_wr.v @@ -0,0 +1,228 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite clock domain crossing module (write) + */ +module axil_cdc_wr # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8) +) +( + /* + * AXI lite slave interface + */ + input wire s_clk, + input wire s_rst, + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [DATA_WIDTH-1:0] s_axil_wdata, + input wire [STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + + /* + * AXI lite master interface + */ + input wire m_clk, + input wire m_rst, + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [DATA_WIDTH-1:0] m_axil_wdata, + output wire [STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready +); + +reg [1:0] s_state_reg = 2'd0; +reg s_flag_reg = 1'b0; +(* srl_style = "register" *) +reg s_flag_sync_reg_1 = 1'b0; +(* srl_style = "register" *) +reg s_flag_sync_reg_2 = 1'b0; + +reg [1:0] m_state_reg = 2'd0; +reg m_flag_reg = 1'b0; +(* srl_style = "register" *) +reg m_flag_sync_reg_1 = 1'b0; +(* srl_style = "register" *) +reg m_flag_sync_reg_2 = 1'b0; + +reg [ADDR_WIDTH-1:0] s_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] s_axil_awprot_reg = 3'd0; +reg s_axil_awvalid_reg = 1'b0; +reg [DATA_WIDTH-1:0] s_axil_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] s_axil_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg s_axil_wvalid_reg = 1'b0; +reg [1:0] s_axil_bresp_reg = 2'b00; +reg s_axil_bvalid_reg = 1'b0; + +reg [ADDR_WIDTH-1:0] m_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] m_axil_awprot_reg = 3'd0; +reg m_axil_awvalid_reg = 1'b0; +reg [DATA_WIDTH-1:0] m_axil_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axil_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axil_wvalid_reg = 1'b0; +reg [1:0] m_axil_bresp_reg = 2'b00; +reg m_axil_bvalid_reg = 1'b1; + +assign s_axil_awready = !s_axil_awvalid_reg && !s_axil_bvalid_reg; +assign s_axil_wready = !s_axil_wvalid_reg && !s_axil_bvalid_reg; +assign s_axil_bresp = s_axil_bresp_reg; +assign s_axil_bvalid = s_axil_bvalid_reg; + +assign m_axil_awaddr = m_axil_awaddr_reg; +assign m_axil_awprot = m_axil_awprot_reg; +assign m_axil_awvalid = m_axil_awvalid_reg; +assign m_axil_wdata = m_axil_wdata_reg; +assign m_axil_wstrb = m_axil_wstrb_reg; +assign m_axil_wvalid = m_axil_wvalid_reg; +assign m_axil_bready = !m_axil_bvalid_reg; + +// slave side +always @(posedge s_clk) begin + s_axil_bvalid_reg <= s_axil_bvalid_reg && !s_axil_bready; + + if (!s_axil_awvalid_reg && !s_axil_bvalid_reg) begin + s_axil_awaddr_reg <= s_axil_awaddr; + s_axil_awprot_reg <= s_axil_awprot; + s_axil_awvalid_reg <= s_axil_awvalid; + end + + if (!s_axil_wvalid_reg && !s_axil_bvalid_reg) begin + s_axil_wdata_reg <= s_axil_wdata; + s_axil_wstrb_reg <= s_axil_wstrb; + s_axil_wvalid_reg <= s_axil_wvalid; + end + + case (s_state_reg) + 2'd0: begin + if (s_axil_awvalid_reg && s_axil_wvalid_reg) begin + s_state_reg <= 2'd1; + s_flag_reg <= 1'b1; + end + end + 2'd1: begin + if (m_flag_sync_reg_2) begin + s_state_reg <= 2'd2; + s_flag_reg <= 1'b0; + s_axil_bresp_reg <= m_axil_bresp_reg; + s_axil_bvalid_reg <= 1'b1; + end + end + 2'd2: begin + if (!m_flag_sync_reg_2) begin + s_state_reg <= 2'd0; + s_axil_awvalid_reg <= 1'b0; + s_axil_wvalid_reg <= 1'b0; + end + end + endcase + + if (s_rst) begin + s_state_reg <= 2'd0; + s_flag_reg <= 1'b0; + s_axil_awvalid_reg <= 1'b0; + s_axil_wvalid_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + end +end + +// synchronization +always @(posedge s_clk) begin + m_flag_sync_reg_1 <= m_flag_reg; + m_flag_sync_reg_2 <= m_flag_sync_reg_1; +end + +always @(posedge m_clk) begin + s_flag_sync_reg_1 <= s_flag_reg; + s_flag_sync_reg_2 <= s_flag_sync_reg_1; +end + +// master side +always @(posedge m_clk) begin + m_axil_awvalid_reg <= m_axil_awvalid_reg && !m_axil_awready; + m_axil_wvalid_reg <= m_axil_wvalid_reg && !m_axil_wready; + + if (!m_axil_bvalid_reg) begin + m_axil_bresp_reg <= m_axil_bresp; + m_axil_bvalid_reg <= m_axil_bvalid; + end + + case (m_state_reg) + 2'd0: begin + if (s_flag_sync_reg_2) begin + m_state_reg <= 2'd1; + m_axil_awaddr_reg <= s_axil_awaddr_reg; + m_axil_awprot_reg <= s_axil_awprot_reg; + m_axil_awvalid_reg <= 1'b1; + m_axil_wdata_reg <= s_axil_wdata_reg; + m_axil_wstrb_reg <= s_axil_wstrb_reg; + m_axil_wvalid_reg <= 1'b1; + m_axil_bvalid_reg <= 1'b0; + end + end + 2'd1: begin + if (m_axil_bvalid_reg) begin + m_flag_reg <= 1'b1; + m_state_reg <= 2'd2; + end + end + 2'd2: begin + if (!s_flag_sync_reg_2) begin + m_state_reg <= 2'd0; + m_flag_reg <= 1'b0; + end + end + endcase + + if (m_rst) begin + m_state_reg <= 2'd0; + m_flag_reg <= 1'b0; + m_axil_awvalid_reg <= 1'b0; + m_axil_wvalid_reg <= 1'b0; + m_axil_bvalid_reg <= 1'b1; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axil_dp_ram.v b/corundum/lib/axi/rtl/axil_dp_ram.v new file mode 100644 index 0000000000000000000000000000000000000000..e2b39639bd2ba89a389bd00923f6a811990479f3 --- /dev/null +++ b/corundum/lib/axi/rtl/axil_dp_ram.v @@ -0,0 +1,317 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Lite dual port RAM + */ +module axil_dp_ram # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Extra pipeline register on output + parameter PIPELINE_OUTPUT = 0 +) +( + input wire a_clk, + input wire a_rst, + + input wire b_clk, + input wire b_rst, + + input wire [ADDR_WIDTH-1:0] s_axil_a_awaddr, + input wire [2:0] s_axil_a_awprot, + input wire s_axil_a_awvalid, + output wire s_axil_a_awready, + input wire [DATA_WIDTH-1:0] s_axil_a_wdata, + input wire [STRB_WIDTH-1:0] s_axil_a_wstrb, + input wire s_axil_a_wvalid, + output wire s_axil_a_wready, + output wire [1:0] s_axil_a_bresp, + output wire s_axil_a_bvalid, + input wire s_axil_a_bready, + input wire [ADDR_WIDTH-1:0] s_axil_a_araddr, + input wire [2:0] s_axil_a_arprot, + input wire s_axil_a_arvalid, + output wire s_axil_a_arready, + output wire [DATA_WIDTH-1:0] s_axil_a_rdata, + output wire [1:0] s_axil_a_rresp, + output wire s_axil_a_rvalid, + input wire s_axil_a_rready, + + input wire [ADDR_WIDTH-1:0] s_axil_b_awaddr, + input wire [2:0] s_axil_b_awprot, + input wire s_axil_b_awvalid, + output wire s_axil_b_awready, + input wire [DATA_WIDTH-1:0] s_axil_b_wdata, + input wire [STRB_WIDTH-1:0] s_axil_b_wstrb, + input wire s_axil_b_wvalid, + output wire s_axil_b_wready, + output wire [1:0] s_axil_b_bresp, + output wire s_axil_b_bvalid, + input wire s_axil_b_bready, + input wire [ADDR_WIDTH-1:0] s_axil_b_araddr, + input wire [2:0] s_axil_b_arprot, + input wire s_axil_b_arvalid, + output wire s_axil_b_arready, + output wire [DATA_WIDTH-1:0] s_axil_b_rdata, + output wire [1:0] s_axil_b_rresp, + output wire s_axil_b_rvalid, + input wire s_axil_b_rready +); + +parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); +parameter WORD_WIDTH = STRB_WIDTH; +parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH; + +reg read_eligible_a; +reg write_eligible_a; + +reg read_eligible_b; +reg write_eligible_b; + +reg mem_wr_en_a; +reg mem_rd_en_a; + +reg mem_wr_en_b; +reg mem_rd_en_b; + +reg last_read_a_reg = 1'b0, last_read_a_next; +reg last_read_b_reg = 1'b0, last_read_b_next; + +reg s_axil_a_awready_reg = 1'b0, s_axil_a_awready_next; +reg s_axil_a_wready_reg = 1'b0, s_axil_a_wready_next; +reg s_axil_a_bvalid_reg = 1'b0, s_axil_a_bvalid_next; +reg s_axil_a_arready_reg = 1'b0, s_axil_a_arready_next; +reg [DATA_WIDTH-1:0] s_axil_a_rdata_reg = {DATA_WIDTH{1'b0}}, s_axil_a_rdata_next; +reg s_axil_a_rvalid_reg = 1'b0, s_axil_a_rvalid_next; +reg [DATA_WIDTH-1:0] s_axil_a_rdata_pipe_reg = {DATA_WIDTH{1'b0}}; +reg s_axil_a_rvalid_pipe_reg = 1'b0; + +reg s_axil_b_awready_reg = 1'b0, s_axil_b_awready_next; +reg s_axil_b_wready_reg = 1'b0, s_axil_b_wready_next; +reg s_axil_b_bvalid_reg = 1'b0, s_axil_b_bvalid_next; +reg s_axil_b_arready_reg = 1'b0, s_axil_b_arready_next; +reg [DATA_WIDTH-1:0] s_axil_b_rdata_reg = {DATA_WIDTH{1'b0}}, s_axil_b_rdata_next; +reg s_axil_b_rvalid_reg = 1'b0, s_axil_b_rvalid_next; +reg [DATA_WIDTH-1:0] s_axil_b_rdata_pipe_reg = {DATA_WIDTH{1'b0}}; +reg s_axil_b_rvalid_pipe_reg = 1'b0; + +// (* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH-1:0] mem[(2**VALID_ADDR_WIDTH)-1:0]; + +wire [VALID_ADDR_WIDTH-1:0] s_axil_a_awaddr_valid = s_axil_a_awaddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] s_axil_a_araddr_valid = s_axil_a_araddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); + +wire [VALID_ADDR_WIDTH-1:0] s_axil_b_awaddr_valid = s_axil_b_awaddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] s_axil_b_araddr_valid = s_axil_b_araddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); + +assign s_axil_a_awready = s_axil_a_awready_reg; +assign s_axil_a_wready = s_axil_a_wready_reg; +assign s_axil_a_bresp = 2'b00; +assign s_axil_a_bvalid = s_axil_a_bvalid_reg; +assign s_axil_a_arready = s_axil_a_arready_reg; +assign s_axil_a_rdata = PIPELINE_OUTPUT ? s_axil_a_rdata_pipe_reg : s_axil_a_rdata_reg; +assign s_axil_a_rresp = 2'b00; +assign s_axil_a_rvalid = PIPELINE_OUTPUT ? s_axil_a_rvalid_pipe_reg : s_axil_a_rvalid_reg; + +assign s_axil_b_awready = s_axil_b_awready_reg; +assign s_axil_b_wready = s_axil_b_wready_reg; +assign s_axil_b_bresp = 2'b00; +assign s_axil_b_bvalid = s_axil_b_bvalid_reg; +assign s_axil_b_arready = s_axil_b_arready_reg; +assign s_axil_b_rdata = PIPELINE_OUTPUT ? s_axil_b_rdata_pipe_reg : s_axil_b_rdata_reg; +assign s_axil_b_rresp = 2'b00; +assign s_axil_b_rvalid = PIPELINE_OUTPUT ? s_axil_b_rvalid_pipe_reg : s_axil_b_rvalid_reg; + +integer i, j; + +initial begin + // two nested loops for smaller number of iterations per loop + // workaround for synthesizer complaints about large loop counts + for (i = 0; i < 2**ADDR_WIDTH; i = i + 2**(ADDR_WIDTH/2)) begin + for (j = i; j < i + 2**(ADDR_WIDTH/2); j = j + 1) begin + mem[j] = 0; + end + end +end + +always @* begin + mem_wr_en_a = 1'b0; + mem_rd_en_a = 1'b0; + + last_read_a_next = last_read_a_reg; + + s_axil_a_awready_next = 1'b0; + s_axil_a_wready_next = 1'b0; + s_axil_a_bvalid_next = s_axil_a_bvalid_reg && !s_axil_a_bready; + + s_axil_a_arready_next = 1'b0; + s_axil_a_rvalid_next = s_axil_a_rvalid_reg && !(s_axil_a_rready || (PIPELINE_OUTPUT && !s_axil_a_rvalid_pipe_reg)); + + write_eligible_a = s_axil_a_awvalid && s_axil_a_wvalid && (!s_axil_a_bvalid || s_axil_a_bready) && (!s_axil_a_awready && !s_axil_a_wready); + read_eligible_a = s_axil_a_arvalid && (!s_axil_a_rvalid || s_axil_a_rready || (PIPELINE_OUTPUT && !s_axil_a_rvalid_pipe_reg)) && (!s_axil_a_arready); + + if (write_eligible_a && (!read_eligible_a || last_read_a_reg)) begin + last_read_a_next = 1'b0; + + s_axil_a_awready_next = 1'b1; + s_axil_a_wready_next = 1'b1; + s_axil_a_bvalid_next = 1'b1; + + mem_wr_en_a = 1'b1; + end else if (read_eligible_a) begin + last_read_a_next = 1'b1; + + s_axil_a_arready_next = 1'b1; + s_axil_a_rvalid_next = 1'b1; + + mem_rd_en_a = 1'b1; + end +end + +always @(posedge a_clk) begin + if (a_rst) begin + last_read_a_reg <= 1'b0; + + s_axil_a_awready_reg <= 1'b0; + s_axil_a_wready_reg <= 1'b0; + s_axil_a_bvalid_reg <= 1'b0; + + s_axil_a_arready_reg <= 1'b0; + s_axil_a_rvalid_reg <= 1'b0; + s_axil_a_rvalid_pipe_reg <= 1'b0; + end else begin + last_read_a_reg <= last_read_a_next; + + s_axil_a_awready_reg <= s_axil_a_awready_next; + s_axil_a_wready_reg <= s_axil_a_wready_next; + s_axil_a_bvalid_reg <= s_axil_a_bvalid_next; + + s_axil_a_arready_reg <= s_axil_a_arready_next; + s_axil_a_rvalid_reg <= s_axil_a_rvalid_next; + + if (!s_axil_a_rvalid_pipe_reg || s_axil_a_rready) begin + s_axil_a_rvalid_pipe_reg <= s_axil_a_rvalid_reg; + end + end + + if (mem_rd_en_a) begin + s_axil_a_rdata_reg <= mem[s_axil_a_araddr_valid]; + end else begin + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (mem_wr_en_a && s_axil_a_wstrb[i]) begin + mem[s_axil_a_awaddr_valid][WORD_SIZE*i +: WORD_SIZE] <= s_axil_a_wdata[WORD_SIZE*i +: WORD_SIZE]; + end + end + end + + if (!s_axil_a_rvalid_pipe_reg || s_axil_a_rready) begin + s_axil_a_rdata_pipe_reg <= s_axil_a_rdata_reg; + end +end + +always @* begin + mem_wr_en_b = 1'b0; + mem_rd_en_b = 1'b0; + + last_read_b_next = last_read_b_reg; + + s_axil_b_awready_next = 1'b0; + s_axil_b_wready_next = 1'b0; + s_axil_b_bvalid_next = s_axil_b_bvalid_reg && !s_axil_b_bready; + + s_axil_b_arready_next = 1'b0; + s_axil_b_rvalid_next = s_axil_b_rvalid_reg && !(s_axil_b_rready || (PIPELINE_OUTPUT && !s_axil_b_rvalid_pipe_reg)); + + write_eligible_b = s_axil_b_awvalid && s_axil_b_wvalid && (!s_axil_b_bvalid || s_axil_b_bready) && (!s_axil_b_awready && !s_axil_b_wready); + read_eligible_b = s_axil_b_arvalid && (!s_axil_b_rvalid || s_axil_b_rready || (PIPELINE_OUTPUT && !s_axil_b_rvalid_pipe_reg)) && (!s_axil_b_arready); + + if (write_eligible_b && (!read_eligible_b || last_read_b_reg)) begin + last_read_b_next = 1'b0; + + s_axil_b_awready_next = 1'b1; + s_axil_b_wready_next = 1'b1; + s_axil_b_bvalid_next = 1'b1; + + mem_wr_en_b = 1'b1; + end else if (read_eligible_b) begin + last_read_b_next = 1'b1; + + s_axil_b_arready_next = 1'b1; + s_axil_b_rvalid_next = 1'b1; + + mem_rd_en_b = 1'b1; + end +end + +always @(posedge b_clk) begin + if (b_rst) begin + last_read_b_reg <= 1'b0; + + s_axil_b_awready_reg <= 1'b0; + s_axil_b_wready_reg <= 1'b0; + s_axil_b_bvalid_reg <= 1'b0; + + s_axil_b_arready_reg <= 1'b0; + s_axil_b_rvalid_reg <= 1'b0; + s_axil_b_rvalid_pipe_reg <= 1'b0; + end else begin + last_read_b_reg <= last_read_b_next; + + s_axil_b_awready_reg <= s_axil_b_awready_next; + s_axil_b_wready_reg <= s_axil_b_wready_next; + s_axil_b_bvalid_reg <= s_axil_b_bvalid_next; + + s_axil_b_arready_reg <= s_axil_b_arready_next; + s_axil_b_rvalid_reg <= s_axil_b_rvalid_next; + + if (!s_axil_b_rvalid_pipe_reg || s_axil_b_rready) begin + s_axil_b_rvalid_pipe_reg <= s_axil_b_rvalid_reg; + end + end + + if (mem_rd_en_b) begin + s_axil_b_rdata_reg <= mem[s_axil_b_araddr_valid]; + end else begin + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (mem_wr_en_b && s_axil_b_wstrb[i]) begin + mem[s_axil_b_awaddr_valid][WORD_SIZE*i +: WORD_SIZE] <= s_axil_b_wdata[WORD_SIZE*i +: WORD_SIZE]; + end + end + end + + if (!s_axil_b_rvalid_pipe_reg || s_axil_b_rready) begin + s_axil_b_rdata_pipe_reg <= s_axil_b_rdata_reg; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axil_interconnect.v b/corundum/lib/axi/rtl/axil_interconnect.v new file mode 100644 index 0000000000000000000000000000000000000000..62108aac336a7bee2132c31d86a89abdc9616d77 --- /dev/null +++ b/corundum/lib/axi/rtl/axil_interconnect.v @@ -0,0 +1,517 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite interconnect + */ +module axil_interconnect # +( + // Number of AXI inputs (slave interfaces) + parameter S_COUNT = 4, + // Number of AXI outputs (master interfaces) + parameter M_COUNT = 4, + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Number of regions per master interface + parameter M_REGIONS = 1, + // Master interface base addresses + // M_COUNT concatenated fields of M_REGIONS concatenated fields of ADDR_WIDTH bits + // set to zero for default addressing based on M_ADDR_WIDTH + parameter M_BASE_ADDR = 0, + // Master interface address widths + // M_COUNT concatenated fields of M_REGIONS concatenated fields of 32 bits + parameter M_ADDR_WIDTH = {M_COUNT{{M_REGIONS{32'd24}}}}, + // Read connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_READ = {M_COUNT{{S_COUNT{1'b1}}}}, + // Write connections between interfaces + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT_WRITE = {M_COUNT{{S_COUNT{1'b1}}}}, + // Secure master (fail operations based on awprot/arprot) + // M_COUNT bits + parameter M_SECURE = {M_COUNT{1'b0}} +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interfaces + */ + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [S_COUNT*3-1:0] s_axil_awprot, + input wire [S_COUNT-1:0] s_axil_awvalid, + output wire [S_COUNT-1:0] s_axil_awready, + input wire [S_COUNT*DATA_WIDTH-1:0] s_axil_wdata, + input wire [S_COUNT*STRB_WIDTH-1:0] s_axil_wstrb, + input wire [S_COUNT-1:0] s_axil_wvalid, + output wire [S_COUNT-1:0] s_axil_wready, + output wire [S_COUNT*2-1:0] s_axil_bresp, + output wire [S_COUNT-1:0] s_axil_bvalid, + input wire [S_COUNT-1:0] s_axil_bready, + input wire [S_COUNT*ADDR_WIDTH-1:0] s_axil_araddr, + input wire [S_COUNT*3-1:0] s_axil_arprot, + input wire [S_COUNT-1:0] s_axil_arvalid, + output wire [S_COUNT-1:0] s_axil_arready, + output wire [S_COUNT*DATA_WIDTH-1:0] s_axil_rdata, + output wire [S_COUNT*2-1:0] s_axil_rresp, + output wire [S_COUNT-1:0] s_axil_rvalid, + input wire [S_COUNT-1:0] s_axil_rready, + + /* + * AXI lite master interfaces + */ + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [M_COUNT*3-1:0] m_axil_awprot, + output wire [M_COUNT-1:0] m_axil_awvalid, + input wire [M_COUNT-1:0] m_axil_awready, + output wire [M_COUNT*DATA_WIDTH-1:0] m_axil_wdata, + output wire [M_COUNT*STRB_WIDTH-1:0] m_axil_wstrb, + output wire [M_COUNT-1:0] m_axil_wvalid, + input wire [M_COUNT-1:0] m_axil_wready, + input wire [M_COUNT*2-1:0] m_axil_bresp, + input wire [M_COUNT-1:0] m_axil_bvalid, + output wire [M_COUNT-1:0] m_axil_bready, + output wire [M_COUNT*ADDR_WIDTH-1:0] m_axil_araddr, + output wire [M_COUNT*3-1:0] m_axil_arprot, + output wire [M_COUNT-1:0] m_axil_arvalid, + input wire [M_COUNT-1:0] m_axil_arready, + input wire [M_COUNT*DATA_WIDTH-1:0] m_axil_rdata, + input wire [M_COUNT*2-1:0] m_axil_rresp, + input wire [M_COUNT-1:0] m_axil_rvalid, + output wire [M_COUNT-1:0] m_axil_rready +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +// default address computation +function [M_COUNT*M_REGIONS*ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] dummy); + integer i; + reg [ADDR_WIDTH-1:0] base; + begin + calcBaseAddrs = {M_COUNT*M_REGIONS*ADDR_WIDTH{1'b0}}; + base = 0; + for (i = 1; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + base = base + 2**M_ADDR_WIDTH[(i-1)*32 +: 32]; // increment + base = base - (base % 2**M_ADDR_WIDTH[i*32 +: 32]); // align + calcBaseAddrs[i * ADDR_WIDTH +: ADDR_WIDTH] = base; + end + end + end +endfunction + +parameter M_BASE_ADDR_INT = M_BASE_ADDR ? M_BASE_ADDR : calcBaseAddrs(0); + +integer i, j; + +// check configuration +initial begin + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && (M_ADDR_WIDTH[i*32 +: 32] < 0 || M_ADDR_WIDTH[i*32 +: 32] > ADDR_WIDTH)) begin + $error("Error: address width out of range (instance %m)"); + $finish; + end + end + + $display("Addressing configuration for axil_interconnect instance %m"); + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32]) begin + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + end + end + + for (i = 0; i < M_COUNT*M_REGIONS; i = i + 1) begin + for (j = i+1; j < M_COUNT*M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[i*32 +: 32] && M_ADDR_WIDTH[j*32 +: 32]) begin + if (((M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32])) <= (M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32])))) && ((M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32])) <= (M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))))) begin + $display("Overlapping regions:"); + $display("%2d (%2d): %x / %2d -- %x-%x", i/M_REGIONS, i%M_REGIONS, M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[i*32 +: 32], M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[i*32 +: 32]), M_BASE_ADDR_INT[i*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[i*32 +: 32]))); + $display("%2d (%2d): %x / %2d -- %x-%x", j/M_REGIONS, j%M_REGIONS, M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH], M_ADDR_WIDTH[j*32 +: 32], M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] & ({ADDR_WIDTH{1'b1}} << M_ADDR_WIDTH[j*32 +: 32]), M_BASE_ADDR_INT[j*ADDR_WIDTH +: ADDR_WIDTH] | ({ADDR_WIDTH{1'b1}} >> (ADDR_WIDTH - M_ADDR_WIDTH[j*32 +: 32]))); + $error("Error: address ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_DECODE = 3'd1, + STATE_WRITE = 3'd2, + STATE_WRITE_RESP = 3'd3, + STATE_WRITE_DROP = 3'd4, + STATE_READ = 3'd5, + STATE_WAIT_IDLE = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg match; + +reg [CL_M_COUNT-1:0] m_select_reg = 2'd0, m_select_next; +reg [ADDR_WIDTH-1:0] axil_addr_reg = {ADDR_WIDTH{1'b0}}, axil_addr_next; +reg axil_addr_valid_reg = 1'b0, axil_addr_valid_next; +reg [2:0] axil_prot_reg = 3'b000, axil_prot_next; +reg [DATA_WIDTH-1:0] axil_data_reg = {DATA_WIDTH{1'b0}}, axil_data_next; +reg [STRB_WIDTH-1:0] axil_wstrb_reg = {STRB_WIDTH{1'b0}}, axil_wstrb_next; +reg [1:0] axil_resp_reg = 2'b00, axil_resp_next; + +reg [S_COUNT-1:0] s_axil_awready_reg = 0, s_axil_awready_next; +reg [S_COUNT-1:0] s_axil_wready_reg = 0, s_axil_wready_next; +reg [S_COUNT-1:0] s_axil_bvalid_reg = 0, s_axil_bvalid_next; +reg [S_COUNT-1:0] s_axil_arready_reg = 0, s_axil_arready_next; +reg [S_COUNT-1:0] s_axil_rvalid_reg = 0, s_axil_rvalid_next; + +reg [M_COUNT-1:0] m_axil_awvalid_reg = 0, m_axil_awvalid_next; +reg [M_COUNT-1:0] m_axil_wvalid_reg = 0, m_axil_wvalid_next; +reg [M_COUNT-1:0] m_axil_bready_reg = 0, m_axil_bready_next; +reg [M_COUNT-1:0] m_axil_arvalid_reg = 0, m_axil_arvalid_next; +reg [M_COUNT-1:0] m_axil_rready_reg = 0, m_axil_rready_next; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = {S_COUNT{axil_resp_reg}}; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = {S_COUNT{axil_data_reg}}; +assign s_axil_rresp = {S_COUNT{axil_resp_reg}}; +assign s_axil_rvalid = s_axil_rvalid_reg; + +assign m_axil_awaddr = {M_COUNT{axil_addr_reg}}; +assign m_axil_awprot = {M_COUNT{axil_prot_reg}}; +assign m_axil_awvalid = m_axil_awvalid_reg; +assign m_axil_wdata = {M_COUNT{axil_data_reg}}; +assign m_axil_wstrb = {M_COUNT{axil_wstrb_reg}}; +assign m_axil_wvalid = m_axil_wvalid_reg; +assign m_axil_bready = m_axil_bready_reg; +assign m_axil_araddr = {M_COUNT{axil_addr_reg}}; +assign m_axil_arprot = {M_COUNT{axil_prot_reg}}; +assign m_axil_arvalid = m_axil_arvalid_reg; +assign m_axil_rready = m_axil_rready_reg; + +// slave side mux +wire [(CL_S_COUNT > 0 ? CL_S_COUNT-1 : 0):0] s_select; + +wire [ADDR_WIDTH-1:0] current_s_axil_awaddr = s_axil_awaddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [2:0] current_s_axil_awprot = s_axil_awprot[s_select*3 +: 3]; +wire current_s_axil_awvalid = s_axil_awvalid[s_select]; +wire current_s_axil_awready = s_axil_awready[s_select]; +wire [DATA_WIDTH-1:0] current_s_axil_wdata = s_axil_wdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_s_axil_wstrb = s_axil_wstrb[s_select*STRB_WIDTH +: STRB_WIDTH]; +wire current_s_axil_wvalid = s_axil_wvalid[s_select]; +wire current_s_axil_wready = s_axil_wready[s_select]; +wire [1:0] current_s_axil_bresp = s_axil_bresp[s_select*2 +: 2]; +wire current_s_axil_bvalid = s_axil_bvalid[s_select]; +wire current_s_axil_bready = s_axil_bready[s_select]; +wire [ADDR_WIDTH-1:0] current_s_axil_araddr = s_axil_araddr[s_select*ADDR_WIDTH +: ADDR_WIDTH]; +wire [2:0] current_s_axil_arprot = s_axil_arprot[s_select*3 +: 3]; +wire current_s_axil_arvalid = s_axil_arvalid[s_select]; +wire current_s_axil_arready = s_axil_arready[s_select]; +wire [DATA_WIDTH-1:0] current_s_axil_rdata = s_axil_rdata[s_select*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_s_axil_rresp = s_axil_rresp[s_select*2 +: 2]; +wire current_s_axil_rvalid = s_axil_rvalid[s_select]; +wire current_s_axil_rready = s_axil_rready[s_select]; + +// master side mux +wire [ADDR_WIDTH-1:0] current_m_axil_awaddr = m_axil_awaddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [2:0] current_m_axil_awprot = m_axil_awprot[m_select_reg*3 +: 3]; +wire current_m_axil_awvalid = m_axil_awvalid[m_select_reg]; +wire current_m_axil_awready = m_axil_awready[m_select_reg]; +wire [DATA_WIDTH-1:0] current_m_axil_wdata = m_axil_wdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [STRB_WIDTH-1:0] current_m_axil_wstrb = m_axil_wstrb[m_select_reg*STRB_WIDTH +: STRB_WIDTH]; +wire current_m_axil_wvalid = m_axil_wvalid[m_select_reg]; +wire current_m_axil_wready = m_axil_wready[m_select_reg]; +wire [1:0] current_m_axil_bresp = m_axil_bresp[m_select_reg*2 +: 2]; +wire current_m_axil_bvalid = m_axil_bvalid[m_select_reg]; +wire current_m_axil_bready = m_axil_bready[m_select_reg]; +wire [ADDR_WIDTH-1:0] current_m_axil_araddr = m_axil_araddr[m_select_reg*ADDR_WIDTH +: ADDR_WIDTH]; +wire [2:0] current_m_axil_arprot = m_axil_arprot[m_select_reg*3 +: 3]; +wire current_m_axil_arvalid = m_axil_arvalid[m_select_reg]; +wire current_m_axil_arready = m_axil_arready[m_select_reg]; +wire [DATA_WIDTH-1:0] current_m_axil_rdata = m_axil_rdata[m_select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [1:0] current_m_axil_rresp = m_axil_rresp[m_select_reg*2 +: 2]; +wire current_m_axil_rvalid = m_axil_rvalid[m_select_reg]; +wire current_m_axil_rready = m_axil_rready[m_select_reg]; + +// arbiter instance +wire [S_COUNT*2-1:0] request; +wire [S_COUNT*2-1:0] acknowledge; +wire [S_COUNT*2-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT:0] grant_encoded; + +wire read = grant_encoded[0]; +assign s_select = grant_encoded >> 1; + +arbiter #( + .PORTS(S_COUNT*2), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +genvar n; + +// request generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign request[2*n] = s_axil_awvalid[n]; + assign request[2*n+1] = s_axil_arvalid[n]; +end +endgenerate + +// acknowledge generation +generate +for (n = 0; n < S_COUNT; n = n + 1) begin + assign acknowledge[2*n] = grant[2*n] && s_axil_bvalid[n] && s_axil_bready[n]; + assign acknowledge[2*n+1] = grant[2*n+1] && s_axil_rvalid[n] && s_axil_rready[n]; +end +endgenerate + +always @* begin + state_next = STATE_IDLE; + + match = 1'b0; + + m_select_next = m_select_reg; + axil_addr_next = axil_addr_reg; + axil_addr_valid_next = axil_addr_valid_reg; + axil_prot_next = axil_prot_reg; + axil_data_next = axil_data_reg; + axil_wstrb_next = axil_wstrb_reg; + axil_resp_next = axil_resp_reg; + + s_axil_awready_next = 0; + s_axil_wready_next = 0; + s_axil_bvalid_next = s_axil_bvalid_reg & ~s_axil_bready; + s_axil_arready_next = 0; + s_axil_rvalid_next = s_axil_rvalid_reg & ~s_axil_rready; + + m_axil_awvalid_next = m_axil_awvalid_reg & ~m_axil_awready; + m_axil_wvalid_next = m_axil_wvalid_reg & ~m_axil_wready; + m_axil_bready_next = 0; + m_axil_arvalid_next = m_axil_arvalid_reg & ~m_axil_arready; + m_axil_rready_next = 0; + + case (state_reg) + STATE_IDLE: begin + // idle state; wait for arbitration + + if (grant_valid) begin + + axil_addr_valid_next = 1'b1; + + if (read) begin + // reading + axil_addr_next = current_s_axil_araddr; + axil_prot_next = current_s_axil_arprot; + s_axil_arready_next[s_select] = 1'b1; + end else begin + // writing + axil_addr_next = current_s_axil_awaddr; + axil_prot_next = current_s_axil_awprot; + s_axil_awready_next[s_select] = 1'b1; + end + + state_next = STATE_DECODE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_DECODE: begin + // decode state; determine master interface + + match = 1'b0; + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = 0; j < M_REGIONS; j = j + 1) begin + if (M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32] && (!M_SECURE[i] || !axil_prot_reg[1]) && ((read ? M_CONNECT_READ : M_CONNECT_WRITE) & (1 << (s_select+i*S_COUNT))) && (axil_addr_reg >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32]) == (M_BASE_ADDR_INT[(i*M_REGIONS+j)*ADDR_WIDTH +: ADDR_WIDTH] >> M_ADDR_WIDTH[(i*M_REGIONS+j)*32 +: 32])) begin + m_select_next = i; + match = 1'b1; + end + end + end + + if (match) begin + if (read) begin + // reading + m_axil_rready_next[m_select_next] = 1'b1; + state_next = STATE_READ; + end else begin + // writing + s_axil_wready_next[s_select] = 1'b1; + state_next = STATE_WRITE; + end + end else begin + // no match; return decode error + axil_data_next = {DATA_WIDTH{1'b0}}; + axil_resp_next = 2'b11; + if (read) begin + // reading + s_axil_rvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + // writing + s_axil_wready_next[s_select] = 1'b1; + state_next = STATE_WRITE_DROP; + end + end + end + STATE_WRITE: begin + // write state; store and forward write data + s_axil_wready_next[s_select] = 1'b1; + + if (axil_addr_valid_reg) begin + m_axil_awvalid_next[m_select_reg] = 1'b1; + end + axil_addr_valid_next = 1'b0; + + if (current_s_axil_wready && current_s_axil_wvalid) begin + s_axil_wready_next[s_select] = 1'b0; + axil_data_next = current_s_axil_wdata; + axil_wstrb_next = current_s_axil_wstrb; + m_axil_wvalid_next[m_select_reg] = 1'b1; + m_axil_bready_next[m_select_reg] = 1'b1; + state_next = STATE_WRITE_RESP; + end else begin + state_next = STATE_WRITE; + end + end + STATE_WRITE_RESP: begin + // write response state; store and forward write response + m_axil_bready_next[m_select_reg] = 1'b1; + + if (current_m_axil_bready && current_m_axil_bvalid) begin + m_axil_bready_next[m_select_reg] = 1'b0; + axil_resp_next = current_m_axil_bresp; + s_axil_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_RESP; + end + end + STATE_WRITE_DROP: begin + // write drop state; drop write data + s_axil_wready_next[s_select] = 1'b1; + + axil_addr_valid_next = 1'b0; + + if (current_s_axil_wready && current_s_axil_wvalid) begin + s_axil_wready_next[s_select] = 1'b0; + s_axil_bvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_WRITE_DROP; + end + end + STATE_READ: begin + // read state; store and forward read response + m_axil_rready_next[m_select_reg] = 1'b1; + + if (axil_addr_valid_reg) begin + m_axil_arvalid_next[m_select_reg] = 1'b1; + end + axil_addr_valid_next = 1'b0; + + if (current_m_axil_rready && current_m_axil_rvalid) begin + m_axil_rready_next[m_select_reg] = 1'b0; + axil_data_next = current_m_axil_rdata; + axil_resp_next = current_m_axil_rresp; + s_axil_rvalid_next[s_select] = 1'b1; + state_next = STATE_WAIT_IDLE; + end else begin + state_next = STATE_READ; + end + end + STATE_WAIT_IDLE: begin + // wait for idle state; wait untl grant valid is deasserted + + if (!grant_valid || acknowledge) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_IDLE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axil_awready_reg <= 0; + s_axil_wready_reg <= 0; + s_axil_bvalid_reg <= 0; + s_axil_arready_reg <= 0; + s_axil_rvalid_reg <= 0; + + m_axil_awvalid_reg <= 0; + m_axil_wvalid_reg <= 0; + m_axil_bready_reg <= 0; + m_axil_arvalid_reg <= 0; + m_axil_rready_reg <= 0; + end else begin + state_reg <= state_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + m_axil_awvalid_reg <= m_axil_awvalid_next; + m_axil_wvalid_reg <= m_axil_wvalid_next; + m_axil_bready_reg <= m_axil_bready_next; + m_axil_arvalid_reg <= m_axil_arvalid_next; + m_axil_rready_reg <= m_axil_rready_next; + end + + m_select_reg <= m_select_next; + axil_addr_reg <= axil_addr_next; + axil_addr_valid_reg <= axil_addr_valid_next; + axil_prot_reg <= axil_prot_next; + axil_data_reg <= axil_data_next; + axil_wstrb_reg <= axil_wstrb_next; + axil_resp_reg <= axil_resp_next; +end + +endmodule diff --git a/corundum/lib/axi/rtl/axil_ram.v b/corundum/lib/axi/rtl/axil_ram.v new file mode 100644 index 0000000000000000000000000000000000000000..049db0eeabc8eaf30aa29f7175ab05aadce923cf --- /dev/null +++ b/corundum/lib/axi/rtl/axil_ram.v @@ -0,0 +1,182 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Lite RAM + */ +module axil_ram # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 16, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // Extra pipeline register on output + parameter PIPELINE_OUTPUT = 0 +) +( + input wire clk, + input wire rst, + + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [DATA_WIDTH-1:0] s_axil_wdata, + input wire [STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready +); + +parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2(STRB_WIDTH); +parameter WORD_WIDTH = STRB_WIDTH; +parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH; + +reg mem_wr_en; +reg mem_rd_en; + +reg s_axil_awready_reg = 1'b0, s_axil_awready_next; +reg s_axil_wready_reg = 1'b0, s_axil_wready_next; +reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next; +reg s_axil_arready_reg = 1'b0, s_axil_arready_next; +reg [DATA_WIDTH-1:0] s_axil_rdata_reg = {DATA_WIDTH{1'b0}}, s_axil_rdata_next; +reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next; +reg [DATA_WIDTH-1:0] s_axil_rdata_pipe_reg = {DATA_WIDTH{1'b0}}; +reg s_axil_rvalid_pipe_reg = 1'b0; + +// (* RAM_STYLE="BLOCK" *) +reg [DATA_WIDTH-1:0] mem[(2**VALID_ADDR_WIDTH)-1:0]; + +wire [VALID_ADDR_WIDTH-1:0] s_axil_awaddr_valid = s_axil_awaddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); +wire [VALID_ADDR_WIDTH-1:0] s_axil_araddr_valid = s_axil_araddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH); + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = 2'b00; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = PIPELINE_OUTPUT ? s_axil_rdata_pipe_reg : s_axil_rdata_reg; +assign s_axil_rresp = 2'b00; +assign s_axil_rvalid = PIPELINE_OUTPUT ? s_axil_rvalid_pipe_reg : s_axil_rvalid_reg; + +integer i, j; + +initial begin + // two nested loops for smaller number of iterations per loop + // workaround for synthesizer complaints about large loop counts + for (i = 0; i < 2**ADDR_WIDTH; i = i + 2**(ADDR_WIDTH/2)) begin + for (j = i; j < i + 2**(ADDR_WIDTH/2); j = j + 1) begin + mem[j] = 0; + end + end +end + +always @* begin + mem_wr_en = 1'b0; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + if (s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && (!s_axil_awready && !s_axil_wready)) begin + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + s_axil_bvalid_next = 1'b1; + + mem_wr_en = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + end else begin + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + end + + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (mem_wr_en && s_axil_wstrb[i]) begin + mem[s_axil_awaddr_valid][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE]; + end + end +end + +always @* begin + mem_rd_en = 1'b0; + + s_axil_arready_next = 1'b0; + s_axil_rvalid_next = s_axil_rvalid_reg && !(s_axil_rready || (PIPELINE_OUTPUT && !s_axil_rvalid_pipe_reg)); + + if (s_axil_arvalid && (!s_axil_rvalid || s_axil_rready || (PIPELINE_OUTPUT && !s_axil_rvalid_pipe_reg)) && (!s_axil_arready)) begin + s_axil_arready_next = 1'b1; + s_axil_rvalid_next = 1'b1; + + mem_rd_en = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + s_axil_rvalid_pipe_reg <= 1'b0; + end else begin + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + if (!s_axil_rvalid_pipe_reg || s_axil_rready) begin + s_axil_rvalid_pipe_reg <= s_axil_rvalid_reg; + end + end + + if (mem_rd_en) begin + s_axil_rdata_reg <= mem[s_axil_araddr_valid]; + end + + if (!s_axil_rvalid_pipe_reg || s_axil_rready) begin + s_axil_rdata_pipe_reg <= s_axil_rdata_reg; + end +end + +endmodule diff --git a/corundum/lib/axi/rtl/axil_register.v b/corundum/lib/axi/rtl/axil_register.v new file mode 100644 index 0000000000000000000000000000000000000000..f94e737e3a5b1b5d2e245499250de0e44c2d274d --- /dev/null +++ b/corundum/lib/axi/rtl/axil_register.v @@ -0,0 +1,186 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite register + */ +module axil_register # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // AW channel register type + // 0 to bypass, 1 for simple buffer + parameter AW_REG_TYPE = 1, + // W channel register type + // 0 to bypass, 1 for simple buffer + parameter W_REG_TYPE = 1, + // B channel register type + // 0 to bypass, 1 for simple buffer + parameter B_REG_TYPE = 1, + // AR channel register type + // 0 to bypass, 1 for simple buffer + parameter AR_REG_TYPE = 1, + // R channel register type + // 0 to bypass, 1 for simple buffer + parameter R_REG_TYPE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interface + */ + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [DATA_WIDTH-1:0] s_axil_wdata, + input wire [STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [DATA_WIDTH-1:0] m_axil_wdata, + output wire [STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready, + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + +axil_register_wr #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .AW_REG_TYPE(AW_REG_TYPE), + .W_REG_TYPE(W_REG_TYPE), + .B_REG_TYPE(B_REG_TYPE) +) +axil_register_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI lite slave interface + */ + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + + /* + * AXI lite master interface + */ + .m_axil_awaddr(m_axil_awaddr), + .m_axil_awprot(m_axil_awprot), + .m_axil_awvalid(m_axil_awvalid), + .m_axil_awready(m_axil_awready), + .m_axil_wdata(m_axil_wdata), + .m_axil_wstrb(m_axil_wstrb), + .m_axil_wvalid(m_axil_wvalid), + .m_axil_wready(m_axil_wready), + .m_axil_bresp(m_axil_bresp), + .m_axil_bvalid(m_axil_bvalid), + .m_axil_bready(m_axil_bready) +); + +axil_register_rd #( + .DATA_WIDTH(DATA_WIDTH), + .ADDR_WIDTH(ADDR_WIDTH), + .STRB_WIDTH(STRB_WIDTH), + .AR_REG_TYPE(AR_REG_TYPE), + .R_REG_TYPE(R_REG_TYPE) +) +axil_register_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI lite slave interface + */ + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + + /* + * AXI lite master interface + */ + .m_axil_araddr(m_axil_araddr), + .m_axil_arprot(m_axil_arprot), + .m_axil_arvalid(m_axil_arvalid), + .m_axil_arready(m_axil_arready), + .m_axil_rdata(m_axil_rdata), + .m_axil_rresp(m_axil_rresp), + .m_axil_rvalid(m_axil_rvalid), + .m_axil_rready(m_axil_rready) +); + +endmodule diff --git a/corundum/lib/axi/rtl/axil_register_rd.v b/corundum/lib/axi/rtl/axil_register_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..8f0bc088d847f70573cc260b6cf85d9d5fba867e --- /dev/null +++ b/corundum/lib/axi/rtl/axil_register_rd.v @@ -0,0 +1,372 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite register (read) + */ +module axil_register_rd # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // AR channel register type + // 0 to bypass, 1 for simple buffer + parameter AR_REG_TYPE = 1, + // R channel register type + // 0 to bypass, 1 for simple buffer + parameter R_REG_TYPE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interface + */ + input wire [ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready +); + +generate + +// AR channel + +if (AR_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axil_arready_reg = 1'b0; + +reg [ADDR_WIDTH-1:0] m_axil_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] m_axil_arprot_reg = 3'd0; +reg m_axil_arvalid_reg = 1'b0, m_axil_arvalid_next; + +reg [ADDR_WIDTH-1:0] temp_m_axil_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] temp_m_axil_arprot_reg = 3'd0; +reg temp_m_axil_arvalid_reg = 1'b0, temp_m_axil_arvalid_next; + +// datapath control +reg store_axil_ar_input_to_output; +reg store_axil_ar_input_to_temp; +reg store_axil_ar_temp_to_output; + +assign s_axil_arready = s_axil_arready_reg; + +assign m_axil_araddr = m_axil_araddr_reg; +assign m_axil_arprot = m_axil_arprot_reg; +assign m_axil_arvalid = m_axil_arvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axil_arready_early = m_axil_arready | (~temp_m_axil_arvalid_reg & (~m_axil_arvalid_reg | ~s_axil_arvalid)); + +always @* begin + // transfer sink ready state to source + m_axil_arvalid_next = m_axil_arvalid_reg; + temp_m_axil_arvalid_next = temp_m_axil_arvalid_reg; + + store_axil_ar_input_to_output = 1'b0; + store_axil_ar_input_to_temp = 1'b0; + store_axil_ar_temp_to_output = 1'b0; + + if (s_axil_arready_reg) begin + // input is ready + if (m_axil_arready | ~m_axil_arvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axil_arvalid_next = s_axil_arvalid; + store_axil_ar_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axil_arvalid_next = s_axil_arvalid; + store_axil_ar_input_to_temp = 1'b1; + end + end else if (m_axil_arready) begin + // input is not ready, but output is ready + m_axil_arvalid_next = temp_m_axil_arvalid_reg; + temp_m_axil_arvalid_next = 1'b0; + store_axil_ar_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_arready_reg <= 1'b0; + m_axil_arvalid_reg <= 1'b0; + temp_m_axil_arvalid_reg <= 1'b0; + end else begin + s_axil_arready_reg <= s_axil_arready_early; + m_axil_arvalid_reg <= m_axil_arvalid_next; + temp_m_axil_arvalid_reg <= temp_m_axil_arvalid_next; + end + + // datapath + if (store_axil_ar_input_to_output) begin + m_axil_araddr_reg <= s_axil_araddr; + m_axil_arprot_reg <= s_axil_arprot; + end else if (store_axil_ar_temp_to_output) begin + m_axil_araddr_reg <= temp_m_axil_araddr_reg; + m_axil_arprot_reg <= temp_m_axil_arprot_reg; + end + + if (store_axil_ar_input_to_temp) begin + temp_m_axil_araddr_reg <= s_axil_araddr; + temp_m_axil_arprot_reg <= s_axil_arprot; + end +end + +end else if (AR_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axil_arready_reg = 1'b0; + +reg [ADDR_WIDTH-1:0] m_axil_araddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] m_axil_arprot_reg = 3'd0; +reg m_axil_arvalid_reg = 1'b0, m_axil_arvalid_next; + +// datapath control +reg store_axil_ar_input_to_output; + +assign s_axil_arready = s_axil_arready_reg; + +assign m_axil_araddr = m_axil_araddr_reg; +assign m_axil_arprot = m_axil_arprot_reg; +assign m_axil_arvalid = m_axil_arvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axil_arready_early = !m_axil_arvalid_next; + +always @* begin + // transfer sink ready state to source + m_axil_arvalid_next = m_axil_arvalid_reg; + + store_axil_ar_input_to_output = 1'b0; + + if (s_axil_arready_reg) begin + m_axil_arvalid_next = s_axil_arvalid; + store_axil_ar_input_to_output = 1'b1; + end else if (m_axil_arready) begin + m_axil_arvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_arready_reg <= 1'b0; + m_axil_arvalid_reg <= 1'b0; + end else begin + s_axil_arready_reg <= s_axil_arready_early; + m_axil_arvalid_reg <= m_axil_arvalid_next; + end + + // datapath + if (store_axil_ar_input_to_output) begin + m_axil_araddr_reg <= s_axil_araddr; + m_axil_arprot_reg <= s_axil_arprot; + end +end + +end else begin + + // bypass AR channel + assign m_axil_araddr = s_axil_araddr; + assign m_axil_arprot = s_axil_arprot; + assign m_axil_arvalid = s_axil_arvalid; + assign s_axil_arready = m_axil_arready; + +end + +// R channel + +if (R_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axil_rready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] s_axil_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axil_rresp_reg = 2'b0; +reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next; + +reg [DATA_WIDTH-1:0] temp_s_axil_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] temp_s_axil_rresp_reg = 2'b0; +reg temp_s_axil_rvalid_reg = 1'b0, temp_s_axil_rvalid_next; + +// datapath control +reg store_axil_r_input_to_output; +reg store_axil_r_input_to_temp; +reg store_axil_r_temp_to_output; + +assign m_axil_rready = m_axil_rready_reg; + +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = s_axil_rresp_reg; +assign s_axil_rvalid = s_axil_rvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axil_rready_early = s_axil_rready | (~temp_s_axil_rvalid_reg & (~s_axil_rvalid_reg | ~m_axil_rvalid)); + +always @* begin + // transfer sink ready state to source + s_axil_rvalid_next = s_axil_rvalid_reg; + temp_s_axil_rvalid_next = temp_s_axil_rvalid_reg; + + store_axil_r_input_to_output = 1'b0; + store_axil_r_input_to_temp = 1'b0; + store_axil_r_temp_to_output = 1'b0; + + if (m_axil_rready_reg) begin + // input is ready + if (s_axil_rready | ~s_axil_rvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axil_rvalid_next = m_axil_rvalid; + store_axil_r_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axil_rvalid_next = m_axil_rvalid; + store_axil_r_input_to_temp = 1'b1; + end + end else if (s_axil_rready) begin + // input is not ready, but output is ready + s_axil_rvalid_next = temp_s_axil_rvalid_reg; + temp_s_axil_rvalid_next = 1'b0; + store_axil_r_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axil_rready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + temp_s_axil_rvalid_reg <= 1'b0; + end else begin + m_axil_rready_reg <= m_axil_rready_early; + s_axil_rvalid_reg <= s_axil_rvalid_next; + temp_s_axil_rvalid_reg <= temp_s_axil_rvalid_next; + end + + // datapath + if (store_axil_r_input_to_output) begin + s_axil_rdata_reg <= m_axil_rdata; + s_axil_rresp_reg <= m_axil_rresp; + end else if (store_axil_r_temp_to_output) begin + s_axil_rdata_reg <= temp_s_axil_rdata_reg; + s_axil_rresp_reg <= temp_s_axil_rresp_reg; + end + + if (store_axil_r_input_to_temp) begin + temp_s_axil_rdata_reg <= m_axil_rdata; + temp_s_axil_rresp_reg <= m_axil_rresp; + end +end + +end else if (R_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axil_rready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] s_axil_rdata_reg = {DATA_WIDTH{1'b0}}; +reg [1:0] s_axil_rresp_reg = 2'b0; +reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next; + +// datapath control +reg store_axil_r_input_to_output; + +assign m_axil_rready = m_axil_rready_reg; + +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = s_axil_rresp_reg; +assign s_axil_rvalid = s_axil_rvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axil_rready_early = !s_axil_rvalid_next; + +always @* begin + // transfer sink ready state to source + s_axil_rvalid_next = s_axil_rvalid_reg; + + store_axil_r_input_to_output = 1'b0; + + if (m_axil_rready_reg) begin + s_axil_rvalid_next = m_axil_rvalid; + store_axil_r_input_to_output = 1'b1; + end else if (s_axil_rready) begin + s_axil_rvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axil_rready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + end else begin + m_axil_rready_reg <= m_axil_rready_early; + s_axil_rvalid_reg <= s_axil_rvalid_next; + end + + // datapath + if (store_axil_r_input_to_output) begin + s_axil_rdata_reg <= m_axil_rdata; + s_axil_rresp_reg <= m_axil_rresp; + end +end + +end else begin + + // bypass R channel + assign s_axil_rdata = m_axil_rdata; + assign s_axil_rresp = m_axil_rresp; + assign s_axil_rvalid = m_axil_rvalid; + assign m_axil_rready = s_axil_rready; + +end + +endgenerate + +endmodule diff --git a/corundum/lib/axi/rtl/axil_register_wr.v b/corundum/lib/axi/rtl/axil_register_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..4000d5d1594c6bc02af12931d2df8df6a3969638 --- /dev/null +++ b/corundum/lib/axi/rtl/axil_register_wr.v @@ -0,0 +1,517 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4 lite register (write) + */ +module axil_register_wr # +( + // Width of data bus in bits + parameter DATA_WIDTH = 32, + // Width of address bus in bits + parameter ADDR_WIDTH = 32, + // Width of wstrb (width of data bus in words) + parameter STRB_WIDTH = (DATA_WIDTH/8), + // AW channel register type + // 0 to bypass, 1 for simple buffer + parameter AW_REG_TYPE = 1, + // W channel register type + // 0 to bypass, 1 for simple buffer + parameter W_REG_TYPE = 1, + // B channel register type + // 0 to bypass, 1 for simple buffer + parameter B_REG_TYPE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI lite slave interface + */ + input wire [ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [DATA_WIDTH-1:0] s_axil_wdata, + input wire [STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + + /* + * AXI lite master interface + */ + output wire [ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [DATA_WIDTH-1:0] m_axil_wdata, + output wire [STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready +); + +generate + +// AW channel + +if (AW_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axil_awready_reg = 1'b0; + +reg [ADDR_WIDTH-1:0] m_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] m_axil_awprot_reg = 3'd0; +reg m_axil_awvalid_reg = 1'b0, m_axil_awvalid_next; + +reg [ADDR_WIDTH-1:0] temp_m_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] temp_m_axil_awprot_reg = 3'd0; +reg temp_m_axil_awvalid_reg = 1'b0, temp_m_axil_awvalid_next; + +// datapath control +reg store_axil_aw_input_to_output; +reg store_axil_aw_input_to_temp; +reg store_axil_aw_temp_to_output; + +assign s_axil_awready = s_axil_awready_reg; + +assign m_axil_awaddr = m_axil_awaddr_reg; +assign m_axil_awprot = m_axil_awprot_reg; +assign m_axil_awvalid = m_axil_awvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axil_awready_early = m_axil_awready | (~temp_m_axil_awvalid_reg & (~m_axil_awvalid_reg | ~s_axil_awvalid)); + +always @* begin + // transfer sink ready state to source + m_axil_awvalid_next = m_axil_awvalid_reg; + temp_m_axil_awvalid_next = temp_m_axil_awvalid_reg; + + store_axil_aw_input_to_output = 1'b0; + store_axil_aw_input_to_temp = 1'b0; + store_axil_aw_temp_to_output = 1'b0; + + if (s_axil_awready_reg) begin + // input is ready + if (m_axil_awready | ~m_axil_awvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axil_awvalid_next = s_axil_awvalid; + store_axil_aw_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axil_awvalid_next = s_axil_awvalid; + store_axil_aw_input_to_temp = 1'b1; + end + end else if (m_axil_awready) begin + // input is not ready, but output is ready + m_axil_awvalid_next = temp_m_axil_awvalid_reg; + temp_m_axil_awvalid_next = 1'b0; + store_axil_aw_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_awready_reg <= 1'b0; + m_axil_awvalid_reg <= 1'b0; + temp_m_axil_awvalid_reg <= 1'b0; + end else begin + s_axil_awready_reg <= s_axil_awready_early; + m_axil_awvalid_reg <= m_axil_awvalid_next; + temp_m_axil_awvalid_reg <= temp_m_axil_awvalid_next; + end + + // datapath + if (store_axil_aw_input_to_output) begin + m_axil_awaddr_reg <= s_axil_awaddr; + m_axil_awprot_reg <= s_axil_awprot; + end else if (store_axil_aw_temp_to_output) begin + m_axil_awaddr_reg <= temp_m_axil_awaddr_reg; + m_axil_awprot_reg <= temp_m_axil_awprot_reg; + end + + if (store_axil_aw_input_to_temp) begin + temp_m_axil_awaddr_reg <= s_axil_awaddr; + temp_m_axil_awprot_reg <= s_axil_awprot; + end +end + +end else if (AW_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axil_awready_reg = 1'b0; + +reg [ADDR_WIDTH-1:0] m_axil_awaddr_reg = {ADDR_WIDTH{1'b0}}; +reg [2:0] m_axil_awprot_reg = 3'd0; +reg m_axil_awvalid_reg = 1'b0, m_axil_awvalid_next; + +// datapath control +reg store_axil_aw_input_to_output; + +assign s_axil_awready = s_axil_awready_reg; + +assign m_axil_awaddr = m_axil_awaddr_reg; +assign m_axil_awprot = m_axil_awprot_reg; +assign m_axil_awvalid = m_axil_awvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axil_awready_eawly = !m_axil_awvalid_next; + +always @* begin + // transfer sink ready state to source + m_axil_awvalid_next = m_axil_awvalid_reg; + + store_axil_aw_input_to_output = 1'b0; + + if (s_axil_awready_reg) begin + m_axil_awvalid_next = s_axil_awvalid; + store_axil_aw_input_to_output = 1'b1; + end else if (m_axil_awready) begin + m_axil_awvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_awready_reg <= 1'b0; + m_axil_awvalid_reg <= 1'b0; + end else begin + s_axil_awready_reg <= s_axil_awready_eawly; + m_axil_awvalid_reg <= m_axil_awvalid_next; + end + + // datapath + if (store_axil_aw_input_to_output) begin + m_axil_awaddr_reg <= s_axil_awaddr; + m_axil_awprot_reg <= s_axil_awprot; + end +end + +end else begin + + // bypass AW channel + assign m_axil_awaddr = s_axil_awaddr; + assign m_axil_awprot = s_axil_awprot; + assign m_axil_awvalid = s_axil_awvalid; + assign s_axil_awready = m_axil_awready; + +end + +// W channel + +if (W_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg s_axil_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axil_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axil_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axil_wvalid_reg = 1'b0, m_axil_wvalid_next; + +reg [DATA_WIDTH-1:0] temp_m_axil_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] temp_m_axil_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg temp_m_axil_wvalid_reg = 1'b0, temp_m_axil_wvalid_next; + +// datapath control +reg store_axil_w_input_to_output; +reg store_axil_w_input_to_temp; +reg store_axil_w_temp_to_output; + +assign s_axil_wready = s_axil_wready_reg; + +assign m_axil_wdata = m_axil_wdata_reg; +assign m_axil_wstrb = m_axil_wstrb_reg; +assign m_axil_wvalid = m_axil_wvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axil_wready_early = m_axil_wready | (~temp_m_axil_wvalid_reg & (~m_axil_wvalid_reg | ~s_axil_wvalid)); + +always @* begin + // transfer sink ready state to source + m_axil_wvalid_next = m_axil_wvalid_reg; + temp_m_axil_wvalid_next = temp_m_axil_wvalid_reg; + + store_axil_w_input_to_output = 1'b0; + store_axil_w_input_to_temp = 1'b0; + store_axil_w_temp_to_output = 1'b0; + + if (s_axil_wready_reg) begin + // input is ready + if (m_axil_wready | ~m_axil_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axil_wvalid_next = s_axil_wvalid; + store_axil_w_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axil_wvalid_next = s_axil_wvalid; + store_axil_w_input_to_temp = 1'b1; + end + end else if (m_axil_wready) begin + // input is not ready, but output is ready + m_axil_wvalid_next = temp_m_axil_wvalid_reg; + temp_m_axil_wvalid_next = 1'b0; + store_axil_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_wready_reg <= 1'b0; + m_axil_wvalid_reg <= 1'b0; + temp_m_axil_wvalid_reg <= 1'b0; + end else begin + s_axil_wready_reg <= s_axil_wready_early; + m_axil_wvalid_reg <= m_axil_wvalid_next; + temp_m_axil_wvalid_reg <= temp_m_axil_wvalid_next; + end + + // datapath + if (store_axil_w_input_to_output) begin + m_axil_wdata_reg <= s_axil_wdata; + m_axil_wstrb_reg <= s_axil_wstrb; + end else if (store_axil_w_temp_to_output) begin + m_axil_wdata_reg <= temp_m_axil_wdata_reg; + m_axil_wstrb_reg <= temp_m_axil_wstrb_reg; + end + + if (store_axil_w_input_to_temp) begin + temp_m_axil_wdata_reg <= s_axil_wdata; + temp_m_axil_wstrb_reg <= s_axil_wstrb; + end +end + +end else if (W_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg s_axil_wready_reg = 1'b0; + +reg [DATA_WIDTH-1:0] m_axil_wdata_reg = {DATA_WIDTH{1'b0}}; +reg [STRB_WIDTH-1:0] m_axil_wstrb_reg = {STRB_WIDTH{1'b0}}; +reg m_axil_wvalid_reg = 1'b0, m_axil_wvalid_next; + +// datapath control +reg store_axil_w_input_to_output; + +assign s_axil_wready = s_axil_wready_reg; + +assign m_axil_wdata = m_axil_wdata_reg; +assign m_axil_wstrb = m_axil_wstrb_reg; +assign m_axil_wvalid = m_axil_wvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire s_axil_wready_ewly = !m_axil_wvalid_next; + +always @* begin + // transfer sink ready state to source + m_axil_wvalid_next = m_axil_wvalid_reg; + + store_axil_w_input_to_output = 1'b0; + + if (s_axil_wready_reg) begin + m_axil_wvalid_next = s_axil_wvalid; + store_axil_w_input_to_output = 1'b1; + end else if (m_axil_wready) begin + m_axil_wvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axil_wready_reg <= 1'b0; + m_axil_wvalid_reg <= 1'b0; + end else begin + s_axil_wready_reg <= s_axil_wready_ewly; + m_axil_wvalid_reg <= m_axil_wvalid_next; + end + + // datapath + if (store_axil_w_input_to_output) begin + m_axil_wdata_reg <= s_axil_wdata; + m_axil_wstrb_reg <= s_axil_wstrb; + end +end + +end else begin + + // bypass W channel + assign m_axil_wdata = s_axil_wdata; + assign m_axil_wstrb = s_axil_wstrb; + assign m_axil_wvalid = s_axil_wvalid; + assign s_axil_wready = m_axil_wready; + +end + +// B channel + +if (B_REG_TYPE > 1) begin +// skid buffer, no bubble cycles + +// datapath registers +reg m_axil_bready_reg = 1'b0; + +reg [1:0] s_axil_bresp_reg = 2'b0; +reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next; + +reg [1:0] temp_s_axil_bresp_reg = 2'b0; +reg temp_s_axil_bvalid_reg = 1'b0, temp_s_axil_bvalid_next; + +// datapath control +reg store_axil_b_input_to_output; +reg store_axil_b_input_to_temp; +reg store_axil_b_temp_to_output; + +assign m_axil_bready = m_axil_bready_reg; + +assign s_axil_bresp = s_axil_bresp_reg; +assign s_axil_bvalid = s_axil_bvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire m_axil_bready_early = s_axil_bready | (~temp_s_axil_bvalid_reg & (~s_axil_bvalid_reg | ~m_axil_bvalid)); + +always @* begin + // transfer sink ready state to source + s_axil_bvalid_next = s_axil_bvalid_reg; + temp_s_axil_bvalid_next = temp_s_axil_bvalid_reg; + + store_axil_b_input_to_output = 1'b0; + store_axil_b_input_to_temp = 1'b0; + store_axil_b_temp_to_output = 1'b0; + + if (m_axil_bready_reg) begin + // input is ready + if (s_axil_bready | ~s_axil_bvalid_reg) begin + // output is ready or currently not valid, transfer data to output + s_axil_bvalid_next = m_axil_bvalid; + store_axil_b_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_s_axil_bvalid_next = m_axil_bvalid; + store_axil_b_input_to_temp = 1'b1; + end + end else if (s_axil_bready) begin + // input is not ready, but output is ready + s_axil_bvalid_next = temp_s_axil_bvalid_reg; + temp_s_axil_bvalid_next = 1'b0; + store_axil_b_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axil_bready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + temp_s_axil_bvalid_reg <= 1'b0; + end else begin + m_axil_bready_reg <= m_axil_bready_early; + s_axil_bvalid_reg <= s_axil_bvalid_next; + temp_s_axil_bvalid_reg <= temp_s_axil_bvalid_next; + end + + // datapath + if (store_axil_b_input_to_output) begin + s_axil_bresp_reg <= m_axil_bresp; + end else if (store_axil_b_temp_to_output) begin + s_axil_bresp_reg <= temp_s_axil_bresp_reg; + end + + if (store_axil_b_input_to_temp) begin + temp_s_axil_bresp_reg <= m_axil_bresp; + end +end + +end else if (B_REG_TYPE == 1) begin +// simple register, inserts bubble cycles + +// datapath registers +reg m_axil_bready_reg = 1'b0; + +reg [1:0] s_axil_bresp_reg = 2'b0; +reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next; + +// datapath control +reg store_axil_b_input_to_output; + +assign m_axil_bready = m_axil_bready_reg; + +assign s_axil_bresp = s_axil_bresp_reg; +assign s_axil_bvalid = s_axil_bvalid_reg; + +// enable ready input next cycle if output buffer will be empty +wire m_axil_bready_early = !s_axil_bvalid_next; + +always @* begin + // transfer sink ready state to source + s_axil_bvalid_next = s_axil_bvalid_reg; + + store_axil_b_input_to_output = 1'b0; + + if (m_axil_bready_reg) begin + s_axil_bvalid_next = m_axil_bvalid; + store_axil_b_input_to_output = 1'b1; + end else if (s_axil_bready) begin + s_axil_bvalid_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axil_bready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + end else begin + m_axil_bready_reg <= m_axil_bready_early; + s_axil_bvalid_reg <= s_axil_bvalid_next; + end + + // datapath + if (store_axil_b_input_to_output) begin + s_axil_bresp_reg <= m_axil_bresp; + end +end + +end else begin + + // bypass B channel + assign s_axil_bresp = m_axil_bresp; + assign s_axil_bvalid = m_axil_bvalid; + assign m_axil_bready = s_axil_bready; + +end + +endgenerate + +endmodule diff --git a/corundum/lib/axi/rtl/priority_encoder.v b/corundum/lib/axi/rtl/priority_encoder.v new file mode 100644 index 0000000000000000000000000000000000000000..73030630244adccf1ab3be09177c060f3f6cfb7e --- /dev/null +++ b/corundum/lib/axi/rtl/priority_encoder.v @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Priority encoder module + */ +module priority_encoder # +( + parameter WIDTH = 4, + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire [WIDTH-1:0] input_unencoded, + output wire output_valid, + output wire [$clog2(WIDTH)-1:0] output_encoded, + output wire [WIDTH-1:0] output_unencoded +); + +// power-of-two width +parameter W1 = 2**$clog2(WIDTH); +parameter W2 = W1/2; + +generate + if (WIDTH == 1) begin + // one input + assign output_valid = input_unencoded; + assign output_encoded = 0; + end else if (WIDTH == 2) begin + // two inputs - just an OR gate + assign output_valid = |input_unencoded; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = input_unencoded[1]; + end else begin + assign output_encoded = ~input_unencoded[0]; + end + end else begin + // more than two inputs - split into two parts and recurse + // also pad input to correct power-of-two width + wire [$clog2(W2)-1:0] out1, out2; + wire valid1, valid2; + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst1 ( + .input_unencoded(input_unencoded[W2-1:0]), + .output_valid(valid1), + .output_encoded(out1) + ); + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst2 ( + .input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}), + .output_valid(valid2), + .output_encoded(out2) + ); + // multiplexer to select part + assign output_valid = valid1 | valid2; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1}; + end else begin + assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2}; + end + end +endgenerate + +// unencoded output +assign output_unencoded = 1 << output_encoded; + +endmodule diff --git a/corundum/lib/axi/syn/axil_cdc.tcl b/corundum/lib/axi/syn/axil_cdc.tcl new file mode 100644 index 0000000000000000000000000000000000000000..97321195d1b7015ee12a854de4ac765732e2f256 --- /dev/null +++ b/corundum/lib/axi/syn/axil_cdc.tcl @@ -0,0 +1,76 @@ +# Copyright (c) 2019 Alex Forencich +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# AXI lite clock domain crossing module timing constraints + +foreach inst [get_cells -hier -filter {(ORIG_REF_NAME == axil_cdc_rd || REF_NAME == axil_cdc_rd || ORIG_REF_NAME == axil_cdc_wr || REF_NAME == axil_cdc_wr)}] { + puts "Inserting timing constraints for axil_cdc instance $inst" + + # get clock periods + set m_clk [get_clocks -of_objects [get_pins $inst/m_flag_reg_reg/C]] + set s_clk [get_clocks -of_objects [get_pins $inst/s_flag_reg_reg/C]] + + set m_clk_period [get_property -min PERIOD $m_clk] + set s_clk_period [get_property -min PERIOD $s_clk] + + set min_clk_period [expr $m_clk_period < $s_clk_period ? $m_clk_period : $s_clk_period] + + set_property ASYNC_REG TRUE [get_cells -quiet -hier -regexp ".*/m_flag_sync_reg_\[12\]_reg" -filter "PARENT == $inst"] + set_property ASYNC_REG TRUE [get_cells -quiet -hier -regexp ".*/s_flag_sync_reg_\[12\]_reg" -filter "PARENT == $inst"] + + set_max_delay -from [get_cells $inst/m_flag_reg_reg] -to [get_cells $inst/m_flag_sync_reg_1_reg] -datapath_only $s_clk_period + set_max_delay -from [get_cells $inst/s_flag_reg_reg] -to [get_cells $inst/s_flag_sync_reg_1_reg] -datapath_only $m_clk_period + + set source [get_cells -quiet -hier -regexp ".*/s_axil_a?(r|w)(addr|prot|data|strb)_reg_reg\\\[\\d+\\\]" -filter "PARENT == $inst"] + set dest [get_cells -quiet -hier -regexp ".*/m_axil_a?(r|w)(addr|prot|data|strb)_reg_reg\\\[\\d+\\\]" -filter "PARENT == $inst"] + + if {[llength $dest]} { + if {![llength $source]} { + # source cells seem to have been merged with something, so go hunt them down + set dest_pins [get_pins -of_objects $dest -filter {REF_PIN_NAME == "D"}] + set nets [get_nets -segments -of_objects $dest_pins] + set source_pins [get_pins -of_objects $nets -filter {IS_LEAF && DIRECTION == "OUT"}] + set source [get_cells -of_objects $source_pins] + } + + if {[llength $source]} { + set_max_delay -from $source -to $dest -datapath_only $m_clk_period + set_bus_skew -from $source -to $dest $s_clk_period + } + } + + set source [get_cells -quiet -hier -regexp ".*/m_axil_(r|b)(resp|data)_reg_reg\\\[\\d+\\\]" -filter "PARENT == $inst"] + set dest [get_cells -quiet -hier -regexp ".*/s_axil_(r|b)(resp|data)_reg_reg\\\[\\d+\\\]" -filter "PARENT == $inst"] + + if {[llength $dest]} { + if {![llength $source]} { + # source cells seem to have been merged with something, so go hunt them down + set dest_pins [get_pins -of_objects $dest -filter {REF_PIN_NAME == "D"}] + set nets [get_nets -segments -of_objects $dest_pins] + set source_pins [get_pins -of_objects $nets -filter {IS_LEAF && DIRECTION == "OUT"}] + set source [get_cells -of_objects $source_pins] + } + + if {[llength $source]} { + set_max_delay -from $source -to $dest -datapath_only $s_clk_period + set_bus_skew -from $source -to $dest $m_clk_period + } + } +} diff --git a/corundum/lib/axis b/corundum/lib/axis new file mode 120000 index 0000000000000000000000000000000000000000..e2ec0d3cdb2dc07f54c03f527bf6c5fdb35d57ff --- /dev/null +++ b/corundum/lib/axis @@ -0,0 +1 @@ +eth/lib/axis/ \ No newline at end of file diff --git a/corundum/lib/eth/.gitignore b/corundum/lib/eth/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..964df4d4afc9fbf5e32167a837ffa1359c80d062 --- /dev/null +++ b/corundum/lib/eth/.gitignore @@ -0,0 +1,6 @@ +*~ +*.lxt +*.pyc +*.vvp +*.kate-swp + diff --git a/corundum/lib/eth/.travis.yml b/corundum/lib/eth/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..15f9a3c31adeed056c2c281df8b5e6979ca10fe2 --- /dev/null +++ b/corundum/lib/eth/.travis.yml @@ -0,0 +1,15 @@ +language: python +python: + - "3.6" +before_install: + - export d=`pwd` + - export PYTHON_EXE=`which python` + - sudo apt-get update -qq + - sudo apt-get install -y iverilog + - git clone https://github.com/jandecaluwe/myhdl.git + - cd $d/myhdl && sudo $PYTHON_EXE setup.py install + - cd $d/myhdl/cosimulation/icarus && make && sudo install -m 0755 -D ./myhdl.vpi /usr/lib/x86_64-linux-gnu/ivl/myhdl.vpi + - cd $d +script: + - cd tb && py.test + diff --git a/corundum/lib/eth/AUTHORS b/corundum/lib/eth/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..7dab2b3a51633a1dd67fd71ceb1c637931f27b85 --- /dev/null +++ b/corundum/lib/eth/AUTHORS @@ -0,0 +1 @@ +Alex Forencich diff --git a/corundum/lib/eth/COPYING b/corundum/lib/eth/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..dc298464e7aade999ebfa48632f6af2779439bcb --- /dev/null +++ b/corundum/lib/eth/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/corundum/lib/eth/README b/corundum/lib/eth/README new file mode 120000 index 0000000000000000000000000000000000000000..42061c01a1c70097d1e4579f29a5adf40abdec95 --- /dev/null +++ b/corundum/lib/eth/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/corundum/lib/eth/README.md b/corundum/lib/eth/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4411126af36370f193b71f57c2bed0425cbb40e5 --- /dev/null +++ b/corundum/lib/eth/README.md @@ -0,0 +1,562 @@ +# Verilog Ethernet Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/ethernet/start + +GitHub repository: https://github.com/alexforencich/verilog-ethernet + +## Introduction + +Collection of Ethernet-related components for gigabit, 10G, and 25G packet +processing (8 bit and 64 bit datapaths). Includes modules for handling +Ethernet frames as well as IP, UDP, and ARP and the components for +constructing a complete UDP/IP stack. Includes MAC modules for gigabit and +10G/25G, a 10G/25G PCS/PMA PHY module, and a 10G/25G combination MAC/PCS/PMA +module. Includes various PTP related components for implementing systems that +require precise time synchronization. Also includes full MyHDL testbench with +intelligent bus cosimulation endpoints. + +For IP and ARP support only, use ip_complete (1G) or ip_complete_64 (10G/25G). + +For UDP, IP, and ARP support, use udp_complete (1G) or udp_complete_64 +(10G/25G). + +Top level gigabit and 10G/25G MAC modules are eth_mac_*, with various +interfaces and with/without FIFOs. Top level 10G/25G PCS/PMA PHY module is +eth_phy_10g. Top level 10G/25G MAC/PCS/PMA combination module is +eth_mac_phy_10g. + +PTP components include a configurable PTP clock (ptp_clock), a PTP clock CDC +module (ptp_clock_cdc) for transferring PTP time across clock domains, and a +configurable PTP period output module for precisely generating arbitrary +frequencies from PTP time. + +## Documentation + +### arp module + +ARP handling logic with parametrizable retry timeout parameters and +parametrizable datapath. + +### arp_cache module + +Basic hash-based cache for ARP entries. Parametrizable depth. + +### arp_eth_rx module + +ARP frame receiver with parametrizable datapath. + +### arp_eth_tx module + +ARP frame transmitter with parametrizable datapath. + +### axis_eth_fcs module + +Ethernet frame check sequence calculator. + +### axis_eth_fcs_64 module + +Ethernet frame check sequence calculator with 64 bit datapath for 10G/25G +Ethernet. + +### axis_eth_fcs_check module + +Ethernet frame check sequence checker. + +### axis_eth_fcs_insert module + +Ethernet frame check sequence inserter. + +### axis_gmii_rx module + +AXI stream GMII/MII frame receiver with clock enable and MII select. + +### axis_gmii_tx module + +AXI stream GMII/MII frame transmitter with clock enable and MII select. + +### axis_xgmii_rx_32 module + +AXI stream XGMII frame receiver with 32 bit datapath. + +### axis_xgmii_rx_64 module + +AXI stream XGMII frame receiver with 64 bit datapath. + +### axis_xgmii_tx_32 module + +AXI stream XGMII frame transmitter with 32 bit datapath. + +### axis_xgmii_tx_64 module + +AXI stream XGMII frame transmitter with 64 bit datapath. + +### eth_arb_mux module + +Ethernet frame arbitrated muliplexer with parametrizable data width and port +count. Supports priority and round-robin arbitration. + +### eth_axis_rx module + +Ethernet frame receiver with parametrizable datapath. + +### eth_axis_tx module + +Ethernet frame transmitter with parametrizable datapath. + +### eth_demux module + +Ethernet frame demuliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### eth_mac_1g module + +Gigabit Ethernet MAC with GMII interface. + +### eth_mac_1g_fifo module + +Gigabit Ethernet MAC with GMII interface and FIFOs. + +### eth_mac_1g_gmii module + +Tri-mode Ethernet MAC with GMII/MII interface and automatic PHY rate +adaptation logic. + +### eth_mac_1g_gmii_fifo module + +Tri-mode Ethernet MAC with GMII/MII interface, FIFOs, and automatic PHY rate +adaptation logic. + +### eth_mac_1g_rgmii module + +Tri-mode Ethernet MAC with RGMII interface and automatic PHY rate adaptation +logic. + +### eth_mac_1g_rgmii_fifo module + +Tri-mode Ethernet MAC with RGMII interface, FIFOs, and automatic PHY rate +adaptation logic. + +### eth_mac_10g module + +10G/25G Ethernet MAC with XGMII interface. Datapath selectable between 32 and +64 bits. + +### eth_mac_10g_fifo module + +10G/25G Ethernet MAC with XGMII interface and FIFOs. Datapath selectable +between 32 and 64 bits. + +### eth_mac_mii module + +Ethernet MAC with MII interface. + +### eth_mac_mii_fifo module + +Ethernet MAC with MII interface and FIFOs. + +### eth_mac_phy_10g module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface. + +### eth_mac_phy_10g_fifo module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface and FIFOs. + +### eth_mac_phy_10g_rx module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface, RX path. + +### eth_mac_phy_10g_tx module + +10G/25G Ethernet MAC/PHY combination module with SERDES interface, TX path. + +### eth_mux module + +Ethernet frame muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### eth_phy_10g module + +10G/25G Ethernet PCS/PMA PHY. + +### eth_phy_10g_rx module + +10G/25G Ethernet PCS/PMA PHY receive-side logic. + +### eth_phy_10g_rx_ber_mon module + +10G/25G Ethernet PCS/PMA PHY BER monitor. + +### eth_phy_10g_rx_frame_sync module + +10G/25G Ethernet PCS/PMA PHY frame synchronizer. + +### eth_phy_10g_tx module + +10G/25G Ethernet PCS/PMA PHY transmit-side logic. + +### gmii_phy_if module + +GMII/MII PHY interface and clocking logic. + +### ip module + +IPv4 block with 8 bit data width for gigabit Ethernet. Manages IPv4 packet +transmssion and reception. Interfaces with ARP module for MAC address lookup. + +### ip_64 module + +IPv4 block with 64 bit data width for 10G/25G Ethernet. Manages IPv4 packet +transmssion and reception. Interfaces with ARP module for MAC address lookup. + +### ip_arb_mux module + +IP frame arbitrated muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### ip_complete module + +IPv4 module with ARP integration. + +Top level for gigabit IP stack. + +### ip_complete_64 module + +IPv4 module with ARP integration and 64 bit data width for 10G/25G Ethernet. + +Top level for 10G/25G IP stack. + +### ip_demux module + +IP frame demuliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### ip_eth_rx module + +IP frame receiver. + +### ip_eth_rx_64 module + +IP frame receiver with 64 bit datapath for 10G/25G Ethernet. + +### ip_eth_tx module + +IP frame transmitter. + +### ip_eth_tx_64 module + +IP frame transmitter with 64 bit datapath for 10G/25G Ethernet. + +### ip_mux module + +IP frame muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### lfsr module + +Fully parametrizable combinatorial parallel LFSR/CRC module. + +### mii_phy_if module + +MII PHY interface and clocking logic. + +### ptp_clock module + +PTP clock module with PPS output. Generates both 64 bit and 96 bit timestamp +formats. Fine frequeny adjustment supported with configurable fractional +nanoseconds field. + +### ptp_clock_cdc module + +PTP clock CDC module with PPS output. Use this module to transfer and deskew a +free-running PTP clock across clock domains. Supports both 64 and 96 bit +timestamp formats. + +### ptp_ts_extract module + +PTP timestamp extract module. Use this module to extract a PTP timestamp +embedded in the tuser sideband signal of an AXI stream interface. + +### ptp_perout module + +PTP period output module. Generates a pulse output, configurable in absolute +start time, period, and width, based on PTP time from a PTP clock. + +### rgmii_phy_if module + +RGMII PHY interface and clocking logic. + +### udp module + +UDP block with 8 bit data width for gigabit Ethernet. Manages UDP packet +transmssion and reception. + +### udp_64 module + +UDP block with 64 bit data width for 10G/25G Ethernet. Manages UDP packet +transmssion and reception. + +### udp_arb_mux module + +UDP frame arbitrated muliplexer with parametrizable data width and port +count. Supports priority and round-robin arbitration. + +### udp_checksum_gen module + +UDP checksum generator module. Calculates UDP length, IP length, and +UDP checksum fields. + +### udp_checksum_gen_64 module + +UDP checksum generator module with 64 bit datapath. Calculates UDP +length, IP length, and UDP checksum fields. + +### udp_complete module + +UDP module with IPv4 and ARP integration. + +Top level for gigabit UDP stack. + +### udp_complete_64 module + +UDP module with IPv4 and ARP integration and 64 bit data width for 10G +Ethernet. + +Top level for 10G/25G UDP stack. + +### udp_demux module + +UDP frame demuliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### udp_ip_rx module + +UDP frame receiver. + +### udp_ip_rx_64 module + +UDP frame receiver with 64 bit datapath for 10G/25G Ethernet. + +### udp_ip_tx module + +UDP frame transmitter. + +### udp_ip_tx_64 module + +UDP frame transmitter with 64 bit datapath for 10G/25G Ethernet. + +### udp_mux module + +UDP frame muliplexer with parametrizable data width and port count. +Supports priority and round-robin arbitration. + +### xgmii_baser_dec_64 module + +XGMII 10GBASE-R decoder for 10G PCS/PMA PHY. + +### xgmii_baser_enc_64 module + +XGMII 10GBASE-R encoder for 10G PCS/PMA PHY. + +### xgmii_deinterleave module + +XGMII de-interleaver for interfacing with PHY cores that interleave the +control and data lines. + +### xgmii_interleave module + +XGMII interleaver for interfacing with PHY cores that interleave the control +and data lines. + +### Common signals + + tdata : Data (width generally DATA_WIDTH) + tkeep : Data word valid (width generally KEEP_WIDTH, present on _64 modules) + tvalid : Data valid + tready : Sink ready + tlast : End-of-frame + tuser : Bad frame (valid with tlast & tvalid) + +### Source Files + + rtl/arp.v : ARP handling logic + rtl/arp_cache.v : ARP LRU cache + rtl/arp_eth_rx.v : ARP frame receiver + rtl/arp_eth_tx.v : ARP frame transmitter + rtl/eth_arb_mux.py : Ethernet frame arbitrated multiplexer generator + rtl/axis_eth_fcs.v : Ethernet FCS calculator + rtl/axis_eth_fcs_64.v : Ethernet FCS calculator (64 bit) + rtl/axis_eth_fcs_insert.v : Ethernet FCS inserter + rtl/axis_eth_fcs_check.v : Ethernet FCS checker + rtl/axis_gmii_rx.v : AXI stream GMII/MII receiver + rtl/axis_gmii_tx.v : AXI stream GMII/MII transmitter + rtl/axis_xgmii_rx_32.v : AXI stream XGMII receiver (32 bit) + rtl/axis_xgmii_rx_64.v : AXI stream XGMII receiver (64 bit) + rtl/axis_xgmii_tx_32.v : AXI stream XGMII transmitter (32 bit) + rtl/axis_xgmii_tx_64.v : AXI stream XGMII transmitter (64 bit) + rtl/eth_arb_mux.v : Ethernet frame arbitrated multiplexer + rtl/eth_axis_rx.v : Ethernet frame receiver + rtl/eth_axis_tx.v : Ethernet frame transmitter + rtl/eth_demux.v : Ethernet frame demultiplexer + rtl/eth_mac_1g.v : Gigabit Ethernet GMII MAC + rtl/eth_mac_1g_fifo.v : Gigabit Ethernet GMII MAC with FIFO + rtl/eth_mac_1g_gmii.v : Tri-mode Ethernet GMII/MII MAC + rtl/eth_mac_1g_gmii_fifo.v : Tri-mode Ethernet GMII/MII MAC with FIFO + rtl/eth_mac_1g_rgmii.v : Tri-mode Ethernet RGMII MAC + rtl/eth_mac_1g_rgmii_fifo.v : Tri-mode Ethernet RGMII MAC with FIFO + rtl/eth_mac_10g.v : 10G/25G Ethernet XGMII MAC + rtl/eth_mac_10g_fifo.v : 10G/25G Ethernet XGMII MAC with FIFO + rtl/eth_mac_mii.v : Ethernet MII MAC + rtl/eth_mac_mii_fifo.v : Ethernet MII MAC with FIFO + rtl/eth_mac_phy_10g.v : 10G/25G Ethernet XGMII MAC/PHY + rtl/eth_mac_phy_10g_fifo.v : 10G/25G Ethernet XGMII MAC/PHY with FIFO + rtl/eth_mac_phy_10g_rx.v : 10G/25G Ethernet XGMII MAC/PHY RX with FIFO + rtl/eth_mac_phy_10g_tx.v : 10G/25G Ethernet XGMII MAC/PHY TX with FIFO + rtl/eth_mux.v : Ethernet frame multiplexer + rtl/gmii_phy_if.v : GMII PHY interface + rtl/iddr.v : Generic DDR input register + rtl/ip.v : IPv4 block + rtl/ip_64.v : IPv4 block (64 bit) + rtl/ip_arb_mux.v : IP frame arbitrated multiplexer + rtl/ip_complete.v : IPv4 stack (IP-ARP integration) + rtl/ip_complete_64.v : IPv4 stack (IP-ARP integration) (64 bit) + rtl/ip_demux.v : IP frame demultiplexer + rtl/ip_eth_rx.v : IPv4 frame receiver + rtl/ip_eth_rx_64.v : IPv4 frame receiver (64 bit) + rtl/ip_eth_tx.v : IPv4 frame transmitter + rtl/ip_eth_tx_64.v : IPv4 frame transmitter (64 bit) + rtl/ip_mux.v : IP frame multiplexer + rtl/lfsr.v : Generic LFSR/CRC module + rtl/mii_phy_if.v : MII PHY interface + rtl/oddr.v : Generic DDR output register + rtl/ptp_clock.v : PTP clock + rtl/ptp_clock_cdc.v : PTP clock CDC + rtl/ptp_ts_extract.v : PTP timestamp extract + rtl/ptp_perout.v : PTP period out + rtl/rgmii_phy_if.v : RGMII PHY interface + rtl/ssio_ddr_in.v : Generic source synchronous IO DDR input module + rtl/ssio_ddr_in_diff.v : Generic source synchronous IO DDR differential input module + rtl/ssio_ddr_out.v : Generic source synchronous IO DDR output module + rtl/ssio_ddr_out_diff.v : Generic source synchronous IO DDR differential output module + rtl/ssio_sdr_in.v : Generic source synchronous IO SDR input module + rtl/ssio_sdr_in_diff.v : Generic source synchronous IO SDR differential input module + rtl/ssio_sdr_out.v : Generic source synchronous IO SDR output module + rtl/ssio_sdr_out_diff.v : Generic source synchronous IO SDR differential output module + rtl/udp.v : UDP block + rtl/udp_64.v : UDP block (64 bit) + rtl/udp_arb_mux.v : UDP frame arbitrated multiplexer + rtl/udp_checksum_gen.v : UDP checksum generator + rtl/udp_checksum_gen_64.v : UDP checksum generator (64 bit) + rtl/udp_complete.v : UDP stack (IP-ARP-UDP) + rtl/udp_complete_64.v : UDP stack (IP-ARP-UDP) (64 bit) + rtl/udp_demux.v : UDP frame demultiplexer + rtl/udp_ip_rx.v : UDP frame receiver + rtl/udp_ip_rx_64.v : UDP frame receiver (64 bit) + rtl/udp_ip_tx.v : UDP frame transmitter + rtl/udp_ip_tx_64.v : UDP frame transmitter (64 bit) + rtl/udp_mux.v : UDP frame multiplexer + rtl/xgmii_baser_dec_64.v : XGMII 10GBASE-R decoder + rtl/xgmii_baser_enc_64.v : XGMII 10GBASE-R encoder + rtl/xgmii_deinterleave.v : XGMII data/control de-interleaver + rtl/xgmii_interleave.v : XGMII data/control interleaver + +### AXI Stream Interface Example + +transfer with header data + + __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + ______________ ___________ + hdr_ready \_________________/ + _____ + hdr_valid ________/ \_____________________________ + _____ + hdr_data XXXXXXXXX_HDR_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + ___________ _____ _____ + tdata XXXXXXXXX_A0________X_A1__X_A2__XXXXXXXXXXXX + ___________ _____ _____ + tkeep XXXXXXXXX_K0________X_K1__X_K2__XXXXXXXXXXXX + _______________________ + tvalid ________/ \___________ + _________________ + tready ______________/ \___________ + _____ + tlast __________________________/ \___________ + + tuser ____________________________________________ + + +two byte transfer with sink pause after each byte + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _________________ + tdata XXXXXXXXX_D0__X_D1______________XXXXXXXXXXXXXXXXXXXXXXXX + _____ _________________ + tkeep XXXXXXXXX_K0__X_K1______________XXXXXXXXXXXXXXXXXXXXXXXX + _______________________ + tvalid ________/ \_______________________ + ______________ _____ ___________ + tready \___________/ \___________/ + _________________ + tlast ______________/ \_______________________ + + tuser ________________________________________________________ + + +two back-to-back packets, no pauses + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__X_B0__X_B1__X_B2__XXXXXXXXXXXX + _____ _____ _____ _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__X_K0__X_K1__X_K2__XXXXXXXXXXXX + ___________________________________ + tvalid ________/ \___________ + ________________________________________________________ + tready + _____ _____ + tlast ____________________/ \___________/ \___________ + + tuser ________________________________________________________ + + +bad frame + + __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__XXXXXXXXXXXX + _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__XXXXXXXXXXXX + _________________ + tvalid ________/ \___________ + ______________________________________ + tready + _____ + tlast ____________________/ \___________ + _____ + tuser ____________________/ \___________ + + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/arp_ep.py : MyHDL ARP frame endpoints + tb/axis_ep.py : MyHDL AXI Stream endpoints + tb/baser_serdes.py : MyHDL 10GBASE-R SERDES endpoints + tb/eth_ep.py : MyHDL Ethernet frame endpoints + tb/gmii_ep.py : MyHDL GMII endpoints + tb/ip_ep.py : MyHDL IP frame endpoints + tb/mii_ep.py : MyHDL MII endpoints + tb/ptp.py : MyHDL PTP clock model + tb/rgmii_ep.py : MyHDL RGMII endpoints + tb/udp_ep.py : MyHDL UDP frame endpoints + tb/xgmii_ep.py : MyHDL XGMII endpoints diff --git a/corundum/lib/eth/lib/axis/.gitignore b/corundum/lib/eth/lib/axis/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..964df4d4afc9fbf5e32167a837ffa1359c80d062 --- /dev/null +++ b/corundum/lib/eth/lib/axis/.gitignore @@ -0,0 +1,6 @@ +*~ +*.lxt +*.pyc +*.vvp +*.kate-swp + diff --git a/corundum/lib/eth/lib/axis/.travis.yml b/corundum/lib/eth/lib/axis/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..a3a8fd49c9ed3f01f80ce7d814616a9a915b33cc --- /dev/null +++ b/corundum/lib/eth/lib/axis/.travis.yml @@ -0,0 +1,15 @@ +language: python +python: + - "3.6" +before_install: + - export d=`pwd` + - export PYTHON_EXE=`which python` + - sudo apt-get update -qq + - sudo apt-get install -y iverilog + - git clone https://github.com/jandecaluwe/myhdl.git + - cd $d/myhdl && sudo $PYTHON_EXE setup.py install + - cd $d/myhdl/cosimulation/icarus && make && sudo install -m 0755 -D ./myhdl.vpi /usr/lib/x86_64-linux-gnu/ivl/myhdl.vpi + - cd $d +script: + - cd tb && IVERILOG_DUMPER=none py.test + diff --git a/corundum/lib/eth/lib/axis/AUTHORS b/corundum/lib/eth/lib/axis/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..7dab2b3a51633a1dd67fd71ceb1c637931f27b85 --- /dev/null +++ b/corundum/lib/eth/lib/axis/AUTHORS @@ -0,0 +1 @@ +Alex Forencich diff --git a/corundum/lib/eth/lib/axis/COPYING b/corundum/lib/eth/lib/axis/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..dc298464e7aade999ebfa48632f6af2779439bcb --- /dev/null +++ b/corundum/lib/eth/lib/axis/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/corundum/lib/eth/lib/axis/README b/corundum/lib/eth/lib/axis/README new file mode 120000 index 0000000000000000000000000000000000000000..42061c01a1c70097d1e4579f29a5adf40abdec95 --- /dev/null +++ b/corundum/lib/eth/lib/axis/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/corundum/lib/eth/lib/axis/README.md b/corundum/lib/eth/lib/axis/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5b4c5dcfa0a0402016c29137d55eacfe357a9121 --- /dev/null +++ b/corundum/lib/eth/lib/axis/README.md @@ -0,0 +1,305 @@ +# Verilog AXI Stream Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/axis/start + +GitHub repository: https://github.com/alexforencich/verilog-axis + +## Introduction + +Collection of AXI Stream bus components. Most components are fully +parametrizable in interface widths. Includes full MyHDL testbench with +intelligent bus cosimulation endpoints. + +## Documentation + +### arbiter module + +General-purpose parametrizable arbiter. Supports priority and round-robin +arbitration. Supports blocking until request release or acknowledge. + +### axis_adapter module + +The axis_adapter module bridges AXI stream busses of differing widths. The +module is parametrizable, but there are certain restrictions. First, the bus +word widths must be identical (e.g. one 8-bit lane and eight 8-bit lanes, but +not one 16-bit lane and one 32-bit lane). Second, the bus widths must be +related by an integer multiple (e.g. 2 words and 6 words, but not 4 words +and 6 words). Wait states will be inserted on the wider bus side when +necessary. + +### axis_arb_mux module + +Frame-aware AXI stream arbitrated muliplexer with parametrizable data width +and port count. Supports priority and round-robin arbitration. + +Wrappers can generated with axis_arb_mux_wrap.py. + +### axis_async_fifo module + +Configurable word-based or frame-based asynchronous FIFO with parametrizable +data width, depth, type, and bad frame detection. Supports power of two +depths only. + +### axis_async_fifo_adapter module + +Configurable word-based or frame-based asynchronous FIFO with parametrizable +data width, depth, type, and bad frame detection. Supports different input +and output data widths, inserting an axis_adapter instance appropriately. +Supports power of two depths only. + +### axis_broadcast module + +AXI stream broadcaster. Duplicates one input stream across multiple output +streams. + +### axis_cobs_decode + +Consistent Overhead Byte Stuffing (COBS) decoder. Fixed 8 bit width. + +### axis_cobs_encode + +Consistent Overhead Byte Stuffing (COBS) encoder. Fixed 8 bit width. +Configurable zero insertion. + +### axis_crosspoint module + +Basic crosspoint switch. tready signal not supported. Parametrizable data +width. + +Wrappers can generated with axis_crosspoint_wrap.py. + +### axis_demux module + +Frame-aware AXI stream demuliplexer with parametrizable data width and port +count. + +### axis_fifo module + +Configurable word-based or frame-based synchronous FIFO with parametrizable +data width, depth, type, and bad frame detection. Supports power of two +depths only. + +### axis_fifo_adapter module + +Configurable word-based or frame-based synchronous FIFO with parametrizable +data width, depth, type, and bad frame detection. Supports different input +and output data widths, inserting an axis_adapter instance appropriately. +Supports power of two depths only. + +### axis_frame_join module + +Frame joiner with optional tag and parametrizable port count. 8 bit data path +only. + +Wrappers can generated with axis_frame_join_wrap.py. + +### axis_frame_length_adjust module + +Frame length adjuster module. Truncates or pads frames as necessary to meet +the specified minimum and maximum length. Reports the original and current +lengths as well as whether the packet was truncated or padded. Length limits +are configurable at run time. + +### axis_frame_length_adjust_fifo module + +Frame length adjuster module with FIFO. Truncates or pads frames as necessary +to meet the specified minimum and maximum length. Reports the original and +current lengths as well as whether the packet was truncated or padded. FIFOs +are used so that the status information can be read before the packet itself. +Length limits are configurable at run time. + +### axis_ll_bridge module + +AXI stream to LocalLink bridge. + +### axis_mux module + +Frame-aware AXI stream muliplexer with parametrizable data width and port +count. + +Wrappers can generated with axis_mux_wrap.py. + +### axis_pipeline_register module + +Parametrizable register pipeline. LENGTH parameter determines number of +register stages. + +### axis_ram_switch module + +Frame-aware AXI stream RAM switch with parametrizable data width, port count, +and FIFO size. Uses block RAM for storing packets in transit, time-sharing +the RAM interface between ports. Functionally equivalent to a combination of +per-port frame FIFOs and width converters connected to an AXI stream switch. + +### axis_rate_limit module + +Fractional rate limiter, supports word and frame modes. Inserts wait states +to limit data rate to specified ratio. Frame mode inserts wait states at end +of frames, word mode ignores frames and inserts wait states at any point. +Parametrizable data width. Rate and mode are configurable at run time. + +### axis_register module + +Datapath register with parameter to select between skid buffer, simple buffer, +and bypass. Use to improve timing for long routes. + +### axis_srl_fifo module + +SRL-based FIFO. Good for small FIFOs. SRLs on Xilinx FPGAs have a very fast +input setup time, so this module can be used to aid in timing closure. + +### axis_srl_register module + +SRL-based register. SRLs on Xilinx FPGAs have a very fast input setup time, +so this module can be used to aid in timing closure. + +### axis_stat_counter module + +Statistics counter module. Counts bytes and frames passing through monitored +AXI stream interface. Trigger signal used to reset and dump counts out of AXI +interface, along with tag value. Use with axis_frame_join_N to form a single +monolithic frame from multiple monitored points with the same trigger. + +### axis_switch module + +Frame-aware AXI stream switch with parametrizable data width and port count. + +Wrappers can generated with axis_switch_wrap.py. + +### axis_tap module + +AXI stream tap module. Used to make a copy of an AXI stream bus without +affecting the bus. Back-pressure on the output results in truncated frames +with tuser set. + +### ll_axis_bridge module + +LocalLink to AXI stream bridge. + +### priority_encoder module + +Parametrizable priority encoder. + +### Common signals + + tdata : Data (width generally DATA_WIDTH) + tkeep : Data word valid (width generally KEEP_WIDTH) + tvalid : Data valid + tready : Sink ready + tlast : End-of-frame + tid : Identifier tag (width generally ID_WIDTH) + tdest : Destination tag (width generally DEST_WIDTH) + tuser : User sideband signals (width generally USER_WIDTH) + +### Common parameters + + DATA_WIDTH : width of tdata signal + KEEP_ENABLE : enable tkeep signal (default DATA_WIDTH>8) + KEEP_WIDTH : width of tkeep signal (default DATA_WIDTH/8) + LAST_ENABLE : enable tlast signal + ID_ENABLE : enable tid signal + ID_WIDTH : width of tid signal + DEST_ENABLE : enable tdest signal + DEST_WIDTH : width of tdest signal + USER_ENABLE : enable tuser signal + USER_WIDTH : width of tuser signal + USER_BAD_FRAME_VALUE : value of tuser indicating bad frame + USER_BAD_FRAME_MASK : bitmask for tuser bad frame indication + +### Source Files + + arbiter.v : General-purpose parametrizable arbiter + axis_adapter.v : Parametrizable bus width adapter + axis_arb_mux.v : Parametrizable arbitrated multiplexer + axis_async_fifo.v : Parametrizable asynchronous FIFO + axis_async_fifo_adapter.v : FIFO/width adapter wrapper + axis_broadcast.v : AXI stream broadcaster + axis_cobs_decode.v : COBS decoder + axis_cobs_encode.v : COBS encoder + axis_crosspoint.v : Parametrizable crosspoint switch + axis_demux.v : Parametrizable demultiplexer + axis_fifo.v : Parametrizable synchronous FIFO + axis_fifo_adapter.v : FIFO/width adapter wrapper + axis_frame_join.v : Parametrizable frame joiner + axis_frame_length_adjust.v : Frame length adjuster + axis_frame_length_adjust_fifo.v : Frame length adjuster with FIFO + axis_ll_bridge.v : AXI stream to LocalLink bridge + axis_mux.v : Multiplexer generator + axis_ram_switch.v : AXI stream RAM switch + axis_rate_limit.v : Fractional rate limiter + axis_register.v : AXI Stream register + axis_srl_fifo.v : SRL-based FIFO + axis_srl_register.v : SRL-based register + axis_switch.v : Parametrizable AXI stream switch + axis_stat_counter.v : Statistics counter + axis_tap.v : AXI stream tap + ll_axis_bridge.v : LocalLink to AXI stream bridge + priority_encoder.v : Parametrizable priority encoder + +### AXI Stream Interface Example + +two byte transfer with sink pause after each byte + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _________________ + tdata XXXXXXXXX_D0__X_D1______________XXXXXXXXXXXXXXXXXXXXXXXX + _____ _________________ + tkeep XXXXXXXXX_K0__X_K1______________XXXXXXXXXXXXXXXXXXXXXXXX + _______________________ + tvalid ________/ \_______________________ + ______________ _____ ___________ + tready \___________/ \___________/ + _________________ + tlast ______________/ \_______________________ + + tuser ________________________________________________________ + + +two back-to-back packets, no pauses + + __ __ __ __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__X_B0__X_B1__X_B2__XXXXXXXXXXXX + _____ _____ _____ _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__X_K0__X_K1__X_K2__XXXXXXXXXXXX + ___________________________________ + tvalid ________/ \___________ + ________________________________________________________ + tready + _____ _____ + tlast ____________________/ \___________/ \___________ + + tuser ________________________________________________________ + + +bad frame + + __ __ __ __ __ __ + clk __/ \__/ \__/ \__/ \__/ \__/ \__ + _____ _____ _____ + tdata XXXXXXXXX_A0__X_A1__X_A2__XXXXXXXXXXXX + _____ _____ _____ + tkeep XXXXXXXXX_K0__X_K1__X_K2__XXXXXXXXXXXX + _________________ + tvalid ________/ \___________ + ______________________________________ + tready + _____ + tlast ____________________/ \___________ + _____ + tuser ____________________/ \___________ + + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/axis_ep.py : MyHDL AXI Stream endpoints + tb/ll_ep.py : MyHDL LocalLink endpoints diff --git a/corundum/lib/eth/lib/axis/rtl/arbiter.v b/corundum/lib/eth/lib/axis/rtl/arbiter.v new file mode 100644 index 0000000000000000000000000000000000000000..8b0443fdbae43c2afc42c074cc93be1603c69964 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/arbiter.v @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Arbiter module + */ +module arbiter # +( + parameter PORTS = 4, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter TYPE = "PRIORITY", + // block type: "NONE", "REQUEST", "ACKNOWLEDGE" + parameter BLOCK = "NONE", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire clk, + input wire rst, + + input wire [PORTS-1:0] request, + input wire [PORTS-1:0] acknowledge, + + output wire [PORTS-1:0] grant, + output wire grant_valid, + output wire [$clog2(PORTS)-1:0] grant_encoded +); + +reg [PORTS-1:0] grant_reg = 0, grant_next; +reg grant_valid_reg = 0, grant_valid_next; +reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next; + +assign grant_valid = grant_valid_reg; +assign grant = grant_reg; +assign grant_encoded = grant_encoded_reg; + +wire request_valid; +wire [$clog2(PORTS)-1:0] request_index; +wire [PORTS-1:0] request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_inst ( + .input_unencoded(request), + .output_valid(request_valid), + .output_encoded(request_index), + .output_unencoded(request_mask) +); + +reg [PORTS-1:0] mask_reg = 0, mask_next; + +wire masked_request_valid; +wire [$clog2(PORTS)-1:0] masked_request_index; +wire [PORTS-1:0] masked_request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_masked ( + .input_unencoded(request & mask_reg), + .output_valid(masked_request_valid), + .output_encoded(masked_request_index), + .output_unencoded(masked_request_mask) +); + +always @* begin + grant_next = 0; + grant_valid_next = 0; + grant_encoded_next = 0; + mask_next = mask_reg; + + if (BLOCK == "REQUEST" && grant_reg & request) begin + // granted request still asserted; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (BLOCK == "ACKNOWLEDGE" && grant_valid && !(grant_reg & acknowledge)) begin + // granted request not yet acknowledged; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (request_valid) begin + if (TYPE == "PRIORITY") begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + end else if (TYPE == "ROUND_ROBIN") begin + if (masked_request_valid) begin + grant_valid_next = 1; + grant_next = masked_request_mask; + grant_encoded_next = masked_request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index); + end else begin + mask_next = {PORTS{1'b1}} << (masked_request_index + 1); + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - request_index); + end else begin + mask_next = {PORTS{1'b1}} << (request_index + 1); + end + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + grant_reg <= 0; + grant_valid_reg <= 0; + grant_encoded_reg <= 0; + mask_reg <= 0; + end else begin + grant_reg <= grant_next; + grant_valid_reg <= grant_valid_next; + grant_encoded_reg <= grant_encoded_next; + mask_reg <= mask_next; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_adapter.v b/corundum/lib/eth/lib/axis/rtl/axis_adapter.v new file mode 100644 index 0000000000000000000000000000000000000000..68b31a508266f19c05682768b28306c1a0006a35 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_adapter.v @@ -0,0 +1,554 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream bus width adapter + */ +module axis_adapter # +( + // Width of input AXI stream interface in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal on input interface + // If disabled, tkeep assumed to be 1'b1 + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on input interface + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interface in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal on output interface + // If disabled, tkeep assumed to be 1'b1 + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on output interface + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +// force keep width to 1 when disabled +parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus word sizes (must be identical) +parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +// output bus is wider +parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT; +// total data and keep widths +parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT; +// required number of segments in wider bus +parameter SEGMENT_COUNT = EXPAND_BUS ? (M_KEEP_WIDTH_INT / S_KEEP_WIDTH_INT) : (S_KEEP_WIDTH_INT / M_KEEP_WIDTH_INT); +parameter SEGMENT_COUNT_WIDTH = SEGMENT_COUNT == 1 ? 1 : $clog2(SEGMENT_COUNT); +// data width and keep width per segment +parameter SEGMENT_DATA_WIDTH = DATA_WIDTH / SEGMENT_COUNT; +parameter SEGMENT_KEEP_WIDTH = KEEP_WIDTH / SEGMENT_COUNT; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end +end + +// state register +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_TRANSFER_IN = 3'd1, + STATE_TRANSFER_OUT = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg [SEGMENT_COUNT_WIDTH-1:0] segment_count_reg = 0, segment_count_next; + +reg last_segment; + +reg [DATA_WIDTH-1:0] temp_tdata_reg = {DATA_WIDTH{1'b0}}, temp_tdata_next; +reg [KEEP_WIDTH-1:0] temp_tkeep_reg = {KEEP_WIDTH{1'b0}}, temp_tkeep_next; +reg temp_tlast_reg = 1'b0, temp_tlast_next; +reg [ID_WIDTH-1:0] temp_tid_reg = {ID_WIDTH{1'b0}}, temp_tid_next; +reg [DEST_WIDTH-1:0] temp_tdest_reg = {DEST_WIDTH{1'b0}}, temp_tdest_next; +reg [USER_WIDTH-1:0] temp_tuser_reg = {USER_WIDTH{1'b0}}, temp_tuser_next; + +// internal datapath +reg [M_DATA_WIDTH-1:0] m_axis_tdata_int; +reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +assign s_axis_tready = s_axis_tready_reg; + +always @* begin + state_next = STATE_IDLE; + + segment_count_next = segment_count_reg; + + last_segment = 0; + + temp_tdata_next = temp_tdata_reg; + temp_tkeep_next = temp_tkeep_reg; + temp_tlast_next = temp_tlast_reg; + temp_tid_next = temp_tid_reg; + temp_tdest_next = temp_tdest_reg; + temp_tuser_next = temp_tuser_reg; + + if (EXPAND_BUS) begin + m_axis_tdata_int = temp_tdata_reg; + m_axis_tkeep_int = temp_tkeep_reg; + m_axis_tlast_int = temp_tlast_reg; + end else begin + m_axis_tdata_int = {M_DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {M_KEEP_WIDTH{1'b0}}; + m_axis_tlast_int = 1'b0; + end + m_axis_tvalid_int = 1'b0; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + s_axis_tready_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - no data in registers + if (SEGMENT_COUNT == 1) begin + // output and input same width - just act like a register + + // accept data next cycle if output register ready next cycle + s_axis_tready_next = m_axis_tready_int_early; + + // transfer through + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + state_next = STATE_IDLE; + end else if (EXPAND_BUS) begin + // output bus is wider + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it in data register + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // first input segment complete + segment_count_next = 1; + + if (s_axis_tlast) begin + // got last signal on first segment, so output it + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + // otherwise, transfer in the rest of the words + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + state_next = STATE_IDLE; + end + end else begin + // output bus is narrower + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store it in data register + segment_count_next = 0; + + // is this the last segment? + if (SEGMENT_COUNT == 1) begin + // last segment by counter value + last_segment = 1'b1; + end else if (S_KEEP_ENABLE && s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin + // last segment by tkeep fall in current segment + last_segment = 1'b1; + end else if (S_KEEP_ENABLE && s_axis_tkeep[(SEGMENT_KEEP_WIDTH*2)-1:SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin + // last segment by tkeep fall at end of current segment + last_segment = 1'b1; + end else begin + last_segment = 1'b0; + end + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // short-circuit and get first word out the door + m_axis_tdata_int = s_axis_tdata[SEGMENT_DATA_WIDTH-1:0]; + m_axis_tkeep_int = s_axis_tkeep[SEGMENT_KEEP_WIDTH-1:0]; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = s_axis_tlast & last_segment; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + if (m_axis_tready_int_reg) begin + // if output register is ready for first word, then move on to the next one + segment_count_next = 1; + end + + if (!last_segment || !m_axis_tready_int_reg) begin + // continue outputting words + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_TRANSFER_IN: begin + // transfer word to temp registers + // only used when output is wider + + // accept new data + s_axis_tready_next = 1'b1; + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in - store in data register + + temp_tdata_next[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH] = s_axis_tdata; + temp_tkeep_next[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + segment_count_next = segment_count_reg + 1; + + if ((segment_count_reg == SEGMENT_COUNT-1) || s_axis_tlast) begin + // terminated by counter or tlast signal, output complete word + // read input word next cycle if output will be ready + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_TRANSFER_OUT; + end else begin + // more words to read + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + state_next = STATE_TRANSFER_IN; + end + end + STATE_TRANSFER_OUT: begin + // transfer word to output registers + + if (EXPAND_BUS) begin + // output bus is wider + + // do not accept new data + s_axis_tready_next = 1'b0; + + // single-cycle output of entire stored word (output wider) + m_axis_tdata_int = temp_tdata_reg; + m_axis_tkeep_int = temp_tkeep_reg; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = temp_tlast_reg; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + if (m_axis_tready_int_reg) begin + // word transfer out + + if (s_axis_tready && s_axis_tvalid) begin + // word transfer in + + // pass complete input word, zero-extended to temp register + temp_tdata_next = s_axis_tdata; + temp_tkeep_next = S_KEEP_ENABLE ? s_axis_tkeep : 1'b1; + temp_tlast_next = s_axis_tlast; + temp_tid_next = s_axis_tid; + temp_tdest_next = s_axis_tdest; + temp_tuser_next = s_axis_tuser; + + // first input segment complete + segment_count_next = 1; + + if (s_axis_tlast) begin + // got last signal on first segment, so output it + s_axis_tready_next = 1'b0; + state_next = STATE_TRANSFER_OUT; + end else begin + // otherwise, transfer in the rest of the words + s_axis_tready_next = 1'b1; + state_next = STATE_TRANSFER_IN; + end + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER_OUT; + end + end else begin + // output bus is narrower + + // do not accept new data + s_axis_tready_next = 1'b0; + + // is this the last segment? + if (segment_count_reg == SEGMENT_COUNT-1) begin + // last segment by counter value + last_segment = 1'b1; + end else if (temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] != {SEGMENT_KEEP_WIDTH{1'b1}}) begin + // last segment by tkeep fall in current segment + last_segment = 1'b1; + end else if (temp_tkeep_reg[(segment_count_reg+1)*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH] == {SEGMENT_KEEP_WIDTH{1'b0}}) begin + // last segment by tkeep fall at end of current segment + last_segment = 1'b1; + end else begin + last_segment = 1'b0; + end + + // output current part of stored word (output narrower) + m_axis_tdata_int = temp_tdata_reg[segment_count_reg*SEGMENT_DATA_WIDTH +: SEGMENT_DATA_WIDTH]; + m_axis_tkeep_int = temp_tkeep_reg[segment_count_reg*SEGMENT_KEEP_WIDTH +: SEGMENT_KEEP_WIDTH]; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = temp_tlast_reg && last_segment; + m_axis_tid_int = temp_tid_reg; + m_axis_tdest_int = temp_tdest_reg; + m_axis_tuser_int = temp_tuser_reg; + + if (m_axis_tready_int_reg) begin + // word transfer out + + segment_count_next = segment_count_reg + 1; + + if (last_segment) begin + // terminated by counter or tlast signal + + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end else begin + // more words to write + state_next = STATE_TRANSFER_OUT; + end + end else begin + state_next = STATE_TRANSFER_OUT; + end + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axis_tready_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_axis_tready_reg <= s_axis_tready_next; + end + + segment_count_reg <= segment_count_next; + + temp_tdata_reg <= temp_tdata_next; + temp_tkeep_reg <= temp_tkeep_next; + temp_tlast_reg <= temp_tlast_next; + temp_tid_reg <= temp_tid_next; + temp_tdest_reg <= temp_tdest_next; + temp_tuser_reg <= temp_tuser_next; +end + +// output datapath logic +reg [M_DATA_WIDTH-1:0] m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}}; +reg [M_KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [M_DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {M_DATA_WIDTH{1'b0}}; +reg [M_KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {M_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = M_KEEP_ENABLE ? m_axis_tkeep_reg : {M_KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_arb_mux.v b/corundum/lib/eth/lib/axis/rtl/axis_arb_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..72a3de5e1b7f27e3ff8d7c664853fe23d85792b1 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_arb_mux.v @@ -0,0 +1,249 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream arbitrated multiplexer + */ +module axis_arb_mux # +( + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = (m_axis_tready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_axis_tvalid[grant_encoded]; +wire current_s_tready = s_axis_tready[grant_encoded]; +wire current_s_tlast = s_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_tvalid & ~grant; +assign acknowledge = grant & s_axis_tvalid & s_axis_tready & s_axis_tlast; + +always @* begin + // pass through selected packet data + m_axis_tdata_int = current_s_tdata; + m_axis_tkeep_int = current_s_tkeep; + m_axis_tvalid_int = current_s_tvalid && m_axis_tready_int_reg && grant_valid; + m_axis_tlast_int = current_s_tlast; + m_axis_tid_int = current_s_tid; + m_axis_tdest_int = current_s_tdest; + m_axis_tuser_int = current_s_tuser; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_arb_mux_wrap.py b/corundum/lib/eth/lib/axis/rtl/axis_arb_mux_wrap.py new file mode 100755 index 0000000000000000000000000000000000000000..f44400500778a43fc7ff64c5ac02415a6f91c2ec --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_arb_mux_wrap.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream arbitrated mux wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_arb_mux_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream arbitrated mux wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream {{n}} port arbitrated mux (wrapper) + */ +module {{name}} # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(n) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +axis_arb_mux #( + .S_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +axis_arb_mux_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/corundum/lib/eth/lib/axis/rtl/axis_async_fifo.v b/corundum/lib/eth/lib/axis/rtl/axis_async_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..39ac3498d5b4ac6b5a395831d85d82f0441798ff --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_async_fifo.v @@ -0,0 +1,538 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream asynchronous FIFO + */ +module axis_async_fifo # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames marked bad + // Requires FRAME_FIFO set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO set + parameter DROP_WHEN_FULL = 0 +) +( + /* + * Common asynchronous reset + */ + input wire async_rst, + + /* + * AXI input + */ + input wire s_clk, + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + input wire m_clk, + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +parameter ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); + +// check configuration +initial begin + if (FRAME_FIFO && !LAST_ENABLE) begin + $error("Error: FRAME_FIFO set requires LAST_ENABLE set (instance %m)"); + $finish; + end + + if (DROP_BAD_FRAME && !FRAME_FIFO) begin + $error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set (instance %m)"); + $finish; + end + + if (DROP_WHEN_FULL && !FRAME_FIFO) begin + $error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set (instance %m)"); + $finish; + end + + if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin + $error("Error: Invalid USER_BAD_FRAME_MASK value (instance %m)"); + $finish; + end +end + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next; +reg [ADDR_WIDTH:0] wr_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_gray_next; +reg [ADDR_WIDTH:0] wr_ptr_sync_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_sync_gray_next; +reg [ADDR_WIDTH:0] wr_ptr_cur_gray_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_gray_next; +reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [ADDR_WIDTH:0] rd_ptr_gray_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_gray_next; +reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}}; + +reg [ADDR_WIDTH:0] wr_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] wr_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync1_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_gray_sync2_reg = {ADDR_WIDTH+1{1'b0}}; + +reg wr_ptr_update_valid_reg = 1'b0, wr_ptr_update_valid_next; +reg wr_ptr_update_reg = 1'b0, wr_ptr_update_next; +reg wr_ptr_update_sync1_reg = 1'b0; +reg wr_ptr_update_sync2_reg = 1'b0; +reg wr_ptr_update_sync3_reg = 1'b0; +reg wr_ptr_update_ack_sync1_reg = 1'b0; +reg wr_ptr_update_ack_sync2_reg = 1'b0; + +reg s_rst_sync1_reg = 1'b1; +reg s_rst_sync2_reg = 1'b1; +reg s_rst_sync3_reg = 1'b1; +reg m_rst_sync1_reg = 1'b1; +reg m_rst_sync2_reg = 1'b1; +reg m_rst_sync3_reg = 1'b1; + +reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0]; +reg [WIDTH-1:0] mem_read_data_reg; +reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next; + +wire [WIDTH-1:0] s_axis; + +reg [WIDTH-1:0] m_axis_reg; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + +// full when first TWO MSBs do NOT match, but rest matches +// (gray code equivalent of first MSB different but rest same) +wire full = ((wr_ptr_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) && + (wr_ptr_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) && + (wr_ptr_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0])); +wire full_cur = ((wr_ptr_cur_gray_reg[ADDR_WIDTH] != rd_ptr_gray_sync2_reg[ADDR_WIDTH]) && + (wr_ptr_cur_gray_reg[ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[ADDR_WIDTH-1]) && + (wr_ptr_cur_gray_reg[ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[ADDR_WIDTH-2:0])); +// empty when pointers match exactly +wire empty = rd_ptr_gray_reg == (FRAME_FIFO ? wr_ptr_gray_sync1_reg : wr_ptr_gray_sync2_reg); +// overflow within packet +wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) && + (wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0])); + +// control signals +reg write; +reg read; +reg store_output; + +reg drop_frame_reg = 1'b0, drop_frame_next; +reg overflow_reg = 1'b0, overflow_next; +reg bad_frame_reg = 1'b0, bad_frame_next; +reg good_frame_reg = 1'b0, good_frame_next; + +reg overflow_sync1_reg = 1'b0; +reg overflow_sync2_reg = 1'b0; +reg overflow_sync3_reg = 1'b0; +reg overflow_sync4_reg = 1'b0; +reg bad_frame_sync1_reg = 1'b0; +reg bad_frame_sync2_reg = 1'b0; +reg bad_frame_sync3_reg = 1'b0; +reg bad_frame_sync4_reg = 1'b0; +reg good_frame_sync1_reg = 1'b0; +reg good_frame_sync2_reg = 1'b0; +reg good_frame_sync3_reg = 1'b0; +reg good_frame_sync4_reg = 1'b0; + +assign s_axis_tready = (FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full) && !s_rst_sync3_reg; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = m_axis_tvalid_reg; + +assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +assign s_status_overflow = overflow_reg; +assign s_status_bad_frame = bad_frame_reg; +assign s_status_good_frame = good_frame_reg; + +assign m_status_overflow = overflow_sync3_reg ^ overflow_sync4_reg; +assign m_status_bad_frame = bad_frame_sync3_reg ^ bad_frame_sync4_reg; +assign m_status_good_frame = good_frame_sync3_reg ^ good_frame_sync4_reg; + +// reset synchronization +always @(posedge s_clk or posedge async_rst) begin + if (async_rst) begin + s_rst_sync1_reg <= 1'b1; + s_rst_sync2_reg <= 1'b1; + s_rst_sync3_reg <= 1'b1; + end else begin + s_rst_sync1_reg <= 1'b0; + s_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg; + s_rst_sync3_reg <= s_rst_sync2_reg; + end +end + +always @(posedge m_clk or posedge async_rst) begin + if (async_rst) begin + m_rst_sync1_reg <= 1'b1; + m_rst_sync2_reg <= 1'b1; + m_rst_sync3_reg <= 1'b1; + end else begin + m_rst_sync1_reg <= 1'b0; + m_rst_sync2_reg <= s_rst_sync1_reg || m_rst_sync1_reg; + m_rst_sync3_reg <= m_rst_sync2_reg; + end +end + +// Write logic +always @* begin + write = 1'b0; + + drop_frame_next = drop_frame_reg; + overflow_next = 1'b0; + bad_frame_next = 1'b0; + good_frame_next = 1'b0; + + wr_ptr_next = wr_ptr_reg; + wr_ptr_cur_next = wr_ptr_cur_reg; + wr_ptr_gray_next = wr_ptr_gray_reg; + wr_ptr_sync_gray_next = wr_ptr_sync_gray_reg; + wr_ptr_cur_gray_next = wr_ptr_cur_gray_reg; + + wr_ptr_update_valid_next = wr_ptr_update_valid_reg; + wr_ptr_update_next = wr_ptr_update_reg; + + if (FRAME_FIFO && wr_ptr_update_valid_reg) begin + // have updated pointer to sync + if (wr_ptr_update_next == wr_ptr_update_ack_sync2_reg) begin + // no sync in progress; sync update + wr_ptr_update_valid_next = 1'b0; + wr_ptr_sync_gray_next = wr_ptr_gray_reg; + wr_ptr_update_next = !wr_ptr_update_ack_sync2_reg; + end + end + + if (s_axis_tready && s_axis_tvalid) begin + // transfer in + if (!FRAME_FIFO) begin + // normal FIFO mode + write = 1'b1; + wr_ptr_next = wr_ptr_reg + 1; + wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1); + end else if (full_cur || full_wr || drop_frame_reg) begin + // full, packet overflow, or currently dropping frame + // drop frame + drop_frame_next = 1'b1; + if (s_axis_tlast) begin + // end of frame, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1); + drop_frame_next = 1'b0; + overflow_next = 1'b1; + end + end else begin + write = 1'b1; + wr_ptr_cur_next = wr_ptr_cur_reg + 1; + wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1); + if (s_axis_tlast) begin + // end of frame + if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin + // bad packet, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + wr_ptr_cur_gray_next = wr_ptr_cur_next ^ (wr_ptr_cur_next >> 1); + bad_frame_next = 1'b1; + end else begin + // good packet, update write pointer + wr_ptr_next = wr_ptr_cur_reg + 1; + wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1); + + if (wr_ptr_update_next == wr_ptr_update_ack_sync2_reg) begin + // no sync in progress; sync update + wr_ptr_update_valid_next = 1'b0; + wr_ptr_sync_gray_next = wr_ptr_gray_next; + wr_ptr_update_next = !wr_ptr_update_ack_sync2_reg; + end else begin + // sync in progress; flag it for later + wr_ptr_update_valid_next = 1'b1; + end + + good_frame_next = 1'b1; + end + end + end + end +end + +always @(posedge s_clk) begin + if (s_rst_sync3_reg) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_sync_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + + wr_ptr_update_valid_reg <= 1'b0; + wr_ptr_update_reg <= 1'b0; + + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + end else begin + wr_ptr_reg <= wr_ptr_next; + wr_ptr_cur_reg <= wr_ptr_cur_next; + wr_ptr_gray_reg <= wr_ptr_gray_next; + wr_ptr_sync_gray_reg <= wr_ptr_sync_gray_next; + wr_ptr_cur_gray_reg <= wr_ptr_cur_gray_next; + + wr_ptr_update_valid_reg <= wr_ptr_update_valid_next; + wr_ptr_update_reg <= wr_ptr_update_next; + + drop_frame_reg <= drop_frame_next; + overflow_reg <= overflow_next; + bad_frame_reg <= bad_frame_next; + good_frame_reg <= good_frame_next; + end + + if (FRAME_FIFO) begin + wr_addr_reg <= wr_ptr_cur_next; + end else begin + wr_addr_reg <= wr_ptr_next; + end + + if (write) begin + mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis; + end +end + +// pointer synchronization +always @(posedge s_clk) begin + if (s_rst_sync3_reg) begin + rd_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}}; + rd_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_update_ack_sync1_reg <= 1'b0; + wr_ptr_update_ack_sync2_reg <= 1'b0; + end else begin + rd_ptr_gray_sync1_reg <= rd_ptr_gray_reg; + rd_ptr_gray_sync2_reg <= rd_ptr_gray_sync1_reg; + wr_ptr_update_ack_sync1_reg <= wr_ptr_update_sync3_reg; + wr_ptr_update_ack_sync2_reg <= wr_ptr_update_ack_sync1_reg; + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + wr_ptr_gray_sync1_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_sync2_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_update_sync1_reg <= 1'b0; + wr_ptr_update_sync2_reg <= 1'b0; + wr_ptr_update_sync3_reg <= 1'b0; + end else begin + if (!FRAME_FIFO) begin + wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg; + end else if (wr_ptr_update_sync2_reg ^ wr_ptr_update_sync3_reg) begin + wr_ptr_gray_sync1_reg <= wr_ptr_sync_gray_reg; + end + wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg; + wr_ptr_update_sync1_reg <= wr_ptr_update_reg; + wr_ptr_update_sync2_reg <= wr_ptr_update_sync1_reg; + wr_ptr_update_sync3_reg <= wr_ptr_update_sync2_reg; + end +end + +// status synchronization +always @(posedge s_clk) begin + if (s_rst_sync3_reg) begin + overflow_sync1_reg <= 1'b0; + bad_frame_sync1_reg <= 1'b0; + good_frame_sync1_reg <= 1'b0; + end else begin + overflow_sync1_reg <= overflow_sync1_reg ^ overflow_reg; + bad_frame_sync1_reg <= bad_frame_sync1_reg ^ bad_frame_reg; + good_frame_sync1_reg <= good_frame_sync1_reg ^ good_frame_reg; + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + overflow_sync2_reg <= 1'b0; + overflow_sync3_reg <= 1'b0; + overflow_sync4_reg <= 1'b0; + bad_frame_sync2_reg <= 1'b0; + bad_frame_sync3_reg <= 1'b0; + bad_frame_sync4_reg <= 1'b0; + good_frame_sync2_reg <= 1'b0; + good_frame_sync3_reg <= 1'b0; + good_frame_sync4_reg <= 1'b0; + end else begin + overflow_sync2_reg <= overflow_sync1_reg; + overflow_sync3_reg <= overflow_sync2_reg; + overflow_sync4_reg <= overflow_sync3_reg; + bad_frame_sync2_reg <= bad_frame_sync1_reg; + bad_frame_sync3_reg <= bad_frame_sync2_reg; + bad_frame_sync4_reg <= bad_frame_sync3_reg; + good_frame_sync2_reg <= good_frame_sync1_reg; + good_frame_sync3_reg <= good_frame_sync2_reg; + good_frame_sync4_reg <= good_frame_sync3_reg; + end +end + +// Read logic +always @* begin + read = 1'b0; + + rd_ptr_next = rd_ptr_reg; + rd_ptr_gray_next = rd_ptr_gray_reg; + + mem_read_data_valid_next = mem_read_data_valid_reg; + + if (store_output || !mem_read_data_valid_reg) begin + // output data not valid OR currently being transferred + if (!empty) begin + // not empty, perform read + read = 1'b1; + mem_read_data_valid_next = 1'b1; + rd_ptr_next = rd_ptr_reg + 1; + rd_ptr_gray_next = rd_ptr_next ^ (rd_ptr_next >> 1); + end else begin + // empty, invalidate + mem_read_data_valid_next = 1'b0; + end + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + rd_ptr_gray_reg <= {ADDR_WIDTH+1{1'b0}}; + mem_read_data_valid_reg <= 1'b0; + end else begin + rd_ptr_reg <= rd_ptr_next; + rd_ptr_gray_reg <= rd_ptr_gray_next; + mem_read_data_valid_reg <= mem_read_data_valid_next; + end + + rd_addr_reg <= rd_ptr_next; + + if (read) begin + mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]]; + end +end + +// Output register +always @* begin + store_output = 1'b0; + + m_axis_tvalid_next = m_axis_tvalid_reg; + + if (m_axis_tready || !m_axis_tvalid) begin + store_output = 1'b1; + m_axis_tvalid_next = mem_read_data_valid_reg; + end +end + +always @(posedge m_clk) begin + if (m_rst_sync3_reg) begin + m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + end + + if (store_output) begin + m_axis_reg <= mem_read_data_reg; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v b/corundum/lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v new file mode 100644 index 0000000000000000000000000000000000000000..a22f1cae66a00528134498c125d530c7fdce45d9 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_async_fifo_adapter.v @@ -0,0 +1,348 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream asynchronous FIFO with width converter + */ +module axis_async_fifo_adapter # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of input AXI stream interface in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal on input interface + // If disabled, tkeep assumed to be 1'b1 + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on input interface + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interface in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal on output interface + // If disabled, tkeep assumed to be 1'b1 + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on output interface + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames marked bad + // Requires FRAME_FIFO set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO set + parameter DROP_WHEN_FULL = 0 +) +( + /* + * AXI input + */ + input wire s_clk, + input wire s_rst, + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + input wire m_clk, + input wire m_rst, + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire s_status_overflow, + output wire s_status_bad_frame, + output wire s_status_good_frame, + output wire m_status_overflow, + output wire m_status_bad_frame, + output wire m_status_good_frame +); + +// force keep width to 1 when disabled +parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus word sizes (must be identical) +parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +// output bus is wider +parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT; +// total data and keep widths +parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] pre_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] pre_fifo_axis_tkeep; +wire pre_fifo_axis_tvalid; +wire pre_fifo_axis_tready; +wire pre_fifo_axis_tlast; +wire [ID_WIDTH-1:0] pre_fifo_axis_tid; +wire [DEST_WIDTH-1:0] pre_fifo_axis_tdest; +wire [USER_WIDTH-1:0] pre_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] post_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] post_fifo_axis_tkeep; +wire post_fifo_axis_tvalid; +wire post_fifo_axis_tready; +wire post_fifo_axis_tlast; +wire [ID_WIDTH-1:0] post_fifo_axis_tid; +wire [DEST_WIDTH-1:0] post_fifo_axis_tdest; +wire [USER_WIDTH-1:0] post_fifo_axis_tuser; + +generate + +if (M_KEEP_WIDTH == S_KEEP_WIDTH) begin + + // same width, no adapter needed + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + + +end else if (EXPAND_BUS) begin + + // output wider, adapt width before FIFO + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(s_clk), + .rst(s_rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(pre_fifo_axis_tdata), + .m_axis_tkeep(pre_fifo_axis_tkeep), + .m_axis_tvalid(pre_fifo_axis_tvalid), + .m_axis_tready(pre_fifo_axis_tready), + .m_axis_tlast(pre_fifo_axis_tlast), + .m_axis_tid(pre_fifo_axis_tid), + .m_axis_tdest(pre_fifo_axis_tdest), + .m_axis_tuser(pre_fifo_axis_tuser) + ); + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + +end else begin + + // input wider, adapt width after FIFO + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(m_clk), + .rst(m_rst), + // AXI input + .s_axis_tdata(post_fifo_axis_tdata), + .s_axis_tkeep(post_fifo_axis_tkeep), + .s_axis_tvalid(post_fifo_axis_tvalid), + .s_axis_tready(post_fifo_axis_tready), + .s_axis_tlast(post_fifo_axis_tlast), + .s_axis_tid(post_fifo_axis_tid), + .s_axis_tdest(post_fifo_axis_tdest), + .s_axis_tuser(post_fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) + ); + +end + +endgenerate + +axis_async_fifo #( + .DEPTH(DEPTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +fifo_inst ( + // Common reset + .async_rst(s_rst | m_rst), + // AXI input + .s_clk(s_clk), + .s_axis_tdata(pre_fifo_axis_tdata), + .s_axis_tkeep(pre_fifo_axis_tkeep), + .s_axis_tvalid(pre_fifo_axis_tvalid), + .s_axis_tready(pre_fifo_axis_tready), + .s_axis_tlast(pre_fifo_axis_tlast), + .s_axis_tid(pre_fifo_axis_tid), + .s_axis_tdest(pre_fifo_axis_tdest), + .s_axis_tuser(pre_fifo_axis_tuser), + // AXI output + .m_clk(m_clk), + .m_axis_tdata(post_fifo_axis_tdata), + .m_axis_tkeep(post_fifo_axis_tkeep), + .m_axis_tvalid(post_fifo_axis_tvalid), + .m_axis_tready(post_fifo_axis_tready), + .m_axis_tlast(post_fifo_axis_tlast), + .m_axis_tid(post_fifo_axis_tid), + .m_axis_tdest(post_fifo_axis_tdest), + .m_axis_tuser(post_fifo_axis_tuser), + // Status + .s_status_overflow(s_status_overflow), + .s_status_bad_frame(s_status_bad_frame), + .s_status_good_frame(s_status_good_frame), + .m_status_overflow(m_status_overflow), + .m_status_bad_frame(m_status_bad_frame), + .m_status_good_frame(m_status_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_broadcast.v b/corundum/lib/eth/lib/axis/rtl/axis_broadcast.v new file mode 100644 index 0000000000000000000000000000000000000000..d2f305fc4d0a9c7b10c9e7ddd598d2db9fbb1319 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_broadcast.v @@ -0,0 +1,191 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream broadcaster + */ +module axis_broadcast # +( + // Number of AXI stream outputs + parameter M_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +// datapath registers +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_tvalid_reg = {M_COUNT{1'b0}}, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// // datapath control +reg store_axis_input_to_output; +reg store_axis_input_to_temp; +reg store_axis_temp_to_output; + +assign s_axis_tready = s_axis_tready_reg; + +assign m_axis_tdata = {M_COUNT{m_axis_tdata_reg}}; +assign m_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = LAST_ENABLE ? {M_COUNT{m_axis_tlast_reg}} : {M_COUNT{1'b1}}; +assign m_axis_tid = ID_ENABLE ? {M_COUNT{m_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? {M_COUNT{m_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? {M_COUNT{m_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +wire s_axis_tready_early = ((m_axis_tready & m_axis_tvalid) == m_axis_tvalid) || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid || !s_axis_tvalid)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg & ~m_axis_tready; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_input_to_output = 1'b0; + store_axis_input_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (s_axis_tready_reg) begin + // input is ready + if (((m_axis_tready & m_axis_tvalid) == m_axis_tvalid) || !m_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = {M_COUNT{s_axis_tvalid}}; + store_axis_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_temp = 1'b1; + end + end else if ((m_axis_tready & m_axis_tvalid) == m_axis_tvalid) begin + // input is not ready, but output is ready + m_axis_tvalid_next = {M_COUNT{temp_m_axis_tvalid_reg}}; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + s_axis_tready_reg <= 1'b0; + m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + temp_m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + end else begin + s_axis_tready_reg <= s_axis_tready_early; + m_axis_tvalid_reg <= m_axis_tvalid_next; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_input_to_output) begin + m_axis_tdata_reg <= s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tkeep; + m_axis_tlast_reg <= s_axis_tlast; + m_axis_tid_reg <= s_axis_tid; + m_axis_tdest_reg <= s_axis_tdest; + m_axis_tuser_reg <= s_axis_tuser; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_input_to_temp) begin + temp_m_axis_tdata_reg <= s_axis_tdata; + temp_m_axis_tkeep_reg <= s_axis_tkeep; + temp_m_axis_tlast_reg <= s_axis_tlast; + temp_m_axis_tid_reg <= s_axis_tid; + temp_m_axis_tdest_reg <= s_axis_tdest; + temp_m_axis_tuser_reg <= s_axis_tuser; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_cobs_decode.v b/corundum/lib/eth/lib/axis/rtl/axis_cobs_decode.v new file mode 100644 index 0000000000000000000000000000000000000000..aed0b1ef77660a24fb295ad8b174bc88863b1054 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_cobs_decode.v @@ -0,0 +1,328 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream consistent overhead byte stuffing (COBS) decoder + */ +module axis_cobs_decode +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser +); + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_SEGMENT = 2'd1, + STATE_NEXT_SEGMENT = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [7:0] count_reg = 8'd0, count_next; +reg suppress_zero_reg = 1'b0, suppress_zero_next; + +reg [7:0] temp_tdata_reg = 8'd0, temp_tdata_next; +reg temp_tvalid_reg = 1'b0, temp_tvalid_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +assign s_axis_tready = s_axis_tready_reg; + +always @* begin + state_next = STATE_IDLE; + + count_next = count_reg; + suppress_zero_next = suppress_zero_reg; + + temp_tdata_next = temp_tdata_reg; + temp_tvalid_next = temp_tvalid_reg; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + s_axis_tready_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state + s_axis_tready_next = m_axis_tready_int_early || !temp_tvalid_reg; + + // output final word + m_axis_tdata_int = temp_tdata_reg; + m_axis_tvalid_int = temp_tvalid_reg; + m_axis_tlast_int = temp_tvalid_reg; + temp_tvalid_next = temp_tvalid_reg && !m_axis_tready_int_reg; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + // skip any leading zeros + if (s_axis_tdata != 8'd0) begin + // store count value and zero suppress + count_next = s_axis_tdata-1; + suppress_zero_next = (s_axis_tdata == 8'd255); + s_axis_tready_next = m_axis_tready_int_early; + if (s_axis_tdata == 8'd1) begin + // next byte will be count value + state_next = STATE_NEXT_SEGMENT; + end else begin + // next byte will be data + state_next = STATE_SEGMENT; + end + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_SEGMENT: begin + // receive segment + s_axis_tready_next = m_axis_tready_int_early; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + // store in temp register + temp_tdata_next = s_axis_tdata; + temp_tvalid_next = 1'b1; + // move temp to output + m_axis_tdata_int = temp_tdata_reg; + m_axis_tvalid_int = temp_tvalid_reg; + // decrement count + count_next = count_reg - 1; + if (s_axis_tdata == 8'd0) begin + // got a zero byte in a frame - mark it as an error and re-sync + temp_tvalid_next = 1'b0; + m_axis_tvalid_int = 1'b1; + m_axis_tuser_int = 1'b1; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end else if (s_axis_tlast) begin + // end of frame + if (count_reg == 8'd1 && !s_axis_tuser) begin + // end of frame indication at correct time, go to idle to output final byte + state_next = STATE_IDLE; + end else begin + // end of frame indication at invalid time or tuser assert, so mark as an error and re-sync + temp_tvalid_next = 1'b0; + m_axis_tvalid_int = 1'b1; + m_axis_tuser_int = 1'b1; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else if (count_reg == 8'd1) begin + // next byte will be count value + state_next = STATE_NEXT_SEGMENT; + end else begin + // next byte will be data + state_next = STATE_SEGMENT; + end + end else begin + state_next = STATE_SEGMENT; + end + end + STATE_NEXT_SEGMENT: begin + // next segment + s_axis_tready_next = m_axis_tready_int_early; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + // store zero in temp if not suppressed + temp_tdata_next = 8'd0; + temp_tvalid_next = !suppress_zero_reg; + // move temp to output + m_axis_tdata_int = temp_tdata_reg; + m_axis_tvalid_int = temp_tvalid_reg; + if (s_axis_tdata == 8'd0) begin + // got a zero byte delineating the end of the frame, so mark as such and re-sync + temp_tvalid_next = 1'b0; + m_axis_tuser_int = s_axis_tuser; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end else if (s_axis_tlast) begin + if (s_axis_tdata == 8'd1 && !s_axis_tuser) begin + // end of frame indication at correct time, go to idle to output final byte + state_next = STATE_IDLE; + end else begin + // end of frame indication at invalid time or tuser assert, so mark as an error and re-sync + temp_tvalid_next = 1'b0; + m_axis_tvalid_int = 1'b1; + m_axis_tuser_int = 1'b1; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + // otherwise, store count value and zero suppress + count_next = s_axis_tdata-1; + suppress_zero_next = (s_axis_tdata == 8'd255); + s_axis_tready_next = m_axis_tready_int_early; + if (s_axis_tdata == 8'd1) begin + // next byte will be count value + state_next = STATE_NEXT_SEGMENT; + end else begin + // next byte will be data + state_next = STATE_SEGMENT; + end + end + end else begin + state_next = STATE_NEXT_SEGMENT; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + temp_tvalid_reg <= 1'b0; + s_axis_tready_reg <= 1'b0; + end else begin + state_reg <= state_next; + temp_tvalid_reg <= temp_tvalid_next; + s_axis_tready_reg <= s_axis_tready_next; + end + + temp_tdata_reg <= temp_tdata_next; + + count_reg <= count_next; + suppress_zero_reg <= suppress_zero_next; +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_cobs_encode.v b/corundum/lib/eth/lib/axis/rtl/axis_cobs_encode.v new file mode 100644 index 0000000000000000000000000000000000000000..a0e24181ea843152a06b7966bc5c313f42ad2623 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_cobs_encode.v @@ -0,0 +1,506 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream consistent overhead byte stuffing (COBS) encoder + */ +module axis_cobs_encode # +( + // append zero for in band framing + parameter APPEND_ZERO = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser +); + +// state register +localparam [1:0] + INPUT_STATE_IDLE = 2'd0, + INPUT_STATE_SEGMENT = 2'd1, + INPUT_STATE_FINAL_ZERO = 2'd2, + INPUT_STATE_APPEND_ZERO = 2'd3; + +reg [1:0] input_state_reg = INPUT_STATE_IDLE, input_state_next; + +localparam [0:0] + OUTPUT_STATE_IDLE = 1'd0, + OUTPUT_STATE_SEGMENT = 1'd1; + +reg [0:0] output_state_reg = OUTPUT_STATE_IDLE, output_state_next; + +reg [7:0] input_count_reg = 8'd0, input_count_next; +reg [7:0] output_count_reg = 8'd0, output_count_next; +reg fail_frame_reg = 1'b0, fail_frame_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_mask; + +reg [7:0] code_fifo_in_tdata; +reg code_fifo_in_tvalid; +reg code_fifo_in_tlast; +reg code_fifo_in_tuser; +wire code_fifo_in_tready; + +wire [7:0] code_fifo_out_tdata; +wire code_fifo_out_tvalid; +wire code_fifo_out_tlast; +wire code_fifo_out_tuser; +reg code_fifo_out_tready; + +assign s_axis_tready = code_fifo_in_tready && data_fifo_in_tready && s_axis_tready_mask; + +axis_fifo #( + .DEPTH(256), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) +) +code_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(code_fifo_in_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(code_fifo_in_tvalid), + .s_axis_tready(code_fifo_in_tready), + .s_axis_tlast(code_fifo_in_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(code_fifo_in_tuser), + // AXI output + .m_axis_tdata(code_fifo_out_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(code_fifo_out_tvalid), + .m_axis_tready(code_fifo_out_tready), + .m_axis_tlast(code_fifo_out_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(code_fifo_out_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +reg [7:0] data_fifo_in_tdata; +reg data_fifo_in_tvalid; +reg data_fifo_in_tlast; +wire data_fifo_in_tready; + +wire [7:0] data_fifo_out_tdata; +wire data_fifo_out_tvalid; +wire data_fifo_out_tlast; +reg data_fifo_out_tready; + +axis_fifo #( + .DEPTH(256), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +data_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(data_fifo_in_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(data_fifo_in_tvalid), + .s_axis_tready(data_fifo_in_tready), + .s_axis_tlast(data_fifo_in_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + // AXI output + .m_axis_tdata(data_fifo_out_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(data_fifo_out_tvalid), + .m_axis_tready(data_fifo_out_tready), + .m_axis_tlast(data_fifo_out_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +always @* begin + input_state_next = INPUT_STATE_IDLE; + + input_count_next = input_count_reg; + + fail_frame_next = fail_frame_reg; + + s_axis_tready_mask = 1'b0; + + code_fifo_in_tdata = 8'd0; + code_fifo_in_tvalid = 1'b0; + code_fifo_in_tlast = 1'b0; + code_fifo_in_tuser = 1'b0; + + data_fifo_in_tdata = s_axis_tdata; + data_fifo_in_tvalid = 1'b0; + data_fifo_in_tlast = 1'b0; + + case (input_state_reg) + INPUT_STATE_IDLE: begin + // idle state + s_axis_tready_mask = 1'b1; + fail_frame_next = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + + if (s_axis_tdata == 8'd0 || (s_axis_tlast && s_axis_tuser)) begin + // got a zero or propagated error, so store a zero code + code_fifo_in_tdata = 8'd1; + code_fifo_in_tvalid = 1'b1; + if (s_axis_tlast) begin + // last byte, so close out the frame + fail_frame_next = s_axis_tuser; + input_state_next = INPUT_STATE_FINAL_ZERO; + end else begin + // return to idle to await next segment + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // got something other than a zero, so store it and init the segment counter + input_count_next = 8'd2; + data_fifo_in_tdata = s_axis_tdata; + data_fifo_in_tvalid = 1'b1; + if (s_axis_tlast) begin + // last byte, so store the code and close out the frame + code_fifo_in_tdata = 8'd2; + code_fifo_in_tvalid = 1'b1; + if (APPEND_ZERO) begin + // zero frame mode, need to add a zero code to end the frame + input_state_next = INPUT_STATE_APPEND_ZERO; + end else begin + // normal frame mode, close out the frame + data_fifo_in_tlast = 1'b1; + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // await more segment data + input_state_next = INPUT_STATE_SEGMENT; + end + end + end else begin + input_state_next = INPUT_STATE_IDLE; + end + end + INPUT_STATE_SEGMENT: begin + // encode segment + s_axis_tready_mask = 1'b1; + fail_frame_next = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + // valid input data + + if (s_axis_tdata == 8'd0 || (s_axis_tlast && s_axis_tuser)) begin + // got a zero or propagated error, so store the code + code_fifo_in_tdata = input_count_reg; + code_fifo_in_tvalid = 1'b1; + if (s_axis_tlast) begin + // last byte, so close out the frame + fail_frame_next = s_axis_tuser; + input_state_next = INPUT_STATE_FINAL_ZERO; + end else begin + // return to idle to await next segment + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // got something other than a zero, so store it and increment the segment counter + input_count_next = input_count_reg+1; + data_fifo_in_tdata = s_axis_tdata; + data_fifo_in_tvalid = 1'b1; + if (input_count_reg == 8'd254) begin + // 254 bytes in frame, so dump and reset counter + code_fifo_in_tdata = input_count_reg+1; + code_fifo_in_tvalid = 1'b1; + input_count_next = 8'd1; + end + if (s_axis_tlast) begin + // last byte, so store the code and close out the frame + code_fifo_in_tdata = input_count_reg+1; + code_fifo_in_tvalid = 1'b1; + if (APPEND_ZERO) begin + // zero frame mode, need to add a zero code to end the frame + input_state_next = INPUT_STATE_APPEND_ZERO; + end else begin + // normal frame mode, close out the frame + data_fifo_in_tlast = 1'b1; + input_state_next = INPUT_STATE_IDLE; + end + end else begin + // await more segment data + input_state_next = INPUT_STATE_SEGMENT; + end + end + end else begin + input_state_next = INPUT_STATE_SEGMENT; + end + end + INPUT_STATE_FINAL_ZERO: begin + // final zero code required + s_axis_tready_mask = 1'b0; + + if (code_fifo_in_tready) begin + // push a zero code and close out frame + if (fail_frame_reg) begin + code_fifo_in_tdata = 8'd2; + code_fifo_in_tuser = 1'b1; + end else begin + code_fifo_in_tdata = 8'd1; + end + code_fifo_in_tvalid = 1'b1; + if (APPEND_ZERO) begin + // zero frame mode, need to add a zero code to end the frame + input_state_next = INPUT_STATE_APPEND_ZERO; + end else begin + // normal frame mode, close out the frame + code_fifo_in_tlast = 1'b1; + fail_frame_next = 1'b0; + input_state_next = INPUT_STATE_IDLE; + end + end else begin + input_state_next = INPUT_STATE_FINAL_ZERO; + end + end + INPUT_STATE_APPEND_ZERO: begin + // append zero for zero framing + s_axis_tready_mask = 1'b0; + + if (code_fifo_in_tready) begin + // push frame termination code and close out frame + code_fifo_in_tdata = 8'd0; + code_fifo_in_tlast = 1'b1; + code_fifo_in_tuser = fail_frame_reg; + code_fifo_in_tvalid = 1'b1; + fail_frame_next = 1'b0; + input_state_next = INPUT_STATE_IDLE; + end else begin + input_state_next = INPUT_STATE_APPEND_ZERO; + end + end + endcase +end + +always @* begin + output_state_next = OUTPUT_STATE_IDLE; + + output_count_next = output_count_reg; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + code_fifo_out_tready = 1'b0; + + data_fifo_out_tready = 1'b0; + + case (output_state_reg) + OUTPUT_STATE_IDLE: begin + // idle state + + if (m_axis_tready_int_reg && code_fifo_out_tvalid) begin + // transfer out code byte and load counter + m_axis_tdata_int = code_fifo_out_tdata; + m_axis_tlast_int = code_fifo_out_tlast; + m_axis_tuser_int = code_fifo_out_tuser && code_fifo_out_tlast; + output_count_next = code_fifo_out_tdata-1; + m_axis_tvalid_int = 1'b1; + code_fifo_out_tready = 1'b1; + if (code_fifo_out_tdata == 8'd0 || code_fifo_out_tdata == 8'd1 || code_fifo_out_tuser) begin + // frame termination and zero codes will be followed by codes + output_state_next = OUTPUT_STATE_IDLE; + end else begin + // transfer out data + output_state_next = OUTPUT_STATE_SEGMENT; + end + end else begin + output_state_next = OUTPUT_STATE_IDLE; + end + end + OUTPUT_STATE_SEGMENT: begin + // segment output + + if (m_axis_tready_int_reg && data_fifo_out_tvalid) begin + // transfer out data byte and decrement counter + m_axis_tdata_int = data_fifo_out_tdata; + m_axis_tlast_int = data_fifo_out_tlast; + output_count_next = output_count_reg - 1; + m_axis_tvalid_int = 1'b1; + data_fifo_out_tready = 1'b1; + if (output_count_reg == 1'b1) begin + // done with segment, get a code byte next + output_state_next = OUTPUT_STATE_IDLE; + end else begin + // more data to transfer + output_state_next = OUTPUT_STATE_SEGMENT; + end + end else begin + output_state_next = OUTPUT_STATE_SEGMENT; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + input_state_reg <= INPUT_STATE_IDLE; + output_state_reg <= OUTPUT_STATE_IDLE; + end else begin + input_state_reg <= input_state_next; + output_state_reg <= output_state_next; + end + + input_count_reg <= input_count_next; + output_count_reg <= output_count_next; + fail_frame_reg <= fail_frame_next; +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_crosspoint.v b/corundum/lib/eth/lib/axis/rtl/axis_crosspoint.v new file mode 100644 index 0000000000000000000000000000000000000000..384889ec9a5f0341beccae29273b9fc9793d22eb --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_crosspoint.v @@ -0,0 +1,151 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream crosspoint + */ +module axis_crosspoint # +( + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Number of AXI stream outputs + parameter M_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire [M_COUNT*$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata_reg = {S_COUNT*DATA_WIDTH{1'b0}}; +reg [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep_reg = {S_COUNT*KEEP_WIDTH{1'b0}}; +reg [S_COUNT-1:0] s_axis_tvalid_reg = {S_COUNT{1'b0}}; +reg [S_COUNT-1:0] s_axis_tlast_reg = {S_COUNT{1'b0}}; +reg [S_COUNT*ID_WIDTH-1:0] s_axis_tid_reg = {S_COUNT*ID_WIDTH{1'b0}}; +reg [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest_reg = {S_COUNT*DEST_WIDTH{1'b0}}; +reg [S_COUNT*USER_WIDTH-1:0] s_axis_tuser_reg = {S_COUNT*USER_WIDTH{1'b0}}; + +reg [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata_reg = {M_COUNT*DATA_WIDTH{1'b0}}; +reg [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep_reg = {M_COUNT*KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_tvalid_reg = {M_COUNT{1'b0}}; +reg [M_COUNT-1:0] m_axis_tlast_reg = {M_COUNT{1'b0}}; +reg [M_COUNT*ID_WIDTH-1:0] m_axis_tid_reg = {M_COUNT*ID_WIDTH{1'b0}}; +reg [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest_reg = {M_COUNT*DEST_WIDTH{1'b0}}; +reg [M_COUNT*USER_WIDTH-1:0] m_axis_tuser_reg = {M_COUNT*USER_WIDTH{1'b0}}; + +reg [M_COUNT*CL_S_COUNT-1:0] select_reg = {M_COUNT*CL_S_COUNT{1'b0}}; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : {M_COUNT{1'b1}}; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {M_COUNT*USER_WIDTH{1'b0}}; + +integer i; + +always @(posedge clk) begin + if (rst) begin + s_axis_tvalid_reg <= {S_COUNT{1'b0}}; + m_axis_tvalid_reg <= {S_COUNT{1'b0}}; + select_reg <= {M_COUNT*CL_S_COUNT{1'b0}}; + end else begin + s_axis_tvalid_reg <= s_axis_tvalid; + for (i = 0; i < M_COUNT; i = i + 1) begin + m_axis_tvalid_reg[i] = s_axis_tvalid_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]]; + end + select_reg <= select; + end + + s_axis_tdata_reg <= s_axis_tdata; + s_axis_tkeep_reg <= s_axis_tkeep; + s_axis_tlast_reg <= s_axis_tlast; + s_axis_tid_reg <= s_axis_tid; + s_axis_tdest_reg <= s_axis_tdest; + s_axis_tuser_reg <= s_axis_tuser; + + for (i = 0; i < M_COUNT; i = i + 1) begin + m_axis_tdata_reg[i*DATA_WIDTH +: DATA_WIDTH] <= s_axis_tdata_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*DATA_WIDTH +: DATA_WIDTH]; + m_axis_tkeep_reg[i*KEEP_WIDTH +: KEEP_WIDTH] <= s_axis_tkeep_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*KEEP_WIDTH +: KEEP_WIDTH]; + m_axis_tlast_reg[i] <= s_axis_tlast_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]]; + m_axis_tid_reg[i*ID_WIDTH +: ID_WIDTH] <= s_axis_tid_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*ID_WIDTH +: ID_WIDTH]; + m_axis_tdest_reg[i*DEST_WIDTH +: DEST_WIDTH] <= s_axis_tdest_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*DEST_WIDTH +: DEST_WIDTH]; + m_axis_tuser_reg[i*USER_WIDTH +: USER_WIDTH] <= s_axis_tuser_reg[select_reg[i*CL_S_COUNT +: CL_S_COUNT]*USER_WIDTH +: USER_WIDTH]; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_crosspoint_wrap.py b/corundum/lib/eth/lib/axis/rtl/axis_crosspoint_wrap.py new file mode 100755 index 0000000000000000000000000000000000000000..34b664156eaea1b039108b95e942410357463cbf --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_crosspoint_wrap.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream crosspoint wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=[4], nargs='+', help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + if type(ports) is int: + m = n = ports + elif len(ports) == 1: + m = n = ports[0] + else: + m, n = ports + + if name is None: + name = "axis_crosspoint_wrap_{0}x{1}".format(m, n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0}x{1} port AXI stream crosspoint wrapper {2}...".format(m, n, name)) + + cm = int(math.ceil(math.log(m, 2))) + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream {{m}}x{{n}} crosspoint (wrapper) + */ +module {{name}} # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(m) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream outputs + */ +{%- for p in range(n) %} + output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata, + output wire [KEEP_WIDTH-1:0] m{{'%02d'%p}}_axis_tkeep, + output wire m{{'%02d'%p}}_axis_tvalid, + output wire m{{'%02d'%p}}_axis_tlast, + output wire [ID_WIDTH-1:0] m{{'%02d'%p}}_axis_tid, + output wire [DEST_WIDTH-1:0] m{{'%02d'%p}}_axis_tdest, + output wire [USER_WIDTH-1:0] m{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * Control + */ +{%- for p in range(n) %} + input wire [{{cm-1}}:0] m{{'%02d'%p}}_select{% if not loop.last %},{% endif %} +{%- endfor %} +); + +axis_crosspoint #( + .S_COUNT({{m}}), + .M_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_crosspoint_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tkeep({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tlast({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tdest({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tuser({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // Control + .select({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_select{% if not loop.last %}, {% endif %}{% endfor %} }) +); + +endmodule + +""") + + output_file.write(t.render( + m=m, + n=n, + cm=cm, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/corundum/lib/eth/lib/axis/rtl/axis_demux.v b/corundum/lib/eth/lib/axis/rtl/axis_demux.v new file mode 100644 index 0000000000000000000000000000000000000000..011db4b8d32101a32739bff4b48b6d5c32df4ec5 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_demux.v @@ -0,0 +1,266 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream demultiplexer + */ +module axis_demux # +( + // Number of AXI stream outputs + parameter M_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg [M_COUNT-1:0] m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg && enable; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_axis_tready_next = 1'b0; + + if (s_axis_tvalid && s_axis_tready) begin + // end of frame detection + if (s_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_axis_tvalid && s_axis_tready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + if (!(s_axis_tready && s_axis_tvalid && s_axis_tlast)) begin + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = 1'b1; + end + end + + s_axis_tready_next = (m_axis_tready_int_early || drop_ctl); + + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = (s_axis_tvalid && s_axis_tready && !drop_ctl) << select_ctl; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_axis_tready_reg <= 1'b0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_axis_tready_reg <= s_axis_tready_next; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_tvalid_reg = {M_COUNT{1'b0}}, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = {M_COUNT{m_axis_tdata_reg}}; +assign m_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = {M_COUNT{m_axis_tlast_reg}}; +assign m_axis_tid = ID_ENABLE ? {M_COUNT{m_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? {M_COUNT{m_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? {M_COUNT{m_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = (m_axis_tready & m_axis_tvalid) || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if ((m_axis_tready & m_axis_tvalid) || !m_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready & m_axis_tvalid) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = {M_COUNT{1'b0}}; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= {M_COUNT{1'b0}}; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_demux_wrap.py b/corundum/lib/eth/lib/axis/rtl/axis_demux_wrap.py new file mode 100755 index 0000000000000000000000000000000000000000..49e70e9eb642532fd76b36fdc1ae5dcc1dbc0e62 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_demux_wrap.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream demux wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_demux_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream demux wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream {{n}} port demux (wrapper) + */ +module {{name}} # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ +{%- for p in range(n) %} + output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata, + output wire [KEEP_WIDTH-1:0] m{{'%02d'%p}}_axis_tkeep, + output wire m{{'%02d'%p}}_axis_tvalid, + input wire m{{'%02d'%p}}_axis_tready, + output wire m{{'%02d'%p}}_axis_tlast, + output wire [ID_WIDTH-1:0] m{{'%02d'%p}}_axis_tid, + output wire [DEST_WIDTH-1:0] m{{'%02d'%p}}_axis_tdest, + output wire [USER_WIDTH-1:0] m{{'%02d'%p}}_axis_tuser, +{% endfor -%} + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [{{cn-1}}:0] select +); + +axis_demux #( + .M_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_demux_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tkeep({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tlast({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tdest({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tuser({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // Control + .enable(enable), + .drop(drop), + .select(select) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/corundum/lib/eth/lib/axis/rtl/axis_fifo.v b/corundum/lib/eth/lib/axis/rtl/axis_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..93f4190613c9bcbb23c664430f6e575702bad8af --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_fifo.v @@ -0,0 +1,342 @@ +/* + +Copyright (c) 2013-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream FIFO + */ +module axis_fifo # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames marked bad + // Requires FRAME_FIFO set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO set + parameter DROP_WHEN_FULL = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire status_overflow, + output wire status_bad_frame, + output wire status_good_frame +); + +parameter ADDR_WIDTH = (KEEP_ENABLE && KEEP_WIDTH > 1) ? $clog2(DEPTH/KEEP_WIDTH) : $clog2(DEPTH); + +// check configuration +initial begin + if (FRAME_FIFO && !LAST_ENABLE) begin + $error("Error: FRAME_FIFO set requires LAST_ENABLE set (instance %m)"); + $finish; + end + + if (DROP_BAD_FRAME && !FRAME_FIFO) begin + $error("Error: DROP_BAD_FRAME set requires FRAME_FIFO set (instance %m)"); + $finish; + end + + if (DROP_WHEN_FULL && !FRAME_FIFO) begin + $error("Error: DROP_WHEN_FULL set requires FRAME_FIFO set (instance %m)"); + $finish; + end + + if (DROP_BAD_FRAME && (USER_BAD_FRAME_MASK & {USER_WIDTH{1'b1}}) == 0) begin + $error("Error: Invalid USER_BAD_FRAME_MASK value (instance %m)"); + $finish; + end +end + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next; +reg [ADDR_WIDTH:0] wr_addr_reg = {ADDR_WIDTH+1{1'b0}}; +reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [ADDR_WIDTH:0] rd_addr_reg = {ADDR_WIDTH+1{1'b0}}; + +reg [WIDTH-1:0] mem[(2**ADDR_WIDTH)-1:0]; +reg [WIDTH-1:0] mem_read_data_reg; +reg mem_read_data_valid_reg = 1'b0, mem_read_data_valid_next; + +wire [WIDTH-1:0] s_axis; + +reg [WIDTH-1:0] m_axis_reg; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + +// full when first MSB different but rest same +wire full = ((wr_ptr_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) && + (wr_ptr_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0])); +wire full_cur = ((wr_ptr_cur_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) && + (wr_ptr_cur_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire empty = wr_ptr_reg == rd_ptr_reg; +// overflow within packet +wire full_wr = ((wr_ptr_reg[ADDR_WIDTH] != wr_ptr_cur_reg[ADDR_WIDTH]) && + (wr_ptr_reg[ADDR_WIDTH-1:0] == wr_ptr_cur_reg[ADDR_WIDTH-1:0])); + +// control signals +reg write; +reg read; +reg store_output; + +reg drop_frame_reg = 1'b0, drop_frame_next; +reg overflow_reg = 1'b0, overflow_next; +reg bad_frame_reg = 1'b0, bad_frame_next; +reg good_frame_reg = 1'b0, good_frame_next; + +assign s_axis_tready = FRAME_FIFO ? (!full_cur || full_wr || DROP_WHEN_FULL) : !full; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = m_axis_tvalid_reg; + +assign m_axis_tdata = m_axis_reg[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_reg[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis_reg[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis_reg[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_reg[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_reg[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +assign status_overflow = overflow_reg; +assign status_bad_frame = bad_frame_reg; +assign status_good_frame = good_frame_reg; + +// Write logic +always @* begin + write = 1'b0; + + drop_frame_next = drop_frame_reg; + overflow_next = 1'b0; + bad_frame_next = 1'b0; + good_frame_next = 1'b0; + + wr_ptr_next = wr_ptr_reg; + wr_ptr_cur_next = wr_ptr_cur_reg; + + if (s_axis_tready && s_axis_tvalid) begin + // transfer in + if (!FRAME_FIFO) begin + // normal FIFO mode + write = 1'b1; + wr_ptr_next = wr_ptr_reg + 1; + end else if (full_cur || full_wr || drop_frame_reg) begin + // full, packet overflow, or currently dropping frame + // drop frame + drop_frame_next = 1'b1; + if (s_axis_tlast) begin + // end of frame, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + drop_frame_next = 1'b0; + overflow_next = 1'b1; + end + end else begin + write = 1'b1; + wr_ptr_cur_next = wr_ptr_cur_reg + 1; + if (s_axis_tlast) begin + // end of frame + if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(s_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin + // bad packet, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + bad_frame_next = 1'b1; + end else begin + // good packet, update write pointer + wr_ptr_next = wr_ptr_cur_reg + 1; + good_frame_next = 1'b1; + end + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; + + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + end else begin + wr_ptr_reg <= wr_ptr_next; + wr_ptr_cur_reg <= wr_ptr_cur_next; + + drop_frame_reg <= drop_frame_next; + overflow_reg <= overflow_next; + bad_frame_reg <= bad_frame_next; + good_frame_reg <= good_frame_next; + end + + if (FRAME_FIFO) begin + wr_addr_reg <= wr_ptr_cur_next; + end else begin + wr_addr_reg <= wr_ptr_next; + end + + if (write) begin + mem[wr_addr_reg[ADDR_WIDTH-1:0]] <= s_axis; + end +end + +// Read logic +always @* begin + read = 1'b0; + + rd_ptr_next = rd_ptr_reg; + + mem_read_data_valid_next = mem_read_data_valid_reg; + + if (store_output || !mem_read_data_valid_reg) begin + // output data not valid OR currently being transferred + if (!empty) begin + // not empty, perform read + read = 1'b1; + mem_read_data_valid_next = 1'b1; + rd_ptr_next = rd_ptr_reg + 1; + end else begin + // empty, invalidate + mem_read_data_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + mem_read_data_valid_reg <= 1'b0; + end else begin + rd_ptr_reg <= rd_ptr_next; + mem_read_data_valid_reg <= mem_read_data_valid_next; + end + + rd_addr_reg <= rd_ptr_next; + + if (read) begin + mem_read_data_reg <= mem[rd_addr_reg[ADDR_WIDTH-1:0]]; + end +end + +// Output register +always @* begin + store_output = 1'b0; + + m_axis_tvalid_next = m_axis_tvalid_reg; + + if (m_axis_tready || !m_axis_tvalid) begin + store_output = 1'b1; + m_axis_tvalid_next = mem_read_data_valid_reg; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + end + + if (store_output) begin + m_axis_reg <= mem_read_data_reg; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_fifo_adapter.v b/corundum/lib/eth/lib/axis/rtl/axis_fifo_adapter.v new file mode 100644 index 0000000000000000000000000000000000000000..94c390c967e8eb32d462de01170a524c8e217076 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_fifo_adapter.v @@ -0,0 +1,338 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream FIFO with width converter + */ +module axis_fifo_adapter # +( + // FIFO depth in words + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter DEPTH = 4096, + // Width of input AXI stream interface in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal on input interface + // If disabled, tkeep assumed to be 1'b1 + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on input interface + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interface in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal on output interface + // If disabled, tkeep assumed to be 1'b1 + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) on output interface + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Frame FIFO mode - operate on frames instead of cycles + // When set, m_axis_tvalid will not be deasserted within a frame + // Requires LAST_ENABLE set + parameter FRAME_FIFO = 0, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames marked bad + // Requires FRAME_FIFO set + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + // Requires FRAME_FIFO set + parameter DROP_WHEN_FULL = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire status_overflow, + output wire status_bad_frame, + output wire status_good_frame +); + +// force keep width to 1 when disabled +parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus word sizes (must be identical) +parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +// output bus is wider +parameter EXPAND_BUS = M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT; +// total data and keep widths +parameter DATA_WIDTH = EXPAND_BUS ? M_DATA_WIDTH : S_DATA_WIDTH; +parameter KEEP_WIDTH = EXPAND_BUS ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT; + +// bus width assertions +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] pre_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] pre_fifo_axis_tkeep; +wire pre_fifo_axis_tvalid; +wire pre_fifo_axis_tready; +wire pre_fifo_axis_tlast; +wire [ID_WIDTH-1:0] pre_fifo_axis_tid; +wire [DEST_WIDTH-1:0] pre_fifo_axis_tdest; +wire [USER_WIDTH-1:0] pre_fifo_axis_tuser; + +wire [DATA_WIDTH-1:0] post_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] post_fifo_axis_tkeep; +wire post_fifo_axis_tvalid; +wire post_fifo_axis_tready; +wire post_fifo_axis_tlast; +wire [ID_WIDTH-1:0] post_fifo_axis_tid; +wire [DEST_WIDTH-1:0] post_fifo_axis_tdest; +wire [USER_WIDTH-1:0] post_fifo_axis_tuser; + +generate + +if (M_KEEP_WIDTH_INT == S_KEEP_WIDTH_INT) begin + + // same width, no adapter needed + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + +end else if (EXPAND_BUS) begin + + // output wider, adapt width before FIFO + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(pre_fifo_axis_tdata), + .m_axis_tkeep(pre_fifo_axis_tkeep), + .m_axis_tvalid(pre_fifo_axis_tvalid), + .m_axis_tready(pre_fifo_axis_tready), + .m_axis_tlast(pre_fifo_axis_tlast), + .m_axis_tid(pre_fifo_axis_tid), + .m_axis_tdest(pre_fifo_axis_tdest), + .m_axis_tuser(pre_fifo_axis_tuser) + ); + + assign m_axis_tdata = post_fifo_axis_tdata; + assign m_axis_tkeep = post_fifo_axis_tkeep; + assign m_axis_tvalid = post_fifo_axis_tvalid; + assign post_fifo_axis_tready = m_axis_tready; + assign m_axis_tlast = post_fifo_axis_tlast; + assign m_axis_tid = post_fifo_axis_tid; + assign m_axis_tdest = post_fifo_axis_tdest; + assign m_axis_tuser = post_fifo_axis_tuser; + +end else begin + + // input wider, adapt width after FIFO + + assign pre_fifo_axis_tdata = s_axis_tdata; + assign pre_fifo_axis_tkeep = s_axis_tkeep; + assign pre_fifo_axis_tvalid = s_axis_tvalid; + assign s_axis_tready = pre_fifo_axis_tready; + assign pre_fifo_axis_tlast = s_axis_tlast; + assign pre_fifo_axis_tid = s_axis_tid; + assign pre_fifo_axis_tdest = s_axis_tdest; + assign pre_fifo_axis_tuser = s_axis_tuser; + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(post_fifo_axis_tdata), + .s_axis_tkeep(post_fifo_axis_tkeep), + .s_axis_tvalid(post_fifo_axis_tvalid), + .s_axis_tready(post_fifo_axis_tready), + .s_axis_tlast(post_fifo_axis_tlast), + .s_axis_tid(post_fifo_axis_tid), + .s_axis_tdest(post_fifo_axis_tdest), + .s_axis_tuser(post_fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser) + ); + +end + +endgenerate + +axis_fifo #( + .DEPTH(DEPTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(EXPAND_BUS ? M_KEEP_ENABLE : S_KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(FRAME_FIFO), + .USER_BAD_FRAME_VALUE(USER_BAD_FRAME_VALUE), + .USER_BAD_FRAME_MASK(USER_BAD_FRAME_MASK), + .DROP_BAD_FRAME(DROP_BAD_FRAME), + .DROP_WHEN_FULL(DROP_WHEN_FULL) +) +fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(pre_fifo_axis_tdata), + .s_axis_tkeep(pre_fifo_axis_tkeep), + .s_axis_tvalid(pre_fifo_axis_tvalid), + .s_axis_tready(pre_fifo_axis_tready), + .s_axis_tlast(pre_fifo_axis_tlast), + .s_axis_tid(pre_fifo_axis_tid), + .s_axis_tdest(pre_fifo_axis_tdest), + .s_axis_tuser(pre_fifo_axis_tuser), + // AXI output + .m_axis_tdata(post_fifo_axis_tdata), + .m_axis_tkeep(post_fifo_axis_tkeep), + .m_axis_tvalid(post_fifo_axis_tvalid), + .m_axis_tready(post_fifo_axis_tready), + .m_axis_tlast(post_fifo_axis_tlast), + .m_axis_tid(post_fifo_axis_tid), + .m_axis_tdest(post_fifo_axis_tdest), + .m_axis_tuser(post_fifo_axis_tuser), + // Status + .status_overflow(status_overflow), + .status_bad_frame(status_bad_frame), + .status_good_frame(status_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_frame_join.v b/corundum/lib/eth/lib/axis/rtl/axis_frame_join.v new file mode 100644 index 0000000000000000000000000000000000000000..018d8cc6f30ed7e694e6e7d17b1c22534fd859d9 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_frame_join.v @@ -0,0 +1,327 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream frame joiner + */ +module axis_frame_join # +( + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Prepend data with tag + parameter TAG_ENABLE = 1, + // Tag field width + parameter TAG_WIDTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Configuration + */ + input wire [TAG_WIDTH-1:0] tag, + + /* + * Status signals + */ + output wire busy +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +parameter TAG_WORD_WIDTH = (TAG_WIDTH + DATA_WIDTH - 1) / DATA_WIDTH; +parameter CL_TAG_WORD_WIDTH = $clog2(TAG_WORD_WIDTH); + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE_TAG = 2'd1, + STATE_TRANSFER = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [CL_TAG_WORD_WIDTH-1:0] frame_ptr_reg = {CL_TAG_WORD_WIDTH{1'b0}}, frame_ptr_next; +reg [CL_S_COUNT-1:0] port_sel_reg = {CL_S_COUNT{1'b0}}, port_sel_next; + +reg busy_reg = 1'b0, busy_next; + +reg output_tuser_reg = 1'b0, output_tuser_next; + +reg [S_COUNT-1:0] s_axis_tready_reg = {S_COUNT{1'b0}}, s_axis_tready_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; + +wire [DATA_WIDTH-1:0] input_tdata = s_axis_tdata[port_sel_reg*DATA_WIDTH +: DATA_WIDTH]; +wire input_tvalid = s_axis_tvalid[port_sel_reg]; +wire input_tlast = s_axis_tlast[port_sel_reg]; +wire input_tuser = s_axis_tuser[port_sel_reg]; + +always @* begin + state_next = STATE_IDLE; + + frame_ptr_next = frame_ptr_reg; + port_sel_next = port_sel_reg; + + s_axis_tready_next = {S_COUNT{1'b0}}; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + output_tuser_next = output_tuser_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = {CL_TAG_WORD_WIDTH{1'b0}}; + port_sel_next = {CL_S_COUNT{1'b0}}; + output_tuser_next = 1'b0; + + if (TAG_ENABLE) begin + // next cycle if started will send tag, so do not enable input + s_axis_tready_next = 1'b0; + end else begin + // next cycle if started will send data, so enable input + s_axis_tready_next = m_axis_tready_int_early; + end + + if (s_axis_tvalid) begin + // input 0 valid; start transferring data + if (TAG_ENABLE) begin + // tag enabled, so transmit it + if (m_axis_tready_int_reg) begin + // output is ready, so short-circuit first tag word + frame_ptr_next = 1; + m_axis_tdata_int = tag; + m_axis_tvalid_int = 1'b1; + end + state_next = STATE_WRITE_TAG; + end else begin + // tag disabled, so transmit data + if (m_axis_tready_int_reg) begin + // output is ready, so short-circuit first data word + m_axis_tdata_int = s_axis_tdata; + m_axis_tvalid_int = 1'b1; + end + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_TAG: begin + // write tag data + if (m_axis_tready_int_reg) begin + // output ready, so send tag word + state_next = STATE_WRITE_TAG; + frame_ptr_next = frame_ptr_reg + 1; + m_axis_tvalid_int = 1'b1; + + m_axis_tdata_int = tag >> frame_ptr_reg*DATA_WIDTH; + if (frame_ptr_reg == TAG_WORD_WIDTH-1) begin + s_axis_tready_next = m_axis_tready_int_early << 0; + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_WRITE_TAG; + end + end + STATE_TRANSFER: begin + // transfer input data + + // set ready for current input + s_axis_tready_next = m_axis_tready_int_early << port_sel_reg; + + if (input_tvalid && m_axis_tready_int_reg) begin + // output ready, transfer byte + state_next = STATE_TRANSFER; + m_axis_tdata_int = input_tdata; + m_axis_tvalid_int = input_tvalid; + + if (input_tlast) begin + // last flag received, switch to next port + port_sel_next = port_sel_reg + 1; + // save tuser - assert tuser out if ANY tuser asserts received + output_tuser_next = output_tuser_next | input_tuser; + // disable input + s_axis_tready_next = {S_COUNT{1'b0}}; + + if (S_COUNT == 1 || port_sel_reg == S_COUNT-1) begin + // last port - send tlast and tuser and revert to idle + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = output_tuser_next; + state_next = STATE_IDLE; + end else begin + // otherwise, disable enable next port + s_axis_tready_next = m_axis_tready_int_early << port_sel_next; + end + end + end else begin + state_next = STATE_TRANSFER; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= {CL_TAG_WORD_WIDTH{1'b0}}; + port_sel_reg <= {CL_S_COUNT{1'b0}}; + s_axis_tready_reg <= {S_COUNT{1'b0}}; + output_tuser_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + port_sel_reg <= port_sel_next; + + s_axis_tready_reg <= s_axis_tready_next; + + output_tuser_reg <= output_tuser_next; + + busy_reg <= state_next != STATE_IDLE; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_frame_join_wrap.py b/corundum/lib/eth/lib/axis/rtl/axis_frame_join_wrap.py new file mode 100755 index 0000000000000000000000000000000000000000..57c10f053b3c2559dcbe23e40cf19b24a018835a --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_frame_join_wrap.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream frame joiner wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_frame_join_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream frame joiner wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream {{n}} port frame joiner (wrapper) + */ +module {{name}} # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Prepend data with tag + parameter TAG_ENABLE = 1, + // Tag field width + parameter TAG_WIDTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(n) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Configuration + */ + input wire [TAG_WIDTH-1:0] tag, + + /* + * Status signals + */ + output wire busy +); + +axis_frame_join #( + .S_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .TAG_ENABLE(TAG_ENABLE), + .TAG_WIDTH(TAG_WIDTH) +) +axis_frame_join_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + // Configuration + .tag(tag), + // Status + .busy(busy) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/corundum/lib/eth/lib/axis/rtl/axis_frame_len.v b/corundum/lib/eth/lib/axis/rtl/axis_frame_len.v new file mode 100644 index 0000000000000000000000000000000000000000..dd4d29207ad4e3c5051e7e71442ff36cd0ec1387 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_frame_len.v @@ -0,0 +1,115 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream frame length measurement + */ +module axis_frame_len # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 64, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Width of length counter + parameter LEN_WIDTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI monitor + */ + input wire [KEEP_WIDTH-1:0] monitor_axis_tkeep, + input wire monitor_axis_tvalid, + input wire monitor_axis_tready, + input wire monitor_axis_tlast, + + /* + * Status + */ + output wire [LEN_WIDTH-1:0] frame_len, + output wire frame_len_valid +); + +reg [LEN_WIDTH-1:0] frame_len_reg = 0, frame_len_next; +reg frame_len_valid_reg = 1'b0, frame_len_valid_next; +reg frame_reg = 1'b0, frame_next; + +assign frame_len = frame_len_reg; +assign frame_len_valid = frame_len_valid_reg; + +integer offset, i, bit_cnt; + +always @* begin + frame_len_next = frame_len_reg; + frame_len_valid_next = 1'b0; + frame_next = frame_reg; + + if (monitor_axis_tready && monitor_axis_tvalid) begin + // valid transfer cycle + + if (monitor_axis_tlast) begin + // end of frame + frame_len_valid_next = 1'b1; + frame_next = 1'b0; + end else if (!frame_reg) begin + // first word after end of frame + frame_len_next = 0; + frame_next = 1'b1; + end + + // increment frame length by number of words transferred + if (KEEP_ENABLE) begin + bit_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + if (monitor_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) bit_cnt = i; + end + frame_len_next = frame_len_next + bit_cnt; + end else begin + frame_len_next = frame_len_next + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + frame_len_reg <= 0; + frame_len_valid_reg <= 0; + frame_reg <= 1'b0; + end else begin + frame_len_reg <= frame_len_next; + frame_len_valid_reg <= frame_len_valid_next; + frame_reg <= frame_next; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_frame_length_adjust.v b/corundum/lib/eth/lib/axis/rtl/axis_frame_length_adjust.v new file mode 100644 index 0000000000000000000000000000000000000000..55257cb1d8d190cc1d48733f57fd44653d925354 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_frame_length_adjust.v @@ -0,0 +1,613 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream frame length adjuster + */ +module axis_frame_length_adjust # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire status_valid, + input wire status_ready, + output wire status_frame_pad, + output wire status_frame_truncate, + output wire [15:0] status_frame_length, + output wire [15:0] status_frame_original_length, + + /* + * Configuration + */ + input wire [15:0] length_min, + input wire [15:0] length_max +); + +// bus word width +localparam DATA_WORD_WIDTH = DATA_WIDTH / KEEP_WIDTH; + +// bus width assertions +initial begin + if (DATA_WORD_WIDTH * KEEP_WIDTH != DATA_WIDTH) begin + $error("Error: data width not evenly divisble (instance %m)"); + $finish; + end +end + +// state register +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_TRANSFER = 3'd1, + STATE_PAD = 3'd2, + STATE_TRUNCATE = 3'd3; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_last_word; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +// frame length counters +reg [15:0] short_counter_reg = 16'd0, short_counter_next = 16'd0; +reg [15:0] long_counter_reg = 16'd0, long_counter_next = 16'd0; + +reg [DATA_WIDTH-1:0] last_word_data_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] last_word_keep_reg = {KEEP_WIDTH{1'b0}}; +reg [ID_WIDTH-1:0] last_word_id_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] last_word_dest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] last_word_user_reg = {USER_WIDTH{1'b0}}; + +reg status_valid_reg = 1'b0, status_valid_next; +reg status_frame_pad_reg = 1'b0, status_frame_pad_next; +reg status_frame_truncate_reg = 1'b0, status_frame_truncate_next; +reg [15:0] status_frame_length_reg = 16'd0, status_frame_length_next; +reg [15:0] status_frame_original_length_reg = 16'd0, status_frame_original_length_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; +assign s_axis_tready = s_axis_tready_reg; + +assign status_valid = status_valid_reg; +assign status_frame_pad = status_frame_pad_reg; +assign status_frame_truncate = status_frame_truncate_reg; +assign status_frame_length = status_frame_length_reg; +assign status_frame_original_length = status_frame_original_length_reg; + +integer i, word_cnt; + +always @* begin + state_next = STATE_IDLE; + + store_last_word = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + short_counter_next = short_counter_reg; + long_counter_next = long_counter_reg; + + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {KEEP_WIDTH{1'b0}}; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tid_int = {ID_WIDTH{1'b0}}; + m_axis_tdest_int = {DEST_WIDTH{1'b0}}; + m_axis_tuser_int = {USER_WIDTH{1'b0}}; + + s_axis_tready_next = 1'b0; + + status_valid_next = status_valid_reg && !status_ready; + status_frame_pad_next = status_frame_pad_reg; + status_frame_truncate_next = status_frame_truncate_reg; + status_frame_length_next = status_frame_length_reg; + status_frame_original_length_next = status_frame_original_length_reg; + + if (KEEP_ENABLE) begin + for (i = 0; i < KEEP_WIDTH; i = i + 1) begin + s_axis_tdata_masked[i*DATA_WORD_WIDTH +: DATA_WORD_WIDTH] = s_axis_tkeep[i] ? s_axis_tdata[i*DATA_WORD_WIDTH +: DATA_WORD_WIDTH] : {DATA_WORD_WIDTH{1'b0}}; + end + end else begin + s_axis_tdata_masked = s_axis_tdata; + end + + case (state_reg) + STATE_IDLE: begin + // idle state + // accept data next cycle if output register ready next cycle + s_axis_tready_next = m_axis_tready_int_early && (!status_valid_reg || status_ready); + + m_axis_tdata_int = s_axis_tdata_masked; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + short_counter_next = length_min; + long_counter_next = length_max; + + if (s_axis_tready && s_axis_tvalid) begin + // transfer through + word_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i; + end + frame_ptr_next = frame_ptr_reg+KEEP_WIDTH; + + if (short_counter_reg > KEEP_WIDTH) begin + short_counter_next = short_counter_reg - KEEP_WIDTH; + end else begin + short_counter_next = 16'd0; + end + + if (long_counter_reg > KEEP_WIDTH) begin + long_counter_next = long_counter_reg - KEEP_WIDTH; + end else begin + long_counter_next = 16'd0; + end + + if (long_counter_reg <= word_cnt) begin + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-long_counter_reg); + if (s_axis_tlast) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = word_cnt > long_counter_reg; + status_frame_length_next = length_max; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + m_axis_tvalid_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_TRUNCATE; + end + end else begin + if (s_axis_tlast) begin + status_frame_original_length_next = frame_ptr_reg+word_cnt; + if (short_counter_reg > word_cnt) begin + if (short_counter_reg > KEEP_WIDTH) begin + frame_ptr_next = frame_ptr_reg + KEEP_WIDTH; + s_axis_tready_next = 1'b0; + m_axis_tkeep_int = {KEEP_WIDTH{1'b1}}; + m_axis_tlast_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_PAD; + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b1; + status_frame_truncate_next = 1'b0; + status_frame_length_next = length_min; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-(length_min - frame_ptr_reg)); + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = 1'b0; + status_frame_length_next = frame_ptr_reg+word_cnt; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER; + end + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_TRANSFER: begin + // transfer data + // accept data next cycle if output register ready next cycle + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata_masked; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; + + if (s_axis_tready && s_axis_tvalid) begin + // transfer through + word_cnt = 1; + for (i = 1; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i; + end + frame_ptr_next = frame_ptr_reg+KEEP_WIDTH; + + if (short_counter_reg > KEEP_WIDTH) begin + short_counter_next = short_counter_reg - KEEP_WIDTH; + end else begin + short_counter_next = 16'd0; + end + + if (long_counter_reg > KEEP_WIDTH) begin + long_counter_next = long_counter_reg - KEEP_WIDTH; + end else begin + long_counter_next = 16'd0; + end + + if (long_counter_reg <= word_cnt) begin + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-long_counter_reg); + if (s_axis_tlast) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = word_cnt > long_counter_reg; + status_frame_length_next = length_max; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + m_axis_tvalid_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_TRUNCATE; + end + end else begin + if (s_axis_tlast) begin + status_frame_original_length_next = frame_ptr_reg+word_cnt; + if (short_counter_reg > word_cnt) begin + if (short_counter_reg > KEEP_WIDTH) begin + frame_ptr_next = frame_ptr_reg + KEEP_WIDTH; + s_axis_tready_next = 1'b0; + m_axis_tkeep_int = {KEEP_WIDTH{1'b1}}; + m_axis_tlast_int = 1'b0; + store_last_word = 1'b1; + state_next = STATE_PAD; + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b1; + status_frame_truncate_next = 1'b0; + status_frame_length_next = length_min; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-short_counter_reg); + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = 1'b0; + status_frame_length_next = frame_ptr_reg+word_cnt; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER; + end + end + end else begin + state_next = STATE_TRANSFER; + end + end + STATE_PAD: begin + // pad to minimum length + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tid_int = last_word_id_reg; + m_axis_tdest_int = last_word_dest_reg; + m_axis_tuser_int = last_word_user_reg; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg + KEEP_WIDTH; + + if (short_counter_reg > KEEP_WIDTH) begin + short_counter_next = short_counter_reg - KEEP_WIDTH; + end else begin + short_counter_next = 16'd0; + end + + if (long_counter_reg > KEEP_WIDTH) begin + long_counter_next = long_counter_reg - KEEP_WIDTH; + end else begin + long_counter_next = 16'd0; + end + + if (short_counter_reg <= KEEP_WIDTH) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b1; + status_frame_truncate_next = 1'b0; + status_frame_length_next = length_min; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + m_axis_tkeep_int = ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-short_counter_reg); + m_axis_tlast_int = 1'b1; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAD; + end + end else begin + state_next = STATE_PAD; + end + end + STATE_TRUNCATE: begin + // drop after maximum length + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = last_word_data_reg; + m_axis_tkeep_int = last_word_keep_reg; + m_axis_tvalid_int = s_axis_tvalid && s_axis_tlast; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = last_word_id_reg; + m_axis_tdest_int = last_word_dest_reg; + m_axis_tuser_int = s_axis_tuser; + + if (s_axis_tready && s_axis_tvalid) begin + word_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (s_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) word_cnt = i; + end + frame_ptr_next = frame_ptr_reg+KEEP_WIDTH; + + if (s_axis_tlast) begin + status_valid_next = 1'b1; + status_frame_pad_next = 1'b0; + status_frame_truncate_next = 1'b1; + status_frame_length_next = length_max; + status_frame_original_length_next = frame_ptr_reg+word_cnt; + s_axis_tready_next = m_axis_tready_int_early && status_ready; + frame_ptr_next = 16'd0; + short_counter_next = length_min; + long_counter_next = length_max; + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRUNCATE; + end + end else begin + state_next = STATE_TRUNCATE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_ptr_reg <= 16'd0; + short_counter_reg <= 16'd0; + long_counter_reg <= 16'd0; + s_axis_tready_reg <= 1'b0; + status_valid_reg <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + short_counter_reg <= short_counter_next; + long_counter_reg <= long_counter_next; + + s_axis_tready_reg <= s_axis_tready_next; + + status_valid_reg <= status_valid_next; + end + + status_frame_pad_reg <= status_frame_pad_next; + status_frame_truncate_reg <= status_frame_truncate_next; + status_frame_length_reg <= status_frame_length_next; + status_frame_original_length_reg <= status_frame_original_length_next; + + if (store_last_word) begin + last_word_data_reg <= m_axis_tdata_int; + last_word_keep_reg <= m_axis_tkeep_int; + last_word_id_reg <= m_axis_tid_int; + last_word_dest_reg <= m_axis_tdest_int; + last_word_user_reg <= m_axis_tuser_int; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_frame_length_adjust_fifo.v b/corundum/lib/eth/lib/axis/rtl/axis_frame_length_adjust_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..ce0acd746e1522841ab6961170fee0eedc02ca2e --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_frame_length_adjust_fifo.v @@ -0,0 +1,237 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream frame length adjuster with FIFO + */ +module axis_frame_length_adjust_fifo # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Depth of data FIFO in words + parameter FRAME_FIFO_DEPTH = 4096, + // Depth of header FIFO + parameter HEADER_FIFO_DEPTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire m_axis_hdr_valid, + input wire m_axis_hdr_ready, + output wire m_axis_hdr_pad, + output wire m_axis_hdr_truncate, + output wire [15:0] m_axis_hdr_length, + output wire [15:0] m_axis_hdr_original_length, + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Configuration + */ + input wire [15:0] length_min, + input wire [15:0] length_max +); + +wire [DATA_WIDTH-1:0] fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] fifo_axis_tkeep; +wire fifo_axis_tvalid; +wire fifo_axis_tready; +wire fifo_axis_tlast; +wire [ID_WIDTH-1:0] fifo_axis_tid; +wire [DEST_WIDTH-1:0] fifo_axis_tdest; +wire [USER_WIDTH-1:0] fifo_axis_tuser; + +wire status_valid; +wire status_ready; +wire status_frame_pad; +wire status_frame_truncate; +wire [15:0] status_frame_length; +wire [15:0] status_frame_original_length; + +axis_frame_length_adjust #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_frame_length_adjust_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tid(s_axis_tid), + .s_axis_tdest(s_axis_tdest), + .s_axis_tuser(s_axis_tuser), + // AXI output + .m_axis_tdata(fifo_axis_tdata), + .m_axis_tkeep(fifo_axis_tkeep), + .m_axis_tvalid(fifo_axis_tvalid), + .m_axis_tready(fifo_axis_tready), + .m_axis_tlast(fifo_axis_tlast), + .m_axis_tid(fifo_axis_tid), + .m_axis_tdest(fifo_axis_tdest), + .m_axis_tuser(fifo_axis_tuser), + // Status + .status_valid(status_valid), + .status_ready(status_ready), + .status_frame_pad(status_frame_pad), + .status_frame_truncate(status_frame_truncate), + .status_frame_length(status_frame_length), + .status_frame_original_length(status_frame_original_length), + // Configuration + .length_min(length_min), + .length_max(length_max) +); + +axis_fifo #( + .DEPTH(FRAME_FIFO_DEPTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(0) +) +frame_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(fifo_axis_tdata), + .s_axis_tkeep(fifo_axis_tkeep), + .s_axis_tvalid(fifo_axis_tvalid), + .s_axis_tready(fifo_axis_tready), + .s_axis_tlast(fifo_axis_tlast), + .s_axis_tid(fifo_axis_tid), + .s_axis_tdest(fifo_axis_tdest), + .s_axis_tuser(fifo_axis_tuser), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +axis_fifo #( + .DEPTH(HEADER_FIFO_DEPTH), + .DATA_WIDTH(1+1+16+16), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +header_fifo_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata({status_frame_pad, status_frame_truncate, status_frame_length, status_frame_original_length}), + .s_axis_tkeep(0), + .s_axis_tvalid(status_valid), + .s_axis_tready(status_ready), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + // AXI output + .m_axis_tdata({m_axis_hdr_pad, m_axis_hdr_truncate, m_axis_hdr_length, m_axis_hdr_original_length}), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_hdr_valid), + .m_axis_tready(m_axis_hdr_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_ll_bridge.v b/corundum/lib/eth/lib/axis/rtl/axis_ll_bridge.v new file mode 100644 index 0000000000000000000000000000000000000000..ad0e002164b0a015ff10ec8b14fa305b62d3c2fc --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_ll_bridge.v @@ -0,0 +1,79 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream to LocalLink bridge + */ +module axis_ll_bridge # +( + parameter DATA_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + + /* + * LocalLink output + */ + output wire [DATA_WIDTH-1:0] ll_data_out, + output wire ll_sof_out_n, + output wire ll_eof_out_n, + output wire ll_src_rdy_out_n, + input wire ll_dst_rdy_in_n +); + +reg last_tlast = 1'b1; + +always @(posedge clk) begin + if (rst) begin + last_tlast = 1'b1; + end else begin + if (s_axis_tvalid && s_axis_tready) last_tlast = s_axis_tlast; + end +end + +// high for packet length 1 -> cannot set SOF and EOF in same cycle +// invalid packets are discarded +wire invalid = s_axis_tvalid && s_axis_tlast && last_tlast; + +assign s_axis_tready = !ll_dst_rdy_in_n; + +assign ll_data_out = s_axis_tdata; +assign ll_sof_out_n = !(last_tlast && s_axis_tvalid && !invalid); +assign ll_eof_out_n = !(s_axis_tlast && !invalid); +assign ll_src_rdy_out_n = !(s_axis_tvalid && !invalid); + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_mux.v b/corundum/lib/eth/lib/axis/rtl/axis_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..716f9af5111df317a3d7be53e85aba5a1d325065 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_mux.v @@ -0,0 +1,263 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream multiplexer + */ +module axis_mux # +( + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_axis_tready_reg = 0, s_axis_tready_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_axis_tvalid[select_reg]; +wire current_s_tready = s_axis_tready[select_reg]; +wire current_s_tlast = s_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_axis_tready_next = 0; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && (s_axis_tvalid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + end + + // generate ready signal on selected port + s_axis_tready_next = (m_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_axis_tdata_int = current_s_tdata; + m_axis_tkeep_int = current_s_tkeep; + m_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_axis_tlast_int = current_s_tlast; + m_axis_tid_int = current_s_tid; + m_axis_tdest_int = current_s_tdest; + m_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_axis_tready_reg <= 0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_axis_tready_reg <= s_axis_tready_next; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_mux_wrap.py b/corundum/lib/eth/lib/axis/rtl/axis_mux_wrap.py new file mode 100755 index 0000000000000000000000000000000000000000..539f49571ebe300879be68b88d17341c81cdd6c9 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_mux_wrap.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream mux wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=4, help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + n = ports + + if name is None: + name = "axis_mux_wrap_{0}".format(n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0} port AXI stream mux wrapper {1}...".format(n, name)) + + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream {{n}} port mux (wrapper) + */ +module {{name}} # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(n) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [{{cn-1}}:0] select +); + +axis_mux #( + .S_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) +) +axis_mux_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(n-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI output + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tready(m_axis_tready), + .m_axis_tlast(m_axis_tlast), + .m_axis_tid(m_axis_tid), + .m_axis_tdest(m_axis_tdest), + .m_axis_tuser(m_axis_tuser), + // Control + .enable(enable), + .select(select) +); + +endmodule + +""") + + output_file.write(t.render( + n=n, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/corundum/lib/eth/lib/axis/rtl/axis_pipeline_register.v b/corundum/lib/eth/lib/axis/rtl/axis_pipeline_register.v new file mode 100644 index 0000000000000000000000000000000000000000..99d8b8537d8d5a4fd296649bf8034a50252f78de --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_pipeline_register.v @@ -0,0 +1,158 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream pipeline register + */ +module axis_pipeline_register # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter REG_TYPE = 2, + // Number of registers in pipeline + parameter LENGTH = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +wire [DATA_WIDTH-1:0] axis_tdata[0:LENGTH]; +wire [KEEP_WIDTH-1:0] axis_tkeep[0:LENGTH]; +wire axis_tvalid[0:LENGTH]; +wire axis_tready[0:LENGTH]; +wire axis_tlast[0:LENGTH]; +wire [ID_WIDTH-1:0] axis_tid[0:LENGTH]; +wire [DEST_WIDTH-1:0] axis_tdest[0:LENGTH]; +wire [USER_WIDTH-1:0] axis_tuser[0:LENGTH]; + +assign axis_tdata[0] = s_axis_tdata; +assign axis_tkeep[0] = s_axis_tkeep; +assign axis_tvalid[0] = s_axis_tvalid; +assign s_axis_tready = axis_tready[0]; +assign axis_tlast[0] = s_axis_tlast; +assign axis_tid[0] = s_axis_tid; +assign axis_tdest[0] = s_axis_tdest; +assign axis_tuser[0] = s_axis_tuser; + +assign m_axis_tdata = axis_tdata[LENGTH]; +assign m_axis_tkeep = axis_tkeep[LENGTH]; +assign m_axis_tvalid = axis_tvalid[LENGTH]; +assign axis_tready[LENGTH] = m_axis_tready; +assign m_axis_tlast = axis_tlast[LENGTH]; +assign m_axis_tid = axis_tid[LENGTH]; +assign m_axis_tdest = axis_tdest[LENGTH]; +assign m_axis_tuser = axis_tuser[LENGTH]; + +generate + genvar i; + + for (i = 0; i < LENGTH; i = i + 1) begin : pipe_reg + axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(LAST_ENABLE), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(REG_TYPE) + ) + reg_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(axis_tdata[i]), + .s_axis_tkeep(axis_tkeep[i]), + .s_axis_tvalid(axis_tvalid[i]), + .s_axis_tready(axis_tready[i]), + .s_axis_tlast(axis_tlast[i]), + .s_axis_tid(axis_tid[i]), + .s_axis_tdest(axis_tdest[i]), + .s_axis_tuser(axis_tuser[i]), + // AXI output + .m_axis_tdata(axis_tdata[i+1]), + .m_axis_tkeep(axis_tkeep[i+1]), + .m_axis_tvalid(axis_tvalid[i+1]), + .m_axis_tready(axis_tready[i+1]), + .m_axis_tlast(axis_tlast[i+1]), + .m_axis_tid(axis_tid[i+1]), + .m_axis_tdest(axis_tdest[i+1]), + .m_axis_tuser(axis_tuser[i+1]) + ); + end +endgenerate + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_ram_switch.v b/corundum/lib/eth/lib/axis/rtl/axis_ram_switch.v new file mode 100644 index 0000000000000000000000000000000000000000..d79cfb9f17f392c271f6c4278842ea7d95ba6d24 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_ram_switch.v @@ -0,0 +1,1068 @@ +/* + +Copyright (c) 2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream RAM switch + */ +module axis_ram_switch # +( + // FIFO depth in words (each virtual FIFO) + // KEEP_WIDTH words per cycle if KEEP_ENABLE set + // Rounded up to nearest power of 2 cycles + parameter FIFO_DEPTH = 4096, + // Command FIFO depth (each virtual FIFO) + // Rounded up to nearest power of 2 + parameter CMD_FIFO_DEPTH = 32, + // Speedup factor (internal data width scaling factor) + // Speedup of 0 scales internal width to provide maximum bandwidth + parameter SPEEDUP = 0, + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Number of AXI stream outputs + parameter M_COUNT = 4, + // Width of input AXI stream interfaces in bits + parameter S_DATA_WIDTH = 8, + // Propagate tkeep signal + parameter S_KEEP_ENABLE = (S_DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter S_KEEP_WIDTH = (S_DATA_WIDTH/8), + // Width of output AXI stream interfaces in bits + parameter M_DATA_WIDTH = 8, + // Propagate tkeep signal + parameter M_KEEP_ENABLE = (M_DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter M_KEEP_WIDTH = (M_DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // tdest signal width + // must be wide enough to uniquely address outputs + parameter DEST_WIDTH = $clog2(M_COUNT), + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1, + // Drop frames marked bad + parameter DROP_BAD_FRAME = 0, + // Drop incoming frames when full + // When set, s_axis_tready is always asserted + parameter DROP_WHEN_FULL = 0, + // Output interface routing base tdest selection + // Concatenate M_COUNT DEST_WIDTH sized constants + // Port selected if M_BASE <= tdest <= M_TOP + // set to zero for default routing with tdest MSBs as port index + parameter M_BASE = 0, + // Output interface routing top tdest selection + // Concatenate M_COUNT DEST_WIDTH sized constants + // Port selected if M_BASE <= tdest <= M_TOP + // set to zero to inherit from M_BASE + parameter M_TOP = 0, + // Interface connection control + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "ROUND_ROBIN", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*S_DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*S_KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ + output wire [M_COUNT*M_DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*M_KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire [S_COUNT-1:0] status_overflow, + output wire [S_COUNT-1:0] status_bad_frame, + output wire [S_COUNT-1:0] status_good_frame +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +// force keep width to 1 when disabled +parameter S_KEEP_WIDTH_INT = S_KEEP_ENABLE ? S_KEEP_WIDTH : 1; +parameter M_KEEP_WIDTH_INT = M_KEEP_ENABLE ? M_KEEP_WIDTH : 1; + +// bus word sizes (must be identical) +parameter S_DATA_WORD_SIZE = S_DATA_WIDTH / S_KEEP_WIDTH_INT; +parameter M_DATA_WORD_SIZE = M_DATA_WIDTH / M_KEEP_WIDTH_INT; +// total data and keep widths +parameter MIN_DATA_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_DATA_WIDTH : S_DATA_WIDTH); +parameter MIN_KEEP_WIDTH = (M_KEEP_WIDTH_INT > S_KEEP_WIDTH_INT ? M_KEEP_WIDTH_INT : S_KEEP_WIDTH_INT); +// speedup factor +parameter M_TOTAL_DATA_WIDTH = M_DATA_WIDTH*M_COUNT; +parameter S_TOTAL_DATA_WIDTH = S_DATA_WIDTH*S_COUNT; +parameter SPEEDUP_INT = SPEEDUP > 0 ? SPEEDUP : (M_TOTAL_DATA_WIDTH > S_TOTAL_DATA_WIDTH ? (M_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH) : (S_TOTAL_DATA_WIDTH / MIN_DATA_WIDTH)); +parameter DATA_WIDTH = MIN_DATA_WIDTH*SPEEDUP_INT; +parameter KEEP_WIDTH = MIN_KEEP_WIDTH*SPEEDUP_INT; + +parameter ADDR_WIDTH = $clog2(FIFO_DEPTH/KEEP_WIDTH); +parameter RAM_ADDR_WIDTH = $clog2(S_COUNT*FIFO_DEPTH/KEEP_WIDTH); + +parameter CMD_ADDR_WIDTH = $clog2(CMD_FIFO_DEPTH); + +integer i, j; + +// check configuration +initial begin + if (S_DATA_WORD_SIZE * S_KEEP_WIDTH_INT != S_DATA_WIDTH) begin + $error("Error: input data width not evenly divisble (instance %m)"); + $finish; + end + + if (M_DATA_WORD_SIZE * M_KEEP_WIDTH_INT != M_DATA_WIDTH) begin + $error("Error: output data width not evenly divisble (instance %m)"); + $finish; + end + + if (S_DATA_WORD_SIZE != M_DATA_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (DEST_WIDTH < CL_M_COUNT) begin + $error("Error: DEST_WIDTH too small for port count (instance %m)"); + $finish; + end + + if (M_BASE == 0) begin + // M_BASE is zero, route with tdest as port index + end else if (M_TOP == 0) begin + // M_TOP is zero, assume equal to M_BASE + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = i+1; j < M_COUNT; j = j + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin + $display("%d: %08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH]); + $display("%d: %08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH]); + $error("Error: ranges overlap (instance %m)"); + $finish; + end + end + end + end else begin + for (i = 0; i < M_COUNT; i = i + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $error("Error: invalid range (instance %m)"); + $finish; + end + end + + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = i+1; j < M_COUNT; j = j + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[j*DEST_WIDTH +: DEST_WIDTH] && M_BASE[j*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $display("%d: %08x-%08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH]); + $display("%d: %08x-%08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH], M_TOP[j*DEST_WIDTH +: DEST_WIDTH]); + $error("Error: ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +// Shared RAM +reg [DATA_WIDTH-1:0] mem[(2**RAM_ADDR_WIDTH)-1:0]; +reg [DATA_WIDTH-1:0] mem_read_data_reg; +reg [M_COUNT-1:0] mem_read_data_valid_reg; + +wire [S_COUNT*DATA_WIDTH-1:0] port_ram_wr_data; +wire [S_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_wr_addr; +wire [S_COUNT-1:0] port_ram_wr_en; +wire [S_COUNT-1:0] port_ram_wr_ack; + +wire [M_COUNT*RAM_ADDR_WIDTH-1:0] port_ram_rd_addr; +wire [M_COUNT-1:0] port_ram_rd_en; +wire [M_COUNT-1:0] port_ram_rd_ack; +wire [M_COUNT*DATA_WIDTH-1:0] port_ram_rd_data; +wire [M_COUNT-1:0] port_ram_rd_data_valid; + +assign port_ram_rd_data = {M_COUNT{mem_read_data_reg}}; +assign port_ram_rd_data_valid = mem_read_data_valid_reg; + +wire [CL_S_COUNT-1:0] ram_wr_sel; +wire ram_wr_en; + +wire [CL_M_COUNT-1:0] ram_rd_sel; +wire ram_rd_en; + +generate + +if (S_COUNT > 1) begin + + arbiter #( + .PORTS(S_COUNT), + .TYPE("ROUND_ROBIN"), + .BLOCK("NONE"), + .LSB_PRIORITY("HIGH") + ) + ram_write_arb_inst ( + .clk(clk), + .rst(rst), + .request(port_ram_wr_en & ~port_ram_wr_ack), + .acknowledge({S_COUNT{1'b0}}), + .grant(port_ram_wr_ack), + .grant_valid(ram_wr_en), + .grant_encoded(ram_wr_sel) + ); + +end else begin + + assign ram_wr_en = port_ram_wr_en; + assign port_ram_wr_ack = port_ram_wr_en; + assign ram_wr_sel = 0; + +end + +endgenerate + +always @(posedge clk) begin + if (ram_wr_en) begin + mem[port_ram_wr_addr[ram_wr_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]] <= port_ram_wr_data[ram_wr_sel*DATA_WIDTH +: DATA_WIDTH]; + end +end + +generate + +if (M_COUNT > 1) begin + + arbiter #( + .PORTS(M_COUNT), + .TYPE("ROUND_ROBIN"), + .BLOCK("NONE"), + .LSB_PRIORITY("HIGH") + ) + ram_read_arb_inst ( + .clk(clk), + .rst(rst), + .request(port_ram_rd_en & ~port_ram_rd_ack), + .acknowledge({M_COUNT{1'b0}}), + .grant(port_ram_rd_ack), + .grant_valid(ram_rd_en), + .grant_encoded(ram_rd_sel) + ); + +end else begin + + assign ram_rd_en = port_ram_rd_en; + assign port_ram_rd_ack = port_ram_rd_en; + assign ram_rd_sel = 0; + +end + +endgenerate + +always @(posedge clk) begin + mem_read_data_valid_reg <= 0; + + if (ram_rd_en) begin + mem_read_data_reg <= mem[port_ram_rd_addr[ram_rd_sel*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]]; + mem_read_data_valid_reg <= 1 << ram_rd_sel; + end + + if (rst) begin + mem_read_data_valid_reg <= 0; + end +end + +// Interconnect +wire [S_COUNT*RAM_ADDR_WIDTH-1:0] int_cmd_addr; +wire [S_COUNT*ADDR_WIDTH-1:0] int_cmd_len; +wire [S_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_id; +wire [S_COUNT*KEEP_WIDTH-1:0] int_cmd_tkeep; +wire [S_COUNT*ID_WIDTH-1:0] int_cmd_tid; +wire [S_COUNT*DEST_WIDTH-1:0] int_cmd_tdest; +wire [S_COUNT*USER_WIDTH-1:0] int_cmd_tuser; + +wire [S_COUNT*M_COUNT-1:0] int_cmd_valid; +wire [M_COUNT*S_COUNT-1:0] int_cmd_ready; + +wire [M_COUNT*CMD_ADDR_WIDTH-1:0] int_cmd_status_id; + +wire [M_COUNT*S_COUNT-1:0] int_cmd_status_valid; +wire [S_COUNT*M_COUNT-1:0] int_cmd_status_ready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + + wire [DATA_WIDTH-1:0] port_axis_tdata; + wire [KEEP_WIDTH-1:0] port_axis_tkeep; + wire port_axis_tvalid; + wire port_axis_tready; + wire port_axis_tlast; + wire [ID_WIDTH-1:0] port_axis_tid; + wire [DEST_WIDTH-1:0] port_axis_tdest; + wire [USER_WIDTH-1:0] port_axis_tuser; + + axis_adapter #( + .S_DATA_WIDTH(S_DATA_WIDTH), + .S_KEEP_ENABLE(S_KEEP_ENABLE), + .S_KEEP_WIDTH(S_KEEP_WIDTH), + .M_DATA_WIDTH(DATA_WIDTH), + .M_KEEP_ENABLE(1), + .M_KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata[S_DATA_WIDTH*m +: S_DATA_WIDTH]), + .s_axis_tkeep(s_axis_tkeep[S_KEEP_WIDTH*m +: S_KEEP_WIDTH]), + .s_axis_tvalid(s_axis_tvalid[m]), + .s_axis_tready(s_axis_tready[m]), + .s_axis_tlast(s_axis_tlast[m]), + .s_axis_tid(s_axis_tid[ID_WIDTH*m +: ID_WIDTH]), + .s_axis_tdest(s_axis_tdest[DEST_WIDTH*m +: DEST_WIDTH]), + .s_axis_tuser(s_axis_tuser[USER_WIDTH*m +: USER_WIDTH]), + // AXI output + .m_axis_tdata(port_axis_tdata), + .m_axis_tkeep(port_axis_tkeep), + .m_axis_tvalid(port_axis_tvalid), + .m_axis_tready(port_axis_tready), + .m_axis_tlast(port_axis_tlast), + .m_axis_tid(port_axis_tid), + .m_axis_tdest(port_axis_tdest), + .m_axis_tuser(port_axis_tuser) + ); + + // decoding + reg [CL_M_COUNT-1:0] select_reg = 0, select_next; + reg drop_reg = 1'b0, drop_next; + reg select_valid_reg = 1'b0, select_valid_next; + + integer k; + + always @* begin + select_next = select_reg; + drop_next = drop_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast); + select_valid_next = select_valid_reg && !(port_axis_tvalid && port_axis_tready && port_axis_tlast); + + if (port_axis_tvalid && !select_valid_reg && !drop_reg) begin + select_next = 1'b0; + select_valid_next = 1'b0; + drop_next = 1'b1; + for (k = 0; k < M_COUNT; k = k + 1) begin + if (M_BASE == 0) begin + // M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index + if (port_axis_tdest[DEST_WIDTH-CL_M_COUNT +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end else if (M_TOP == 0) begin + // M_TOP is zero, assume equal to M_BASE + if (port_axis_tdest == M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end else begin + if (port_axis_tdest >= M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && port_axis_tdest <= M_TOP[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end + end + end + end + + always @(posedge clk) begin + select_reg <= select_next; + drop_reg <= drop_next; + select_valid_reg <= select_valid_next; + + if (rst) begin + select_valid_reg <= 1'b0; + end + end + + // status arbitration + wire [M_COUNT-1:0] request; + wire [M_COUNT-1:0] acknowledge; + wire [M_COUNT-1:0] grant; + wire grant_valid; + wire [CL_M_COUNT-1:0] grant_encoded; + + arbiter #( + .PORTS(M_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) + ) + cmd_status_arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) + ); + + // mux + wire [CMD_ADDR_WIDTH-1:0] cmd_status_id_mux = int_cmd_status_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH]; + wire cmd_status_valid_mux = int_cmd_status_valid[grant_encoded*S_COUNT+m] && grant_valid; + wire cmd_status_ready_mux; + + assign int_cmd_status_ready[m*M_COUNT +: M_COUNT] = (grant_valid && cmd_status_ready_mux) << grant_encoded; + + for (n = 0; n < M_COUNT; n = n + 1) begin + assign request[n] = int_cmd_status_valid[m+n*S_COUNT] && !grant[n]; + assign acknowledge[n] = grant[n] && int_cmd_status_valid[m+n*S_COUNT] && cmd_status_ready_mux; + end + + reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; + reg [ADDR_WIDTH:0] wr_ptr_cur_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_cur_next; + reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; + + reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next; + + // full when first MSB different but rest same + wire full = wr_ptr_cur_reg == (rd_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); + // empty when pointers match exactly + wire empty = wr_ptr_reg == rd_ptr_reg; + // overflow within packet + wire full_wr = wr_ptr_cur_reg == (wr_ptr_reg ^ {1'b1, {ADDR_WIDTH{1'b0}}}); + + reg drop_frame_reg = 1'b0, drop_frame_next; + reg overflow_reg = 1'b0, overflow_next; + reg bad_frame_reg = 1'b0, bad_frame_next; + reg good_frame_reg = 1'b0, good_frame_next; + + reg [DATA_WIDTH-1:0] ram_wr_data_reg = {DATA_WIDTH{1'b0}}, ram_wr_data_next; + reg [ADDR_WIDTH-1:0] ram_wr_addr_reg = {ADDR_WIDTH{1'b0}}, ram_wr_addr_next; + reg ram_wr_en_reg = 1'b0, ram_wr_en_next; + wire ram_wr_ack; + + reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_active = 0; + reg [2**CMD_ADDR_WIDTH-1:0] cmd_table_commit = 0; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_start[2**CMD_ADDR_WIDTH-1:0]; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_addr_end[2**CMD_ADDR_WIDTH-1:0]; + reg [ADDR_WIDTH-1:0] cmd_table_len[2**CMD_ADDR_WIDTH-1:0]; + reg [CL_M_COUNT-1:0] cmd_table_select[2**CMD_ADDR_WIDTH-1:0]; + reg [KEEP_WIDTH-1:0] cmd_table_tkeep[2**CMD_ADDR_WIDTH-1:0]; + reg [ID_WIDTH-1:0] cmd_table_tid[2**CMD_ADDR_WIDTH-1:0]; + reg [DEST_WIDTH-1:0] cmd_table_tdest[2**CMD_ADDR_WIDTH-1:0]; + reg [USER_WIDTH-1:0] cmd_table_tuser[2**CMD_ADDR_WIDTH-1:0]; + + reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_start_ptr_reg = 0; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_start; + reg [RAM_ADDR_WIDTH+1-1:0] cmd_table_start_addr_end; + reg [ADDR_WIDTH-1:0] cmd_table_start_len; + reg [CL_M_COUNT-1:0] cmd_table_start_select; + reg [KEEP_WIDTH-1:0] cmd_table_start_tkeep; + reg [ID_WIDTH-1:0] cmd_table_start_tid; + reg [DEST_WIDTH-1:0] cmd_table_start_tdest; + reg [USER_WIDTH-1:0] cmd_table_start_tuser; + reg cmd_table_start_en; + reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_read_ptr_reg = 0; + reg cmd_table_read_en; + reg [CMD_ADDR_WIDTH-1:0] cmd_table_commit_ptr; + reg cmd_table_commit_en; + reg [CMD_ADDR_WIDTH+1-1:0] cmd_table_finish_ptr_reg = 0; + reg cmd_table_finish_en; + + reg [RAM_ADDR_WIDTH-1:0] cmd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, cmd_addr_next; + reg [ADDR_WIDTH-1:0] cmd_len_reg = {ADDR_WIDTH{1'b0}}, cmd_len_next; + reg [CMD_ADDR_WIDTH-1:0] cmd_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_id_next; + reg [KEEP_WIDTH-1:0] cmd_tkeep_reg = {KEEP_WIDTH{1'b0}}, cmd_tkeep_next; + reg [ID_WIDTH-1:0] cmd_tid_reg = {ID_WIDTH{1'b0}}, cmd_tid_next; + reg [DEST_WIDTH-1:0] cmd_tdest_reg = {DEST_WIDTH{1'b0}}, cmd_tdest_next; + reg [USER_WIDTH-1:0] cmd_tuser_reg = {USER_WIDTH{1'b0}}, cmd_tuser_next; + reg [M_COUNT-1:0] cmd_valid_reg = 0, cmd_valid_next; + + reg cmd_status_ready_reg = 1'b0, cmd_status_ready_next; + + wire [M_COUNT-1:0] port_cmd_ready; + for (n = 0; n < M_COUNT; n = n + 1) begin + assign port_cmd_ready[n] = int_cmd_ready[m+n*S_COUNT]; + end + + assign port_axis_tready = (select_valid_reg && (!ram_wr_en_reg || ram_wr_ack) && (!full || full_wr || DROP_WHEN_FULL) && ($unsigned(cmd_table_start_ptr_reg - cmd_table_finish_ptr_reg) < 2**CMD_ADDR_WIDTH)) || drop_reg; + + assign port_ram_wr_data[m*DATA_WIDTH +: DATA_WIDTH] = ram_wr_data_reg; + assign port_ram_wr_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_wr_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH); + assign port_ram_wr_en[m] = ram_wr_en_reg; + assign ram_wr_ack = port_ram_wr_ack[m]; + + assign int_cmd_addr[m*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = cmd_addr_reg[ADDR_WIDTH-1:0] | (m << ADDR_WIDTH); + assign int_cmd_len[m*ADDR_WIDTH +: ADDR_WIDTH] = cmd_len_reg; + assign int_cmd_id[m*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_id_reg; + assign int_cmd_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH] = cmd_tkeep_reg; + assign int_cmd_tid[m*ID_WIDTH +: ID_WIDTH] = cmd_tid_reg; + assign int_cmd_tdest[m*DEST_WIDTH +: DEST_WIDTH] = cmd_tdest_reg; + assign int_cmd_tuser[m*USER_WIDTH +: USER_WIDTH] = cmd_tuser_reg; + assign int_cmd_valid[m*M_COUNT +: M_COUNT] = cmd_valid_reg; + + assign cmd_status_ready_mux = cmd_status_ready_reg; + + assign status_overflow[m] = overflow_reg; + assign status_bad_frame[m] = bad_frame_reg; + assign status_good_frame[m] = good_frame_reg; + + always @* begin + wr_ptr_next = wr_ptr_reg; + wr_ptr_cur_next = wr_ptr_cur_reg; + rd_ptr_next = rd_ptr_reg; + + len_next = len_reg; + + drop_frame_next = drop_frame_reg; + overflow_next = 1'b0; + bad_frame_next = 1'b0; + good_frame_next = 1'b0; + + ram_wr_data_next = ram_wr_data_reg; + ram_wr_addr_next = ram_wr_addr_reg; + ram_wr_en_next = ram_wr_en_reg && !ram_wr_ack; + + cmd_table_start_addr_start = wr_ptr_reg; + cmd_table_start_addr_end = wr_ptr_cur_reg + 1; + cmd_table_start_len = len_reg; + cmd_table_start_select = select_reg; + cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1; + cmd_table_start_tid = port_axis_tid; + cmd_table_start_tdest = port_axis_tdest; + cmd_table_start_tuser = port_axis_tuser; + cmd_table_start_en = 1'b0; + + cmd_table_read_en = 1'b0; + + cmd_table_commit_ptr = 0; + cmd_table_commit_en = 1'b0; + + cmd_table_finish_en = 1'b0; + + cmd_addr_next = cmd_addr_reg; + cmd_len_next = cmd_len_reg; + cmd_id_next = cmd_id_reg; + cmd_tkeep_next = cmd_tkeep_reg; + cmd_tid_next = cmd_tid_reg; + cmd_tdest_next = cmd_tdest_reg; + cmd_tuser_next = cmd_tuser_reg; + cmd_valid_next = cmd_valid_reg; + + cmd_status_ready_next = 1'b0; + + // issue memory writes and commands + if (port_axis_tready && port_axis_tvalid && select_valid_reg && !drop_reg) begin + if (full || full_wr || drop_frame_reg) begin + // full, packet overflow, or currently dropping frame + // drop frame + drop_frame_next = 1'b1; + if (port_axis_tlast) begin + // end of frame, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + drop_frame_next = 1'b0; + overflow_next = 1'b1; + end + end else begin + wr_ptr_cur_next = wr_ptr_cur_reg + 1; + len_next = len_reg + 1; + + // issue write operation + ram_wr_data_next = port_axis_tdata; + ram_wr_addr_next = wr_ptr_cur_reg; + ram_wr_en_next = 1'b1; + + if (port_axis_tlast) begin + // end of frame + len_next = 0; + if (DROP_BAD_FRAME && USER_BAD_FRAME_MASK & ~(port_axis_tuser ^ USER_BAD_FRAME_VALUE)) begin + // bad packet, reset write pointer + wr_ptr_cur_next = wr_ptr_reg; + bad_frame_next = 1'b1; + end else begin + // good packet, update write pointer + wr_ptr_next = wr_ptr_cur_reg + 1; + good_frame_next = 1'b1; + + cmd_table_start_addr_start = wr_ptr_reg; + cmd_table_start_addr_end = wr_ptr_cur_reg + 1; + cmd_table_start_len = len_reg; + cmd_table_start_select = select_reg; + cmd_table_start_tkeep = S_KEEP_ENABLE ? port_axis_tkeep : 1'b1; + cmd_table_start_tid = port_axis_tid; + cmd_table_start_tdest = port_axis_tdest; + cmd_table_start_tuser = port_axis_tuser; + cmd_table_start_en = 1'b1; + end + end + end + end + + // read + cmd_valid_next = cmd_valid_reg & ~port_cmd_ready; + if (!cmd_valid_reg && cmd_table_active[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_read_ptr_reg != cmd_table_start_ptr_reg) begin + cmd_table_read_en = 1'b1; + cmd_addr_next = cmd_table_addr_start[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_len_next = cmd_table_len[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_id_next = cmd_table_read_ptr_reg; + cmd_tkeep_next = cmd_table_tkeep[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_tid_next = cmd_table_tid[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_tdest_next = cmd_table_tdest[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_tuser_next = cmd_table_tuser[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_valid_next = 1 << cmd_table_select[cmd_table_read_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + end + + // commit + if (cmd_status_valid_mux) begin + cmd_status_ready_next = 1'b1; + cmd_table_commit_ptr = cmd_status_id_mux; + cmd_table_commit_en = 1'b1; + end + + // clean-up + if (cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_commit[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] && cmd_table_finish_ptr_reg != cmd_table_start_ptr_reg) begin + // update read pointer + rd_ptr_next = cmd_table_addr_end[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]]; + cmd_table_finish_en = 1'b1; + end + end + + always @(posedge clk) begin + wr_ptr_reg <= wr_ptr_next; + wr_ptr_cur_reg <= wr_ptr_cur_next; + rd_ptr_reg <= rd_ptr_next; + + len_reg <= len_next; + + drop_frame_reg <= drop_frame_next; + overflow_reg <= overflow_next; + bad_frame_reg <= bad_frame_next; + good_frame_reg <= good_frame_next; + + ram_wr_data_reg <= ram_wr_data_next; + ram_wr_addr_reg <= ram_wr_addr_next; + ram_wr_en_reg <= ram_wr_en_next; + + cmd_addr_reg <= cmd_addr_next; + cmd_len_reg <= cmd_len_next; + cmd_id_reg <= cmd_id_next; + cmd_tkeep_reg <= cmd_tkeep_next; + cmd_tid_reg <= cmd_tid_next; + cmd_tdest_reg <= cmd_tdest_next; + cmd_tuser_reg <= cmd_tuser_next; + cmd_valid_reg <= cmd_valid_next; + + cmd_status_ready_reg <= cmd_status_ready_next; + + if (cmd_table_start_en) begin + cmd_table_start_ptr_reg <= cmd_table_start_ptr_reg + 1; + cmd_table_active[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1; + cmd_table_commit[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b0; + cmd_table_addr_start[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_start; + cmd_table_addr_end[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_addr_end; + cmd_table_len[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_len; + cmd_table_select[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_select; + cmd_table_tkeep[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tkeep; + cmd_table_tid[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tid; + cmd_table_tdest[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tdest; + cmd_table_tuser[cmd_table_start_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= cmd_table_start_tuser; + end + + if (cmd_table_read_en) begin + cmd_table_read_ptr_reg <= cmd_table_read_ptr_reg + 1; + end + + if (cmd_table_commit_en) begin + cmd_table_commit[cmd_table_commit_ptr] <= 1'b1; + end + + if (cmd_table_finish_en) begin + cmd_table_finish_ptr_reg <= cmd_table_finish_ptr_reg + 1; + cmd_table_active[cmd_table_finish_ptr_reg[CMD_ADDR_WIDTH-1:0]] <= 1'b1; + end + + if (rst) begin + wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + wr_ptr_cur_reg <= {ADDR_WIDTH+1{1'b0}}; + rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; + len_reg <= {ADDR_WIDTH{1'b0}}; + drop_frame_reg <= 1'b0; + overflow_reg <= 1'b0; + bad_frame_reg <= 1'b0; + good_frame_reg <= 1'b0; + ram_wr_en_reg <= 1'b0; + cmd_valid_reg <= 1'b0; + cmd_status_ready_reg <= 1'b0; + cmd_table_start_ptr_reg <= 0; + cmd_table_read_ptr_reg <= 0; + cmd_table_finish_ptr_reg <= 0; + end + end + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + + // command arbitration + wire [S_COUNT-1:0] request; + wire [S_COUNT-1:0] acknowledge; + wire [S_COUNT-1:0] grant; + wire grant_valid; + wire [CL_S_COUNT-1:0] grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) + ) + cmd_arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) + ); + + // mux + wire [RAM_ADDR_WIDTH-1:0] cmd_addr_mux = int_cmd_addr[grant_encoded*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]; + wire [ADDR_WIDTH-1:0] cmd_len_mux = int_cmd_len[grant_encoded*ADDR_WIDTH +: ADDR_WIDTH]; + wire [CMD_ADDR_WIDTH-1:0] cmd_id_mux = int_cmd_id[grant_encoded*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH]; + wire [KEEP_WIDTH-1:0] cmd_tkeep_mux = int_cmd_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; + wire [ID_WIDTH-1:0] cmd_tid_mux = int_cmd_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; + wire [DEST_WIDTH-1:0] cmd_tdest_mux = int_cmd_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; + wire [USER_WIDTH-1:0] cmd_tuser_mux = int_cmd_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + wire cmd_valid_mux = int_cmd_valid[grant_encoded*M_COUNT+n] && grant_valid; + wire cmd_ready_mux; + + assign int_cmd_ready[n*S_COUNT +: S_COUNT] = (grant_valid && cmd_ready_mux) << grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign request[m] = int_cmd_valid[m*M_COUNT+n] && !grant[m]; + assign acknowledge[m] = grant[m] && int_cmd_valid[m*M_COUNT+n] && cmd_ready_mux; + end + + reg [RAM_ADDR_WIDTH-1:0] rd_ptr_reg = {RAM_ADDR_WIDTH-1{1'b0}}, rd_ptr_next; + + reg [ADDR_WIDTH-1:0] len_reg = {ADDR_WIDTH{1'b0}}, len_next; + + reg [CL_S_COUNT-1:0] src_reg = 0, src_next; + reg [CMD_ADDR_WIDTH-1:0] id_reg = 0, id_next; + + reg [KEEP_WIDTH-1:0] last_cycle_tkeep_reg = {KEEP_WIDTH{1'b0}}, last_cycle_tkeep_next; + reg [ID_WIDTH-1:0] tid_reg = {ID_WIDTH{1'b0}}, tid_next; + reg [DEST_WIDTH-1:0] tdest_reg = {DEST_WIDTH{1'b0}}, tdest_next; + reg [USER_WIDTH-1:0] tuser_reg = {USER_WIDTH{1'b0}}, tuser_next; + + reg [DATA_WIDTH-1:0] out_axis_tdata_reg = {DATA_WIDTH{1'b0}}, out_axis_tdata_next; + reg [KEEP_WIDTH-1:0] out_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, out_axis_tkeep_next; + reg out_axis_tvalid_reg = 1'b0, out_axis_tvalid_next; + wire out_axis_tready; + reg out_axis_tlast_reg = 1'b0, out_axis_tlast_next; + reg [ID_WIDTH-1:0] out_axis_tid_reg = {ID_WIDTH{1'b0}}, out_axis_tid_next; + reg [DEST_WIDTH-1:0] out_axis_tdest_reg = {DEST_WIDTH{1'b0}}, out_axis_tdest_next; + reg [USER_WIDTH-1:0] out_axis_tuser_reg = {USER_WIDTH{1'b0}}, out_axis_tuser_next; + + reg [RAM_ADDR_WIDTH-1:0] ram_rd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, ram_rd_addr_next; + reg ram_rd_en_reg = 1'b0, ram_rd_en_next; + wire ram_rd_ack; + wire [DATA_WIDTH-1:0] ram_rd_data; + wire ram_rd_data_valid; + + reg cmd_valid_reg = 1'b0, cmd_valid_next; + + reg [CMD_ADDR_WIDTH-1:0] cmd_status_id_reg = {CMD_ADDR_WIDTH{1'b0}}, cmd_status_id_next; + reg [S_COUNT-1:0] cmd_status_valid_reg = 0, cmd_status_valid_next; + + wire [S_COUNT-1:0] port_cmd_status_ready; + for (m = 0; m < S_COUNT; m = m + 1) begin + assign port_cmd_status_ready[m] = int_cmd_status_ready[m*M_COUNT+n]; + end + + reg [DATA_WIDTH-1:0] out_fifo_tdata[31:0]; + reg [KEEP_WIDTH-1:0] out_fifo_tkeep[31:0]; + reg out_fifo_tlast[31:0]; + reg [ID_WIDTH-1:0] out_fifo_tid[31:0]; + reg [DEST_WIDTH-1:0] out_fifo_tdest[31:0]; + reg [USER_WIDTH-1:0] out_fifo_tuser[31:0]; + + reg [5:0] out_fifo_data_wr_ptr_reg = 0; + reg [DATA_WIDTH-1:0] out_fifo_data_wr_tdata; + reg out_fifo_data_wr_en; + reg [5:0] out_fifo_ctrl_wr_ptr_reg = 0; + reg [KEEP_WIDTH-1:0] out_fifo_ctrl_wr_tkeep; + reg out_fifo_ctrl_wr_tlast; + reg [ID_WIDTH-1:0] out_fifo_ctrl_wr_tid; + reg [DEST_WIDTH-1:0] out_fifo_ctrl_wr_tdest; + reg [USER_WIDTH-1:0] out_fifo_ctrl_wr_tuser; + reg out_fifo_ctrl_wr_en; + reg [5:0] out_fifo_rd_ptr_reg = 0; + reg out_fifo_rd_en; + + assign port_ram_rd_addr[n*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH] = ram_rd_addr_reg; + assign port_ram_rd_en[n] = ram_rd_en_reg; + assign ram_rd_ack = port_ram_rd_ack[n]; + assign ram_rd_data = port_ram_rd_data[n*DATA_WIDTH +: DATA_WIDTH]; + assign ram_rd_data_valid = port_ram_rd_data_valid[n]; + + assign cmd_ready_mux = !cmd_valid_reg; + + assign int_cmd_status_id[n*CMD_ADDR_WIDTH +: CMD_ADDR_WIDTH] = cmd_status_id_reg; + assign int_cmd_status_valid[n*S_COUNT +: S_COUNT] = cmd_status_valid_reg; + + always @* begin + rd_ptr_next = rd_ptr_reg; + + len_next = len_reg; + + src_next = src_reg; + id_next = id_reg; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + tid_next = tid_reg; + tdest_next = tdest_reg; + tuser_next = tuser_reg; + + out_axis_tdata_next = out_axis_tdata_reg; + out_axis_tkeep_next = out_axis_tkeep_reg; + out_axis_tvalid_next = out_axis_tvalid_reg && !out_axis_tready; + out_axis_tlast_next = out_axis_tlast_reg; + out_axis_tid_next = out_axis_tid_reg; + out_axis_tdest_next = out_axis_tdest_reg; + out_axis_tuser_next = out_axis_tuser_reg; + + ram_rd_addr_next = ram_rd_addr_reg; + ram_rd_en_next = ram_rd_en_reg && !ram_rd_ack; + + cmd_valid_next = cmd_valid_reg; + + cmd_status_id_next = cmd_status_id_reg; + cmd_status_valid_next = cmd_status_valid_reg & ~port_cmd_status_ready; + + out_fifo_data_wr_tdata = ram_rd_data; + out_fifo_data_wr_en = 1'b0; + out_fifo_ctrl_wr_tkeep = len_reg == 0 ? last_cycle_tkeep_reg : {KEEP_WIDTH{1'b1}}; + out_fifo_ctrl_wr_tlast = len_reg == 0; + out_fifo_ctrl_wr_tid = tid_reg; + out_fifo_ctrl_wr_tdest = tdest_reg; + out_fifo_ctrl_wr_tuser = tuser_reg; + out_fifo_ctrl_wr_en = 1'b0; + out_fifo_rd_en = 1'b0; + + // receive commands + if (!cmd_valid_reg && cmd_valid_mux) begin + cmd_valid_next = 1'b1; + rd_ptr_next = cmd_addr_mux; + len_next = cmd_len_mux; + last_cycle_tkeep_next = cmd_tkeep_mux; + id_next = cmd_id_mux; + tid_next = cmd_tid_mux; + tdest_next = cmd_tdest_mux; + tuser_next = cmd_tuser_mux; + + src_next = grant_encoded; + end + + // process commands and issue memory reads + if (cmd_valid_reg && (!ram_rd_en_reg || ram_rd_ack) && ($unsigned(out_fifo_ctrl_wr_ptr_reg - out_fifo_rd_ptr_reg) < 32)) begin + // update counters + rd_ptr_next[ADDR_WIDTH-1:0] = rd_ptr_reg[ADDR_WIDTH-1:0] + 1; + len_next = len_reg - 1; + + // issue memory read + ram_rd_addr_next = rd_ptr_reg; + ram_rd_en_next = 1'b1; + + // write output control FIFO + out_fifo_ctrl_wr_tkeep = len_reg == 0 ? last_cycle_tkeep_reg : {KEEP_WIDTH{1'b1}}; + out_fifo_ctrl_wr_tlast = len_reg == 0; + out_fifo_ctrl_wr_tid = tid_reg; + out_fifo_ctrl_wr_tdest = tdest_reg; + out_fifo_ctrl_wr_tuser = tuser_reg; + out_fifo_ctrl_wr_en = 1'b1; + + if (len_reg == 0) begin + // indicate operation complete + cmd_status_id_next = id_reg; + cmd_status_valid_next = 1 << src_reg; + cmd_valid_next = 1'b0; + end + end + + // write RAM read data to output data FIFO + if (ram_rd_data_valid) begin + out_fifo_data_wr_tdata = ram_rd_data; + out_fifo_data_wr_en = 1'b1; + end + + // generate output AXI stream data from control and data FIFOs + if ((out_axis_tready || !out_axis_tvalid_reg) && (out_fifo_rd_ptr_reg != out_fifo_ctrl_wr_ptr_reg) && (out_fifo_rd_ptr_reg != out_fifo_data_wr_ptr_reg)) begin + out_fifo_rd_en = 1'b1; + out_axis_tdata_next = out_fifo_tdata[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tkeep_next = out_fifo_tkeep[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tvalid_next = 1'b1; + out_axis_tlast_next = out_fifo_tlast[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tid_next = out_fifo_tid[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tdest_next = out_fifo_tdest[out_fifo_rd_ptr_reg[4:0]]; + out_axis_tuser_next = out_fifo_tuser[out_fifo_rd_ptr_reg[4:0]]; + end + end + + always @(posedge clk) begin + rd_ptr_reg <= rd_ptr_next; + + len_reg <= len_next; + + src_reg <= src_next; + id_reg <= id_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + tid_reg <= tid_next; + tdest_reg <= tdest_next; + tuser_reg <= tuser_next; + + out_axis_tdata_reg <= out_axis_tdata_next; + out_axis_tkeep_reg <= out_axis_tkeep_next; + out_axis_tvalid_reg <= out_axis_tvalid_next; + out_axis_tlast_reg <= out_axis_tlast_next; + out_axis_tid_reg <= out_axis_tid_next; + out_axis_tdest_reg <= out_axis_tdest_next; + out_axis_tuser_reg <= out_axis_tuser_next; + + ram_rd_addr_reg <= ram_rd_addr_next; + ram_rd_en_reg <= ram_rd_en_next; + + cmd_valid_reg <= cmd_valid_next; + + cmd_status_id_reg <= cmd_status_id_next; + cmd_status_valid_reg <= cmd_status_valid_next; + + if (out_fifo_data_wr_en) begin + out_fifo_data_wr_ptr_reg <= out_fifo_data_wr_ptr_reg + 1; + out_fifo_tdata[out_fifo_data_wr_ptr_reg[4:0]] <= out_fifo_data_wr_tdata; + end + + if (out_fifo_ctrl_wr_en) begin + out_fifo_ctrl_wr_ptr_reg <= out_fifo_ctrl_wr_ptr_reg + 1; + out_fifo_tkeep[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tkeep; + out_fifo_tlast[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tlast; + out_fifo_tid[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tid; + out_fifo_tdest[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tdest; + out_fifo_tuser[out_fifo_ctrl_wr_ptr_reg[4:0]] <= out_fifo_ctrl_wr_tuser; + end + + if (out_fifo_rd_en) begin + out_fifo_rd_ptr_reg <= out_fifo_rd_ptr_reg + 1; + end + + if (rst) begin + len_reg <= 0; + out_axis_tvalid_reg <= 1'b0; + ram_rd_en_reg <= 1'b0; + cmd_valid_reg <= 1'b0; + cmd_status_valid_reg <= 0; + out_fifo_data_wr_ptr_reg <= 0; + out_fifo_ctrl_wr_ptr_reg <= 0; + out_fifo_rd_ptr_reg <= 0; + end + end + + axis_adapter #( + .S_DATA_WIDTH(DATA_WIDTH), + .S_KEEP_ENABLE(1), + .S_KEEP_WIDTH(KEEP_WIDTH), + .M_DATA_WIDTH(M_DATA_WIDTH), + .M_KEEP_ENABLE(M_KEEP_ENABLE), + .M_KEEP_WIDTH(M_KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH) + ) + adapter_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(out_axis_tdata_reg), + .s_axis_tkeep(out_axis_tkeep_reg), + .s_axis_tvalid(out_axis_tvalid_reg), + .s_axis_tready(out_axis_tready), + .s_axis_tlast(out_axis_tlast_reg), + .s_axis_tid(out_axis_tid_reg), + .s_axis_tdest(out_axis_tdest_reg), + .s_axis_tuser(out_axis_tuser_reg), + // AXI output + .m_axis_tdata(m_axis_tdata[M_DATA_WIDTH*n +: M_DATA_WIDTH]), + .m_axis_tkeep(m_axis_tkeep[M_KEEP_WIDTH*n +: M_KEEP_WIDTH]), + .m_axis_tvalid(m_axis_tvalid[n]), + .m_axis_tready(m_axis_tready[n]), + .m_axis_tlast(m_axis_tlast[n]), + .m_axis_tid(m_axis_tid[ID_WIDTH*n +: ID_WIDTH]), + .m_axis_tdest(m_axis_tdest[DEST_WIDTH*n +: DEST_WIDTH]), + .m_axis_tuser(m_axis_tuser[USER_WIDTH*n +: USER_WIDTH]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_rate_limit.v b/corundum/lib/eth/lib/axis/rtl/axis_rate_limit.v new file mode 100644 index 0000000000000000000000000000000000000000..bebe1b3ce3d7bd3bcfc80ef7406290210c049947 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_rate_limit.v @@ -0,0 +1,256 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream rate limiter + */ +module axis_rate_limit # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Configuration + */ + input wire [7:0] rate_num, + input wire [7:0] rate_denom, + input wire rate_by_frame +); + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +reg [23:0] acc_reg = 24'd0, acc_next; +reg pause; +reg frame_reg = 1'b0, frame_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +assign s_axis_tready = s_axis_tready_reg; + +always @* begin + acc_next = acc_reg; + pause = 1'b0; + frame_next = frame_reg; + + if (acc_reg >= rate_num) begin + acc_next = acc_reg - rate_num; + end + + if (s_axis_tready && s_axis_tvalid) begin + // read input + frame_next = !s_axis_tlast; + acc_next = acc_reg + (rate_denom - rate_num); + end + + if (acc_next >= rate_num) begin + if (LAST_ENABLE && rate_by_frame) begin + pause = !frame_next; + end else begin + pause = 1'b1; + end + end + + s_axis_tready_next = m_axis_tready_int_early && !pause; + + m_axis_tdata_int = s_axis_tdata; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid && s_axis_tready; + m_axis_tlast_int = s_axis_tlast; + m_axis_tid_int = s_axis_tid; + m_axis_tdest_int = s_axis_tdest; + m_axis_tuser_int = s_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + acc_reg <= 24'd0; + frame_reg <= 1'b0; + s_axis_tready_reg <= 1'b0; + end else begin + acc_reg <= acc_next; + frame_reg <= frame_next; + s_axis_tready_reg <= s_axis_tready_next; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_register.v b/corundum/lib/eth/lib/axis/rtl/axis_register.v new file mode 100644 index 0000000000000000000000000000000000000000..609e9d3e662f023427f892a2aa0d58c309d6e8fb --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_register.v @@ -0,0 +1,276 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream register + */ +module axis_register # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter REG_TYPE = 2 +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +generate + +if (REG_TYPE > 1) begin + // skid buffer, no bubble cycles + + // datapath registers + reg s_axis_tready_reg = 1'b0; + + reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; + reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; + reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + reg m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; + reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; + reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; + reg temp_m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + // datapath control + reg store_axis_input_to_output; + reg store_axis_input_to_temp; + reg store_axis_temp_to_output; + + assign s_axis_tready = s_axis_tready_reg; + + assign m_axis_tdata = m_axis_tdata_reg; + assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = m_axis_tvalid_reg; + assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1; + assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + + // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) + wire s_axis_tready_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !s_axis_tvalid)); + + always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_input_to_output = 1'b0; + store_axis_input_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (s_axis_tready_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + s_axis_tready_reg <= 1'b0; + m_axis_tvalid_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + s_axis_tready_reg <= s_axis_tready_early; + m_axis_tvalid_reg <= m_axis_tvalid_next; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_input_to_output) begin + m_axis_tdata_reg <= s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tkeep; + m_axis_tlast_reg <= s_axis_tlast; + m_axis_tid_reg <= s_axis_tid; + m_axis_tdest_reg <= s_axis_tdest; + m_axis_tuser_reg <= s_axis_tuser; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_input_to_temp) begin + temp_m_axis_tdata_reg <= s_axis_tdata; + temp_m_axis_tkeep_reg <= s_axis_tkeep; + temp_m_axis_tlast_reg <= s_axis_tlast; + temp_m_axis_tid_reg <= s_axis_tid; + temp_m_axis_tdest_reg <= s_axis_tdest; + temp_m_axis_tuser_reg <= s_axis_tuser; + end + end + +end else if (REG_TYPE == 1) begin + // simple register, inserts bubble cycles + + // datapath registers + reg s_axis_tready_reg = 1'b0; + + reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; + reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; + reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; + reg m_axis_tlast_reg = 1'b0; + reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; + reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; + reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + + // datapath control + reg store_axis_input_to_output; + + assign s_axis_tready = s_axis_tready_reg; + + assign m_axis_tdata = m_axis_tdata_reg; + assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = m_axis_tvalid_reg; + assign m_axis_tlast = LAST_ENABLE ? m_axis_tlast_reg : 1'b1; + assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + + // enable ready input next cycle if output buffer will be empty + wire s_axis_tready_early = !m_axis_tvalid_next; + + always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + + store_axis_input_to_output = 1'b0; + + if (s_axis_tready_reg) begin + m_axis_tvalid_next = s_axis_tvalid; + store_axis_input_to_output = 1'b1; + end else if (m_axis_tready) begin + m_axis_tvalid_next = 1'b0; + end + end + + always @(posedge clk) begin + if (rst) begin + s_axis_tready_reg <= 1'b0; + m_axis_tvalid_reg <= 1'b0; + end else begin + s_axis_tready_reg <= s_axis_tready_early; + m_axis_tvalid_reg <= m_axis_tvalid_next; + end + + // datapath + if (store_axis_input_to_output) begin + m_axis_tdata_reg <= s_axis_tdata; + m_axis_tkeep_reg <= s_axis_tkeep; + m_axis_tlast_reg <= s_axis_tlast; + m_axis_tid_reg <= s_axis_tid; + m_axis_tdest_reg <= s_axis_tdest; + m_axis_tuser_reg <= s_axis_tuser; + end + end + +end else begin + // bypass + + assign m_axis_tdata = s_axis_tdata; + assign m_axis_tkeep = KEEP_ENABLE ? s_axis_tkeep : {KEEP_WIDTH{1'b1}}; + assign m_axis_tvalid = s_axis_tvalid; + assign m_axis_tlast = LAST_ENABLE ? s_axis_tlast : 1'b1; + assign m_axis_tid = ID_ENABLE ? s_axis_tid : {ID_WIDTH{1'b0}}; + assign m_axis_tdest = DEST_ENABLE ? s_axis_tdest : {DEST_WIDTH{1'b0}}; + assign m_axis_tuser = USER_ENABLE ? s_axis_tuser : {USER_WIDTH{1'b0}}; + + assign s_axis_tready = m_axis_tready; + +end + +endgenerate + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_srl_fifo.v b/corundum/lib/eth/lib/axis/rtl/axis_srl_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..53f1d3118fd1d46465fcabbf798c9692c3a3527a --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_srl_fifo.v @@ -0,0 +1,195 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream SRL-based FIFO + */ +module axis_srl_fifo # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // FIFO depth in cycles + parameter DEPTH = 16 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Status + */ + output wire [$clog2(DEPTH+1)-1:0] count +); + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [WIDTH-1:0] data_reg[DEPTH-1:0]; +reg [$clog2(DEPTH+1)-1:0] ptr_reg = 0; +reg full_reg = 1'b0, full_next; +reg empty_reg = 1'b1, empty_next; + +wire [WIDTH-1:0] s_axis; + +wire [WIDTH-1:0] m_axis = data_reg[ptr_reg-1]; + +assign s_axis_tready = !full_reg; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = !empty_reg; + +assign m_axis_tdata = m_axis[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +assign count = ptr_reg; + +wire ptr_empty = ptr_reg == 0; +wire ptr_empty1 = ptr_reg == 1; +wire ptr_full = ptr_reg == DEPTH; +wire ptr_full1 = ptr_reg == DEPTH-1; + +reg shift; +reg inc; +reg dec; + +integer i; + +initial begin + for (i = 0; i < DEPTH; i = i + 1) begin + data_reg[i] <= 0; + end +end + +always @* begin + shift = 1'b0; + inc = 1'b0; + dec = 1'b0; + full_next = full_reg; + empty_next = empty_reg; + + if (m_axis_tready && s_axis_tvalid && s_axis_tready) begin + shift = 1'b1; + inc = ptr_empty; + empty_next = 1'b0; + end else if (m_axis_tready && m_axis_tvalid) begin + dec = 1'b1; + full_next = 1'b0; + empty_next = ptr_empty1; + end else if (s_axis_tvalid && s_axis_tready) begin + shift = 1'b1; + inc = 1'b1; + full_next = ptr_full1; + empty_next = 1'b0; + end +end + +always @(posedge clk) begin + if (rst) begin + ptr_reg <= 0; + full_reg <= 1'b0; + empty_reg <= 1'b1; + end else begin + if (inc) begin + ptr_reg <= ptr_reg + 1; + end else if (dec) begin + ptr_reg <= ptr_reg - 1; + end else begin + ptr_reg <= ptr_reg; + end + + full_reg <= full_next; + empty_reg <= empty_next; + end + + if (shift) begin + data_reg[0] <= s_axis; + for (i = 0; i < DEPTH-1; i = i + 1) begin + data_reg[i+1] <= data_reg[i]; + end + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_srl_register.v b/corundum/lib/eth/lib/axis/rtl/axis_srl_register.v new file mode 100644 index 0000000000000000000000000000000000000000..1548d3415cbe4e0e8b1aa7a6f46567c32b58ef59 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_srl_register.v @@ -0,0 +1,154 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream SRL-based FIFO register + */ +module axis_srl_register # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tlast signal + parameter LAST_ENABLE = 1, + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +localparam KEEP_OFFSET = DATA_WIDTH; +localparam LAST_OFFSET = KEEP_OFFSET + (KEEP_ENABLE ? KEEP_WIDTH : 0); +localparam ID_OFFSET = LAST_OFFSET + (LAST_ENABLE ? 1 : 0); +localparam DEST_OFFSET = ID_OFFSET + (ID_ENABLE ? ID_WIDTH : 0); +localparam USER_OFFSET = DEST_OFFSET + (DEST_ENABLE ? DEST_WIDTH : 0); +localparam WIDTH = USER_OFFSET + (USER_ENABLE ? USER_WIDTH : 0); + +reg [WIDTH-1:0] data_reg[1:0]; +reg valid_reg[1:0]; +reg ptr_reg = 0; +reg full_reg = 0; + +wire [WIDTH-1:0] s_axis; + +wire [WIDTH-1:0] m_axis = data_reg[ptr_reg]; + +assign s_axis_tready = !full_reg; + +generate + assign s_axis[DATA_WIDTH-1:0] = s_axis_tdata; + if (KEEP_ENABLE) assign s_axis[KEEP_OFFSET +: KEEP_WIDTH] = s_axis_tkeep; + if (LAST_ENABLE) assign s_axis[LAST_OFFSET] = s_axis_tlast; + if (ID_ENABLE) assign s_axis[ID_OFFSET +: ID_WIDTH] = s_axis_tid; + if (DEST_ENABLE) assign s_axis[DEST_OFFSET +: DEST_WIDTH] = s_axis_tdest; + if (USER_ENABLE) assign s_axis[USER_OFFSET +: USER_WIDTH] = s_axis_tuser; +endgenerate + +assign m_axis_tvalid = valid_reg[ptr_reg]; + +assign m_axis_tdata = m_axis[DATA_WIDTH-1:0]; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis[KEEP_OFFSET +: KEEP_WIDTH] : {KEEP_WIDTH{1'b1}}; +assign m_axis_tlast = LAST_ENABLE ? m_axis[LAST_OFFSET] : 1'b1; +assign m_axis_tid = ID_ENABLE ? m_axis[ID_OFFSET +: ID_WIDTH] : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis[DEST_OFFSET +: DEST_WIDTH] : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis[USER_OFFSET +: USER_WIDTH] : {USER_WIDTH{1'b0}}; + +integer i; + +initial begin + for (i = 0; i < 2; i = i + 1) begin + data_reg[i] <= 0; + valid_reg[i] <= 0; + end +end + +always @(posedge clk) begin + if (rst) begin + ptr_reg <= 0; + full_reg <= 0; + end else begin + // transfer empty to full + full_reg <= !m_axis_tready && m_axis_tvalid; + + // transfer in if not full + if (s_axis_tready) begin + data_reg[0] <= s_axis; + valid_reg[0] <= s_axis_tvalid; + for (i = 0; i < 1; i = i + 1) begin + data_reg[i+1] <= data_reg[i]; + valid_reg[i+1] <= valid_reg[i]; + end + ptr_reg <= valid_reg[0]; + end + + if (m_axis_tready) begin + ptr_reg <= 0; + end + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_stat_counter.v b/corundum/lib/eth/lib/axis/rtl/axis_stat_counter.v new file mode 100644 index 0000000000000000000000000000000000000000..b68fbdd4a49e222425ab848f4183a7948957aca6 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_stat_counter.v @@ -0,0 +1,366 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream statistics counter + */ +module axis_stat_counter # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 64, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Prepend data with tag + parameter TAG_ENABLE = 1, + // Tag field width + parameter TAG_WIDTH = 16, + // Count cycles + parameter TICK_COUNT_ENABLE = 1, + // Cycle counter width + parameter TICK_COUNT_WIDTH = 32, + // Count bytes + parameter BYTE_COUNT_ENABLE = 1, + // Byte counter width + parameter BYTE_COUNT_WIDTH = 32, + // Count frames + parameter FRAME_COUNT_ENABLE = 1, + // Frame counter width + parameter FRAME_COUNT_WIDTH = 32 +) +( + input wire clk, + input wire rst, + + /* + * AXI monitor + */ + input wire [KEEP_WIDTH-1:0] monitor_axis_tkeep, + input wire monitor_axis_tvalid, + input wire monitor_axis_tready, + input wire monitor_axis_tlast, + + /* + * AXI status data output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Configuration + */ + input wire [TAG_WIDTH-1:0] tag, + input wire trigger, + + /* + * Status + */ + output wire busy +); + +localparam TAG_BYTE_WIDTH = (TAG_WIDTH + 7) / 8; +localparam TICK_COUNT_BYTE_WIDTH = (TICK_COUNT_WIDTH + 7) / 8; +localparam BYTE_COUNT_BYTE_WIDTH = (BYTE_COUNT_WIDTH + 7) / 8; +localparam FRAME_COUNT_BYTE_WIDTH = (FRAME_COUNT_WIDTH + 7) / 8; +localparam TOTAL_LENGTH = TAG_BYTE_WIDTH + TICK_COUNT_BYTE_WIDTH + BYTE_COUNT_BYTE_WIDTH + FRAME_COUNT_BYTE_WIDTH; + +// state register +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_OUTPUT_DATA = 2'd1; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg [TICK_COUNT_WIDTH-1:0] tick_count_reg = 0, tick_count_next; +reg [BYTE_COUNT_WIDTH-1:0] byte_count_reg = 0, byte_count_next; +reg [FRAME_COUNT_WIDTH-1:0] frame_count_reg = 0, frame_count_next; +reg frame_reg = 1'b0, frame_next; + +reg store_output; +reg [$clog2(TOTAL_LENGTH)-1:0] frame_ptr_reg = 0, frame_ptr_next; + +reg [TICK_COUNT_WIDTH-1:0] tick_count_output_reg = 0; +reg [BYTE_COUNT_WIDTH-1:0] byte_count_output_reg = 0; +reg [FRAME_COUNT_WIDTH-1:0] frame_count_output_reg = 0; + +reg busy_reg = 1'b0; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign busy = busy_reg; + +integer offset, i, bit_cnt; + +always @* begin + state_next = STATE_IDLE; + + tick_count_next = tick_count_reg; + byte_count_next = byte_count_reg; + frame_count_next = frame_count_reg; + frame_next = frame_reg; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + store_output = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + // data readout + + case (state_reg) + STATE_IDLE: begin + if (trigger) begin + store_output = 1'b1; + tick_count_next = 0; + byte_count_next = 0; + frame_count_next = 0; + frame_ptr_next = 0; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = 1; + if (TAG_ENABLE) begin + m_axis_tdata_int = tag[(TAG_BYTE_WIDTH-1)*8 +: 8]; + end else if (TICK_COUNT_ENABLE) begin + m_axis_tdata_int = tick_count_reg[(TICK_COUNT_BYTE_WIDTH-1)*8 +: 8]; + end else if (BYTE_COUNT_ENABLE) begin + m_axis_tdata_int = byte_count_reg[(BYTE_COUNT_BYTE_WIDTH-1)*8 +: 8]; + end else if (FRAME_COUNT_ENABLE) begin + m_axis_tdata_int = frame_count_reg[(FRAME_COUNT_BYTE_WIDTH-1)*8 +: 8]; + end + m_axis_tvalid_int = 1'b1; + end + + state_next = STATE_OUTPUT_DATA; + end else begin + state_next = STATE_IDLE; + end + end + STATE_OUTPUT_DATA: begin + if (m_axis_tready_int_reg) begin + state_next = STATE_OUTPUT_DATA; + frame_ptr_next = frame_ptr_reg + 1; + m_axis_tvalid_int = 1'b1; + + offset = 0; + if (TAG_ENABLE) begin + for (i = TAG_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = tag[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (TICK_COUNT_ENABLE) begin + for (i = TICK_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = tick_count_output_reg[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (BYTE_COUNT_ENABLE) begin + for (i = BYTE_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = byte_count_output_reg[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (FRAME_COUNT_ENABLE) begin + for (i = FRAME_COUNT_BYTE_WIDTH-1; i >= 0; i = i - 1) begin + if (frame_ptr_reg == offset) begin + m_axis_tdata_int = frame_count_output_reg[i*8 +: 8]; + end + offset = offset + 1; + end + end + if (frame_ptr_reg == offset-1) begin + m_axis_tlast_int = 1'b1; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_OUTPUT_DATA; + end + end + endcase + + // stats collection + + // increment tick count by number of words that can be transferred per cycle + tick_count_next = tick_count_next + (KEEP_ENABLE ? KEEP_WIDTH : 1); + + if (monitor_axis_tready && monitor_axis_tvalid) begin + // valid transfer cycle + + // increment byte count by number of words transferred + if (KEEP_ENABLE) begin + bit_cnt = 0; + for (i = 0; i <= KEEP_WIDTH; i = i + 1) begin + //bit_cnt = bit_cnt + monitor_axis_tkeep[i]; + if (monitor_axis_tkeep == ({KEEP_WIDTH{1'b1}}) >> (KEEP_WIDTH-i)) bit_cnt = i; + end + byte_count_next = byte_count_next + bit_cnt; + end else begin + byte_count_next = byte_count_next + 1; + end + + // count frames + if (monitor_axis_tlast) begin + // end of frame + frame_next = 1'b0; + end else if (!frame_reg) begin + // first word after end of frame + frame_count_next = frame_count_next + 1; + frame_next = 1'b1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + tick_count_reg <= 0; + byte_count_reg <= 0; + frame_count_reg <= 0; + frame_reg <= 1'b0; + frame_ptr_reg <= 0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + tick_count_reg <= tick_count_next; + byte_count_reg <= byte_count_next; + frame_count_reg <= frame_count_next; + frame_reg <= frame_next; + frame_ptr_reg <= frame_ptr_next; + + busy_reg <= state_next != STATE_IDLE; + end + + if (store_output) begin + tick_count_output_reg <= tick_count_reg; + byte_count_output_reg <= byte_count_reg; + frame_count_output_reg <= frame_count_reg; + end +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_switch.v b/corundum/lib/eth/lib/axis/rtl/axis_switch.v new file mode 100644 index 0000000000000000000000000000000000000000..0e785851e717f83a7c23ee95a347cd73fe2aa220 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_switch.v @@ -0,0 +1,350 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream switch + */ +module axis_switch # +( + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Number of AXI stream outputs + parameter M_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // tdest signal width + // must be wide enough to uniquely address outputs + parameter DEST_WIDTH = $clog2(M_COUNT), + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Output interface routing base tdest selection + // Concatenate M_COUNT DEST_WIDTH sized constants + // Port selected if M_BASE <= tdest <= M_TOP + // set to zero for default routing with tdest MSBs as port index + parameter M_BASE = 0, + // Output interface routing top tdest selection + // Concatenate M_COUNT DEST_WIDTH sized constants + // Port selected if M_BASE <= tdest <= M_TOP + // set to zero to inherit from M_BASE + parameter M_TOP = 0, + // Interface connection control + // M_COUNT concatenated fields of S_COUNT bits + parameter M_CONNECT = {M_COUNT{{S_COUNT{1'b1}}}}, + // Input interface register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_REG_TYPE = 0, + // Output interface register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_REG_TYPE = 2, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "ROUND_ROBIN", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream outputs + */ + output wire [M_COUNT*DATA_WIDTH-1:0] m_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_axis_tkeep, + output wire [M_COUNT-1:0] m_axis_tvalid, + input wire [M_COUNT-1:0] m_axis_tready, + output wire [M_COUNT-1:0] m_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); +parameter CL_M_COUNT = $clog2(M_COUNT); + +integer i, j; + +// check configuration +initial begin + if (DEST_WIDTH < CL_M_COUNT) begin + $error("Error: DEST_WIDTH too small for port count (instance %m)"); + $finish; + end + + if (M_BASE == 0) begin + // M_BASE is zero, route with tdest as port index + end else if (M_TOP == 0) begin + // M_TOP is zero, assume equal to M_BASE + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = i+1; j < M_COUNT; j = j + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] == M_BASE[j*DEST_WIDTH +: DEST_WIDTH]) begin + $display("%d: %08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH]); + $display("%d: %08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH]); + $error("Error: ranges overlap (instance %m)"); + $finish; + end + end + end + end else begin + for (i = 0; i < M_COUNT; i = i + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] > M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $error("Error: invalid range (instance %m)"); + $finish; + end + end + + for (i = 0; i < M_COUNT; i = i + 1) begin + for (j = i+1; j < M_COUNT; j = j + 1) begin + if (M_BASE[i*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[j*DEST_WIDTH +: DEST_WIDTH] && M_BASE[j*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[i*DEST_WIDTH +: DEST_WIDTH]) begin + $display("%d: %08x-%08x", i, M_BASE[i*DEST_WIDTH +: DEST_WIDTH], M_TOP[i*DEST_WIDTH +: DEST_WIDTH]); + $display("%d: %08x-%08x", j, M_BASE[j*DEST_WIDTH +: DEST_WIDTH], M_TOP[j*DEST_WIDTH +: DEST_WIDTH]); + $error("Error: ranges overlap (instance %m)"); + $finish; + end + end + end + end +end + +wire [S_COUNT*DATA_WIDTH-1:0] int_s_axis_tdata; +wire [S_COUNT*KEEP_WIDTH-1:0] int_s_axis_tkeep; +wire [S_COUNT-1:0] int_s_axis_tvalid; +wire [S_COUNT-1:0] int_s_axis_tready; +wire [S_COUNT-1:0] int_s_axis_tlast; +wire [S_COUNT*ID_WIDTH-1:0] int_s_axis_tid; +wire [S_COUNT*DEST_WIDTH-1:0] int_s_axis_tdest; +wire [S_COUNT*USER_WIDTH-1:0] int_s_axis_tuser; + +wire [S_COUNT*M_COUNT-1:0] int_axis_tvalid; +wire [M_COUNT*S_COUNT-1:0] int_axis_tready; + +generate + + genvar m, n; + + for (m = 0; m < S_COUNT; m = m + 1) begin : s_ifaces + + // decoding + reg [CL_M_COUNT-1:0] select_reg = 0, select_next; + reg drop_reg = 1'b0, drop_next; + reg select_valid_reg = 1'b0, select_valid_next; + + integer k; + + always @* begin + select_next = select_reg; + drop_next = drop_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]); + select_valid_next = select_valid_reg && !(int_s_axis_tvalid[m] && int_s_axis_tready[m] && int_s_axis_tlast[m]); + + if (int_s_axis_tvalid[m] && !select_valid_reg) begin + select_next = 1'b0; + select_valid_next = 1'b0; + drop_next = 1'b1; + for (k = 0; k < M_COUNT; k = k + 1) begin + if (M_BASE == 0) begin + // M_BASE is zero, route with $clog2(M_COUNT) MSBs of tdest as port index + if (int_s_axis_tdest[m*DEST_WIDTH+(DEST_WIDTH-CL_M_COUNT) +: CL_M_COUNT] == k && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end else if (M_TOP == 0) begin + // M_TOP is zero, assume equal to M_BASE + if (int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] == M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end else begin + if (int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] >= M_BASE[k*DEST_WIDTH +: DEST_WIDTH] && int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH] <= M_TOP[k*DEST_WIDTH +: DEST_WIDTH] && (M_CONNECT & (1 << (m+k*S_COUNT)))) begin + select_next = k; + select_valid_next = 1'b1; + drop_next = 1'b0; + end + end + end + end + end + + always @(posedge clk) begin + if (rst) begin + select_valid_reg <= 1'b0; + end else begin + select_valid_reg <= select_valid_next; + end + + select_reg <= select_next; + drop_reg <= drop_next; + end + + // forwarding + assign int_axis_tvalid[m*M_COUNT +: M_COUNT] = (int_s_axis_tvalid[m] && select_valid_reg && !drop_reg) << select_reg; + assign int_s_axis_tready[m] = int_axis_tready[select_reg*S_COUNT+m] || drop_reg; + + // S side register + axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(S_REG_TYPE) + ) + reg_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata[m*DATA_WIDTH +: DATA_WIDTH]), + .s_axis_tkeep(s_axis_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH]), + .s_axis_tvalid(s_axis_tvalid[m]), + .s_axis_tready(s_axis_tready[m]), + .s_axis_tlast(s_axis_tlast[m]), + .s_axis_tid(s_axis_tid[m*ID_WIDTH +: ID_WIDTH]), + .s_axis_tdest(s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH]), + .s_axis_tuser(s_axis_tuser[m*USER_WIDTH +: USER_WIDTH]), + // AXI output + .m_axis_tdata(int_s_axis_tdata[m*DATA_WIDTH +: DATA_WIDTH]), + .m_axis_tkeep(int_s_axis_tkeep[m*KEEP_WIDTH +: KEEP_WIDTH]), + .m_axis_tvalid(int_s_axis_tvalid[m]), + .m_axis_tready(int_s_axis_tready[m]), + .m_axis_tlast(int_s_axis_tlast[m]), + .m_axis_tid(int_s_axis_tid[m*ID_WIDTH +: ID_WIDTH]), + .m_axis_tdest(int_s_axis_tdest[m*DEST_WIDTH +: DEST_WIDTH]), + .m_axis_tuser(int_s_axis_tuser[m*USER_WIDTH +: USER_WIDTH]) + ); + end // s_ifaces + + for (n = 0; n < M_COUNT; n = n + 1) begin : m_ifaces + + // arbitration + wire [S_COUNT-1:0] request; + wire [S_COUNT-1:0] acknowledge; + wire [S_COUNT-1:0] grant; + wire grant_valid; + wire [CL_S_COUNT-1:0] grant_encoded; + + arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) + ) + arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) + ); + + // mux + wire [DATA_WIDTH-1:0] s_axis_tdata_mux = int_s_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; + wire [KEEP_WIDTH-1:0] s_axis_tkeep_mux = int_s_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; + wire s_axis_tvalid_mux = int_axis_tvalid[grant_encoded*M_COUNT+n] && grant_valid; + wire s_axis_tready_mux; + wire s_axis_tlast_mux = int_s_axis_tlast[grant_encoded]; + wire [ID_WIDTH-1:0] s_axis_tid_mux = int_s_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; + wire [DEST_WIDTH-1:0] s_axis_tdest_mux = int_s_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; + wire [USER_WIDTH-1:0] s_axis_tuser_mux = int_s_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + + assign int_axis_tready[n*S_COUNT +: S_COUNT] = (grant_valid && s_axis_tready_mux) << grant_encoded; + + for (m = 0; m < S_COUNT; m = m + 1) begin + assign request[m] = int_axis_tvalid[m*M_COUNT+n] && !grant[m]; + assign acknowledge[m] = grant[m] && int_axis_tvalid[m*M_COUNT+n] && s_axis_tlast_mux && s_axis_tready_mux; + end + + // M side register + axis_register #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(1), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .REG_TYPE(M_REG_TYPE) + ) + reg_inst ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_axis_tdata_mux), + .s_axis_tkeep(s_axis_tkeep_mux), + .s_axis_tvalid(s_axis_tvalid_mux), + .s_axis_tready(s_axis_tready_mux), + .s_axis_tlast(s_axis_tlast_mux), + .s_axis_tid(s_axis_tid_mux), + .s_axis_tdest(s_axis_tdest_mux), + .s_axis_tuser(s_axis_tuser_mux), + // AXI output + .m_axis_tdata(m_axis_tdata[n*DATA_WIDTH +: DATA_WIDTH]), + .m_axis_tkeep(m_axis_tkeep[n*KEEP_WIDTH +: KEEP_WIDTH]), + .m_axis_tvalid(m_axis_tvalid[n]), + .m_axis_tready(m_axis_tready[n]), + .m_axis_tlast(m_axis_tlast[n]), + .m_axis_tid(m_axis_tid[n*ID_WIDTH +: ID_WIDTH]), + .m_axis_tdest(m_axis_tdest[n*DEST_WIDTH +: DEST_WIDTH]), + .m_axis_tuser(m_axis_tuser[n*USER_WIDTH +: USER_WIDTH]) + ); + end // m_ifaces + +endgenerate + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/axis_switch_wrap.py b/corundum/lib/eth/lib/axis/rtl/axis_switch_wrap.py new file mode 100755 index 0000000000000000000000000000000000000000..a55e83299eecbf5b6eba79b48899d30627205fd7 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_switch_wrap.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +""" +Generates an AXI Stream switch wrapper with the specified number of ports +""" + +from __future__ import print_function + +import argparse +import math +from jinja2 import Template + +def main(): + parser = argparse.ArgumentParser(description=__doc__.strip()) + parser.add_argument('-p', '--ports', type=int, default=[4], nargs='+', help="number of ports") + parser.add_argument('-n', '--name', type=str, help="module name") + parser.add_argument('-o', '--output', type=str, help="output file name") + + args = parser.parse_args() + + try: + generate(**args.__dict__) + except IOError as ex: + print(ex) + exit(1) + +def generate(ports=4, name=None, output=None): + if type(ports) is int: + m = n = ports + elif len(ports) == 1: + m = n = ports[0] + else: + m, n = ports + + if name is None: + name = "axis_switch_wrap_{0}x{1}".format(m, n) + + if output is None: + output = name + ".v" + + print("Opening file '{0}'...".format(output)) + + output_file = open(output, 'w') + + print("Generating {0}x{1} port AXI stream switch wrapper {2}...".format(m, n, name)) + + cm = int(math.ceil(math.log(m, 2))) + cn = int(math.ceil(math.log(n, 2))) + + t = Template(u"""/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream {{m}}x{{n}} switch (wrapper) + */ +module {{name}} # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // tdest signal width + // must be wide enough to uniquely address outputs + parameter DEST_WIDTH = {{cm}}, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, +{%- for p in range(n) %} + // Output interface routing base tdest selection + // Port selected if M_BASE <= tdest <= M_TOP + parameter M{{'%02d'%p}}_BASE = {{p}}, + // Output interface routing top tdest selection + // Port selected if M_BASE <= tdest <= M_TOP + parameter M{{'%02d'%p}}_TOP = {{p}}, + // Interface connection control + parameter M{{'%02d'%p}}_CONNECT = {{m}}'b{% for p in range(m) %}1{% endfor %}, +{%- endfor %} + // Input interface register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter S_REG_TYPE = 0, + // Output interface register type + // 0 to bypass, 1 for simple buffer, 2 for skid buffer + parameter M_REG_TYPE = 2, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "ROUND_ROBIN", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ +{%- for p in range(m) %} + input wire [DATA_WIDTH-1:0] s{{'%02d'%p}}_axis_tdata, + input wire [KEEP_WIDTH-1:0] s{{'%02d'%p}}_axis_tkeep, + input wire s{{'%02d'%p}}_axis_tvalid, + output wire s{{'%02d'%p}}_axis_tready, + input wire s{{'%02d'%p}}_axis_tlast, + input wire [ID_WIDTH-1:0] s{{'%02d'%p}}_axis_tid, + input wire [DEST_WIDTH-1:0] s{{'%02d'%p}}_axis_tdest, + input wire [USER_WIDTH-1:0] s{{'%02d'%p}}_axis_tuser, +{% endfor %} + /* + * AXI Stream outputs + */ +{%- for p in range(n) %} + output wire [DATA_WIDTH-1:0] m{{'%02d'%p}}_axis_tdata, + output wire [KEEP_WIDTH-1:0] m{{'%02d'%p}}_axis_tkeep, + output wire m{{'%02d'%p}}_axis_tvalid, + input wire m{{'%02d'%p}}_axis_tready, + output wire m{{'%02d'%p}}_axis_tlast, + output wire [ID_WIDTH-1:0] m{{'%02d'%p}}_axis_tid, + output wire [DEST_WIDTH-1:0] m{{'%02d'%p}}_axis_tdest, + output wire [USER_WIDTH-1:0] m{{'%02d'%p}}_axis_tuser{% if not loop.last %},{% endif %} +{% endfor -%} +); + +// parameter sizing helpers +function [31:0] w_32(input [31:0] val); + w_32 = val; +endfunction + +function [S_COUNT-1:0] w_s(input [S_COUNT-1:0] val); + w_s = val; +endfunction + +axis_switch #( + .S_COUNT({{m}}), + .M_COUNT({{n}}), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .M_BASE({ {% for p in range(n-1,-1,-1) %}w_32(M{{'%02d'%p}}_BASE){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_TOP({ {% for p in range(n-1,-1,-1) %}w_32(M{{'%02d'%p}}_TOP){% if not loop.last %}, {% endif %}{% endfor %} }), + .M_CONNECT({ {% for p in range(n-1,-1,-1) %}w_s(M{{'%02d'%p}}_CONNECT){% if not loop.last %}, {% endif %}{% endfor %} }), + .S_REG_TYPE(S_REG_TYPE), + .M_REG_TYPE(M_REG_TYPE), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +axis_switch_inst ( + .clk(clk), + .rst(rst), + // AXI inputs + .s_axis_tdata({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tkeep({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tvalid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tready({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tlast({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tid({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tdest({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .s_axis_tuser({ {% for p in range(m-1,-1,-1) %}s{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }), + // AXI outputs + .m_axis_tdata({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdata{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tkeep({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tkeep{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tvalid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tvalid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tready({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tready{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tlast({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tlast{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tid({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tid{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tdest({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tdest{% if not loop.last %}, {% endif %}{% endfor %} }), + .m_axis_tuser({ {% for p in range(n-1,-1,-1) %}m{{'%02d'%p}}_axis_tuser{% if not loop.last %}, {% endif %}{% endfor %} }) +); + +endmodule + +""") + + output_file.write(t.render( + m=m, + n=n, + cm=cm, + cn=cn, + name=name + )) + + print("Done") + +if __name__ == "__main__": + main() + diff --git a/corundum/lib/eth/lib/axis/rtl/axis_tap.v b/corundum/lib/eth/lib/axis/rtl/axis_tap.v new file mode 100644 index 0000000000000000000000000000000000000000..543010e4001eb6cf226b690ca97e7bd311743edd --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/axis_tap.v @@ -0,0 +1,328 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream tap + */ +module axis_tap # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // tuser value for bad frame marker + parameter USER_BAD_FRAME_VALUE = 1'b1, + // tuser mask for bad frame marker + parameter USER_BAD_FRAME_MASK = 1'b1 +) +( + input wire clk, + input wire rst, + + /* + * AXI tap + */ + input wire [DATA_WIDTH-1:0] tap_axis_tdata, + input wire [KEEP_WIDTH-1:0] tap_axis_tkeep, + input wire tap_axis_tvalid, + input wire tap_axis_tready, + input wire tap_axis_tlast, + input wire [ID_WIDTH-1:0] tap_axis_tid, + input wire [DEST_WIDTH-1:0] tap_axis_tdest, + input wire [USER_WIDTH-1:0] tap_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +// datapath control signals +reg store_last_word; + +reg [ID_WIDTH-1:0] last_word_id_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] last_word_dest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] last_word_user_reg = {USER_WIDTH{1'b0}}; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_TRANSFER = 2'd1, + STATE_TRUNCATE = 2'd2, + STATE_WAIT = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg frame_reg = 1'b0, frame_next; + +always @* begin + state_next = STATE_IDLE; + + store_last_word = 1'b0; + + frame_next = frame_reg; + + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {KEEP_WIDTH{1'b0}}; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tid_int = {ID_WIDTH{1'b0}}; + m_axis_tdest_int = {DEST_WIDTH{1'b0}}; + m_axis_tuser_int = {USER_WIDTH{1'b0}}; + + if (tap_axis_tready && tap_axis_tvalid) begin + frame_next = !tap_axis_tlast; + end + + case (state_reg) + STATE_IDLE: begin + if (tap_axis_tready && tap_axis_tvalid) begin + // start of frame + if (m_axis_tready_int_reg) begin + m_axis_tdata_int = tap_axis_tdata; + m_axis_tkeep_int = tap_axis_tkeep; + m_axis_tvalid_int = tap_axis_tvalid && tap_axis_tready; + m_axis_tlast_int = tap_axis_tlast; + m_axis_tid_int = tap_axis_tid; + m_axis_tdest_int = tap_axis_tdest; + m_axis_tuser_int = tap_axis_tuser; + if (tap_axis_tlast) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRANSFER; + end + end else begin + state_next = STATE_WAIT; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_TRANSFER: begin + if (tap_axis_tready && tap_axis_tvalid) begin + // transfer data + if (m_axis_tready_int_reg) begin + m_axis_tdata_int = tap_axis_tdata; + m_axis_tkeep_int = tap_axis_tkeep; + m_axis_tvalid_int = tap_axis_tvalid && tap_axis_tready; + m_axis_tlast_int = tap_axis_tlast; + m_axis_tid_int = tap_axis_tid; + m_axis_tdest_int = tap_axis_tdest; + m_axis_tuser_int = tap_axis_tuser; + if (tap_axis_tlast) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_TRANSFER; + end + end else begin + store_last_word = 1'b1; + state_next = STATE_TRUNCATE; + end + end else begin + state_next = STATE_TRANSFER; + end + end + STATE_TRUNCATE: begin + if (m_axis_tready_int_reg) begin + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {{KEEP_WIDTH-1{1'b0}}, 1'b1}; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tid_int = last_word_id_reg; + m_axis_tdest_int = last_word_dest_reg; + m_axis_tuser_int = (last_word_user_reg & ~USER_BAD_FRAME_MASK) | (USER_BAD_FRAME_VALUE & USER_BAD_FRAME_MASK); + if (frame_next) begin + state_next = STATE_WAIT; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRUNCATE; + end + end + STATE_WAIT: begin + if (tap_axis_tready && tap_axis_tvalid) begin + if (tap_axis_tlast) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT; + end + end else begin + state_next = STATE_WAIT; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + frame_reg <= 1'b0; + end else begin + state_reg <= state_next; + frame_reg <= frame_next; + end + + if (store_last_word) begin + last_word_id_reg <= tap_axis_tid; + last_word_dest_reg <= tap_axis_tdest; + last_word_user_reg <= tap_axis_tuser; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/ll_axis_bridge.v b/corundum/lib/eth/lib/axis/rtl/ll_axis_bridge.v new file mode 100644 index 0000000000000000000000000000000000000000..ab2aeab01b6074c0a98ba5535b0f5fc7a4a96a18 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/ll_axis_bridge.v @@ -0,0 +1,64 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * LocalLink to AXI4-Stream bridge + */ +module ll_axis_bridge # +( + parameter DATA_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * LocalLink input + */ + input wire [DATA_WIDTH-1:0] ll_data_in, + input wire ll_sof_in_n, + input wire ll_eof_in_n, + input wire ll_src_rdy_in_n, + output wire ll_dst_rdy_out_n, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast +); + +assign m_axis_tdata = ll_data_in; +assign m_axis_tvalid = !ll_src_rdy_in_n; +assign m_axis_tlast = !ll_eof_in_n; + +assign ll_dst_rdy_out_n = !m_axis_tready; + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/priority_encoder.v b/corundum/lib/eth/lib/axis/rtl/priority_encoder.v new file mode 100644 index 0000000000000000000000000000000000000000..73030630244adccf1ab3be09177c060f3f6cfb7e --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/priority_encoder.v @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Priority encoder module + */ +module priority_encoder # +( + parameter WIDTH = 4, + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire [WIDTH-1:0] input_unencoded, + output wire output_valid, + output wire [$clog2(WIDTH)-1:0] output_encoded, + output wire [WIDTH-1:0] output_unencoded +); + +// power-of-two width +parameter W1 = 2**$clog2(WIDTH); +parameter W2 = W1/2; + +generate + if (WIDTH == 1) begin + // one input + assign output_valid = input_unencoded; + assign output_encoded = 0; + end else if (WIDTH == 2) begin + // two inputs - just an OR gate + assign output_valid = |input_unencoded; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = input_unencoded[1]; + end else begin + assign output_encoded = ~input_unencoded[0]; + end + end else begin + // more than two inputs - split into two parts and recurse + // also pad input to correct power-of-two width + wire [$clog2(W2)-1:0] out1, out2; + wire valid1, valid2; + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst1 ( + .input_unencoded(input_unencoded[W2-1:0]), + .output_valid(valid1), + .output_encoded(out1) + ); + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst2 ( + .input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}), + .output_valid(valid2), + .output_encoded(out2) + ); + // multiplexer to select part + assign output_valid = valid1 | valid2; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1}; + end else begin + assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2}; + end + end +endgenerate + +// unencoded output +assign output_unencoded = 1 << output_encoded; + +endmodule diff --git a/corundum/lib/eth/lib/axis/rtl/sync_reset.v b/corundum/lib/eth/lib/axis/rtl/sync_reset.v new file mode 100644 index 0000000000000000000000000000000000000000..65c5cfa760f7c0a9e40b956bbb42310e38161b86 --- /dev/null +++ b/corundum/lib/eth/lib/axis/rtl/sync_reset.v @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2014-2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog-2001 + +`timescale 1ns / 1ps + +/* + * Synchronizes an active-high asynchronous reset signal to a given clock by + * using a pipeline of N registers. + */ +module sync_reset # +( + // depth of synchronizer + parameter N = 2 +) +( + input wire clk, + input wire rst, + output wire out +); + +(* srl_style = "register" *) +reg [N-1:0] sync_reg = {N{1'b1}}; + +assign out = sync_reg[N-1]; + +always @(posedge clk or posedge rst) begin + if (rst) begin + sync_reg <= {N{1'b1}}; + end else begin + sync_reg <= {sync_reg[N-2:0], 1'b0}; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/arp.v b/corundum/lib/eth/rtl/arp.v new file mode 100644 index 0000000000000000000000000000000000000000..76ad2271b3bd8000037f8e601e1aa7340e0d3a78 --- /dev/null +++ b/corundum/lib/eth/rtl/arp.v @@ -0,0 +1,440 @@ +/* + +Copyright (c) 2014-2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * ARP block for IPv4, ethernet frame interface + */ +module arp # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Log2 of ARP cache size + parameter CACHE_ADDR_WIDTH = 9, + // ARP request retry count + parameter REQUEST_RETRY_COUNT = 4, + // ARP request retry interval (in cycles) + parameter REQUEST_RETRY_INTERVAL = 125000000*2, + // ARP request timeout (in cycles) + parameter REQUEST_TIMEOUT = 125000000*30 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * ARP requests + */ + input wire arp_request_valid, + output wire arp_request_ready, + input wire [31:0] arp_request_ip, + output wire arp_response_valid, + input wire arp_response_ready, + output wire arp_response_error, + output wire [47:0] arp_response_mac, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_cache +); + +localparam [15:0] + ARP_OPER_ARP_REQUEST = 16'h0001, + ARP_OPER_ARP_REPLY = 16'h0002, + ARP_OPER_INARP_REQUEST = 16'h0008, + ARP_OPER_INARP_REPLY = 16'h0009; + +wire incoming_frame_valid; +reg incoming_frame_ready; +wire [47:0] incoming_eth_dest_mac; +wire [47:0] incoming_eth_src_mac; +wire [15:0] incoming_eth_type; +wire [15:0] incoming_arp_htype; +wire [15:0] incoming_arp_ptype; +wire [7:0] incoming_arp_hlen; +wire [7:0] incoming_arp_plen; +wire [15:0] incoming_arp_oper; +wire [47:0] incoming_arp_sha; +wire [31:0] incoming_arp_spa; +wire [47:0] incoming_arp_tha; +wire [31:0] incoming_arp_tpa; + +/* + * ARP frame processing + */ +arp_eth_rx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH) +) +arp_eth_rx_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // ARP frame output + .m_frame_valid(incoming_frame_valid), + .m_frame_ready(incoming_frame_ready), + .m_eth_dest_mac(incoming_eth_dest_mac), + .m_eth_src_mac(incoming_eth_src_mac), + .m_eth_type(incoming_eth_type), + .m_arp_htype(incoming_arp_htype), + .m_arp_ptype(incoming_arp_ptype), + .m_arp_hlen(incoming_arp_hlen), + .m_arp_plen(incoming_arp_plen), + .m_arp_oper(incoming_arp_oper), + .m_arp_sha(incoming_arp_sha), + .m_arp_spa(incoming_arp_spa), + .m_arp_tha(incoming_arp_tha), + .m_arp_tpa(incoming_arp_tpa), + // Status signals + .busy(), + .error_header_early_termination(), + .error_invalid_header() +); + +reg outgoing_frame_valid_reg = 1'b0, outgoing_frame_valid_next; +wire outgoing_frame_ready; +reg [47:0] outgoing_eth_dest_mac_reg = 48'd0, outgoing_eth_dest_mac_next; +reg [15:0] outgoing_arp_oper_reg = 16'd0, outgoing_arp_oper_next; +reg [47:0] outgoing_arp_tha_reg = 48'd0, outgoing_arp_tha_next; +reg [31:0] outgoing_arp_tpa_reg = 32'd0, outgoing_arp_tpa_next; + +arp_eth_tx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(KEEP_ENABLE), + .KEEP_WIDTH(KEEP_WIDTH) +) +arp_eth_tx_inst ( + .clk(clk), + .rst(rst), + // ARP frame input + .s_frame_valid(outgoing_frame_valid_reg), + .s_frame_ready(outgoing_frame_ready), + .s_eth_dest_mac(outgoing_eth_dest_mac_reg), + .s_eth_src_mac(local_mac), + .s_eth_type(16'h0806), + .s_arp_htype(16'h0001), + .s_arp_ptype(16'h0800), + .s_arp_oper(outgoing_arp_oper_reg), + .s_arp_sha(local_mac), + .s_arp_spa(local_ip), + .s_arp_tha(outgoing_arp_tha_reg), + .s_arp_tpa(outgoing_arp_tpa_reg), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy() +); + +reg cache_query_request_valid_reg = 1'b0, cache_query_request_valid_next; +reg [31:0] cache_query_request_ip_reg = 32'd0, cache_query_request_ip_next; +wire cache_query_response_valid; +wire cache_query_response_error; +wire [47:0] cache_query_response_mac; + +reg cache_write_request_valid_reg = 1'b0, cache_write_request_valid_next; +reg [31:0] cache_write_request_ip_reg = 32'd0, cache_write_request_ip_next; +reg [47:0] cache_write_request_mac_reg = 48'd0, cache_write_request_mac_next; + +/* + * ARP cache + */ +arp_cache #( + .CACHE_ADDR_WIDTH(CACHE_ADDR_WIDTH) +) +arp_cache_inst ( + .clk(clk), + .rst(rst), + // Query cache + .query_request_valid(cache_query_request_valid_reg), + .query_request_ready(), + .query_request_ip(cache_query_request_ip_reg), + .query_response_valid(cache_query_response_valid), + .query_response_ready(1'b1), + .query_response_error(cache_query_response_error), + .query_response_mac(cache_query_response_mac), + // Write cache + .write_request_valid(cache_write_request_valid_reg), + .write_request_ready(), + .write_request_ip(cache_write_request_ip_reg), + .write_request_mac(cache_write_request_mac_reg), + // Configuration + .clear_cache(clear_cache) +); + +reg arp_request_operation_reg = 1'b0, arp_request_operation_next; + +reg arp_request_ready_reg = 1'b0, arp_request_ready_next; +reg [31:0] arp_request_ip_reg = 32'd0, arp_request_ip_next; + +reg arp_response_valid_reg = 1'b0, arp_response_valid_next; +reg arp_response_error_reg = 1'b0, arp_response_error_next; +reg [47:0] arp_response_mac_reg = 48'd0, arp_response_mac_next; + +reg [5:0] arp_request_retry_cnt_reg = 6'd0, arp_request_retry_cnt_next; +reg [35:0] arp_request_timer_reg = 36'd0, arp_request_timer_next; + +assign arp_request_ready = arp_request_ready_reg; + +assign arp_response_valid = arp_response_valid_reg; +assign arp_response_error = arp_response_error_reg; +assign arp_response_mac = arp_response_mac_reg; + +always @* begin + incoming_frame_ready = 1'b0; + + outgoing_frame_valid_next = outgoing_frame_valid_reg && !outgoing_frame_ready; + outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg; + outgoing_arp_oper_next = outgoing_arp_oper_reg; + outgoing_arp_tha_next = outgoing_arp_tha_reg; + outgoing_arp_tpa_next = outgoing_arp_tpa_reg; + + cache_query_request_valid_next = 1'b0; + cache_query_request_ip_next = cache_query_request_ip_reg; + + cache_write_request_valid_next = 1'b0; + cache_write_request_mac_next = cache_write_request_mac_reg; + cache_write_request_ip_next = cache_write_request_ip_reg; + + arp_request_ready_next = 1'b0; + arp_request_ip_next = arp_request_ip_reg; + arp_request_operation_next = arp_request_operation_reg; + arp_request_retry_cnt_next = arp_request_retry_cnt_reg; + arp_request_timer_next = arp_request_timer_reg; + arp_response_valid_next = arp_response_valid_reg && !arp_response_ready; + arp_response_error_next = 1'b0; + arp_response_mac_next = 48'd0; + + // manage incoming frames + incoming_frame_ready = outgoing_frame_ready; + if (incoming_frame_valid && incoming_frame_ready) begin + if (incoming_eth_type == 16'h0806 && incoming_arp_htype == 16'h0001 && incoming_arp_ptype == 16'h0800) begin + // store sender addresses in cache + cache_write_request_valid_next = 1'b1; + cache_write_request_ip_next = incoming_arp_spa; + cache_write_request_mac_next = incoming_arp_sha; + if (incoming_arp_oper == ARP_OPER_ARP_REQUEST) begin + // ARP request + if (incoming_arp_tpa == local_ip) begin + // send reply frame to valid incoming request + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = incoming_eth_src_mac; + outgoing_arp_oper_next = ARP_OPER_ARP_REPLY; + outgoing_arp_tha_next = incoming_arp_sha; + outgoing_arp_tpa_next = incoming_arp_spa; + end + end else if (incoming_arp_oper == ARP_OPER_INARP_REQUEST) begin + // INARP request + if (incoming_arp_tha == local_mac) begin + // send reply frame to valid incoming request + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = incoming_eth_src_mac; + outgoing_arp_oper_next = ARP_OPER_INARP_REPLY; + outgoing_arp_tha_next = incoming_arp_sha; + outgoing_arp_tpa_next = incoming_arp_spa; + end + end + end + end + + // manage ARP lookup requests + if (arp_request_operation_reg) begin + arp_request_ready_next = 1'b0; + cache_query_request_valid_next = 1'b1; + arp_request_timer_next = arp_request_timer_reg - 1; + // if we got a response, it will go in the cache, so when the query succeds, we're done + if (cache_query_response_valid && !cache_query_response_error) begin + arp_request_operation_next = 1'b0; + cache_query_request_valid_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = cache_query_response_mac; + end + // timer timeout + if (arp_request_timer_reg == 0) begin + if (arp_request_retry_cnt_reg > 0) begin + // have more retries + // send ARP request frame + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = 48'hffffffffffff; + outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST; + outgoing_arp_tha_next = 48'h000000000000; + outgoing_arp_tpa_next = arp_request_ip_reg; + arp_request_retry_cnt_next = arp_request_retry_cnt_reg - 1; + if (arp_request_retry_cnt_reg > 1) begin + arp_request_timer_next = REQUEST_RETRY_INTERVAL; + end else begin + arp_request_timer_next = REQUEST_TIMEOUT; + end + end else begin + // out of retries + arp_request_operation_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b1; + cache_query_request_valid_next = 1'b0; + end + end + end else begin + arp_request_ready_next = !arp_response_valid_next; + if (cache_query_request_valid_reg) begin + cache_query_request_valid_next = 1'b1; + if (cache_query_response_valid) begin + if (cache_query_response_error) begin + arp_request_operation_next = 1'b1; + // send ARP request frame + outgoing_frame_valid_next = 1'b1; + outgoing_eth_dest_mac_next = 48'hffffffffffff; + outgoing_arp_oper_next = ARP_OPER_ARP_REQUEST; + outgoing_arp_tha_next = 48'h000000000000; + outgoing_arp_tpa_next = arp_request_ip_reg; + arp_request_retry_cnt_next = REQUEST_RETRY_COUNT-1; + arp_request_timer_next = REQUEST_RETRY_INTERVAL; + end else begin + cache_query_request_valid_next = 1'b0; + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = cache_query_response_mac; + end + end + end else if (arp_request_valid && arp_request_ready) begin + if (~(arp_request_ip | subnet_mask) == 0) begin + // broadcast address + // (all bits in request IP set where subnet mask is clear) + arp_response_valid_next = 1'b1; + arp_response_error_next = 1'b0; + arp_response_mac_next = 48'hffffffffffff; + end else if (((arp_request_ip ^ gateway_ip) & subnet_mask) == 0) begin + // within subnet, look up IP directly + // (no bits differ between request IP and gateway IP where subnet mask is set) + cache_query_request_valid_next = 1'b1; + cache_query_request_ip_next = arp_request_ip; + arp_request_ip_next = arp_request_ip; + end else begin + // outside of subnet, so look up gateway address + cache_query_request_valid_next = 1'b1; + cache_query_request_ip_next = gateway_ip; + arp_request_ip_next = gateway_ip; + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + outgoing_frame_valid_reg <= 1'b0; + cache_query_request_valid_reg <= 1'b0; + cache_write_request_valid_reg <= 1'b0; + arp_request_ready_reg <= 1'b0; + arp_request_operation_reg <= 1'b0; + arp_request_retry_cnt_reg <= 6'd0; + arp_request_timer_reg <= 36'd0; + arp_response_valid_reg <= 1'b0; + end else begin + outgoing_frame_valid_reg <= outgoing_frame_valid_next; + cache_query_request_valid_reg <= cache_query_request_valid_next; + cache_write_request_valid_reg <= cache_write_request_valid_next; + arp_request_ready_reg <= arp_request_ready_next; + arp_request_operation_reg <= arp_request_operation_next; + arp_request_retry_cnt_reg <= arp_request_retry_cnt_next; + arp_request_timer_reg <= arp_request_timer_next; + arp_response_valid_reg <= arp_response_valid_next; + end + + cache_query_request_ip_reg <= cache_query_request_ip_next; + outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next; + outgoing_arp_oper_reg <= outgoing_arp_oper_next; + outgoing_arp_tha_reg <= outgoing_arp_tha_next; + outgoing_arp_tpa_reg <= outgoing_arp_tpa_next; + cache_write_request_mac_reg <= cache_write_request_mac_next; + cache_write_request_ip_reg <= cache_write_request_ip_next; + arp_request_ip_reg <= arp_request_ip_next; + arp_response_error_reg <= arp_response_error_next; + arp_response_mac_reg <= arp_response_mac_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/arp_cache.v b/corundum/lib/eth/rtl/arp_cache.v new file mode 100644 index 0000000000000000000000000000000000000000..e0c4c0662201f084af391512e832356851ff5b7b --- /dev/null +++ b/corundum/lib/eth/rtl/arp_cache.v @@ -0,0 +1,243 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * ARP cache + */ +module arp_cache #( + parameter CACHE_ADDR_WIDTH = 9 +) +( + input wire clk, + input wire rst, + + /* + * Cache query + */ + input wire query_request_valid, + output wire query_request_ready, + input wire [31:0] query_request_ip, + + output wire query_response_valid, + input wire query_response_ready, + output wire query_response_error, + output wire [47:0] query_response_mac, + + /* + * Cache write + */ + input wire write_request_valid, + output wire write_request_ready, + input wire [31:0] write_request_ip, + input wire [47:0] write_request_mac, + + /* + * Configuration + */ + input wire clear_cache +); + +reg mem_write = 0; +reg store_query = 0; +reg store_write = 0; + +reg query_ip_valid_reg = 0, query_ip_valid_next; +reg [31:0] query_ip_reg = 0; +reg write_ip_valid_reg = 0, write_ip_valid_next; +reg [31:0] write_ip_reg = 0; +reg [47:0] write_mac_reg = 0; +reg clear_cache_reg = 0, clear_cache_next; + +reg [CACHE_ADDR_WIDTH-1:0] wr_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, wr_ptr_next; +reg [CACHE_ADDR_WIDTH-1:0] rd_ptr_reg = {CACHE_ADDR_WIDTH{1'b0}}, rd_ptr_next; + +reg valid_mem[(2**CACHE_ADDR_WIDTH)-1:0]; +reg [31:0] ip_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0]; +reg [47:0] mac_addr_mem[(2**CACHE_ADDR_WIDTH)-1:0]; + +reg query_request_ready_reg = 0, query_request_ready_next; + +reg query_response_valid_reg = 0, query_response_valid_next; +reg query_response_error_reg = 0, query_response_error_next; +reg [47:0] query_response_mac_reg = 0; + +reg write_request_ready_reg = 0, write_request_ready_next; + +wire [31:0] query_request_hash; +wire [31:0] write_request_hash; + +assign query_request_ready = query_request_ready_reg; + +assign query_response_valid = query_response_valid_reg; +assign query_response_error = query_response_error_reg; +assign query_response_mac = query_response_mac_reg; + +assign write_request_ready = write_request_ready_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +rd_hash ( + .data_in(query_request_ip), + .state_in(32'hffffffff), + .data_out(), + .state_out(query_request_hash) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +wr_hash ( + .data_in(write_request_ip), + .state_in(32'hffffffff), + .data_out(), + .state_out(write_request_hash) +); + +integer i; + +initial begin + for (i = 0; i < 2**CACHE_ADDR_WIDTH; i = i + 1) begin + valid_mem[i] = 1'b0; + ip_addr_mem[i] = 32'd0; + mac_addr_mem[i] = 48'd0; + end +end + +always @* begin + mem_write = 1'b0; + store_query = 1'b0; + store_write = 1'b0; + + wr_ptr_next = wr_ptr_reg; + rd_ptr_next = rd_ptr_reg; + + clear_cache_next = clear_cache_reg | clear_cache; + + query_ip_valid_next = query_ip_valid_reg; + + query_request_ready_next = (~query_ip_valid_reg || ~query_request_valid || query_response_ready) && !clear_cache_next; + + query_response_valid_next = query_response_valid_reg & ~query_response_ready; + query_response_error_next = query_response_error_reg; + + if (query_ip_valid_reg && (~query_request_valid || query_response_ready)) begin + query_response_valid_next = 1; + query_ip_valid_next = 0; + if (valid_mem[rd_ptr_reg] && ip_addr_mem[rd_ptr_reg] == query_ip_reg) begin + query_response_error_next = 0; + end else begin + query_response_error_next = 1; + end + end + + if (query_request_valid && query_request_ready && (~query_ip_valid_reg || ~query_request_valid || query_response_ready)) begin + store_query = 1; + query_ip_valid_next = 1; + rd_ptr_next = query_request_hash[CACHE_ADDR_WIDTH-1:0]; + end + + write_ip_valid_next = write_ip_valid_reg; + + write_request_ready_next = !clear_cache_next; + + if (write_ip_valid_reg) begin + write_ip_valid_next = 0; + mem_write = 1; + end + + if (write_request_valid && write_request_ready) begin + store_write = 1; + write_ip_valid_next = 1; + wr_ptr_next = write_request_hash[CACHE_ADDR_WIDTH-1:0]; + end + + if (clear_cache) begin + clear_cache_next = 1'b1; + wr_ptr_next = 0; + end else if (clear_cache_reg) begin + wr_ptr_next = wr_ptr_reg + 1; + clear_cache_next = wr_ptr_next != 0; + mem_write = 1; + end +end + +always @(posedge clk) begin + if (rst) begin + query_ip_valid_reg <= 1'b0; + query_request_ready_reg <= 1'b0; + query_response_valid_reg <= 1'b0; + write_ip_valid_reg <= 1'b0; + write_request_ready_reg <= 1'b0; + clear_cache_reg <= 1'b1; + wr_ptr_reg <= 0; + end else begin + query_ip_valid_reg <= query_ip_valid_next; + query_request_ready_reg <= query_request_ready_next; + query_response_valid_reg <= query_response_valid_next; + write_ip_valid_reg <= write_ip_valid_next; + write_request_ready_reg <= write_request_ready_next; + clear_cache_reg <= clear_cache_next; + wr_ptr_reg <= wr_ptr_next; + end + + query_response_error_reg <= query_response_error_next; + + if (store_query) begin + query_ip_reg <= query_request_ip; + end + + if (store_write) begin + write_ip_reg <= write_request_ip; + write_mac_reg <= write_request_mac; + end + + rd_ptr_reg <= rd_ptr_next; + + query_response_mac_reg <= mac_addr_mem[rd_ptr_reg]; + + if (mem_write) begin + valid_mem[wr_ptr_reg] <= !clear_cache_reg; + ip_addr_mem[wr_ptr_reg] <= write_ip_reg; + mac_addr_mem[wr_ptr_reg] <= write_mac_reg; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/arp_eth_rx.v b/corundum/lib/eth/rtl/arp_eth_rx.v new file mode 100644 index 0000000000000000000000000000000000000000..eb7ea983f1bc606bee17210fe7f1663797a8993a --- /dev/null +++ b/corundum/lib/eth/rtl/arp_eth_rx.v @@ -0,0 +1,323 @@ +/* + +Copyright (c) 2014-2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * ARP ethernet frame receiver (Ethernet frame in, ARP frame out) + */ +module arp_eth_rx # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * ARP frame output + */ + output wire m_frame_valid, + input wire m_frame_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [15:0] m_arp_htype, + output wire [15:0] m_arp_ptype, + output wire [7:0] m_arp_hlen, + output wire [7:0] m_arp_plen, + output wire [15:0] m_arp_oper, + output wire [47:0] m_arp_sha, + output wire [31:0] m_arp_spa, + output wire [47:0] m_arp_tha, + output wire [31:0] m_arp_tpa, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_invalid_header +); + +parameter CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +parameter OFFSET = 28 % KEEP_WIDTH; + +// bus width assertions +initial begin + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +/* + +ARP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0806) 2 octets + HTYPE (1) 2 octets + PTYPE (0x0800) 2 octets + HLEN (6) 1 octets + PLEN (4) 1 octets + OPER 2 octets + SHA Sender MAC 6 octets + SPA Sender IP 4 octets + THA Target MAC 6 octets + TPA Target IP 4 octets + +This module receives an Ethernet frame with header fields in parallel and +payload on an AXI stream interface, decodes the ARP packet fields, and +produces the frame fields in parallel. + +*/ + +// datapath control signals +reg store_eth_hdr; + +reg read_eth_header_reg = 1'b1, read_eth_header_next; +reg read_arp_header_reg = 1'b0, read_arp_header_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg m_frame_valid_reg = 1'b0, m_frame_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [15:0] m_arp_htype_reg = 16'd0, m_arp_htype_next; +reg [15:0] m_arp_ptype_reg = 16'd0, m_arp_ptype_next; +reg [7:0] m_arp_hlen_reg = 8'd0, m_arp_hlen_next; +reg [7:0] m_arp_plen_reg = 8'd0, m_arp_plen_next; +reg [15:0] m_arp_oper_reg = 16'd0, m_arp_oper_next; +reg [47:0] m_arp_sha_reg = 48'd0, m_arp_sha_next; +reg [31:0] m_arp_spa_reg = 32'd0, m_arp_spa_next; +reg [47:0] m_arp_tha_reg = 48'd0, m_arp_tha_next; +reg [31:0] m_arp_tpa_reg = 32'd0, m_arp_tpa_next; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_invalid_header_reg = 1'b0, error_invalid_header_next; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_frame_valid = m_frame_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_arp_htype = m_arp_htype_reg; +assign m_arp_ptype = m_arp_ptype_reg; +assign m_arp_hlen = m_arp_hlen_reg; +assign m_arp_plen = m_arp_plen_reg; +assign m_arp_oper = m_arp_oper_reg; +assign m_arp_sha = m_arp_sha_reg; +assign m_arp_spa = m_arp_spa_reg; +assign m_arp_tha = m_arp_tha_reg; +assign m_arp_tpa = m_arp_tpa_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_invalid_header = error_invalid_header_reg; + +always @* begin + read_eth_header_next = read_eth_header_reg; + read_arp_header_next = read_arp_header_reg; + ptr_next = ptr_reg; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + + m_frame_valid_next = m_frame_valid_reg && !m_frame_ready; + + m_arp_htype_next = m_arp_htype_reg; + m_arp_ptype_next = m_arp_ptype_reg; + m_arp_hlen_next = m_arp_hlen_reg; + m_arp_plen_next = m_arp_plen_reg; + m_arp_oper_next = m_arp_oper_reg; + m_arp_sha_next = m_arp_sha_reg; + m_arp_spa_next = m_arp_spa_reg; + m_arp_tha_next = m_arp_tha_reg; + m_arp_tpa_next = m_arp_tpa_reg; + + error_header_early_termination_next = 1'b0; + error_invalid_header_next = 1'b0; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + if (read_eth_header_reg) begin + store_eth_hdr = 1'b1; + ptr_next = 0; + read_eth_header_next = 1'b0; + read_arp_header_next = 1'b1; + end + end + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + if (read_arp_header_reg) begin + // word transfer in - store it + ptr_next = ptr_reg + 1; + + `define _HEADER_FIELD_(offset, field) \ + if (ptr_reg == offset/KEEP_WIDTH && (!KEEP_ENABLE || s_eth_payload_axis_tkeep[offset%KEEP_WIDTH])) begin \ + field = s_eth_payload_axis_tdata[(offset%KEEP_WIDTH)*8 +: 8]; \ + end + + `_HEADER_FIELD_(0, m_arp_htype_next[1*8 +: 8]) + `_HEADER_FIELD_(1, m_arp_htype_next[0*8 +: 8]) + `_HEADER_FIELD_(2, m_arp_ptype_next[1*8 +: 8]) + `_HEADER_FIELD_(3, m_arp_ptype_next[0*8 +: 8]) + `_HEADER_FIELD_(4, m_arp_hlen_next[0*8 +: 8]) + `_HEADER_FIELD_(5, m_arp_plen_next[0*8 +: 8]) + `_HEADER_FIELD_(6, m_arp_oper_next[1*8 +: 8]) + `_HEADER_FIELD_(7, m_arp_oper_next[0*8 +: 8]) + `_HEADER_FIELD_(8, m_arp_sha_next[5*8 +: 8]) + `_HEADER_FIELD_(9, m_arp_sha_next[4*8 +: 8]) + `_HEADER_FIELD_(10, m_arp_sha_next[3*8 +: 8]) + `_HEADER_FIELD_(11, m_arp_sha_next[2*8 +: 8]) + `_HEADER_FIELD_(12, m_arp_sha_next[1*8 +: 8]) + `_HEADER_FIELD_(13, m_arp_sha_next[0*8 +: 8]) + `_HEADER_FIELD_(14, m_arp_spa_next[3*8 +: 8]) + `_HEADER_FIELD_(15, m_arp_spa_next[2*8 +: 8]) + `_HEADER_FIELD_(16, m_arp_spa_next[1*8 +: 8]) + `_HEADER_FIELD_(17, m_arp_spa_next[0*8 +: 8]) + `_HEADER_FIELD_(18, m_arp_tha_next[5*8 +: 8]) + `_HEADER_FIELD_(19, m_arp_tha_next[4*8 +: 8]) + `_HEADER_FIELD_(20, m_arp_tha_next[3*8 +: 8]) + `_HEADER_FIELD_(21, m_arp_tha_next[2*8 +: 8]) + `_HEADER_FIELD_(22, m_arp_tha_next[1*8 +: 8]) + `_HEADER_FIELD_(23, m_arp_tha_next[0*8 +: 8]) + `_HEADER_FIELD_(24, m_arp_tpa_next[3*8 +: 8]) + `_HEADER_FIELD_(25, m_arp_tpa_next[2*8 +: 8]) + `_HEADER_FIELD_(26, m_arp_tpa_next[1*8 +: 8]) + `_HEADER_FIELD_(27, m_arp_tpa_next[0*8 +: 8]) + + if (ptr_reg == 27/KEEP_WIDTH && (!KEEP_ENABLE || s_eth_payload_axis_tkeep[27%KEEP_WIDTH])) begin + read_arp_header_next = 1'b0; + end + + `undef _HEADER_FIELD_ + end + + if (s_eth_payload_axis_tlast) begin + if (read_arp_header_next) begin + // don't have the whole header + error_header_early_termination_next = 1'b1; + end else if (m_arp_hlen_next != 4'd6 || m_arp_plen_next != 4'd4) begin + // lengths not valid + error_invalid_header_next = 1'b1; + end else begin + // otherwise, transfer tuser + m_frame_valid_next = !s_eth_payload_axis_tuser; + end + + ptr_next = 1'b0; + read_eth_header_next = 1'b1; + read_arp_header_next = 1'b0; + end + end + + if (read_eth_header_next) begin + s_eth_hdr_ready_next = !m_frame_valid_next; + end else begin + s_eth_payload_axis_tready_next = 1'b1; + end +end + +always @(posedge clk) begin + read_eth_header_reg <= read_eth_header_next; + read_arp_header_reg <= read_arp_header_next; + ptr_reg <= ptr_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + m_frame_valid_reg <= m_frame_valid_next; + + m_arp_htype_reg <= m_arp_htype_next; + m_arp_ptype_reg <= m_arp_ptype_next; + m_arp_hlen_reg <= m_arp_hlen_next; + m_arp_plen_reg <= m_arp_plen_next; + m_arp_oper_reg <= m_arp_oper_next; + m_arp_sha_reg <= m_arp_sha_next; + m_arp_spa_reg <= m_arp_spa_next; + m_arp_tha_reg <= m_arp_tha_next; + m_arp_tpa_reg <= m_arp_tpa_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_invalid_header_reg <= error_invalid_header_next; + + busy_reg <= read_arp_header_next; + + // datapath + if (store_eth_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + end + + if (rst) begin + read_eth_header_reg <= 1'b1; + read_arp_header_reg <= 1'b0; + ptr_reg <= 0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_frame_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_invalid_header_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/arp_eth_tx.v b/corundum/lib/eth/rtl/arp_eth_tx.v new file mode 100644 index 0000000000000000000000000000000000000000..7ea1b3dce61113d1200ef5ea5cfa359e92e2491c --- /dev/null +++ b/corundum/lib/eth/rtl/arp_eth_tx.v @@ -0,0 +1,360 @@ +/* + +Copyright (c) 2014-2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * ARP ethernet frame transmitter (ARP frame in, Ethernet frame out) + */ +module arp_eth_tx # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * ARP frame input + */ + input wire s_frame_valid, + output wire s_frame_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [15:0] s_arp_htype, + input wire [15:0] s_arp_ptype, + input wire [15:0] s_arp_oper, + input wire [47:0] s_arp_sha, + input wire [31:0] s_arp_spa, + input wire [47:0] s_arp_tha, + input wire [31:0] s_arp_tpa, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +parameter CYCLE_COUNT = (28+KEEP_WIDTH-1)/KEEP_WIDTH; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +parameter OFFSET = 28 % KEEP_WIDTH; + +// bus width assertions +initial begin + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +/* + +ARP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0806) 2 octets + HTYPE (1) 2 octets + PTYPE (0x0800) 2 octets + HLEN (6) 1 octets + PLEN (4) 1 octets + OPER 2 octets + SHA Sender MAC 6 octets + SPA Sender IP 4 octets + THA Target MAC 6 octets + TPA Target IP 4 octets + +This module receives an ARP frame with header fields in parallel and +transmits the complete Ethernet payload on an AXI interface. + +*/ + +// datapath control signals +reg store_frame; + +reg send_arp_header_reg = 1'b0, send_arp_header_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg [15:0] arp_htype_reg = 16'd0; +reg [15:0] arp_ptype_reg = 16'd0; +reg [15:0] arp_oper_reg = 16'd0; +reg [47:0] arp_sha_reg = 48'd0; +reg [31:0] arp_spa_reg = 32'd0; +reg [47:0] arp_tha_reg = 48'd0; +reg [31:0] arp_tpa_reg = 32'd0; + +reg s_frame_ready_reg = 1'b0, s_frame_ready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_frame_ready = s_frame_ready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; + +always @* begin + send_arp_header_next = send_arp_header_reg; + ptr_next = ptr_reg; + + s_frame_ready_next = 1'b0; + + store_frame = 1'b0; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + m_eth_payload_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_eth_payload_axis_tkeep_int = {KEEP_WIDTH{1'b0}}; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + if (s_frame_ready && s_frame_valid) begin + store_frame = 1'b1; + m_eth_hdr_valid_next = 1'b1; + ptr_next = 0; + send_arp_header_next = 1'b1; + end + + if (m_eth_payload_axis_tready_int_reg) begin + if (send_arp_header_reg) begin + ptr_next = ptr_reg + 1; + + m_eth_payload_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_eth_payload_axis_tkeep_int = {KEEP_WIDTH{1'b0}}; + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + `define _HEADER_FIELD_(offset, field) \ + if (ptr_reg == offset/KEEP_WIDTH) begin \ + m_eth_payload_axis_tdata_int[(offset%KEEP_WIDTH)*8 +: 8] = field; \ + m_eth_payload_axis_tkeep_int[offset%KEEP_WIDTH] = 1'b1; \ + end + + `_HEADER_FIELD_(0, arp_htype_reg[1*8 +: 8]) + `_HEADER_FIELD_(1, arp_htype_reg[0*8 +: 8]) + `_HEADER_FIELD_(2, arp_ptype_reg[1*8 +: 8]) + `_HEADER_FIELD_(3, arp_ptype_reg[0*8 +: 8]) + `_HEADER_FIELD_(4, 8'd6) + `_HEADER_FIELD_(5, 8'd4) + `_HEADER_FIELD_(6, arp_oper_reg[1*8 +: 8]) + `_HEADER_FIELD_(7, arp_oper_reg[0*8 +: 8]) + `_HEADER_FIELD_(8, arp_sha_reg[5*8 +: 8]) + `_HEADER_FIELD_(9, arp_sha_reg[4*8 +: 8]) + `_HEADER_FIELD_(10, arp_sha_reg[3*8 +: 8]) + `_HEADER_FIELD_(11, arp_sha_reg[2*8 +: 8]) + `_HEADER_FIELD_(12, arp_sha_reg[1*8 +: 8]) + `_HEADER_FIELD_(13, arp_sha_reg[0*8 +: 8]) + `_HEADER_FIELD_(14, arp_spa_reg[3*8 +: 8]) + `_HEADER_FIELD_(15, arp_spa_reg[2*8 +: 8]) + `_HEADER_FIELD_(16, arp_spa_reg[1*8 +: 8]) + `_HEADER_FIELD_(17, arp_spa_reg[0*8 +: 8]) + `_HEADER_FIELD_(18, arp_tha_reg[5*8 +: 8]) + `_HEADER_FIELD_(19, arp_tha_reg[4*8 +: 8]) + `_HEADER_FIELD_(20, arp_tha_reg[3*8 +: 8]) + `_HEADER_FIELD_(21, arp_tha_reg[2*8 +: 8]) + `_HEADER_FIELD_(22, arp_tha_reg[1*8 +: 8]) + `_HEADER_FIELD_(23, arp_tha_reg[0*8 +: 8]) + `_HEADER_FIELD_(24, arp_tpa_reg[3*8 +: 8]) + `_HEADER_FIELD_(25, arp_tpa_reg[2*8 +: 8]) + `_HEADER_FIELD_(26, arp_tpa_reg[1*8 +: 8]) + `_HEADER_FIELD_(27, arp_tpa_reg[0*8 +: 8]) + + if (ptr_reg == 27/KEEP_WIDTH) begin + m_eth_payload_axis_tlast_int = 1'b1; + send_arp_header_next = 1'b0; + end + + `undef _HEADER_FIELD_ + end + end + + s_frame_ready_next = !m_eth_hdr_valid_next && !send_arp_header_next; +end + +always @(posedge clk) begin + send_arp_header_reg <= send_arp_header_next; + ptr_reg <= ptr_next; + + s_frame_ready_reg <= s_frame_ready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + busy_reg <= send_arp_header_next; + + if (store_frame) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + arp_htype_reg <= s_arp_htype; + arp_ptype_reg <= s_arp_ptype; + arp_oper_reg <= s_arp_oper; + arp_sha_reg <= s_arp_sha; + arp_spa_reg <= s_arp_spa; + arp_tha_reg <= s_arp_tha; + arp_tpa_reg <= s_arp_tpa; + end + + if (rst) begin + send_arp_header_reg <= 1'b0; + ptr_reg <= 0; + s_frame_ready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_baser_rx_64.v b/corundum/lib/eth/rtl/axis_baser_rx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..533fdcfb212dea3dd9e0be8917a3c1ac553921c9 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_baser_rx_64.v @@ -0,0 +1,615 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream 10GBASE-R frame receiver (10GBASE-R in, AXI out) + */ +module axis_baser_rx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded input + */ + input wire [DATA_WIDTH-1:0] encoded_rx_data, + input wire [HDR_WIDTH-1:0] encoded_rx_hdr, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_bad_frame, + output wire error_bad_fcs, + output wire rx_bad_block +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +localparam [3:0] + INPUT_TYPE_IDLE = 4'd0, + INPUT_TYPE_ERROR = 4'd1, + INPUT_TYPE_START_0 = 4'd2, + INPUT_TYPE_START_4 = 4'd3, + INPUT_TYPE_DATA = 4'd4, + INPUT_TYPE_TERM_0 = 4'd8, + INPUT_TYPE_TERM_1 = 4'd9, + INPUT_TYPE_TERM_2 = 4'd10, + INPUT_TYPE_TERM_3 = 4'd11, + INPUT_TYPE_TERM_4 = 4'd12, + INPUT_TYPE_TERM_5 = 4'd13, + INPUT_TYPE_TERM_6 = 4'd14, + INPUT_TYPE_TERM_7 = 4'd15; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_LAST = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc_last; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_data = 32'd0; + +reg delay_type_valid = 1'b0; +reg [3:0] delay_type = INPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] input_data_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] input_data_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] input_data_crc = {DATA_WIDTH{1'b0}}; + +reg [3:0] input_type_d0 = INPUT_TYPE_IDLE; +reg [3:0] input_type_d1 = INPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, m_axis_tkeep_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg [1:0] start_packet_reg = 2'b00; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; +reg rx_bad_block_reg = 1'b0; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0; + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] crc_state3 = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next7; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; +wire crc_valid7 = crc_next7 == ~32'h2144df1c; + +reg crc_valid7_save = 1'b0; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; +assign rx_bad_block = rx_bad_block_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(input_data_crc[7:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(input_data_crc[15:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(input_data_crc[23:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(input_data_crc[31:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(input_data_d0[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc_last = 1'b0; + + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'd0; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (input_type_d1 == INPUT_TYPE_START_0) begin + // start condition + reset_crc = 1'b0; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'hff; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + if (input_type_d0 == INPUT_TYPE_DATA) begin + state_next = STATE_PAYLOAD; + end else if (input_type_d0[3]) begin + // INPUT_TYPE_TERM_* + if (input_type_d0 <= INPUT_TYPE_TERM_4) begin + // end this cycle + reset_crc = 1'b1; + case (input_type_d0) + INPUT_TYPE_TERM_0: m_axis_tkeep_next = 8'b00001111; + INPUT_TYPE_TERM_1: m_axis_tkeep_next = 8'b00011111; + INPUT_TYPE_TERM_2: m_axis_tkeep_next = 8'b00111111; + INPUT_TYPE_TERM_3: m_axis_tkeep_next = 8'b01111111; + INPUT_TYPE_TERM_4: m_axis_tkeep_next = 8'b11111111; + endcase + m_axis_tlast_next = 1'b1; + if ((input_type_d0 == INPUT_TYPE_TERM_0 && crc_valid7_save) || + (input_type_d0 == INPUT_TYPE_TERM_1 && crc_valid0) || + (input_type_d0 == INPUT_TYPE_TERM_2 && crc_valid1) || + (input_type_d0 == INPUT_TYPE_TERM_3 && crc_valid2) || + (input_type_d0 == INPUT_TYPE_TERM_4 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + // need extra cycle + update_crc_last = 1'b1; + state_next = STATE_LAST; + end + end else begin + // control or error characters in packet + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_LAST: begin + // last cycle of packet + m_axis_tdata_next = input_data_d1; + m_axis_tkeep_next = 8'hff; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + case (input_type_d1) + INPUT_TYPE_TERM_5: m_axis_tkeep_next = 8'b00000001; + INPUT_TYPE_TERM_6: m_axis_tkeep_next = 8'b00000011; + INPUT_TYPE_TERM_7: m_axis_tkeep_next = 8'b00000111; + endcase + + if ((input_type_d1 == INPUT_TYPE_TERM_5 && crc_valid0) || + (input_type_d1 == INPUT_TYPE_TERM_6 && crc_valid1) || + (input_type_d1 == INPUT_TYPE_TERM_7 && crc_valid2)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + rx_bad_block_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + + input_type_d0 <= INPUT_TYPE_IDLE; + input_type_d1 <= INPUT_TYPE_IDLE; + + lanes_swapped <= 1'b0; + + delay_type_valid <= 1'b0; + delay_type <= INPUT_TYPE_IDLE; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + rx_bad_block_reg <= 1'b0; + + delay_type_valid <= 1'b0; + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + lanes_swapped <= 1'b0; + start_packet_reg <= 2'b01; + input_type_d0 <= INPUT_TYPE_START_0; + end else if (encoded_rx_hdr == SYNC_CTRL && (encoded_rx_data[7:0] == BLOCK_TYPE_START_4 || encoded_rx_data[7:0] == BLOCK_TYPE_OS_START)) begin + lanes_swapped <= 1'b1; + start_packet_reg <= 2'b10; + delay_type_valid <= 1'b1; + if (delay_type_valid) begin + input_type_d0 <= delay_type; + end else begin + input_type_d0 <= INPUT_TYPE_IDLE; + end + end else if (lanes_swapped) begin + if (delay_type_valid) begin + input_type_d0 <= delay_type; + end else if (encoded_rx_hdr == SYNC_DATA) begin + input_type_d0 <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_TERM_0: input_type_d0 <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_1: input_type_d0 <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_2: input_type_d0 <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_3: input_type_d0 <= INPUT_TYPE_TERM_7; + BLOCK_TYPE_TERM_4: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_5: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_6: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + BLOCK_TYPE_TERM_7: begin + delay_type_valid <= 1'b1; + input_type_d0 <= INPUT_TYPE_DATA; + end + default: begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + endcase + end else begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + end else begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_type_d0 <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_TERM_0: input_type_d0 <= INPUT_TYPE_TERM_0; + BLOCK_TYPE_TERM_1: input_type_d0 <= INPUT_TYPE_TERM_1; + BLOCK_TYPE_TERM_2: input_type_d0 <= INPUT_TYPE_TERM_2; + BLOCK_TYPE_TERM_3: input_type_d0 <= INPUT_TYPE_TERM_3; + BLOCK_TYPE_TERM_4: input_type_d0 <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_5: input_type_d0 <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_6: input_type_d0 <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_7: input_type_d0 <= INPUT_TYPE_TERM_7; + default: begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + endcase + end else begin + rx_bad_block_reg <= 1'b1; + input_type_d0 <= INPUT_TYPE_ERROR; + end + end + + input_type_d1 <= input_type_d0; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else begin + crc_state <= crc_next7; + end + + if (update_crc_last) begin + crc_state3 <= crc_next3; + end else begin + crc_state3 <= crc_next7; + end + end + + if (PTP_TS_WIDTH == 96 && $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + ptp_ts_reg[45:16] <= $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + ptp_ts_reg[95:48] <= ptp_ts_reg[95:48] + 1; + end + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + end else if (encoded_rx_hdr == SYNC_CTRL && (encoded_rx_data[7:0] == BLOCK_TYPE_START_4 || encoded_rx_data[7:0] == BLOCK_TYPE_OS_START)) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + end + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tkeep_reg <= m_axis_tkeep_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + if (encoded_rx_hdr == SYNC_DATA) begin + swap_data <= encoded_rx_data[63:32]; + end else begin + swap_data <= {8'd0, encoded_rx_data[63:40]}; + end + + if (encoded_rx_hdr == SYNC_CTRL && encoded_rx_data[7:0] == BLOCK_TYPE_START_0) begin + input_data_d0 <= encoded_rx_data; + input_data_crc <= encoded_rx_data; + end else if (encoded_rx_hdr == SYNC_CTRL && (encoded_rx_data[7:0] == BLOCK_TYPE_START_4 || encoded_rx_data[7:0] == BLOCK_TYPE_OS_START)) begin + input_data_d0 <= {encoded_rx_data[31:0], swap_data}; + input_data_crc <= {encoded_rx_data[31:0], swap_data}; + end else if (lanes_swapped) begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_data_d0 <= {encoded_rx_data[31:0], swap_data}; + input_data_crc <= {encoded_rx_data[31:0], swap_data}; + end else begin + input_data_d0 <= {encoded_rx_data[39:8], swap_data}; + input_data_crc <= {encoded_rx_data[39:8], swap_data}; + end + end else begin + if (encoded_rx_hdr == SYNC_DATA) begin + input_data_d0 <= encoded_rx_data; + input_data_crc <= encoded_rx_data; + end else begin + input_data_d0 <= {8'd0, encoded_rx_data[63:8]}; + input_data_crc <= {8'd0, encoded_rx_data[63:8]}; + end + end + + crc_valid7_save <= crc_valid7; + + if (state_next == STATE_LAST) begin + input_data_crc[31:0] <= input_data_crc[63:32]; + end + + input_data_d1 <= input_data_d0; + + if (encoded_rx_hdr == SYNC_DATA) begin + delay_type <= INPUT_TYPE_DATA; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_START_4: delay_type <= INPUT_TYPE_START_0; + BLOCK_TYPE_TERM_0: delay_type <= INPUT_TYPE_TERM_4; + BLOCK_TYPE_TERM_1: delay_type <= INPUT_TYPE_TERM_5; + BLOCK_TYPE_TERM_2: delay_type <= INPUT_TYPE_TERM_6; + BLOCK_TYPE_TERM_3: delay_type <= INPUT_TYPE_TERM_7; + BLOCK_TYPE_TERM_4: delay_type <= INPUT_TYPE_TERM_0; + BLOCK_TYPE_TERM_5: delay_type <= INPUT_TYPE_TERM_1; + BLOCK_TYPE_TERM_6: delay_type <= INPUT_TYPE_TERM_2; + BLOCK_TYPE_TERM_7: delay_type <= INPUT_TYPE_TERM_3; + default: delay_type <= INPUT_TYPE_ERROR; + endcase + end else begin + delay_type <= INPUT_TYPE_ERROR; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_baser_tx_64.v b/corundum/lib/eth/rtl/axis_baser_tx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..35b86809f74e86c194c9213da4adaf1b51521867 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_baser_tx_64.v @@ -0,0 +1,916 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream 10GBASE-R frame transmitter (AXI in, 10GBASE-R out) + */ +module axis_baser_tx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * 10GBASE-R encoded interface + */ + output wire [DATA_WIDTH-1:0] encoded_tx_data, + output wire [HDR_WIDTH-1:0] encoded_tx_hdr, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfff8; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0007; + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +localparam [3:0] + OUTPUT_TYPE_IDLE = 4'd0, + OUTPUT_TYPE_ERROR = 4'd1, + OUTPUT_TYPE_START_0 = 4'd2, + OUTPUT_TYPE_START_4 = 4'd3, + OUTPUT_TYPE_DATA = 4'd4, + OUTPUT_TYPE_TERM_0 = 4'd8, + OUTPUT_TYPE_TERM_1 = 4'd9, + OUTPUT_TYPE_TERM_2 = 4'd10, + OUTPUT_TYPE_TERM_3 = 4'd11, + OUTPUT_TYPE_TERM_4 = 4'd12, + OUTPUT_TYPE_TERM_5 = 4'd13, + OUTPUT_TYPE_TERM_6 = 4'd14, + OUTPUT_TYPE_TERM_7 = 4'd15; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_PAD = 3'd2, + STATE_FCS_1 = 3'd3, + STATE_FCS_2 = 3'd4, + STATE_IFG = 3'd5, + STATE_WAIT_END = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg swap_lanes; +reg unswap_lanes; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_data = 32'd0; + +reg delay_type_valid = 1'b0; +reg [3:0] delay_type = OUTPUT_TYPE_IDLE; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] s_tdata_reg = {DATA_WIDTH{1'b0}}, s_tdata_next; +reg [7:0] s_tkeep_reg = 8'd0, s_tkeep_next; + +reg [DATA_WIDTH-1:0] fcs_output_data_0; +reg [DATA_WIDTH-1:0] fcs_output_data_1; +reg [3:0] fcs_output_type_0; +reg [3:0] fcs_output_type_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; +reg m_axis_ptp_ts_valid_int_reg = 1'b0, m_axis_ptp_ts_valid_int_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +reg [DATA_WIDTH-1:0] encoded_tx_data_reg = {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; +reg [HDR_WIDTH-1:0] encoded_tx_hdr_reg = SYNC_CTRL; + +reg [DATA_WIDTH-1:0] output_data_reg = {DATA_WIDTH{1'b0}}, output_data_next; +reg [3:0] output_type_reg = OUTPUT_TYPE_IDLE, output_type_next; + +reg [1:0] start_packet_reg = 2'b00, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign encoded_tx_data = encoded_tx_data_reg; +assign encoded_tx_hdr = encoded_tx_hdr_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(s_tdata_reg[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(s_tdata_reg[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(s_tdata_reg[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_tdata_reg[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (s_tkeep_reg) + 8'bzzzzzz01: begin + fcs_output_data_0 = {24'd0, ~crc_next0[31:0], s_tdata_reg[7:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_5; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 8'bzzzzz011: begin + fcs_output_data_0 = {16'd0, ~crc_next1[31:0], s_tdata_reg[15:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_6; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 8'bzzzz0111: begin + fcs_output_data_0 = {8'd0, ~crc_next2[31:0], s_tdata_reg[23:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_TERM_7; + fcs_output_type_1 = OUTPUT_TYPE_IDLE; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 8'bzzz01111: begin + fcs_output_data_0 = {~crc_next3[31:0], s_tdata_reg[31:0]}; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_0; + ifg_offset = 8'd8; + extra_cycle = 1'b1; + end + 8'bzz011111: begin + fcs_output_data_0 = {~crc_next4[23:0], s_tdata_reg[39:0]}; + fcs_output_data_1 = {56'd0, ~crc_next4[31:24]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_1; + ifg_offset = 8'd7; + extra_cycle = 1'b1; + end + 8'bz0111111: begin + fcs_output_data_0 = {~crc_next5[15:0], s_tdata_reg[47:0]}; + fcs_output_data_1 = {48'd0, ~crc_next5[31:16]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_2; + ifg_offset = 8'd6; + extra_cycle = 1'b1; + end + 8'b01111111: begin + fcs_output_data_0 = {~crc_next6[7:0], s_tdata_reg[55:0]}; + fcs_output_data_1 = {40'd0, ~crc_next6[31:8]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_3; + ifg_offset = 8'd5; + extra_cycle = 1'b1; + end + 8'b11111111: begin + fcs_output_data_0 = s_tdata_reg; + fcs_output_data_1 = {32'd0, ~crc_next7[31:0]}; + fcs_output_type_0 = OUTPUT_TYPE_DATA; + fcs_output_type_1 = OUTPUT_TYPE_TERM_4; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_data_0 = 64'd0; + fcs_output_data_1 = 64'd0; + fcs_output_type_0 = OUTPUT_TYPE_ERROR; + fcs_output_type_1 = OUTPUT_TYPE_ERROR; + ifg_offset = 8'd0; + extra_cycle = 1'b1; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + swap_lanes = 1'b0; + unswap_lanes = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + s_tkeep_next = s_tkeep_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + m_axis_ptp_ts_valid_int_next = 1'b0; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_IDLE; + + start_packet_next = 2'b00; + error_underflow_next = 1'b0; + + if (m_axis_ptp_ts_valid_int_reg) begin + m_axis_ptp_ts_valid_next = 1'b1; + if (PTP_TS_WIDTH == 96 && $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + m_axis_ptp_ts_next[45:16] = $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + m_axis_ptp_ts_next[95:48] = m_axis_ptp_ts_reg[95:48] + 1; + end + end + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd8; + reset_crc = 1'b1; + s_axis_tready_next = 1'b1; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_IDLE; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + // XGMII start and preamble + if (ifg_count_reg > 8'd0) begin + // need to send more idles - swap lanes + swap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b10; + end else begin + // no more idles - unswap + unswap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b01; + end + output_data_next = {ETH_SFD, {7{ETH_PRE}}}; + output_type_next = OUTPUT_TYPE_START_0; + s_axis_tready_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + unswap_lanes = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd8; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_DATA; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + s_axis_tready_next = 1'b0; + if (s_axis_tuser[0]) begin + output_type_next = OUTPUT_TYPE_ERROR; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(s_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + s_tkeep_next = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + output_type_next = OUTPUT_TYPE_ERROR; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + s_axis_tready_next = 1'b0; + + output_data_next = s_tdata_reg; + output_type_next = OUTPUT_TYPE_DATA; + + s_tdata_next = 64'd0; + s_tkeep_next = 8'hff; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + s_axis_tready_next = 1'b0; + + output_data_next = fcs_output_data_0; + output_type_next = fcs_output_type_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + (lanes_swapped ? 8'd4 : 8'd0) + deficit_idle_count_reg; + if (extra_cycle) begin + state_next = STATE_FCS_2; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_2: begin + // last cycle + s_axis_tready_next = 1'b0; + + output_data_next = fcs_output_data_1; + output_type_next = fcs_output_type_1; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + s_axis_tready_next = 1'b1; + + if (ifg_count_reg > 8'd4) begin + ifg_count_next = ifg_count_reg - 8'd4; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + m_axis_ptp_ts_valid_int_reg <= 1'b0; + + encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + + output_data_reg <= {DATA_WIDTH{1'b0}}; + output_type_reg <= OUTPUT_TYPE_IDLE; + + start_packet_reg <= 2'b00; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + lanes_swapped <= 1'b0; + + delay_type_valid <= 1'b0; + delay_type <= OUTPUT_TYPE_IDLE; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + m_axis_ptp_ts_valid_int_reg <= m_axis_ptp_ts_valid_int_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + delay_type_valid <= 1'b0; + + if (swap_lanes || (lanes_swapped && !unswap_lanes)) begin + lanes_swapped <= 1'b1; + output_data_reg <= {output_data_next[31:0], swap_data}; + if (delay_type_valid) begin + output_type_reg <= delay_type; + end else if (output_type_next == OUTPUT_TYPE_START_0) begin + output_type_reg <= OUTPUT_TYPE_START_4; + end else if (output_type_next[3]) begin + // OUTPUT_TYPE_TERM_* + if (output_type_next[2]) begin + delay_type_valid <= 1'b1; + output_type_reg <= OUTPUT_TYPE_DATA; + end else begin + output_type_reg <= output_type_next ^ 4'd4; + end + end else begin + output_type_reg <= output_type_next; + end + end else begin + lanes_swapped <= 1'b0; + output_data_reg <= output_data_next; + output_type_reg <= output_type_next; + end + + case (output_type_reg) + OUTPUT_TYPE_IDLE: begin + encoded_tx_data_reg <= {{8{CTRL_IDLE}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_ERROR: begin + encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_START_0: begin + encoded_tx_data_reg <= {output_data_reg[63:8], BLOCK_TYPE_START_0}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_START_4: begin + encoded_tx_data_reg <= {output_data_reg[63:40], 4'd0, {4{CTRL_IDLE}}, BLOCK_TYPE_START_4}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_DATA: begin + encoded_tx_data_reg <= output_data_reg; + encoded_tx_hdr_reg <= SYNC_DATA; + end + OUTPUT_TYPE_TERM_0: begin + encoded_tx_data_reg <= {{7{CTRL_IDLE}}, 7'd0, BLOCK_TYPE_TERM_0}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_1: begin + encoded_tx_data_reg <= {{6{CTRL_IDLE}}, 6'd0, output_data_reg[7:0], BLOCK_TYPE_TERM_1}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_2: begin + encoded_tx_data_reg <= {{5{CTRL_IDLE}}, 5'd0, output_data_reg[15:0], BLOCK_TYPE_TERM_2}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_3: begin + encoded_tx_data_reg <= {{4{CTRL_IDLE}}, 4'd0, output_data_reg[23:0], BLOCK_TYPE_TERM_3}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_4: begin + encoded_tx_data_reg <= {{3{CTRL_IDLE}}, 3'd0, output_data_reg[31:0], BLOCK_TYPE_TERM_4}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_5: begin + encoded_tx_data_reg <= {{2{CTRL_IDLE}}, 2'd0, output_data_reg[39:0], BLOCK_TYPE_TERM_5}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_6: begin + encoded_tx_data_reg <= {{1{CTRL_IDLE}}, 1'd0, output_data_reg[47:0], BLOCK_TYPE_TERM_6}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + OUTPUT_TYPE_TERM_7: begin + encoded_tx_data_reg <= {output_data_reg[55:0], BLOCK_TYPE_TERM_7}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + default: begin + encoded_tx_data_reg <= {{8{CTRL_ERROR}}, BLOCK_TYPE_CTRL}; + encoded_tx_hdr_reg <= SYNC_CTRL; + end + endcase + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + s_tdata_reg <= s_tdata_next; + s_tkeep_reg <= s_tkeep_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + + swap_data <= output_data_next[63:32]; + + delay_type <= output_type_next ^ 4'd4; +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_eth_fcs.v b/corundum/lib/eth/rtl/axis_eth_fcs.v new file mode 100644 index 0000000000000000000000000000000000000000..16e6a193f222639e56738eb30c096dbeaf63b421 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_eth_fcs.v @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS Generator + */ +module axis_eth_fcs +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * FCS output + */ + output wire [31:0] output_fcs, + output wire output_fcs_valid +); + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] fcs_reg = 32'h00000000; +reg fcs_valid_reg = 1'b0; + +wire [31:0] crc_next; + +assign s_axis_tready = 1; +assign output_fcs = fcs_reg; +assign output_fcs_valid = fcs_valid_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_axis_tdata), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @(posedge clk) begin + if (rst) begin + crc_state <= 32'hFFFFFFFF; + fcs_reg <= 32'h00000000; + fcs_valid_reg <= 1'b0; + end else begin + fcs_valid_reg <= 1'b0; + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + crc_state <= 32'hFFFFFFFF; + fcs_reg <= ~crc_next; + fcs_valid_reg <= 1'b1; + end else begin + crc_state <= crc_next; + end + end + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_eth_fcs_64.v b/corundum/lib/eth/rtl/axis_eth_fcs_64.v new file mode 100644 index 0000000000000000000000000000000000000000..e2fa4e37132ffe7a64ebcd6031b67fe1b78aea52 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_eth_fcs_64.v @@ -0,0 +1,227 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS Generator (64 bit datapath) + */ +module axis_eth_fcs_64 +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] s_axis_tdata, + input wire [7:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * FCS output + */ + output wire [31:0] output_fcs, + output wire output_fcs_valid +); + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] fcs_reg = 32'h00000000; +reg fcs_valid_reg = 1'b0; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +assign s_axis_tready = 1'b1; +assign output_fcs = fcs_reg; +assign output_fcs_valid = fcs_valid_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_axis_tdata[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_axis_tdata[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_axis_tdata[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_axis_tdata[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(s_axis_tdata[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(s_axis_tdata[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(s_axis_tdata[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_axis_tdata[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +always @(posedge clk) begin + if (rst) begin + crc_state <= 32'hFFFFFFFF; + fcs_reg <= 1'b0; + fcs_valid_reg <= 1'b0; + end else begin + fcs_valid_reg <= 1'b0; + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + crc_state <= 32'hFFFFFFFF; + case (s_axis_tkeep) + 8'b00000001: fcs_reg <= ~crc_next0; + 8'b00000011: fcs_reg <= ~crc_next1; + 8'b00000111: fcs_reg <= ~crc_next2; + 8'b00001111: fcs_reg <= ~crc_next3; + 8'b00011111: fcs_reg <= ~crc_next4; + 8'b00111111: fcs_reg <= ~crc_next5; + 8'b01111111: fcs_reg <= ~crc_next6; + 8'b11111111: fcs_reg <= ~crc_next7; + endcase + fcs_valid_reg <= 1'b1; + end else begin + crc_state <= crc_next7; + end + end + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_eth_fcs_check.v b/corundum/lib/eth/rtl/axis_eth_fcs_check.v new file mode 100644 index 0000000000000000000000000000000000000000..00625238eef9230738e7f6a3ec5ea9a5bda9c8fa --- /dev/null +++ b/corundum/lib/eth/rtl/axis_eth_fcs_check.v @@ -0,0 +1,341 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS checker + */ +module axis_eth_fcs_check +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy, + output wire error_bad_fcs +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; +reg shift_in; +reg shift_reset; + +reg [7:0] s_axis_tdata_d0 = 8'd0; +reg [7:0] s_axis_tdata_d1 = 8'd0; +reg [7:0] s_axis_tdata_d2 = 8'd0; +reg [7:0] s_axis_tdata_d3 = 8'd0; + +reg s_axis_tvalid_d0 = 1'b0; +reg s_axis_tvalid_d1 = 1'b0; +reg s_axis_tvalid_d2 = 1'b0; +reg s_axis_tvalid_d3 = 1'b0; + +reg busy_reg = 1'b0; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_axis_tdata_d3), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + shift_in = 1'b0; + shift_reset = 1'b0; + + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata_d3; + m_axis_tvalid_int = s_axis_tvalid_d3 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + + if (s_axis_tvalid_d3) begin + reset_crc = 1'b0; + update_crc = 1'b1; + if (s_axis_tlast) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + if ({s_axis_tdata, s_axis_tdata_d0, s_axis_tdata_d1, s_axis_tdata_d2} != ~crc_next) begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata_d3; + m_axis_tvalid_int = s_axis_tvalid_d3 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + update_crc = 1'b1; + if (s_axis_tlast) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + if ({s_axis_tdata, s_axis_tdata_d0, s_axis_tdata_d1, s_axis_tdata_d2} != ~crc_next) begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + s_axis_tvalid_d0 <= 1'b0; + s_axis_tvalid_d1 <= 1'b0; + s_axis_tvalid_d2 <= 1'b0; + s_axis_tvalid_d3 <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + error_bad_fcs_reg <= error_bad_fcs_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + if (shift_reset) begin + s_axis_tvalid_d0 <= 1'b0; + s_axis_tvalid_d1 <= 1'b0; + s_axis_tvalid_d2 <= 1'b0; + s_axis_tvalid_d3 <= 1'b0; + end else if (shift_in) begin + s_axis_tvalid_d0 <= s_axis_tvalid; + s_axis_tvalid_d1 <= s_axis_tvalid_d0; + s_axis_tvalid_d2 <= s_axis_tvalid_d1; + s_axis_tvalid_d3 <= s_axis_tvalid_d2; + end + end + + if (shift_in) begin + s_axis_tdata_d0 <= s_axis_tdata; + s_axis_tdata_d1 <= s_axis_tdata_d0; + s_axis_tdata_d2 <= s_axis_tdata_d1; + s_axis_tdata_d3 <= s_axis_tdata_d2; + end +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_eth_fcs_check_64.v b/corundum/lib/eth/rtl/axis_eth_fcs_check_64.v new file mode 100644 index 0000000000000000000000000000000000000000..1fd172066818ea49ceacb4c5b3d2bb41a761724f --- /dev/null +++ b/corundum/lib/eth/rtl/axis_eth_fcs_check_64.v @@ -0,0 +1,478 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS checker (64 bit datapath) + */ +module axis_eth_fcs_check_64 +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] s_axis_tdata, + input wire [7:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [63:0] m_axis_tdata, + output wire [7:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy, + output wire error_bad_fcs +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_LAST = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; +reg shift_in; +reg shift_reset; + +reg [7:0] last_cycle_tkeep_reg = 8'd0, last_cycle_tkeep_next; +reg last_cycle_tuser_reg = 1'b0, last_cycle_tuser_next; + +reg [63:0] s_axis_tdata_d0 = 64'd0; +reg [7:0] s_axis_tkeep_d0 = 8'd0; +reg s_axis_tvalid_d0 = 1'b0; +reg s_axis_tuser_d0 = 1'b0; + +reg busy_reg = 1'b0; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] crc_state3 = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next7; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; + +// internal datapath +reg [63:0] m_axis_tdata_int; +reg [7:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +wire last_cycle = state_reg == STATE_LAST; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(last_cycle ? s_axis_tdata_d0[39:32] : s_axis_tdata[7:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(last_cycle ? s_axis_tdata_d0[47:32] : s_axis_tdata[15:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(last_cycle ? s_axis_tdata_d0[55:32] : s_axis_tdata[23:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(last_cycle ? s_axis_tdata_d0[63:32] : s_axis_tdata[31:0]), + .state_in(last_cycle ? crc_state3 : crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_axis_tdata[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + shift_in = 1'b0; + shift_reset = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + last_cycle_tuser_next = last_cycle_tuser_reg; + + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 64'd0; + m_axis_tkeep_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata_d0; + m_axis_tkeep_int = s_axis_tkeep_d0; + m_axis_tvalid_int = s_axis_tvalid_d0 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + reset_crc = 1'b0; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tkeep[7:4] == 0) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + m_axis_tkeep_int = {s_axis_tkeep[3:0], 4'b1111}; + if ((s_axis_tkeep[3:0] == 4'b0001 && crc_valid0) || + (s_axis_tkeep[3:0] == 4'b0011 && crc_valid1) || + (s_axis_tkeep[3:0] == 4'b0111 && crc_valid2) || + (s_axis_tkeep[3:0] == 4'b1111 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + last_cycle_tkeep_next = {4'b0000, s_axis_tkeep[7:4]}; + last_cycle_tuser_next = s_axis_tuser; + s_axis_tready_next = 1'b0; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata_d0; + m_axis_tkeep_int = s_axis_tkeep_d0; + m_axis_tvalid_int = s_axis_tvalid_d0 && s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + shift_in = 1'b1; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tkeep[7:4] == 0) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = s_axis_tuser; + m_axis_tkeep_int = {s_axis_tkeep[3:0], 4'b1111}; + if ((s_axis_tkeep[3:0] == 4'b0001 && crc_valid0) || + (s_axis_tkeep[3:0] == 4'b0011 && crc_valid1) || + (s_axis_tkeep[3:0] == 4'b0111 && crc_valid2) || + (s_axis_tkeep[3:0] == 4'b1111 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + last_cycle_tkeep_next = {4'b0000, s_axis_tkeep[7:4]}; + last_cycle_tuser_next = s_axis_tuser; + s_axis_tready_next = 1'b0; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = s_axis_tdata_d0; + m_axis_tkeep_int = last_cycle_tkeep_reg; + m_axis_tvalid_int = s_axis_tvalid_d0; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = last_cycle_tuser_reg; + + if ((s_axis_tkeep_d0[7:4] == 4'b0001 && crc_valid0) || + (s_axis_tkeep_d0[7:4] == 4'b0011 && crc_valid1) || + (s_axis_tkeep_d0[7:4] == 4'b0111 && crc_valid2) || + (s_axis_tkeep_d0[7:4] == 4'b1111 && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_int = 1'b1; + error_bad_fcs_next = 1'b1; + end + + if (m_axis_tready_int_reg) begin + shift_reset = 1'b1; + reset_crc = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end else begin + state_next = STATE_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + s_axis_tvalid_d0 <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + error_bad_fcs_reg <= error_bad_fcs_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + crc_state3 <= crc_next3; + end + + if (shift_reset) begin + s_axis_tvalid_d0 <= 1'b0; + end else if (shift_in) begin + s_axis_tvalid_d0 <= s_axis_tvalid; + end + end + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + last_cycle_tuser_reg <= last_cycle_tuser_next; + + if (shift_in) begin + s_axis_tdata_d0 <= s_axis_tdata; + s_axis_tkeep_d0 <= s_axis_tkeep; + s_axis_tuser_d0 <= s_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_axis_tdata_reg = 64'd0; +reg [7:0] m_axis_tkeep_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_axis_tkeep_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_eth_fcs_insert.v b/corundum/lib/eth/rtl/axis_eth_fcs_insert.v new file mode 100644 index 0000000000000000000000000000000000000000..a73baab0bdba2271ac8148e1bb61d1e4e84f8798 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_eth_fcs_insert.v @@ -0,0 +1,368 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS inserter + */ +module axis_eth_fcs_insert # +( + parameter ENABLE_PADDING = 0, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [7:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] m_axis_tdata, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_PAD = 2'd2, + STATE_FCS = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg busy_reg = 1'b0; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +// internal datapath +reg [7:0] m_axis_tdata_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(m_axis_tdata_int), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + frame_ptr_next = 16'd1; + reset_crc = 1'b0; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_axis_tready && s_axis_tvalid) begin + frame_ptr_next = frame_ptr_reg + 16'd1; + update_crc = 1'b1; + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_PAD: begin + // insert padding + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 8'd0; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg + 16'd1; + update_crc = 1'b1; + if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end else begin + state_next = STATE_PAD; + end + end + STATE_FCS: begin + // send FCS + s_axis_tready_next = 1'b0; + + case (frame_ptr_reg) + 2'd0: m_axis_tdata_int = ~crc_state[7:0]; + 2'd1: m_axis_tdata_int = ~crc_state[15:8]; + 2'd2: m_axis_tdata_int = ~crc_state[23:16]; + 2'd3: m_axis_tdata_int = ~crc_state[31:24]; + endcase + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (m_axis_tready_int_reg) begin + frame_ptr_next = frame_ptr_reg + 16'd1; + + if (frame_ptr_reg < 16'd3) begin + state_next = STATE_FCS; + end else begin + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_FCS; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 1'b0; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + end +end + +// output datapath logic +reg [7:0] m_axis_tdata_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_axis_tdata_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_eth_fcs_insert_64.v b/corundum/lib/eth/rtl/axis_eth_fcs_insert_64.v new file mode 100644 index 0000000000000000000000000000000000000000..887cc4315ea6e768f7a706a04b9476a2ed406adc --- /dev/null +++ b/corundum/lib/eth/rtl/axis_eth_fcs_insert_64.v @@ -0,0 +1,717 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream Ethernet FCS inserter (64 bit datapath) + */ +module axis_eth_fcs_insert_64 # +( + parameter ENABLE_PADDING = 0, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [63:0] s_axis_tdata, + input wire [7:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * AXI output + */ + output wire [63:0] m_axis_tdata, + output wire [7:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status + */ + output wire busy +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_PAD = 2'd2, + STATE_FCS = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [63:0] s_axis_tdata_masked; + +reg [63:0] fcs_s_tdata; +reg [7:0] fcs_s_tkeep; + +reg [63:0] fcs_m_tdata_0; +reg [63:0] fcs_m_tdata_1; +reg [7:0] fcs_m_tkeep_0; +reg [7:0] fcs_m_tkeep_1; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [63:0] last_cycle_tdata_reg = 64'd0, last_cycle_tdata_next; +reg [7:0] last_cycle_tkeep_reg = 8'd0, last_cycle_tkeep_next; + +reg busy_reg = 1'b0; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +// internal datapath +reg [63:0] m_axis_tdata_int; +reg [7:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign busy = busy_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(fcs_s_tdata[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(fcs_s_tdata[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(fcs_s_tdata[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(fcs_s_tdata[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(fcs_s_tdata[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(fcs_s_tdata[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(fcs_s_tdata[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(fcs_s_tdata[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (fcs_s_tkeep) + 8'bzzzzzz01: begin + fcs_m_tdata_0 = {24'd0, ~crc_next0[31:0], fcs_s_tdata[7:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b00011111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzzzzz011: begin + fcs_m_tdata_0 = {16'd0, ~crc_next1[31:0], fcs_s_tdata[15:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b00111111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzzzz0111: begin + fcs_m_tdata_0 = {8'd0, ~crc_next2[31:0], fcs_s_tdata[23:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b01111111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzzz01111: begin + fcs_m_tdata_0 = {~crc_next3[31:0], fcs_s_tdata[31:0]}; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000000; + end + 8'bzz011111: begin + fcs_m_tdata_0 = {~crc_next4[23:0], fcs_s_tdata[39:0]}; + fcs_m_tdata_1 = {56'd0, ~crc_next4[31:24]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000001; + end + 8'bz0111111: begin + fcs_m_tdata_0 = {~crc_next5[15:0], fcs_s_tdata[47:0]}; + fcs_m_tdata_1 = {48'd0, ~crc_next5[31:16]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000011; + end + 8'b01111111: begin + fcs_m_tdata_0 = {~crc_next6[7:0], fcs_s_tdata[55:0]}; + fcs_m_tdata_1 = {40'd0, ~crc_next6[31:8]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00000111; + end + 8'b11111111: begin + fcs_m_tdata_0 = fcs_s_tdata; + fcs_m_tdata_1 = {32'd0, ~crc_next7[31:0]}; + fcs_m_tkeep_0 = 8'b11111111; + fcs_m_tkeep_1 = 8'b00001111; + end + default: begin + fcs_m_tdata_0 = 64'd0; + fcs_m_tdata_1 = 64'd0; + fcs_m_tkeep_0 = 8'd0; + fcs_m_tkeep_1 = 8'd0; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + last_cycle_tdata_next = last_cycle_tdata_reg; + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + s_axis_tready_next = 1'b0; + + fcs_s_tdata = 64'd0; + fcs_s_tkeep = 8'd0; + + m_axis_tdata_int = 64'd0; + m_axis_tkeep_int = 8'd0; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + reset_crc = 1'b1; + + m_axis_tdata_int = s_axis_tdata_masked; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + fcs_s_tdata = s_axis_tdata_masked; + fcs_s_tkeep = s_axis_tkeep; + + if (s_axis_tready && s_axis_tvalid) begin + reset_crc = 1'b0; + update_crc = 1'b1; + frame_ptr_next = keep2count(s_axis_tkeep); + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + if (ENABLE_PADDING && frame_ptr_next < MIN_FRAME_LENGTH-4) begin + m_axis_tkeep_int = 8'hff; + fcs_s_tkeep = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_next < MIN_FRAME_LENGTH-4) begin + s_axis_tready_next = 1'b0; + state_next = STATE_PAD; + end else begin + m_axis_tkeep_int = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + fcs_s_tkeep = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 1'b0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end else begin + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + s_axis_tready_next = m_axis_tready_int_early; + + m_axis_tdata_int = s_axis_tdata_masked; + m_axis_tkeep_int = s_axis_tkeep; + m_axis_tvalid_int = s_axis_tvalid; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + fcs_s_tdata = s_axis_tdata_masked; + fcs_s_tkeep = s_axis_tkeep; + + if (s_axis_tready && s_axis_tvalid) begin + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + if (s_axis_tlast) begin + if (s_axis_tuser) begin + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b1; + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + if (ENABLE_PADDING && frame_ptr_next < MIN_FRAME_LENGTH-4) begin + m_axis_tkeep_int = 8'hff; + fcs_s_tkeep = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_next < MIN_FRAME_LENGTH-4) begin + s_axis_tready_next = 1'b0; + state_next = STATE_PAD; + end else begin + m_axis_tkeep_int = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + fcs_s_tkeep = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end else begin + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_PAD: begin + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = 64'd0; + m_axis_tkeep_int = 8'hff; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + fcs_s_tdata = 64'd0; + fcs_s_tkeep = 8'hff; + + if (m_axis_tready_int_reg) begin + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_next < MIN_FRAME_LENGTH-4) begin + state_next = STATE_PAD; + end else begin + m_axis_tkeep_int = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + fcs_s_tkeep = 8'hff >> (8-((MIN_FRAME_LENGTH-4) & 7)); + + m_axis_tdata_int = fcs_m_tdata_0; + last_cycle_tdata_next = fcs_m_tdata_1; + m_axis_tkeep_int = fcs_m_tkeep_0; + last_cycle_tkeep_next = fcs_m_tkeep_1; + + reset_crc = 1'b1; + + if (fcs_m_tkeep_1 == 8'd0) begin + m_axis_tlast_int = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 16'd0; + state_next = STATE_IDLE; + end else begin + s_axis_tready_next = 1'b0; + state_next = STATE_FCS; + end + end + end else begin + state_next = STATE_PAD; + end + end + STATE_FCS: begin + // last cycle + s_axis_tready_next = 1'b0; + + m_axis_tdata_int = last_cycle_tdata_reg; + m_axis_tkeep_int = last_cycle_tkeep_reg; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = 1'b1; + m_axis_tuser_int = 1'b0; + + if (m_axis_tready_int_reg) begin + reset_crc = 1'b1; + s_axis_tready_next = m_axis_tready_int_early; + frame_ptr_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_FCS; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 1'b0; + + s_axis_tready_reg <= 1'b0; + + busy_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + busy_reg <= state_next != STATE_IDLE; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + last_cycle_tdata_reg <= last_cycle_tdata_next; + last_cycle_tkeep_reg <= last_cycle_tkeep_next; +end + +// output datapath logic +reg [63:0] m_axis_tdata_reg = 64'd0; +reg [7:0] m_axis_tkeep_reg = 8'd0; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_axis_tkeep_reg = 8'd0; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_gmii_rx.v b/corundum/lib/eth/rtl/axis_gmii_rx.v new file mode 100644 index 0000000000000000000000000000000000000000..b09240162907aa44ab39e0e9fb8df0b330941c62 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_gmii_rx.v @@ -0,0 +1,351 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream GMII frame receiver (GMII in, AXI out) + */ +module axis_gmii_rx # +( + parameter DATA_WIDTH = 8, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * GMII input + */ + input wire [DATA_WIDTH-1:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Status + */ + output wire start_packet, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 8) begin + $error("Error: Interface width must be 8"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_WAIT_LAST = 3'd2; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg mii_odd = 1'b0; +reg mii_locked = 1'b0; + +reg [DATA_WIDTH-1:0] gmii_rxd_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d2 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d3 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] gmii_rxd_d4 = {DATA_WIDTH{1'b0}}; + +reg gmii_rx_dv_d0 = 1'b0; +reg gmii_rx_dv_d1 = 1'b0; +reg gmii_rx_dv_d2 = 1'b0; +reg gmii_rx_dv_d3 = 1'b0; +reg gmii_rx_dv_d4 = 1'b0; + +reg gmii_rx_er_d0 = 1'b0; +reg gmii_rx_er_d1 = 1'b0; +reg gmii_rx_er_d2 = 1'b0; +reg gmii_rx_er_d3 = 1'b0; +reg gmii_rx_er_d4 = 1'b0; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0, ptp_ts_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(gmii_rxd_d4), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + start_packet_next = 1'b0; + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + ptp_ts_next = ptp_ts_reg; + + if (!clk_enable) begin + // clock disabled - hold state + state_next = state_reg; + end else if (mii_select && !mii_odd) begin + // MII even cycle - hold state + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (gmii_rx_dv_d4 && !gmii_rx_er_d4 && gmii_rxd_d4 == ETH_SFD) begin + ptp_ts_next = ptp_ts; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + update_crc = 1'b1; + + m_axis_tdata_next = gmii_rxd_d4; + m_axis_tvalid_next = 1'b1; + + if (gmii_rx_dv_d4 && gmii_rx_er_d4) begin + // error + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else if (!gmii_rx_dv) begin + // end of packet + m_axis_tlast_next = 1'b1; + if (gmii_rx_er_d0 || gmii_rx_er_d1 || gmii_rx_er_d2 || gmii_rx_er_d3) begin + // error received in FCS bytes + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + end else if ({gmii_rxd_d0, gmii_rxd_d1, gmii_rxd_d2, gmii_rxd_d3} == ~crc_next) begin + // FCS good + m_axis_tuser_next = 1'b0; + end else begin + // FCS bad + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_WAIT_LAST: begin + // wait for end of packet + + if (~gmii_rx_dv) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + mii_locked <= 1'b0; + mii_odd <= 1'b0; + + gmii_rx_dv_d0 <= 1'b0; + gmii_rx_dv_d1 <= 1'b0; + gmii_rx_dv_d2 <= 1'b0; + gmii_rx_dv_d3 <= 1'b0; + gmii_rx_dv_d4 <= 1'b0; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= start_packet_next; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + + if (clk_enable) begin + if (mii_select) begin + mii_odd <= !mii_odd; + + if (mii_locked) begin + mii_locked <= gmii_rx_dv; + end else if (gmii_rx_dv && {gmii_rxd[3:0], gmii_rxd_d0[7:4]} == ETH_SFD) begin + mii_locked <= 1'b1; + mii_odd <= 1'b1; + end + + if (mii_odd) begin + gmii_rx_dv_d0 <= gmii_rx_dv & gmii_rx_dv_d0; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + end else begin + gmii_rx_dv_d0 <= gmii_rx_dv; + end + end else begin + gmii_rx_dv_d0 <= gmii_rx_dv; + gmii_rx_dv_d1 <= gmii_rx_dv_d0 & gmii_rx_dv; + gmii_rx_dv_d2 <= gmii_rx_dv_d1 & gmii_rx_dv; + gmii_rx_dv_d3 <= gmii_rx_dv_d2 & gmii_rx_dv; + gmii_rx_dv_d4 <= gmii_rx_dv_d3 & gmii_rx_dv; + end + end + end + + ptp_ts_reg <= ptp_ts_next; + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + // delay input + if (clk_enable) begin + if (mii_select) begin + gmii_rxd_d0 <= {gmii_rxd[3:0], gmii_rxd_d0[7:4]}; + + if (mii_odd) begin + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_er_d0 <= gmii_rx_er | gmii_rx_er_d0; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end else begin + gmii_rx_er_d0 <= gmii_rx_er; + end + end else begin + gmii_rxd_d0 <= gmii_rxd; + gmii_rxd_d1 <= gmii_rxd_d0; + gmii_rxd_d2 <= gmii_rxd_d1; + gmii_rxd_d3 <= gmii_rxd_d2; + gmii_rxd_d4 <= gmii_rxd_d3; + + gmii_rx_er_d0 <= gmii_rx_er; + gmii_rx_er_d1 <= gmii_rx_er_d0; + gmii_rx_er_d2 <= gmii_rx_er_d1; + gmii_rx_er_d3 <= gmii_rx_er_d2; + gmii_rx_er_d4 <= gmii_rx_er_d3; + end + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_gmii_tx.v b/corundum/lib/eth/rtl/axis_gmii_tx.v new file mode 100644 index 0000000000000000000000000000000000000000..de4c43998dc58b9f1f5b7c1df426172de28dd301 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_gmii_tx.v @@ -0,0 +1,448 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream GMII frame transmitter (AXI in, GMII out) + */ +module axis_gmii_tx # +( + parameter DATA_WIDTH = 8, + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * GMII output + */ + output wire [DATA_WIDTH-1:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Control + */ + input wire clk_enable, + input wire mii_select, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 8) begin + $error("Error: Interface width must be 8"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PREAMBLE = 3'd1, + STATE_PAYLOAD = 3'd2, + STATE_LAST = 3'd3, + STATE_PAD = 3'd4, + STATE_FCS = 3'd5, + STATE_WAIT_END = 3'd6, + STATE_IFG = 3'd7; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [7:0] s_tdata_reg = 8'd0, s_tdata_next; + +reg mii_odd_reg = 1'b0, mii_odd_next; +reg [3:0] mii_msn_reg = 4'b0, mii_msn_next; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] gmii_txd_reg = 8'd0, gmii_txd_next; +reg gmii_tx_en_reg = 1'b0, gmii_tx_en_next; +reg gmii_tx_er_reg = 1'b0, gmii_tx_er_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; +wire [31:0] crc_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign gmii_txd = gmii_txd_reg; +assign gmii_tx_en = gmii_tx_en_reg; +assign gmii_tx_er = gmii_tx_er_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg), + .state_in(crc_state), + .data_out(), + .state_out(crc_next) +); + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + mii_odd_next = mii_odd_reg; + mii_msn_next = mii_msn_reg; + + frame_ptr_next = frame_ptr_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + + gmii_txd_next = {DATA_WIDTH{1'b0}}; + gmii_tx_en_next = 1'b0; + gmii_tx_er_next = 1'b0; + + start_packet_next = 1'b0; + error_underflow_next = 1'b0; + + if (!clk_enable) begin + // clock disabled - hold state and outputs + gmii_txd_next = gmii_txd_reg; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else if (mii_select && mii_odd_reg) begin + // MII odd cycle - hold state, output MSN + mii_odd_next = 1'b0; + gmii_txd_next = {4'd0, mii_msn_reg}; + gmii_tx_en_next = gmii_tx_en_reg; + gmii_tx_er_next = gmii_tx_er_reg; + state_next = state_reg; + end else begin + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + mii_odd_next = 1'b0; + + if (s_axis_tvalid) begin + mii_odd_next = 1'b1; + frame_ptr_next = 16'd1; + gmii_txd_next = ETH_PRE; + gmii_tx_en_next = 1'b1; + state_next = STATE_PREAMBLE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // send preamble + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = ETH_PRE; + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg == 16'd6) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + state_next = STATE_PREAMBLE; + end else if (frame_ptr_reg == 16'd7) begin + // end of preamble; start payload + frame_ptr_next = 16'd0; + if (s_axis_tready_reg) begin + s_axis_tready_next = 1'b1; + s_tdata_next = s_axis_tdata; + end + gmii_txd_next = ETH_SFD; + m_axis_ptp_ts_next = ptp_ts; + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_next = 1'b1; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + state_next = STATE_PREAMBLE; + end + end + STATE_PAYLOAD: begin + // send payload + + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + s_tdata_next = s_axis_tdata; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = !s_axis_tready_reg; + if (s_axis_tuser[0]) begin + gmii_tx_er_next = 1'b1; + frame_ptr_next = 1'b0; + state_next = STATE_IFG; + end else begin + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + gmii_tx_er_next = 1'b1; + frame_ptr_next = 16'd0; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_LAST: begin + // last payload word + + update_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = s_tdata_reg; + gmii_tx_en_next = 1'b1; + + if (ENABLE_PADDING && frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + s_tdata_next = 8'd0; + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_PAD: begin + // send padding + + update_crc = 1'b1; + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + gmii_txd_next = 8'd0; + gmii_tx_en_next = 1'b1; + + s_tdata_next = 8'd0; + + if (frame_ptr_reg < MIN_FRAME_LENGTH-5) begin + state_next = STATE_PAD; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_FCS; + end + end + STATE_FCS: begin + // send FCS + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + case (frame_ptr_reg) + 2'd0: gmii_txd_next = ~crc_state[7:0]; + 2'd1: gmii_txd_next = ~crc_state[15:8]; + 2'd2: gmii_txd_next = ~crc_state[23:16]; + 2'd3: gmii_txd_next = ~crc_state[31:24]; + endcase + gmii_tx_en_next = 1'b1; + + if (frame_ptr_reg < 3) begin + state_next = STATE_FCS; + end else begin + frame_ptr_next = 16'd0; + state_next = STATE_IFG; + end + end + STATE_WAIT_END: begin + // wait for end of frame + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + s_axis_tready_next = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + STATE_IFG: begin + // send IFG + + reset_crc = 1'b1; + + mii_odd_next = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd1; + + if (frame_ptr_reg < ifg_delay-1) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + endcase + + if (mii_select) begin + mii_msn_next = gmii_txd_next[7:4]; + gmii_txd_next[7:4] = 4'd0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + + gmii_tx_en_reg <= 1'b0; + gmii_tx_er_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + + gmii_tx_en_reg <= gmii_tx_en_next; + gmii_tx_er_reg <= gmii_tx_er_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next; + end + end + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + + mii_odd_reg <= mii_odd_next; + mii_msn_reg <= mii_msn_next; + + s_tdata_reg <= s_tdata_next; + + gmii_txd_reg <= gmii_txd_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_xgmii_rx_32.v b/corundum/lib/eth/rtl/axis_xgmii_rx_32.v new file mode 100644 index 0000000000000000000000000000000000000000..a295b020c5f83d1a5db3ab8330c2abeb00e8dc59 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_xgmii_rx_32.v @@ -0,0 +1,435 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream XGMII frame receiver (XGMII in, AXI out) + */ +module axis_xgmii_rx_32 # +( + parameter DATA_WIDTH = 32, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * XGMII input + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire start_packet, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 32) begin + $error("Error: Interface width must be 32"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PREAMBLE = 2'd1, + STATE_PAYLOAD = 2'd2, + STATE_LAST = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; + +reg [3:0] last_cycle_tkeep_reg = 4'd0, last_cycle_tkeep_next; + +reg [DATA_WIDTH-1:0] xgmii_rxd_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_d2 = {DATA_WIDTH{1'b0}}; + +reg [CTRL_WIDTH-1:0] xgmii_rxc_d0 = {CTRL_WIDTH{1'b0}}; +reg [CTRL_WIDTH-1:0] xgmii_rxc_d1 = {CTRL_WIDTH{1'b0}}; +reg [CTRL_WIDTH-1:0] xgmii_rxc_d2 = {CTRL_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, m_axis_tkeep_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0, ptp_ts_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; + +reg crc_valid0_save = 1'b0; +reg crc_valid1_save = 1'b0; +reg crc_valid2_save = 1'b0; +reg crc_valid3_save = 1'b0; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +wire last_cycle = state_reg == STATE_LAST; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(xgmii_rxd_d0[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(xgmii_rxd_d0[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(xgmii_rxd_d0[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(xgmii_rxd_d0[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +// detect control characters +reg [3:0] detect_term = 4'd0; + +reg [3:0] detect_term_save; + +integer i; + +// mask errors to within packet +reg [3:0] control_masked; +reg [3:0] tkeep_mask; + +always @* begin + casez (detect_term) + 4'b0000: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 4'b1111; + end + 4'bzzz1: begin + control_masked = 0; + tkeep_mask = 4'b0000; + end + 4'bzz10: begin + control_masked = xgmii_rxc_d0[0]; + tkeep_mask = 4'b0001; + end + 4'bz100: begin + control_masked = xgmii_rxc_d0[1:0]; + tkeep_mask = 4'b0011; + end + 4'b1000: begin + control_masked = xgmii_rxc_d0[2:0]; + tkeep_mask = 4'b0111; + end + default: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 4'b1111; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + start_packet_next = 1'b0; + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + ptp_ts_next = ptp_ts_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (xgmii_rxc_d2[0] && xgmii_rxd_d2[7:0] == XGMII_START) begin + // start condition + if (control_masked) begin + // control or error characters in first data word + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = 4'h1; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + state_next = STATE_PREAMBLE; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // drop preamble + ptp_ts_next = ptp_ts; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end + STATE_PAYLOAD: begin + // read payload + m_axis_tdata_next = xgmii_rxd_d2; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + last_cycle_tkeep_next = tkeep_mask; + + if (control_masked) begin + // control or error characters in packet + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end else if (detect_term) begin + if (detect_term[0]) begin + // end this cycle + reset_crc = 1'b1; + m_axis_tkeep_next = 4'b1111; + m_axis_tlast_next = 1'b1; + if (detect_term[0] && crc_valid3_save) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + // need extra cycle + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle of packet + m_axis_tdata_next = xgmii_rxd_d2; + m_axis_tkeep_next = last_cycle_tkeep_reg; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + if ((detect_term_save[1] && crc_valid0_save) || + (detect_term_save[2] && crc_valid1_save) || + (detect_term_save[3] && crc_valid2_save)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 1'b0; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + xgmii_rxc_d0 <= {CTRL_WIDTH{1'b0}}; + xgmii_rxc_d1 <= {CTRL_WIDTH{1'b0}}; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= start_packet_next; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + xgmii_rxc_d0 <= xgmii_rxc; + xgmii_rxc_d1 <= xgmii_rxc_d0; + xgmii_rxc_d2 <= xgmii_rxc_d1; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else begin + crc_state <= crc_next3; + end + end + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tkeep_reg <= m_axis_tkeep_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + ptp_ts_reg <= ptp_ts_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + + for (i = 0; i < 4; i = i + 1) begin + detect_term[i] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + + detect_term_save <= detect_term; + + crc_valid0_save <= crc_valid0; + crc_valid1_save <= crc_valid1; + crc_valid2_save <= crc_valid2; + crc_valid3_save <= crc_valid3; + + xgmii_rxd_d0 <= xgmii_rxd; + xgmii_rxd_d1 <= xgmii_rxd_d0; + xgmii_rxd_d2 <= xgmii_rxd_d1; +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_xgmii_rx_64.v b/corundum/lib/eth/rtl/axis_xgmii_rx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..8ef42e16be6bb05013606e781ba47ab706354c52 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_xgmii_rx_64.v @@ -0,0 +1,555 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream XGMII frame receiver (XGMII in, AXI out) + */ +module axis_xgmii_rx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * XGMII input + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_bad_frame, + output wire error_bad_fcs +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_PAYLOAD = 2'd1, + STATE_LAST = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc_last; + +reg [7:0] last_cycle_tkeep_reg = 8'd0, last_cycle_tkeep_next; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_rxd = 32'd0; +reg [3:0] swap_rxc = 4'd0; + +reg [DATA_WIDTH-1:0] xgmii_rxd_d0 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_d1 = {DATA_WIDTH{1'b0}}; +reg [DATA_WIDTH-1:0] xgmii_rxd_crc = {DATA_WIDTH{1'b0}}; + +reg [CTRL_WIDTH-1:0] xgmii_rxc_d0 = {CTRL_WIDTH{1'b0}}; +reg [CTRL_WIDTH-1:0] xgmii_rxc_d1 = {CTRL_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}, m_axis_tdata_next; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}, m_axis_tkeep_next; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0, m_axis_tlast_next; +reg m_axis_tuser_reg = 1'b0, m_axis_tuser_next; + +reg [1:0] start_packet_reg = 2'b00; +reg error_bad_frame_reg = 1'b0, error_bad_frame_next; +reg error_bad_fcs_reg = 1'b0, error_bad_fcs_next; + +reg [PTP_TS_WIDTH-1:0] ptp_ts_reg = 0; + +reg [31:0] crc_state = 32'hFFFFFFFF; +reg [31:0] crc_state3 = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next7; + +wire crc_valid0 = crc_next0 == ~32'h2144df1c; +wire crc_valid1 = crc_next1 == ~32'h2144df1c; +wire crc_valid2 = crc_next2 == ~32'h2144df1c; +wire crc_valid3 = crc_next3 == ~32'h2144df1c; +wire crc_valid7 = crc_next7 == ~32'h2144df1c; + +reg crc_valid7_save = 1'b0; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = PTP_TS_ENABLE ? {ptp_ts_reg, m_axis_tuser_reg} : m_axis_tuser_reg; + +assign start_packet = start_packet_reg; +assign error_bad_frame = error_bad_frame_reg; +assign error_bad_fcs = error_bad_fcs_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(xgmii_rxd_crc[7:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(xgmii_rxd_crc[15:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(xgmii_rxd_crc[23:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(xgmii_rxd_crc[31:0]), + .state_in(crc_state3), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(xgmii_rxd_crc[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +// detect control characters +reg [7:0] detect_term = 8'd0; + +reg [7:0] detect_term_save = 8'd0; + +integer i; + +// mask errors to within packet +reg [7:0] control_masked; +reg [7:0] tkeep_mask; + +always @* begin + casez (detect_term) + 8'b00000000: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 8'b11111111; + end + 8'bzzzzzzz1: begin + control_masked = 0; + tkeep_mask = 8'b00000000; + end + 8'bzzzzzz10: begin + control_masked = xgmii_rxc_d0[0]; + tkeep_mask = 8'b00000001; + end + 8'bzzzzz100: begin + control_masked = xgmii_rxc_d0[1:0]; + tkeep_mask = 8'b00000011; + end + 8'bzzzz1000: begin + control_masked = xgmii_rxc_d0[2:0]; + tkeep_mask = 8'b00000111; + end + 8'bzzz10000: begin + control_masked = xgmii_rxc_d0[3:0]; + tkeep_mask = 8'b00001111; + end + 8'bzz100000: begin + control_masked = xgmii_rxc_d0[4:0]; + tkeep_mask = 8'b00011111; + end + 8'bz1000000: begin + control_masked = xgmii_rxc_d0[5:0]; + tkeep_mask = 8'b00111111; + end + 8'b10000000: begin + control_masked = xgmii_rxc_d0[6:0]; + tkeep_mask = 8'b01111111; + end + default: begin + control_masked = xgmii_rxc_d0; + tkeep_mask = 8'b11111111; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc_last = 1'b0; + + last_cycle_tkeep_next = last_cycle_tkeep_reg; + + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b0; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + error_bad_frame_next = 1'b0; + error_bad_fcs_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for packet + reset_crc = 1'b1; + + if (xgmii_rxc_d1[0] && xgmii_rxd_d1[7:0] == XGMII_START) begin + // start condition + if (control_masked) begin + // control or error characters in first data word + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = 8'h01; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // read payload + m_axis_tdata_next = xgmii_rxd_d1; + m_axis_tkeep_next = {KEEP_WIDTH{1'b1}}; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b0; + m_axis_tuser_next = 1'b0; + + last_cycle_tkeep_next = {4'b0000, tkeep_mask[7:4]}; + + if (control_masked) begin + // control or error characters in packet + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + reset_crc = 1'b1; + state_next = STATE_IDLE; + end else if (detect_term) begin + if (detect_term[4:0]) begin + // end this cycle + reset_crc = 1'b1; + m_axis_tkeep_next = {tkeep_mask[3:0], 4'b1111}; + m_axis_tlast_next = 1'b1; + if ((detect_term[0] && crc_valid7_save) || + (detect_term[1] && crc_valid0) || + (detect_term[2] && crc_valid1) || + (detect_term[3] && crc_valid2) || + (detect_term[4] && crc_valid3)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + // need extra cycle + update_crc_last = 1'b1; + state_next = STATE_LAST; + end + end else begin + state_next = STATE_PAYLOAD; + end + end + STATE_LAST: begin + // last cycle of packet + m_axis_tdata_next = xgmii_rxd_d1; + m_axis_tkeep_next = last_cycle_tkeep_reg; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b0; + + reset_crc = 1'b1; + + if ((detect_term_save[5] && crc_valid0) || + (detect_term_save[6] && crc_valid1) || + (detect_term_save[7] && crc_valid2)) begin + // CRC valid + end else begin + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + error_bad_fcs_next = 1'b1; + end + + if (xgmii_rxc_d1[0] && xgmii_rxd_d1[7:0] == XGMII_START) begin + // start condition + if (control_masked) begin + // control or error characters in first data word + m_axis_tdata_next = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_next = 8'h01; + m_axis_tvalid_next = 1'b1; + m_axis_tlast_next = 1'b1; + m_axis_tuser_next = 1'b1; + error_bad_frame_next = 1'b1; + state_next = STATE_IDLE; + end else begin + reset_crc = 1'b0; + state_next = STATE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + m_axis_tvalid_reg <= 1'b0; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= 1'b0; + error_bad_fcs_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + crc_state3 <= 32'hFFFFFFFF; + + xgmii_rxc_d0 <= {CTRL_WIDTH{1'b0}}; + xgmii_rxc_d1 <= {CTRL_WIDTH{1'b0}}; + + lanes_swapped <= 1'b0; + end else begin + state_reg <= state_next; + + m_axis_tvalid_reg <= m_axis_tvalid_next; + + start_packet_reg <= 2'b00; + error_bad_frame_reg <= error_bad_frame_next; + error_bad_fcs_reg <= error_bad_fcs_next; + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START) begin + lanes_swapped <= 1'b0; + start_packet_reg <= 2'b01; + xgmii_rxc_d0 <= xgmii_rxc; + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == XGMII_START) begin + lanes_swapped <= 1'b1; + start_packet_reg <= 2'b10; + xgmii_rxc_d0 <= {xgmii_rxc[3:0], swap_rxc}; + end else if (lanes_swapped) begin + xgmii_rxc_d0 <= {xgmii_rxc[3:0], swap_rxc}; + end else begin + xgmii_rxc_d0 <= xgmii_rxc; + end + + xgmii_rxc_d1 <= xgmii_rxc_d0; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else begin + crc_state <= crc_next7; + end + + if (update_crc_last) begin + crc_state3 <= crc_next3; + end else begin + crc_state3 <= crc_next7; + end + end + + if (PTP_TS_WIDTH == 96 && $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + ptp_ts_reg[45:16] <= $signed({1'b0, ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + ptp_ts_reg[95:48] <= ptp_ts_reg[95:48] + 1; + end + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == XGMII_START) begin + if (PTP_TS_WIDTH == 96) begin + ptp_ts_reg[45:0] <= ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + ptp_ts_reg[95:48] <= ptp_ts[95:48]; + end else begin + ptp_ts_reg <= ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + end + + m_axis_tdata_reg <= m_axis_tdata_next; + m_axis_tkeep_reg <= m_axis_tkeep_next; + m_axis_tlast_reg <= m_axis_tlast_next; + m_axis_tuser_reg <= m_axis_tuser_next; + + last_cycle_tkeep_reg <= last_cycle_tkeep_next; + + detect_term_save <= detect_term; + + swap_rxd <= xgmii_rxd[63:32]; + swap_rxc <= xgmii_rxc[7:4]; + + if (xgmii_rxc[0] && xgmii_rxd[7:0] == XGMII_START) begin + xgmii_rxd_d0 <= xgmii_rxd; + xgmii_rxd_crc <= xgmii_rxd; + + for (i = 0; i < 8; i = i + 1) begin + detect_term[i] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end else if (xgmii_rxc[4] && xgmii_rxd[39:32] == XGMII_START) begin + xgmii_rxd_d0 <= {xgmii_rxd[31:0], swap_rxd}; + xgmii_rxd_crc <= {xgmii_rxd[31:0], swap_rxd}; + + for (i = 0; i < 4; i = i + 1) begin + detect_term[i] <= swap_rxc[i] && (swap_rxd[i*8 +: 8] == XGMII_TERM); + detect_term[i+4] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end else if (lanes_swapped) begin + xgmii_rxd_d0 <= {xgmii_rxd[31:0], swap_rxd}; + xgmii_rxd_crc <= {xgmii_rxd[31:0], swap_rxd}; + + for (i = 0; i < 4; i = i + 1) begin + detect_term[i] <= swap_rxc[i] && (swap_rxd[i*8 +: 8] == XGMII_TERM); + detect_term[i+4] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end else begin + xgmii_rxd_d0 <= xgmii_rxd; + xgmii_rxd_crc <= xgmii_rxd; + + for (i = 0; i < 8; i = i + 1) begin + detect_term[i] <= xgmii_rxc[i] && (xgmii_rxd[i*8 +: 8] == XGMII_TERM); + end + end + + crc_valid7_save <= crc_valid7; + + if (state_next == STATE_LAST) begin + xgmii_rxd_crc[31:0] <= xgmii_rxd_crc[63:32]; + end + + xgmii_rxd_d1 <= xgmii_rxd_d0; +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_xgmii_tx_32.v b/corundum/lib/eth/rtl/axis_xgmii_tx_32.v new file mode 100644 index 0000000000000000000000000000000000000000..15f4a70502d0b139b1b1c967f67fdb1a460c7a22 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_xgmii_tx_32.v @@ -0,0 +1,626 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream XGMII frame transmitter (AXI in, XGMII out) + */ +module axis_xgmii_tx_32 # +( + parameter DATA_WIDTH = 32, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * XGMII output + */ + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 32) begin + $error("Error: Interface width must be 32"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfffc; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0003; + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [3:0] + STATE_IDLE = 4'd0, + STATE_PREAMBLE = 4'd1, + STATE_PAYLOAD = 4'd2, + STATE_PAD = 4'd3, + STATE_FCS_1 = 4'd4, + STATE_FCS_2 = 4'd5, + STATE_FCS_3 = 4'd6, + STATE_IFG = 4'd7, + STATE_WAIT_END = 4'd8; + +reg [3:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] s_tdata_reg ={DATA_WIDTH{1'b0}}, s_tdata_next; +reg [KEEP_WIDTH-1:0] s_tkeep_reg = {KEEP_WIDTH{1'b0}}, s_tkeep_next; + +reg [DATA_WIDTH-1:0] fcs_output_txd_0; +reg [DATA_WIDTH-1:0] fcs_output_txd_1; +reg [CTRL_WIDTH-1:0] fcs_output_txc_0; +reg [CTRL_WIDTH-1:0] fcs_output_txc_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; + +reg [DATA_WIDTH-1:0] xgmii_txd_reg = {CTRL_WIDTH{XGMII_IDLE}}, xgmii_txd_next; +reg [CTRL_WIDTH-1:0] xgmii_txc_reg = {CTRL_WIDTH{1'b1}}, xgmii_txc_next; + +reg start_packet_reg = 1'b0, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign xgmii_txd = xgmii_txd_reg; +assign xgmii_txc = xgmii_txc_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +function [2:0] keep2count; + input [3:0] k; + casez (k) + 4'bzzz0: keep2count = 3'd0; + 4'bzz01: keep2count = 3'd1; + 4'bz011: keep2count = 3'd2; + 4'b0111: keep2count = 3'd3; + 4'b1111: keep2count = 3'd4; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 4; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (s_tkeep_reg) + 4'bzz01: begin + fcs_output_txd_0 = {~crc_next0[23:0], s_tdata_reg[7:0]}; + fcs_output_txd_1 = {{2{XGMII_IDLE}}, XGMII_TERM, ~crc_next0[31:24]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1110; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 4'bz011: begin + fcs_output_txd_0 = {~crc_next1[15:0], s_tdata_reg[15:0]}; + fcs_output_txd_1 = {XGMII_IDLE, XGMII_TERM, ~crc_next1[31:16]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1100; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 4'b0111: begin + fcs_output_txd_0 = {~crc_next2[7:0], s_tdata_reg[23:0]}; + fcs_output_txd_1 = {XGMII_TERM, ~crc_next2[31:8]}; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b1000; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 4'b1111: begin + fcs_output_txd_0 = s_tdata_reg; + fcs_output_txd_1 = ~crc_next3; + fcs_output_txc_0 = 4'b0000; + fcs_output_txc_1 = 4'b0000; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_txd_0 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txd_1 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txc_0 = {CTRL_WIDTH{1'b1}}; + fcs_output_txc_1 = {CTRL_WIDTH{1'b1}}; + ifg_offset = 8'd0; + extra_cycle = 1'b0; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + s_tkeep_next = s_tkeep_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + start_packet_next = 1'b0; + error_underflow_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd4; + reset_crc = 1'b1; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + // XGMII start and preamble + xgmii_txd_next = {{3{ETH_PRE}}, XGMII_START}; + xgmii_txc_next = 4'b0001; + s_axis_tready_next = 1'b1; + state_next = STATE_PREAMBLE; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + state_next = STATE_IDLE; + end + end + STATE_PREAMBLE: begin + // send preamble + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + xgmii_txd_next = {ETH_SFD, {3{ETH_PRE}}}; + xgmii_txc_next = 4'b0000; + s_axis_tready_next = 1'b1; + m_axis_ptp_ts_next = ptp_ts; + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_next = 1'b1; + start_packet_next = 1'b1; + state_next = STATE_PAYLOAD; + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd4; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = 4'b0000; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + s_axis_tready_next = 1'b0; + if (s_axis_tuser[0]) begin + xgmii_txd_next = {XGMII_TERM, {3{XGMII_ERROR}}}; + xgmii_txc_next = 4'b1111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd10; + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(s_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + s_tkeep_next = 4'hf; + frame_ptr_next = frame_ptr_reg + 16'd4; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-4)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 4'hf >> ((4-MIN_FL_NOCRC_LS) % 4); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + xgmii_txd_next = {XGMII_TERM, {3{XGMII_ERROR}}}; + xgmii_txc_next = 4'b1111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd10; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + s_axis_tready_next = 1'b0; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = {CTRL_WIDTH{1'b0}}; + + s_tdata_next = 32'd0; + s_tkeep_next = 4'hf; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd4; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-4)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 4'hf >> ((4-MIN_FL_NOCRC_LS) % 4); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_0; + xgmii_txc_next = fcs_output_txc_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + deficit_idle_count_reg; + state_next = STATE_FCS_2; + end + STATE_FCS_2: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_1; + xgmii_txc_next = fcs_output_txc_1; + + frame_ptr_next = 16'd0; + + if (extra_cycle) begin + state_next = STATE_FCS_3; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_3: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = {{3{XGMII_IDLE}}, XGMII_TERM}; + xgmii_txc_next = 4'b1111; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd4) begin + ifg_count_next = ifg_count_reg - 8'd4; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + s_axis_tready_next = 1'b1; + + if (ifg_count_reg > 8'd4) begin + ifg_count_next = ifg_count_reg - 8'd4; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd3) begin + state_next = STATE_IFG; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd0) begin + state_next = STATE_IFG; + end else begin + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + + xgmii_txd_reg <= {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_reg <= {CTRL_WIDTH{1'b1}}; + + start_packet_reg <= 1'b0; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + + xgmii_txd_reg <= xgmii_txd_next; + xgmii_txc_reg <= xgmii_txc_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next3; + end + end + + s_tdata_reg <= s_tdata_next; + s_tkeep_reg <= s_tkeep_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/axis_xgmii_tx_64.v b/corundum/lib/eth/rtl/axis_xgmii_tx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..4e527c64a1e0bf96a600324e2d0afbf6fbbe7e95 --- /dev/null +++ b/corundum/lib/eth/rtl/axis_xgmii_tx_64.v @@ -0,0 +1,783 @@ +/* + +Copyright (c) 2015-2017 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream XGMII frame transmitter (AXI in, XGMII out) + */ +module axis_xgmii_tx_64 # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * XGMII output + */ + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + + /* + * Status + */ + output wire [1:0] start_packet, + output wire error_underflow +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +localparam MIN_FL_NOCRC = MIN_FRAME_LENGTH-4; +localparam MIN_FL_NOCRC_MS = MIN_FL_NOCRC & 16'hfff8; +localparam MIN_FL_NOCRC_LS = MIN_FL_NOCRC & 16'h0007; + +localparam [7:0] + ETH_PRE = 8'h55, + ETH_SFD = 8'hD5; + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe; + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_PAYLOAD = 3'd1, + STATE_PAD = 3'd2, + STATE_FCS_1 = 3'd3, + STATE_FCS_2 = 3'd4, + STATE_IFG = 3'd5, + STATE_WAIT_END = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg reset_crc; +reg update_crc; + +reg swap_lanes; +reg unswap_lanes; + +reg lanes_swapped = 1'b0; +reg [31:0] swap_txd = 32'd0; +reg [3:0] swap_txc = 4'd0; + +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] s_tdata_reg = {DATA_WIDTH{1'b0}}, s_tdata_next; +reg [KEEP_WIDTH-1:0] s_tkeep_reg = {KEEP_WIDTH{1'b0}}, s_tkeep_next; + +reg [DATA_WIDTH-1:0] fcs_output_txd_0; +reg [DATA_WIDTH-1:0] fcs_output_txd_1; +reg [CTRL_WIDTH-1:0] fcs_output_txc_0; +reg [CTRL_WIDTH-1:0] fcs_output_txc_1; + +reg [7:0] ifg_offset; + +reg extra_cycle; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [7:0] ifg_count_reg = 8'd0, ifg_count_next; +reg [1:0] deficit_idle_count_reg = 2'd0, deficit_idle_count_next; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg [PTP_TS_WIDTH-1:0] m_axis_ptp_ts_reg = 0, m_axis_ptp_ts_next; +reg [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag_reg = 0, m_axis_ptp_ts_tag_next; +reg m_axis_ptp_ts_valid_reg = 1'b0, m_axis_ptp_ts_valid_next; +reg m_axis_ptp_ts_valid_int_reg = 1'b0, m_axis_ptp_ts_valid_int_next; + +reg [31:0] crc_state = 32'hFFFFFFFF; + +wire [31:0] crc_next0; +wire [31:0] crc_next1; +wire [31:0] crc_next2; +wire [31:0] crc_next3; +wire [31:0] crc_next4; +wire [31:0] crc_next5; +wire [31:0] crc_next6; +wire [31:0] crc_next7; + +reg [DATA_WIDTH-1:0] xgmii_txd_reg = {CTRL_WIDTH{XGMII_IDLE}}, xgmii_txd_next; +reg [CTRL_WIDTH-1:0] xgmii_txc_reg = {CTRL_WIDTH{1'b1}}, xgmii_txc_next; + +reg start_packet_reg = 2'b00, start_packet_next; +reg error_underflow_reg = 1'b0, error_underflow_next; + +assign s_axis_tready = s_axis_tready_reg; + +assign xgmii_txd = xgmii_txd_reg; +assign xgmii_txc = xgmii_txc_reg; + +assign m_axis_ptp_ts = PTP_TS_ENABLE ? m_axis_ptp_ts_reg : 0; +assign m_axis_ptp_ts_tag = PTP_TAG_ENABLE ? m_axis_ptp_ts_tag_reg : 0; +assign m_axis_ptp_ts_valid = PTP_TS_ENABLE || PTP_TAG_ENABLE ? m_axis_ptp_ts_valid_reg : 1'b0; + +assign start_packet = start_packet_reg; +assign error_underflow = error_underflow_reg; + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(8), + .STYLE("AUTO") +) +eth_crc_8 ( + .data_in(s_tdata_reg[7:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next0) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(16), + .STYLE("AUTO") +) +eth_crc_16 ( + .data_in(s_tdata_reg[15:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next1) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(24), + .STYLE("AUTO") +) +eth_crc_24 ( + .data_in(s_tdata_reg[23:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next2) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(32), + .STYLE("AUTO") +) +eth_crc_32 ( + .data_in(s_tdata_reg[31:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next3) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(40), + .STYLE("AUTO") +) +eth_crc_40 ( + .data_in(s_tdata_reg[39:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next4) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(48), + .STYLE("AUTO") +) +eth_crc_48 ( + .data_in(s_tdata_reg[47:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next5) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(56), + .STYLE("AUTO") +) +eth_crc_56 ( + .data_in(s_tdata_reg[55:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next6) +); + +lfsr #( + .LFSR_WIDTH(32), + .LFSR_POLY(32'h4c11db7), + .LFSR_CONFIG("GALOIS"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(64), + .STYLE("AUTO") +) +eth_crc_64 ( + .data_in(s_tdata_reg[63:0]), + .state_in(crc_state), + .data_out(), + .state_out(crc_next7) +); + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < 8; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = s_axis_tkeep[j] ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// FCS cycle calculation +always @* begin + casez (s_tkeep_reg) + 8'bzzzzzz01: begin + fcs_output_txd_0 = {{2{XGMII_IDLE}}, XGMII_TERM, ~crc_next0[31:0], s_tdata_reg[7:0]}; + fcs_output_txd_1 = {8{XGMII_IDLE}}; + fcs_output_txc_0 = 8'b11100000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd3; + extra_cycle = 1'b0; + end + 8'bzzzzz011: begin + fcs_output_txd_0 = {XGMII_IDLE, XGMII_TERM, ~crc_next1[31:0], s_tdata_reg[15:0]}; + fcs_output_txd_1 = {8{XGMII_IDLE}}; + fcs_output_txc_0 = 8'b11000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd2; + extra_cycle = 1'b0; + end + 8'bzzzz0111: begin + fcs_output_txd_0 = {XGMII_TERM, ~crc_next2[31:0], s_tdata_reg[23:0]}; + fcs_output_txd_1 = {8{XGMII_IDLE}}; + fcs_output_txc_0 = 8'b10000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd1; + extra_cycle = 1'b0; + end + 8'bzzz01111: begin + fcs_output_txd_0 = {~crc_next3[31:0], s_tdata_reg[31:0]}; + fcs_output_txd_1 = {{7{XGMII_IDLE}}, XGMII_TERM}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111111; + ifg_offset = 8'd8; + extra_cycle = 1'b1; + end + 8'bzz011111: begin + fcs_output_txd_0 = {~crc_next4[23:0], s_tdata_reg[39:0]}; + fcs_output_txd_1 = {{6{XGMII_IDLE}}, XGMII_TERM, ~crc_next4[31:24]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111110; + ifg_offset = 8'd7; + extra_cycle = 1'b1; + end + 8'bz0111111: begin + fcs_output_txd_0 = {~crc_next5[15:0], s_tdata_reg[47:0]}; + fcs_output_txd_1 = {{5{XGMII_IDLE}}, XGMII_TERM, ~crc_next5[31:16]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111100; + ifg_offset = 8'd6; + extra_cycle = 1'b1; + end + 8'b01111111: begin + fcs_output_txd_0 = {~crc_next6[7:0], s_tdata_reg[55:0]}; + fcs_output_txd_1 = {{4{XGMII_IDLE}}, XGMII_TERM, ~crc_next6[31:8]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11111000; + ifg_offset = 8'd5; + extra_cycle = 1'b1; + end + 8'b11111111: begin + fcs_output_txd_0 = s_tdata_reg; + fcs_output_txd_1 = {{3{XGMII_IDLE}}, XGMII_TERM, ~crc_next7[31:0]}; + fcs_output_txc_0 = 8'b00000000; + fcs_output_txc_1 = 8'b11110000; + ifg_offset = 8'd4; + extra_cycle = 1'b1; + end + default: begin + fcs_output_txd_0 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txd_1 = {CTRL_WIDTH{XGMII_ERROR}}; + fcs_output_txc_0 = {CTRL_WIDTH{1'b1}}; + fcs_output_txc_1 = {CTRL_WIDTH{1'b1}}; + ifg_offset = 8'd0; + extra_cycle = 1'b1; + end + endcase +end + +always @* begin + state_next = STATE_IDLE; + + reset_crc = 1'b0; + update_crc = 1'b0; + + swap_lanes = 1'b0; + unswap_lanes = 1'b0; + + frame_ptr_next = frame_ptr_reg; + + ifg_count_next = ifg_count_reg; + deficit_idle_count_next = deficit_idle_count_reg; + + s_axis_tready_next = 1'b0; + + s_tdata_next = s_tdata_reg; + s_tkeep_next = s_tkeep_reg; + + m_axis_ptp_ts_next = m_axis_ptp_ts_reg; + m_axis_ptp_ts_tag_next = m_axis_ptp_ts_tag_reg; + m_axis_ptp_ts_valid_next = 1'b0; + m_axis_ptp_ts_valid_int_next = 1'b0; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + start_packet_next = 2'b00; + error_underflow_next = 1'b0; + + if (m_axis_ptp_ts_valid_int_reg) begin + m_axis_ptp_ts_valid_next = 1'b1; + if (PTP_TS_WIDTH == 96 && $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000) > 0) begin + // ns field rollover + m_axis_ptp_ts_next[45:16] = $signed({1'b0, m_axis_ptp_ts_reg[45:16]}) - $signed(31'd1000000000); + m_axis_ptp_ts_next[95:48] = m_axis_ptp_ts_reg[95:48] + 1; + end + end + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + frame_ptr_next = 16'd8; + reset_crc = 1'b1; + s_axis_tready_next = 1'b1; + + // XGMII idle + xgmii_txd_next = {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_next = {CTRL_WIDTH{1'b1}}; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + // XGMII start and preamble + if (ifg_count_reg > 8'd0) begin + // need to send more idles - swap lanes + swap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (((PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS) * 3) >> 1); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b10; + end else begin + // no more idles - unswap + unswap_lanes = 1'b1; + if (PTP_TS_WIDTH == 96) begin + m_axis_ptp_ts_next[45:0] = ptp_ts[45:0] + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + m_axis_ptp_ts_next[95:48] = ptp_ts[95:48]; + end else begin + m_axis_ptp_ts_next = ptp_ts + (PTP_PERIOD_NS * 2**16 + PTP_PERIOD_FNS); + end + m_axis_ptp_ts_tag_next = s_axis_tuser >> 1; + m_axis_ptp_ts_valid_int_next = 1'b1; + start_packet_next = 2'b01; + end + xgmii_txd_next = {ETH_SFD, {6{ETH_PRE}}, XGMII_START}; + xgmii_txc_next = 8'b00000001; + s_axis_tready_next = 1'b1; + state_next = STATE_PAYLOAD; + end else begin + ifg_count_next = 8'd0; + deficit_idle_count_next = 2'd0; + unswap_lanes = 1'b1; + state_next = STATE_IDLE; + end + end + STATE_PAYLOAD: begin + // transfer payload + update_crc = 1'b1; + s_axis_tready_next = 1'b1; + + frame_ptr_next = frame_ptr_reg + 16'd8; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = 8'b00000000; + + s_tdata_next = s_axis_tdata_masked; + s_tkeep_next = s_axis_tkeep; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + frame_ptr_next = frame_ptr_reg + keep2count(s_axis_tkeep); + s_axis_tready_next = 1'b0; + if (s_axis_tuser[0]) begin + xgmii_txd_next = {{3{XGMII_IDLE}}, XGMII_TERM, {4{XGMII_ERROR}}}; + xgmii_txc_next = 8'b11111111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b0; + + if (ENABLE_PADDING && (frame_ptr_reg < MIN_FL_NOCRC_MS || (frame_ptr_reg == MIN_FL_NOCRC_MS && keep2count(s_axis_tkeep) < MIN_FL_NOCRC_LS))) begin + s_tkeep_next = 8'hff; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end else begin + state_next = STATE_FCS_1; + end + end + end else begin + state_next = STATE_PAYLOAD; + end + end else begin + // tvalid deassert, fail frame + xgmii_txd_next = {{3{XGMII_IDLE}}, XGMII_TERM, {4{XGMII_ERROR}}}; + xgmii_txc_next = 8'b11111111; + frame_ptr_next = 16'd0; + ifg_count_next = 8'd8; + error_underflow_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + STATE_PAD: begin + // pad frame to MIN_FRAME_LENGTH + s_axis_tready_next = 1'b0; + + xgmii_txd_next = s_tdata_reg; + xgmii_txc_next = {CTRL_WIDTH{1'b0}}; + + s_tdata_next = 64'd0; + s_tkeep_next = 8'hff; + + update_crc = 1'b1; + frame_ptr_next = frame_ptr_reg + 16'd8; + + if (frame_ptr_reg < (MIN_FL_NOCRC_LS > 0 ? MIN_FL_NOCRC_MS : MIN_FL_NOCRC_MS-8)) begin + state_next = STATE_PAD; + end else begin + s_tkeep_next = 8'hff >> ((8-MIN_FL_NOCRC_LS) % 8); + + state_next = STATE_FCS_1; + end + end + STATE_FCS_1: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_0; + xgmii_txc_next = fcs_output_txc_0; + + frame_ptr_next = 16'd0; + + ifg_count_next = (ifg_delay > 8'd12 ? ifg_delay : 8'd12) - ifg_offset + (lanes_swapped ? 8'd4 : 8'd0) + deficit_idle_count_reg; + if (extra_cycle) begin + state_next = STATE_FCS_2; + end else begin + state_next = STATE_IFG; + end + end + STATE_FCS_2: begin + // last cycle + s_axis_tready_next = 1'b0; + + xgmii_txd_next = fcs_output_txd_1; + xgmii_txc_next = fcs_output_txc_1; + + reset_crc = 1'b1; + frame_ptr_next = 16'd0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_IFG: begin + // send IFG + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end + STATE_WAIT_END: begin + // wait for end of frame + s_axis_tready_next = 1'b1; + + if (ifg_count_reg > 8'd8) begin + ifg_count_next = ifg_count_reg - 8'd8; + end else begin + ifg_count_next = 8'd0; + end + + reset_crc = 1'b1; + + if (s_axis_tvalid) begin + if (s_axis_tlast) begin + s_axis_tready_next = 1'b0; + + if (ENABLE_DIC) begin + if (ifg_count_next > 8'd7) begin + state_next = STATE_IFG; + end else begin + if (ifg_count_next >= 8'd4) begin + deficit_idle_count_next = ifg_count_next - 8'd4; + end else begin + deficit_idle_count_next = ifg_count_next; + ifg_count_next = 8'd0; + end + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end else begin + if (ifg_count_next > 8'd4) begin + state_next = STATE_IFG; + end else begin + s_axis_tready_next = 1'b1; + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + + frame_ptr_reg <= 16'd0; + + ifg_count_reg <= 8'd0; + deficit_idle_count_reg <= 2'd0; + + s_axis_tready_reg <= 1'b0; + + m_axis_ptp_ts_valid_reg <= 1'b0; + m_axis_ptp_ts_valid_int_reg <= 1'b0; + + xgmii_txd_reg <= {CTRL_WIDTH{XGMII_IDLE}}; + xgmii_txc_reg <= {CTRL_WIDTH{1'b1}}; + + start_packet_reg <= 2'b00; + error_underflow_reg <= 1'b0; + + crc_state <= 32'hFFFFFFFF; + + lanes_swapped <= 1'b0; + end else begin + state_reg <= state_next; + + frame_ptr_reg <= frame_ptr_next; + + ifg_count_reg <= ifg_count_next; + deficit_idle_count_reg <= deficit_idle_count_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_axis_ptp_ts_valid_reg <= m_axis_ptp_ts_valid_next; + m_axis_ptp_ts_valid_int_reg <= m_axis_ptp_ts_valid_int_next; + + start_packet_reg <= start_packet_next; + error_underflow_reg <= error_underflow_next; + + if (swap_lanes || (lanes_swapped && !unswap_lanes)) begin + lanes_swapped <= 1'b1; + xgmii_txd_reg <= {xgmii_txd_next[31:0], swap_txd}; + xgmii_txc_reg <= {xgmii_txc_next[3:0], swap_txc}; + end else begin + lanes_swapped <= 1'b0; + xgmii_txd_reg <= xgmii_txd_next; + xgmii_txc_reg <= xgmii_txc_next; + end + + // datapath + if (reset_crc) begin + crc_state <= 32'hFFFFFFFF; + end else if (update_crc) begin + crc_state <= crc_next7; + end + end + + s_tdata_reg <= s_tdata_next; + s_tkeep_reg <= s_tkeep_next; + + m_axis_ptp_ts_reg <= m_axis_ptp_ts_next; + m_axis_ptp_ts_tag_reg <= m_axis_ptp_ts_tag_next; + + swap_txd <= xgmii_txd_next[63:32]; + swap_txc <= xgmii_txc_next[7:4]; +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_arb_mux.v b/corundum/lib/eth/rtl/eth_arb_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..8e5784f3935be376d446f0d39aaf20a1128a8677 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_arb_mux.v @@ -0,0 +1,310 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ethernet arbitrated multiplexer + */ +module eth_arb_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame inputs + */ + input wire [S_COUNT-1:0] s_eth_hdr_valid, + output wire [S_COUNT-1:0] s_eth_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_eth_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_eth_payload_axis_tready, + input wire [S_COUNT-1:0] s_eth_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_eth_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg frame_reg = 1'b0, frame_next; + +reg s_eth_hdr_ready_mask_reg = 1'b0, s_eth_hdr_ready_mask_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = (!s_eth_hdr_ready_mask_reg && grant_valid) << grant_encoded; + +assign s_eth_payload_axis_tready = (m_eth_payload_axis_tready_int_reg && grant_valid) << grant_encoded; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_eth_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_eth_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_eth_payload_axis_tvalid[grant_encoded]; +wire current_s_tready = s_eth_payload_axis_tready[grant_encoded]; +wire current_s_tlast = s_eth_payload_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_eth_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_eth_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_eth_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_eth_hdr_valid & ~grant; +assign acknowledge = grant & s_eth_payload_axis_tvalid & s_eth_payload_axis_tready & s_eth_payload_axis_tlast; + +always @* begin + frame_next = frame_reg; + + s_eth_hdr_ready_mask_next = s_eth_hdr_ready_mask_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + + if (s_eth_payload_axis_tvalid[grant_encoded] && s_eth_payload_axis_tready[grant_encoded]) begin + // end of frame detection + if (s_eth_payload_axis_tlast[grant_encoded]) begin + frame_next = 1'b0; + s_eth_hdr_ready_mask_next = 1'b0; + end + end + + if (!frame_reg && grant_valid) begin + // start of frame + frame_next = 1'b1; + + s_eth_hdr_ready_mask_next = 1'b1; + + m_eth_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48]; + m_eth_type_next = s_eth_type[grant_encoded*16 +: 16]; + end + + // pass through selected packet data + m_eth_payload_axis_tdata_int = current_s_tdata; + m_eth_payload_axis_tkeep_int = current_s_tkeep; + m_eth_payload_axis_tvalid_int = current_s_tvalid && m_eth_payload_axis_tready_int_reg && grant_valid; + m_eth_payload_axis_tlast_int = current_s_tlast; + m_eth_payload_axis_tid_int = current_s_tid; + m_eth_payload_axis_tdest_int = current_s_tdest; + m_eth_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + frame_reg <= 1'b0; + s_eth_hdr_ready_mask_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + end else begin + frame_reg <= frame_next; + s_eth_hdr_ready_mask_reg <= s_eth_hdr_ready_mask_next; + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tid = ID_ENABLE ? m_eth_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_eth_payload_axis_tdest = DEST_ENABLE ? m_eth_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_eth_payload_axis_tuser = USER_ENABLE ? m_eth_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg; + m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_axis_rx.v b/corundum/lib/eth/rtl/eth_axis_rx.v new file mode 100644 index 0000000000000000000000000000000000000000..e0a20e431e5e00e551e199102802cef373bfec60 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_axis_rx.v @@ -0,0 +1,397 @@ +/* + +Copyright (c) 2014-2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream ethernet frame receiver (AXI in, Ethernet frame out) + */ +module eth_axis_rx # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire s_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination +); + +parameter CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +parameter OFFSET = 14 % KEEP_WIDTH; + +// bus width assertions +initial begin + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +/* + +Ethernet frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype 2 octets + +This module receives an Ethernet frame on an AXI stream interface, decodes +and strips the headers, then produces the header fields in parallel along +with the payload in a separate AXI stream. + +*/ + +reg read_eth_header_reg = 1'b1, read_eth_header_next; +reg read_eth_payload_reg = 1'b0, read_eth_payload_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg flush_save; +reg transfer_in_save; + +reg s_axis_tready_reg = 1'b0, s_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; + +reg [DATA_WIDTH-1:0] save_axis_tdata_reg = 64'd0; +reg [KEEP_WIDTH-1:0] save_axis_tkeep_reg = 8'd0; +reg save_axis_tlast_reg = 1'b0; +reg save_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] shift_axis_tdata; +reg [KEEP_WIDTH-1:0] shift_axis_tkeep; +reg shift_axis_tvalid; +reg shift_axis_tlast; +reg shift_axis_tuser; +reg shift_axis_input_tready; +reg shift_axis_extra_cycle_reg = 1'b0; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_axis_tready = s_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; + +always @* begin + if (OFFSET == 0) begin + // passthrough if no overlap + shift_axis_tdata = s_axis_tdata; + shift_axis_tkeep = s_axis_tkeep; + shift_axis_tvalid = s_axis_tvalid; + shift_axis_tlast = s_axis_tlast; + shift_axis_tuser = s_axis_tuser; + shift_axis_input_tready = 1'b1; + end else if (shift_axis_extra_cycle_reg) begin + shift_axis_tdata = {s_axis_tdata, save_axis_tdata_reg} >> (OFFSET*8); + shift_axis_tkeep = {{KEEP_WIDTH{1'b0}}, save_axis_tkeep_reg} >> OFFSET; + shift_axis_tvalid = 1'b1; + shift_axis_tlast = save_axis_tlast_reg; + shift_axis_tuser = save_axis_tuser_reg; + shift_axis_input_tready = flush_save; + end else begin + shift_axis_tdata = {s_axis_tdata, save_axis_tdata_reg} >> (OFFSET*8); + shift_axis_tkeep = {s_axis_tkeep, save_axis_tkeep_reg} >> OFFSET; + shift_axis_tvalid = s_axis_tvalid; + shift_axis_tlast = (s_axis_tlast && ((s_axis_tkeep & ({KEEP_WIDTH{1'b1}} << OFFSET)) == 0)); + shift_axis_tuser = (s_axis_tuser && ((s_axis_tkeep & ({KEEP_WIDTH{1'b1}} << OFFSET)) == 0)); + shift_axis_input_tready = !(s_axis_tlast && s_axis_tready && s_axis_tvalid); + end +end + +always @* begin + read_eth_header_next = read_eth_header_reg; + read_eth_payload_next = read_eth_payload_reg; + ptr_next = ptr_reg; + + s_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_axis_input_tready && (!m_eth_hdr_valid || m_eth_hdr_ready); + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + + error_header_early_termination_next = 1'b0; + + m_eth_payload_axis_tdata_int = shift_axis_tdata; + m_eth_payload_axis_tkeep_int = shift_axis_tkeep; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = shift_axis_tlast; + m_eth_payload_axis_tuser_int = shift_axis_tuser; + + if ((s_axis_tready && s_axis_tvalid) || (m_eth_payload_axis_tready_int_reg && shift_axis_extra_cycle_reg)) begin + transfer_in_save = 1'b1; + + if (read_eth_header_reg) begin + // word transfer in - store it + ptr_next = ptr_reg + 1; + + `define _HEADER_FIELD_(offset, field) \ + if (ptr_reg == offset/KEEP_WIDTH && (!KEEP_ENABLE || s_axis_tkeep[offset%KEEP_WIDTH])) begin \ + field = s_axis_tdata[(offset%KEEP_WIDTH)*8 +: 8]; \ + end + + `_HEADER_FIELD_(0, m_eth_dest_mac_next[5*8 +: 8]) + `_HEADER_FIELD_(1, m_eth_dest_mac_next[4*8 +: 8]) + `_HEADER_FIELD_(2, m_eth_dest_mac_next[3*8 +: 8]) + `_HEADER_FIELD_(3, m_eth_dest_mac_next[2*8 +: 8]) + `_HEADER_FIELD_(4, m_eth_dest_mac_next[1*8 +: 8]) + `_HEADER_FIELD_(5, m_eth_dest_mac_next[0*8 +: 8]) + `_HEADER_FIELD_(6, m_eth_src_mac_next[5*8 +: 8]) + `_HEADER_FIELD_(7, m_eth_src_mac_next[4*8 +: 8]) + `_HEADER_FIELD_(8, m_eth_src_mac_next[3*8 +: 8]) + `_HEADER_FIELD_(9, m_eth_src_mac_next[2*8 +: 8]) + `_HEADER_FIELD_(10, m_eth_src_mac_next[1*8 +: 8]) + `_HEADER_FIELD_(11, m_eth_src_mac_next[0*8 +: 8]) + `_HEADER_FIELD_(12, m_eth_type_next[1*8 +: 8]) + `_HEADER_FIELD_(13, m_eth_type_next[0*8 +: 8]) + + if (ptr_reg == 13/KEEP_WIDTH && (!KEEP_ENABLE || s_axis_tkeep[13%KEEP_WIDTH])) begin + if (!shift_axis_tlast) begin + m_eth_hdr_valid_next = 1'b1; + read_eth_header_next = 1'b0; + read_eth_payload_next = 1'b1; + end + end + + `undef _HEADER_FIELD_ + end + + if (read_eth_payload_reg) begin + // transfer payload + m_eth_payload_axis_tdata_int = shift_axis_tdata; + m_eth_payload_axis_tkeep_int = shift_axis_tkeep; + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tlast_int = shift_axis_tlast; + m_eth_payload_axis_tuser_int = shift_axis_tuser; + end + + if (shift_axis_tlast) begin + if (read_eth_header_next) begin + // don't have the whole header + error_header_early_termination_next = 1'b1; + end + + flush_save = 1'b1; + ptr_next = 1'b0; + read_eth_header_next = 1'b1; + read_eth_payload_next = 1'b0; + end + end +end + +always @(posedge clk) begin + read_eth_header_reg <= read_eth_header_next; + read_eth_payload_reg <= read_eth_payload_next; + ptr_reg <= ptr_next; + + s_axis_tready_reg <= s_axis_tready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + + busy_reg <= (read_eth_payload_next || ptr_next != 0); + + if (transfer_in_save) begin + save_axis_tdata_reg <= s_axis_tdata; + save_axis_tkeep_reg <= s_axis_tkeep; + save_axis_tuser_reg <= s_axis_tuser; + end + + if (flush_save) begin + save_axis_tlast_reg <= 1'b0; + shift_axis_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_axis_tlast_reg <= s_axis_tlast; + shift_axis_extra_cycle_reg <= OFFSET ? s_axis_tlast && ((s_axis_tkeep & ({KEEP_WIDTH{1'b1}} << OFFSET)) != 0) : 1'b0; + end + + if (rst) begin + read_eth_header_reg <= 1'b1; + read_eth_payload_reg <= 1'b0; + ptr_reg <= 0; + s_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + save_axis_tlast_reg <= 1'b0; + shift_axis_extra_cycle_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_axis_tx.v b/corundum/lib/eth/rtl/eth_axis_tx.v new file mode 100644 index 0000000000000000000000000000000000000000..5d582c42e0993b9e05cc385cdc31d723a28e1c5f --- /dev/null +++ b/corundum/lib/eth/rtl/eth_axis_tx.v @@ -0,0 +1,400 @@ +/* + +Copyright (c) 2014-2020 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream ethernet frame transmitter (Ethernet frame in, AXI out) + */ +module eth_axis_tx # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + // If disabled, tkeep assumed to be 1'b1 + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire m_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +parameter CYCLE_COUNT = (14+KEEP_WIDTH-1)/KEEP_WIDTH; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +parameter OFFSET = 14 % KEEP_WIDTH; + +// bus width assertions +initial begin + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +/* + +Ethernet frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype 2 octets + +This module receives an Ethernet frame with header fields in parallel along +with the payload in an AXI stream, combines the header with the payload, and +transmits the complete Ethernet frame on the output AXI stream interface. + +*/ + +// datapath control signals +reg store_eth_hdr; + +reg send_eth_header_reg = 1'b0, send_eth_header_next; +reg send_eth_payload_reg = 1'b0, send_eth_payload_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg flush_save; +reg transfer_in_save; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +reg [DATA_WIDTH-1:0] save_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] save_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg save_eth_payload_axis_tlast_reg = 1'b0; +reg save_eth_payload_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] shift_eth_payload_axis_tdata; +reg [KEEP_WIDTH-1:0] shift_eth_payload_axis_tkeep; +reg shift_eth_payload_axis_tvalid; +reg shift_eth_payload_axis_tlast; +reg shift_eth_payload_axis_tuser; +reg shift_eth_payload_axis_input_tready; +reg shift_eth_payload_axis_extra_cycle_reg = 1'b0; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign busy = busy_reg; + +always @* begin + if (OFFSET == 0) begin + // passthrough if no overlap + shift_eth_payload_axis_tdata = s_eth_payload_axis_tdata; + shift_eth_payload_axis_tkeep = s_eth_payload_axis_tkeep; + shift_eth_payload_axis_tvalid = s_eth_payload_axis_tvalid; + shift_eth_payload_axis_tlast = s_eth_payload_axis_tlast; + shift_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + shift_eth_payload_axis_input_tready = 1'b1; + end else if (shift_eth_payload_axis_extra_cycle_reg) begin + shift_eth_payload_axis_tdata = {s_eth_payload_axis_tdata, save_eth_payload_axis_tdata_reg} >> ((KEEP_WIDTH-OFFSET)*8); + shift_eth_payload_axis_tkeep = {{KEEP_WIDTH{1'b0}}, save_eth_payload_axis_tkeep_reg} >> (KEEP_WIDTH-OFFSET); + shift_eth_payload_axis_tvalid = 1'b1; + shift_eth_payload_axis_tlast = save_eth_payload_axis_tlast_reg; + shift_eth_payload_axis_tuser = save_eth_payload_axis_tuser_reg; + shift_eth_payload_axis_input_tready = flush_save; + end else begin + shift_eth_payload_axis_tdata = {s_eth_payload_axis_tdata, save_eth_payload_axis_tdata_reg} >> ((KEEP_WIDTH-OFFSET)*8); + shift_eth_payload_axis_tkeep = {s_eth_payload_axis_tkeep, save_eth_payload_axis_tkeep_reg} >> (KEEP_WIDTH-OFFSET); + shift_eth_payload_axis_tvalid = s_eth_payload_axis_tvalid; + shift_eth_payload_axis_tlast = (s_eth_payload_axis_tlast && ((s_eth_payload_axis_tkeep & ({KEEP_WIDTH{1'b1}} << (KEEP_WIDTH-OFFSET))) == 0)); + shift_eth_payload_axis_tuser = (s_eth_payload_axis_tuser && ((s_eth_payload_axis_tkeep & ({KEEP_WIDTH{1'b1}} << (KEEP_WIDTH-OFFSET))) == 0)); + shift_eth_payload_axis_input_tready = !(s_eth_payload_axis_tlast && s_eth_payload_axis_tready && s_eth_payload_axis_tvalid); + end +end + +always @* begin + send_eth_header_next = send_eth_header_reg; + send_eth_payload_next = send_eth_payload_reg; + ptr_next = ptr_reg; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + m_axis_tdata_int = {DATA_WIDTH{1'b0}}; + m_axis_tkeep_int = {KEEP_WIDTH{1'b0}}; + m_axis_tvalid_int = 1'b0; + m_axis_tlast_int = 1'b0; + m_axis_tuser_int = 1'b0; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + store_eth_hdr = 1'b1; + ptr_next = 0; + send_eth_header_next = 1'b1; + send_eth_payload_next = (OFFSET != 0) && (CYCLE_COUNT == 1); + s_eth_payload_axis_tready_next = send_eth_payload_next && m_axis_tready_int_early; + end + + if (send_eth_payload_reg) begin + s_eth_payload_axis_tready_next = m_axis_tready_int_early && shift_eth_payload_axis_input_tready; + + if ((s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) || (m_axis_tready_int_reg && shift_eth_payload_axis_extra_cycle_reg)) begin + transfer_in_save = 1'b1; + + m_axis_tdata_int = shift_eth_payload_axis_tdata; + m_axis_tkeep_int = shift_eth_payload_axis_tkeep; + m_axis_tvalid_int = 1'b1; + m_axis_tlast_int = shift_eth_payload_axis_tlast; + m_axis_tuser_int = shift_eth_payload_axis_tuser; + + if (shift_eth_payload_axis_tlast) begin + flush_save = 1'b1; + s_eth_payload_axis_tready_next = 1'b0; + ptr_next = 0; + send_eth_payload_next = 1'b0; + end + end + end + + if (m_axis_tready_int_reg && (!OFFSET || !send_eth_payload_reg || m_axis_tvalid_int)) begin + if (send_eth_header_reg) begin + ptr_next = ptr_reg + 1; + + if ((OFFSET != 0) && (CYCLE_COUNT == 1 || ptr_next == CYCLE_COUNT-1) && !send_eth_payload_reg) begin + send_eth_payload_next = 1'b1; + s_eth_payload_axis_tready_next = m_axis_tready_int_early && shift_eth_payload_axis_input_tready; + end + + m_axis_tvalid_int = 1'b1; + + `define _HEADER_FIELD_(offset, field) \ + if (ptr_reg == offset/KEEP_WIDTH) begin \ + m_axis_tdata_int[(offset%KEEP_WIDTH)*8 +: 8] = field; \ + m_axis_tkeep_int[offset%KEEP_WIDTH] = 1'b1; \ + end + + `_HEADER_FIELD_(0, eth_dest_mac_reg[5*8 +: 8]) + `_HEADER_FIELD_(1, eth_dest_mac_reg[4*8 +: 8]) + `_HEADER_FIELD_(2, eth_dest_mac_reg[3*8 +: 8]) + `_HEADER_FIELD_(3, eth_dest_mac_reg[2*8 +: 8]) + `_HEADER_FIELD_(4, eth_dest_mac_reg[1*8 +: 8]) + `_HEADER_FIELD_(5, eth_dest_mac_reg[0*8 +: 8]) + `_HEADER_FIELD_(6, eth_src_mac_reg[5*8 +: 8]) + `_HEADER_FIELD_(7, eth_src_mac_reg[4*8 +: 8]) + `_HEADER_FIELD_(8, eth_src_mac_reg[3*8 +: 8]) + `_HEADER_FIELD_(9, eth_src_mac_reg[2*8 +: 8]) + `_HEADER_FIELD_(10, eth_src_mac_reg[1*8 +: 8]) + `_HEADER_FIELD_(11, eth_src_mac_reg[0*8 +: 8]) + `_HEADER_FIELD_(12, eth_type_reg[1*8 +: 8]) + `_HEADER_FIELD_(13, eth_type_reg[0*8 +: 8]) + + if (ptr_reg == 13/KEEP_WIDTH) begin + if (!send_eth_payload_reg) begin + s_eth_payload_axis_tready_next = m_axis_tready_int_early; + send_eth_payload_next = 1'b1; + end + send_eth_header_next = 1'b0; + end + + `undef _HEADER_FIELD_ + end + end + + s_eth_hdr_ready_next = !(send_eth_header_next || send_eth_payload_next); +end + +always @(posedge clk) begin + send_eth_header_reg <= send_eth_header_next; + send_eth_payload_reg <= send_eth_payload_next; + ptr_reg <= ptr_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + busy_reg <= send_eth_header_next || send_eth_payload_next; + + if (store_eth_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + end + + if (transfer_in_save) begin + save_eth_payload_axis_tdata_reg <= s_eth_payload_axis_tdata; + save_eth_payload_axis_tkeep_reg <= s_eth_payload_axis_tkeep; + save_eth_payload_axis_tuser_reg <= s_eth_payload_axis_tuser; + end + + if (flush_save) begin + save_eth_payload_axis_tlast_reg <= 1'b0; + shift_eth_payload_axis_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_eth_payload_axis_tlast_reg <= s_eth_payload_axis_tlast; + shift_eth_payload_axis_extra_cycle_reg <= OFFSET ? s_eth_payload_axis_tlast && ((s_eth_payload_axis_tkeep & ({KEEP_WIDTH{1'b1}} << (KEEP_WIDTH-OFFSET))) != 0) : 1'b0; + end + + if (rst) begin + send_eth_header_reg <= 1'b0; + send_eth_payload_reg <= 1'b0; + ptr_reg <= 0; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + busy_reg <= 1'b0; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg m_axis_tuser_reg = 1'b0; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg temp_m_axis_tuser_reg = 1'b0; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tuser = m_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_demux.v b/corundum/lib/eth/rtl/eth_demux.v new file mode 100644 index 0000000000000000000000000000000000000000..0433b9ef6023beb1429f72d0ce2a43b6ca13c392 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_demux.v @@ -0,0 +1,305 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ethernet demultiplexer + */ +module eth_demux # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire [ID_WIDTH-1:0] s_eth_payload_axis_tid, + input wire [DEST_WIDTH-1:0] s_eth_payload_axis_tdest, + input wire [USER_WIDTH-1:0] s_eth_payload_axis_tuser, + + /* + * Ethernet frame outputs + */ + output wire [M_COUNT-1:0] m_eth_hdr_valid, + input wire [M_COUNT-1:0] m_eth_hdr_ready, + output wire [M_COUNT*48-1:0] m_eth_dest_mac, + output wire [M_COUNT*48-1:0] m_eth_src_mac, + output wire [M_COUNT*16-1:0] m_eth_type, + output wire [M_COUNT*DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire [M_COUNT-1:0] m_eth_payload_axis_tvalid, + input wire [M_COUNT-1:0] m_eth_payload_axis_tready, + output wire [M_COUNT-1:0] m_eth_payload_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_eth_payload_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_eth_payload_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_eth_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; + +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg [M_COUNT-1:0] m_eth_hdr_valid_reg = 0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg [M_COUNT-1:0] m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg && enable; + +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg && enable; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = {M_COUNT{m_eth_dest_mac_reg}}; +assign m_eth_src_mac = {M_COUNT{m_eth_src_mac_reg}}; +assign m_eth_type = {M_COUNT{m_eth_type_reg}}; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_eth_hdr_ready_next = 1'b0; + + s_eth_payload_axis_tready_next = 1'b0; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg & ~m_eth_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + + if (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready) begin + // end of frame detection + if (s_eth_payload_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_eth_hdr_valid && s_eth_hdr_ready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = frame_ctl; + + s_eth_hdr_ready_next = 1'b0; + + m_eth_hdr_valid_next = (!drop_ctl) << select_ctl; + m_eth_dest_mac_next = s_eth_dest_mac; + m_eth_src_mac_next = s_eth_src_mac; + m_eth_type_next = s_eth_type; + end + + s_eth_hdr_ready_next = !frame_next && !m_eth_hdr_valid_next; + + s_eth_payload_axis_tready_next = (m_eth_payload_axis_tready_int_early || drop_ctl) && frame_ctl; + + m_eth_payload_axis_tdata_int = s_eth_payload_axis_tdata; + m_eth_payload_axis_tkeep_int = s_eth_payload_axis_tkeep; + m_eth_payload_axis_tvalid_int = (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && !drop_ctl) << select_ctl; + m_eth_payload_axis_tlast_int = s_eth_payload_axis_tlast; + m_eth_payload_axis_tid_int = s_eth_payload_axis_tid; + m_eth_payload_axis_tdest_int = s_eth_payload_axis_tdest; + m_eth_payload_axis_tuser_int = s_eth_payload_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_eth_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_eth_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = {M_COUNT{m_eth_payload_axis_tdata_reg}}; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_eth_payload_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = {M_COUNT{m_eth_payload_axis_tlast_reg}}; +assign m_eth_payload_axis_tid = ID_ENABLE ? {M_COUNT{m_eth_payload_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_eth_payload_axis_tdest = DEST_ENABLE ? {M_COUNT{m_eth_payload_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_eth_payload_axis_tuser = USER_ENABLE ? {M_COUNT{m_eth_payload_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = (m_eth_payload_axis_tready & m_eth_payload_axis_tvalid) || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if ((m_eth_payload_axis_tready & m_eth_payload_axis_tvalid) || !m_eth_payload_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready & m_eth_payload_axis_tvalid) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg; + m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_10g.v b/corundum/lib/eth/rtl/eth_mac_10g.v new file mode 100644 index 0000000000000000000000000000000000000000..187389bce241884eb6ca2e897227732d5dbef2bf --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_10g.v @@ -0,0 +1,252 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet MAC + */ +module eth_mac_10g # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter TX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter RX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_WIDTH = 96, + parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1, + parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire [TX_USER_WIDTH-1:0] tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire [RX_USER_WIDTH-1:0] rx_axis_tuser, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * PTP + */ + input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts, + output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag, + output wire tx_axis_ptp_ts_valid, + + /* + * Status + */ + output wire [1:0] tx_start_packet, + output wire tx_error_underflow, + output wire [1:0] rx_start_packet, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 32 && DATA_WIDTH != 64) begin + $error("Error: Interface width must be 32 or 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH || CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +generate + +if (DATA_WIDTH == 64) begin + +axis_xgmii_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH) +) +axis_xgmii_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .ptp_ts(rx_ptp_ts), + .start_packet(rx_start_packet), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +axis_xgmii_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH) +) +axis_xgmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .ifg_delay(ifg_delay), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow) +); + +end else begin + +axis_xgmii_rx_32 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH) +) +axis_xgmii_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .ptp_ts(rx_ptp_ts), + .start_packet(rx_start_packet[0]), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +assign rx_start_packet[1] = 1'b0; + +axis_xgmii_tx_32 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH) +) +axis_xgmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .ifg_delay(ifg_delay), + .start_packet(tx_start_packet[0]) +); + +assign tx_start_packet[1] = 1'b0; + +end + +endgenerate + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_10g_fifo.v b/corundum/lib/eth/rtl/eth_mac_10g_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..8f03e28a679d4d61cbde090dde47019ecd8d515b --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_10g_fifo.v @@ -0,0 +1,678 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet MAC with TX and RX FIFOs + */ +module eth_mac_10g_fifo # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter AXIS_DATA_WIDTH = DATA_WIDTH, + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_DEPTH = 4096, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO, + parameter LOGIC_PTP_PERIOD_NS = 4'h6, + parameter LOGIC_PTP_PERIOD_FNS = 16'h6666, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_USE_SAMPLE_CLOCK = 0, + parameter TX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_FIFO_DEPTH = 64, + parameter RX_PTP_TS_FIFO_DEPTH = 64, + parameter PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = 0, + parameter PTP_TAG_WIDTH = 16 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + input wire logic_clk, + input wire logic_rst, + input wire ptp_sample_clk, + + /* + * AXI input + */ + input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * Transmit timestamp tag input + */ + input wire [PTP_TAG_WIDTH-1:0] s_axis_tx_ptp_ts_tag, + input wire s_axis_tx_ptp_ts_valid, + output wire s_axis_tx_ptp_ts_ready, + + /* + * Transmit timestamp output + */ + output wire [PTP_TS_WIDTH-1:0] m_axis_tx_ptp_ts_96, + output wire [PTP_TAG_WIDTH-1:0] m_axis_tx_ptp_ts_tag, + output wire m_axis_tx_ptp_ts_valid, + input wire m_axis_tx_ptp_ts_ready, + + /* + * AXI output + */ + output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * Receive timestamp output + */ + output wire [PTP_TS_WIDTH-1:0] m_axis_rx_ptp_ts_96, + output wire m_axis_rx_ptp_ts_valid, + input wire m_axis_rx_ptp_ts_ready, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_rxd, + input wire [CTRL_WIDTH-1:0] xgmii_rxc, + output wire [DATA_WIDTH-1:0] xgmii_txd, + output wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * PTP clock + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts_96, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +parameter KEEP_WIDTH = DATA_WIDTH/8; + +localparam TX_USER_WIDTH = (TX_PTP_TS_ENABLE && TX_PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1; +localparam RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1; + +wire [DATA_WIDTH-1:0] tx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] tx_fifo_axis_tkeep; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire [TX_USER_WIDTH-1:0] tx_fifo_axis_tuser; + +wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata_int; +wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep_int; +wire tx_axis_tvalid_int; +wire tx_axis_tready_int; +wire tx_axis_tlast_int; +wire [TX_USER_WIDTH-1:0] tx_axis_tuser_int; + +wire [DATA_WIDTH-1:0] rx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_fifo_axis_tkeep; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire [RX_USER_WIDTH-1:0] rx_fifo_axis_tuser; + +wire [RX_USER_WIDTH-1:0] rx_axis_tuser_int; + +wire [PTP_TS_WIDTH-1:0] tx_ptp_ts_96; +wire [PTP_TS_WIDTH-1:0] rx_ptp_ts_96; + +wire [PTP_TS_WIDTH-1:0] tx_axis_ptp_ts_96; +wire [PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag; +wire tx_axis_ptp_ts_valid; + +wire [PTP_TS_WIDTH-1:0] rx_axis_ptp_ts_96; +wire rx_axis_ptp_ts_valid; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +// PTP timestamping +generate + +if (TX_PTP_TS_ENABLE) begin + + ptp_clock_cdc #( + .TS_WIDTH(PTP_TS_WIDTH), + .NS_WIDTH(4), + .FNS_WIDTH(16), + .INPUT_PERIOD_NS(LOGIC_PTP_PERIOD_NS), + .INPUT_PERIOD_FNS(LOGIC_PTP_PERIOD_FNS), + .OUTPUT_PERIOD_NS(PTP_PERIOD_NS), + .OUTPUT_PERIOD_FNS(PTP_PERIOD_FNS), + .USE_SAMPLE_CLOCK(PTP_USE_SAMPLE_CLOCK) + ) + tx_ptp_cdc ( + .input_clk(logic_clk), + .input_rst(logic_rst), + .output_clk(tx_clk), + .output_rst(tx_rst), + .sample_clk(ptp_sample_clk), + .input_ts(ptp_ts_96), + .output_ts(tx_ptp_ts_96), + .output_ts_step(), + .output_pps() + ); + + if (TX_PTP_TAG_ENABLE) begin + + ptp_tag_insert #( + .DATA_WIDTH(AXIS_DATA_WIDTH), + .KEEP_WIDTH(AXIS_KEEP_WIDTH), + .TAG_WIDTH(PTP_TAG_WIDTH), + .TAG_OFFSET(1), + .USER_WIDTH(TX_USER_WIDTH) + ) + tx_ptp_tag_insert ( + .clk(logic_clk), + .rst(logic_rst), + + // AXI stream input + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + + // AXI stream input + .m_axis_tdata(tx_axis_tdata_int), + .m_axis_tkeep(tx_axis_tkeep_int), + .m_axis_tvalid(tx_axis_tvalid_int), + .m_axis_tready(tx_axis_tready_int), + .m_axis_tlast(tx_axis_tlast_int), + .m_axis_tuser(tx_axis_tuser_int), + + // Tag input + .s_axis_tag(s_axis_tx_ptp_ts_tag), + .s_axis_tag_valid(s_axis_tx_ptp_ts_valid), + .s_axis_tag_ready(s_axis_tx_ptp_ts_ready) + ); + + axis_async_fifo #( + .DEPTH(TX_PTP_TS_FIFO_DEPTH), + .DATA_WIDTH(PTP_TAG_WIDTH+PTP_TS_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + tx_ptp_ts_fifo ( + .async_rst(logic_rst | tx_rst), + + // AXI input + .s_clk(tx_clk), + .s_axis_tdata({tx_axis_ptp_ts_tag, tx_axis_ptp_ts_96}), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_ptp_ts_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_clk(logic_clk), + .m_axis_tdata({m_axis_tx_ptp_ts_tag, m_axis_tx_ptp_ts_96}), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_tx_ptp_ts_valid), + .m_axis_tready(m_axis_tx_ptp_ts_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() + ); + + end else begin + + assign tx_axis_tdata_int = tx_axis_tdata; + assign tx_axis_tkeep_int = tx_axis_tkeep; + assign tx_axis_tvalid_int = tx_axis_tvalid; + assign tx_axis_tready = tx_axis_tready_int; + assign tx_axis_tlast_int = tx_axis_tlast; + assign tx_axis_tuser_int = tx_axis_tuser; + + axis_async_fifo #( + .DEPTH(TX_PTP_TS_FIFO_DEPTH), + .DATA_WIDTH(PTP_TS_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + tx_ptp_ts_fifo ( + .async_rst(logic_rst | tx_rst), + + // AXI input + .s_clk(tx_clk), + .s_axis_tdata(tx_axis_ptp_ts_96), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_ptp_ts_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(m_axis_tx_ptp_ts_96), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_tx_ptp_ts_valid), + .m_axis_tready(m_axis_tx_ptp_ts_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() + ); + + assign s_axis_tx_ptp_ts_ready = 1'b0; + assign m_axis_tx_ptp_ts_tag = {PTP_TAG_WIDTH{1'b0}}; + + end + +end else begin + + assign s_axis_tx_ptp_ts_ready = 1'b0; + + assign m_axis_tx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + assign m_axis_tx_ptp_ts_tag = {PTP_TAG_WIDTH{1'b0}}; + assign m_axis_tx_ptp_ts_valid = 1'b0; + + assign tx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + + assign tx_axis_tdata_int = tx_axis_tdata; + assign tx_axis_tkeep_int = tx_axis_tkeep; + assign tx_axis_tvalid_int = tx_axis_tvalid; + assign tx_axis_tready = tx_axis_tready_int; + assign tx_axis_tlast_int = tx_axis_tlast; + assign tx_axis_tuser_int = tx_axis_tuser; + +end + +if (RX_PTP_TS_ENABLE) begin + + ptp_clock_cdc #( + .TS_WIDTH(PTP_TS_WIDTH), + .NS_WIDTH(4), + .FNS_WIDTH(16), + .INPUT_PERIOD_NS(LOGIC_PTP_PERIOD_NS), + .INPUT_PERIOD_FNS(LOGIC_PTP_PERIOD_FNS), + .OUTPUT_PERIOD_NS(PTP_PERIOD_NS), + .OUTPUT_PERIOD_FNS(PTP_PERIOD_FNS), + .USE_SAMPLE_CLOCK(PTP_USE_SAMPLE_CLOCK) + ) + rx_ptp_cdc ( + .input_clk(logic_clk), + .input_rst(logic_rst), + .output_clk(rx_clk), + .output_rst(rx_rst), + .sample_clk(ptp_sample_clk), + .input_ts(ptp_ts_96), + .output_ts(rx_ptp_ts_96), + .output_ts_step(), + .output_pps() + ); + + axis_fifo #( + .DEPTH(RX_PTP_TS_FIFO_DEPTH), + .DATA_WIDTH(PTP_TS_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + rx_ptp_ts_fifo ( + .clk(logic_clk), + .rst(logic_rst), + + // AXI input + .s_axis_tdata(rx_axis_ptp_ts_96), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_axis_ptp_ts_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata(m_axis_rx_ptp_ts_96), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_rx_ptp_ts_valid), + .m_axis_tready(m_axis_rx_ptp_ts_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() + ); + + ptp_ts_extract #( + .TS_WIDTH(PTP_TS_WIDTH), + .TS_OFFSET(1), + .USER_WIDTH(RX_USER_WIDTH) + ) + rx_ptp_ts_extract ( + .clk(logic_clk), + .rst(logic_rst), + + // AXI stream input + .s_axis_tvalid(rx_axis_tvalid && rx_axis_tready), + .s_axis_tlast(rx_axis_tlast), + .s_axis_tuser(rx_axis_tuser_int), + + // Timestamp output + .m_axis_ts(rx_axis_ptp_ts_96), + .m_axis_ts_valid(rx_axis_ptp_ts_valid) + ); + +end else begin + + assign m_axis_rx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + assign m_axis_rx_ptp_ts_valid = 1'b0; + + assign rx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + +end + +assign rx_axis_tuser = rx_axis_tuser_int[0]; + +endgenerate + +eth_mac_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .TX_PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .TX_PTP_TS_WIDTH(PTP_TS_WIDTH), + .TX_PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .TX_PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .RX_PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .RX_PTP_TS_WIDTH(PTP_TS_WIDTH), + .TX_USER_WIDTH(TX_USER_WIDTH), + .RX_USER_WIDTH(RX_USER_WIDTH) +) +eth_mac_10g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tkeep(tx_fifo_axis_tkeep), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tkeep(rx_fifo_axis_tkeep), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + + .tx_ptp_ts(tx_ptp_ts_96), + .rx_ptp_ts(rx_ptp_ts_96), + .tx_axis_ptp_ts(tx_axis_ptp_ts_96), + .tx_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .tx_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + + .ifg_delay(ifg_delay) +); + +axis_async_fifo_adapter #( + .DEPTH(TX_FIFO_DEPTH), + .S_DATA_WIDTH(AXIS_DATA_WIDTH), + .S_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .S_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .M_DATA_WIDTH(DATA_WIDTH), + .M_KEEP_ENABLE(1), + .M_KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(TX_USER_WIDTH), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // AXI input + .s_clk(logic_clk), + .s_rst(logic_rst), + .s_axis_tdata(tx_axis_tdata_int), + .s_axis_tkeep(tx_axis_tkeep_int), + .s_axis_tvalid(tx_axis_tvalid_int), + .s_axis_tready(tx_axis_tready_int), + .s_axis_tlast(tx_axis_tlast_int), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser_int), + // AXI output + .m_clk(tx_clk), + .m_rst(tx_rst), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(tx_fifo_axis_tkeep), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo_adapter #( + .DEPTH(RX_FIFO_DEPTH), + .S_DATA_WIDTH(DATA_WIDTH), + .S_KEEP_ENABLE(1), + .S_KEEP_WIDTH(KEEP_WIDTH), + .M_DATA_WIDTH(AXIS_DATA_WIDTH), + .M_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .M_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(RX_USER_WIDTH), + .FRAME_FIFO(RX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(RX_DROP_WHEN_FULL) +) +rx_fifo ( + // AXI input + .s_clk(rx_clk), + .s_rst(rx_rst), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(rx_fifo_axis_tkeep), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_rst(logic_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser_int), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_1g.v b/corundum/lib/eth/rtl/eth_mac_1g.v new file mode 100644 index 0000000000000000000000000000000000000000..4f1075c19c15269eca3e9a1abf8c8b0902851636 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_1g.v @@ -0,0 +1,167 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC + */ +module eth_mac_1g # +( + parameter DATA_WIDTH = 8, + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter RX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_WIDTH = 96, + parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1, + parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire [TX_USER_WIDTH-1:0] tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire [RX_USER_WIDTH-1:0] rx_axis_tuser, + + /* + * GMII interface + */ + input wire [DATA_WIDTH-1:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + output wire [DATA_WIDTH-1:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * PTP + */ + input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts, + output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag, + output wire tx_axis_ptp_ts_valid, + + /* + * Control + */ + input wire rx_clk_enable, + input wire tx_clk_enable, + input wire rx_mii_select, + input wire tx_mii_select, + + /* + * Status + */ + output wire tx_start_packet, + output wire tx_error_underflow, + output wire rx_start_packet, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +axis_gmii_rx #( + .DATA_WIDTH(DATA_WIDTH), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH) +) +axis_gmii_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .ptp_ts(rx_ptp_ts), + .clk_enable(rx_clk_enable), + .mii_select(rx_mii_select), + .start_packet(rx_start_packet), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs) +); + +axis_gmii_tx #( + .DATA_WIDTH(DATA_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH) +) +axis_gmii_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .clk_enable(tx_clk_enable), + .mii_select(tx_mii_select), + .ifg_delay(ifg_delay), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_1g_fifo.v b/corundum/lib/eth/rtl/eth_mac_1g_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..0110bbdd1749004a25c32567efeaf5abcdeff057 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_1g_fifo.v @@ -0,0 +1,317 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC with TX and RX FIFOs + */ +module eth_mac_1g_fifo # +( + parameter AXIS_DATA_WIDTH = 8, + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_DEPTH = 4096, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Control + */ + input wire rx_clk_enable, + input wire tx_clk_enable, + input wire rx_mii_select, + input wire tx_mii_select, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .rx_clk_enable(rx_clk_enable), + .tx_clk_enable(tx_clk_enable), + .rx_mii_select(rx_mii_select), + .tx_mii_select(tx_mii_select), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo_adapter #( + .DEPTH(TX_FIFO_DEPTH), + .S_DATA_WIDTH(AXIS_DATA_WIDTH), + .S_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .S_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .M_DATA_WIDTH(8), + .M_KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // AXI input + .s_clk(logic_clk), + .s_rst(logic_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_rst(tx_rst), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo_adapter #( + .DEPTH(RX_FIFO_DEPTH), + .S_DATA_WIDTH(8), + .S_KEEP_ENABLE(0), + .M_DATA_WIDTH(AXIS_DATA_WIDTH), + .M_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .M_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // AXI input + .s_clk(rx_clk), + .s_rst(rx_rst), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_rst(logic_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_1g_gmii.v b/corundum/lib/eth/rtl/eth_mac_1g_gmii.v new file mode 100644 index 0000000000000000000000000000000000000000..dc0927c83719bf5b1cc2111abca1ebdd37f52e2b --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_1g_gmii.v @@ -0,0 +1,248 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC with GMII interface + */ +module eth_mac_1g_gmii # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire gtx_clk, + input wire gtx_rst, + output wire rx_clk, + output wire rx_rst, + output wire tx_clk, + output wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire gmii_rx_clk, + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + input wire mii_tx_clk, + output wire gmii_tx_clk, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Status + */ + output wire tx_error_underflow, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] mac_gmii_rxd; +wire mac_gmii_rx_dv; +wire mac_gmii_rx_er; +wire [7:0] mac_gmii_txd; +wire mac_gmii_tx_en; +wire mac_gmii_tx_er; + +reg [1:0] speed_reg = 2'b10; +reg mii_select_reg = 1'b0; + +(* srl_style = "register" *) +reg [1:0] tx_mii_select_sync = 2'd0; + +always @(posedge tx_clk) begin + tx_mii_select_sync <= {tx_mii_select_sync[0], mii_select_reg}; +end + +(* srl_style = "register" *) +reg [1:0] rx_mii_select_sync = 2'd0; + +always @(posedge rx_clk) begin + rx_mii_select_sync <= {rx_mii_select_sync[0], mii_select_reg}; +end + +// PHY speed detection +reg [2:0] rx_prescale = 3'd0; + +always @(posedge rx_clk) begin + rx_prescale <= rx_prescale + 3'd1; +end + +(* srl_style = "register" *) +reg [2:0] rx_prescale_sync = 3'd0; + +always @(posedge gtx_clk) begin + rx_prescale_sync <= {rx_prescale_sync[1:0], rx_prescale[2]}; +end + +reg [6:0] rx_speed_count_1 = 0; +reg [1:0] rx_speed_count_2 = 0; + +always @(posedge gtx_clk) begin + if (gtx_rst) begin + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end else begin + rx_speed_count_1 <= rx_speed_count_1 + 1; + + if (rx_prescale_sync[1] ^ rx_prescale_sync[2]) begin + rx_speed_count_2 <= rx_speed_count_2 + 1; + end + + if (&rx_speed_count_1) begin + // reference count overflow - 10M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b00; + mii_select_reg <= 1'b1; + end + + if (&rx_speed_count_2) begin + // prescaled count overflow - 100M or 1000M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + if (rx_speed_count_1[6:5]) begin + // large reference count - 100M + speed_reg <= 2'b01; + mii_select_reg <= 1'b1; + end else begin + // small reference count - 1000M + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end + end + end +end + +assign speed = speed_reg; + +gmii_phy_if #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE) +) +gmii_phy_if_inst ( + .clk(gtx_clk), + .rst(gtx_rst), + + .mac_gmii_rx_clk(rx_clk), + .mac_gmii_rx_rst(rx_rst), + .mac_gmii_rxd(mac_gmii_rxd), + .mac_gmii_rx_dv(mac_gmii_rx_dv), + .mac_gmii_rx_er(mac_gmii_rx_er), + .mac_gmii_tx_clk(tx_clk), + .mac_gmii_tx_rst(tx_rst), + .mac_gmii_txd(mac_gmii_txd), + .mac_gmii_tx_en(mac_gmii_tx_en), + .mac_gmii_tx_er(mac_gmii_tx_er), + + .phy_gmii_rx_clk(gmii_rx_clk), + .phy_gmii_rxd(gmii_rxd), + .phy_gmii_rx_dv(gmii_rx_dv), + .phy_gmii_rx_er(gmii_rx_er), + .phy_mii_tx_clk(mii_tx_clk), + .phy_gmii_tx_clk(gmii_tx_clk), + .phy_gmii_txd(gmii_txd), + .phy_gmii_tx_en(gmii_tx_en), + .phy_gmii_tx_er(gmii_tx_er), + + .mii_select(mii_select_reg) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_gmii_rxd), + .gmii_rx_dv(mac_gmii_rx_dv), + .gmii_rx_er(mac_gmii_rx_er), + .gmii_txd(mac_gmii_txd), + .gmii_tx_en(mac_gmii_tx_en), + .gmii_tx_er(mac_gmii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(1'b1), + .rx_mii_select(rx_mii_select_sync[1]), + .tx_mii_select(tx_mii_select_sync[1]), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_1g_gmii_fifo.v b/corundum/lib/eth/rtl/eth_mac_1g_gmii_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..845aad5759fd5391ce1fe204a95e29a099f2a696 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_1g_gmii_fifo.v @@ -0,0 +1,344 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC with GMII interface and TX and RX FIFOs + */ +module eth_mac_1g_gmii_fifo # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter AXIS_DATA_WIDTH = 8, + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_DEPTH = 4096, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire gtx_clk, + input wire gtx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * GMII interface + */ + input wire gmii_rx_clk, + input wire [7:0] gmii_rxd, + input wire gmii_rx_dv, + input wire gmii_rx_er, + input wire mii_tx_clk, + output wire gmii_tx_clk, + output wire [7:0] gmii_txd, + output wire gmii_tx_en, + output wire gmii_tx_er, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire tx_clk; +wire rx_clk; +wire tx_rst; +wire rx_rst; + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +wire [1:0] speed_int; + +reg [1:0] speed_sync_reg_1 = 2'b10; +reg [1:0] speed_sync_reg_2 = 2'b10; + +assign speed = speed_sync_reg_2; + +always @(posedge logic_clk) begin + speed_sync_reg_1 <= speed_int; + speed_sync_reg_2 <= speed_sync_reg_1; +end + +eth_mac_1g_gmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_gmii_inst ( + .gtx_clk(gtx_clk), + .gtx_rst(gtx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .gmii_rx_clk(gmii_rx_clk), + .gmii_rxd(gmii_rxd), + .gmii_rx_dv(gmii_rx_dv), + .gmii_rx_er(gmii_rx_er), + .gmii_tx_clk(gmii_tx_clk), + .mii_tx_clk(mii_tx_clk), + .gmii_txd(gmii_txd), + .gmii_tx_en(gmii_tx_en), + .gmii_tx_er(gmii_tx_er), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .speed(speed_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo_adapter #( + .DEPTH(TX_FIFO_DEPTH), + .S_DATA_WIDTH(AXIS_DATA_WIDTH), + .S_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .S_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .M_DATA_WIDTH(8), + .M_KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // AXI input + .s_clk(logic_clk), + .s_rst(logic_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_rst(tx_rst), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo_adapter #( + .DEPTH(RX_FIFO_DEPTH), + .S_DATA_WIDTH(8), + .S_KEEP_ENABLE(0), + .M_DATA_WIDTH(AXIS_DATA_WIDTH), + .M_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .M_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // AXI input + .s_clk(rx_clk), + .s_rst(rx_rst), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_rst(logic_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_1g_rgmii.v b/corundum/lib/eth/rtl/eth_mac_1g_rgmii.v new file mode 100644 index 0000000000000000000000000000000000000000..a29d576edc7d4b0ca29cb46abc101ccd84cfcc15 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_1g_rgmii.v @@ -0,0 +1,249 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC with RGMII interface + */ +module eth_mac_1g_rgmii # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Use 90 degree clock for RGMII transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire gtx_clk, + input wire gtx_clk90, + input wire gtx_rst, + output wire rx_clk, + output wire rx_rst, + output wire tx_clk, + output wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * RGMII interface + */ + input wire rgmii_rx_clk, + input wire [3:0] rgmii_rxd, + input wire rgmii_rx_ctl, + output wire rgmii_tx_clk, + output wire [3:0] rgmii_txd, + output wire rgmii_tx_ctl, + + /* + * Status + */ + output wire tx_error_underflow, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [7:0] mac_gmii_rxd; +wire mac_gmii_rx_dv; +wire mac_gmii_rx_er; +wire mac_gmii_tx_clk_en; +wire [7:0] mac_gmii_txd; +wire mac_gmii_tx_en; +wire mac_gmii_tx_er; + +reg [1:0] speed_reg = 2'b10; +reg mii_select_reg = 1'b0; + +(* srl_style = "register" *) +reg [1:0] tx_mii_select_sync = 2'd0; + +always @(posedge tx_clk) begin + tx_mii_select_sync <= {tx_mii_select_sync[0], mii_select_reg}; +end + +(* srl_style = "register" *) +reg [1:0] rx_mii_select_sync = 2'd0; + +always @(posedge rx_clk) begin + rx_mii_select_sync <= {rx_mii_select_sync[0], mii_select_reg}; +end + +// PHY speed detection +reg [2:0] rx_prescale = 3'd0; + +always @(posedge rx_clk) begin + rx_prescale <= rx_prescale + 3'd1; +end + +(* srl_style = "register" *) +reg [2:0] rx_prescale_sync = 3'd0; + +always @(posedge gtx_clk) begin + rx_prescale_sync <= {rx_prescale_sync[1:0], rx_prescale[2]}; +end + +reg [6:0] rx_speed_count_1 = 0; +reg [1:0] rx_speed_count_2 = 0; + +always @(posedge gtx_clk) begin + if (gtx_rst) begin + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end else begin + rx_speed_count_1 <= rx_speed_count_1 + 1; + + if (rx_prescale_sync[1] ^ rx_prescale_sync[2]) begin + rx_speed_count_2 <= rx_speed_count_2 + 1; + end + + if (&rx_speed_count_1) begin + // reference count overflow - 10M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + speed_reg <= 2'b00; + mii_select_reg <= 1'b1; + end + + if (&rx_speed_count_2) begin + // prescaled count overflow - 100M or 1000M + rx_speed_count_1 <= 0; + rx_speed_count_2 <= 0; + if (rx_speed_count_1[6:5]) begin + // large reference count - 100M + speed_reg <= 2'b01; + mii_select_reg <= 1'b1; + end else begin + // small reference count - 1000M + speed_reg <= 2'b10; + mii_select_reg <= 1'b0; + end + end + end +end + +assign speed = speed_reg; + +rgmii_phy_if #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .USE_CLK90(USE_CLK90) +) +rgmii_phy_if_inst ( + .clk(gtx_clk), + .clk90(gtx_clk90), + .rst(gtx_rst), + + .mac_gmii_rx_clk(rx_clk), + .mac_gmii_rx_rst(rx_rst), + .mac_gmii_rxd(mac_gmii_rxd), + .mac_gmii_rx_dv(mac_gmii_rx_dv), + .mac_gmii_rx_er(mac_gmii_rx_er), + .mac_gmii_tx_clk(tx_clk), + .mac_gmii_tx_rst(tx_rst), + .mac_gmii_tx_clk_en(mac_gmii_tx_clk_en), + .mac_gmii_txd(mac_gmii_txd), + .mac_gmii_tx_en(mac_gmii_tx_en), + .mac_gmii_tx_er(mac_gmii_tx_er), + + .phy_rgmii_rx_clk(rgmii_rx_clk), + .phy_rgmii_rxd(rgmii_rxd), + .phy_rgmii_rx_ctl(rgmii_rx_ctl), + .phy_rgmii_tx_clk(rgmii_tx_clk), + .phy_rgmii_txd(rgmii_txd), + .phy_rgmii_tx_ctl(rgmii_tx_ctl), + + .speed(speed) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_gmii_rxd), + .gmii_rx_dv(mac_gmii_rx_dv), + .gmii_rx_er(mac_gmii_rx_er), + .gmii_txd(mac_gmii_txd), + .gmii_tx_en(mac_gmii_tx_en), + .gmii_tx_er(mac_gmii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(mac_gmii_tx_clk_en), + .rx_mii_select(rx_mii_select_sync[1]), + .tx_mii_select(tx_mii_select_sync[1]), + .tx_error_underflow(tx_error_underflow), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_1g_rgmii_fifo.v b/corundum/lib/eth/rtl/eth_mac_1g_rgmii_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..640dbe624d236ecc73fb8f71261561652b59b2ba --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_1g_rgmii_fifo.v @@ -0,0 +1,343 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 1G Ethernet MAC with RGMII interface and TX and RX FIFOs + */ +module eth_mac_1g_rgmii_fifo # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Use 90 degree clock for RGMII transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + parameter AXIS_DATA_WIDTH = 8, + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_DEPTH = 4096, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire gtx_clk, + input wire gtx_clk90, + input wire gtx_rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * RGMII interface + */ + input wire rgmii_rx_clk, + input wire [3:0] rgmii_rxd, + input wire rgmii_rx_ctl, + output wire rgmii_tx_clk, + output wire [3:0] rgmii_txd, + output wire rgmii_tx_ctl, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + output wire [1:0] speed, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire tx_clk; +wire rx_clk; +wire tx_rst; +wire rx_rst; + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +wire [1:0] speed_int; + +reg [1:0] speed_sync_reg_1 = 2'b10; +reg [1:0] speed_sync_reg_2 = 2'b10; + +assign speed = speed_sync_reg_2; + +always @(posedge logic_clk) begin + speed_sync_reg_1 <= speed_int; + speed_sync_reg_2 <= speed_sync_reg_1; +end + +eth_mac_1g_rgmii #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .USE_CLK90(USE_CLK90), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_rgmii_inst ( + .gtx_clk(gtx_clk), + .gtx_clk90(gtx_clk90), + .gtx_rst(gtx_rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .rgmii_rx_clk(rgmii_rx_clk), + .rgmii_rxd(rgmii_rxd), + .rgmii_rx_ctl(rgmii_rx_ctl), + .rgmii_tx_clk(rgmii_tx_clk), + .rgmii_txd(rgmii_txd), + .rgmii_tx_ctl(rgmii_tx_ctl), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .speed(speed_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo_adapter #( + .DEPTH(TX_FIFO_DEPTH), + .S_DATA_WIDTH(AXIS_DATA_WIDTH), + .S_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .S_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .M_DATA_WIDTH(8), + .M_KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // AXI input + .s_clk(logic_clk), + .s_rst(logic_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_rst(tx_rst), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo_adapter #( + .DEPTH(RX_FIFO_DEPTH), + .S_DATA_WIDTH(8), + .S_KEEP_ENABLE(0), + .M_DATA_WIDTH(AXIS_DATA_WIDTH), + .M_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .M_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // AXI input + .s_clk(rx_clk), + .s_rst(rx_rst), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_rst(logic_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_mii.v b/corundum/lib/eth/rtl/eth_mac_mii.v new file mode 100644 index 0000000000000000000000000000000000000000..75c1f86c951a076db1a930c1fd52dbdefef01817 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_mii.v @@ -0,0 +1,166 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10M/100M Ethernet MAC with MII interface + */ +module eth_mac_mii # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64 +) +( + input wire rst, + output wire rx_clk, + output wire rx_rst, + output wire tx_clk, + output wire tx_rst, + + /* + * AXI input + */ + input wire [7:0] tx_axis_tdata, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [7:0] rx_axis_tdata, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * MII interface + */ + input wire mii_rx_clk, + input wire [3:0] mii_rxd, + input wire mii_rx_dv, + input wire mii_rx_er, + input wire mii_tx_clk, + output wire [3:0] mii_txd, + output wire mii_tx_en, + output wire mii_tx_er, + + /* + * Status + */ + output wire tx_start_packet, + output wire tx_error_underflow, + output wire rx_start_packet, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire [3:0] mac_mii_rxd; +wire mac_mii_rx_dv; +wire mac_mii_rx_er; +wire [3:0] mac_mii_txd; +wire mac_mii_tx_en; +wire mac_mii_tx_er; + +mii_phy_if #( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE) +) +mii_phy_if_inst ( + .rst(rst), + + .mac_mii_rx_clk(rx_clk), + .mac_mii_rx_rst(rx_rst), + .mac_mii_rxd(mac_mii_rxd), + .mac_mii_rx_dv(mac_mii_rx_dv), + .mac_mii_rx_er(mac_mii_rx_er), + .mac_mii_tx_clk(tx_clk), + .mac_mii_tx_rst(tx_rst), + .mac_mii_txd(mac_mii_txd), + .mac_mii_tx_en(mac_mii_tx_en), + .mac_mii_tx_er(mac_mii_tx_er), + + .phy_mii_rx_clk(mii_rx_clk), + .phy_mii_rxd(mii_rxd), + .phy_mii_rx_dv(mii_rx_dv), + .phy_mii_rx_er(mii_rx_er), + .phy_mii_tx_clk(mii_tx_clk), + .phy_mii_txd(mii_txd), + .phy_mii_tx_en(mii_tx_en), + .phy_mii_tx_er(mii_tx_er) +); + +eth_mac_1g #( + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_axis_tdata), + .tx_axis_tvalid(tx_axis_tvalid), + .tx_axis_tready(tx_axis_tready), + .tx_axis_tlast(tx_axis_tlast), + .tx_axis_tuser(tx_axis_tuser), + .rx_axis_tdata(rx_axis_tdata), + .rx_axis_tvalid(rx_axis_tvalid), + .rx_axis_tlast(rx_axis_tlast), + .rx_axis_tuser(rx_axis_tuser), + .gmii_rxd(mac_mii_rxd), + .gmii_rx_dv(mac_mii_rx_dv), + .gmii_rx_er(mac_mii_rx_er), + .gmii_txd(mac_mii_txd), + .gmii_tx_en(mac_mii_tx_en), + .gmii_tx_er(mac_mii_tx_er), + .rx_clk_enable(1'b1), + .tx_clk_enable(1'b1), + .rx_mii_select(1'b1), + .tx_mii_select(1'b1), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .rx_start_packet(rx_start_packet), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .ifg_delay(ifg_delay) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_mii_fifo.v b/corundum/lib/eth/rtl/eth_mac_mii_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..5f33c240caef7df13b29a22d767d4e04994bae5c --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_mii_fifo.v @@ -0,0 +1,321 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10M/100M Ethernet MAC with MII interface and TX and RX FIFOs + */ +module eth_mac_mii_fifo # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + parameter AXIS_DATA_WIDTH = 8, + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter TX_FIFO_DEPTH = 4096, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO +) +( + input wire rst, + input wire logic_clk, + input wire logic_rst, + + /* + * AXI input + */ + input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * AXI output + */ + output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * MII interface + */ + input wire mii_rx_clk, + input wire [3:0] mii_rxd, + input wire mii_rx_dv, + input wire mii_rx_er, + input wire mii_tx_clk, + output wire [3:0] mii_txd, + output wire mii_tx_en, + output wire mii_tx_er, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * Configuration + */ + input wire [7:0] ifg_delay +); + +wire tx_clk; +wire rx_clk; +wire tx_rst; +wire rx_rst; + +wire [7:0] tx_fifo_axis_tdata; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire tx_fifo_axis_tuser; + +wire [7:0] rx_fifo_axis_tdata; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire rx_fifo_axis_tuser; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [1:0] rx_sync_reg_1 = 2'd0; +reg [1:0] rx_sync_reg_2 = 2'd0; +reg [1:0] rx_sync_reg_3 = 2'd0; +reg [1:0] rx_sync_reg_4 = 2'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 2'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 2'd0; + rx_sync_reg_3 <= 2'd0; + rx_sync_reg_4 <= 2'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +eth_mac_mii #( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .ENABLE_PADDING(ENABLE_PADDING), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH) +) +eth_mac_1g_mii_inst ( + .rst(rst), + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + .mii_rx_clk(mii_rx_clk), + .mii_rxd(mii_rxd), + .mii_rx_dv(mii_rx_dv), + .mii_rx_er(mii_rx_er), + .mii_tx_clk(mii_tx_clk), + .mii_txd(mii_txd), + .mii_tx_en(mii_tx_en), + .mii_tx_er(mii_tx_er), + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .ifg_delay(ifg_delay) +); + +axis_async_fifo_adapter #( + .DEPTH(TX_FIFO_DEPTH), + .S_DATA_WIDTH(AXIS_DATA_WIDTH), + .S_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .S_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .M_DATA_WIDTH(8), + .M_KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // AXI input + .s_clk(logic_clk), + .s_rst(logic_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser), + // AXI output + .m_clk(tx_clk), + .m_rst(tx_rst), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo_adapter #( + .DEPTH(RX_FIFO_DEPTH), + .S_DATA_WIDTH(8), + .S_KEEP_ENABLE(0), + .M_DATA_WIDTH(AXIS_DATA_WIDTH), + .M_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .M_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +rx_fifo ( + // AXI input + .s_clk(rx_clk), + .s_rst(rx_rst), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_rst(logic_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_phy_10g.v b/corundum/lib/eth/rtl/eth_mac_phy_10g.v new file mode 100644 index 0000000000000000000000000000000000000000..4071bc1104f54adcd3ad9df2c1e593619631bdee --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_phy_10g.v @@ -0,0 +1,200 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter TX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = TX_PTP_TS_ENABLE, + parameter TX_PTP_TAG_WIDTH = 16, + parameter RX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_WIDTH = 96, + parameter TX_USER_WIDTH = (TX_PTP_TAG_ENABLE ? TX_PTP_TAG_WIDTH : 0) + 1, + parameter RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? RX_PTP_TS_WIDTH : 0) + 1, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter TX_SERDES_PIPELINE = 0, + parameter RX_SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] tx_axis_tdata, + input wire [KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire [TX_USER_WIDTH-1:0] tx_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] rx_axis_tdata, + output wire [KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + output wire rx_axis_tlast, + output wire [RX_USER_WIDTH-1:0] rx_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * PTP + */ + input wire [TX_PTP_TS_WIDTH-1:0] tx_ptp_ts, + input wire [RX_PTP_TS_WIDTH-1:0] rx_ptp_ts, + output wire [TX_PTP_TS_WIDTH-1:0] tx_axis_ptp_ts, + output wire [TX_PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag, + output wire tx_axis_ptp_ts_valid, + + /* + * Status + */ + output wire [1:0] tx_start_packet, + output wire tx_error_underflow, + output wire [1:0] rx_start_packet, + output wire [6:0] rx_error_count, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + input wire tx_prbs31_enable, + input wire rx_prbs31_enable +); + +eth_mac_phy_10g_rx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(RX_PTP_TS_WIDTH), + .USER_WIDTH(RX_USER_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_mac_phy_10g_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tuser(rx_axis_tuser), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .ptp_ts(rx_ptp_ts), + .rx_start_packet(rx_start_packet), + .rx_error_count(rx_error_count), + .rx_error_bad_frame(rx_error_bad_frame), + .rx_error_bad_fcs(rx_error_bad_fcs), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +eth_mac_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .PTP_TS_WIDTH(TX_PTP_TS_WIDTH), + .PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(TX_PTP_TAG_WIDTH), + .USER_WIDTH(TX_USER_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(TX_SERDES_PIPELINE) +) +eth_mac_phy_10g_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .ptp_ts(tx_ptp_ts), + .m_axis_ptp_ts(tx_axis_ptp_ts), + .m_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + .tx_start_packet(tx_start_packet), + .tx_error_underflow(tx_error_underflow), + .ifg_delay(ifg_delay), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_phy_10g_fifo.v b/corundum/lib/eth/rtl/eth_mac_phy_10g_fifo.v new file mode 100644 index 0000000000000000000000000000000000000000..fc293d4c4da0031c78f8d014d15d299375e23c3b --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_phy_10g_fifo.v @@ -0,0 +1,708 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination with TX and RX FIFOs + */ +module eth_mac_phy_10g_fifo # +( + parameter DATA_WIDTH = 64, + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter AXIS_DATA_WIDTH = DATA_WIDTH, + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter TX_SERDES_PIPELINE = 0, + parameter RX_SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4, + parameter TX_FIFO_DEPTH = 4096, + parameter TX_FRAME_FIFO = 1, + parameter TX_DROP_BAD_FRAME = TX_FRAME_FIFO, + parameter TX_DROP_WHEN_FULL = 0, + parameter RX_FIFO_DEPTH = 4096, + parameter RX_FRAME_FIFO = 1, + parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO, + parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO, + parameter LOGIC_PTP_PERIOD_NS = 4'h6, + parameter LOGIC_PTP_PERIOD_FNS = 16'h6666, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_USE_SAMPLE_CLOCK = 0, + parameter TX_PTP_TS_ENABLE = 0, + parameter RX_PTP_TS_ENABLE = 0, + parameter TX_PTP_TS_FIFO_DEPTH = 64, + parameter RX_PTP_TS_FIFO_DEPTH = 64, + parameter PTP_TS_WIDTH = 96, + parameter TX_PTP_TAG_ENABLE = 0, + parameter PTP_TAG_WIDTH = 16 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + input wire logic_clk, + input wire logic_rst, + input wire ptp_sample_clk, + + /* + * AXI input + */ + input wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + input wire tx_axis_tvalid, + output wire tx_axis_tready, + input wire tx_axis_tlast, + input wire tx_axis_tuser, + + /* + * Transmit timestamp tag input + */ + input wire [PTP_TAG_WIDTH-1:0] s_axis_tx_ptp_ts_tag, + input wire s_axis_tx_ptp_ts_valid, + output wire s_axis_tx_ptp_ts_ready, + + /* + * Transmit timestamp output + */ + output wire [PTP_TS_WIDTH-1:0] m_axis_tx_ptp_ts_96, + output wire [PTP_TAG_WIDTH-1:0] m_axis_tx_ptp_ts_tag, + output wire m_axis_tx_ptp_ts_valid, + input wire m_axis_tx_ptp_ts_ready, + + /* + * AXI output + */ + output wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + output wire rx_axis_tvalid, + input wire rx_axis_tready, + output wire rx_axis_tlast, + output wire rx_axis_tuser, + + /* + * Receive timestamp output + */ + output wire [PTP_TS_WIDTH-1:0] m_axis_rx_ptp_ts_96, + output wire m_axis_rx_ptp_ts_valid, + input wire m_axis_rx_ptp_ts_ready, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire tx_error_underflow, + output wire tx_fifo_overflow, + output wire tx_fifo_bad_frame, + output wire tx_fifo_good_frame, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + output wire rx_fifo_overflow, + output wire rx_fifo_bad_frame, + output wire rx_fifo_good_frame, + + /* + * PTP clock + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts_96, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + input wire tx_prbs31_enable, + input wire rx_prbs31_enable +); + +parameter KEEP_WIDTH = DATA_WIDTH/8; + +localparam TX_USER_WIDTH = (TX_PTP_TS_ENABLE && TX_PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1; +localparam RX_USER_WIDTH = (RX_PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1; + +wire [DATA_WIDTH-1:0] tx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] tx_fifo_axis_tkeep; +wire tx_fifo_axis_tvalid; +wire tx_fifo_axis_tready; +wire tx_fifo_axis_tlast; +wire [TX_USER_WIDTH-1:0] tx_fifo_axis_tuser; + +wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata_int; +wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep_int; +wire tx_axis_tvalid_int; +wire tx_axis_tready_int; +wire tx_axis_tlast_int; +wire [TX_USER_WIDTH-1:0] tx_axis_tuser_int; + +wire [DATA_WIDTH-1:0] rx_fifo_axis_tdata; +wire [KEEP_WIDTH-1:0] rx_fifo_axis_tkeep; +wire rx_fifo_axis_tvalid; +wire rx_fifo_axis_tlast; +wire [RX_USER_WIDTH-1:0] rx_fifo_axis_tuser; + +wire [RX_USER_WIDTH-1:0] rx_axis_tuser_int; + +wire [PTP_TS_WIDTH-1:0] tx_ptp_ts_96; +wire [PTP_TS_WIDTH-1:0] rx_ptp_ts_96; + +wire [PTP_TS_WIDTH-1:0] tx_axis_ptp_ts_96; +wire [PTP_TAG_WIDTH-1:0] tx_axis_ptp_ts_tag; +wire tx_axis_ptp_ts_valid; + +wire [PTP_TS_WIDTH-1:0] rx_axis_ptp_ts_96; +wire rx_axis_ptp_ts_valid; + +// synchronize MAC status signals into logic clock domain +wire tx_error_underflow_int; + +reg [0:0] tx_sync_reg_1 = 1'b0; +reg [0:0] tx_sync_reg_2 = 1'b0; +reg [0:0] tx_sync_reg_3 = 1'b0; +reg [0:0] tx_sync_reg_4 = 1'b0; + +assign tx_error_underflow = tx_sync_reg_3[0] ^ tx_sync_reg_4[0]; + +always @(posedge tx_clk or posedge tx_rst) begin + if (tx_rst) begin + tx_sync_reg_1 <= 1'b0; + end else begin + tx_sync_reg_1 <= tx_sync_reg_1 ^ {tx_error_underflow_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + tx_sync_reg_2 <= 1'b0; + tx_sync_reg_3 <= 1'b0; + tx_sync_reg_4 <= 1'b0; + end else begin + tx_sync_reg_2 <= tx_sync_reg_1; + tx_sync_reg_3 <= tx_sync_reg_2; + tx_sync_reg_4 <= tx_sync_reg_3; + end +end + +wire rx_error_bad_frame_int; +wire rx_error_bad_fcs_int; + +reg [4:0] rx_sync_reg_1 = 5'd0; +reg [4:0] rx_sync_reg_2 = 5'd0; +reg [4:0] rx_sync_reg_3 = 5'd0; +reg [4:0] rx_sync_reg_4 = 5'd0; + +assign rx_error_bad_frame = rx_sync_reg_3[0] ^ rx_sync_reg_4[0]; +assign rx_error_bad_fcs = rx_sync_reg_3[1] ^ rx_sync_reg_4[1]; +assign rx_bad_block = rx_sync_reg_3[2] ^ rx_sync_reg_4[2]; +assign rx_block_lock = rx_sync_reg_3[3] ^ rx_sync_reg_4[3]; +assign rx_high_ber = rx_sync_reg_3[4] ^ rx_sync_reg_4[4]; + +always @(posedge rx_clk or posedge rx_rst) begin + if (rx_rst) begin + rx_sync_reg_1 <= 5'd0; + end else begin + rx_sync_reg_1 <= rx_sync_reg_1 ^ {rx_high_ber_int, rx_block_lock_int, rx_bad_block_int, rx_error_bad_frame_int, rx_error_bad_frame_int}; + end +end + +always @(posedge logic_clk or posedge logic_rst) begin + if (logic_rst) begin + rx_sync_reg_2 <= 5'd0; + rx_sync_reg_3 <= 5'd0; + rx_sync_reg_4 <= 5'd0; + end else begin + rx_sync_reg_2 <= rx_sync_reg_1; + rx_sync_reg_3 <= rx_sync_reg_2; + rx_sync_reg_4 <= rx_sync_reg_3; + end +end + +// PTP timestamping +generate + +if (TX_PTP_TS_ENABLE) begin + + ptp_clock_cdc #( + .TS_WIDTH(PTP_TS_WIDTH), + .NS_WIDTH(4), + .FNS_WIDTH(16), + .INPUT_PERIOD_NS(LOGIC_PTP_PERIOD_NS), + .INPUT_PERIOD_FNS(LOGIC_PTP_PERIOD_FNS), + .OUTPUT_PERIOD_NS(PTP_PERIOD_NS), + .OUTPUT_PERIOD_FNS(PTP_PERIOD_FNS), + .USE_SAMPLE_CLOCK(PTP_USE_SAMPLE_CLOCK) + ) + tx_ptp_cdc ( + .input_clk(logic_clk), + .input_rst(logic_rst), + .output_clk(tx_clk), + .output_rst(tx_rst), + .sample_clk(ptp_sample_clk), + .input_ts(ptp_ts_96), + .output_ts(tx_ptp_ts_96), + .output_ts_step(), + .output_pps() + ); + + if (TX_PTP_TAG_ENABLE) begin + + ptp_tag_insert #( + .DATA_WIDTH(AXIS_DATA_WIDTH), + .KEEP_WIDTH(AXIS_KEEP_WIDTH), + .TAG_WIDTH(PTP_TAG_WIDTH), + .TAG_OFFSET(1), + .USER_WIDTH(TX_USER_WIDTH) + ) + tx_ptp_tag_insert ( + .clk(logic_clk), + .rst(logic_rst), + + // AXI stream input + .s_axis_tdata(tx_axis_tdata), + .s_axis_tkeep(tx_axis_tkeep), + .s_axis_tvalid(tx_axis_tvalid), + .s_axis_tready(tx_axis_tready), + .s_axis_tlast(tx_axis_tlast), + .s_axis_tuser(tx_axis_tuser), + + // AXI stream input + .m_axis_tdata(tx_axis_tdata_int), + .m_axis_tkeep(tx_axis_tkeep_int), + .m_axis_tvalid(tx_axis_tvalid_int), + .m_axis_tready(tx_axis_tready_int), + .m_axis_tlast(tx_axis_tlast_int), + .m_axis_tuser(tx_axis_tuser_int), + + // Tag input + .s_axis_tag(s_axis_tx_ptp_ts_tag), + .s_axis_tag_valid(s_axis_tx_ptp_ts_valid), + .s_axis_tag_ready(s_axis_tx_ptp_ts_ready) + ); + + axis_async_fifo #( + .DEPTH(TX_PTP_TS_FIFO_DEPTH), + .DATA_WIDTH(PTP_TAG_WIDTH+PTP_TS_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + tx_ptp_ts_fifo ( + .async_rst(logic_rst | tx_rst), + + // AXI input + .s_clk(tx_clk), + .s_axis_tdata({tx_axis_ptp_ts_tag, tx_axis_ptp_ts_96}), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_ptp_ts_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_clk(logic_clk), + .m_axis_tdata({m_axis_tx_ptp_ts_tag, m_axis_tx_ptp_ts_96}), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_tx_ptp_ts_valid), + .m_axis_tready(m_axis_tx_ptp_ts_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() + ); + + end else begin + + assign tx_axis_tdata_int = tx_axis_tdata; + assign tx_axis_tkeep_int = tx_axis_tkeep; + assign tx_axis_tvalid_int = tx_axis_tvalid; + assign tx_axis_tready = tx_axis_tready_int; + assign tx_axis_tlast_int = tx_axis_tlast; + assign tx_axis_tuser_int = tx_axis_tuser; + + axis_async_fifo #( + .DEPTH(TX_PTP_TS_FIFO_DEPTH), + .DATA_WIDTH(PTP_TS_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + tx_ptp_ts_fifo ( + .async_rst(logic_rst | tx_rst), + + // AXI input + .s_clk(tx_clk), + .s_axis_tdata(tx_axis_ptp_ts_96), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_axis_ptp_ts_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_clk(logic_clk), + .m_axis_tdata(m_axis_tx_ptp_ts_96), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_tx_ptp_ts_valid), + .m_axis_tready(m_axis_tx_ptp_ts_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() + ); + + assign s_axis_tx_ptp_ts_ready = 1'b0; + assign m_axis_tx_ptp_ts_tag = {PTP_TAG_WIDTH{1'b0}}; + + end + +end else begin + + assign s_axis_tx_ptp_ts_ready = 1'b0; + + assign m_axis_tx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + assign m_axis_tx_ptp_ts_tag = {PTP_TAG_WIDTH{1'b0}}; + assign m_axis_tx_ptp_ts_valid = 1'b0; + + assign tx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + + assign tx_axis_tdata_int = tx_axis_tdata; + assign tx_axis_tkeep_int = tx_axis_tkeep; + assign tx_axis_tvalid_int = tx_axis_tvalid; + assign tx_axis_tready = tx_axis_tready_int; + assign tx_axis_tlast_int = tx_axis_tlast; + assign tx_axis_tuser_int = tx_axis_tuser; + +end + +if (RX_PTP_TS_ENABLE) begin + + ptp_clock_cdc #( + .TS_WIDTH(PTP_TS_WIDTH), + .NS_WIDTH(4), + .FNS_WIDTH(16), + .INPUT_PERIOD_NS(LOGIC_PTP_PERIOD_NS), + .INPUT_PERIOD_FNS(LOGIC_PTP_PERIOD_FNS), + .OUTPUT_PERIOD_NS(PTP_PERIOD_NS), + .OUTPUT_PERIOD_FNS(PTP_PERIOD_FNS), + .USE_SAMPLE_CLOCK(PTP_USE_SAMPLE_CLOCK) + ) + rx_ptp_cdc ( + .input_clk(logic_clk), + .input_rst(logic_rst), + .output_clk(rx_clk), + .output_rst(rx_rst), + .sample_clk(ptp_sample_clk), + .input_ts(ptp_ts_96), + .output_ts(rx_ptp_ts_96), + .output_ts_step(), + .output_pps() + ); + + axis_fifo #( + .DEPTH(RX_PTP_TS_FIFO_DEPTH), + .DATA_WIDTH(PTP_TS_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + rx_ptp_ts_fifo ( + .clk(logic_clk), + .rst(logic_rst), + + // AXI input + .s_axis_tdata(rx_axis_ptp_ts_96), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_axis_ptp_ts_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata(m_axis_rx_ptp_ts_96), + .m_axis_tkeep(), + .m_axis_tvalid(m_axis_rx_ptp_ts_valid), + .m_axis_tready(m_axis_rx_ptp_ts_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() + ); + + ptp_ts_extract #( + .TS_WIDTH(PTP_TS_WIDTH), + .TS_OFFSET(1), + .USER_WIDTH(RX_USER_WIDTH) + ) + rx_ptp_ts_extract ( + .clk(logic_clk), + .rst(logic_rst), + + // AXI stream input + .s_axis_tvalid(rx_axis_tvalid && rx_axis_tready), + .s_axis_tlast(rx_axis_tlast), + .s_axis_tuser(rx_axis_tuser_int), + + // Timestamp output + .m_axis_ts(rx_axis_ptp_ts_96), + .m_axis_ts_valid(rx_axis_ptp_ts_valid) + ); + +end else begin + + assign m_axis_rx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + assign m_axis_rx_ptp_ts_valid = 1'b0; + + assign rx_ptp_ts_96 = {PTP_TS_WIDTH{1'b0}}; + +end + +assign rx_axis_tuser = rx_axis_tuser_int[0]; + +endgenerate + +eth_mac_phy_10g #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .TX_PTP_TS_ENABLE(TX_PTP_TS_ENABLE), + .TX_PTP_TS_WIDTH(PTP_TS_WIDTH), + .TX_PTP_TAG_ENABLE(TX_PTP_TAG_ENABLE), + .TX_PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .RX_PTP_TS_ENABLE(RX_PTP_TS_ENABLE), + .RX_PTP_TS_WIDTH(PTP_TS_WIDTH), + .TX_USER_WIDTH(TX_USER_WIDTH), + .RX_USER_WIDTH(RX_USER_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .TX_SERDES_PIPELINE(TX_SERDES_PIPELINE), + .RX_SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_mac_phy_10g_inst ( + .tx_clk(tx_clk), + .tx_rst(tx_rst), + .rx_clk(rx_clk), + .rx_rst(rx_rst), + + .tx_axis_tdata(tx_fifo_axis_tdata), + .tx_axis_tkeep(tx_fifo_axis_tkeep), + .tx_axis_tvalid(tx_fifo_axis_tvalid), + .tx_axis_tready(tx_fifo_axis_tready), + .tx_axis_tlast(tx_fifo_axis_tlast), + .tx_axis_tuser(tx_fifo_axis_tuser), + + .rx_axis_tdata(rx_fifo_axis_tdata), + .rx_axis_tkeep(rx_fifo_axis_tkeep), + .rx_axis_tvalid(rx_fifo_axis_tvalid), + .rx_axis_tlast(rx_fifo_axis_tlast), + .rx_axis_tuser(rx_fifo_axis_tuser), + + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + + .tx_ptp_ts(tx_ptp_ts_96), + .rx_ptp_ts(rx_ptp_ts_96), + .tx_axis_ptp_ts(tx_axis_ptp_ts_96), + .tx_axis_ptp_ts_tag(tx_axis_ptp_ts_tag), + .tx_axis_ptp_ts_valid(tx_axis_ptp_ts_valid), + + .tx_error_underflow(tx_error_underflow_int), + .rx_error_bad_frame(rx_error_bad_frame_int), + .rx_error_bad_fcs(rx_error_bad_fcs_int), + .rx_bad_block(rx_bad_block_int), + .rx_block_lock(rx_block_lock_int), + .rx_high_ber(rx_high_ber_int), + + .ifg_delay(ifg_delay), + + .tx_prbs31_enable(tx_prbs31_enable), + .rx_prbs31_enable(rx_prbs31_enable) +); + +axis_async_fifo_adapter #( + .DEPTH(TX_FIFO_DEPTH), + .S_DATA_WIDTH(AXIS_DATA_WIDTH), + .S_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .S_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .M_DATA_WIDTH(DATA_WIDTH), + .M_KEEP_ENABLE(1), + .M_KEEP_WIDTH(KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(TX_USER_WIDTH), + .FRAME_FIFO(TX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(TX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(TX_DROP_WHEN_FULL) +) +tx_fifo ( + // AXI input + .s_clk(logic_clk), + .s_rst(logic_rst), + .s_axis_tdata(tx_axis_tdata_int), + .s_axis_tkeep(tx_axis_tkeep_int), + .s_axis_tvalid(tx_axis_tvalid_int), + .s_axis_tready(tx_axis_tready_int), + .s_axis_tlast(tx_axis_tlast_int), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser_int), + // AXI output + .m_clk(tx_clk), + .m_rst(tx_rst), + .m_axis_tdata(tx_fifo_axis_tdata), + .m_axis_tkeep(tx_fifo_axis_tkeep), + .m_axis_tvalid(tx_fifo_axis_tvalid), + .m_axis_tready(tx_fifo_axis_tready), + .m_axis_tlast(tx_fifo_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_axis_tuser), + // Status + .s_status_overflow(tx_fifo_overflow), + .s_status_bad_frame(tx_fifo_bad_frame), + .s_status_good_frame(tx_fifo_good_frame), + .m_status_overflow(), + .m_status_bad_frame(), + .m_status_good_frame() +); + +axis_async_fifo_adapter #( + .DEPTH(RX_FIFO_DEPTH), + .S_DATA_WIDTH(DATA_WIDTH), + .S_KEEP_ENABLE(1), + .S_KEEP_WIDTH(KEEP_WIDTH), + .M_DATA_WIDTH(AXIS_DATA_WIDTH), + .M_KEEP_ENABLE(AXIS_KEEP_ENABLE), + .M_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(RX_USER_WIDTH), + .FRAME_FIFO(RX_FRAME_FIFO), + .USER_BAD_FRAME_VALUE(1'b1), + .USER_BAD_FRAME_MASK(1'b1), + .DROP_BAD_FRAME(RX_DROP_BAD_FRAME), + .DROP_WHEN_FULL(RX_DROP_WHEN_FULL) +) +rx_fifo ( + // AXI input + .s_clk(rx_clk), + .s_rst(rx_rst), + .s_axis_tdata(rx_fifo_axis_tdata), + .s_axis_tkeep(rx_fifo_axis_tkeep), + .s_axis_tvalid(rx_fifo_axis_tvalid), + .s_axis_tready(), + .s_axis_tlast(rx_fifo_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_fifo_axis_tuser), + // AXI output + .m_clk(logic_clk), + .m_rst(logic_rst), + .m_axis_tdata(rx_axis_tdata), + .m_axis_tkeep(rx_axis_tkeep), + .m_axis_tvalid(rx_axis_tvalid), + .m_axis_tready(rx_axis_tready), + .m_axis_tlast(rx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser_int), + // Status + .s_status_overflow(), + .s_status_bad_frame(), + .s_status_good_frame(), + .m_status_overflow(rx_fifo_overflow), + .m_status_bad_frame(rx_fifo_bad_frame), + .m_status_good_frame(rx_fifo_good_frame) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_phy_10g_rx.v b/corundum/lib/eth/rtl/eth_mac_phy_10g_rx.v new file mode 100644 index 0000000000000000000000000000000000000000..4e3c66767bd9b2ffe43fa3a904544f13bbf61b36 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_phy_10g_rx.v @@ -0,0 +1,163 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g_rx # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter USER_WIDTH = (PTP_TS_ENABLE ? PTP_TS_WIDTH : 0) + 1, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * SERDES interface + */ + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + + /* + * Status + */ + output wire [1:0] rx_start_packet, + output wire [6:0] rx_error_count, + output wire rx_error_bad_frame, + output wire rx_error_bad_fcs, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire rx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH * 32 != DATA_WIDTH) begin + $error("Error: HDR_WIDTH must be equal to DATA_WIDTH/32"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_rx_data; +wire [HDR_WIDTH-1:0] encoded_rx_hdr; + +eth_phy_10g_rx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +axis_baser_rx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +axis_baser_rx_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .m_axis_tdata(m_axis_tdata), + .m_axis_tkeep(m_axis_tkeep), + .m_axis_tvalid(m_axis_tvalid), + .m_axis_tlast(m_axis_tlast), + .m_axis_tuser(m_axis_tuser), + .ptp_ts(ptp_ts), + .start_packet(rx_start_packet), + .error_bad_frame(rx_error_bad_frame), + .error_bad_fcs(rx_error_bad_fcs), + .rx_bad_block(rx_bad_block) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mac_phy_10g_tx.v b/corundum/lib/eth/rtl/eth_mac_phy_10g_tx.v new file mode 100644 index 0000000000000000000000000000000000000000..29b7e244bed74f0fb44e46e738621d4c1fcc4bdb --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mac_phy_10g_tx.v @@ -0,0 +1,167 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet MAC/PHY combination + */ +module eth_mac_phy_10g_tx # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = (DATA_WIDTH/32), + parameter ENABLE_PADDING = 1, + parameter ENABLE_DIC = 1, + parameter MIN_FRAME_LENGTH = 64, + parameter PTP_PERIOD_NS = 4'h6, + parameter PTP_PERIOD_FNS = 16'h6666, + parameter PTP_TS_ENABLE = 0, + parameter PTP_TS_WIDTH = 96, + parameter PTP_TAG_ENABLE = PTP_TS_ENABLE, + parameter PTP_TAG_WIDTH = 16, + parameter USER_WIDTH = (PTP_TAG_ENABLE ? PTP_TAG_WIDTH : 0) + 1, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + + /* + * PTP + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts, + output wire [PTP_TS_WIDTH-1:0] m_axis_ptp_ts, + output wire [PTP_TAG_WIDTH-1:0] m_axis_ptp_ts_tag, + output wire m_axis_ptp_ts_valid, + + /* + * Status + */ + output wire [1:0] tx_start_packet, + output wire tx_error_underflow, + + /* + * Configuration + */ + input wire [7:0] ifg_delay, + input wire tx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH * 32 != DATA_WIDTH) begin + $error("Error: HDR_WIDTH must be equal to DATA_WIDTH/32"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; + +axis_baser_tx_64 #( + .DATA_WIDTH(DATA_WIDTH), + .KEEP_WIDTH(KEEP_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .ENABLE_PADDING(ENABLE_PADDING), + .ENABLE_DIC(ENABLE_DIC), + .MIN_FRAME_LENGTH(MIN_FRAME_LENGTH), + .PTP_PERIOD_NS(PTP_PERIOD_NS), + .PTP_PERIOD_FNS(PTP_PERIOD_FNS), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .PTP_TAG_ENABLE(PTP_TAG_ENABLE), + .PTP_TAG_WIDTH(PTP_TAG_WIDTH), + .USER_WIDTH(USER_WIDTH) +) +axis_baser_tx_inst ( + .clk(clk), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tkeep(s_axis_tkeep), + .s_axis_tvalid(s_axis_tvalid), + .s_axis_tready(s_axis_tready), + .s_axis_tlast(s_axis_tlast), + .s_axis_tuser(s_axis_tuser), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .ptp_ts(ptp_ts), + .m_axis_ptp_ts(m_axis_ptp_ts), + .m_axis_ptp_ts_tag(m_axis_ptp_ts_tag), + .m_axis_ptp_ts_valid(m_axis_ptp_ts_valid), + .start_packet(tx_start_packet), + .error_underflow(tx_error_underflow), + .ifg_delay(ifg_delay) +); + +eth_phy_10g_tx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE) +) +eth_phy_10g_tx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_mux.v b/corundum/lib/eth/rtl/eth_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..4fe32659dc993b2fcd5cf56251071b045091fdd1 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_mux.v @@ -0,0 +1,299 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ethernet multiplexer + */ +module eth_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame inputs + */ + input wire [S_COUNT-1:0] s_eth_hdr_valid, + output wire [S_COUNT-1:0] s_eth_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*DATA_WIDTH-1:0] s_eth_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_eth_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_eth_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_eth_payload_axis_tready, + input wire [S_COUNT-1:0] s_eth_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_eth_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_eth_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [DATA_WIDTH-1:0] m_eth_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_eth_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_eth_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_eth_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_eth_hdr_ready_reg = 0, s_eth_hdr_ready_next; + +reg [S_COUNT-1:0] s_eth_payload_axis_tready_reg = 0, s_eth_payload_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; + +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_eth_payload_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_eth_payload_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_eth_payload_axis_tvalid[select_reg]; +wire current_s_tready = s_eth_payload_axis_tready[select_reg]; +wire current_s_tlast = s_eth_payload_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_eth_payload_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_eth_payload_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_eth_payload_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_eth_hdr_ready_next = 0; + + s_eth_payload_axis_tready_next = 0; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && !m_eth_hdr_valid && (s_eth_hdr_valid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + + s_eth_hdr_ready_next = (1 << select); + + m_eth_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[select*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[select*48 +: 48]; + m_eth_type_next = s_eth_type[select*16 +: 16]; + end + + // generate ready signal on selected port + s_eth_payload_axis_tready_next = (m_eth_payload_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_eth_payload_axis_tdata_int = current_s_tdata; + m_eth_payload_axis_tkeep_int = current_s_tkeep; + m_eth_payload_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_eth_payload_axis_tlast_int = current_s_tlast; + m_eth_payload_axis_tid_int = current_s_tid; + m_eth_payload_axis_tdest_int = current_s_tdest; + m_eth_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_eth_hdr_ready_reg <= 0; + s_eth_payload_axis_tready_reg <= 0; + m_eth_hdr_valid_reg <= 1'b0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_eth_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_eth_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_eth_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_eth_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_eth_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = KEEP_ENABLE ? m_eth_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tid = ID_ENABLE ? m_eth_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_eth_payload_axis_tdest = DEST_ENABLE ? m_eth_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_eth_payload_axis_tuser = USER_ENABLE ? m_eth_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tid_reg <= temp_m_eth_payload_axis_tid_reg; + m_eth_payload_axis_tdest_reg <= temp_m_eth_payload_axis_tdest_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tid_reg <= m_eth_payload_axis_tid_int; + temp_m_eth_payload_axis_tdest_reg <= m_eth_payload_axis_tdest_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_phy_10g.v b/corundum/lib/eth/rtl/eth_phy_10g.v new file mode 100644 index 0000000000000000000000000000000000000000..3db237317accc4cfb29d62a6da757cc69ad2830d --- /dev/null +++ b/corundum/lib/eth/rtl/eth_phy_10g.v @@ -0,0 +1,128 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet PHY + */ +module eth_phy_10g # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter TX_SERDES_PIPELINE = 0, + parameter RX_SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire rx_clk, + input wire rx_rst, + input wire tx_clk, + input wire tx_rst, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire [6:0] rx_error_count, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire tx_prbs31_enable, + input wire rx_prbs31_enable +); + +eth_phy_10g_rx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(RX_SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_inst ( + .clk(rx_clk), + .rst(rx_rst), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_bad_block(rx_bad_block), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +eth_phy_10g_tx #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(TX_SERDES_PIPELINE) +) +eth_phy_10g_tx_inst ( + .clk(tx_clk), + .rst(tx_rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_phy_10g_rx.v b/corundum/lib/eth/rtl/eth_phy_10g_rx.v new file mode 100644 index 0000000000000000000000000000000000000000..959382617f07ab86b6ea96314a563b2a0a11e632 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_phy_10g_rx.v @@ -0,0 +1,135 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet PHY RX + */ +module eth_phy_10g_rx # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * XGMII interface + */ + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * SERDES interface + */ + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire [6:0] rx_error_count, + output wire rx_bad_block, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire rx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_rx_data; +wire [HDR_WIDTH-1:0] encoded_rx_hdr; + +eth_phy_10g_rx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .serdes_rx_data(serdes_rx_data), + .serdes_rx_hdr(serdes_rx_hdr), + .serdes_rx_bitslip(serdes_rx_bitslip), + .rx_error_count(rx_error_count), + .rx_block_lock(rx_block_lock), + .rx_high_ber(rx_high_ber), + .rx_prbs31_enable(rx_prbs31_enable) +); + +xgmii_baser_dec_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +xgmii_baser_dec_inst ( + .clk(clk), + .rst(rst), + .encoded_rx_data(encoded_rx_data), + .encoded_rx_hdr(encoded_rx_hdr), + .xgmii_rxd(xgmii_rxd), + .xgmii_rxc(xgmii_rxc), + .rx_bad_block(rx_bad_block) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_phy_10g_rx_ber_mon.v b/corundum/lib/eth/rtl/eth_phy_10g_rx_ber_mon.v new file mode 100644 index 0000000000000000000000000000000000000000..a1b5d0b37c803586ba1dfa7b39d06da97afcc4fb --- /dev/null +++ b/corundum/lib/eth/rtl/eth_phy_10g_rx_ber_mon.v @@ -0,0 +1,120 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet PHY BER monitor + */ +module eth_phy_10g_rx_ber_mon # +( + parameter HDR_WIDTH = 2, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * SERDES interface + */ + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + + /* + * Status + */ + output wire rx_high_ber +); + +// bus width assertions +initial begin + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +parameter COUNT_WIDTH = $clog2(COUNT_125US); + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +reg [COUNT_WIDTH-1:0] time_count_reg = 0, time_count_next; +reg [3:0] ber_count_reg = 4'd0, ber_count_next; + +reg rx_high_ber_reg = 1'b0, rx_high_ber_next; + +assign rx_high_ber = rx_high_ber_reg; + +always @* begin + if (time_count_reg > 0) begin + time_count_next = time_count_reg-1; + end else begin + time_count_next = time_count_reg; + end + ber_count_next = ber_count_reg; + + rx_high_ber_next = rx_high_ber_reg; + + if (serdes_rx_hdr == SYNC_CTRL || serdes_rx_hdr == SYNC_DATA) begin + // valid header + if (ber_count_reg != 4'd15) begin + if (time_count_reg == 0) begin + rx_high_ber_next = 1'b0; + end + end + end else begin + // invalid header + if (ber_count_reg == 4'd15) begin + rx_high_ber_next = 1'b1; + end else begin + ber_count_next = ber_count_reg + 1; + if (time_count_reg == 0) begin + rx_high_ber_next = 1'b0; + end + end + end + if (time_count_reg == 0) begin + // 125 us timer expired + ber_count_next = 4'd0; + time_count_next = COUNT_125US; + end +end + +always @(posedge clk) begin + if (rst) begin + time_count_reg <= COUNT_125US; + ber_count_reg <= 4'd0; + rx_high_ber_reg <= 1'b0; + end else begin + time_count_reg <= time_count_next; + ber_count_reg <= ber_count_next; + rx_high_ber_reg <= rx_high_ber_next; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_phy_10g_rx_frame_sync.v b/corundum/lib/eth/rtl/eth_phy_10g_rx_frame_sync.v new file mode 100644 index 0000000000000000000000000000000000000000..14fdf72b5afeea2095c146ea8579563a7010c1f5 --- /dev/null +++ b/corundum/lib/eth/rtl/eth_phy_10g_rx_frame_sync.v @@ -0,0 +1,133 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet PHY frame sync + */ +module eth_phy_10g_rx_frame_sync # +( + parameter HDR_WIDTH = 2, + parameter SLIP_COUNT_WIDTH = 3 +) +( + input wire clk, + input wire rst, + + /* + * SERDES interface + */ + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire rx_block_lock +); + +// bus width assertions +initial begin + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +reg [5:0] sh_count_reg = 6'd0, sh_count_next; +reg [3:0] sh_invalid_count_reg = 4'd0, sh_invalid_count_next; +reg [SLIP_COUNT_WIDTH-1:0] slip_count_reg = 0, slip_count_next; + +reg serdes_rx_bitslip_reg = 1'b0, serdes_rx_bitslip_next; + +reg rx_block_lock_reg = 1'b0, rx_block_lock_next; + +assign serdes_rx_bitslip = serdes_rx_bitslip_reg; +assign rx_block_lock = rx_block_lock_reg; + +always @* begin + sh_count_next = sh_count_reg; + sh_invalid_count_next = sh_invalid_count_reg; + slip_count_next = slip_count_reg; + + serdes_rx_bitslip_next = 1'b0; + + rx_block_lock_next = rx_block_lock_reg; + + if (slip_count_reg) begin + slip_count_next = slip_count_reg-1; + end else if (serdes_rx_hdr == SYNC_CTRL || serdes_rx_hdr == SYNC_DATA) begin + // valid header + sh_count_next = sh_count_reg + 1; + if (&sh_count_reg) begin + // valid count overflow, reset + sh_count_next = 0; + sh_invalid_count_next = 0; + if (!sh_invalid_count_reg) begin + rx_block_lock_next = 1'b1; + end + end + end else begin + // invalid header + sh_count_next = sh_count_reg + 1; + sh_invalid_count_next = sh_invalid_count_reg + 1; + if (!rx_block_lock_reg || &sh_invalid_count_reg) begin + // invalid count overflow, lost block lock + sh_count_next = 0; + sh_invalid_count_next = 0; + rx_block_lock_next = 1'b0; + serdes_rx_bitslip_next = 1'b1; + slip_count_next = {SLIP_COUNT_WIDTH{1'b1}}; + end else if (&sh_count_reg) begin + // valid count overflow, reset + sh_count_next = 0; + sh_invalid_count_next = 0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + sh_count_reg <= 6'd0; + sh_invalid_count_reg <= 4'd0; + slip_count_reg <= 0; + rx_block_lock_reg <= 1'b0; + end else begin + sh_count_reg <= sh_count_next; + sh_invalid_count_reg <= sh_invalid_count_next; + slip_count_reg <= slip_count_next; + rx_block_lock_reg <= rx_block_lock_next; + end + + serdes_rx_bitslip_reg <= serdes_rx_bitslip_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/eth_phy_10g_rx_if.v b/corundum/lib/eth/rtl/eth_phy_10g_rx_if.v new file mode 100644 index 0000000000000000000000000000000000000000..c22dba6306eda8377b394470eb73f05d8d04b92b --- /dev/null +++ b/corundum/lib/eth/rtl/eth_phy_10g_rx_if.v @@ -0,0 +1,242 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet PHY RX IF + */ +module eth_phy_10g_rx_if # +( + parameter DATA_WIDTH = 64, + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0, + parameter SLIP_COUNT_WIDTH = 3, + parameter COUNT_125US = 125000/6.4 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded interface + */ + output wire [DATA_WIDTH-1:0] encoded_rx_data, + output wire [HDR_WIDTH-1:0] encoded_rx_hdr, + + /* + * SERDES interface + */ + input wire [DATA_WIDTH-1:0] serdes_rx_data, + input wire [HDR_WIDTH-1:0] serdes_rx_hdr, + output wire serdes_rx_bitslip, + + /* + * Status + */ + output wire [6:0] rx_error_count, + output wire rx_block_lock, + output wire rx_high_ber, + + /* + * Configuration + */ + input wire rx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] serdes_rx_data_rev, serdes_rx_data_int; +wire [HDR_WIDTH-1:0] serdes_rx_hdr_rev, serdes_rx_hdr_int; + +generate + genvar n; + + if (BIT_REVERSE) begin + for (n = 0; n < DATA_WIDTH; n = n + 1) begin + assign serdes_rx_data_rev[n] = serdes_rx_data[DATA_WIDTH-n-1]; + end + + for (n = 0; n < HDR_WIDTH; n = n + 1) begin + assign serdes_rx_hdr_rev[n] = serdes_rx_hdr[HDR_WIDTH-n-1]; + end + end else begin + assign serdes_rx_data_rev = serdes_rx_data; + assign serdes_rx_hdr_rev = serdes_rx_hdr; + end + + if (SERDES_PIPELINE > 0) begin + (* srl_style = "register" *) + reg [DATA_WIDTH-1:0] serdes_rx_data_pipe_reg[SERDES_PIPELINE-1:0]; + (* srl_style = "register" *) + reg [HDR_WIDTH-1:0] serdes_rx_hdr_pipe_reg[SERDES_PIPELINE-1:0]; + + for (n = 0; n < SERDES_PIPELINE; n = n + 1) begin + initial begin + serdes_rx_data_pipe_reg[n] <= {DATA_WIDTH{1'b0}}; + serdes_rx_hdr_pipe_reg[n] <= {HDR_WIDTH{1'b0}}; + end + + always @(posedge clk) begin + serdes_rx_data_pipe_reg[n] <= n == 0 ? serdes_rx_data_rev : serdes_rx_data_pipe_reg[n-1]; + serdes_rx_hdr_pipe_reg[n] <= n == 0 ? serdes_rx_hdr_rev : serdes_rx_hdr_pipe_reg[n-1]; + end + end + + assign serdes_rx_data_int = serdes_rx_data_pipe_reg[SERDES_PIPELINE-1]; + assign serdes_rx_hdr_int = serdes_rx_hdr_pipe_reg[SERDES_PIPELINE-1]; + end else begin + assign serdes_rx_data_int = serdes_rx_data_rev; + assign serdes_rx_hdr_int = serdes_rx_hdr_rev; + end + +endgenerate + +wire [DATA_WIDTH-1:0] descrambled_rx_data; + +reg [DATA_WIDTH-1:0] encoded_rx_data_reg = {DATA_WIDTH{1'b0}}; +reg [HDR_WIDTH-1:0] encoded_rx_hdr_reg = {HDR_WIDTH{1'b0}}; + +reg [57:0] scrambler_state_reg = {58{1'b1}}; +wire [57:0] scrambler_state; + +reg [30:0] prbs31_state_reg = 31'h7fffffff; +wire [30:0] prbs31_state; +wire [DATA_WIDTH+HDR_WIDTH-1:0] prbs31_data; + +reg [6:0] rx_error_count_reg = 0; +reg [5:0] rx_error_count_1_reg = 0; +reg [5:0] rx_error_count_2_reg = 0; +reg [5:0] rx_error_count_1_temp = 0; +reg [5:0] rx_error_count_2_temp = 0; + +lfsr #( + .LFSR_WIDTH(58), + .LFSR_POLY(58'h8000000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(1), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH), + .STYLE("AUTO") +) +descrambler_inst ( + .data_in(serdes_rx_data_int), + .state_in(scrambler_state_reg), + .data_out(descrambled_rx_data), + .state_out(scrambler_state) +); + +lfsr #( + .LFSR_WIDTH(31), + .LFSR_POLY(31'h10000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(1), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH+HDR_WIDTH), + .STYLE("AUTO") +) +prbs31_check_inst ( + .data_in(~{serdes_rx_data_int, serdes_rx_hdr_int}), + .state_in(prbs31_state_reg), + .data_out(prbs31_data), + .state_out(prbs31_state) +); + +integer i; + +always @* begin + rx_error_count_1_temp = 0; + rx_error_count_2_temp = 0; + for (i = 0; i < DATA_WIDTH+HDR_WIDTH; i = i + 1) begin + if (i & 1) begin + rx_error_count_1_temp = rx_error_count_1_temp + prbs31_data[i]; + end else begin + rx_error_count_2_temp = rx_error_count_2_temp + prbs31_data[i]; + end + end +end + +always @(posedge clk) begin + scrambler_state_reg <= scrambler_state; + + encoded_rx_data_reg <= SCRAMBLER_DISABLE ? serdes_rx_data_int : descrambled_rx_data; + encoded_rx_hdr_reg <= serdes_rx_hdr_int; + + if (PRBS31_ENABLE && rx_prbs31_enable) begin + prbs31_state_reg <= prbs31_state; + + rx_error_count_1_reg <= rx_error_count_1_temp; + rx_error_count_2_reg <= rx_error_count_2_temp; + rx_error_count_reg <= rx_error_count_1_reg + rx_error_count_2_reg; + end +end + +assign encoded_rx_data = encoded_rx_data_reg; +assign encoded_rx_hdr = encoded_rx_hdr_reg; + +assign rx_error_count = rx_error_count_reg; + +wire serdes_rx_bitslip_int; +assign serdes_rx_bitslip = serdes_rx_bitslip_int && !(PRBS31_ENABLE && rx_prbs31_enable); + +eth_phy_10g_rx_frame_sync #( + .HDR_WIDTH(HDR_WIDTH), + .SLIP_COUNT_WIDTH(SLIP_COUNT_WIDTH) +) +eth_phy_10g_rx_frame_sync_inst ( + .clk(clk), + .rst(rst), + .serdes_rx_hdr(serdes_rx_hdr_int), + .serdes_rx_bitslip(serdes_rx_bitslip_int), + .rx_block_lock(rx_block_lock) +); + +eth_phy_10g_rx_ber_mon #( + .HDR_WIDTH(HDR_WIDTH), + .COUNT_125US(COUNT_125US) +) +eth_phy_10g_rx_ber_mon_inst ( + .clk(clk), + .rst(rst), + .serdes_rx_hdr(serdes_rx_hdr_int), + .rx_high_ber(rx_high_ber) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_phy_10g_tx.v b/corundum/lib/eth/rtl/eth_phy_10g_tx.v new file mode 100644 index 0000000000000000000000000000000000000000..86488353ef2932fdec75269a1ef4bceb908a46aa --- /dev/null +++ b/corundum/lib/eth/rtl/eth_phy_10g_tx.v @@ -0,0 +1,117 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet PHY TX + */ +module eth_phy_10g_tx # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0 +) +( + input wire clk, + input wire rst, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + + /* + * Configuration + */ + input wire tx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +wire [DATA_WIDTH-1:0] encoded_tx_data; +wire [HDR_WIDTH-1:0] encoded_tx_hdr; + +xgmii_baser_enc_64 #( + .DATA_WIDTH(DATA_WIDTH), + .CTRL_WIDTH(CTRL_WIDTH), + .HDR_WIDTH(HDR_WIDTH) +) +xgmii_baser_enc_inst ( + .clk(clk), + .rst(rst), + .xgmii_txd(xgmii_txd), + .xgmii_txc(xgmii_txc), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr) +); + +eth_phy_10g_tx_if #( + .DATA_WIDTH(DATA_WIDTH), + .HDR_WIDTH(HDR_WIDTH), + .BIT_REVERSE(BIT_REVERSE), + .SCRAMBLER_DISABLE(SCRAMBLER_DISABLE), + .PRBS31_ENABLE(PRBS31_ENABLE), + .SERDES_PIPELINE(SERDES_PIPELINE) +) +eth_phy_10g_tx_if_inst ( + .clk(clk), + .rst(rst), + .encoded_tx_data(encoded_tx_data), + .encoded_tx_hdr(encoded_tx_hdr), + .serdes_tx_data(serdes_tx_data), + .serdes_tx_hdr(serdes_tx_hdr), + .tx_prbs31_enable(tx_prbs31_enable) +); + +endmodule diff --git a/corundum/lib/eth/rtl/eth_phy_10g_tx_if.v b/corundum/lib/eth/rtl/eth_phy_10g_tx_if.v new file mode 100644 index 0000000000000000000000000000000000000000..70ea70b7b3be7ed5fa6c9b47ef80e5ebbd1e1c8f --- /dev/null +++ b/corundum/lib/eth/rtl/eth_phy_10g_tx_if.v @@ -0,0 +1,179 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * 10G Ethernet PHY TX IF + */ +module eth_phy_10g_tx_if # +( + parameter DATA_WIDTH = 64, + parameter HDR_WIDTH = 2, + parameter BIT_REVERSE = 0, + parameter SCRAMBLER_DISABLE = 0, + parameter PRBS31_ENABLE = 0, + parameter SERDES_PIPELINE = 0 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded interface + */ + input wire [DATA_WIDTH-1:0] encoded_tx_data, + input wire [HDR_WIDTH-1:0] encoded_tx_hdr, + + /* + * SERDES interface + */ + output wire [DATA_WIDTH-1:0] serdes_tx_data, + output wire [HDR_WIDTH-1:0] serdes_tx_hdr, + + /* + * Configuration + */ + input wire tx_prbs31_enable +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +reg [57:0] scrambler_state_reg = {58{1'b1}}; +wire [57:0] scrambler_state; +wire [DATA_WIDTH-1:0] scrambled_data; + +reg [30:0] prbs31_state_reg = 31'h7fffffff; +wire [30:0] prbs31_state; +wire [DATA_WIDTH+HDR_WIDTH-1:0] prbs31_data; + +reg [DATA_WIDTH-1:0] serdes_tx_data_reg = {DATA_WIDTH{1'b0}}; +reg [HDR_WIDTH-1:0] serdes_tx_hdr_reg = {HDR_WIDTH{1'b0}}; + +wire [DATA_WIDTH-1:0] serdes_tx_data_int; +wire [HDR_WIDTH-1:0] serdes_tx_hdr_int; + +generate + genvar n; + + if (BIT_REVERSE) begin + for (n = 0; n < DATA_WIDTH; n = n + 1) begin + assign serdes_tx_data_int[n] = serdes_tx_data_reg[DATA_WIDTH-n-1]; + end + + for (n = 0; n < HDR_WIDTH; n = n + 1) begin + assign serdes_tx_hdr_int[n] = serdes_tx_hdr_reg[HDR_WIDTH-n-1]; + end + end else begin + assign serdes_tx_data_int = serdes_tx_data_reg; + assign serdes_tx_hdr_int = serdes_tx_hdr_reg; + end + + if (SERDES_PIPELINE > 0) begin + (* srl_style = "register" *) + reg [DATA_WIDTH-1:0] serdes_tx_data_pipe_reg[SERDES_PIPELINE-1:0]; + (* srl_style = "register" *) + reg [HDR_WIDTH-1:0] serdes_tx_hdr_pipe_reg[SERDES_PIPELINE-1:0]; + + for (n = 0; n < SERDES_PIPELINE; n = n + 1) begin + initial begin + serdes_tx_data_pipe_reg[n] <= {DATA_WIDTH{1'b0}}; + serdes_tx_hdr_pipe_reg[n] <= {HDR_WIDTH{1'b0}}; + end + + always @(posedge clk) begin + serdes_tx_data_pipe_reg[n] <= n == 0 ? serdes_tx_data_int : serdes_tx_data_pipe_reg[n-1]; + serdes_tx_hdr_pipe_reg[n] <= n == 0 ? serdes_tx_hdr_int : serdes_tx_hdr_pipe_reg[n-1]; + end + end + + assign serdes_tx_data = serdes_tx_data_pipe_reg[SERDES_PIPELINE-1]; + assign serdes_tx_hdr = serdes_tx_hdr_pipe_reg[SERDES_PIPELINE-1]; + end else begin + assign serdes_tx_data = serdes_tx_data_int; + assign serdes_tx_hdr = serdes_tx_hdr_int; + end + +endgenerate + +lfsr #( + .LFSR_WIDTH(58), + .LFSR_POLY(58'h8000000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH), + .STYLE("AUTO") +) +scrambler_inst ( + .data_in(encoded_tx_data), + .state_in(scrambler_state_reg), + .data_out(scrambled_data), + .state_out(scrambler_state) +); + +lfsr #( + .LFSR_WIDTH(31), + .LFSR_POLY(31'h10000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(0), + .REVERSE(1), + .DATA_WIDTH(DATA_WIDTH+HDR_WIDTH), + .STYLE("AUTO") +) +prbs31_gen_inst ( + .data_in({DATA_WIDTH+HDR_WIDTH{1'b0}}), + .state_in(prbs31_state_reg), + .data_out(prbs31_data), + .state_out(prbs31_state) +); + +always @(posedge clk) begin + scrambler_state_reg <= scrambler_state; + + if (PRBS31_ENABLE && tx_prbs31_enable) begin + prbs31_state_reg <= prbs31_state; + + serdes_tx_data_reg <= ~prbs31_data[DATA_WIDTH+HDR_WIDTH-1:HDR_WIDTH]; + serdes_tx_hdr_reg <= ~prbs31_data[HDR_WIDTH-1:0]; + end else begin + serdes_tx_data_reg <= SCRAMBLER_DISABLE ? encoded_tx_data : scrambled_data; + serdes_tx_hdr_reg <= encoded_tx_hdr; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/gmii_phy_if.v b/corundum/lib/eth/rtl/gmii_phy_if.v new file mode 100644 index 0000000000000000000000000000000000000000..be6ea4ec34951714a5afd3612e43fe9b7ec505d7 --- /dev/null +++ b/corundum/lib/eth/rtl/gmii_phy_if.v @@ -0,0 +1,148 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * GMII PHY interface + */ +module gmii_phy_if # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2" +) +( + input wire clk, + input wire rst, + + /* + * GMII interface to MAC + */ + output wire mac_gmii_rx_clk, + output wire mac_gmii_rx_rst, + output wire [7:0] mac_gmii_rxd, + output wire mac_gmii_rx_dv, + output wire mac_gmii_rx_er, + output wire mac_gmii_tx_clk, + output wire mac_gmii_tx_rst, + input wire [7:0] mac_gmii_txd, + input wire mac_gmii_tx_en, + input wire mac_gmii_tx_er, + + /* + * GMII interface to PHY + */ + input wire phy_gmii_rx_clk, + input wire [7:0] phy_gmii_rxd, + input wire phy_gmii_rx_dv, + input wire phy_gmii_rx_er, + input wire phy_mii_tx_clk, + output wire phy_gmii_tx_clk, + output wire [7:0] phy_gmii_txd, + output wire phy_gmii_tx_en, + output wire phy_gmii_tx_er, + + /* + * Control + */ + input wire mii_select +); + +ssio_sdr_in # +( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(10) +) +rx_ssio_sdr_inst ( + .input_clk(phy_gmii_rx_clk), + .input_d({phy_gmii_rxd, phy_gmii_rx_dv, phy_gmii_rx_er}), + .output_clk(mac_gmii_rx_clk), + .output_q({mac_gmii_rxd, mac_gmii_rx_dv, mac_gmii_rx_er}) +); + +ssio_sdr_out # +( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(10) +) +tx_ssio_sdr_inst ( + .clk(mac_gmii_tx_clk), + .input_d({mac_gmii_txd, mac_gmii_tx_en, mac_gmii_tx_er}), + .output_clk(phy_gmii_tx_clk), + .output_q({phy_gmii_txd, phy_gmii_tx_en, phy_gmii_tx_er}) +); + +generate + +if (TARGET == "XILINX") begin + BUFGMUX + gmii_bufgmux_inst ( + .I0(clk), + .I1(phy_mii_tx_clk), + .S(mii_select), + .O(mac_gmii_tx_clk) + ); +end else begin + assign mac_gmii_tx_clk = mii_select ? phy_mii_tx_clk : clk; +end + +endgenerate + +// reset sync +reg [3:0] tx_rst_reg = 4'hf; +assign mac_gmii_tx_rst = tx_rst_reg[0]; + +always @(posedge mac_gmii_tx_clk or posedge rst) begin + if (rst) begin + tx_rst_reg <= 4'hf; + end else begin + tx_rst_reg <= {1'b0, tx_rst_reg[3:1]}; + end +end + +reg [3:0] rx_rst_reg = 4'hf; +assign mac_gmii_rx_rst = rx_rst_reg[0]; + +always @(posedge mac_gmii_rx_clk or posedge rst) begin + if (rst) begin + rx_rst_reg <= 4'hf; + end else begin + rx_rst_reg <= {1'b0, rx_rst_reg[3:1]}; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/iddr.v b/corundum/lib/eth/rtl/iddr.v new file mode 100644 index 0000000000000000000000000000000000000000..473a06d3574cced78808c66bd57c6a815846f7da --- /dev/null +++ b/corundum/lib/eth/rtl/iddr.v @@ -0,0 +1,151 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic IDDR module + */ +module iddr # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] d, + + output wire [WIDTH-1:0] q1, + output wire [WIDTH-1:0] q2 +); + +/* + +Provides a consistent input DDR flip flop across multiple FPGA families + _____ _____ _____ _____ ____ + clk ____/ \_____/ \_____/ \_____/ \_____/ + _ _____ _____ _____ _____ _____ _____ _____ _____ _____ _ + d _X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7__X_D8__X_ + _______ ___________ ___________ ___________ ___________ _ + q1 _______X___________X____D0_____X____D2_____X____D4_____X_ + _______ ___________ ___________ ___________ ___________ _ + q2 _______X___________X____D1_____X____D3_____X____D5_____X_ + +*/ + +genvar n; + +generate + +if (TARGET == "XILINX") begin + for (n = 0; n < WIDTH; n = n + 1) begin : iddr + if (IODDR_STYLE == "IODDR") begin + IDDR #( + .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), + .SRTYPE("ASYNC") + ) + iddr_inst ( + .Q1(q1[n]), + .Q2(q2[n]), + .C(clk), + .CE(1'b1), + .D(d[n]), + .R(1'b0), + .S(1'b0) + ); + end else if (IODDR_STYLE == "IODDR2") begin + IDDR2 #( + .DDR_ALIGNMENT("C0") + ) + iddr_inst ( + .Q0(q1[n]), + .Q1(q2[n]), + .C0(clk), + .C1(~clk), + .CE(1'b1), + .D(d[n]), + .R(1'b0), + .S(1'b0) + ); + end + end +end else if (TARGET == "ALTERA") begin + wire [WIDTH-1:0] q1_int; + reg [WIDTH-1:0] q1_delay; + + altddio_in #( + .WIDTH(WIDTH), + .POWER_UP_HIGH("OFF") + ) + altddio_in_inst ( + .aset(1'b0), + .datain(d), + .inclocken(1'b1), + .inclock(clk), + .aclr(1'b0), + .dataout_h(q1_int), + .dataout_l(q2) + ); + + always @(posedge clk) begin + q1_delay <= q1_int; + end + + assign q1 = q1_delay; +end else begin + reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}}; + reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}}; + + reg [WIDTH-1:0] q_reg_1 = {WIDTH{1'b0}}; + reg [WIDTH-1:0] q_reg_2 = {WIDTH{1'b0}}; + + always @(posedge clk) begin + d_reg_1 <= d; + end + + always @(negedge clk) begin + d_reg_2 <= d; + end + + always @(posedge clk) begin + q_reg_1 <= d_reg_1; + q_reg_2 <= d_reg_2; + end + + assign q1 = q_reg_1; + assign q2 = q_reg_2; +end + +endgenerate + +endmodule diff --git a/corundum/lib/eth/rtl/ip.v b/corundum/lib/eth/rtl/ip.v new file mode 100644 index 0000000000000000000000000000000000000000..6c884ce3531dc87b0473c6c1c8ea61d79c2309a1 --- /dev/null +++ b/corundum/lib/eth/rtl/ip.v @@ -0,0 +1,341 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IPv4 block, ethernet frame interface + */ +module ip +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * ARP requests + */ + output wire arp_request_valid, + input wire arp_request_ready, + output wire [31:0] arp_request_ip, + input wire arp_response_valid, + output wire arp_response_ready, + input wire arp_response_error, + input wire [47:0] arp_response_mac, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_ARP_QUERY = 2'd1, + STATE_WAIT_PACKET = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg outgoing_ip_hdr_valid_reg = 1'b0, outgoing_ip_hdr_valid_next; +wire outgoing_ip_hdr_ready; +reg [47:0] outgoing_eth_dest_mac_reg = 48'h000000000000, outgoing_eth_dest_mac_next; +wire outgoing_ip_payload_axis_tready; + +/* + * IP frame processing + */ +ip_eth_rx +ip_eth_rx_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination), + .error_invalid_header(rx_error_invalid_header), + .error_invalid_checksum(rx_error_invalid_checksum) +); + +ip_eth_tx +ip_eth_tx_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(outgoing_ip_hdr_valid_reg), + .s_ip_hdr_ready(outgoing_ip_hdr_ready), + .s_eth_dest_mac(outgoing_eth_dest_mac_reg), + .s_eth_src_mac(local_mac), + .s_eth_type(16'h0800), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(16'd0), + .s_ip_flags(3'b010), + .s_ip_fragment_offset(13'd0), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(outgoing_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; + +reg arp_request_valid_reg = 1'b0, arp_request_valid_next; + +reg arp_response_ready_reg = 1'b0, arp_response_ready_next; + +reg drop_packet_reg = 1'b0, drop_packet_next; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = outgoing_ip_payload_axis_tready || drop_packet_reg; + +assign arp_request_valid = arp_request_valid_reg; +assign arp_request_ip = s_ip_dest_ip; +assign arp_response_ready = arp_response_ready_reg; + +assign tx_error_arp_failed = arp_response_error; + +always @* begin + state_next = STATE_IDLE; + + arp_request_valid_next = arp_request_valid_reg && !arp_request_ready; + arp_response_ready_next = 1'b0; + drop_packet_next = 1'b0; + + s_ip_hdr_ready_next = 1'b0; + + outgoing_ip_hdr_valid_next = outgoing_ip_hdr_valid_reg && !outgoing_ip_hdr_ready; + outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg; + + case (state_reg) + STATE_IDLE: begin + // wait for outgoing packet + if (s_ip_hdr_valid) begin + // initiate ARP request + arp_request_valid_next = 1'b1; + arp_response_ready_next = 1'b1; + state_next = STATE_ARP_QUERY; + end else begin + state_next = STATE_IDLE; + end + end + STATE_ARP_QUERY: begin + arp_response_ready_next = 1'b1; + + if (arp_response_valid) begin + // wait for ARP reponse + if (arp_response_error) begin + // did not get MAC address; drop packet + s_ip_hdr_ready_next = 1'b1; + drop_packet_next = 1'b1; + state_next = STATE_WAIT_PACKET; + end else begin + // got MAC address; send packet + s_ip_hdr_ready_next = 1'b1; + outgoing_ip_hdr_valid_next = 1'b1; + outgoing_eth_dest_mac_next = arp_response_mac; + state_next = STATE_WAIT_PACKET; + end + end else begin + state_next = STATE_ARP_QUERY; + end + end + STATE_WAIT_PACKET: begin + drop_packet_next = drop_packet_reg; + + // wait for packet transfer to complete + if (s_ip_payload_axis_tlast && s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_PACKET; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + arp_request_valid_reg <= 1'b0; + arp_response_ready_reg <= 1'b0; + drop_packet_reg <= 1'b0; + s_ip_hdr_ready_reg <= 1'b0; + outgoing_ip_hdr_valid_reg <= 1'b0; + end else begin + state_reg <= state_next; + + arp_request_valid_reg <= arp_request_valid_next; + arp_response_ready_reg <= arp_response_ready_next; + drop_packet_reg <= drop_packet_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + + outgoing_ip_hdr_valid_reg <= outgoing_ip_hdr_valid_next; + end + + outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_64.v b/corundum/lib/eth/rtl/ip_64.v new file mode 100644 index 0000000000000000000000000000000000000000..be1464f4ad8dbcfc714e88a8da94b1c2af0b462f --- /dev/null +++ b/corundum/lib/eth/rtl/ip_64.v @@ -0,0 +1,349 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IPv4 block, ethernet frame interface (64 bit datapath) + */ +module ip_64 +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * ARP requests + */ + output wire arp_request_valid, + input wire arp_request_ready, + output wire [31:0] arp_request_ip, + input wire arp_response_valid, + output wire arp_response_ready, + input wire arp_response_error, + input wire [47:0] arp_response_mac, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip +); + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_ARP_QUERY = 2'd1, + STATE_WAIT_PACKET = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +reg outgoing_ip_hdr_valid_reg = 1'b0, outgoing_ip_hdr_valid_next; +wire outgoing_ip_hdr_ready; +reg [47:0] outgoing_eth_dest_mac_reg = 48'h000000000000, outgoing_eth_dest_mac_next; +wire outgoing_ip_payload_axis_tready; + +/* + * IP frame processing + */ +ip_eth_rx_64 +ip_eth_rx_64_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination), + .error_invalid_header(rx_error_invalid_header), + .error_invalid_checksum(rx_error_invalid_checksum) +); + +ip_eth_tx_64 +ip_eth_tx_64_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(outgoing_ip_hdr_valid_reg), + .s_ip_hdr_ready(outgoing_ip_hdr_ready), + .s_eth_dest_mac(outgoing_eth_dest_mac_reg), + .s_eth_src_mac(local_mac), + .s_eth_type(16'h0800), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(16'd0), + .s_ip_flags(3'b010), + .s_ip_fragment_offset(13'd0), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(outgoing_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; + +reg arp_request_valid_reg = 1'b0, arp_request_valid_next; + +reg arp_response_ready_reg = 1'b0, arp_response_ready_next; + +reg drop_packet_reg = 1'b0, drop_packet_next; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = outgoing_ip_payload_axis_tready || drop_packet_reg; + +assign arp_request_valid = arp_request_valid_reg; +assign arp_request_ip = s_ip_dest_ip; +assign arp_response_ready = arp_response_ready_reg; + +assign tx_error_arp_failed = arp_response_error; + +always @* begin + state_next = STATE_IDLE; + + arp_request_valid_next = arp_request_valid_reg && !arp_request_ready; + arp_response_ready_next = 1'b0; + drop_packet_next = 1'b0; + + s_ip_hdr_ready_next = 1'b0; + + outgoing_ip_hdr_valid_next = outgoing_ip_hdr_valid_reg && !outgoing_ip_hdr_ready; + outgoing_eth_dest_mac_next = outgoing_eth_dest_mac_reg; + + case (state_reg) + STATE_IDLE: begin + // wait for outgoing packet + if (s_ip_hdr_valid) begin + // initiate ARP request + arp_request_valid_next = 1'b1; + arp_response_ready_next = 1'b1; + state_next = STATE_ARP_QUERY; + end else begin + state_next = STATE_IDLE; + end + end + STATE_ARP_QUERY: begin + arp_response_ready_next = 1'b1; + + if (arp_response_valid) begin + // wait for ARP reponse + if (arp_response_error) begin + // did not get MAC address; drop packet + s_ip_hdr_ready_next = 1'b1; + drop_packet_next = 1'b1; + state_next = STATE_WAIT_PACKET; + end else begin + // got MAC address; send packet + s_ip_hdr_ready_next = 1'b1; + outgoing_ip_hdr_valid_next = 1'b1; + outgoing_eth_dest_mac_next = arp_response_mac; + state_next = STATE_WAIT_PACKET; + end + end else begin + state_next = STATE_ARP_QUERY; + end + end + STATE_WAIT_PACKET: begin + drop_packet_next = drop_packet_reg; + + // wait for packet transfer to complete + if (s_ip_payload_axis_tlast && s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_PACKET; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + arp_request_valid_reg <= 1'b0; + arp_response_ready_reg <= 1'b0; + drop_packet_reg <= 1'b0; + s_ip_hdr_ready_reg <= 1'b0; + outgoing_ip_hdr_valid_reg <= 1'b0; + end else begin + state_reg <= state_next; + + arp_request_valid_reg <= arp_request_valid_next; + arp_response_ready_reg <= arp_response_ready_next; + drop_packet_reg <= drop_packet_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + + outgoing_ip_hdr_valid_reg <= outgoing_ip_hdr_valid_next; + end + + outgoing_eth_dest_mac_reg <= outgoing_eth_dest_mac_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_arb_mux.v b/corundum/lib/eth/rtl/ip_arb_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..7e99ece312902590042d5eab44e6bb0e5ca38631 --- /dev/null +++ b/corundum/lib/eth/rtl/ip_arb_mux.v @@ -0,0 +1,401 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IP arbitrated multiplexer + */ +module ip_arb_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * IP frame inputs + */ + input wire [S_COUNT-1:0] s_ip_hdr_valid, + output wire [S_COUNT-1:0] s_ip_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_ip_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_ip_payload_axis_tready, + input wire [S_COUNT-1:0] s_ip_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_ip_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg frame_reg = 1'b0, frame_next; + +reg s_ip_hdr_ready_mask_reg = 1'b0, s_ip_hdr_ready_mask_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = (!s_ip_hdr_ready_mask_reg && grant_valid) << grant_encoded; + +assign s_ip_payload_axis_tready = (m_ip_payload_axis_tready_int_reg && grant_valid) << grant_encoded; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_ip_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_ip_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_ip_payload_axis_tvalid[grant_encoded]; +wire current_s_tready = s_ip_payload_axis_tready[grant_encoded]; +wire current_s_tlast = s_ip_payload_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_ip_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_ip_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_ip_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_ip_hdr_valid & ~grant; +assign acknowledge = grant & s_ip_payload_axis_tvalid & s_ip_payload_axis_tready & s_ip_payload_axis_tlast; + +always @* begin + frame_next = frame_reg; + + s_ip_hdr_ready_mask_next = s_ip_hdr_ready_mask_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + + if (s_ip_payload_axis_tvalid[grant_encoded] && s_ip_payload_axis_tready[grant_encoded]) begin + // end of frame detection + if (s_ip_payload_axis_tlast[grant_encoded]) begin + frame_next = 1'b0; + s_ip_hdr_ready_mask_next = 1'b0; + end + end + + if (!frame_reg && grant_valid) begin + // start of frame + frame_next = 1'b1; + + s_ip_hdr_ready_mask_next = 1'b1; + + m_ip_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48]; + m_eth_type_next = s_eth_type[grant_encoded*16 +: 16]; + m_ip_version_next = s_ip_version[grant_encoded*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[grant_encoded*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[grant_encoded*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[grant_encoded*2 +: 2]; + m_ip_length_next = s_ip_length[grant_encoded*16 +: 16]; + m_ip_identification_next = s_ip_identification[grant_encoded*16 +: 16]; + m_ip_flags_next = s_ip_flags[grant_encoded*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[grant_encoded*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[grant_encoded*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[grant_encoded*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[grant_encoded*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[grant_encoded*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[grant_encoded*32 +: 32]; + end + + // pass through selected packet data + m_ip_payload_axis_tdata_int = current_s_tdata; + m_ip_payload_axis_tkeep_int = current_s_tkeep; + m_ip_payload_axis_tvalid_int = current_s_tvalid && m_ip_payload_axis_tready_int_reg && grant_valid; + m_ip_payload_axis_tlast_int = current_s_tlast; + m_ip_payload_axis_tid_int = current_s_tid; + m_ip_payload_axis_tdest_int = current_s_tdest; + m_ip_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + frame_reg <= 1'b0; + s_ip_hdr_ready_mask_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + end else begin + frame_reg <= frame_next; + s_ip_hdr_ready_mask_reg <= s_ip_hdr_ready_mask_next; + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? m_ip_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tid = ID_ENABLE ? m_ip_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_ip_payload_axis_tdest = DEST_ENABLE ? m_ip_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_ip_payload_axis_tuser = USER_ENABLE ? m_ip_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg; + m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_complete.v b/corundum/lib/eth/rtl/ip_complete.v new file mode 100644 index 0000000000000000000000000000000000000000..f16d697b3fea8ce9f5eba995b95f6f33f355c7db --- /dev/null +++ b/corundum/lib/eth/rtl/ip_complete.v @@ -0,0 +1,440 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IPv4 and ARP block, ethernet frame interface + */ +module ip_complete #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2, + parameter ARP_REQUEST_TIMEOUT = 125000000*30 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +/* + +This module integrates the IP and ARP modules for a complete IP stack + +*/ + +wire arp_request_valid; +wire arp_request_ready; +wire [31:0] arp_request_ip; +wire arp_response_valid; +wire arp_response_ready; +wire arp_response_error; +wire [47:0] arp_response_mac; + +wire ip_rx_eth_hdr_valid; +wire ip_rx_eth_hdr_ready; +wire [47:0] ip_rx_eth_dest_mac; +wire [47:0] ip_rx_eth_src_mac; +wire [15:0] ip_rx_eth_type; +wire [7:0] ip_rx_eth_payload_axis_tdata; +wire ip_rx_eth_payload_axis_tvalid; +wire ip_rx_eth_payload_axis_tready; +wire ip_rx_eth_payload_axis_tlast; +wire ip_rx_eth_payload_axis_tuser; + +wire ip_tx_eth_hdr_valid; +wire ip_tx_eth_hdr_ready; +wire [47:0] ip_tx_eth_dest_mac; +wire [47:0] ip_tx_eth_src_mac; +wire [15:0] ip_tx_eth_type; +wire [7:0] ip_tx_eth_payload_axis_tdata; +wire ip_tx_eth_payload_axis_tvalid; +wire ip_tx_eth_payload_axis_tready; +wire ip_tx_eth_payload_axis_tlast; +wire ip_tx_eth_payload_axis_tuser; + +wire arp_rx_eth_hdr_valid; +wire arp_rx_eth_hdr_ready; +wire [47:0] arp_rx_eth_dest_mac; +wire [47:0] arp_rx_eth_src_mac; +wire [15:0] arp_rx_eth_type; +wire [7:0] arp_rx_eth_payload_axis_tdata; +wire arp_rx_eth_payload_axis_tvalid; +wire arp_rx_eth_payload_axis_tready; +wire arp_rx_eth_payload_axis_tlast; +wire arp_rx_eth_payload_axis_tuser; + +wire arp_tx_eth_hdr_valid; +wire arp_tx_eth_hdr_ready; +wire [47:0] arp_tx_eth_dest_mac; +wire [47:0] arp_tx_eth_src_mac; +wire [15:0] arp_tx_eth_type; +wire [7:0] arp_tx_eth_payload_axis_tdata; +wire arp_tx_eth_payload_axis_tvalid; +wire arp_tx_eth_payload_axis_tready; +wire arp_tx_eth_payload_axis_tlast; +wire arp_tx_eth_payload_axis_tuser; + +/* + * Input classifier (eth_type) + */ +wire s_select_ip = (s_eth_type == 16'h0800); +wire s_select_arp = (s_eth_type == 16'h0806); +wire s_select_none = !(s_select_ip || s_select_arp); + +reg s_select_ip_reg = 1'b0; +reg s_select_arp_reg = 1'b0; +reg s_select_none_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end else begin + if (s_eth_payload_axis_tvalid) begin + if ((!s_select_ip_reg && !s_select_arp_reg && !s_select_none_reg) || + (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && s_eth_payload_axis_tlast)) begin + s_select_ip_reg <= s_select_ip; + s_select_arp_reg <= s_select_arp; + s_select_none_reg <= s_select_none; + end + end else begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end + end +end + +assign ip_rx_eth_hdr_valid = s_select_ip && s_eth_hdr_valid; +assign ip_rx_eth_dest_mac = s_eth_dest_mac; +assign ip_rx_eth_src_mac = s_eth_src_mac; +assign ip_rx_eth_type = 16'h0800; +assign ip_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign ip_rx_eth_payload_axis_tvalid = s_select_ip_reg && s_eth_payload_axis_tvalid; +assign ip_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign ip_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign arp_rx_eth_hdr_valid = s_select_arp && s_eth_hdr_valid; +assign arp_rx_eth_dest_mac = s_eth_dest_mac; +assign arp_rx_eth_src_mac = s_eth_src_mac; +assign arp_rx_eth_type = 16'h0806; +assign arp_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign arp_rx_eth_payload_axis_tvalid = s_select_arp_reg && s_eth_payload_axis_tvalid; +assign arp_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign arp_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign s_eth_hdr_ready = (s_select_ip && ip_rx_eth_hdr_ready) || + (s_select_arp && arp_rx_eth_hdr_ready) || + (s_select_none); + +assign s_eth_payload_axis_tready = (s_select_ip_reg && ip_rx_eth_payload_axis_tready) || + (s_select_arp_reg && arp_rx_eth_payload_axis_tready) || + s_select_none_reg; + +/* + * Output arbiter + */ +eth_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +eth_arb_mux_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid({ip_tx_eth_hdr_valid, arp_tx_eth_hdr_valid}), + .s_eth_hdr_ready({ip_tx_eth_hdr_ready, arp_tx_eth_hdr_ready}), + .s_eth_dest_mac({ip_tx_eth_dest_mac, arp_tx_eth_dest_mac}), + .s_eth_src_mac({ip_tx_eth_src_mac, arp_tx_eth_src_mac}), + .s_eth_type({ip_tx_eth_type, arp_tx_eth_type}), + .s_eth_payload_axis_tdata({ip_tx_eth_payload_axis_tdata, arp_tx_eth_payload_axis_tdata}), + .s_eth_payload_axis_tkeep(0), + .s_eth_payload_axis_tvalid({ip_tx_eth_payload_axis_tvalid, arp_tx_eth_payload_axis_tvalid}), + .s_eth_payload_axis_tready({ip_tx_eth_payload_axis_tready, arp_tx_eth_payload_axis_tready}), + .s_eth_payload_axis_tlast({ip_tx_eth_payload_axis_tlast, arp_tx_eth_payload_axis_tlast}), + .s_eth_payload_axis_tid(0), + .s_eth_payload_axis_tdest(0), + .s_eth_payload_axis_tuser({ip_tx_eth_payload_axis_tuser, arp_tx_eth_payload_axis_tuser}), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(), + .m_eth_payload_axis_tdest(), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser) +); + +/* + * IP module + */ +ip +ip_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(ip_rx_eth_hdr_valid), + .s_eth_hdr_ready(ip_rx_eth_hdr_ready), + .s_eth_dest_mac(ip_rx_eth_dest_mac), + .s_eth_src_mac(ip_rx_eth_src_mac), + .s_eth_type(ip_rx_eth_type), + .s_eth_payload_axis_tdata(ip_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(ip_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(ip_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(ip_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(ip_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(ip_tx_eth_hdr_valid), + .m_eth_hdr_ready(ip_tx_eth_hdr_ready), + .m_eth_dest_mac(ip_tx_eth_dest_mac), + .m_eth_src_mac(ip_tx_eth_src_mac), + .m_eth_type(ip_tx_eth_type), + .m_eth_payload_axis_tdata(ip_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(ip_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(ip_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(ip_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(ip_tx_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Status + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip) +); + +/* + * ARP module + */ +arp #( + .CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +arp_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(arp_rx_eth_hdr_valid), + .s_eth_hdr_ready(arp_rx_eth_hdr_ready), + .s_eth_dest_mac(arp_rx_eth_dest_mac), + .s_eth_src_mac(arp_rx_eth_src_mac), + .s_eth_type(arp_rx_eth_type), + .s_eth_payload_axis_tdata(arp_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(arp_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(arp_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(arp_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(arp_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(arp_tx_eth_hdr_valid), + .m_eth_hdr_ready(arp_tx_eth_hdr_ready), + .m_eth_dest_mac(arp_tx_eth_dest_mac), + .m_eth_src_mac(arp_tx_eth_src_mac), + .m_eth_type(arp_tx_eth_type), + .m_eth_payload_axis_tdata(arp_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(arp_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(arp_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(arp_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(arp_tx_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_cache(clear_arp_cache) +); + +endmodule diff --git a/corundum/lib/eth/rtl/ip_complete_64.v b/corundum/lib/eth/rtl/ip_complete_64.v new file mode 100644 index 0000000000000000000000000000000000000000..3dc4f03a413020c9127f38c2eb41087f11a67ba2 --- /dev/null +++ b/corundum/lib/eth/rtl/ip_complete_64.v @@ -0,0 +1,459 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IPv4 and ARP block, ethernet frame interface (64 bit datapath) + */ +module ip_complete_64 #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 156250000*2, + parameter ARP_REQUEST_TIMEOUT = 156250000*30 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire rx_error_invalid_header, + output wire rx_error_invalid_checksum, + output wire tx_error_payload_early_termination, + output wire tx_error_arp_failed, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +/* + +This module integrates the IP and ARP modules for a complete IP stack + +*/ + +wire arp_request_valid; +wire arp_request_ready; +wire [31:0] arp_request_ip; +wire arp_response_valid; +wire arp_response_ready; +wire arp_response_error; +wire [47:0] arp_response_mac; + +wire ip_rx_eth_hdr_valid; +wire ip_rx_eth_hdr_ready; +wire [47:0] ip_rx_eth_dest_mac; +wire [47:0] ip_rx_eth_src_mac; +wire [15:0] ip_rx_eth_type; +wire [63:0] ip_rx_eth_payload_axis_tdata; +wire [7:0] ip_rx_eth_payload_axis_tkeep; +wire ip_rx_eth_payload_axis_tvalid; +wire ip_rx_eth_payload_axis_tready; +wire ip_rx_eth_payload_axis_tlast; +wire ip_rx_eth_payload_axis_tuser; + +wire ip_tx_eth_hdr_valid; +wire ip_tx_eth_hdr_ready; +wire [47:0] ip_tx_eth_dest_mac; +wire [47:0] ip_tx_eth_src_mac; +wire [15:0] ip_tx_eth_type; +wire [63:0] ip_tx_eth_payload_axis_tdata; +wire [7:0] ip_tx_eth_payload_axis_tkeep; +wire ip_tx_eth_payload_axis_tvalid; +wire ip_tx_eth_payload_axis_tready; +wire ip_tx_eth_payload_axis_tlast; +wire ip_tx_eth_payload_axis_tuser; + +wire arp_rx_eth_hdr_valid; +wire arp_rx_eth_hdr_ready; +wire [47:0] arp_rx_eth_dest_mac; +wire [47:0] arp_rx_eth_src_mac; +wire [15:0] arp_rx_eth_type; +wire [63:0] arp_rx_eth_payload_axis_tdata; +wire [7:0] arp_rx_eth_payload_axis_tkeep; +wire arp_rx_eth_payload_axis_tvalid; +wire arp_rx_eth_payload_axis_tready; +wire arp_rx_eth_payload_axis_tlast; +wire arp_rx_eth_payload_axis_tuser; + +wire arp_tx_eth_hdr_valid; +wire arp_tx_eth_hdr_ready; +wire [47:0] arp_tx_eth_dest_mac; +wire [47:0] arp_tx_eth_src_mac; +wire [15:0] arp_tx_eth_type; +wire [63:0] arp_tx_eth_payload_axis_tdata; +wire [7:0] arp_tx_eth_payload_axis_tkeep; +wire arp_tx_eth_payload_axis_tvalid; +wire arp_tx_eth_payload_axis_tready; +wire arp_tx_eth_payload_axis_tlast; +wire arp_tx_eth_payload_axis_tuser; + +/* + * Input classifier (eth_type) + */ +wire s_select_ip = (s_eth_type == 16'h0800); +wire s_select_arp = (s_eth_type == 16'h0806); +wire s_select_none = !(s_select_ip || s_select_arp); + +reg s_select_ip_reg = 1'b0; +reg s_select_arp_reg = 1'b0; +reg s_select_none_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end else begin + if (s_eth_payload_axis_tvalid) begin + if ((!s_select_ip_reg && !s_select_arp_reg && !s_select_none_reg) || + (s_eth_payload_axis_tvalid && s_eth_payload_axis_tready && s_eth_payload_axis_tlast)) begin + s_select_ip_reg <= s_select_ip; + s_select_arp_reg <= s_select_arp; + s_select_none_reg <= s_select_none; + end + end else begin + s_select_ip_reg <= 1'b0; + s_select_arp_reg <= 1'b0; + s_select_none_reg <= 1'b0; + end + end +end + +assign ip_rx_eth_hdr_valid = s_select_ip && s_eth_hdr_valid; +assign ip_rx_eth_dest_mac = s_eth_dest_mac; +assign ip_rx_eth_src_mac = s_eth_src_mac; +assign ip_rx_eth_type = 16'h0800; +assign ip_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign ip_rx_eth_payload_axis_tkeep = s_eth_payload_axis_tkeep; +assign ip_rx_eth_payload_axis_tvalid = s_select_ip_reg && s_eth_payload_axis_tvalid; +assign ip_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign ip_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign arp_rx_eth_hdr_valid = s_select_arp && s_eth_hdr_valid; +assign arp_rx_eth_dest_mac = s_eth_dest_mac; +assign arp_rx_eth_src_mac = s_eth_src_mac; +assign arp_rx_eth_type = 16'h0806; +assign arp_rx_eth_payload_axis_tdata = s_eth_payload_axis_tdata; +assign arp_rx_eth_payload_axis_tkeep = s_eth_payload_axis_tkeep; +assign arp_rx_eth_payload_axis_tvalid = s_select_arp_reg && s_eth_payload_axis_tvalid; +assign arp_rx_eth_payload_axis_tlast = s_eth_payload_axis_tlast; +assign arp_rx_eth_payload_axis_tuser = s_eth_payload_axis_tuser; + +assign s_eth_hdr_ready = (s_select_ip && ip_rx_eth_hdr_ready) || + (s_select_arp && arp_rx_eth_hdr_ready) || + (s_select_none); + +assign s_eth_payload_axis_tready = (s_select_ip_reg && ip_rx_eth_payload_axis_tready) || + (s_select_arp_reg && arp_rx_eth_payload_axis_tready) || + s_select_none_reg; + +/* + * Output arbiter + */ +eth_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +eth_arb_mux_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame inputs + .s_eth_hdr_valid({ip_tx_eth_hdr_valid, arp_tx_eth_hdr_valid}), + .s_eth_hdr_ready({ip_tx_eth_hdr_ready, arp_tx_eth_hdr_ready}), + .s_eth_dest_mac({ip_tx_eth_dest_mac, arp_tx_eth_dest_mac}), + .s_eth_src_mac({ip_tx_eth_src_mac, arp_tx_eth_src_mac}), + .s_eth_type({ip_tx_eth_type, arp_tx_eth_type}), + .s_eth_payload_axis_tdata({ip_tx_eth_payload_axis_tdata, arp_tx_eth_payload_axis_tdata}), + .s_eth_payload_axis_tkeep({ip_tx_eth_payload_axis_tkeep, arp_tx_eth_payload_axis_tkeep}), + .s_eth_payload_axis_tvalid({ip_tx_eth_payload_axis_tvalid, arp_tx_eth_payload_axis_tvalid}), + .s_eth_payload_axis_tready({ip_tx_eth_payload_axis_tready, arp_tx_eth_payload_axis_tready}), + .s_eth_payload_axis_tlast({ip_tx_eth_payload_axis_tlast, arp_tx_eth_payload_axis_tlast}), + .s_eth_payload_axis_tid(0), + .s_eth_payload_axis_tdest(0), + .s_eth_payload_axis_tuser({ip_tx_eth_payload_axis_tuser, arp_tx_eth_payload_axis_tuser}), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tid(), + .m_eth_payload_axis_tdest(), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser) +); + +/* + * IP module + */ +ip_64 +ip_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(ip_rx_eth_hdr_valid), + .s_eth_hdr_ready(ip_rx_eth_hdr_ready), + .s_eth_dest_mac(ip_rx_eth_dest_mac), + .s_eth_src_mac(ip_rx_eth_src_mac), + .s_eth_type(ip_rx_eth_type), + .s_eth_payload_axis_tdata(ip_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(ip_rx_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(ip_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(ip_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(ip_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(ip_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(ip_tx_eth_hdr_valid), + .m_eth_hdr_ready(ip_tx_eth_hdr_ready), + .m_eth_dest_mac(ip_tx_eth_dest_mac), + .m_eth_src_mac(ip_tx_eth_src_mac), + .m_eth_type(ip_tx_eth_type), + .m_eth_payload_axis_tdata(ip_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(ip_tx_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(ip_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(ip_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(ip_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(ip_tx_eth_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_ip_eth_dest_mac(m_ip_eth_dest_mac), + .m_ip_eth_src_mac(m_ip_eth_src_mac), + .m_ip_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Status + .rx_busy(rx_busy), + .tx_busy(tx_busy), + .rx_error_header_early_termination(rx_error_header_early_termination), + .rx_error_payload_early_termination(rx_error_payload_early_termination), + .rx_error_invalid_header(rx_error_invalid_header), + .rx_error_invalid_checksum(rx_error_invalid_checksum), + .tx_error_payload_early_termination(tx_error_payload_early_termination), + .tx_error_arp_failed(tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip) +); + +/* + * ARP module + */ +arp #( + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +arp_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(arp_rx_eth_hdr_valid), + .s_eth_hdr_ready(arp_rx_eth_hdr_ready), + .s_eth_dest_mac(arp_rx_eth_dest_mac), + .s_eth_src_mac(arp_rx_eth_src_mac), + .s_eth_type(arp_rx_eth_type), + .s_eth_payload_axis_tdata(arp_rx_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(arp_rx_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(arp_rx_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(arp_rx_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(arp_rx_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(arp_rx_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(arp_tx_eth_hdr_valid), + .m_eth_hdr_ready(arp_tx_eth_hdr_ready), + .m_eth_dest_mac(arp_tx_eth_dest_mac), + .m_eth_src_mac(arp_tx_eth_src_mac), + .m_eth_type(arp_tx_eth_type), + .m_eth_payload_axis_tdata(arp_tx_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(arp_tx_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(arp_tx_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(arp_tx_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(arp_tx_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(arp_tx_eth_payload_axis_tuser), + // ARP requests + .arp_request_valid(arp_request_valid), + .arp_request_ready(arp_request_ready), + .arp_request_ip(arp_request_ip), + .arp_response_valid(arp_response_valid), + .arp_response_ready(arp_response_ready), + .arp_response_error(arp_response_error), + .arp_response_mac(arp_response_mac), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_cache(clear_arp_cache) +); + +endmodule diff --git a/corundum/lib/eth/rtl/ip_demux.v b/corundum/lib/eth/rtl/ip_demux.v new file mode 100644 index 0000000000000000000000000000000000000000..b86e60bd93037b3a8bc68e0b4252ee17291c3eb6 --- /dev/null +++ b/corundum/lib/eth/rtl/ip_demux.v @@ -0,0 +1,396 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IP demultiplexer + */ +module ip_demux # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [DATA_WIDTH-1:0] s_ip_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire [ID_WIDTH-1:0] s_ip_payload_axis_tid, + input wire [DEST_WIDTH-1:0] s_ip_payload_axis_tdest, + input wire [USER_WIDTH-1:0] s_ip_payload_axis_tuser, + + /* + * IP frame outputs + */ + output wire [M_COUNT-1:0] m_ip_hdr_valid, + input wire [M_COUNT-1:0] m_ip_hdr_ready, + output wire [M_COUNT*48-1:0] m_eth_dest_mac, + output wire [M_COUNT*48-1:0] m_eth_src_mac, + output wire [M_COUNT*16-1:0] m_eth_type, + output wire [M_COUNT*4-1:0] m_ip_version, + output wire [M_COUNT*4-1:0] m_ip_ihl, + output wire [M_COUNT*6-1:0] m_ip_dscp, + output wire [M_COUNT*2-1:0] m_ip_ecn, + output wire [M_COUNT*16-1:0] m_ip_length, + output wire [M_COUNT*16-1:0] m_ip_identification, + output wire [M_COUNT*3-1:0] m_ip_flags, + output wire [M_COUNT*13-1:0] m_ip_fragment_offset, + output wire [M_COUNT*8-1:0] m_ip_ttl, + output wire [M_COUNT*8-1:0] m_ip_protocol, + output wire [M_COUNT*16-1:0] m_ip_header_checksum, + output wire [M_COUNT*32-1:0] m_ip_source_ip, + output wire [M_COUNT*32-1:0] m_ip_dest_ip, + output wire [M_COUNT*DATA_WIDTH-1:0] m_ip_payload_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep, + output wire [M_COUNT-1:0] m_ip_payload_axis_tvalid, + input wire [M_COUNT-1:0] m_ip_payload_axis_tready, + output wire [M_COUNT-1:0] m_ip_payload_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_ip_payload_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_ip_payload_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_ip_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; + +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg [M_COUNT-1:0] m_ip_hdr_valid_reg = 0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int; +reg [M_COUNT-1:0] m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg && enable; + +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg && enable; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = {M_COUNT{m_eth_dest_mac_reg}}; +assign m_eth_src_mac = {M_COUNT{m_eth_src_mac_reg}}; +assign m_eth_type = {M_COUNT{m_eth_type_reg}}; +assign m_ip_version = {M_COUNT{m_ip_version_reg}}; +assign m_ip_ihl = {M_COUNT{m_ip_ihl_reg}}; +assign m_ip_dscp = {M_COUNT{m_ip_dscp_reg}}; +assign m_ip_ecn = {M_COUNT{m_ip_ecn_reg}}; +assign m_ip_length = {M_COUNT{m_ip_length_reg}}; +assign m_ip_identification = {M_COUNT{m_ip_identification_reg}}; +assign m_ip_flags = {M_COUNT{m_ip_flags_reg}}; +assign m_ip_fragment_offset = {M_COUNT{m_ip_fragment_offset_reg}}; +assign m_ip_ttl = {M_COUNT{m_ip_ttl_reg}}; +assign m_ip_protocol = {M_COUNT{m_ip_protocol_reg}}; +assign m_ip_header_checksum = {M_COUNT{m_ip_header_checksum_reg}}; +assign m_ip_source_ip = {M_COUNT{m_ip_source_ip_reg}}; +assign m_ip_dest_ip = {M_COUNT{m_ip_dest_ip_reg}}; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_ip_hdr_ready_next = 1'b0; + + s_ip_payload_axis_tready_next = 1'b0; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg & ~m_ip_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + + if (s_ip_payload_axis_tvalid && s_ip_payload_axis_tready) begin + // end of frame detection + if (s_ip_payload_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_ip_hdr_valid && s_ip_hdr_ready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = frame_ctl; + + s_ip_hdr_ready_next = 1'b0; + + m_ip_hdr_valid_next = (!drop_ctl) << select_ctl; + m_eth_dest_mac_next = s_eth_dest_mac; + m_eth_src_mac_next = s_eth_src_mac; + m_eth_type_next = s_eth_type; + m_ip_version_next = s_ip_version; + m_ip_ihl_next = s_ip_ihl; + m_ip_dscp_next = s_ip_dscp; + m_ip_ecn_next = s_ip_ecn; + m_ip_length_next = s_ip_length; + m_ip_identification_next = s_ip_identification; + m_ip_flags_next = s_ip_flags; + m_ip_fragment_offset_next = s_ip_fragment_offset; + m_ip_ttl_next = s_ip_ttl; + m_ip_protocol_next = s_ip_protocol; + m_ip_header_checksum_next = s_ip_header_checksum; + m_ip_source_ip_next = s_ip_source_ip; + m_ip_dest_ip_next = s_ip_dest_ip; + end + + s_ip_hdr_ready_next = !frame_next && !m_ip_hdr_valid_next; + + s_ip_payload_axis_tready_next = (m_ip_payload_axis_tready_int_early || drop_ctl) && frame_ctl; + + m_ip_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_ip_payload_axis_tkeep_int = s_ip_payload_axis_tkeep; + m_ip_payload_axis_tvalid_int = (s_ip_payload_axis_tvalid && s_ip_payload_axis_tready && !drop_ctl) << select_ctl; + m_ip_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_ip_payload_axis_tid_int = s_ip_payload_axis_tid; + m_ip_payload_axis_tdest_int = s_ip_payload_axis_tdest; + m_ip_payload_axis_tuser_int = s_ip_payload_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_ip_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_ip_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = {M_COUNT{m_ip_payload_axis_tdata_reg}}; +assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_ip_payload_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = {M_COUNT{m_ip_payload_axis_tlast_reg}}; +assign m_ip_payload_axis_tid = ID_ENABLE ? {M_COUNT{m_ip_payload_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_ip_payload_axis_tdest = DEST_ENABLE ? {M_COUNT{m_ip_payload_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_ip_payload_axis_tuser = USER_ENABLE ? {M_COUNT{m_ip_payload_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = (m_ip_payload_axis_tready & m_ip_payload_axis_tvalid) || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if ((m_ip_payload_axis_tready & m_ip_payload_axis_tvalid) || !m_ip_payload_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready & m_ip_payload_axis_tvalid) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg; + m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_eth_rx.v b/corundum/lib/eth/rtl/ip_eth_rx.v new file mode 100644 index 0000000000000000000000000000000000000000..5d0579148759f6b05901b72a267ac72bd80b381c --- /dev/null +++ b/corundum/lib/eth/rtl/ip_eth_rx.v @@ -0,0 +1,577 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IP ethernet frame receiver (Ethernet frame in, IP frame out) + */ +module ip_eth_rx +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination, + output wire error_invalid_header, + output wire error_invalid_checksum +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an Ethernet frame with header fields in parallel and +payload on an AXI stream interface, decodes and strips the IP header fields, +then produces the header fields in parallel along with the IP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; +reg store_ip_version_ihl; +reg store_ip_dscp_ecn; +reg store_ip_length_0; +reg store_ip_length_1; +reg store_ip_identification_0; +reg store_ip_identification_1; +reg store_ip_flags_fragment_offset_0; +reg store_ip_flags_fragment_offset_1; +reg store_ip_ttl; +reg store_ip_protocol; +reg store_ip_header_checksum_0; +reg store_ip_header_checksum_1; +reg store_ip_source_ip_0; +reg store_ip_source_ip_1; +reg store_ip_source_ip_2; +reg store_ip_source_ip_3; +reg store_ip_dest_ip_0; +reg store_ip_dest_ip_1; +reg store_ip_dest_ip_2; +reg store_ip_dest_ip_3; +reg store_last_word; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; +reg error_invalid_header_reg = 1'b0, error_invalid_header_next; +reg error_invalid_checksum_reg = 1'b0, error_invalid_checksum_next; + +// internal datapath +reg [7:0] m_ip_payload_axis_tdata_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; +assign error_invalid_header = error_invalid_header_reg; +assign error_invalid_checksum = error_invalid_checksum_reg; + +function [15:0] add1c16b; + input [15:0] a, b; + reg [16:0] t; + begin + t = a+b; + add1c16b = t[15:0] + t[16]; + end +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + store_ip_version_ihl = 1'b0; + store_ip_dscp_ecn = 1'b0; + store_ip_length_0 = 1'b0; + store_ip_length_1 = 1'b0; + store_ip_identification_0 = 1'b0; + store_ip_identification_1 = 1'b0; + store_ip_flags_fragment_offset_0 = 1'b0; + store_ip_flags_fragment_offset_1 = 1'b0; + store_ip_ttl = 1'b0; + store_ip_protocol = 1'b0; + store_ip_header_checksum_0 = 1'b0; + store_ip_header_checksum_1 = 1'b0; + store_ip_source_ip_0 = 1'b0; + store_ip_source_ip_1 = 1'b0; + store_ip_source_ip_2 = 1'b0; + store_ip_source_ip_3 = 1'b0; + store_ip_dest_ip_0 = 1'b0; + store_ip_dest_ip_1 = 1'b0; + store_ip_dest_ip_2 = 1'b0; + store_ip_dest_ip_3 = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_next = hdr_sum_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + error_invalid_header_next = 1'b0; + error_invalid_checksum_next = 1'b0; + + m_ip_payload_axis_tdata_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + hdr_ptr_next = 16'd0; + hdr_sum_next = 16'd0; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b1; + store_eth_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header + s_eth_payload_axis_tready_next = 1'b1; + word_count_next = m_ip_length_reg - 5*4; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + // word transfer in - store it + hdr_ptr_next = hdr_ptr_reg + 6'd1; + state_next = STATE_READ_HEADER; + + if (hdr_ptr_reg[0]) begin + hdr_sum_next = add1c16b(hdr_sum_reg, {8'd0, s_eth_payload_axis_tdata}); + end else begin + hdr_sum_next = add1c16b(hdr_sum_reg, {s_eth_payload_axis_tdata, 8'd0}); + end + + case (hdr_ptr_reg) + 6'h00: store_ip_version_ihl = 1'b1; + 6'h01: store_ip_dscp_ecn = 1'b1; + 6'h02: store_ip_length_1 = 1'b1; + 6'h03: store_ip_length_0 = 1'b1; + 6'h04: store_ip_identification_1 = 1'b1; + 6'h05: store_ip_identification_0 = 1'b1; + 6'h06: store_ip_flags_fragment_offset_1 = 1'b1; + 6'h07: store_ip_flags_fragment_offset_0 = 1'b1; + 6'h08: store_ip_ttl = 1'b1; + 6'h09: store_ip_protocol = 1'b1; + 6'h0A: store_ip_header_checksum_1 = 1'b1; + 6'h0B: store_ip_header_checksum_0 = 1'b1; + 6'h0C: store_ip_source_ip_3 = 1'b1; + 6'h0D: store_ip_source_ip_2 = 1'b1; + 6'h0E: store_ip_source_ip_1 = 1'b1; + 6'h0F: store_ip_source_ip_0 = 1'b1; + 6'h10: store_ip_dest_ip_3 = 1'b1; + 6'h11: store_ip_dest_ip_2 = 1'b1; + 6'h12: store_ip_dest_ip_1 = 1'b1; + 6'h13: begin + store_ip_dest_ip_0 = 1'b1; + if (m_ip_version_reg != 4'd4 || m_ip_ihl_reg != 4'd5) begin + error_invalid_header_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else if (hdr_sum_next != 16'hffff) begin + error_invalid_checksum_next = 1'b1; + state_next = STATE_WAIT_LAST; + end else begin + m_ip_hdr_valid_next = 1'b1; + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + end + end + endcase + + if (s_eth_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + m_ip_hdr_valid_next = 1'b0; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = s_eth_payload_axis_tdata; + m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd1; + if (s_eth_payload_axis_tlast) begin + if (word_count_reg > 16'd1) begin + // end of frame, but length does not match + m_ip_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tvalid_int = s_eth_payload_axis_tvalid && s_eth_payload_axis_tlast; + m_ip_payload_axis_tlast_int = s_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_eth_payload_axis_tuser; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + if (s_eth_payload_axis_tlast) begin + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = 1'b1; + + if (s_eth_payload_axis_tready && s_eth_payload_axis_tvalid) begin + if (s_eth_payload_axis_tlast) begin + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + error_invalid_header_reg <= 1'b0; + error_invalid_checksum_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + error_invalid_header_reg <= error_invalid_header_next; + error_invalid_checksum_reg <= error_invalid_checksum_next; + + busy_reg <= state_next != STATE_IDLE; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + + // datapath + if (store_eth_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + end + + if (store_ip_version_ihl) {m_ip_version_reg, m_ip_ihl_reg} <= s_eth_payload_axis_tdata; + if (store_ip_dscp_ecn) {m_ip_dscp_reg, m_ip_ecn_reg} <= s_eth_payload_axis_tdata; + if (store_ip_length_0) m_ip_length_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_length_1) m_ip_length_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_identification_0) m_ip_identification_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_identification_1) m_ip_identification_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_flags_fragment_offset_0) m_ip_fragment_offset_reg[ 7:0] <= s_eth_payload_axis_tdata; + if (store_ip_flags_fragment_offset_1) {m_ip_flags_reg, m_ip_fragment_offset_reg[12:8]} <= s_eth_payload_axis_tdata; + if (store_ip_ttl) m_ip_ttl_reg <= s_eth_payload_axis_tdata; + if (store_ip_protocol) m_ip_protocol_reg <= s_eth_payload_axis_tdata; + if (store_ip_header_checksum_0) m_ip_header_checksum_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_header_checksum_1) m_ip_header_checksum_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_0) m_ip_source_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_1) m_ip_source_ip_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_2) m_ip_source_ip_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_ip_source_ip_3) m_ip_source_ip_reg[31:24] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_0) m_ip_dest_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_1) m_ip_dest_ip_reg[15: 8] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_2) m_ip_dest_ip_reg[23:16] <= s_eth_payload_axis_tdata; + if (store_ip_dest_ip_3) m_ip_dest_ip_reg[31:24] <= s_eth_payload_axis_tdata; +end + +// output datapath logic +reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_eth_rx_64.v b/corundum/lib/eth/rtl/ip_eth_rx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..47554645c989f42f13453b693d5459753b59a96f --- /dev/null +++ b/corundum/lib/eth/rtl/ip_eth_rx_64.v @@ -0,0 +1,686 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IP ethernet frame receiver (Ethernet frame in, IP frame out, 64 bit datapath) + */ +module ip_eth_rx_64 +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination, + output wire error_invalid_header, + output wire error_invalid_checksum +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an Ethernet frame with header fields in parallel and +payload on an AXI stream interface, decodes and strips the IP header fields, +then produces the header fields in parallel along with the IP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_eth_hdr; +reg store_hdr_word_0; +reg store_hdr_word_1; +reg store_hdr_word_2; +reg store_last_word; + +reg flush_save; +reg transfer_in_save; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [16:0] hdr_sum_high_reg = 17'd0; +reg [16:0] hdr_sum_low_reg = 17'd0; +reg [19:0] hdr_sum_temp; +reg [19:0] hdr_sum_reg = 20'd0, hdr_sum_next; +reg check_hdr_reg = 1'b0, check_hdr_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg s_eth_hdr_ready_reg = 1'b0, s_eth_hdr_ready_next; +reg s_eth_payload_axis_tready_reg = 1'b0, s_eth_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; +reg error_invalid_header_reg = 1'b0, error_invalid_header_next; +reg error_invalid_checksum_reg = 1'b0, error_invalid_checksum_next; + +reg [63:0] save_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] save_eth_payload_axis_tkeep_reg = 8'd0; +reg save_eth_payload_axis_tlast_reg = 1'b0; +reg save_eth_payload_axis_tuser_reg = 1'b0; + +reg [63:0] shift_eth_payload_axis_tdata; +reg [7:0] shift_eth_payload_axis_tkeep; +reg shift_eth_payload_axis_tvalid; +reg shift_eth_payload_axis_tlast; +reg shift_eth_payload_axis_tuser; +reg shift_eth_payload_s_tready; +reg shift_eth_payload_extra_cycle_reg = 1'b0; + +// internal datapath +reg [63:0] m_ip_payload_axis_tdata_int; +reg [7:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_eth_hdr_ready = s_eth_hdr_ready_reg; +assign s_eth_payload_axis_tready = s_eth_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; +assign error_invalid_header = error_invalid_header_reg; +assign error_invalid_checksum = error_invalid_checksum_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + shift_eth_payload_axis_tdata[31:0] = save_eth_payload_axis_tdata_reg[63:32]; + shift_eth_payload_axis_tkeep[3:0] = save_eth_payload_axis_tkeep_reg[7:4]; + + if (shift_eth_payload_extra_cycle_reg) begin + shift_eth_payload_axis_tdata[63:32] = 32'd0; + shift_eth_payload_axis_tkeep[7:4] = 4'd0; + shift_eth_payload_axis_tvalid = 1'b1; + shift_eth_payload_axis_tlast = save_eth_payload_axis_tlast_reg; + shift_eth_payload_axis_tuser = save_eth_payload_axis_tuser_reg; + shift_eth_payload_s_tready = flush_save; + end else begin + shift_eth_payload_axis_tdata[63:32] = s_eth_payload_axis_tdata[31:0]; + shift_eth_payload_axis_tkeep[7:4] = s_eth_payload_axis_tkeep[3:0]; + shift_eth_payload_axis_tvalid = s_eth_payload_axis_tvalid; + shift_eth_payload_axis_tlast = (s_eth_payload_axis_tlast && (s_eth_payload_axis_tkeep[7:4] == 0)); + shift_eth_payload_axis_tuser = (s_eth_payload_axis_tuser && (s_eth_payload_axis_tkeep[7:4] == 0)); + shift_eth_payload_s_tready = !(s_eth_payload_axis_tlast && s_eth_payload_axis_tvalid && transfer_in_save); + end +end + +always @* begin + state_next = STATE_IDLE; + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b0; + + store_eth_hdr = 1'b0; + store_hdr_word_0 = 1'b0; + store_hdr_word_1 = 1'b0; + store_hdr_word_2 = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_temp = 32'd0; + hdr_sum_next = hdr_sum_reg; + check_hdr_next = check_hdr_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + error_invalid_header_next = 1'b0; + error_invalid_checksum_next = 1'b0; + + m_ip_payload_axis_tdata_int = 64'd0; + m_ip_payload_axis_tkeep_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + hdr_ptr_next = 6'd0; + hdr_sum_next = 32'd0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + + if (s_eth_hdr_ready && s_eth_hdr_valid) begin + s_eth_hdr_ready_next = 1'b0; + s_eth_payload_axis_tready_next = 1'b1; + store_eth_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + word_count_next = m_ip_length_reg - 5*4; + + if (s_eth_payload_axis_tvalid) begin + // word transfer in - store it + hdr_ptr_next = hdr_ptr_reg + 6'd8; + transfer_in_save = 1'b1; + state_next = STATE_READ_HEADER; + + case (hdr_ptr_reg) + 6'h00: begin + store_hdr_word_0 = 1'b1; + end + 6'h08: begin + store_hdr_word_1 = 1'b1; + hdr_sum_next = hdr_sum_high_reg + hdr_sum_low_reg; + end + 6'h10: begin + store_hdr_word_2 = 1'b1; + hdr_sum_next = hdr_sum_reg + hdr_sum_high_reg + hdr_sum_low_reg; + + // check header checksum on next cycle for improved timing + check_hdr_next = 1'b1; + + if (m_ip_version_reg != 4'd4 || m_ip_ihl_reg != 4'd5) begin + error_invalid_header_next = 1'b1; + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + state_next = STATE_WAIT_LAST; + end else begin + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early && shift_eth_payload_s_tready; + state_next = STATE_READ_PAYLOAD; + end + end + endcase + + if (shift_eth_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + error_invalid_header_next = 1'b0; + error_invalid_checksum_next = 1'b0; + m_ip_hdr_valid_next = 1'b0; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + s_eth_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early && shift_eth_payload_s_tready; + + m_ip_payload_axis_tdata_int = shift_eth_payload_axis_tdata; + m_ip_payload_axis_tkeep_int = shift_eth_payload_axis_tkeep; + m_ip_payload_axis_tvalid_int = shift_eth_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = shift_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = shift_eth_payload_axis_tuser; + + store_last_word = 1'b1; + + if (m_ip_payload_axis_tready_int_reg && shift_eth_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + transfer_in_save = 1'b1; + if (word_count_reg <= 8) begin + // have entire payload + m_ip_payload_axis_tkeep_int = shift_eth_payload_axis_tkeep & count2keep(word_count_reg); + if (shift_eth_payload_axis_tlast) begin + if (keep2count(shift_eth_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + end + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_reg && !check_hdr_reg; + state_next = STATE_IDLE; + end else begin + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + if (shift_eth_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_reg && !check_hdr_reg; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + + if (check_hdr_reg) begin + check_hdr_next = 1'b0; + + hdr_sum_temp = hdr_sum_reg[15:0] + hdr_sum_reg[19:16] + hdr_sum_low_reg; + + if (hdr_sum_temp != 19'h0ffff && hdr_sum_temp != 19'h1fffe) begin + // bad checksum + error_invalid_checksum_next = 1'b1; + m_ip_payload_axis_tvalid_int = 1'b0; + if (shift_eth_payload_axis_tlast && shift_eth_payload_axis_tvalid) begin + // only one payload cycle; return to idle now + s_eth_hdr_ready_next = !m_ip_hdr_valid_reg && !check_hdr_reg; + state_next = STATE_IDLE; + end else begin + // drop payload + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + state_next = STATE_WAIT_LAST; + end + end else begin + // good checksum; transfer header + m_ip_hdr_valid_next = 1'b1; + end + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = m_ip_payload_axis_tready_int_early && shift_eth_payload_s_tready; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tkeep_int = last_word_keep_reg; + m_ip_payload_axis_tvalid_int = shift_eth_payload_axis_tvalid && shift_eth_payload_axis_tlast; + m_ip_payload_axis_tlast_int = shift_eth_payload_axis_tlast; + m_ip_payload_axis_tuser_int = shift_eth_payload_axis_tuser; + + if (m_ip_payload_axis_tready_int_reg && shift_eth_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_eth_payload_axis_tlast) begin + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_eth_payload_axis_tready_next = shift_eth_payload_s_tready; + + if (shift_eth_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_eth_payload_axis_tlast) begin + s_eth_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_eth_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_eth_hdr_ready_reg <= 1'b0; + s_eth_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + save_eth_payload_axis_tlast_reg <= 1'b0; + shift_eth_payload_extra_cycle_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + error_invalid_header_reg <= 1'b0; + error_invalid_checksum_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_eth_hdr_ready_reg <= s_eth_hdr_ready_next; + s_eth_payload_axis_tready_reg <= s_eth_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + error_invalid_header_reg <= error_invalid_header_next; + error_invalid_checksum_reg <= error_invalid_checksum_next; + + busy_reg <= state_next != STATE_IDLE; + + // datapath + if (flush_save) begin + save_eth_payload_axis_tlast_reg <= 1'b0; + shift_eth_payload_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_eth_payload_axis_tlast_reg <= s_eth_payload_axis_tlast; + shift_eth_payload_extra_cycle_reg <= s_eth_payload_axis_tlast && (s_eth_payload_axis_tkeep[7:4] != 0); + end + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + check_hdr_reg <= check_hdr_next; + + if (s_eth_payload_axis_tvalid) begin + hdr_sum_low_reg <= s_eth_payload_axis_tdata[15:0] + s_eth_payload_axis_tdata[31:16]; + hdr_sum_high_reg <= s_eth_payload_axis_tdata[47:32] + s_eth_payload_axis_tdata[63:48]; + end + + // datapath + if (store_eth_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + last_word_keep_reg <= m_ip_payload_axis_tkeep_int; + end + + if (store_hdr_word_0) begin + {m_ip_version_reg, m_ip_ihl_reg} <= s_eth_payload_axis_tdata[ 7: 0]; + {m_ip_dscp_reg, m_ip_ecn_reg} <= s_eth_payload_axis_tdata[15: 8]; + m_ip_length_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_ip_length_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + m_ip_identification_reg[15: 8] <= s_eth_payload_axis_tdata[39:32]; + m_ip_identification_reg[ 7: 0] <= s_eth_payload_axis_tdata[47:40]; + {m_ip_flags_reg, m_ip_fragment_offset_reg[12:8]} <= s_eth_payload_axis_tdata[55:48]; + m_ip_fragment_offset_reg[ 7:0] <= s_eth_payload_axis_tdata[63:56]; + end + + if (store_hdr_word_1) begin + m_ip_ttl_reg <= s_eth_payload_axis_tdata[ 7: 0]; + m_ip_protocol_reg <= s_eth_payload_axis_tdata[15: 8]; + m_ip_header_checksum_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_ip_header_checksum_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + m_ip_source_ip_reg[31:24] <= s_eth_payload_axis_tdata[39:32]; + m_ip_source_ip_reg[23:16] <= s_eth_payload_axis_tdata[47:40]; + m_ip_source_ip_reg[15: 8] <= s_eth_payload_axis_tdata[55:48]; + m_ip_source_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata[63:56]; + end + + if (store_hdr_word_2) begin + m_ip_dest_ip_reg[31:24] <= s_eth_payload_axis_tdata[ 7: 0]; + m_ip_dest_ip_reg[23:16] <= s_eth_payload_axis_tdata[15: 8]; + m_ip_dest_ip_reg[15: 8] <= s_eth_payload_axis_tdata[23:16]; + m_ip_dest_ip_reg[ 7: 0] <= s_eth_payload_axis_tdata[31:24]; + end + + if (transfer_in_save) begin + save_eth_payload_axis_tdata_reg <= s_eth_payload_axis_tdata; + save_eth_payload_axis_tkeep_reg <= s_eth_payload_axis_tkeep; + save_eth_payload_axis_tuser_reg <= s_eth_payload_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_ip_payload_axis_tkeep_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_ip_payload_axis_tkeep_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = m_ip_payload_axis_tkeep_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_eth_tx.v b/corundum/lib/eth/rtl/ip_eth_tx.v new file mode 100644 index 0000000000000000000000000000000000000000..14619f88f850c4148e63b63a0a1d2208c9df7e77 --- /dev/null +++ b/corundum/lib/eth/rtl/ip_eth_tx.v @@ -0,0 +1,497 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IP ethernet frame transmitter (IP frame in, Ethernet frame out) + */ +module ip_eth_tx +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an IP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the Ethernet headers, and transmits the complete Ethernet payload on an AXI +interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_PAYLOAD = 3'd2, + STATE_WRITE_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_last_word; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [15:0] hdr_sum_reg = 16'd0, hdr_sum_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_length_reg = 16'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [7:0] ip_protocol_reg = 8'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [7:0] m_eth_payload_axis_tdata_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [15:0] add1c16b; + input [15:0] a, b; + reg [16:0] t; + begin + t = a+b; + add1c16b = t[15:0] + t[16]; + end +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_next = hdr_sum_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_eth_payload_axis_tdata_int = 8'd0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + hdr_ptr_next = 6'd0; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + store_ip_hdr = 1'b1; + s_ip_hdr_ready_next = 1'b0; + m_eth_hdr_valid_next = 1'b1; + if (m_eth_payload_axis_tready_int_reg) begin + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl + hdr_ptr_next = 6'd1; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header + word_count_next = ip_length_reg - 5*4; + + if (m_eth_payload_axis_tready_int_reg) begin + hdr_ptr_next = hdr_ptr_reg + 6'd1; + m_eth_payload_axis_tvalid_int = 1; + state_next = STATE_WRITE_HEADER; + case (hdr_ptr_reg) + 6'h00: begin + m_eth_payload_axis_tdata_int = {4'd4, 4'd5}; // ip_version, ip_ihl + end + 6'h01: begin + m_eth_payload_axis_tdata_int = {ip_dscp_reg, ip_ecn_reg}; + hdr_sum_next = {4'd4, 4'd5, ip_dscp_reg, ip_ecn_reg}; + end + 6'h02: begin + m_eth_payload_axis_tdata_int = ip_length_reg[15: 8]; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_length_reg); + end + 6'h03: begin + m_eth_payload_axis_tdata_int = ip_length_reg[ 7: 0]; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_identification_reg); + end + 6'h04: begin + m_eth_payload_axis_tdata_int = ip_identification_reg[15: 8]; + hdr_sum_next = add1c16b(hdr_sum_reg, {ip_flags_reg, ip_fragment_offset_reg}); + end + 6'h05: begin + m_eth_payload_axis_tdata_int = ip_identification_reg[ 7: 0]; + hdr_sum_next = add1c16b(hdr_sum_reg, {ip_ttl_reg, ip_protocol_reg}); + end + 6'h06: begin + m_eth_payload_axis_tdata_int = {ip_flags_reg, ip_fragment_offset_reg[12:8]}; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[31:16]); + end + 6'h07: begin + m_eth_payload_axis_tdata_int = ip_fragment_offset_reg[ 7: 0]; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_source_ip_reg[15:0]); + end + 6'h08: begin + m_eth_payload_axis_tdata_int = ip_ttl_reg; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[31:16]); + end + 6'h09: begin + m_eth_payload_axis_tdata_int = ip_protocol_reg; + hdr_sum_next = add1c16b(hdr_sum_reg, ip_dest_ip_reg[15:0]); + end + 6'h0A: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[15: 8]; + 6'h0B: m_eth_payload_axis_tdata_int = ~hdr_sum_reg[ 7: 0]; + 6'h0C: m_eth_payload_axis_tdata_int = ip_source_ip_reg[31:24]; + 6'h0D: m_eth_payload_axis_tdata_int = ip_source_ip_reg[23:16]; + 6'h0E: m_eth_payload_axis_tdata_int = ip_source_ip_reg[15: 8]; + 6'h0F: m_eth_payload_axis_tdata_int = ip_source_ip_reg[ 7: 0]; + 6'h10: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[31:24]; + 6'h11: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[23:16]; + 6'h12: m_eth_payload_axis_tdata_int = ip_dest_ip_reg[15: 8]; + 6'h13: begin + m_eth_payload_axis_tdata_int = ip_dest_ip_reg[ 7: 0]; + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + + m_eth_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid; + m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 6'd1; + if (s_ip_payload_axis_tlast) begin + if (word_count_reg != 16'd1) begin + // end of frame, but length does not match + m_eth_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_eth_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + + m_eth_payload_axis_tdata_int = last_word_data_reg; + m_eth_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast; + m_eth_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_length_reg <= s_ip_length; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_protocol_reg <= s_ip_protocol; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_eth_payload_axis_tdata_int; + end +end + +// output datapath logic +reg [7:0] m_eth_payload_axis_tdata_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_eth_payload_axis_tdata_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready || (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg || !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready || !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_eth_tx_64.v b/corundum/lib/eth/rtl/ip_eth_tx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..471cf239f2ffe668f0bc382a03fa0cd6d8c2b478 --- /dev/null +++ b/corundum/lib/eth/rtl/ip_eth_tx_64.v @@ -0,0 +1,648 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IP ethernet frame transmitter (IP frame in, Ethernet frame out, 64 bit datapath) + */ +module ip_eth_tx_64 +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +IP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + payload length octets + +This module receives an IP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the Ethernet headers, and transmits the complete Ethernet payload on an AXI +interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_HEADER_LAST = 3'd2, + STATE_WRITE_PAYLOAD = 3'd3, + STATE_WRITE_PAYLOAD_LAST = 3'd4, + STATE_WAIT_LAST = 3'd5; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_last_word; + +reg [5:0] hdr_ptr_reg = 6'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg flush_save; +reg transfer_in_save; + +reg [19:0] hdr_sum_temp; +reg [19:0] hdr_sum_reg = 20'd0, hdr_sum_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_length_reg = 16'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [7:0] ip_protocol_reg = 8'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg m_eth_hdr_valid_reg = 1'b0, m_eth_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +reg [63:0] save_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] save_ip_payload_axis_tkeep_reg = 8'd0; +reg save_ip_payload_axis_tlast_reg = 1'b0; +reg save_ip_payload_axis_tuser_reg = 1'b0; + +reg [63:0] shift_ip_payload_axis_tdata; +reg [7:0] shift_ip_payload_axis_tkeep; +reg shift_ip_payload_axis_tvalid; +reg shift_ip_payload_axis_tlast; +reg shift_ip_payload_axis_tuser; +reg shift_ip_payload_s_tready; +reg shift_ip_payload_extra_cycle_reg = 1'b0; + +// internal datapath +reg [63:0] m_eth_payload_axis_tdata_int; +reg [7:0] m_eth_payload_axis_tkeep_int; +reg m_eth_payload_axis_tvalid_int; +reg m_eth_payload_axis_tready_int_reg = 1'b0; +reg m_eth_payload_axis_tlast_int; +reg m_eth_payload_axis_tuser_int; +wire m_eth_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_eth_hdr_valid = m_eth_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + shift_ip_payload_axis_tdata[31:0] = save_ip_payload_axis_tdata_reg[63:32]; + shift_ip_payload_axis_tkeep[3:0] = save_ip_payload_axis_tkeep_reg[7:4]; + + if (shift_ip_payload_extra_cycle_reg) begin + shift_ip_payload_axis_tdata[63:32] = 32'd0; + shift_ip_payload_axis_tkeep[7:4] = 4'd0; + shift_ip_payload_axis_tvalid = 1'b1; + shift_ip_payload_axis_tlast = save_ip_payload_axis_tlast_reg; + shift_ip_payload_axis_tuser = save_ip_payload_axis_tuser_reg; + shift_ip_payload_s_tready = flush_save; + end else begin + shift_ip_payload_axis_tdata[63:32] = s_ip_payload_axis_tdata[31:0]; + shift_ip_payload_axis_tkeep[7:4] = s_ip_payload_axis_tkeep[3:0]; + shift_ip_payload_axis_tvalid = s_ip_payload_axis_tvalid; + shift_ip_payload_axis_tlast = (s_ip_payload_axis_tlast && (s_ip_payload_axis_tkeep[7:4] == 0)); + shift_ip_payload_axis_tuser = (s_ip_payload_axis_tuser && (s_ip_payload_axis_tkeep[7:4] == 0)); + shift_ip_payload_s_tready = !(s_ip_payload_axis_tlast && s_ip_payload_axis_tvalid && transfer_in_save) && !save_ip_payload_axis_tlast_reg; + end +end + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + + store_last_word = 1'b0; + + flush_save = 1'b0; + transfer_in_save = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + hdr_sum_temp = 20'd0; + hdr_sum_next = hdr_sum_reg; + + m_eth_hdr_valid_next = m_eth_hdr_valid_reg && !m_eth_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_eth_payload_axis_tdata_int = 1'b0; + m_eth_payload_axis_tkeep_int = 1'b0; + m_eth_payload_axis_tvalid_int = 1'b0; + m_eth_payload_axis_tlast_int = 1'b0; + m_eth_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + hdr_ptr_next = 6'd0; + flush_save = 1'b1; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + store_ip_hdr = 1'b1; + hdr_sum_next = {4'd4, 4'd5, s_ip_dscp, s_ip_ecn} + + s_ip_length + + s_ip_identification + + {s_ip_flags, s_ip_fragment_offset} + + {s_ip_ttl, s_ip_protocol} + + s_ip_source_ip[31:16] + + s_ip_source_ip[15: 0] + + s_ip_dest_ip[31:16] + + s_ip_dest_ip[15: 0]; + s_ip_hdr_ready_next = 1'b0; + m_eth_hdr_valid_next = 1'b1; + if (m_eth_payload_axis_tready_int_reg) begin + m_eth_payload_axis_tvalid_int = 1'b1; + m_eth_payload_axis_tdata_int[ 7: 0] = {4'd4, 4'd5}; // ip_version, ip_ihl + m_eth_payload_axis_tdata_int[15: 8] = {s_ip_dscp, s_ip_ecn}; + m_eth_payload_axis_tdata_int[23:16] = s_ip_length[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = s_ip_length[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = s_ip_identification[15: 8]; + m_eth_payload_axis_tdata_int[47:40] = s_ip_identification[ 7: 0]; + m_eth_payload_axis_tdata_int[55:48] = {s_ip_flags, s_ip_fragment_offset[12: 8]}; + m_eth_payload_axis_tdata_int[63:56] = s_ip_fragment_offset[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + hdr_ptr_next = 6'd8; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header + word_count_next = ip_length_reg - 5*4 + 4; + + if (m_eth_payload_axis_tready_int_reg) begin + hdr_ptr_next = hdr_ptr_reg + 6'd8; + m_eth_payload_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (hdr_ptr_reg) + 6'h00: begin + m_eth_payload_axis_tdata_int[ 7: 0] = {4'd4, 4'd5}; // ip_version, ip_ihl + m_eth_payload_axis_tdata_int[15: 8] = {ip_dscp_reg, ip_ecn_reg}; + m_eth_payload_axis_tdata_int[23:16] = ip_length_reg[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = ip_length_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = ip_identification_reg[15: 8]; + m_eth_payload_axis_tdata_int[47:40] = ip_identification_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[55:48] = {ip_flags_reg, ip_fragment_offset_reg[12: 8]}; + m_eth_payload_axis_tdata_int[63:56] = ip_fragment_offset_reg[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + end + 6'h08: begin + hdr_sum_temp = hdr_sum_reg[15:0] + hdr_sum_reg[19:16]; + hdr_sum_temp = hdr_sum_temp[15:0] + hdr_sum_temp[16]; + m_eth_payload_axis_tdata_int[ 7: 0] = ip_ttl_reg; + m_eth_payload_axis_tdata_int[15: 8] = ip_protocol_reg; + m_eth_payload_axis_tdata_int[23:16] = ~hdr_sum_temp[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = ~hdr_sum_temp[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = ip_source_ip_reg[31:24]; + m_eth_payload_axis_tdata_int[47:40] = ip_source_ip_reg[23:16]; + m_eth_payload_axis_tdata_int[55:48] = ip_source_ip_reg[15: 8]; + m_eth_payload_axis_tdata_int[63:56] = ip_source_ip_reg[ 7: 0]; + m_eth_payload_axis_tkeep_int = 8'hff; + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early; + state_next = STATE_WRITE_HEADER_LAST; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_HEADER_LAST: begin + // last header word requires first payload word; process accordingly + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_ip_payload_s_tready; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + m_eth_payload_axis_tvalid_int = 1'b1; + transfer_in_save = 1'b1; + + m_eth_payload_axis_tdata_int[ 7: 0] = ip_dest_ip_reg[31:24]; + m_eth_payload_axis_tdata_int[15: 8] = ip_dest_ip_reg[23:16]; + m_eth_payload_axis_tdata_int[23:16] = ip_dest_ip_reg[15: 8]; + m_eth_payload_axis_tdata_int[31:24] = ip_dest_ip_reg[ 7: 0]; + m_eth_payload_axis_tdata_int[39:32] = shift_ip_payload_axis_tdata[39:32]; + m_eth_payload_axis_tdata_int[47:40] = shift_ip_payload_axis_tdata[47:40]; + m_eth_payload_axis_tdata_int[55:48] = shift_ip_payload_axis_tdata[55:48]; + m_eth_payload_axis_tdata_int[63:56] = shift_ip_payload_axis_tdata[63:56]; + m_eth_payload_axis_tkeep_int = {shift_ip_payload_axis_tkeep[7:4], 4'hF}; + m_eth_payload_axis_tlast_int = shift_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = shift_ip_payload_axis_tuser; + word_count_next = word_count_reg - 16'd8; + + if (keep2count(m_eth_payload_axis_tkeep_int) >= word_count_reg) begin + // have entire payload + m_eth_payload_axis_tkeep_int = count2keep(word_count_reg); + if (shift_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + store_last_word = 1'b1; + s_ip_payload_axis_tready_next = shift_ip_payload_s_tready; + m_eth_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + if (shift_ip_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + s_ip_payload_axis_tready_next = shift_ip_payload_s_tready; + m_eth_payload_axis_tuser_int = 1'b1; + state_next = STATE_WAIT_LAST; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_HEADER_LAST; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_ip_payload_s_tready; + + m_eth_payload_axis_tdata_int = shift_ip_payload_axis_tdata; + m_eth_payload_axis_tkeep_int = shift_ip_payload_axis_tkeep; + m_eth_payload_axis_tvalid_int = shift_ip_payload_axis_tvalid; + m_eth_payload_axis_tlast_int = shift_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = shift_ip_payload_axis_tuser; + + store_last_word = 1'b1; + + if (m_eth_payload_axis_tready_int_reg && shift_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + transfer_in_save = 1'b1; + if (word_count_reg <= 8) begin + // have entire payload + m_eth_payload_axis_tkeep_int = count2keep(word_count_reg); + if (shift_ip_payload_axis_tlast) begin + if (keep2count(shift_ip_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_eth_payload_axis_tuser_int = 1'b1; + end + s_ip_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + m_eth_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + if (shift_ip_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_eth_payload_axis_tuser_int = 1'b1; + s_ip_payload_axis_tready_next = 1'b0; + flush_save = 1'b1; + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_eth_payload_axis_tready_int_early && shift_ip_payload_s_tready; + + m_eth_payload_axis_tdata_int = last_word_data_reg; + m_eth_payload_axis_tkeep_int = last_word_keep_reg; + m_eth_payload_axis_tvalid_int = shift_ip_payload_axis_tvalid && shift_ip_payload_axis_tlast; + m_eth_payload_axis_tlast_int = shift_ip_payload_axis_tlast; + m_eth_payload_axis_tuser_int = shift_ip_payload_axis_tuser; + + if (m_eth_payload_axis_tready_int_reg && shift_ip_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = shift_ip_payload_s_tready; + + if (shift_ip_payload_axis_tvalid) begin + transfer_in_save = 1'b1; + if (shift_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_eth_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_eth_hdr_valid_reg <= 1'b0; + save_ip_payload_axis_tlast_reg <= 1'b0; + shift_ip_payload_extra_cycle_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_eth_hdr_valid_reg <= m_eth_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + + if (flush_save) begin + save_ip_payload_axis_tlast_reg <= 1'b0; + shift_ip_payload_extra_cycle_reg <= 1'b0; + end else if (transfer_in_save) begin + save_ip_payload_axis_tlast_reg <= s_ip_payload_axis_tlast; + shift_ip_payload_extra_cycle_reg <= s_ip_payload_axis_tlast && (s_ip_payload_axis_tkeep[7:4] != 0); + end + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + hdr_sum_reg <= hdr_sum_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_length_reg <= s_ip_length; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_protocol_reg <= s_ip_protocol; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_eth_payload_axis_tdata_int; + last_word_keep_reg <= m_eth_payload_axis_tkeep_int; + end + + if (transfer_in_save) begin + save_ip_payload_axis_tdata_reg <= s_ip_payload_axis_tdata; + save_ip_payload_axis_tkeep_reg <= s_ip_payload_axis_tkeep; + save_ip_payload_axis_tuser_reg <= s_ip_payload_axis_tuser; + end +end + +// output datapath logic +reg [63:0] m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_eth_payload_axis_tkeep_reg = 8'd0; +reg m_eth_payload_axis_tvalid_reg = 1'b0, m_eth_payload_axis_tvalid_next; +reg m_eth_payload_axis_tlast_reg = 1'b0; +reg m_eth_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_eth_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_eth_payload_axis_tkeep_reg = 8'd0; +reg temp_m_eth_payload_axis_tvalid_reg = 1'b0, temp_m_eth_payload_axis_tvalid_next; +reg temp_m_eth_payload_axis_tlast_reg = 1'b0; +reg temp_m_eth_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_eth_payload_int_to_output; +reg store_eth_payload_int_to_temp; +reg store_eth_payload_axis_temp_to_output; + +assign m_eth_payload_axis_tdata = m_eth_payload_axis_tdata_reg; +assign m_eth_payload_axis_tkeep = m_eth_payload_axis_tkeep_reg; +assign m_eth_payload_axis_tvalid = m_eth_payload_axis_tvalid_reg; +assign m_eth_payload_axis_tlast = m_eth_payload_axis_tlast_reg; +assign m_eth_payload_axis_tuser = m_eth_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_eth_payload_axis_tready_int_early = m_eth_payload_axis_tready | (!temp_m_eth_payload_axis_tvalid_reg && (!m_eth_payload_axis_tvalid_reg | !m_eth_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + + store_eth_payload_int_to_output = 1'b0; + store_eth_payload_int_to_temp = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b0; + + if (m_eth_payload_axis_tready_int_reg) begin + // input is ready + if (m_eth_payload_axis_tready | !m_eth_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_eth_payload_axis_tvalid_next = m_eth_payload_axis_tvalid_int; + store_eth_payload_int_to_temp = 1'b1; + end + end else if (m_eth_payload_axis_tready) begin + // input is not ready, but output is ready + m_eth_payload_axis_tvalid_next = temp_m_eth_payload_axis_tvalid_reg; + temp_m_eth_payload_axis_tvalid_next = 1'b0; + store_eth_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_eth_payload_axis_tvalid_reg <= 1'b0; + m_eth_payload_axis_tready_int_reg <= 1'b0; + temp_m_eth_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_eth_payload_axis_tvalid_reg <= m_eth_payload_axis_tvalid_next; + m_eth_payload_axis_tready_int_reg <= m_eth_payload_axis_tready_int_early; + temp_m_eth_payload_axis_tvalid_reg <= temp_m_eth_payload_axis_tvalid_next; + end + + // datapath + if (store_eth_payload_int_to_output) begin + m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end else if (store_eth_payload_axis_temp_to_output) begin + m_eth_payload_axis_tdata_reg <= temp_m_eth_payload_axis_tdata_reg; + m_eth_payload_axis_tkeep_reg <= temp_m_eth_payload_axis_tkeep_reg; + m_eth_payload_axis_tlast_reg <= temp_m_eth_payload_axis_tlast_reg; + m_eth_payload_axis_tuser_reg <= temp_m_eth_payload_axis_tuser_reg; + end + + if (store_eth_payload_int_to_temp) begin + temp_m_eth_payload_axis_tdata_reg <= m_eth_payload_axis_tdata_int; + temp_m_eth_payload_axis_tkeep_reg <= m_eth_payload_axis_tkeep_int; + temp_m_eth_payload_axis_tlast_reg <= m_eth_payload_axis_tlast_int; + temp_m_eth_payload_axis_tuser_reg <= m_eth_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ip_mux.v b/corundum/lib/eth/rtl/ip_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..f05edf6b3c5d4f3ca69db04684d568de73487479 --- /dev/null +++ b/corundum/lib/eth/rtl/ip_mux.v @@ -0,0 +1,390 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IP multiplexer + */ +module ip_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * IP frame inputs + */ + input wire [S_COUNT-1:0] s_ip_hdr_valid, + output wire [S_COUNT-1:0] s_ip_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*DATA_WIDTH-1:0] s_ip_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_ip_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_ip_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_ip_payload_axis_tready, + input wire [S_COUNT-1:0] s_ip_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_ip_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_ip_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [DATA_WIDTH-1:0] m_ip_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_ip_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_ip_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_ip_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_ip_hdr_ready_reg = 0, s_ip_hdr_ready_next; + +reg [S_COUNT-1:0] s_ip_payload_axis_tready_reg = 0, s_ip_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; + +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_ip_payload_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_ip_payload_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_ip_payload_axis_tvalid[select_reg]; +wire current_s_tready = s_ip_payload_axis_tready[select_reg]; +wire current_s_tlast = s_ip_payload_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_ip_payload_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_ip_payload_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_ip_payload_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_ip_hdr_ready_next = 0; + + s_ip_payload_axis_tready_next = 0; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && !m_ip_hdr_valid && (s_ip_hdr_valid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + + s_ip_hdr_ready_next = (1 << select); + + m_ip_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[select*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[select*48 +: 48]; + m_eth_type_next = s_eth_type[select*16 +: 16]; + m_ip_version_next = s_ip_version[select*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[select*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[select*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[select*2 +: 2]; + m_ip_length_next = s_ip_length[select*16 +: 16]; + m_ip_identification_next = s_ip_identification[select*16 +: 16]; + m_ip_flags_next = s_ip_flags[select*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[select*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[select*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[select*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[select*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[select*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[select*32 +: 32]; + end + + // generate ready signal on selected port + s_ip_payload_axis_tready_next = (m_ip_payload_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_ip_payload_axis_tdata_int = current_s_tdata; + m_ip_payload_axis_tkeep_int = current_s_tkeep; + m_ip_payload_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_ip_payload_axis_tlast_int = current_s_tlast; + m_ip_payload_axis_tid_int = current_s_tid; + m_ip_payload_axis_tdest_int = current_s_tdest; + m_ip_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_ip_hdr_ready_reg <= 0; + s_ip_payload_axis_tready_reg <= 0; + m_ip_hdr_valid_reg <= 1'b0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_ip_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_ip_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_ip_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_ip_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_ip_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = KEEP_ENABLE ? m_ip_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tid = ID_ENABLE ? m_ip_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_ip_payload_axis_tdest = DEST_ENABLE ? m_ip_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_ip_payload_axis_tuser = USER_ENABLE ? m_ip_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tid_reg <= temp_m_ip_payload_axis_tid_reg; + m_ip_payload_axis_tdest_reg <= temp_m_ip_payload_axis_tdest_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tid_reg <= m_ip_payload_axis_tid_int; + temp_m_ip_payload_axis_tdest_reg <= m_ip_payload_axis_tdest_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/lfsr.v b/corundum/lib/eth/rtl/lfsr.v new file mode 100644 index 0000000000000000000000000000000000000000..1548daef709f0e9418195daf925ef2cdfa975c3f --- /dev/null +++ b/corundum/lib/eth/rtl/lfsr.v @@ -0,0 +1,442 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Parametrizable combinatorial parallel LFSR/CRC + */ +module lfsr # +( + // width of LFSR + parameter LFSR_WIDTH = 31, + // LFSR polynomial + parameter LFSR_POLY = 31'h10000001, + // LFSR configuration: "GALOIS", "FIBONACCI" + parameter LFSR_CONFIG = "FIBONACCI", + // LFSR feed forward enable + parameter LFSR_FEED_FORWARD = 0, + // bit-reverse input and output + parameter REVERSE = 0, + // width of data input + parameter DATA_WIDTH = 8, + // implementation style: "AUTO", "LOOP", "REDUCTION" + parameter STYLE = "AUTO" +) +( + input wire [DATA_WIDTH-1:0] data_in, + input wire [LFSR_WIDTH-1:0] state_in, + output wire [DATA_WIDTH-1:0] data_out, + output wire [LFSR_WIDTH-1:0] state_out +); + +/* + +Fully parametrizable combinatorial parallel LFSR/CRC module. Implements an unrolled LFSR +next state computation, shifting DATA_WIDTH bits per pass through the module. Input data +is XORed with LFSR feedback path, tie data_in to zero if this is not required. + +Works in two parts: statically computes a set of bit masks, then uses these bit masks to +select bits for XORing to compute the next state. + +Ports: + +data_in + +Data bits to be shifted through the LFSR (DATA_WIDTH bits) + +state_in + +LFSR/CRC current state input (LFSR_WIDTH bits) + +data_out + +Data bits shifted out of LFSR (DATA_WIDTH bits) + +state_out + +LFSR/CRC next state output (LFSR_WIDTH bits) + +Parameters: + +LFSR_WIDTH + +Specify width of LFSR/CRC register + +LFSR_POLY + +Specify the LFSR/CRC polynomial in hex format. For example, the polynomial + +x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + +would be represented as + +32'h04c11db7 + +Note that the largest term (x^32) is suppressed. This term is generated automatically based +on LFSR_WIDTH. + +LFSR_CONFIG + +Specify the LFSR configuration, either Fibonacci or Galois. Fibonacci is generally used +for linear-feedback shift registers (LFSR) for pseudorandom binary sequence (PRBS) generators, +scramblers, and descrambers, while Galois is generally used for cyclic redundancy check +generators and checkers. + +Fibonacci style (example for 64b66b scrambler, 0x8000000001) + + DIN (LSB first) + | + V + (+)<---------------------------(+)<-----------------------------. + | ^ | + | .----. .----. .----. | .----. .----. .----. | + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--' + | '----' '----' '----' '----' '----' '----' + V + DOUT + +Galois style (example for CRC16, 0x8005) + + ,-------------------+-------------------------+----------(+)<-- DIN (MSB first) + | | | ^ + | .----. .----. V .----. .----. V .----. | + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |--+---> DOUT + '----' '----' '----' '----' '----' + +LFSR_FEED_FORWARD + +Generate feed forward instead of feed back LFSR. Enable this for PRBS checking and self- +synchronous descrambling. + +Fibonacci feed-forward style (example for 64b66b descrambler, 0x8000000001) + + DIN (LSB first) + | + | .----. .----. .----. .----. .----. .----. + +->| 0 |->| 1 |->...->| 38 |-+->| 39 |->...->| 56 |->| 57 |--. + | '----' '----' '----' | '----' '----' '----' | + | V | + (+)<---------------------------(+)------------------------------' + | + V + DOUT + +Galois feed-forward style + + ,-------------------+-------------------------+------------+--- DIN (MSB first) + | | | | + | .----. .----. V .----. .----. V .----. V + `->| 0 |->| 1 |->(+)->| 2 |->...->| 14 |->(+)->| 15 |->(+)-> DOUT + '----' '----' '----' '----' '----' + +REVERSE + +Bit-reverse LFSR input and output. Shifts MSB first by default, set REVERSE for LSB first. + +DATA_WIDTH + +Specify width of input and output data bus. The module will perform one shift per input +data bit, so if the input data bus is not required tie data_in to zero and set DATA_WIDTH +to the required number of shifts per clock cycle. + +STYLE + +Specify implementation style. Can be "AUTO", "LOOP", or "REDUCTION". When "AUTO" +is selected, implemenation will be "LOOP" or "REDUCTION" based on synthesis translate +directives. "REDUCTION" and "LOOP" are functionally identical, however they simulate +and synthesize differently. "REDUCTION" is implemented with a loop over a Verilog +reduction operator. "LOOP" is implemented as a doubly-nested loop with no reduction +operator. "REDUCTION" is very fast for simulation in iverilog and synthesizes well in +Quartus but synthesizes poorly in ISE, likely due to large inferred XOR gates causing +problems with the optimizer. "LOOP" synthesizes will in both ISE and Quartus. "AUTO" +will default to "REDUCTION" when simulating and "LOOP" for synthesizers that obey +synthesis translate directives. + +Settings for common LFSR/CRC implementations: + +Name Configuration Length Polynomial Initial value Notes +CRC16-IBM Galois, bit-reverse 16 16'h8005 16'hffff +CRC16-CCITT Galois 16 16'h1021 16'h1d0f +CRC32 Galois, bit-reverse 32 32'h04c11db7 32'hffffffff Ethernet FCS; invert final output +PRBS6 Fibonacci 6 6'h21 any +PRBS7 Fibonacci 7 7'h41 any +PRBS9 Fibonacci 9 9'h021 any ITU V.52 +PRBS10 Fibonacci 10 10'h081 any ITU +PRBS11 Fibonacci 11 11'h201 any ITU O.152 +PRBS15 Fibonacci, inverted 15 15'h4001 any ITU O.152 +PRBS17 Fibonacci 17 17'h04001 any +PRBS20 Fibonacci 20 20'h00009 any ITU V.57 +PRBS23 Fibonacci, inverted 23 23'h040001 any ITU O.151 +PRBS29 Fibonacci, inverted 29 29'h08000001 any +PRBS31 Fibonacci, inverted 31 31'h10000001 any +64b66b Fibonacci, bit-reverse 58 58'h8000000001 any 10G Ethernet +128b130b Galois, bit-reverse 23 23'h210125 any PCIe gen 3 + +*/ + +reg [LFSR_WIDTH-1:0] lfsr_mask_state[LFSR_WIDTH-1:0]; +reg [DATA_WIDTH-1:0] lfsr_mask_data[LFSR_WIDTH-1:0]; +reg [LFSR_WIDTH-1:0] output_mask_state[DATA_WIDTH-1:0]; +reg [DATA_WIDTH-1:0] output_mask_data[DATA_WIDTH-1:0]; + +reg [LFSR_WIDTH-1:0] state_val = 0; +reg [DATA_WIDTH-1:0] data_val = 0; + +integer i, j, k; + +initial begin + // init bit masks + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + lfsr_mask_state[i] = {LFSR_WIDTH{1'b0}}; + lfsr_mask_state[i][i] = 1'b1; + lfsr_mask_data[i] = {DATA_WIDTH{1'b0}}; + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + output_mask_state[i] = {LFSR_WIDTH{1'b0}}; + if (i < LFSR_WIDTH) begin + output_mask_state[i][i] = 1'b1; + end + output_mask_data[i] = {DATA_WIDTH{1'b0}}; + end + + // simulate shift register + if (LFSR_CONFIG == "FIBONACCI") begin + // Fibonacci configuration + for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ (1 << i); + + // add XOR inputs from correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if (LFSR_POLY & (1 << j)) begin + state_val = lfsr_mask_state[j-1] ^ state_val; + data_val = lfsr_mask_data[j-1] ^ data_val; + end + end + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = 1 << i; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + end + end else if (LFSR_CONFIG == "GALOIS") begin + // Galois configuration + for (i = DATA_WIDTH-1; i >= 0; i = i - 1) begin + // determine shift in value + // current value in last FF, XOR with input data bit (MSB first) + state_val = lfsr_mask_state[LFSR_WIDTH-1]; + data_val = lfsr_mask_data[LFSR_WIDTH-1]; + data_val = data_val ^ (1 << i); + + // shift + for (j = LFSR_WIDTH-1; j > 0; j = j - 1) begin + lfsr_mask_state[j] = lfsr_mask_state[j-1]; + lfsr_mask_data[j] = lfsr_mask_data[j-1]; + end + for (j = DATA_WIDTH-1; j > 0; j = j - 1) begin + output_mask_state[j] = output_mask_state[j-1]; + output_mask_data[j] = output_mask_data[j-1]; + end + output_mask_state[0] = state_val; + output_mask_data[0] = data_val; + if (LFSR_FEED_FORWARD) begin + // only shift in new input data + state_val = {LFSR_WIDTH{1'b0}}; + data_val = 1 << i; + end + lfsr_mask_state[0] = state_val; + lfsr_mask_data[0] = data_val; + + // add XOR inputs at correct indicies + for (j = 1; j < LFSR_WIDTH; j = j + 1) begin + if (LFSR_POLY & (1 << j)) begin + lfsr_mask_state[j] = lfsr_mask_state[j] ^ state_val; + lfsr_mask_data[j] = lfsr_mask_data[j] ^ data_val; + end + end + end + end else begin + $error("Error: unknown configuration setting!"); + $finish; + end + + // reverse bits if selected + if (REVERSE) begin + // reverse order + for (i = 0; i < LFSR_WIDTH/2; i = i + 1) begin + state_val = lfsr_mask_state[i]; + data_val = lfsr_mask_data[i]; + lfsr_mask_state[i] = lfsr_mask_state[LFSR_WIDTH-i-1]; + lfsr_mask_data[i] = lfsr_mask_data[LFSR_WIDTH-i-1]; + lfsr_mask_state[LFSR_WIDTH-i-1] = state_val; + lfsr_mask_data[LFSR_WIDTH-i-1] = data_val; + end + for (i = 0; i < DATA_WIDTH/2; i = i + 1) begin + state_val = output_mask_state[i]; + data_val = output_mask_data[i]; + output_mask_state[i] = output_mask_state[DATA_WIDTH-i-1]; + output_mask_data[i] = output_mask_data[DATA_WIDTH-i-1]; + output_mask_state[DATA_WIDTH-i-1] = state_val; + output_mask_data[DATA_WIDTH-i-1] = data_val; + end + // reverse bits + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_val = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + state_val[j] = lfsr_mask_state[i][LFSR_WIDTH-j-1]; + end + lfsr_mask_state[i] = state_val; + + data_val = 0; + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + data_val[j] = lfsr_mask_data[i][DATA_WIDTH-j-1]; + end + lfsr_mask_data[i] = data_val; + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + state_val = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + state_val[j] = output_mask_state[i][LFSR_WIDTH-j-1]; + end + output_mask_state[i] = state_val; + + data_val = 0; + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + data_val[j] = output_mask_data[i][DATA_WIDTH-j-1]; + end + output_mask_data[i] = data_val; + end + end + + // for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + // $display("%b %b", lfsr_mask_state[i], lfsr_mask_data[i]); + // end +end + +// synthesis translate_off +`define SIMULATION +// synthesis translate_on + +`ifdef SIMULATION +// "AUTO" style is "REDUCTION" for faster simulation +parameter STYLE_INT = (STYLE == "AUTO") ? "REDUCTION" : STYLE; +`else +// "AUTO" style is "LOOP" for better synthesis result +parameter STYLE_INT = (STYLE == "AUTO") ? "LOOP" : STYLE; +`endif + +genvar n; + +generate + +if (STYLE_INT == "REDUCTION") begin + + // use Verilog reduction operator + // fast in iverilog + // significantly larger than generated code with ISE (inferred wide XORs may be tripping up optimizer) + // slightly smaller than generated code with Quartus + // --> better for simulation + + for (n = 0; n < LFSR_WIDTH; n = n + 1) begin : loop1 + assign state_out[n] = ^{(state_in & lfsr_mask_state[n]), (data_in & lfsr_mask_data[n])}; + end + for (n = 0; n < DATA_WIDTH; n = n + 1) begin : loop2 + assign data_out[n] = ^{(state_in & output_mask_state[n]), (data_in & output_mask_data[n])}; + end + +end else if (STYLE_INT == "LOOP") begin + + // use nested loops + // very slow in iverilog + // slightly smaller than generated code with ISE + // same size as generated code with Quartus + // --> better for synthesis + + reg [LFSR_WIDTH-1:0] state_out_reg = 0; + reg [DATA_WIDTH-1:0] data_out_reg = 0; + + assign state_out = state_out_reg; + assign data_out = data_out_reg; + + always @* begin + for (i = 0; i < LFSR_WIDTH; i = i + 1) begin + state_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (lfsr_mask_state[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (lfsr_mask_data[i][j]) begin + state_out_reg[i] = state_out_reg[i] ^ data_in[j]; + end + end + end + for (i = 0; i < DATA_WIDTH; i = i + 1) begin + data_out_reg[i] = 0; + for (j = 0; j < LFSR_WIDTH; j = j + 1) begin + if (output_mask_state[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ state_in[j]; + end + end + for (j = 0; j < DATA_WIDTH; j = j + 1) begin + if (output_mask_data[i][j]) begin + data_out_reg[i] = data_out_reg[i] ^ data_in[j]; + end + end + end + end + +end else begin + + initial begin + $error("Error: unknown style setting!"); + $finish; + end + +end + +endgenerate + +endmodule diff --git a/corundum/lib/eth/rtl/mii_phy_if.v b/corundum/lib/eth/rtl/mii_phy_if.v new file mode 100644 index 0000000000000000000000000000000000000000..0e8cb8ce2c3a8ead31249f7d211d1e55c18f1517 --- /dev/null +++ b/corundum/lib/eth/rtl/mii_phy_if.v @@ -0,0 +1,137 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * MII PHY interface + */ +module mii_phy_if # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2" +) +( + input wire rst, + + /* + * MII interface to MAC + */ + output wire mac_mii_rx_clk, + output wire mac_mii_rx_rst, + output wire [3:0] mac_mii_rxd, + output wire mac_mii_rx_dv, + output wire mac_mii_rx_er, + output wire mac_mii_tx_clk, + output wire mac_mii_tx_rst, + input wire [3:0] mac_mii_txd, + input wire mac_mii_tx_en, + input wire mac_mii_tx_er, + + /* + * MII interface to PHY + */ + input wire phy_mii_rx_clk, + input wire [3:0] phy_mii_rxd, + input wire phy_mii_rx_dv, + input wire phy_mii_rx_er, + input wire phy_mii_tx_clk, + output wire [3:0] phy_mii_txd, + output wire phy_mii_tx_en, + output wire phy_mii_tx_er +); + +ssio_sdr_in # +( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(6) +) +rx_ssio_sdr_inst ( + .input_clk(phy_mii_rx_clk), + .input_d({phy_mii_rxd, phy_mii_rx_dv, phy_mii_rx_er}), + .output_clk(mac_mii_rx_clk), + .output_q({mac_mii_rxd, mac_mii_rx_dv, mac_mii_rx_er}) +); + +(* IOB = "TRUE" *) +reg [3:0] phy_mii_txd_reg = 4'd0; +(* IOB = "TRUE" *) +reg phy_mii_tx_en_reg = 1'b0, phy_mii_tx_er_reg = 1'b0; + +assign phy_mii_txd = phy_mii_txd_reg; +assign phy_mii_tx_en = phy_mii_tx_en_reg; +assign phy_mii_tx_er = phy_mii_tx_er_reg; + +always @(posedge mac_mii_tx_clk) begin + phy_mii_txd_reg <= mac_mii_txd; + phy_mii_tx_en_reg <= mac_mii_tx_en; + phy_mii_tx_er_reg <= mac_mii_tx_er; +end + +generate + +if (TARGET == "XILINX") begin + BUFG + mii_bufg_inst ( + .I(phy_mii_tx_clk), + .O(mac_mii_tx_clk) + ); +end else begin + assign mac_mii_tx_clk = phy_mii_tx_clk; +end + +endgenerate + +// reset sync +reg [3:0] tx_rst_reg = 4'hf; +assign mac_mii_tx_rst = tx_rst_reg[0]; + +always @(posedge mac_mii_tx_clk or posedge rst) begin + if (rst) begin + tx_rst_reg <= 4'hf; + end else begin + tx_rst_reg <= {1'b0, tx_rst_reg[3:1]}; + end +end + +reg [3:0] rx_rst_reg = 4'hf; +assign mac_mii_rx_rst = rx_rst_reg[0]; + +always @(posedge mac_mii_rx_clk or posedge rst) begin + if (rst) begin + rx_rst_reg <= 4'hf; + end else begin + rx_rst_reg <= {1'b0, rx_rst_reg[3:1]}; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/oddr.v b/corundum/lib/eth/rtl/oddr.v new file mode 100644 index 0000000000000000000000000000000000000000..db2d1442988080e8d99496b51391560654833e2e --- /dev/null +++ b/corundum/lib/eth/rtl/oddr.v @@ -0,0 +1,142 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic ODDR module + */ +module oddr # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] d1, + input wire [WIDTH-1:0] d2, + + output wire [WIDTH-1:0] q +); + +/* + +Provides a consistent output DDR flip flop across multiple FPGA families + _____ _____ _____ _____ + clk ____/ \_____/ \_____/ \_____/ \_____ + _ ___________ ___________ ___________ ___________ __ + d1 _X____D0_____X____D2_____X____D4_____X____D6_____X__ + _ ___________ ___________ ___________ ___________ __ + d2 _X____D1_____X____D3_____X____D5_____X____D7_____X__ + _____ _____ _____ _____ _____ _____ _____ _____ ____ + d _____X_D0__X_D1__X_D2__X_D3__X_D4__X_D5__X_D6__X_D7_ + +*/ + +genvar n; + +generate + +if (TARGET == "XILINX") begin + for (n = 0; n < WIDTH; n = n + 1) begin : oddr + if (IODDR_STYLE == "IODDR") begin + ODDR #( + .DDR_CLK_EDGE("SAME_EDGE"), + .SRTYPE("ASYNC") + ) + oddr_inst ( + .Q(q[n]), + .C(clk), + .CE(1'b1), + .D1(d1[n]), + .D2(d2[n]), + .R(1'b0), + .S(1'b0) + ); + end else if (IODDR_STYLE == "IODDR2") begin + ODDR2 #( + .DDR_ALIGNMENT("C0"), + .SRTYPE("ASYNC") + ) + oddr_inst ( + .Q(q[n]), + .C0(clk), + .C1(~clk), + .CE(1'b1), + .D0(d1[n]), + .D1(d2[n]), + .R(1'b0), + .S(1'b0) + ); + end + end +end else if (TARGET == "ALTERA") begin + altddio_out #( + .WIDTH(WIDTH), + .POWER_UP_HIGH("OFF"), + .OE_REG("UNUSED") + ) + altddio_out_inst ( + .aset(1'b0), + .datain_h(d1), + .datain_l(d2), + .outclocken(1'b1), + .outclock(clk), + .aclr(1'b0), + .dataout(q) + ); +end else begin + reg [WIDTH-1:0] d_reg_1 = {WIDTH{1'b0}}; + reg [WIDTH-1:0] d_reg_2 = {WIDTH{1'b0}}; + + reg [WIDTH-1:0] q_reg = {WIDTH{1'b0}}; + + always @(posedge clk) begin + d_reg_1 <= d1; + d_reg_2 <= d2; + end + + always @(posedge clk) begin + q_reg <= d1; + end + + always @(negedge clk) begin + q_reg <= d_reg_2; + end + + assign q = q_reg; +end + +endgenerate + +endmodule diff --git a/corundum/lib/eth/rtl/ptp_clock.v b/corundum/lib/eth/rtl/ptp_clock.v new file mode 100644 index 0000000000000000000000000000000000000000..f1f27d64685b1497a8fef888fd5891c70d56cdcb --- /dev/null +++ b/corundum/lib/eth/rtl/ptp_clock.v @@ -0,0 +1,246 @@ +/* + +Copyright (c) 2015-2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PTP clock module + */ +module ptp_clock # +( + parameter PERIOD_NS_WIDTH = 4, + parameter OFFSET_NS_WIDTH = 4, + parameter DRIFT_NS_WIDTH = 4, + parameter FNS_WIDTH = 16, + parameter PERIOD_NS = 4'h6, + parameter PERIOD_FNS = 16'h6666, + parameter DRIFT_ENABLE = 1, + parameter DRIFT_NS = 4'h0, + parameter DRIFT_FNS = 16'h0002, + parameter DRIFT_RATE = 16'h0005 +) +( + input wire clk, + input wire rst, + + /* + * Timestamp inputs for synchronization + */ + input wire [95:0] input_ts_96, + input wire input_ts_96_valid, + input wire [63:0] input_ts_64, + input wire input_ts_64_valid, + + /* + * Period adjustment + */ + input wire [PERIOD_NS_WIDTH-1:0] input_period_ns, + input wire [FNS_WIDTH-1:0] input_period_fns, + input wire input_period_valid, + + /* + * Offset adjustment + */ + input wire [OFFSET_NS_WIDTH-1:0] input_adj_ns, + input wire [FNS_WIDTH-1:0] input_adj_fns, + input wire [15:0] input_adj_count, + input wire input_adj_valid, + output wire input_adj_active, + + /* + * Drift adjustment + */ + input wire [DRIFT_NS_WIDTH-1:0] input_drift_ns, + input wire [FNS_WIDTH-1:0] input_drift_fns, + input wire [15:0] input_drift_rate, + input wire input_drift_valid, + + /* + * Timestamp outputs + */ + output wire [95:0] output_ts_96, + output wire [63:0] output_ts_64, + output wire output_ts_step, + + /* + * PPS output + */ + output wire output_pps +); + +parameter INC_NS_WIDTH = $clog2(2**PERIOD_NS_WIDTH + 2**OFFSET_NS_WIDTH + 2**DRIFT_NS_WIDTH); + +reg [PERIOD_NS_WIDTH-1:0] period_ns_reg = PERIOD_NS; +reg [FNS_WIDTH-1:0] period_fns_reg = PERIOD_FNS; + +reg [OFFSET_NS_WIDTH-1:0] adj_ns_reg = 0; +reg [FNS_WIDTH-1:0] adj_fns_reg = 0; +reg [15:0] adj_count_reg = 0; +reg adj_active_reg = 0; + +reg [DRIFT_NS_WIDTH-1:0] drift_ns_reg = DRIFT_NS; +reg [FNS_WIDTH-1:0] drift_fns_reg = DRIFT_FNS; +reg [15:0] drift_rate_reg = DRIFT_RATE; + +reg [INC_NS_WIDTH-1:0] ts_inc_ns_reg = 0; +reg [FNS_WIDTH-1:0] ts_inc_fns_reg = 0; + +reg [47:0] ts_96_s_reg = 0; +reg [29:0] ts_96_ns_reg = 0; +reg [FNS_WIDTH-1:0] ts_96_fns_reg = 0; +reg [29:0] ts_96_ns_inc_reg = 0; +reg [FNS_WIDTH-1:0] ts_96_fns_inc_reg = 0; +reg [30:0] ts_96_ns_ovf_reg = 31'h7fffffff; +reg [FNS_WIDTH-1:0] ts_96_fns_ovf_reg = 16'hffff; + +reg [47:0] ts_64_ns_reg = 0; +reg [FNS_WIDTH-1:0] ts_64_fns_reg = 0; + +reg ts_step_reg = 1'b0; + +reg [15:0] drift_cnt = 0; + +reg [47:0] temp; + +reg pps_reg = 0; + +assign input_adj_active = adj_active_reg; + +assign output_ts_96[95:48] = ts_96_s_reg; +assign output_ts_96[47:46] = 2'b00; +assign output_ts_96[45:16] = ts_96_ns_reg; +assign output_ts_96[15:0] = FNS_WIDTH > 16 ? ts_96_fns_reg >> (FNS_WIDTH-16) : ts_96_fns_reg << (16-FNS_WIDTH); +assign output_ts_64[63:16] = ts_64_ns_reg; +assign output_ts_64[15:0] = FNS_WIDTH > 16 ? ts_64_fns_reg >> (FNS_WIDTH-16) : ts_64_fns_reg << (16-FNS_WIDTH); +assign output_ts_step = ts_step_reg; + +assign output_pps = pps_reg; + +always @(posedge clk) begin + ts_step_reg <= 0; + + // latch parameters + if (input_period_valid) begin + period_ns_reg <= input_period_ns; + period_fns_reg <= input_period_fns; + end + + if (input_adj_valid) begin + adj_ns_reg <= input_adj_ns; + adj_fns_reg <= input_adj_fns; + adj_count_reg <= input_adj_count; + end + + if (DRIFT_ENABLE && input_drift_valid) begin + drift_ns_reg <= input_drift_ns; + drift_fns_reg <= input_drift_fns; + drift_rate_reg <= input_drift_rate; + end + + // timestamp increment calculation + {ts_inc_ns_reg, ts_inc_fns_reg} <= $signed({period_ns_reg, period_fns_reg}) + + (adj_active_reg ? $signed({adj_ns_reg, adj_fns_reg}) : 0) + + ((DRIFT_ENABLE && drift_cnt == 0) ? $signed({drift_ns_reg, drift_fns_reg}) : 0); + + // offset adjust counter + if (adj_count_reg > 0) begin + adj_count_reg <= adj_count_reg - 1; + adj_active_reg <= 1; + ts_step_reg <= 1; + end else begin + adj_active_reg <= 0; + end + + // drift counter + if (drift_cnt == 0) begin + drift_cnt <= drift_rate_reg-1; + end else begin + drift_cnt <= drift_cnt - 1; + end + + // 96 bit timestamp + if (input_ts_96_valid) begin + // load timestamp + {ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= (FNS_WIDTH > 16 ? input_ts_96[45:0] << (FNS_WIDTH-16) : input_ts_96[45:0] >> (16-FNS_WIDTH)) + {ts_inc_ns_reg, ts_inc_fns_reg}; + {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= (FNS_WIDTH > 16 ? input_ts_96[45:0] << (FNS_WIDTH-16) : input_ts_96[45:0] >> (16-FNS_WIDTH)) + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + ts_96_s_reg <= input_ts_96[95:48]; + ts_96_ns_reg <= input_ts_96[45:16]; + ts_96_fns_reg <= FNS_WIDTH > 16 ? input_ts_96[15:0] << (FNS_WIDTH-16) : input_ts_96[15:0] >> (16-FNS_WIDTH); + ts_step_reg <= 1; + end else if (!ts_96_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + // increment seconds field, pre-compute both normal increment and overflow values + {ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} + {ts_inc_ns_reg, ts_inc_fns_reg}; + {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + {ts_96_ns_reg, ts_96_fns_reg} <= {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg}; + ts_96_s_reg <= ts_96_s_reg + 1; + end else begin + // no increment seconds field, pre-compute both normal increment and overflow values + {ts_96_ns_inc_reg, ts_96_fns_inc_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg} + {ts_inc_ns_reg, ts_inc_fns_reg}; + {ts_96_ns_ovf_reg, ts_96_fns_ovf_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg} + {ts_inc_ns_reg, ts_inc_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + {ts_96_ns_reg, ts_96_fns_reg} <= {ts_96_ns_inc_reg, ts_96_fns_inc_reg}; + ts_96_s_reg <= ts_96_s_reg; + end + + // 64 bit timestamp + if (input_ts_64_valid) begin + // load timestamp + {ts_64_ns_reg, ts_64_fns_reg} <= input_ts_64; + ts_step_reg <= 1; + end else begin + {ts_64_ns_reg, ts_64_fns_reg} <= {ts_64_ns_reg, ts_64_fns_reg} + {ts_inc_ns_reg, ts_inc_fns_reg}; + end + + pps_reg <= !ts_96_ns_ovf_reg[30]; + + if (rst) begin + period_ns_reg <= PERIOD_NS; + period_fns_reg <= PERIOD_FNS; + adj_ns_reg <= 0; + adj_fns_reg <= 0; + adj_count_reg <= 0; + adj_active_reg <= 0; + drift_ns_reg <= DRIFT_NS; + drift_fns_reg <= DRIFT_FNS; + drift_rate_reg <= DRIFT_RATE; + ts_inc_ns_reg <= 0; + ts_inc_fns_reg <= 0; + ts_96_s_reg <= 0; + ts_96_ns_reg <= 0; + ts_96_fns_reg <= 0; + ts_96_ns_inc_reg <= 0; + ts_96_fns_inc_reg <= 0; + ts_96_ns_ovf_reg <= 31'h7fffffff; + ts_96_fns_ovf_reg <= {FNS_WIDTH{1'b1}}; + ts_64_ns_reg <= 0; + ts_64_fns_reg <= 0; + ts_step_reg <= 0; + drift_cnt <= 0; + pps_reg <= 0; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ptp_clock_cdc.v b/corundum/lib/eth/rtl/ptp_clock_cdc.v new file mode 100644 index 0000000000000000000000000000000000000000..8045abca2e3e514ecb2e4aca53b3a2c7610952eb --- /dev/null +++ b/corundum/lib/eth/rtl/ptp_clock_cdc.v @@ -0,0 +1,464 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PTP clock CDC (clock domain crossing) module + */ +module ptp_clock_cdc # +( + parameter TS_WIDTH = 96, + parameter NS_WIDTH = 4, + parameter FNS_WIDTH = 16, + parameter INPUT_PERIOD_NS = 4'h6, + parameter INPUT_PERIOD_FNS = 16'h6666, + parameter OUTPUT_PERIOD_NS = 4'h6, + parameter OUTPUT_PERIOD_FNS = 16'h6666, + parameter USE_SAMPLE_CLOCK = 1, + parameter LOG_FIFO_DEPTH = 3, + parameter LOG_RATE = 3 +) +( + input wire input_clk, + input wire input_rst, + input wire output_clk, + input wire output_rst, + input wire sample_clk, + + /* + * Timestamp inputs from source PTP clock + */ + input wire [TS_WIDTH-1:0] input_ts, + + /* + * Timestamp outputs + */ + output wire [TS_WIDTH-1:0] output_ts, + output wire output_ts_step, + + /* + * PPS output + */ + output wire output_pps +); + +// bus width assertions +initial begin + if (TS_WIDTH != 64 && TS_WIDTH != 96) begin + $error("Error: Timestamp width must be 64 or 96"); + $finish; + end +end + +parameter TS_NS_WIDTH = TS_WIDTH == 96 ? 30 : 48; + +parameter FIFO_ADDR_WIDTH = LOG_FIFO_DEPTH+1; +parameter LOG_AVG = 6; +parameter LOG_AVG_SCALE = LOG_AVG+8; +parameter LOG_AVG_SYNC_RATE = LOG_RATE; +parameter WR_PERIOD = ((((INPUT_PERIOD_NS << 16) + INPUT_PERIOD_FNS) + 64'd0) << 16) / ((OUTPUT_PERIOD_NS << 16) + OUTPUT_PERIOD_FNS) / 2**(LOG_RATE+1); + +reg [NS_WIDTH-1:0] period_ns_reg = OUTPUT_PERIOD_NS; +reg [FNS_WIDTH-1:0] period_fns_reg = OUTPUT_PERIOD_FNS; + +reg [47:0] ts_s_reg = 0; +reg [TS_NS_WIDTH-1:0] ts_ns_reg = 0; +reg [FNS_WIDTH-1:0] ts_fns_reg = 0; +reg [TS_NS_WIDTH-1:0] ts_ns_inc_reg = 0; +reg [FNS_WIDTH-1:0] ts_fns_inc_reg = 0; +reg [TS_NS_WIDTH+1-1:0] ts_ns_ovf_reg = {TS_NS_WIDTH+1{1'b1}}; +reg [FNS_WIDTH-1:0] ts_fns_ovf_reg = {FNS_WIDTH{1'b1}}; + +reg ts_step_reg = 1'b0; + +reg pps_reg = 0; + +reg [FIFO_ADDR_WIDTH:0] wr_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, wr_ptr_next; +reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, wr_ptr_gray_next; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, rd_ptr_next; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, rd_ptr_gray_next; + +reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +wire [FIFO_ADDR_WIDTH:0] wr_ptr_sync2; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +wire [FIFO_ADDR_WIDTH:0] rd_ptr_sync2; + +reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sample_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sample_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +wire [FIFO_ADDR_WIDTH:0] wr_ptr_sample_sync2; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sample_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sample_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}}; +wire [FIFO_ADDR_WIDTH:0] rd_ptr_sample_sync2; + +reg [15:0] wr_acc_reg = 16'd0, wr_acc_next; +reg [15:0] wr_inc_reg = WR_PERIOD, wr_inc_next; +reg [31:0] err_int_reg = 0, err_int_next; + +reg [LOG_RATE-1:0] rd_cnt_reg = {LOG_RATE{1'b0}}, rd_cnt_next; + +reg [LOG_FIFO_DEPTH+LOG_AVG_SCALE+2-1:0] sample_acc_reg = 0; +reg [LOG_FIFO_DEPTH+LOG_AVG_SCALE+2-1:0] sample_avg_reg = 0; +reg [LOG_AVG_SYNC_RATE-1:0] sample_cnt_reg = 0; +reg sample_update_reg = 1'b0; +reg sample_update_sync1_reg = 1'b0; +reg sample_update_sync2_reg = 1'b0; +reg sample_update_sync3_reg = 1'b0; + +reg [TS_WIDTH-1:0] mem[(2**FIFO_ADDR_WIDTH)-1:0]; +reg [TS_WIDTH-1:0] mem_read_data_reg = 0; + +// full when first TWO MSBs do NOT match, but rest matches +// (gray code equivalent of first MSB different but rest same) +wire full = ((wr_ptr_gray_reg[FIFO_ADDR_WIDTH] != rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH]) && + (wr_ptr_gray_reg[FIFO_ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH-1]) && + (wr_ptr_gray_reg[FIFO_ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH-2:0])); +// empty when pointers match exactly +wire empty = rd_ptr_gray_reg == wr_ptr_gray_sync2_reg; + +wire [FIFO_ADDR_WIDTH:0] wr_depth = wr_ptr_reg - rd_ptr_sync2; +wire [FIFO_ADDR_WIDTH:0] rd_depth = wr_ptr_sync2 - rd_ptr_reg; +wire [FIFO_ADDR_WIDTH:0] sample_depth = wr_ptr_sample_sync2 - rd_ptr_sample_sync2; + +// control signals +reg write; +reg read; + +generate + +if (TS_WIDTH == 96) begin + assign output_ts[95:48] = ts_s_reg; + assign output_ts[47:46] = 2'b00; + assign output_ts[45:16] = ts_ns_reg; + assign output_ts[15:0] = FNS_WIDTH > 16 ? ts_fns_reg >> (FNS_WIDTH-16) : ts_fns_reg << (16-FNS_WIDTH); +end else if (TS_WIDTH == 64) begin + assign output_ts[63:16] = ts_ns_reg; + assign output_ts[15:0] = FNS_WIDTH > 16 ? ts_fns_reg >> (FNS_WIDTH-16) : ts_fns_reg << (16-FNS_WIDTH); +end + +endgenerate + +assign output_ts_step = ts_step_reg; + +assign output_pps = pps_reg; + +generate + + genvar n; + + for (n = 0; n < FIFO_ADDR_WIDTH+1; n = n + 1) begin + assign wr_ptr_sync2[n] = ^wr_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH+1-1:n]; + assign rd_ptr_sync2[n] = ^rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH+1-1:n]; + assign wr_ptr_sample_sync2[n] = ^wr_ptr_gray_sample_sync2_reg[FIFO_ADDR_WIDTH+1-1:n]; + assign rd_ptr_sample_sync2[n] = ^rd_ptr_gray_sample_sync2_reg[FIFO_ADDR_WIDTH+1-1:n]; + end + +endgenerate + +// pointer sync +always @(posedge input_clk) begin + if (input_rst) begin + rd_ptr_gray_sync1_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + rd_ptr_gray_sync2_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + rd_ptr_gray_sync1_reg <= rd_ptr_gray_reg; + rd_ptr_gray_sync2_reg <= rd_ptr_gray_sync1_reg; + end +end + +always @(posedge output_clk) begin + if (output_rst) begin + wr_ptr_gray_sync1_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_sync2_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg; + wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg; + end +end + +always @(posedge sample_clk) begin + rd_ptr_gray_sample_sync1_reg <= rd_ptr_gray_reg; + rd_ptr_gray_sample_sync2_reg <= rd_ptr_gray_sample_sync1_reg; + wr_ptr_gray_sample_sync1_reg <= wr_ptr_gray_reg; + wr_ptr_gray_sample_sync2_reg <= wr_ptr_gray_sample_sync1_reg; +end + +always @(posedge sample_clk) begin + if (USE_SAMPLE_CLOCK) begin + sample_acc_reg <= sample_acc_reg + ((sample_depth * 2**LOG_AVG_SCALE - sample_acc_reg) >> LOG_AVG); + sample_cnt_reg <= sample_cnt_reg + 1; + + if (sample_cnt_reg == 0) begin + sample_update_reg <= !sample_update_reg; + sample_avg_reg <= sample_acc_reg; + end + end +end + +always @(posedge input_clk) begin + sample_update_sync1_reg <= sample_update_reg; + sample_update_sync2_reg <= sample_update_sync1_reg; + sample_update_sync3_reg <= sample_update_sync2_reg; +end + +reg [LOG_FIFO_DEPTH+LOG_AVG_SCALE+2-1:0] sample_avg_sync_reg = 0; +reg sample_avg_sync_valid_reg = 0; + +always @(posedge input_clk) begin + if (USE_SAMPLE_CLOCK) begin + sample_avg_sync_valid_reg <= 1'b0; + if (sample_update_sync2_reg ^ sample_update_sync3_reg) begin + sample_avg_sync_reg <= sample_avg_reg; + sample_avg_sync_valid_reg <= 1'b1; + end + end else begin + sample_acc_reg <= sample_acc_reg + ((wr_depth * 2**LOG_AVG_SCALE - sample_acc_reg) >> LOG_AVG); + sample_cnt_reg <= sample_cnt_reg + 1; + + sample_avg_sync_valid_reg <= 1'b0; + if (sample_cnt_reg == 0) begin + sample_avg_sync_reg <= sample_acc_reg; + sample_avg_sync_valid_reg <= 1'b1; + end + end +end + +always @* begin + write = 1'b0; + + wr_ptr_next = wr_ptr_reg; + wr_ptr_gray_next = wr_ptr_gray_reg; + + wr_acc_next = wr_acc_reg + wr_inc_reg; + wr_inc_next = wr_inc_reg; + + err_int_next = err_int_reg; + + if (sample_avg_sync_valid_reg) begin + // updated sampled FIFO depth + err_int_next = err_int_reg + (sample_avg_sync_reg - (2**LOG_FIFO_DEPTH * 2**LOG_AVG_SCALE)); + wr_inc_next = WR_PERIOD + (((2**LOG_FIFO_DEPTH * 2**LOG_AVG_SCALE) - sample_avg_sync_reg) >> 8) - ($signed(err_int_reg) >> 13); + if ($signed(wr_inc_next) > $signed(WR_PERIOD*4)) begin + wr_inc_next = WR_PERIOD*4; + end else if ($signed(wr_inc_next) < $signed(WR_PERIOD/4)) begin + wr_inc_next = WR_PERIOD/4; + end + end + + if (!full && wr_acc_reg[15] != wr_acc_next[15]) begin + write = 1'b1; + wr_ptr_next = wr_ptr_reg + 1; + wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1); + end +end + +always @(posedge input_clk) begin + wr_ptr_reg <= wr_ptr_next; + wr_ptr_gray_reg <= wr_ptr_gray_next; + + wr_acc_reg <= wr_acc_next; + wr_inc_reg <= wr_inc_next; + + err_int_reg <= err_int_next; + + if (write) begin + mem[wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= input_ts; + end + + if (input_rst) begin + wr_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + wr_ptr_gray_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + wr_acc_reg <= 16'd0; + wr_inc_reg <= WR_PERIOD; + + err_int_reg <= 0; + end +end + +always @* begin + read = 1'b0; + + rd_ptr_next = rd_ptr_reg; + rd_ptr_gray_next = rd_ptr_gray_reg; + + rd_cnt_next = rd_cnt_reg + 1; + + if (!empty && rd_cnt_reg == 0) begin + read = 1'b1; + rd_ptr_next = rd_ptr_reg + 1; + rd_ptr_gray_next = rd_ptr_next ^ (rd_ptr_next >> 1); + end +end + +always @(posedge output_clk) begin + rd_ptr_reg <= rd_ptr_next; + rd_ptr_gray_reg <= rd_ptr_gray_next; + + rd_cnt_reg <= rd_cnt_next; + + if (!empty) begin + mem_read_data_reg <= mem[rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]]; + end + + if (read) begin + + end + + if (output_rst) begin + rd_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + rd_ptr_gray_reg <= {FIFO_ADDR_WIDTH+1{1'b0}}; + rd_cnt_reg <= {LOG_RATE{1'b0}}; + end +end + +reg sec_mismatch_reg = 1'b0; +reg diff_valid_reg = 1'b0; +reg diff_offset_valid_reg = 1'b0; + +reg [TS_NS_WIDTH+1-1:0] ts_ns_diff_reg = 31'd0; +reg [FNS_WIDTH-1:0] ts_fns_diff_reg = 16'd0; + +reg [48:0] time_err_int_reg = 32'd0; + +always @(posedge output_clk) begin + ts_step_reg <= 0; + + diff_valid_reg <= 1'b0; + diff_offset_valid_reg <= 1'b0; + + // 96 bit timestamp + if (TS_WIDTH == 96) begin + if (!ts_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + // increment seconds field, pre-compute both normal increment and overflow values + {ts_ns_inc_reg, ts_fns_inc_reg} <= {ts_ns_ovf_reg, ts_fns_ovf_reg} + {period_ns_reg, period_fns_reg}; + {ts_ns_ovf_reg, ts_fns_ovf_reg} <= {ts_ns_ovf_reg, ts_fns_ovf_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + {ts_ns_reg, ts_fns_reg} <= {ts_ns_ovf_reg, ts_fns_ovf_reg}; + ts_s_reg <= ts_s_reg + 1; + end else begin + // no increment seconds field, pre-compute both normal increment and overflow values + {ts_ns_inc_reg, ts_fns_inc_reg} <= {ts_ns_inc_reg, ts_fns_inc_reg} + {period_ns_reg, period_fns_reg}; + {ts_ns_ovf_reg, ts_fns_ovf_reg} <= {ts_ns_inc_reg, ts_fns_inc_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + {ts_ns_reg, ts_fns_reg} <= {ts_ns_inc_reg, ts_fns_inc_reg}; + ts_s_reg <= ts_s_reg; + end + end else if (TS_WIDTH == 64) begin + {ts_ns_reg, ts_fns_reg} <= {ts_ns_reg, ts_fns_reg} + {period_ns_reg, period_fns_reg}; + end + + // FIFO dequeue + if (read) begin + // dequeue from FIFO + if (TS_WIDTH == 96) begin + if (mem_read_data_reg[95:48] != ts_s_reg) begin + // seconds field doesn't match + if (!sec_mismatch_reg) begin + // ignore the first time + sec_mismatch_reg <= 1'b1; + end else begin + // two seconds mismatches in a row; step the clock + sec_mismatch_reg <= 1'b0; + + {ts_ns_inc_reg, ts_fns_inc_reg} <= (FNS_WIDTH > 16 ? mem_read_data_reg[45:0] << (FNS_WIDTH-16) : mem_read_data_reg[45:0] >> (16-FNS_WIDTH)) + {period_ns_reg, period_fns_reg}; + {ts_ns_ovf_reg, ts_fns_ovf_reg} <= (FNS_WIDTH > 16 ? mem_read_data_reg[45:0] << (FNS_WIDTH-16) : mem_read_data_reg[45:0] >> (16-FNS_WIDTH)) + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}}; + ts_s_reg <= mem_read_data_reg[95:48]; + ts_ns_reg <= mem_read_data_reg[45:16]; + ts_fns_reg <= FNS_WIDTH > 16 ? mem_read_data_reg[15:0] << (FNS_WIDTH-16) : mem_read_data_reg[15:0] >> (16-FNS_WIDTH); + ts_step_reg <= 1; + end + end else begin + // compute difference + sec_mismatch_reg <= 1'b0; + diff_valid_reg <= 1'b1; + {ts_ns_diff_reg, ts_fns_diff_reg} <= {ts_ns_reg, ts_fns_reg} - (FNS_WIDTH > 16 ? mem_read_data_reg[45:0] << (FNS_WIDTH-16) : mem_read_data_reg[45:0] >> (16-FNS_WIDTH)); + end + end else if (TS_WIDTH == 64) begin + if (mem_read_data_reg[63:48] != ts_ns_reg[47:32]) begin + // high-order bits don't match + if (!sec_mismatch_reg) begin + // ignore the first time + sec_mismatch_reg <= 1'b1; + end else begin + // two seconds mismatches in a row; step the clock + sec_mismatch_reg <= 1'b0; + + ts_ns_reg <= mem_read_data_reg[63:16]; + ts_fns_reg <= FNS_WIDTH > 16 ? mem_read_data_reg[15:0] << (FNS_WIDTH-16) : mem_read_data_reg[15:0] >> (16-FNS_WIDTH); + ts_step_reg <= 1; + end + end else begin + // compute difference + sec_mismatch_reg <= 1'b0; + diff_valid_reg <= 1'b1; + {ts_ns_diff_reg, ts_fns_diff_reg} <= {ts_ns_reg, ts_fns_reg} - (FNS_WIDTH > 16 ? mem_read_data_reg[63:0] << (FNS_WIDTH-16) : mem_read_data_reg[63:0] >> (16-FNS_WIDTH)); + end + end + end else if (diff_valid_reg) begin + // offset difference by FIFO delay + diff_offset_valid_reg <= 1'b1; + diff_valid_reg <= 1'b0; + {ts_ns_diff_reg, ts_fns_diff_reg} <= {ts_ns_diff_reg, ts_fns_diff_reg} - ({period_ns_reg, period_fns_reg} * 2**(LOG_RATE + LOG_FIFO_DEPTH)); + end else if (diff_offset_valid_reg) begin + // PI control + diff_offset_valid_reg <= 1'b0; + if (($signed({ts_ns_diff_reg, ts_fns_diff_reg}) / 2**10) + ($signed(time_err_int_reg) / 2**16) > 4*2**16) begin + // limit positive adjustment + time_err_int_reg <= 0; + {period_ns_reg, period_fns_reg} <= $signed(OUTPUT_PERIOD_NS*2**16 + OUTPUT_PERIOD_FNS) - ({4'd4, {FNS_WIDTH{1'b0}}}); + end else if (($signed({ts_ns_diff_reg, ts_fns_diff_reg}) / 2**10) + ($signed(time_err_int_reg) / 2**16) < -8*2**16) begin + // limit negative adjustment + time_err_int_reg <= 0; + {period_ns_reg, period_fns_reg} <= $signed(OUTPUT_PERIOD_NS*2**16 + OUTPUT_PERIOD_FNS) + ({4'd8, {FNS_WIDTH{1'b0}}}); + end else begin + time_err_int_reg <= $signed(time_err_int_reg) + $signed({ts_ns_diff_reg, ts_fns_diff_reg}); + {period_ns_reg, period_fns_reg} <= $signed(OUTPUT_PERIOD_NS*2**16 + OUTPUT_PERIOD_FNS) - ($signed({ts_ns_diff_reg, ts_fns_diff_reg}) / 2**10) - ($signed(time_err_int_reg) / 2**16); + end + end + + if (TS_WIDTH == 96) begin + pps_reg <= !ts_ns_ovf_reg[30]; + end else if (TS_WIDTH == 64) begin + pps_reg <= 1'b0; // not currently implemented for 64 bit timestamp format + end + + if (output_rst) begin + period_ns_reg <= OUTPUT_PERIOD_NS; + period_fns_reg <= OUTPUT_PERIOD_FNS; + ts_s_reg <= 0; + ts_ns_reg <= 0; + ts_fns_reg <= 0; + ts_ns_inc_reg <= 0; + ts_fns_inc_reg <= 0; + ts_ns_ovf_reg <= {TS_NS_WIDTH+1{1'b1}}; + ts_fns_ovf_reg <= {FNS_WIDTH{1'b1}}; + ts_step_reg <= 0; + pps_reg <= 0; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ptp_perout.v b/corundum/lib/eth/rtl/ptp_perout.v new file mode 100644 index 0000000000000000000000000000000000000000..54ba07a652ad35145db55aa2fb75e0d1fc5fba0f --- /dev/null +++ b/corundum/lib/eth/rtl/ptp_perout.v @@ -0,0 +1,329 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PTP period out module + */ +module ptp_perout # +( + parameter FNS_ENABLE = 1, + parameter OUT_START_S = 48'h0, + parameter OUT_START_NS = 30'h0, + parameter OUT_START_FNS = 16'h0000, + parameter OUT_PERIOD_S = 48'd1, + parameter OUT_PERIOD_NS = 30'd0, + parameter OUT_PERIOD_FNS = 16'h0000, + parameter OUT_WIDTH_S = 48'h0, + parameter OUT_WIDTH_NS = 30'd1000, + parameter OUT_WIDTH_FNS = 16'h0000 +) +( + input wire clk, + input wire rst, + + /* + * Timestamp input from PTP clock + */ + input wire [95:0] input_ts_96, + input wire input_ts_step, + + /* + * Control + */ + input wire enable, + input wire [95:0] input_start, + input wire input_start_valid, + input wire [95:0] input_period, + input wire input_period_valid, + input wire [95:0] input_width, + input wire input_width_valid, + + /* + * Status + */ + output wire locked, + output wire error, + + /* + * Pulse output + */ + output wire output_pulse +); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_UPDATE_RISE_1 = 3'd1, + STATE_UPDATE_RISE_2 = 3'd2, + STATE_UPDATE_FALL_1 = 3'd3, + STATE_UPDATE_FALL_2 = 3'd4, + STATE_WAIT_EDGE = 3'd5; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg [47:0] time_s_reg = 0; +reg [30:0] time_ns_reg = 0; +reg [15:0] time_fns_reg = 0; + +reg [47:0] next_rise_s_reg = 0, next_rise_s_next; +reg [30:0] next_rise_ns_reg = 0, next_rise_ns_next; +reg [15:0] next_rise_fns_reg = 0, next_rise_fns_next; + +reg [47:0] next_fall_s_reg = 0, next_fall_s_next; +reg [30:0] next_fall_ns_reg = 0, next_fall_ns_next; +reg [15:0] next_fall_fns_reg = 0, next_fall_fns_next; + +reg [47:0] start_s_reg = OUT_START_S; +reg [30:0] start_ns_reg = OUT_START_NS; +reg [15:0] start_fns_reg = OUT_START_FNS; + +reg [47:0] period_s_reg = OUT_PERIOD_S; +reg [30:0] period_ns_reg = OUT_PERIOD_NS; +reg [15:0] period_fns_reg = OUT_PERIOD_FNS; + +reg [47:0] width_s_reg = OUT_WIDTH_S; +reg [30:0] width_ns_reg = OUT_WIDTH_NS; +reg [15:0] width_fns_reg = OUT_WIDTH_FNS; + +reg [29:0] ts_96_ns_inc_reg = 0, ts_96_ns_inc_next; +reg [15:0] ts_96_fns_inc_reg = 0, ts_96_fns_inc_next; +reg [30:0] ts_96_ns_ovf_reg = 0, ts_96_ns_ovf_next; +reg [15:0] ts_96_fns_ovf_reg = 0, ts_96_fns_ovf_next; + +reg locked_reg = 1'b0, locked_next; +reg error_reg = 1'b0; +reg level_reg = 1'b0, level_next; +reg output_reg = 1'b0, output_next; + +assign locked = locked_reg; +assign error = error_reg; +assign output_pulse = output_reg; + +always @* begin + state_next = STATE_IDLE; + + next_rise_s_next = next_rise_s_reg; + next_rise_ns_next = next_rise_ns_reg; + next_rise_fns_next = next_rise_fns_reg; + + next_fall_s_next = next_fall_s_reg; + next_fall_ns_next = next_fall_ns_reg; + next_fall_fns_next = next_fall_fns_reg; + + ts_96_ns_inc_next = ts_96_ns_inc_reg; + ts_96_fns_inc_next = ts_96_fns_inc_reg; + + ts_96_ns_ovf_next = ts_96_ns_ovf_reg; + ts_96_fns_ovf_next = ts_96_fns_ovf_reg; + + locked_next = locked_reg; + level_next = level_reg; + output_next = output_reg; + + case (state_reg) + STATE_IDLE: begin + // set next rise to start time + next_rise_s_next = start_s_reg; + next_rise_ns_next = start_ns_reg; + if (FNS_ENABLE) begin + next_rise_fns_next = start_fns_reg; + end + locked_next = 1'b0; + level_next = 1'b0; + output_next = 1'b0; + if (input_start_valid || input_period_valid) begin + state_next = STATE_IDLE; + end else begin + state_next = STATE_UPDATE_FALL_1; + end + end + STATE_UPDATE_RISE_1: begin + // set next rise time to next rise time plus period + {ts_96_ns_inc_next, ts_96_fns_inc_next} = {next_rise_ns_reg, next_rise_fns_reg} + {period_ns_reg, period_fns_reg}; + {ts_96_ns_ovf_next, ts_96_fns_ovf_next} = {next_rise_ns_reg, next_rise_fns_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, 16'd0}; + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_UPDATE_RISE_2; + end + end + STATE_UPDATE_RISE_2: begin + if (!ts_96_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + next_rise_s_next = next_rise_s_reg + period_s_reg + 1; + next_rise_ns_next = ts_96_ns_ovf_reg; + next_rise_fns_next = ts_96_fns_ovf_reg; + end else begin + // no increment seconds field + next_rise_s_next = next_rise_s_reg + period_s_reg; + next_rise_ns_next = ts_96_ns_inc_reg; + next_rise_fns_next = ts_96_fns_inc_reg; + end + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_EDGE; + end + end + STATE_UPDATE_FALL_1: begin + // set next fall time to next rise time plus width + {ts_96_ns_inc_next, ts_96_fns_inc_next} = {next_rise_ns_reg, next_rise_fns_reg} + {width_ns_reg, width_fns_reg}; + {ts_96_ns_ovf_next, ts_96_fns_ovf_next} = {next_rise_ns_reg, next_rise_fns_reg} + {width_ns_reg, width_fns_reg} - {31'd1_000_000_000, 16'd0}; + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_UPDATE_FALL_2; + end + end + STATE_UPDATE_FALL_2: begin + if (!ts_96_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + next_fall_s_next = next_rise_s_reg + width_s_reg + 1; + next_fall_ns_next = ts_96_ns_ovf_reg; + next_fall_fns_next = ts_96_fns_ovf_reg; + end else begin + // no increment seconds field + next_fall_s_next = next_rise_s_reg + width_s_reg; + next_fall_ns_next = ts_96_ns_inc_reg; + next_fall_fns_next = ts_96_fns_inc_reg; + end + if (input_start_valid || input_period_valid) begin + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_EDGE; + end + end + STATE_WAIT_EDGE: begin + if (input_start_valid || input_period_valid) begin + state_next = STATE_IDLE; + end else if ((time_s_reg > next_rise_s_reg) || (time_s_reg == next_rise_s_reg && {time_ns_reg, time_fns_reg} > {next_rise_ns_reg, next_rise_fns_reg})) begin + // rising edge + level_next = 1'b1; + output_next = enable && locked_reg; + state_next = STATE_UPDATE_RISE_1; + end else if ((time_s_reg > next_fall_s_reg) || (time_s_reg == next_fall_s_reg && {time_ns_reg, time_fns_reg} > {next_fall_ns_reg, next_fall_fns_reg})) begin + // falling edge + level_next = 1'b0; + output_next = 1'b0; + state_next = STATE_UPDATE_FALL_1; + end else begin + locked_next = locked_reg || level_reg; + state_next = STATE_WAIT_EDGE; + end + end + endcase +end + +always @(posedge clk) begin + state_reg <= state_next; + + time_s_reg <= input_ts_96[95:48]; + time_ns_reg <= input_ts_96[45:16]; + if (FNS_ENABLE) begin + time_fns_reg <= input_ts_96[15:0]; + end + + if (input_start_valid) begin + start_s_reg <= input_start[95:48]; + start_ns_reg <= input_start[45:16]; + if (FNS_ENABLE) begin + start_fns_reg <= input_start[15:0]; + end + end + + if (input_period_valid) begin + period_s_reg <= input_period[95:48]; + period_ns_reg <= input_period[45:16]; + if (FNS_ENABLE) begin + period_fns_reg <= input_period[15:0]; + end + end + + if (input_width_valid) begin + width_s_reg <= input_width[95:48]; + width_ns_reg <= input_width[45:16]; + if (FNS_ENABLE) begin + width_fns_reg <= input_width[15:0]; + end + end + + next_rise_s_reg <= next_rise_s_next; + next_rise_ns_reg <= next_rise_ns_next; + if (FNS_ENABLE) begin + next_rise_fns_reg <= next_rise_fns_next; + end + + next_fall_s_reg <= next_fall_s_next; + next_fall_ns_reg <= next_fall_ns_next; + if (FNS_ENABLE) begin + next_fall_fns_reg <= next_fall_fns_next; + end + + ts_96_ns_inc_reg <= ts_96_ns_inc_next; + if (FNS_ENABLE) begin + ts_96_fns_inc_reg <= ts_96_fns_inc_next; + end + + ts_96_ns_ovf_reg <= ts_96_ns_ovf_next; + if (FNS_ENABLE) begin + ts_96_fns_ovf_reg <= ts_96_fns_ovf_next; + end + + locked_reg <= locked_next; + level_reg <= level_next; + output_reg <= output_next; + + if (rst) begin + state_reg <= STATE_IDLE; + + start_s_reg <= OUT_START_S; + start_ns_reg <= OUT_START_NS; + start_fns_reg <= OUT_START_FNS; + + period_s_reg <= OUT_PERIOD_S; + period_ns_reg <= OUT_PERIOD_NS; + period_fns_reg <= OUT_PERIOD_FNS; + + width_s_reg <= OUT_WIDTH_S; + width_ns_reg <= OUT_WIDTH_NS; + width_fns_reg <= OUT_WIDTH_FNS; + + locked_reg <= 1'b0; + error_reg <= 1'b0; + output_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ptp_tag_insert.v b/corundum/lib/eth/rtl/ptp_tag_insert.v new file mode 100644 index 0000000000000000000000000000000000000000..e693d15492d4de637519d9fd00113a8930b4cf6f --- /dev/null +++ b/corundum/lib/eth/rtl/ptp_tag_insert.v @@ -0,0 +1,107 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PTP tag insert module + */ +module ptp_tag_insert # +( + parameter DATA_WIDTH = 64, + parameter KEEP_WIDTH = DATA_WIDTH/8, + parameter TAG_WIDTH = 16, + parameter TAG_OFFSET = 1, + parameter USER_WIDTH = TAG_WIDTH+TAG_OFFSET +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Tag input + */ + input wire [TAG_WIDTH-1:0] s_axis_tag, + input wire s_axis_tag_valid, + output wire s_axis_tag_ready +); + +reg [TAG_WIDTH-1:0] tag_reg = {TAG_WIDTH{1'b0}}; +reg tag_valid_reg = 1'b0; + +reg [USER_WIDTH-1:0] user; + +assign s_axis_tready = m_axis_tready && tag_valid_reg; + +assign m_axis_tdata = s_axis_tdata; +assign m_axis_tkeep = s_axis_tkeep; +assign m_axis_tvalid = s_axis_tvalid && tag_valid_reg; +assign m_axis_tlast = s_axis_tlast; +assign m_axis_tuser = user; + +assign s_axis_tag_ready = !tag_valid_reg; + +always @* begin + user = s_axis_tuser; + user[TAG_OFFSET +: TAG_WIDTH] = tag_reg; +end + +always @(posedge clk) begin + if (tag_valid_reg) begin + if (s_axis_tvalid && s_axis_tready && s_axis_tlast) begin + tag_valid_reg <= 1'b0; + end + end else begin + tag_reg <= s_axis_tag; + tag_valid_reg <= s_axis_tag_valid; + end + + if (rst) begin + tag_valid_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ptp_ts_extract.v b/corundum/lib/eth/rtl/ptp_ts_extract.v new file mode 100644 index 0000000000000000000000000000000000000000..8a97d4b38f765c55a4ccfe361317750341181bf8 --- /dev/null +++ b/corundum/lib/eth/rtl/ptp_ts_extract.v @@ -0,0 +1,71 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PTP timestamp extract module + */ +module ptp_ts_extract # +( + parameter TS_WIDTH = 96, + parameter TS_OFFSET = 1, + parameter USER_WIDTH = TS_WIDTH+TS_OFFSET +) +( + input wire clk, + input wire rst, + + /* + * AXI stream input + */ + input wire s_axis_tvalid, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * Timestamp output + */ + output wire [TS_WIDTH-1:0] m_axis_ts, + output wire m_axis_ts_valid +); + +reg frame_reg = 1'b0; + +assign m_axis_ts = s_axis_tuser >> TS_OFFSET; +assign m_axis_ts_valid = s_axis_tvalid && !frame_reg; + +always @(posedge clk) begin + if (s_axis_tvalid) begin + frame_reg <= !s_axis_tlast; + end + + if (rst) begin + frame_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/rgmii_phy_if.v b/corundum/lib/eth/rtl/rgmii_phy_if.v new file mode 100644 index 0000000000000000000000000000000000000000..15f5943f5d4e5ab6c6cd0c2ffaa40c46c73110af --- /dev/null +++ b/corundum/lib/eth/rtl/rgmii_phy_if.v @@ -0,0 +1,262 @@ +/* + +Copyright (c) 2015-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * RGMII PHY interface + */ +module rgmii_phy_if # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Use 90 degree clock for RGMII transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE" +) +( + input wire clk, + input wire clk90, + input wire rst, + + /* + * GMII interface to MAC + */ + output wire mac_gmii_rx_clk, + output wire mac_gmii_rx_rst, + output wire [7:0] mac_gmii_rxd, + output wire mac_gmii_rx_dv, + output wire mac_gmii_rx_er, + output wire mac_gmii_tx_clk, + output wire mac_gmii_tx_rst, + output wire mac_gmii_tx_clk_en, + input wire [7:0] mac_gmii_txd, + input wire mac_gmii_tx_en, + input wire mac_gmii_tx_er, + + /* + * RGMII interface to PHY + */ + input wire phy_rgmii_rx_clk, + input wire [3:0] phy_rgmii_rxd, + input wire phy_rgmii_rx_ctl, + output wire phy_rgmii_tx_clk, + output wire [3:0] phy_rgmii_txd, + output wire phy_rgmii_tx_ctl, + + /* + * Control + */ + input wire [1:0] speed +); + +// receive + +wire rgmii_rx_ctl_1; +wire rgmii_rx_ctl_2; + +ssio_ddr_in # +( + .TARGET(TARGET), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(5) +) +rx_ssio_ddr_inst ( + .input_clk(phy_rgmii_rx_clk), + .input_d({phy_rgmii_rxd, phy_rgmii_rx_ctl}), + .output_clk(mac_gmii_rx_clk), + .output_q1({mac_gmii_rxd[3:0], rgmii_rx_ctl_1}), + .output_q2({mac_gmii_rxd[7:4], rgmii_rx_ctl_2}) +); + +assign mac_gmii_rx_dv = rgmii_rx_ctl_1; +assign mac_gmii_rx_er = rgmii_rx_ctl_1 ^ rgmii_rx_ctl_2; + +// transmit + +reg rgmii_tx_clk_1 = 1'b1; +reg rgmii_tx_clk_2 = 1'b0; +reg rgmii_tx_clk_rise = 1'b1; +reg rgmii_tx_clk_fall = 1'b1; + +reg [5:0] count_reg = 6'd0, count_next; + +always @(posedge clk) begin + if (rst) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_rise <= 1'b1; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end else begin + rgmii_tx_clk_1 <= rgmii_tx_clk_2; + + if (speed == 2'b00) begin + // 10M + count_reg <= count_reg + 1; + rgmii_tx_clk_rise <= 1'b0; + rgmii_tx_clk_fall <= 1'b0; + if (count_reg == 24) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b1; + rgmii_tx_clk_rise <= 1'b1; + end else if (count_reg >= 49) begin + rgmii_tx_clk_1 <= 1'b0; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end + end else if (speed == 2'b01) begin + // 100M + count_reg <= count_reg + 1; + rgmii_tx_clk_rise <= 1'b0; + rgmii_tx_clk_fall <= 1'b0; + if (count_reg == 2) begin + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b1; + rgmii_tx_clk_rise <= 1'b1; + end else if (count_reg >= 4) begin + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_fall <= 1'b1; + count_reg <= 0; + end + end else begin + // 1000M + rgmii_tx_clk_1 <= 1'b1; + rgmii_tx_clk_2 <= 1'b0; + rgmii_tx_clk_rise <= 1'b1; + rgmii_tx_clk_fall <= 1'b1; + end + end +end + +reg [3:0] rgmii_txd_1; +reg [3:0] rgmii_txd_2; +reg rgmii_tx_ctl_1; +reg rgmii_tx_ctl_2; + +reg gmii_clk_en; + +always @* begin + if (speed == 2'b00) begin + // 10M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[3:0]; + if (rgmii_tx_clk_2) begin + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en; + end else begin + rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + end + gmii_clk_en = rgmii_tx_clk_fall; + end else if (speed == 2'b01) begin + // 100M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[3:0]; + if (rgmii_tx_clk_2) begin + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en; + end else begin + rgmii_tx_ctl_1 = mac_gmii_tx_en ^ mac_gmii_tx_er; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + end + gmii_clk_en = rgmii_tx_clk_fall; + end else begin + // 1000M + rgmii_txd_1 = mac_gmii_txd[3:0]; + rgmii_txd_2 = mac_gmii_txd[7:4]; + rgmii_tx_ctl_1 = mac_gmii_tx_en; + rgmii_tx_ctl_2 = mac_gmii_tx_en ^ mac_gmii_tx_er; + gmii_clk_en = 1; + end +end + +wire phy_rgmii_tx_clk_new; +wire [3:0] phy_rgmii_txd_new; +wire phy_rgmii_tx_ctl_new; + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(1) +) +clk_oddr_inst ( + .clk(USE_CLK90 == "TRUE" ? clk90 : clk), + .d1(rgmii_tx_clk_1), + .d2(rgmii_tx_clk_2), + .q(phy_rgmii_tx_clk) +); + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(5) +) +data_oddr_inst ( + .clk(clk), + .d1({rgmii_txd_1, rgmii_tx_ctl_1}), + .d2({rgmii_txd_2, rgmii_tx_ctl_2}), + .q({phy_rgmii_txd, phy_rgmii_tx_ctl}) +); + +assign mac_gmii_tx_clk = clk; + +assign mac_gmii_tx_clk_en = gmii_clk_en; + +// reset sync +reg [3:0] tx_rst_reg = 4'hf; +assign mac_gmii_tx_rst = tx_rst_reg[0]; + +always @(posedge mac_gmii_tx_clk or posedge rst) begin + if (rst) begin + tx_rst_reg <= 4'hf; + end else begin + tx_rst_reg <= {1'b0, tx_rst_reg[3:1]}; + end +end + +reg [3:0] rx_rst_reg = 4'hf; +assign mac_gmii_rx_rst = rx_rst_reg[0]; + +always @(posedge mac_gmii_rx_clk or posedge rst) begin + if (rst) begin + rx_rst_reg <= 4'hf; + end else begin + rx_rst_reg <= {1'b0, rx_rst_reg[3:1]}; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_ddr_in.v b/corundum/lib/eth/rtl/ssio_ddr_in.v new file mode 100644 index 0000000000000000000000000000000000000000..4c2f6f4213e666fe883717f08ef65ef8c1ef642c --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_ddr_in.v @@ -0,0 +1,171 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous DDR input + */ +module ssio_ddr_in # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q1, + output wire [WIDTH-1:0] output_q2 +); + +wire clk_int; +wire clk_io; + +generate + +if (TARGET == "XILINX") begin + + // use Xilinx clocking primitives + + if (CLOCK_INPUT_STYLE == "BUFG") begin + + // buffer RX clock + BUFG + clk_bufg ( + .I(input_clk), + .O(clk_int) + ); + + // pass through RX clock to logic and input buffers + assign clk_io = clk_int; + assign output_clk = clk_int; + + end else if (CLOCK_INPUT_STYLE == "BUFR") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to logic + BUFR #( + .BUFR_DIVIDE("BYPASS") + ) + clk_bufr ( + .I(clk_int), + .O(output_clk), + .CE(1'b1), + .CLR(1'b0) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO2") begin + + // pass through RX clock to input buffers + BUFIO2 #( + .DIVIDE(1), + .DIVIDE_BYPASS("TRUE"), + .I_INVERT("FALSE"), + .USE_DOUBLER("FALSE") + ) + clk_bufio ( + .I(input_clk), + .DIVCLK(clk_int), + .IOCLK(clk_io), + .SERDESSTROBE() + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end + +end else begin + + // pass through RX clock to input buffers + assign clk_io = input_clk; + + // pass through RX clock to logic + assign clk_int = input_clk; + assign output_clk = clk_int; + +end + +endgenerate + +iddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(WIDTH) +) +data_iddr_inst ( + .clk(clk_io), + .d(input_d), + .q1(output_q1), + .q2(output_q2) +); + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_ddr_in_diff.v b/corundum/lib/eth/rtl/ssio_ddr_in_diff.v new file mode 100644 index 0000000000000000000000000000000000000000..cab4279154e2b26d01b06f99ad357f8a3218ad5d --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_ddr_in_diff.v @@ -0,0 +1,119 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous DDR input + */ +module ssio_ddr_in_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk_p, + input wire input_clk_n, + + input wire [WIDTH-1:0] input_d_p, + input wire [WIDTH-1:0] input_d_n, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q1, + output wire [WIDTH-1:0] output_q2 +); + +wire input_clk; +wire [WIDTH-1:0] input_d; + +genvar n; + +generate + +if (TARGET == "XILINX") begin + IBUFDS + clk_ibufds_inst ( + .I(input_clk_p), + .IB(input_clk_n), + .O(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + IBUFDS + data_ibufds_inst ( + .I(input_d_p[n]), + .IB(input_d_n[n]), + .O(input_d[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_INBUF_DIFF + clk_inbuf_diff_inst ( + .i(input_clk_p), + .ibar(input_clk_n), + .o(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_INBUF_DIFF + data_inbuf_diff_inst ( + .i(input_d_p[n]), + .ibar(input_d_n[n]), + .o(input_d[n]) + ); + end +end else begin + assign input_clk = input_clk_p; + assign input_d = input_d_p; +end + +endgenerate + +ssio_ddr_in #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(WIDTH) +) +ssio_ddr_in_inst( + .input_clk(input_clk), + .input_d(input_d), + .output_clk(output_clk), + .output_q1(output_q1), + .output_q2(output_q2) +); + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_ddr_out.v b/corundum/lib/eth/rtl/ssio_ddr_out.v new file mode 100644 index 0000000000000000000000000000000000000000..c77aa691ee2603d83e4231b3f5970bf17bf24244 --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_ddr_out.v @@ -0,0 +1,82 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous DDR output + */ +module ssio_ddr_out # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Use 90 degree clock for transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + input wire clk90, + + input wire [WIDTH-1:0] input_d1, + input wire [WIDTH-1:0] input_d2, + + output wire output_clk, + output wire [WIDTH-1:0] output_q +); + +wire ref_clk = USE_CLK90 == "TRUE" ? clk90 : clk; + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(1) +) +clk_oddr_inst ( + .clk(ref_clk), + .d1(1'b1), + .d2(1'b0), + .q(output_clk) +); + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(WIDTH) +) +data_oddr_inst ( + .clk(clk), + .d1(input_d1), + .d2(input_d2), + .q(output_q) +); + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_ddr_out_diff.v b/corundum/lib/eth/rtl/ssio_ddr_out_diff.v new file mode 100644 index 0000000000000000000000000000000000000000..ba1beadc41864da0db65fb2ac62a82361a668a5d --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_ddr_out_diff.v @@ -0,0 +1,119 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous DDR output + */ +module ssio_ddr_out_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Use 90 degree clock for transmit ("TRUE", "FALSE") + parameter USE_CLK90 = "TRUE", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + input wire clk90, + + input wire [WIDTH-1:0] input_d1, + input wire [WIDTH-1:0] input_d2, + + output wire output_clk_p, + output wire output_clk_n, + output wire [WIDTH-1:0] output_q_p, + output wire [WIDTH-1:0] output_q_n +); + +wire output_clk; +wire [WIDTH-1:0] output_q; + +ssio_ddr_out #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .USE_CLK90(USE_CLK90), + .WIDTH(WIDTH) +) +ssio_ddr_out_inst( + .clk(clk), + .clk90(clk90), + .input_d1(input_d1), + .input_d2(input_d2), + .output_clk(output_clk), + .output_q(output_q) +); + +genvar n; + +generate + +if (TARGET == "XILINX") begin + OBUFDS + clk_obufds_inst ( + .I(output_clk), + .O(output_clk_p), + .OB(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + OBUFDS + data_obufds_inst ( + .I(output_q[n]), + .O(output_q_p[n]), + .OB(output_q_n[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_OUTBUF_DIFF + clk_outbuf_diff_inst ( + .i(output_clk), + .o(output_clk_p), + .obar(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_OUTBUF_DIFF + data_outbuf_diff_inst ( + .i(output_q[n]), + .o(output_q_p[n]), + .obar(output_q_n[n]) + ); + end +end else begin + assign output_clk_p = output_clk; + assign output_clk_n = ~output_clk; + assign output_q_p = output_q; + assign output_q_n = ~output_q; +end + +endgenerate + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_sdr_in.v b/corundum/lib/eth/rtl/ssio_sdr_in.v new file mode 100644 index 0000000000000000000000000000000000000000..09797f4c8bff221cf68909eb5d37bedac9f06138 --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_sdr_in.v @@ -0,0 +1,163 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous SDR input + */ +module ssio_sdr_in # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q +); + +wire clk_int; +wire clk_io; + +generate + +if (TARGET == "XILINX") begin + + // use Xilinx clocking primitives + + if (CLOCK_INPUT_STYLE == "BUFG") begin + + // buffer RX clock + BUFG + clk_bufg ( + .I(input_clk), + .O(clk_int) + ); + + // pass through RX clock to logic and input buffers + assign clk_io = clk_int; + assign output_clk = clk_int; + + end else if (CLOCK_INPUT_STYLE == "BUFR") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to logic + BUFR #( + .BUFR_DIVIDE("BYPASS") + ) + clk_bufr ( + .I(clk_int), + .O(output_clk), + .CE(1'b1), + .CLR(1'b0) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO") begin + + assign clk_int = input_clk; + + // pass through RX clock to input buffers + BUFIO + clk_bufio ( + .I(clk_int), + .O(clk_io) + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end else if (CLOCK_INPUT_STYLE == "BUFIO2") begin + + // pass through RX clock to input buffers + BUFIO2 #( + .DIVIDE(1), + .DIVIDE_BYPASS("TRUE"), + .I_INVERT("FALSE"), + .USE_DOUBLER("FALSE") + ) + clk_bufio ( + .I(input_clk), + .DIVCLK(clk_int), + .IOCLK(clk_io), + .SERDESSTROBE() + ); + + // pass through RX clock to MAC + BUFG + clk_bufg ( + .I(clk_int), + .O(output_clk) + ); + + end + +end else begin + + // pass through RX clock to input buffers + assign clk_io = input_clk; + + // pass through RX clock to logic + assign clk_int = input_clk; + assign output_clk = clk_int; + +end + +endgenerate + +(* IOB = "TRUE" *) +reg [WIDTH-1:0] output_q_reg = {WIDTH{1'b0}}; + +assign output_q = output_q_reg; + +always @(posedge clk_io) begin + output_q_reg <= input_d; +end + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_sdr_in_diff.v b/corundum/lib/eth/rtl/ssio_sdr_in_diff.v new file mode 100644 index 0000000000000000000000000000000000000000..625014389891303726e67f0ed46ceff64eb8b163 --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_sdr_in_diff.v @@ -0,0 +1,113 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous SDR input + */ +module ssio_sdr_in_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // Clock input style ("BUFG", "BUFR", "BUFIO", "BUFIO2") + // Use BUFR for Virtex-5, Virtex-6, 7-series + // Use BUFG for Ultrascale + // Use BUFIO2 for Spartan-6 + parameter CLOCK_INPUT_STYLE = "BUFIO2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire input_clk_p, + input wire input_clk_n, + + input wire [WIDTH-1:0] input_d_p, + input wire [WIDTH-1:0] input_d_n, + + output wire output_clk, + + output wire [WIDTH-1:0] output_q +); + +wire input_clk; +wire [WIDTH-1:0] input_d; + +genvar n; + +generate + +if (TARGET == "XILINX") begin + IBUFDS + clk_ibufds_inst ( + .I(input_clk_p), + .IB(input_clk_n), + .O(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + IBUFDS + data_ibufds_inst ( + .I(input_d_p[n]), + .IB(input_d_n[n]), + .O(input_d[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_INBUF_DIFF + clk_inbuf_diff_inst ( + .i(input_clk_p), + .ibar(input_clk_n), + .o(input_clk) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_INBUF_DIFF + data_inbuf_diff_inst ( + .i(input_d_p[n]), + .ibar(input_d_n[n]), + .o(input_d[n]) + ); + end +end else begin + assign input_clk = input_clk_p; + assign input_d = input_d_p; +end + +endgenerate + +ssio_sdr_in #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .CLOCK_INPUT_STYLE(CLOCK_INPUT_STYLE), + .WIDTH(WIDTH) +) +ssio_ddr_in_inst( + .input_clk(input_clk), + .input_d(input_d), + .output_clk(output_clk), + .output_q(output_q) +); + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_sdr_out.v b/corundum/lib/eth/rtl/ssio_sdr_out.v new file mode 100644 index 0000000000000000000000000000000000000000..702f9060a4f42cc0f46f44d18491a0aa563a5c6c --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_sdr_out.v @@ -0,0 +1,73 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous SDR output + */ +module ssio_sdr_out # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk, + output wire [WIDTH-1:0] output_q +); + +oddr #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(1) +) +clk_oddr_inst ( + .clk(clk), + .d1(1'b0), + .d2(1'b1), + .q(output_clk) +); + +(* IOB = "TRUE" *) +reg [WIDTH-1:0] output_q_reg = {WIDTH{1'b0}}; + +assign output_q = output_q_reg; + +always @(posedge clk) begin + output_q_reg <= input_d; +end + +endmodule diff --git a/corundum/lib/eth/rtl/ssio_sdr_out_diff.v b/corundum/lib/eth/rtl/ssio_sdr_out_diff.v new file mode 100644 index 0000000000000000000000000000000000000000..5fe26076bda475a72b061c6997d41987f32d5965 --- /dev/null +++ b/corundum/lib/eth/rtl/ssio_sdr_out_diff.v @@ -0,0 +1,112 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Generic source synchronous SDR output + */ +module ssio_sdr_out_diff # +( + // target ("SIM", "GENERIC", "XILINX", "ALTERA") + parameter TARGET = "GENERIC", + // IODDR style ("IODDR", "IODDR2") + // Use IODDR for Virtex-4, Virtex-5, Virtex-6, 7 Series, Ultrascale + // Use IODDR2 for Spartan-6 + parameter IODDR_STYLE = "IODDR2", + // Width of register in bits + parameter WIDTH = 1 +) +( + input wire clk, + + input wire [WIDTH-1:0] input_d, + + output wire output_clk_p, + output wire output_clk_n, + output wire [WIDTH-1:0] output_q_p, + output wire [WIDTH-1:0] output_q_n +); + +wire output_clk; +wire [WIDTH-1:0] output_q; + +ssio_sdr_out #( + .TARGET(TARGET), + .IODDR_STYLE(IODDR_STYLE), + .WIDTH(WIDTH) +) +ssio_ddr_out_inst( + .clk(clk), + .input_d(input_d), + .output_clk(output_clk), + .output_q(output_q) +); + +genvar n; + +generate + +if (TARGET == "XILINX") begin + OBUFDS + clk_obufds_inst ( + .I(output_clk), + .O(output_clk_p), + .OB(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + OBUFDS + data_obufds_inst ( + .I(output_q[n]), + .O(output_q_p[n]), + .OB(output_q_n[n]) + ); + end +end else if (TARGET == "ALTERA") begin + ALT_OUTBUF_DIFF + clk_outbuf_diff_inst ( + .i(output_clk), + .o(output_clk_p), + .obar(output_clk_n) + ); + for (n = 0; n < WIDTH; n = n + 1) begin + ALT_OUTBUF_DIFF + data_outbuf_diff_inst ( + .i(output_q[n]), + .o(output_q_p[n]), + .obar(output_q_n[n]) + ); + end +end else begin + assign output_clk_p = output_clk; + assign output_clk_n = ~output_clk; + assign output_q_p = output_q; + assign output_q_n = ~output_q; +end + +endgenerate + +endmodule diff --git a/corundum/lib/eth/rtl/udp.v b/corundum/lib/eth/rtl/udp.v new file mode 100644 index 0000000000000000000000000000000000000000..bca73aa484c58a67dd8a83293157c7aee9154ffb --- /dev/null +++ b/corundum/lib/eth/rtl/udp.v @@ -0,0 +1,413 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP block, IP interface + */ +module udp # +( + parameter CHECKSUM_GEN_ENABLE = 1, + parameter CHECKSUM_PAYLOAD_FIFO_DEPTH = 2048, + parameter CHECKSUM_HEADER_FIFO_DEPTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_ip_eth_dest_mac, + input wire [47:0] s_ip_eth_src_mac, + input wire [15:0] s_ip_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_udp_eth_dest_mac, + input wire [47:0] s_udp_eth_src_mac, + input wire [15:0] s_udp_eth_type, + input wire [3:0] s_udp_ip_version, + input wire [3:0] s_udp_ip_ihl, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [15:0] s_udp_ip_identification, + input wire [2:0] s_udp_ip_flags, + input wire [12:0] s_udp_ip_fragment_offset, + input wire [7:0] s_udp_ip_ttl, + input wire [15:0] s_udp_ip_header_checksum, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire tx_error_payload_early_termination +); + +wire tx_udp_hdr_valid; +wire tx_udp_hdr_ready; +wire [47:0] tx_udp_eth_dest_mac; +wire [47:0] tx_udp_eth_src_mac; +wire [15:0] tx_udp_eth_type; +wire [3:0] tx_udp_ip_version; +wire [3:0] tx_udp_ip_ihl; +wire [5:0] tx_udp_ip_dscp; +wire [1:0] tx_udp_ip_ecn; +wire [15:0] tx_udp_ip_identification; +wire [2:0] tx_udp_ip_flags; +wire [12:0] tx_udp_ip_fragment_offset; +wire [7:0] tx_udp_ip_ttl; +wire [15:0] tx_udp_ip_header_checksum; +wire [31:0] tx_udp_ip_source_ip; +wire [31:0] tx_udp_ip_dest_ip; +wire [15:0] tx_udp_source_port; +wire [15:0] tx_udp_dest_port; +wire [15:0] tx_udp_length; +wire [15:0] tx_udp_checksum; +wire [7:0] tx_udp_payload_axis_tdata; +wire tx_udp_payload_axis_tvalid; +wire tx_udp_payload_axis_tready; +wire tx_udp_payload_axis_tlast; +wire tx_udp_payload_axis_tuser; + +udp_ip_rx +udp_ip_rx_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_ip_eth_dest_mac), + .s_eth_src_mac(s_ip_eth_src_mac), + .s_eth_type(s_ip_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_udp_eth_dest_mac), + .m_eth_src_mac(m_udp_eth_src_mac), + .m_eth_type(m_udp_eth_type), + .m_ip_version(m_udp_ip_version), + .m_ip_ihl(m_udp_ip_ihl), + .m_ip_dscp(m_udp_ip_dscp), + .m_ip_ecn(m_udp_ip_ecn), + .m_ip_length(m_udp_ip_length), + .m_ip_identification(m_udp_ip_identification), + .m_ip_flags(m_udp_ip_flags), + .m_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_ip_ttl(m_udp_ip_ttl), + .m_ip_protocol(m_udp_ip_protocol), + .m_ip_header_checksum(m_udp_ip_header_checksum), + .m_ip_source_ip(m_udp_ip_source_ip), + .m_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination) +); + +generate + +if (CHECKSUM_GEN_ENABLE) begin + + udp_checksum_gen #( + .PAYLOAD_FIFO_DEPTH(CHECKSUM_PAYLOAD_FIFO_DEPTH), + .HEADER_FIFO_DEPTH(CHECKSUM_HEADER_FIFO_DEPTH) + ) + udp_checksum_gen_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_udp_eth_dest_mac), + .s_eth_src_mac(s_udp_eth_src_mac), + .s_eth_type(s_udp_eth_type), + .s_ip_version(s_udp_ip_version), + .s_ip_ihl(s_udp_ip_ihl), + .s_ip_dscp(s_udp_ip_dscp), + .s_ip_ecn(s_udp_ip_ecn), + .s_ip_identification(s_udp_ip_identification), + .s_ip_flags(s_udp_ip_flags), + .s_ip_fragment_offset(s_udp_ip_fragment_offset), + .s_ip_ttl(s_udp_ip_ttl), + .s_ip_header_checksum(s_udp_ip_header_checksum), + .s_ip_source_ip(s_udp_ip_source_ip), + .s_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(tx_udp_hdr_valid), + .m_udp_hdr_ready(tx_udp_hdr_ready), + .m_eth_dest_mac(tx_udp_eth_dest_mac), + .m_eth_src_mac(tx_udp_eth_src_mac), + .m_eth_type(tx_udp_eth_type), + .m_ip_version(tx_udp_ip_version), + .m_ip_ihl(tx_udp_ip_ihl), + .m_ip_dscp(tx_udp_ip_dscp), + .m_ip_ecn(tx_udp_ip_ecn), + .m_ip_length(), + .m_ip_identification(tx_udp_ip_identification), + .m_ip_flags(tx_udp_ip_flags), + .m_ip_fragment_offset(tx_udp_ip_fragment_offset), + .m_ip_ttl(tx_udp_ip_ttl), + .m_ip_protocol(), + .m_ip_header_checksum(tx_udp_ip_header_checksum), + .m_ip_source_ip(tx_udp_ip_source_ip), + .m_ip_dest_ip(tx_udp_ip_dest_ip), + .m_udp_source_port(tx_udp_source_port), + .m_udp_dest_port(tx_udp_dest_port), + .m_udp_length(tx_udp_length), + .m_udp_checksum(tx_udp_checksum), + .m_udp_payload_axis_tdata(tx_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(tx_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(tx_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(tx_udp_payload_axis_tuser), + // Status signals + .busy() + ); + +end else begin + + assign tx_udp_hdr_valid = s_udp_hdr_valid; + assign s_udp_hdr_ready = tx_udp_hdr_ready; + assign tx_udp_eth_dest_mac = s_udp_eth_dest_mac; + assign tx_udp_eth_src_mac = s_udp_eth_src_mac; + assign tx_udp_eth_type = s_udp_eth_type; + assign tx_udp_ip_version = s_udp_ip_version; + assign tx_udp_ip_ihl = s_udp_ip_ihl; + assign tx_udp_ip_dscp = s_udp_ip_dscp; + assign tx_udp_ip_ecn = s_udp_ip_ecn; + assign tx_udp_ip_identification = s_udp_ip_identification; + assign tx_udp_ip_flags = s_udp_ip_flags; + assign tx_udp_ip_fragment_offset = s_udp_ip_fragment_offset; + assign tx_udp_ip_ttl = s_udp_ip_ttl; + assign tx_udp_ip_header_checksum = s_udp_ip_header_checksum; + assign tx_udp_ip_source_ip = s_udp_ip_source_ip; + assign tx_udp_ip_dest_ip = s_udp_ip_dest_ip; + assign tx_udp_source_port = s_udp_source_port; + assign tx_udp_dest_port = s_udp_dest_port; + assign tx_udp_length = s_udp_length; + assign tx_udp_checksum = s_udp_checksum; + assign tx_udp_payload_axis_tdata = s_udp_payload_axis_tdata; + assign tx_udp_payload_axis_tvalid = s_udp_payload_axis_tvalid; + assign s_udp_payload_axis_tready = tx_udp_payload_axis_tready; + assign tx_udp_payload_axis_tlast = s_udp_payload_axis_tlast; + assign tx_udp_payload_axis_tuser = s_udp_payload_axis_tuser; + +end + +endgenerate + +udp_ip_tx +udp_ip_tx_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(tx_udp_hdr_valid), + .s_udp_hdr_ready(tx_udp_hdr_ready), + .s_eth_dest_mac(tx_udp_eth_dest_mac), + .s_eth_src_mac(tx_udp_eth_src_mac), + .s_eth_type(tx_udp_eth_type), + .s_ip_version(tx_udp_ip_version), + .s_ip_ihl(tx_udp_ip_ihl), + .s_ip_dscp(tx_udp_ip_dscp), + .s_ip_ecn(tx_udp_ip_ecn), + .s_ip_identification(tx_udp_ip_identification), + .s_ip_flags(tx_udp_ip_flags), + .s_ip_fragment_offset(tx_udp_ip_fragment_offset), + .s_ip_ttl(tx_udp_ip_ttl), + .s_ip_protocol(8'h11), + .s_ip_header_checksum(tx_udp_ip_header_checksum), + .s_ip_source_ip(tx_udp_ip_source_ip), + .s_ip_dest_ip(tx_udp_ip_dest_ip), + .s_udp_source_port(tx_udp_source_port), + .s_udp_dest_port(tx_udp_dest_port), + .s_udp_length(tx_udp_length), + .s_udp_checksum(tx_udp_checksum), + .s_udp_payload_axis_tdata(tx_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(tx_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(tx_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(tx_udp_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +endmodule diff --git a/corundum/lib/eth/rtl/udp_64.v b/corundum/lib/eth/rtl/udp_64.v new file mode 100644 index 0000000000000000000000000000000000000000..25393dae670f84d03d4830f551c594aec6a0e550 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_64.v @@ -0,0 +1,424 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP block, IP interface (64 bit datapath) + */ +module udp_64 # +( + parameter CHECKSUM_GEN_ENABLE = 1, + parameter CHECKSUM_PAYLOAD_FIFO_DEPTH = 2048, + parameter CHECKSUM_HEADER_FIFO_DEPTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_ip_eth_dest_mac, + input wire [47:0] s_ip_eth_src_mac, + input wire [15:0] s_ip_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_udp_eth_dest_mac, + input wire [47:0] s_udp_eth_src_mac, + input wire [15:0] s_udp_eth_type, + input wire [3:0] s_udp_ip_version, + input wire [3:0] s_udp_ip_ihl, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [15:0] s_udp_ip_identification, + input wire [2:0] s_udp_ip_flags, + input wire [12:0] s_udp_ip_fragment_offset, + input wire [7:0] s_udp_ip_ttl, + input wire [15:0] s_udp_ip_header_checksum, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire rx_busy, + output wire tx_busy, + output wire rx_error_header_early_termination, + output wire rx_error_payload_early_termination, + output wire tx_error_payload_early_termination +); + +wire tx_udp_hdr_valid; +wire tx_udp_hdr_ready; +wire [47:0] tx_udp_eth_dest_mac; +wire [47:0] tx_udp_eth_src_mac; +wire [15:0] tx_udp_eth_type; +wire [3:0] tx_udp_ip_version; +wire [3:0] tx_udp_ip_ihl; +wire [5:0] tx_udp_ip_dscp; +wire [1:0] tx_udp_ip_ecn; +wire [15:0] tx_udp_ip_identification; +wire [2:0] tx_udp_ip_flags; +wire [12:0] tx_udp_ip_fragment_offset; +wire [7:0] tx_udp_ip_ttl; +wire [15:0] tx_udp_ip_header_checksum; +wire [31:0] tx_udp_ip_source_ip; +wire [31:0] tx_udp_ip_dest_ip; +wire [15:0] tx_udp_source_port; +wire [15:0] tx_udp_dest_port; +wire [15:0] tx_udp_length; +wire [15:0] tx_udp_checksum; +wire [63:0] tx_udp_payload_axis_tdata; +wire [7:0] tx_udp_payload_axis_tkeep; +wire tx_udp_payload_axis_tvalid; +wire tx_udp_payload_axis_tready; +wire tx_udp_payload_axis_tlast; +wire tx_udp_payload_axis_tuser; + +udp_ip_rx_64 +udp_ip_rx_64_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(s_ip_hdr_valid), + .s_ip_hdr_ready(s_ip_hdr_ready), + .s_eth_dest_mac(s_ip_eth_dest_mac), + .s_eth_src_mac(s_ip_eth_src_mac), + .s_eth_type(s_ip_eth_type), + .s_ip_version(s_ip_version), + .s_ip_ihl(s_ip_ihl), + .s_ip_dscp(s_ip_dscp), + .s_ip_ecn(s_ip_ecn), + .s_ip_length(s_ip_length), + .s_ip_identification(s_ip_identification), + .s_ip_flags(s_ip_flags), + .s_ip_fragment_offset(s_ip_fragment_offset), + .s_ip_ttl(s_ip_ttl), + .s_ip_protocol(s_ip_protocol), + .s_ip_header_checksum(s_ip_header_checksum), + .s_ip_source_ip(s_ip_source_ip), + .s_ip_dest_ip(s_ip_dest_ip), + .s_ip_payload_axis_tdata(s_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(s_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(s_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(s_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(s_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(s_ip_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_eth_dest_mac(m_udp_eth_dest_mac), + .m_eth_src_mac(m_udp_eth_src_mac), + .m_eth_type(m_udp_eth_type), + .m_ip_version(m_udp_ip_version), + .m_ip_ihl(m_udp_ip_ihl), + .m_ip_dscp(m_udp_ip_dscp), + .m_ip_ecn(m_udp_ip_ecn), + .m_ip_length(m_udp_ip_length), + .m_ip_identification(m_udp_ip_identification), + .m_ip_flags(m_udp_ip_flags), + .m_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_ip_ttl(m_udp_ip_ttl), + .m_ip_protocol(m_udp_ip_protocol), + .m_ip_header_checksum(m_udp_ip_header_checksum), + .m_ip_source_ip(m_udp_ip_source_ip), + .m_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status signals + .busy(rx_busy), + .error_header_early_termination(rx_error_header_early_termination), + .error_payload_early_termination(rx_error_payload_early_termination) +); + +generate + +if (CHECKSUM_GEN_ENABLE) begin + + udp_checksum_gen_64 #( + .PAYLOAD_FIFO_DEPTH(CHECKSUM_PAYLOAD_FIFO_DEPTH), + .HEADER_FIFO_DEPTH(CHECKSUM_HEADER_FIFO_DEPTH) + ) + udp_checksum_gen_64_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_eth_dest_mac(s_udp_eth_dest_mac), + .s_eth_src_mac(s_udp_eth_src_mac), + .s_eth_type(s_udp_eth_type), + .s_ip_version(s_udp_ip_version), + .s_ip_ihl(s_udp_ip_ihl), + .s_ip_dscp(s_udp_ip_dscp), + .s_ip_ecn(s_udp_ip_ecn), + .s_ip_identification(s_udp_ip_identification), + .s_ip_flags(s_udp_ip_flags), + .s_ip_fragment_offset(s_udp_ip_fragment_offset), + .s_ip_ttl(s_udp_ip_ttl), + .s_ip_header_checksum(s_udp_ip_header_checksum), + .s_ip_source_ip(s_udp_ip_source_ip), + .s_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(tx_udp_hdr_valid), + .m_udp_hdr_ready(tx_udp_hdr_ready), + .m_eth_dest_mac(tx_udp_eth_dest_mac), + .m_eth_src_mac(tx_udp_eth_src_mac), + .m_eth_type(tx_udp_eth_type), + .m_ip_version(tx_udp_ip_version), + .m_ip_ihl(tx_udp_ip_ihl), + .m_ip_dscp(tx_udp_ip_dscp), + .m_ip_ecn(tx_udp_ip_ecn), + .m_ip_length(), + .m_ip_identification(tx_udp_ip_identification), + .m_ip_flags(tx_udp_ip_flags), + .m_ip_fragment_offset(tx_udp_ip_fragment_offset), + .m_ip_ttl(tx_udp_ip_ttl), + .m_ip_header_checksum(tx_udp_ip_header_checksum), + .m_ip_source_ip(tx_udp_ip_source_ip), + .m_ip_dest_ip(tx_udp_ip_dest_ip), + .m_udp_source_port(tx_udp_source_port), + .m_udp_dest_port(tx_udp_dest_port), + .m_udp_length(tx_udp_length), + .m_udp_checksum(tx_udp_checksum), + .m_udp_payload_axis_tdata(tx_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(tx_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(tx_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(tx_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(tx_udp_payload_axis_tuser), + // Status signals + .busy() + ); + +end else begin + + assign tx_udp_hdr_valid = s_udp_hdr_valid; + assign s_udp_hdr_ready = tx_udp_hdr_ready; + assign tx_udp_eth_dest_mac = s_udp_eth_dest_mac; + assign tx_udp_eth_src_mac = s_udp_eth_src_mac; + assign tx_udp_eth_type = s_udp_eth_type; + assign tx_udp_ip_version = s_udp_ip_version; + assign tx_udp_ip_ihl = s_udp_ip_ihl; + assign tx_udp_ip_dscp = s_udp_ip_dscp; + assign tx_udp_ip_ecn = s_udp_ip_ecn; + assign tx_udp_ip_identification = s_udp_ip_identification; + assign tx_udp_ip_flags = s_udp_ip_flags; + assign tx_udp_ip_fragment_offset = s_udp_ip_fragment_offset; + assign tx_udp_ip_ttl = s_udp_ip_ttl; + assign tx_udp_ip_header_checksum = s_udp_ip_header_checksum; + assign tx_udp_ip_source_ip = s_udp_ip_source_ip; + assign tx_udp_ip_dest_ip = s_udp_ip_dest_ip; + assign tx_udp_source_port = s_udp_source_port; + assign tx_udp_dest_port = s_udp_dest_port; + assign tx_udp_length = s_udp_length; + assign tx_udp_checksum = s_udp_checksum; + assign tx_udp_payload_axis_tdata = s_udp_payload_axis_tdata; + assign tx_udp_payload_axis_tkeep = s_udp_payload_axis_tkeep; + assign tx_udp_payload_axis_tvalid = s_udp_payload_axis_tvalid; + assign s_udp_payload_axis_tready = tx_udp_payload_axis_tready; + assign tx_udp_payload_axis_tlast = s_udp_payload_axis_tlast; + assign tx_udp_payload_axis_tuser = s_udp_payload_axis_tuser; + +end + +endgenerate + +udp_ip_tx_64 +udp_ip_tx_64_inst ( + .clk(clk), + .rst(rst), + // UDP frame input + .s_udp_hdr_valid(tx_udp_hdr_valid), + .s_udp_hdr_ready(tx_udp_hdr_ready), + .s_eth_dest_mac(tx_udp_eth_dest_mac), + .s_eth_src_mac(tx_udp_eth_src_mac), + .s_eth_type(tx_udp_eth_type), + .s_ip_version(tx_udp_ip_version), + .s_ip_ihl(tx_udp_ip_ihl), + .s_ip_dscp(tx_udp_ip_dscp), + .s_ip_ecn(tx_udp_ip_ecn), + .s_ip_identification(tx_udp_ip_identification), + .s_ip_flags(tx_udp_ip_flags), + .s_ip_fragment_offset(tx_udp_ip_fragment_offset), + .s_ip_ttl(tx_udp_ip_ttl), + .s_ip_protocol(8'h11), + .s_ip_header_checksum(tx_udp_ip_header_checksum), + .s_ip_source_ip(tx_udp_ip_source_ip), + .s_ip_dest_ip(tx_udp_ip_dest_ip), + .s_udp_source_port(tx_udp_source_port), + .s_udp_dest_port(tx_udp_dest_port), + .s_udp_length(tx_udp_length), + .s_udp_checksum(tx_udp_checksum), + .s_udp_payload_axis_tdata(tx_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(tx_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(tx_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(tx_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(tx_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(tx_udp_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(m_ip_hdr_valid), + .m_ip_hdr_ready(m_ip_hdr_ready), + .m_eth_dest_mac(m_ip_eth_dest_mac), + .m_eth_src_mac(m_ip_eth_src_mac), + .m_eth_type(m_ip_eth_type), + .m_ip_version(m_ip_version), + .m_ip_ihl(m_ip_ihl), + .m_ip_dscp(m_ip_dscp), + .m_ip_ecn(m_ip_ecn), + .m_ip_length(m_ip_length), + .m_ip_identification(m_ip_identification), + .m_ip_flags(m_ip_flags), + .m_ip_fragment_offset(m_ip_fragment_offset), + .m_ip_ttl(m_ip_ttl), + .m_ip_protocol(m_ip_protocol), + .m_ip_header_checksum(m_ip_header_checksum), + .m_ip_source_ip(m_ip_source_ip), + .m_ip_dest_ip(m_ip_dest_ip), + .m_ip_payload_axis_tdata(m_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(m_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(m_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(m_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(m_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(m_ip_payload_axis_tuser), + // Status signals + .busy(tx_busy), + .error_payload_early_termination(tx_error_payload_early_termination) +); + +endmodule diff --git a/corundum/lib/eth/rtl/udp_arb_mux.v b/corundum/lib/eth/rtl/udp_arb_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..2969b1b4d24c4d4ea3ef3de21452809229fb1d86 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_arb_mux.v @@ -0,0 +1,429 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP arbitrated multiplexer + */ +module udp_arb_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * UDP frame inputs + */ + input wire [S_COUNT-1:0] s_udp_hdr_valid, + output wire [S_COUNT-1:0] s_udp_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*16-1:0] s_udp_source_port, + input wire [S_COUNT*16-1:0] s_udp_dest_port, + input wire [S_COUNT*16-1:0] s_udp_length, + input wire [S_COUNT*16-1:0] s_udp_checksum, + input wire [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_udp_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_udp_payload_axis_tready, + input wire [S_COUNT-1:0] s_udp_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_udp_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg frame_reg = 1'b0, frame_next; + +reg s_udp_hdr_ready_mask_reg = 1'b0, s_udp_hdr_ready_mask_next; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; +reg [15:0] m_udp_source_port_reg = 16'd0, m_udp_source_port_next; +reg [15:0] m_udp_dest_port_reg = 16'd0, m_udp_dest_port_next; +reg [15:0] m_udp_length_reg = 16'd0, m_udp_length_next; +reg [15:0] m_udp_checksum_reg = 16'd0, m_udp_checksum_next; + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = (!s_udp_hdr_ready_mask_reg && grant_valid) << grant_encoded; + +assign s_udp_payload_axis_tready = (m_udp_payload_axis_tready_int_reg && grant_valid) << grant_encoded; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_udp_payload_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_udp_payload_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_udp_payload_axis_tvalid[grant_encoded]; +wire current_s_tready = s_udp_payload_axis_tready[grant_encoded]; +wire current_s_tlast = s_udp_payload_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_udp_payload_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_udp_payload_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_udp_payload_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_udp_hdr_valid & ~grant; +assign acknowledge = grant & s_udp_payload_axis_tvalid & s_udp_payload_axis_tready & s_udp_payload_axis_tlast; + +always @* begin + frame_next = frame_reg; + + s_udp_hdr_ready_mask_next = s_udp_hdr_ready_mask_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + m_udp_source_port_next = m_udp_source_port_reg; + m_udp_dest_port_next = m_udp_dest_port_reg; + m_udp_length_next = m_udp_length_reg; + m_udp_checksum_next = m_udp_checksum_reg; + + if (s_udp_payload_axis_tvalid[grant_encoded] && s_udp_payload_axis_tready[grant_encoded]) begin + // end of frame detection + if (s_udp_payload_axis_tlast[grant_encoded]) begin + frame_next = 1'b0; + s_udp_hdr_ready_mask_next = 1'b0; + end + end + + if (!frame_reg && grant_valid) begin + // start of frame + frame_next = 1'b1; + + s_udp_hdr_ready_mask_next = 1'b1; + + m_udp_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[grant_encoded*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[grant_encoded*48 +: 48]; + m_eth_type_next = s_eth_type[grant_encoded*16 +: 16]; + m_ip_version_next = s_ip_version[grant_encoded*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[grant_encoded*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[grant_encoded*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[grant_encoded*2 +: 2]; + m_ip_length_next = s_ip_length[grant_encoded*16 +: 16]; + m_ip_identification_next = s_ip_identification[grant_encoded*16 +: 16]; + m_ip_flags_next = s_ip_flags[grant_encoded*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[grant_encoded*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[grant_encoded*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[grant_encoded*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[grant_encoded*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[grant_encoded*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[grant_encoded*32 +: 32]; + m_udp_source_port_next = s_udp_source_port[grant_encoded*16 +: 16]; + m_udp_dest_port_next = s_udp_dest_port[grant_encoded*16 +: 16]; + m_udp_length_next = s_udp_length[grant_encoded*16 +: 16]; + m_udp_checksum_next = s_udp_checksum[grant_encoded*16 +: 16]; + end + + // pass through selected packet data + m_udp_payload_axis_tdata_int = current_s_tdata; + m_udp_payload_axis_tkeep_int = current_s_tkeep; + m_udp_payload_axis_tvalid_int = current_s_tvalid && m_udp_payload_axis_tready_int_reg && grant_valid; + m_udp_payload_axis_tlast_int = current_s_tlast; + m_udp_payload_axis_tid_int = current_s_tid; + m_udp_payload_axis_tdest_int = current_s_tdest; + m_udp_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + frame_reg <= 1'b0; + s_udp_hdr_ready_mask_reg <= 1'b0; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + frame_reg <= frame_next; + s_udp_hdr_ready_mask_reg <= s_udp_hdr_ready_mask_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; + m_udp_source_port_reg <= m_udp_source_port_next; + m_udp_dest_port_reg <= m_udp_dest_port_next; + m_udp_length_reg <= m_udp_length_next; + m_udp_checksum_reg <= m_udp_checksum_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tkeep = KEEP_ENABLE ? m_udp_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tid = ID_ENABLE ? m_udp_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_udp_payload_axis_tdest = DEST_ENABLE ? m_udp_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_udp_payload_axis_tuser = USER_ENABLE ? m_udp_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tid_reg <= temp_m_udp_payload_axis_tid_reg; + m_udp_payload_axis_tdest_reg <= temp_m_udp_payload_axis_tdest_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + temp_m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_checksum_gen.v b/corundum/lib/eth/rtl/udp_checksum_gen.v new file mode 100644 index 0000000000000000000000000000000000000000..e6e474cac4e57cfc44941456d7622f0b88a94e47 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_checksum_gen.v @@ -0,0 +1,557 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP checksum calculation module + */ +module udp_checksum_gen # +( + parameter PAYLOAD_FIFO_DEPTH = 2048, + parameter HEADER_FIFO_DEPTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel and payload on +an AXI stream interface, calculates the length and checksum, then produces the +header fields in parallel along with the UDP payload in a separate AXI stream. + +*/ + +parameter HEADER_FIFO_ADDR_WIDTH = $clog2(HEADER_FIFO_DEPTH); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_SUM_HEADER_1 = 3'd1, + STATE_SUM_HEADER_2 = 3'd2, + STATE_SUM_HEADER_3 = 3'd3, + STATE_SUM_PAYLOAD = 3'd4, + STATE_FINISH_SUM = 3'd5; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg shift_payload_in; +reg [31:0] checksum_part; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [31:0] checksum_reg = 32'd0, checksum_next; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; +reg [3:0] ip_version_reg = 4'd0; +reg [3:0] ip_ihl_reg = 4'd0; +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [15:0] ip_header_checksum_reg = 16'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; + +reg hdr_valid_reg = 0, hdr_valid_next; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +/* + * UDP Payload FIFO + */ +wire [7:0] s_udp_payload_fifo_tdata; +wire s_udp_payload_fifo_tvalid; +wire s_udp_payload_fifo_tready; +wire s_udp_payload_fifo_tlast; +wire s_udp_payload_fifo_tuser; + +wire [7:0] m_udp_payload_fifo_tdata; +wire m_udp_payload_fifo_tvalid; +wire m_udp_payload_fifo_tready; +wire m_udp_payload_fifo_tlast; +wire m_udp_payload_fifo_tuser; + +axis_fifo #( + .DEPTH(PAYLOAD_FIFO_DEPTH), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) +) +payload_fifo ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_udp_payload_fifo_tdata), + .s_axis_tkeep(0), + .s_axis_tvalid(s_udp_payload_fifo_tvalid), + .s_axis_tready(s_udp_payload_fifo_tready), + .s_axis_tlast(s_udp_payload_fifo_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(s_udp_payload_fifo_tuser), + // AXI output + .m_axis_tdata(m_udp_payload_fifo_tdata), + .m_axis_tkeep(), + .m_axis_tvalid(m_udp_payload_fifo_tvalid), + .m_axis_tready(m_udp_payload_fifo_tready), + .m_axis_tlast(m_udp_payload_fifo_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(m_udp_payload_fifo_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +assign s_udp_payload_fifo_tdata = s_udp_payload_axis_tdata; +assign s_udp_payload_fifo_tvalid = s_udp_payload_axis_tvalid && shift_payload_in; +assign s_udp_payload_axis_tready = s_udp_payload_fifo_tready && shift_payload_in; +assign s_udp_payload_fifo_tlast = s_udp_payload_axis_tlast; +assign s_udp_payload_fifo_tuser = s_udp_payload_axis_tuser; + +assign m_udp_payload_axis_tdata = m_udp_payload_fifo_tdata; +assign m_udp_payload_axis_tvalid = m_udp_payload_fifo_tvalid; +assign m_udp_payload_fifo_tready = m_udp_payload_axis_tready; +assign m_udp_payload_axis_tlast = m_udp_payload_fifo_tlast; +assign m_udp_payload_axis_tuser = m_udp_payload_fifo_tuser; + +/* + * UDP Header FIFO + */ +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_wr_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_wr_ptr_next; +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_rd_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_rd_ptr_next; + +reg [47:0] eth_dest_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [47:0] eth_src_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] eth_type_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_version_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_ihl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [5:0] ip_dscp_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [1:0] ip_ecn_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_identification_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [2:0] ip_flags_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [12:0] ip_fragment_offset_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [7:0] ip_ttl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_header_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_source_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_dest_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_source_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_dest_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_length_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; + +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; + +// full when first MSB different but rest same +wire header_fifo_full = ((header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH] != header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH]) && + (header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0] == header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire header_fifo_empty = header_fifo_wr_ptr_reg == header_fifo_rd_ptr_reg; + +// control signals +reg header_fifo_write; +reg header_fifo_read; + +wire header_fifo_ready = !header_fifo_full; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; + +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_udp_length_reg + 16'd20; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = 8'h11; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// Write logic +always @* begin + header_fifo_write = 1'b0; + + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg; + + if (hdr_valid_reg) begin + // input data valid + if (~header_fifo_full) begin + // not full, perform write + header_fifo_write = 1'b1; + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_wr_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + header_fifo_wr_ptr_reg <= header_fifo_wr_ptr_next; + end + + if (header_fifo_write) begin + eth_dest_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_dest_mac_reg; + eth_src_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_src_mac_reg; + eth_type_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_type_reg; + ip_version_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_version_reg; + ip_ihl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ihl_reg; + ip_dscp_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dscp_reg; + ip_ecn_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ecn_reg; + ip_identification_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_identification_reg; + ip_flags_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_flags_reg; + ip_fragment_offset_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_fragment_offset_reg; + ip_ttl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ttl_reg; + ip_header_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_header_checksum_reg; + ip_source_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_source_ip_reg; + ip_dest_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dest_ip_reg; + udp_source_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_source_port_reg; + udp_dest_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_dest_port_reg; + udp_length_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= frame_ptr_reg; + udp_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= checksum_reg[15:0]; + end +end + +// Read logic +always @* begin + header_fifo_read = 1'b0; + + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg; + + if (m_udp_hdr_ready || !m_udp_hdr_valid) begin + // output data not valid OR currently being transferred + if (!header_fifo_empty) begin + // not empty, perform read + header_fifo_read = 1'b1; + m_udp_hdr_valid_next = 1'b1; + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg + 1; + end else begin + // empty, invalidate + m_udp_hdr_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_rd_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + header_fifo_rd_ptr_reg <= header_fifo_rd_ptr_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + if (header_fifo_read) begin + m_eth_dest_mac_reg <= eth_dest_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_src_mac_reg <= eth_src_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_type_reg <= eth_type_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_version_reg <= ip_version_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ihl_reg <= ip_ihl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dscp_reg <= ip_dscp_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ecn_reg <= ip_ecn_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_identification_reg <= ip_identification_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_flags_reg <= ip_flags_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_fragment_offset_reg <= ip_fragment_offset_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ttl_reg <= ip_ttl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_header_checksum_reg <= ip_header_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_source_ip_reg <= ip_source_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dest_ip_reg <= ip_dest_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_source_port_reg <= udp_source_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_dest_port_reg <= udp_dest_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_length_reg <= udp_length_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_checksum_reg <= udp_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + end +end + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; + +assign busy = busy_reg; + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + shift_payload_in = 1'b0; + + frame_ptr_next = frame_ptr_reg; + checksum_next = checksum_reg; + + hdr_valid_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state + s_udp_hdr_ready_next = header_fifo_ready; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + frame_ptr_next = 0; + // 16'h0011 = zero padded type field + // 16'h0010 = header length times two + checksum_next = 16'h0011 + 16'h0010; + s_udp_hdr_ready_next = 1'b0; + state_next = STATE_SUM_HEADER_1; + end else begin + state_next = STATE_IDLE; + end + end + STATE_SUM_HEADER_1: begin + // sum pseudo header and header + checksum_next = checksum_reg + ip_source_ip_reg[31:16] + ip_source_ip_reg[15:0]; + state_next = STATE_SUM_HEADER_2; + end + STATE_SUM_HEADER_2: begin + // sum pseudo header and header + checksum_next = checksum_reg + ip_dest_ip_reg[31:16] + ip_dest_ip_reg[15:0]; + state_next = STATE_SUM_HEADER_3; + end + STATE_SUM_HEADER_3: begin + // sum pseudo header and header + checksum_next = checksum_reg + udp_source_port_reg + udp_dest_port_reg; + frame_ptr_next = 8; + state_next = STATE_SUM_PAYLOAD; + end + STATE_SUM_PAYLOAD: begin + // sum payload + shift_payload_in = 1'b1; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + // checksum computation for payload - alternately store and accumulate + // add 2 for length calculation (two length fields in pseudo header) + if (frame_ptr_reg[0]) begin + checksum_next = checksum_reg + {8'h00, s_udp_payload_axis_tdata} + 2; + end else begin + checksum_next = checksum_reg + {s_udp_payload_axis_tdata, 8'h00} + 2; + end + + frame_ptr_next = frame_ptr_reg + 1; + + if (s_udp_payload_axis_tlast) begin + state_next = STATE_FINISH_SUM; + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end + STATE_FINISH_SUM: begin + // add MSW (twice!) for proper ones complement sum + checksum_part = checksum_reg[15:0] + checksum_reg[31:16]; + checksum_next = ~(checksum_part[15:0] + checksum_part[16]); + hdr_valid_next = 1; + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + hdr_valid_reg <= hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + end + + frame_ptr_reg <= frame_ptr_next; + checksum_reg <= checksum_next; + + // datapath + if (store_udp_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + ip_version_reg <= s_ip_version; + ip_ihl_reg <= s_ip_ihl; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_header_checksum_reg <= s_ip_header_checksum; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_checksum_gen_64.v b/corundum/lib/eth/rtl/udp_checksum_gen_64.v new file mode 100644 index 0000000000000000000000000000000000000000..9cc7b94c5a9667bf44c05b5cdc9e171275372eda --- /dev/null +++ b/corundum/lib/eth/rtl/udp_checksum_gen_64.v @@ -0,0 +1,593 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP checksum calculation module (64 bit datapath) + */ +module udp_checksum_gen_64 # +( + parameter PAYLOAD_FIFO_DEPTH = 2048, + parameter HEADER_FIFO_DEPTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel and payload on +an AXI stream interface, calculates the length and checksum, then produces the +header fields in parallel along with the UDP payload in a separate AXI stream. + +*/ + +parameter HEADER_FIFO_ADDR_WIDTH = $clog2(HEADER_FIFO_DEPTH); + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_SUM_HEADER = 3'd1, + STATE_SUM_PAYLOAD = 3'd2, + STATE_FINISH_SUM_1 = 3'd3, + STATE_FINISH_SUM_2 = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg shift_payload_in; +reg [31:0] checksum_part; + +reg [15:0] frame_ptr_reg = 16'd0, frame_ptr_next; + +reg [31:0] checksum_reg = 32'd0, checksum_next; +reg [16:0] checksum_temp1_reg = 17'd0, checksum_temp1_next; +reg [16:0] checksum_temp2_reg = 17'd0, checksum_temp2_next; + +reg [47:0] eth_dest_mac_reg = 48'd0; +reg [47:0] eth_src_mac_reg = 48'd0; +reg [15:0] eth_type_reg = 16'd0; +reg [3:0] ip_version_reg = 4'd0; +reg [3:0] ip_ihl_reg = 4'd0; +reg [5:0] ip_dscp_reg = 6'd0; +reg [1:0] ip_ecn_reg = 2'd0; +reg [15:0] ip_identification_reg = 16'd0; +reg [2:0] ip_flags_reg = 3'd0; +reg [12:0] ip_fragment_offset_reg = 13'd0; +reg [7:0] ip_ttl_reg = 8'd0; +reg [15:0] ip_header_checksum_reg = 16'd0; +reg [31:0] ip_source_ip_reg = 32'd0; +reg [31:0] ip_dest_ip_reg = 32'd0; +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; + +reg hdr_valid_reg = 0, hdr_valid_next; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg busy_reg = 1'b0; + +/* + * UDP Payload FIFO + */ +wire [63:0] s_udp_payload_fifo_tdata; +wire [7:0] s_udp_payload_fifo_tkeep; +wire s_udp_payload_fifo_tvalid; +wire s_udp_payload_fifo_tready; +wire s_udp_payload_fifo_tlast; +wire s_udp_payload_fifo_tuser; + +wire [63:0] m_udp_payload_fifo_tdata; +wire [7:0] m_udp_payload_fifo_tkeep; +wire m_udp_payload_fifo_tvalid; +wire m_udp_payload_fifo_tready; +wire m_udp_payload_fifo_tlast; +wire m_udp_payload_fifo_tuser; + +axis_fifo #( + .DEPTH(PAYLOAD_FIFO_DEPTH), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .KEEP_WIDTH(8), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) +) +payload_fifo ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(s_udp_payload_fifo_tdata), + .s_axis_tkeep(s_udp_payload_fifo_tkeep), + .s_axis_tvalid(s_udp_payload_fifo_tvalid), + .s_axis_tready(s_udp_payload_fifo_tready), + .s_axis_tlast(s_udp_payload_fifo_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(s_udp_payload_fifo_tuser), + // AXI output + .m_axis_tdata(m_udp_payload_fifo_tdata), + .m_axis_tkeep(m_udp_payload_fifo_tkeep), + .m_axis_tvalid(m_udp_payload_fifo_tvalid), + .m_axis_tready(m_udp_payload_fifo_tready), + .m_axis_tlast(m_udp_payload_fifo_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(m_udp_payload_fifo_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +assign s_udp_payload_fifo_tdata = s_udp_payload_axis_tdata; +assign s_udp_payload_fifo_tkeep = s_udp_payload_axis_tkeep; +assign s_udp_payload_fifo_tvalid = s_udp_payload_axis_tvalid && shift_payload_in; +assign s_udp_payload_axis_tready = s_udp_payload_fifo_tready && shift_payload_in; +assign s_udp_payload_fifo_tlast = s_udp_payload_axis_tlast; +assign s_udp_payload_fifo_tuser = s_udp_payload_axis_tuser; + +assign m_udp_payload_axis_tdata = m_udp_payload_fifo_tdata; +assign m_udp_payload_axis_tkeep = m_udp_payload_fifo_tkeep; +assign m_udp_payload_axis_tvalid = m_udp_payload_fifo_tvalid; +assign m_udp_payload_fifo_tready = m_udp_payload_axis_tready; +assign m_udp_payload_axis_tlast = m_udp_payload_fifo_tlast; +assign m_udp_payload_axis_tuser = m_udp_payload_fifo_tuser; + +/* + * UDP Header FIFO + */ +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_wr_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_wr_ptr_next; +reg [HEADER_FIFO_ADDR_WIDTH:0] header_fifo_rd_ptr_reg = {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}, header_fifo_rd_ptr_next; + +reg [47:0] eth_dest_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [47:0] eth_src_mac_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] eth_type_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_version_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [3:0] ip_ihl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [5:0] ip_dscp_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [1:0] ip_ecn_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_identification_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [2:0] ip_flags_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [12:0] ip_fragment_offset_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [7:0] ip_ttl_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] ip_header_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_source_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [31:0] ip_dest_ip_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_source_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_dest_port_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_length_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; +reg [15:0] udp_checksum_mem[(2**HEADER_FIFO_ADDR_WIDTH)-1:0]; + +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; + +// full when first MSB different but rest same +wire header_fifo_full = ((header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH] != header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH]) && + (header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0] == header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0])); +// empty when pointers match exactly +wire header_fifo_empty = header_fifo_wr_ptr_reg == header_fifo_rd_ptr_reg; + +// control signals +reg header_fifo_write; +reg header_fifo_read; + +wire header_fifo_ready = !header_fifo_full; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; + +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_udp_length_reg + 16'd20; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = 8'h11; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// Write logic +always @* begin + header_fifo_write = 1'b0; + + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg; + + if (hdr_valid_reg) begin + // input data valid + if (~header_fifo_full) begin + // not full, perform write + header_fifo_write = 1'b1; + header_fifo_wr_ptr_next = header_fifo_wr_ptr_reg + 1; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_wr_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + end else begin + header_fifo_wr_ptr_reg <= header_fifo_wr_ptr_next; + end + + if (header_fifo_write) begin + eth_dest_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_dest_mac_reg; + eth_src_mac_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_src_mac_reg; + eth_type_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= eth_type_reg; + ip_version_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_version_reg; + ip_ihl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ihl_reg; + ip_dscp_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dscp_reg; + ip_ecn_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ecn_reg; + ip_identification_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_identification_reg; + ip_flags_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_flags_reg; + ip_fragment_offset_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_fragment_offset_reg; + ip_ttl_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_ttl_reg; + ip_header_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_header_checksum_reg; + ip_source_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_source_ip_reg; + ip_dest_ip_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= ip_dest_ip_reg; + udp_source_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_source_port_reg; + udp_dest_port_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= udp_dest_port_reg; + udp_length_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= frame_ptr_reg; + udp_checksum_mem[header_fifo_wr_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]] <= checksum_reg[15:0]; + end +end + +// Read logic +always @* begin + header_fifo_read = 1'b0; + + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg; + + if (m_udp_hdr_ready || !m_udp_hdr_valid) begin + // output data not valid OR currently being transferred + if (!header_fifo_empty) begin + // not empty, perform read + header_fifo_read = 1'b1; + m_udp_hdr_valid_next = 1'b1; + header_fifo_rd_ptr_next = header_fifo_rd_ptr_reg + 1; + end else begin + // empty, invalidate + m_udp_hdr_valid_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + header_fifo_rd_ptr_reg <= {HEADER_FIFO_ADDR_WIDTH+1{1'b0}}; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + header_fifo_rd_ptr_reg <= header_fifo_rd_ptr_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + if (header_fifo_read) begin + m_eth_dest_mac_reg <= eth_dest_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_src_mac_reg <= eth_src_mac_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_eth_type_reg <= eth_type_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_version_reg <= ip_version_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ihl_reg <= ip_ihl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dscp_reg <= ip_dscp_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ecn_reg <= ip_ecn_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_identification_reg <= ip_identification_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_flags_reg <= ip_flags_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_fragment_offset_reg <= ip_fragment_offset_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_ttl_reg <= ip_ttl_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_header_checksum_reg <= ip_header_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_source_ip_reg <= ip_source_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_ip_dest_ip_reg <= ip_dest_ip_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_source_port_reg <= udp_source_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_dest_port_reg <= udp_dest_port_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_length_reg <= udp_length_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + m_udp_checksum_reg <= udp_checksum_mem[header_fifo_rd_ptr_reg[HEADER_FIFO_ADDR_WIDTH-1:0]]; + end +end + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; + +assign busy = busy_reg; + +integer i, word_cnt; + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + shift_payload_in = 1'b0; + + frame_ptr_next = frame_ptr_reg; + checksum_next = checksum_reg; + checksum_temp1_next = checksum_temp1_reg; + checksum_temp2_next = checksum_temp2_reg; + + hdr_valid_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state + s_udp_hdr_ready_next = header_fifo_ready; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + frame_ptr_next = 0; + // 16'h0011 = zero padded type field + // 16'h0010 = header length times two + checksum_next = 16'h0011 + 16'h0010; + checksum_temp1_next = s_ip_source_ip[31:16]; + checksum_temp2_next = s_ip_source_ip[15:0]; + s_udp_hdr_ready_next = 1'b0; + state_next = STATE_SUM_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_SUM_HEADER: begin + // sum pseudo header and header + checksum_next = checksum_reg + checksum_temp1_reg + checksum_temp2_reg; + checksum_temp1_next = ip_dest_ip_reg[31:16] + ip_dest_ip_reg[15:0]; + checksum_temp2_next = udp_source_port_reg + udp_dest_port_reg; + frame_ptr_next = 8; + state_next = STATE_SUM_PAYLOAD; + end + STATE_SUM_PAYLOAD: begin + // sum payload + shift_payload_in = 1'b1; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + word_cnt = 1; + for (i = 1; i <= 8; i = i + 1) begin + if (s_udp_payload_axis_tkeep == 8'hff >> (8-i)) word_cnt = i; + end + + checksum_temp1_next = 0; + checksum_temp2_next = 0; + + for (i = 0; i < 4; i = i + 1) begin + if (s_udp_payload_axis_tkeep[i]) begin + if (i & 1) begin + checksum_temp1_next = checksum_temp1_next + {8'h00, s_udp_payload_axis_tdata[i*8 +: 8]}; + end else begin + checksum_temp1_next = checksum_temp1_next + {s_udp_payload_axis_tdata[i*8 +: 8], 8'h00}; + end + end + end + + for (i = 4; i < 8; i = i + 1) begin + if (s_udp_payload_axis_tkeep[i]) begin + if (i & 1) begin + checksum_temp2_next = checksum_temp2_next + {8'h00, s_udp_payload_axis_tdata[i*8 +: 8]}; + end else begin + checksum_temp2_next = checksum_temp2_next + {s_udp_payload_axis_tdata[i*8 +: 8], 8'h00}; + end + end + end + + // add length * 2 (two copies of length field in pseudo header) + checksum_next = checksum_reg + checksum_temp1_reg + checksum_temp2_reg + (word_cnt << 1); + + frame_ptr_next = frame_ptr_reg + word_cnt; + + if (s_udp_payload_axis_tlast) begin + state_next = STATE_FINISH_SUM_1; + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end else begin + state_next = STATE_SUM_PAYLOAD; + end + end + STATE_FINISH_SUM_1: begin + // empty pipeline + checksum_next = checksum_reg + checksum_temp1_reg + checksum_temp2_reg; + state_next = STATE_FINISH_SUM_2; + end + STATE_FINISH_SUM_2: begin + // add MSW (twice!) for proper ones complement sum + checksum_part = checksum_reg[15:0] + checksum_reg[31:16]; + checksum_next = ~(checksum_part[15:0] + checksum_part[16]); + hdr_valid_next = 1; + state_next = STATE_IDLE; + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + hdr_valid_reg <= hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + end + + frame_ptr_reg <= frame_ptr_next; + checksum_reg <= checksum_next; + checksum_temp1_reg <= checksum_temp1_next; + checksum_temp2_reg <= checksum_temp2_next; + + // datapath + if (store_udp_hdr) begin + eth_dest_mac_reg <= s_eth_dest_mac; + eth_src_mac_reg <= s_eth_src_mac; + eth_type_reg <= s_eth_type; + ip_version_reg <= s_ip_version; + ip_ihl_reg <= s_ip_ihl; + ip_dscp_reg <= s_ip_dscp; + ip_ecn_reg <= s_ip_ecn; + ip_identification_reg <= s_ip_identification; + ip_flags_reg <= s_ip_flags; + ip_fragment_offset_reg <= s_ip_fragment_offset; + ip_ttl_reg <= s_ip_ttl; + ip_header_checksum_reg <= s_ip_header_checksum; + ip_source_ip_reg <= s_ip_source_ip; + ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_complete.v b/corundum/lib/eth/rtl/udp_complete.v new file mode 100644 index 0000000000000000000000000000000000000000..4432c9091bf11eda38efcd6268fc3ecec39835c7 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_complete.v @@ -0,0 +1,637 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IPv4 and ARP block with UDP support, ethernet frame interface + */ +module udp_complete #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2, + parameter ARP_REQUEST_TIMEOUT = 125000000*30, + parameter UDP_CHECKSUM_GEN_ENABLE = 1, + parameter UDP_CHECKSUM_PAYLOAD_FIFO_DEPTH = 2048, + parameter UDP_CHECKSUM_HEADER_FIFO_DEPTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [7:0] s_eth_payload_axis_tdata, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [7:0] m_eth_payload_axis_tdata, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [7:0] s_udp_ip_ttl, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status + */ + output wire ip_rx_busy, + output wire ip_tx_busy, + output wire udp_rx_busy, + output wire udp_tx_busy, + output wire ip_rx_error_header_early_termination, + output wire ip_rx_error_payload_early_termination, + output wire ip_rx_error_invalid_header, + output wire ip_rx_error_invalid_checksum, + output wire ip_tx_error_payload_early_termination, + output wire ip_tx_error_arp_failed, + output wire udp_rx_error_header_early_termination, + output wire udp_rx_error_payload_early_termination, + output wire udp_tx_error_payload_early_termination, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +wire ip_rx_ip_hdr_valid; +wire ip_rx_ip_hdr_ready; +wire [47:0] ip_rx_ip_eth_dest_mac; +wire [47:0] ip_rx_ip_eth_src_mac; +wire [15:0] ip_rx_ip_eth_type; +wire [3:0] ip_rx_ip_version; +wire [3:0] ip_rx_ip_ihl; +wire [5:0] ip_rx_ip_dscp; +wire [1:0] ip_rx_ip_ecn; +wire [15:0] ip_rx_ip_length; +wire [15:0] ip_rx_ip_identification; +wire [2:0] ip_rx_ip_flags; +wire [12:0] ip_rx_ip_fragment_offset; +wire [7:0] ip_rx_ip_ttl; +wire [7:0] ip_rx_ip_protocol; +wire [15:0] ip_rx_ip_header_checksum; +wire [31:0] ip_rx_ip_source_ip; +wire [31:0] ip_rx_ip_dest_ip; +wire [7:0] ip_rx_ip_payload_axis_tdata; +wire ip_rx_ip_payload_axis_tvalid; +wire ip_rx_ip_payload_axis_tlast; +wire ip_rx_ip_payload_axis_tuser; +wire ip_rx_ip_payload_axis_tready; + +wire ip_tx_ip_hdr_valid; +wire ip_tx_ip_hdr_ready; +wire [5:0] ip_tx_ip_dscp; +wire [1:0] ip_tx_ip_ecn; +wire [15:0] ip_tx_ip_length; +wire [7:0] ip_tx_ip_ttl; +wire [7:0] ip_tx_ip_protocol; +wire [31:0] ip_tx_ip_source_ip; +wire [31:0] ip_tx_ip_dest_ip; +wire [7:0] ip_tx_ip_payload_axis_tdata; +wire ip_tx_ip_payload_axis_tvalid; +wire ip_tx_ip_payload_axis_tlast; +wire ip_tx_ip_payload_axis_tuser; +wire ip_tx_ip_payload_axis_tready; + +wire udp_rx_ip_hdr_valid; +wire udp_rx_ip_hdr_ready; +wire [47:0] udp_rx_ip_eth_dest_mac; +wire [47:0] udp_rx_ip_eth_src_mac; +wire [15:0] udp_rx_ip_eth_type; +wire [3:0] udp_rx_ip_version; +wire [3:0] udp_rx_ip_ihl; +wire [5:0] udp_rx_ip_dscp; +wire [1:0] udp_rx_ip_ecn; +wire [15:0] udp_rx_ip_length; +wire [15:0] udp_rx_ip_identification; +wire [2:0] udp_rx_ip_flags; +wire [12:0] udp_rx_ip_fragment_offset; +wire [7:0] udp_rx_ip_ttl; +wire [7:0] udp_rx_ip_protocol; +wire [15:0] udp_rx_ip_header_checksum; +wire [31:0] udp_rx_ip_source_ip; +wire [31:0] udp_rx_ip_dest_ip; +wire [7:0] udp_rx_ip_payload_axis_tdata; +wire udp_rx_ip_payload_axis_tvalid; +wire udp_rx_ip_payload_axis_tlast; +wire udp_rx_ip_payload_axis_tuser; +wire udp_rx_ip_payload_axis_tready; + +wire udp_tx_ip_hdr_valid; +wire udp_tx_ip_hdr_ready; +wire [5:0] udp_tx_ip_dscp; +wire [1:0] udp_tx_ip_ecn; +wire [15:0] udp_tx_ip_length; +wire [7:0] udp_tx_ip_ttl; +wire [7:0] udp_tx_ip_protocol; +wire [31:0] udp_tx_ip_source_ip; +wire [31:0] udp_tx_ip_dest_ip; +wire [7:0] udp_tx_ip_payload_axis_tdata; +wire udp_tx_ip_payload_axis_tvalid; +wire udp_tx_ip_payload_axis_tlast; +wire udp_tx_ip_payload_axis_tuser; +wire udp_tx_ip_payload_axis_tready; + +/* + * Input classifier (ip_protocol) + */ +wire s_select_udp = (ip_rx_ip_protocol == 8'h11); +wire s_select_ip = !s_select_udp; + +reg s_select_udp_reg = 1'b0; +reg s_select_ip_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end else begin + if (ip_rx_ip_payload_axis_tvalid) begin + if ((!s_select_udp_reg && !s_select_ip_reg) || + (ip_rx_ip_payload_axis_tvalid && ip_rx_ip_payload_axis_tready && ip_rx_ip_payload_axis_tlast)) begin + s_select_udp_reg <= s_select_udp; + s_select_ip_reg <= s_select_ip; + end + end else begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end + end +end + +// IP frame to UDP module +assign udp_rx_ip_hdr_valid = s_select_udp && ip_rx_ip_hdr_valid; +assign udp_rx_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign udp_rx_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign udp_rx_ip_eth_type = ip_rx_ip_eth_type; +assign udp_rx_ip_version = ip_rx_ip_version; +assign udp_rx_ip_ihl = ip_rx_ip_ihl; +assign udp_rx_ip_dscp = ip_rx_ip_dscp; +assign udp_rx_ip_ecn = ip_rx_ip_ecn; +assign udp_rx_ip_length = ip_rx_ip_length; +assign udp_rx_ip_identification = ip_rx_ip_identification; +assign udp_rx_ip_flags = ip_rx_ip_flags; +assign udp_rx_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign udp_rx_ip_ttl = ip_rx_ip_ttl; +assign udp_rx_ip_protocol = 8'h11; +assign udp_rx_ip_header_checksum = ip_rx_ip_header_checksum; +assign udp_rx_ip_source_ip = ip_rx_ip_source_ip; +assign udp_rx_ip_dest_ip = ip_rx_ip_dest_ip; +assign udp_rx_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign udp_rx_ip_payload_axis_tvalid = s_select_udp_reg && ip_rx_ip_payload_axis_tvalid; +assign udp_rx_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign udp_rx_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +// External IP frame output +assign m_ip_hdr_valid = s_select_ip && ip_rx_ip_hdr_valid; +assign m_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign m_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign m_ip_eth_type = ip_rx_ip_eth_type; +assign m_ip_version = ip_rx_ip_version; +assign m_ip_ihl = ip_rx_ip_ihl; +assign m_ip_dscp = ip_rx_ip_dscp; +assign m_ip_ecn = ip_rx_ip_ecn; +assign m_ip_length = ip_rx_ip_length; +assign m_ip_identification = ip_rx_ip_identification; +assign m_ip_flags = ip_rx_ip_flags; +assign m_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign m_ip_ttl = ip_rx_ip_ttl; +assign m_ip_protocol = ip_rx_ip_protocol; +assign m_ip_header_checksum = ip_rx_ip_header_checksum; +assign m_ip_source_ip = ip_rx_ip_source_ip; +assign m_ip_dest_ip = ip_rx_ip_dest_ip; +assign m_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign m_ip_payload_axis_tvalid = s_select_ip_reg && ip_rx_ip_payload_axis_tvalid; +assign m_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign m_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +assign ip_rx_ip_hdr_ready = (s_select_udp && udp_rx_ip_hdr_ready) || + (s_select_ip && m_ip_hdr_ready); + +assign ip_rx_ip_payload_axis_tready = (s_select_udp_reg && udp_rx_ip_payload_axis_tready) || + (s_select_ip_reg && m_ip_payload_axis_tready); + +/* + * Output arbiter + */ +ip_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(8), + .KEEP_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +ip_arb_mux_inst ( + .clk(clk), + .rst(rst), + // IP frame inputs + .s_ip_hdr_valid({s_ip_hdr_valid, udp_tx_ip_hdr_valid}), + .s_ip_hdr_ready({s_ip_hdr_ready, udp_tx_ip_hdr_ready}), + .s_eth_dest_mac(0), + .s_eth_src_mac(0), + .s_eth_type(0), + .s_ip_version(0), + .s_ip_ihl(0), + .s_ip_dscp({s_ip_dscp, udp_tx_ip_dscp}), + .s_ip_ecn({s_ip_ecn, udp_tx_ip_ecn}), + .s_ip_length({s_ip_length, udp_tx_ip_length}), + .s_ip_identification(0), + .s_ip_flags(0), + .s_ip_fragment_offset(0), + .s_ip_ttl({s_ip_ttl, udp_tx_ip_ttl}), + .s_ip_protocol({s_ip_protocol, udp_tx_ip_protocol}), + .s_ip_header_checksum(0), + .s_ip_source_ip({s_ip_source_ip, udp_tx_ip_source_ip}), + .s_ip_dest_ip({s_ip_dest_ip, udp_tx_ip_dest_ip}), + .s_ip_payload_axis_tdata({s_ip_payload_axis_tdata, udp_tx_ip_payload_axis_tdata}), + .s_ip_payload_axis_tkeep(0), + .s_ip_payload_axis_tvalid({s_ip_payload_axis_tvalid, udp_tx_ip_payload_axis_tvalid}), + .s_ip_payload_axis_tready({s_ip_payload_axis_tready, udp_tx_ip_payload_axis_tready}), + .s_ip_payload_axis_tlast({s_ip_payload_axis_tlast, udp_tx_ip_payload_axis_tlast}), + .s_ip_payload_axis_tid(0), + .s_ip_payload_axis_tdest(0), + .s_ip_payload_axis_tuser({s_ip_payload_axis_tuser, udp_tx_ip_payload_axis_tuser}), + // IP frame output + .m_ip_hdr_valid(ip_tx_ip_hdr_valid), + .m_ip_hdr_ready(ip_tx_ip_hdr_ready), + .m_eth_dest_mac(), + .m_eth_src_mac(), + .m_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(ip_tx_ip_dscp), + .m_ip_ecn(ip_tx_ip_ecn), + .m_ip_length(ip_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(ip_tx_ip_ttl), + .m_ip_protocol(ip_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(ip_tx_ip_source_ip), + .m_ip_dest_ip(ip_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(), + .m_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(), + .m_ip_payload_axis_tdest(), + .m_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser) +); + +/* + * IP stack + */ +ip_complete #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +ip_complete_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(ip_tx_ip_hdr_valid), + .s_ip_hdr_ready(ip_tx_ip_hdr_ready), + .s_ip_dscp(ip_tx_ip_dscp), + .s_ip_ecn(ip_tx_ip_ecn), + .s_ip_length(ip_tx_ip_length), + .s_ip_ttl(ip_tx_ip_ttl), + .s_ip_protocol(ip_tx_ip_protocol), + .s_ip_source_ip(ip_tx_ip_source_ip), + .s_ip_dest_ip(ip_tx_ip_dest_ip), + .s_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(ip_rx_ip_hdr_valid), + .m_ip_hdr_ready(ip_rx_ip_hdr_ready), + .m_ip_eth_dest_mac(ip_rx_ip_eth_dest_mac), + .m_ip_eth_src_mac(ip_rx_ip_eth_src_mac), + .m_ip_eth_type(ip_rx_ip_eth_type), + .m_ip_version(ip_rx_ip_version), + .m_ip_ihl(ip_rx_ip_ihl), + .m_ip_dscp(ip_rx_ip_dscp), + .m_ip_ecn(ip_rx_ip_ecn), + .m_ip_length(ip_rx_ip_length), + .m_ip_identification(ip_rx_ip_identification), + .m_ip_flags(ip_rx_ip_flags), + .m_ip_fragment_offset(ip_rx_ip_fragment_offset), + .m_ip_ttl(ip_rx_ip_ttl), + .m_ip_protocol(ip_rx_ip_protocol), + .m_ip_header_checksum(ip_rx_ip_header_checksum), + .m_ip_source_ip(ip_rx_ip_source_ip), + .m_ip_dest_ip(ip_rx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_rx_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(ip_rx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_rx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_rx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(ip_rx_ip_payload_axis_tuser), + // Status + .rx_busy(ip_rx_busy), + .tx_busy(ip_tx_busy), + .rx_error_header_early_termination(ip_rx_error_header_early_termination), + .rx_error_payload_early_termination(ip_rx_error_payload_early_termination), + .rx_error_invalid_header(ip_rx_error_invalid_header), + .rx_error_invalid_checksum(ip_rx_error_invalid_checksum), + .tx_error_payload_early_termination(ip_tx_error_payload_early_termination), + .tx_error_arp_failed(ip_tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_arp_cache(clear_arp_cache) +); + +/* + * UDP interface + */ +udp #( + .CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE), + .CHECKSUM_PAYLOAD_FIFO_DEPTH(UDP_CHECKSUM_PAYLOAD_FIFO_DEPTH), + .CHECKSUM_HEADER_FIFO_DEPTH(UDP_CHECKSUM_HEADER_FIFO_DEPTH) +) +udp_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(udp_rx_ip_hdr_valid), + .s_ip_hdr_ready(udp_rx_ip_hdr_ready), + .s_ip_eth_dest_mac(udp_rx_ip_eth_dest_mac), + .s_ip_eth_src_mac(udp_rx_ip_eth_src_mac), + .s_ip_eth_type(udp_rx_ip_eth_type), + .s_ip_version(udp_rx_ip_version), + .s_ip_ihl(udp_rx_ip_ihl), + .s_ip_dscp(udp_rx_ip_dscp), + .s_ip_ecn(udp_rx_ip_ecn), + .s_ip_length(udp_rx_ip_length), + .s_ip_identification(udp_rx_ip_identification), + .s_ip_flags(udp_rx_ip_flags), + .s_ip_fragment_offset(udp_rx_ip_fragment_offset), + .s_ip_ttl(udp_rx_ip_ttl), + .s_ip_protocol(udp_rx_ip_protocol), + .s_ip_header_checksum(udp_rx_ip_header_checksum), + .s_ip_source_ip(udp_rx_ip_source_ip), + .s_ip_dest_ip(udp_rx_ip_dest_ip), + .s_ip_payload_axis_tdata(udp_rx_ip_payload_axis_tdata), + .s_ip_payload_axis_tvalid(udp_rx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(udp_rx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(udp_rx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(udp_rx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(udp_tx_ip_hdr_valid), + .m_ip_hdr_ready(udp_tx_ip_hdr_ready), + .m_ip_eth_dest_mac(), + .m_ip_eth_src_mac(), + .m_ip_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(udp_tx_ip_dscp), + .m_ip_ecn(udp_tx_ip_ecn), + .m_ip_length(udp_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(udp_tx_ip_ttl), + .m_ip_protocol(udp_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(udp_tx_ip_source_ip), + .m_ip_dest_ip(udp_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(udp_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tvalid(udp_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(udp_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(udp_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(udp_tx_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_eth_dest_mac(48'd0), + .s_udp_eth_src_mac(48'd0), + .s_udp_eth_type(16'd0), + .s_udp_ip_version(4'd0), + .s_udp_ip_ihl(4'd0), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_identification(16'd0), + .s_udp_ip_flags(3'd0), + .s_udp_ip_fragment_offset(13'd0), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_header_checksum(16'd0), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status + .rx_busy(udp_rx_busy), + .tx_busy(udp_tx_busy), + .rx_error_header_early_termination(udp_rx_error_header_early_termination), + .rx_error_payload_early_termination(udp_rx_error_payload_early_termination), + .tx_error_payload_early_termination(udp_tx_error_payload_early_termination) +); + +endmodule diff --git a/corundum/lib/eth/rtl/udp_complete_64.v b/corundum/lib/eth/rtl/udp_complete_64.v new file mode 100644 index 0000000000000000000000000000000000000000..2ab69c91b7f7877640dd94d005c0d74f5e63675a --- /dev/null +++ b/corundum/lib/eth/rtl/udp_complete_64.v @@ -0,0 +1,657 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * IPv4 and ARP block with UDP support, ethernet frame interface (64 bit datapath) + */ +module udp_complete_64 #( + parameter ARP_CACHE_ADDR_WIDTH = 9, + parameter ARP_REQUEST_RETRY_COUNT = 4, + parameter ARP_REQUEST_RETRY_INTERVAL = 125000000*2, + parameter ARP_REQUEST_TIMEOUT = 125000000*30, + parameter UDP_CHECKSUM_GEN_ENABLE = 1, + parameter UDP_CHECKSUM_PAYLOAD_FIFO_DEPTH = 2048, + parameter UDP_CHECKSUM_HEADER_FIFO_DEPTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * Ethernet frame input + */ + input wire s_eth_hdr_valid, + output wire s_eth_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [63:0] s_eth_payload_axis_tdata, + input wire [7:0] s_eth_payload_axis_tkeep, + input wire s_eth_payload_axis_tvalid, + output wire s_eth_payload_axis_tready, + input wire s_eth_payload_axis_tlast, + input wire s_eth_payload_axis_tuser, + + /* + * Ethernet frame output + */ + output wire m_eth_hdr_valid, + input wire m_eth_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [63:0] m_eth_payload_axis_tdata, + output wire [7:0] m_eth_payload_axis_tkeep, + output wire m_eth_payload_axis_tvalid, + input wire m_eth_payload_axis_tready, + output wire m_eth_payload_axis_tlast, + output wire m_eth_payload_axis_tuser, + + /* + * IP input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * IP output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_ip_eth_dest_mac, + output wire [47:0] m_ip_eth_src_mac, + output wire [15:0] m_ip_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * UDP input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [5:0] s_udp_ip_dscp, + input wire [1:0] s_udp_ip_ecn, + input wire [7:0] s_udp_ip_ttl, + input wire [31:0] s_udp_ip_source_ip, + input wire [31:0] s_udp_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * UDP output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_udp_eth_dest_mac, + output wire [47:0] m_udp_eth_src_mac, + output wire [15:0] m_udp_eth_type, + output wire [3:0] m_udp_ip_version, + output wire [3:0] m_udp_ip_ihl, + output wire [5:0] m_udp_ip_dscp, + output wire [1:0] m_udp_ip_ecn, + output wire [15:0] m_udp_ip_length, + output wire [15:0] m_udp_ip_identification, + output wire [2:0] m_udp_ip_flags, + output wire [12:0] m_udp_ip_fragment_offset, + output wire [7:0] m_udp_ip_ttl, + output wire [7:0] m_udp_ip_protocol, + output wire [15:0] m_udp_ip_header_checksum, + output wire [31:0] m_udp_ip_source_ip, + output wire [31:0] m_udp_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status + */ + output wire ip_rx_busy, + output wire ip_tx_busy, + output wire udp_rx_busy, + output wire udp_tx_busy, + output wire ip_rx_error_header_early_termination, + output wire ip_rx_error_payload_early_termination, + output wire ip_rx_error_invalid_header, + output wire ip_rx_error_invalid_checksum, + output wire ip_tx_error_payload_early_termination, + output wire ip_tx_error_arp_failed, + output wire udp_rx_error_header_early_termination, + output wire udp_rx_error_payload_early_termination, + output wire udp_tx_error_payload_early_termination, + + /* + * Configuration + */ + input wire [47:0] local_mac, + input wire [31:0] local_ip, + input wire [31:0] gateway_ip, + input wire [31:0] subnet_mask, + input wire clear_arp_cache +); + +wire ip_rx_ip_hdr_valid; +wire ip_rx_ip_hdr_ready; +wire [47:0] ip_rx_ip_eth_dest_mac; +wire [47:0] ip_rx_ip_eth_src_mac; +wire [15:0] ip_rx_ip_eth_type; +wire [3:0] ip_rx_ip_version; +wire [3:0] ip_rx_ip_ihl; +wire [5:0] ip_rx_ip_dscp; +wire [1:0] ip_rx_ip_ecn; +wire [15:0] ip_rx_ip_length; +wire [15:0] ip_rx_ip_identification; +wire [2:0] ip_rx_ip_flags; +wire [12:0] ip_rx_ip_fragment_offset; +wire [7:0] ip_rx_ip_ttl; +wire [7:0] ip_rx_ip_protocol; +wire [15:0] ip_rx_ip_header_checksum; +wire [31:0] ip_rx_ip_source_ip; +wire [31:0] ip_rx_ip_dest_ip; +wire [63:0] ip_rx_ip_payload_axis_tdata; +wire [7:0] ip_rx_ip_payload_axis_tkeep; +wire ip_rx_ip_payload_axis_tvalid; +wire ip_rx_ip_payload_axis_tlast; +wire ip_rx_ip_payload_axis_tuser; +wire ip_rx_ip_payload_axis_tready; + +wire ip_tx_ip_hdr_valid; +wire ip_tx_ip_hdr_ready; +wire [5:0] ip_tx_ip_dscp; +wire [1:0] ip_tx_ip_ecn; +wire [15:0] ip_tx_ip_length; +wire [7:0] ip_tx_ip_ttl; +wire [7:0] ip_tx_ip_protocol; +wire [31:0] ip_tx_ip_source_ip; +wire [31:0] ip_tx_ip_dest_ip; +wire [63:0] ip_tx_ip_payload_axis_tdata; +wire [7:0] ip_tx_ip_payload_axis_tkeep; +wire ip_tx_ip_payload_axis_tvalid; +wire ip_tx_ip_payload_axis_tlast; +wire ip_tx_ip_payload_axis_tuser; +wire ip_tx_ip_payload_axis_tready; + +wire udp_rx_ip_hdr_valid; +wire udp_rx_ip_hdr_ready; +wire [47:0] udp_rx_ip_eth_dest_mac; +wire [47:0] udp_rx_ip_eth_src_mac; +wire [15:0] udp_rx_ip_eth_type; +wire [3:0] udp_rx_ip_version; +wire [3:0] udp_rx_ip_ihl; +wire [5:0] udp_rx_ip_dscp; +wire [1:0] udp_rx_ip_ecn; +wire [15:0] udp_rx_ip_length; +wire [15:0] udp_rx_ip_identification; +wire [2:0] udp_rx_ip_flags; +wire [12:0] udp_rx_ip_fragment_offset; +wire [7:0] udp_rx_ip_ttl; +wire [7:0] udp_rx_ip_protocol; +wire [15:0] udp_rx_ip_header_checksum; +wire [31:0] udp_rx_ip_source_ip; +wire [31:0] udp_rx_ip_dest_ip; +wire [63:0] udp_rx_ip_payload_axis_tdata; +wire [7:0] udp_rx_ip_payload_axis_tkeep; +wire udp_rx_ip_payload_axis_tvalid; +wire udp_rx_ip_payload_axis_tlast; +wire udp_rx_ip_payload_axis_tuser; +wire udp_rx_ip_payload_axis_tready; + +wire udp_tx_ip_hdr_valid; +wire udp_tx_ip_hdr_ready; +wire [5:0] udp_tx_ip_dscp; +wire [1:0] udp_tx_ip_ecn; +wire [15:0] udp_tx_ip_length; +wire [7:0] udp_tx_ip_ttl; +wire [7:0] udp_tx_ip_protocol; +wire [31:0] udp_tx_ip_source_ip; +wire [31:0] udp_tx_ip_dest_ip; +wire [63:0] udp_tx_ip_payload_axis_tdata; +wire [7:0] udp_tx_ip_payload_axis_tkeep; +wire udp_tx_ip_payload_axis_tvalid; +wire udp_tx_ip_payload_axis_tlast; +wire udp_tx_ip_payload_axis_tuser; +wire udp_tx_ip_payload_axis_tready; + +/* + * Input classifier (ip_protocol) + */ +wire s_select_udp = (ip_rx_ip_protocol == 8'h11); +wire s_select_ip = !s_select_udp; + +reg s_select_udp_reg = 1'b0; +reg s_select_ip_reg = 1'b0; + +always @(posedge clk) begin + if (rst) begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end else begin + if (ip_rx_ip_payload_axis_tvalid) begin + if ((!s_select_udp_reg && !s_select_ip_reg) || + (ip_rx_ip_payload_axis_tvalid && ip_rx_ip_payload_axis_tready && ip_rx_ip_payload_axis_tlast)) begin + s_select_udp_reg <= s_select_udp; + s_select_ip_reg <= s_select_ip; + end + end else begin + s_select_udp_reg <= 1'b0; + s_select_ip_reg <= 1'b0; + end + end +end + +// IP frame to UDP module +assign udp_rx_ip_hdr_valid = s_select_udp && ip_rx_ip_hdr_valid; +assign udp_rx_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign udp_rx_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign udp_rx_ip_eth_type = ip_rx_ip_eth_type; +assign udp_rx_ip_version = ip_rx_ip_version; +assign udp_rx_ip_ihl = ip_rx_ip_ihl; +assign udp_rx_ip_dscp = ip_rx_ip_dscp; +assign udp_rx_ip_ecn = ip_rx_ip_ecn; +assign udp_rx_ip_length = ip_rx_ip_length; +assign udp_rx_ip_identification = ip_rx_ip_identification; +assign udp_rx_ip_flags = ip_rx_ip_flags; +assign udp_rx_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign udp_rx_ip_ttl = ip_rx_ip_ttl; +assign udp_rx_ip_protocol = 8'h11; +assign udp_rx_ip_header_checksum = ip_rx_ip_header_checksum; +assign udp_rx_ip_source_ip = ip_rx_ip_source_ip; +assign udp_rx_ip_dest_ip = ip_rx_ip_dest_ip; +assign udp_rx_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign udp_rx_ip_payload_axis_tkeep = ip_rx_ip_payload_axis_tkeep; +assign udp_rx_ip_payload_axis_tvalid = s_select_udp_reg && ip_rx_ip_payload_axis_tvalid; +assign udp_rx_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign udp_rx_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +// External IP frame output +assign m_ip_hdr_valid = s_select_ip && ip_rx_ip_hdr_valid; +assign m_ip_eth_dest_mac = ip_rx_ip_eth_dest_mac; +assign m_ip_eth_src_mac = ip_rx_ip_eth_src_mac; +assign m_ip_eth_type = ip_rx_ip_eth_type; +assign m_ip_version = ip_rx_ip_version; +assign m_ip_ihl = ip_rx_ip_ihl; +assign m_ip_dscp = ip_rx_ip_dscp; +assign m_ip_ecn = ip_rx_ip_ecn; +assign m_ip_length = ip_rx_ip_length; +assign m_ip_identification = ip_rx_ip_identification; +assign m_ip_flags = ip_rx_ip_flags; +assign m_ip_fragment_offset = ip_rx_ip_fragment_offset; +assign m_ip_ttl = ip_rx_ip_ttl; +assign m_ip_protocol = ip_rx_ip_protocol; +assign m_ip_header_checksum = ip_rx_ip_header_checksum; +assign m_ip_source_ip = ip_rx_ip_source_ip; +assign m_ip_dest_ip = ip_rx_ip_dest_ip; +assign m_ip_payload_axis_tdata = ip_rx_ip_payload_axis_tdata; +assign m_ip_payload_axis_tkeep = ip_rx_ip_payload_axis_tkeep; +assign m_ip_payload_axis_tvalid = s_select_ip_reg && ip_rx_ip_payload_axis_tvalid; +assign m_ip_payload_axis_tlast = ip_rx_ip_payload_axis_tlast; +assign m_ip_payload_axis_tuser = ip_rx_ip_payload_axis_tuser; + +assign ip_rx_ip_hdr_ready = (s_select_udp && udp_rx_ip_hdr_ready) || + (s_select_ip && m_ip_hdr_ready); + +assign ip_rx_ip_payload_axis_tready = (s_select_udp_reg && udp_rx_ip_payload_axis_tready) || + (s_select_ip_reg && m_ip_payload_axis_tready); + +/* + * Output arbiter + */ +ip_arb_mux #( + .S_COUNT(2), + .DATA_WIDTH(64), + .KEEP_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .ARB_TYPE("PRIORITY"), + .LSB_PRIORITY("HIGH") +) +ip_arb_mux_inst ( + .clk(clk), + .rst(rst), + // IP frame inputs + .s_ip_hdr_valid({s_ip_hdr_valid, udp_tx_ip_hdr_valid}), + .s_ip_hdr_ready({s_ip_hdr_ready, udp_tx_ip_hdr_ready}), + .s_eth_dest_mac(0), + .s_eth_src_mac(0), + .s_eth_type(0), + .s_ip_version(0), + .s_ip_ihl(0), + .s_ip_dscp({s_ip_dscp, udp_tx_ip_dscp}), + .s_ip_ecn({s_ip_ecn, udp_tx_ip_ecn}), + .s_ip_length({s_ip_length, udp_tx_ip_length}), + .s_ip_identification(0), + .s_ip_flags(0), + .s_ip_fragment_offset(0), + .s_ip_ttl({s_ip_ttl, udp_tx_ip_ttl}), + .s_ip_protocol({s_ip_protocol, udp_tx_ip_protocol}), + .s_ip_header_checksum(0), + .s_ip_source_ip({s_ip_source_ip, udp_tx_ip_source_ip}), + .s_ip_dest_ip({s_ip_dest_ip, udp_tx_ip_dest_ip}), + .s_ip_payload_axis_tdata({s_ip_payload_axis_tdata, udp_tx_ip_payload_axis_tdata}), + .s_ip_payload_axis_tkeep({s_ip_payload_axis_tkeep, udp_tx_ip_payload_axis_tkeep}), + .s_ip_payload_axis_tvalid({s_ip_payload_axis_tvalid, udp_tx_ip_payload_axis_tvalid}), + .s_ip_payload_axis_tready({s_ip_payload_axis_tready, udp_tx_ip_payload_axis_tready}), + .s_ip_payload_axis_tlast({s_ip_payload_axis_tlast, udp_tx_ip_payload_axis_tlast}), + .s_ip_payload_axis_tid(0), + .s_ip_payload_axis_tdest(0), + .s_ip_payload_axis_tuser({s_ip_payload_axis_tuser, udp_tx_ip_payload_axis_tuser}), + // IP frame output + .m_ip_hdr_valid(ip_tx_ip_hdr_valid), + .m_ip_hdr_ready(ip_tx_ip_hdr_ready), + .m_eth_dest_mac(), + .m_eth_src_mac(), + .m_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(ip_tx_ip_dscp), + .m_ip_ecn(ip_tx_ip_ecn), + .m_ip_length(ip_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(ip_tx_ip_ttl), + .m_ip_protocol(ip_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(ip_tx_ip_source_ip), + .m_ip_dest_ip(ip_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(ip_tx_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tid(), + .m_ip_payload_axis_tdest(), + .m_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser) +); + +/* + * IP stack + */ +ip_complete_64 #( + .ARP_CACHE_ADDR_WIDTH(ARP_CACHE_ADDR_WIDTH), + .ARP_REQUEST_RETRY_COUNT(ARP_REQUEST_RETRY_COUNT), + .ARP_REQUEST_RETRY_INTERVAL(ARP_REQUEST_RETRY_INTERVAL), + .ARP_REQUEST_TIMEOUT(ARP_REQUEST_TIMEOUT) +) +ip_complete_64_inst ( + .clk(clk), + .rst(rst), + // Ethernet frame input + .s_eth_hdr_valid(s_eth_hdr_valid), + .s_eth_hdr_ready(s_eth_hdr_ready), + .s_eth_dest_mac(s_eth_dest_mac), + .s_eth_src_mac(s_eth_src_mac), + .s_eth_type(s_eth_type), + .s_eth_payload_axis_tdata(s_eth_payload_axis_tdata), + .s_eth_payload_axis_tkeep(s_eth_payload_axis_tkeep), + .s_eth_payload_axis_tvalid(s_eth_payload_axis_tvalid), + .s_eth_payload_axis_tready(s_eth_payload_axis_tready), + .s_eth_payload_axis_tlast(s_eth_payload_axis_tlast), + .s_eth_payload_axis_tuser(s_eth_payload_axis_tuser), + // Ethernet frame output + .m_eth_hdr_valid(m_eth_hdr_valid), + .m_eth_hdr_ready(m_eth_hdr_ready), + .m_eth_dest_mac(m_eth_dest_mac), + .m_eth_src_mac(m_eth_src_mac), + .m_eth_type(m_eth_type), + .m_eth_payload_axis_tdata(m_eth_payload_axis_tdata), + .m_eth_payload_axis_tkeep(m_eth_payload_axis_tkeep), + .m_eth_payload_axis_tvalid(m_eth_payload_axis_tvalid), + .m_eth_payload_axis_tready(m_eth_payload_axis_tready), + .m_eth_payload_axis_tlast(m_eth_payload_axis_tlast), + .m_eth_payload_axis_tuser(m_eth_payload_axis_tuser), + // IP frame input + .s_ip_hdr_valid(ip_tx_ip_hdr_valid), + .s_ip_hdr_ready(ip_tx_ip_hdr_ready), + .s_ip_dscp(ip_tx_ip_dscp), + .s_ip_ecn(ip_tx_ip_ecn), + .s_ip_length(ip_tx_ip_length), + .s_ip_ttl(ip_tx_ip_ttl), + .s_ip_protocol(ip_tx_ip_protocol), + .s_ip_source_ip(ip_tx_ip_source_ip), + .s_ip_dest_ip(ip_tx_ip_dest_ip), + .s_ip_payload_axis_tdata(ip_tx_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(ip_tx_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(ip_tx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(ip_tx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(ip_tx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(ip_tx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(ip_rx_ip_hdr_valid), + .m_ip_hdr_ready(ip_rx_ip_hdr_ready), + .m_ip_eth_dest_mac(ip_rx_ip_eth_dest_mac), + .m_ip_eth_src_mac(ip_rx_ip_eth_src_mac), + .m_ip_eth_type(ip_rx_ip_eth_type), + .m_ip_version(ip_rx_ip_version), + .m_ip_ihl(ip_rx_ip_ihl), + .m_ip_dscp(ip_rx_ip_dscp), + .m_ip_ecn(ip_rx_ip_ecn), + .m_ip_length(ip_rx_ip_length), + .m_ip_identification(ip_rx_ip_identification), + .m_ip_flags(ip_rx_ip_flags), + .m_ip_fragment_offset(ip_rx_ip_fragment_offset), + .m_ip_ttl(ip_rx_ip_ttl), + .m_ip_protocol(ip_rx_ip_protocol), + .m_ip_header_checksum(ip_rx_ip_header_checksum), + .m_ip_source_ip(ip_rx_ip_source_ip), + .m_ip_dest_ip(ip_rx_ip_dest_ip), + .m_ip_payload_axis_tdata(ip_rx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(ip_rx_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(ip_rx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(ip_rx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(ip_rx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(ip_rx_ip_payload_axis_tuser), + // Status + .rx_busy(ip_rx_busy), + .tx_busy(ip_tx_busy), + .rx_error_header_early_termination(ip_rx_error_header_early_termination), + .rx_error_payload_early_termination(ip_rx_error_payload_early_termination), + .rx_error_invalid_header(ip_rx_error_invalid_header), + .rx_error_invalid_checksum(ip_rx_error_invalid_checksum), + .tx_error_payload_early_termination(ip_tx_error_payload_early_termination), + .tx_error_arp_failed(ip_tx_error_arp_failed), + // Configuration + .local_mac(local_mac), + .local_ip(local_ip), + .gateway_ip(gateway_ip), + .subnet_mask(subnet_mask), + .clear_arp_cache(clear_arp_cache) +); + +/* + * UDP interface + */ +udp_64 #( + .CHECKSUM_GEN_ENABLE(UDP_CHECKSUM_GEN_ENABLE), + .CHECKSUM_PAYLOAD_FIFO_DEPTH(UDP_CHECKSUM_PAYLOAD_FIFO_DEPTH), + .CHECKSUM_HEADER_FIFO_DEPTH(UDP_CHECKSUM_HEADER_FIFO_DEPTH) +) +udp_64_inst ( + .clk(clk), + .rst(rst), + // IP frame input + .s_ip_hdr_valid(udp_rx_ip_hdr_valid), + .s_ip_hdr_ready(udp_rx_ip_hdr_ready), + .s_ip_eth_dest_mac(udp_rx_ip_eth_dest_mac), + .s_ip_eth_src_mac(udp_rx_ip_eth_src_mac), + .s_ip_eth_type(udp_rx_ip_eth_type), + .s_ip_version(udp_rx_ip_version), + .s_ip_ihl(udp_rx_ip_ihl), + .s_ip_dscp(udp_rx_ip_dscp), + .s_ip_ecn(udp_rx_ip_ecn), + .s_ip_length(udp_rx_ip_length), + .s_ip_identification(udp_rx_ip_identification), + .s_ip_flags(udp_rx_ip_flags), + .s_ip_fragment_offset(udp_rx_ip_fragment_offset), + .s_ip_ttl(udp_rx_ip_ttl), + .s_ip_protocol(udp_rx_ip_protocol), + .s_ip_header_checksum(udp_rx_ip_header_checksum), + .s_ip_source_ip(udp_rx_ip_source_ip), + .s_ip_dest_ip(udp_rx_ip_dest_ip), + .s_ip_payload_axis_tdata(udp_rx_ip_payload_axis_tdata), + .s_ip_payload_axis_tkeep(udp_rx_ip_payload_axis_tkeep), + .s_ip_payload_axis_tvalid(udp_rx_ip_payload_axis_tvalid), + .s_ip_payload_axis_tready(udp_rx_ip_payload_axis_tready), + .s_ip_payload_axis_tlast(udp_rx_ip_payload_axis_tlast), + .s_ip_payload_axis_tuser(udp_rx_ip_payload_axis_tuser), + // IP frame output + .m_ip_hdr_valid(udp_tx_ip_hdr_valid), + .m_ip_hdr_ready(udp_tx_ip_hdr_ready), + .m_ip_eth_dest_mac(), + .m_ip_eth_src_mac(), + .m_ip_eth_type(), + .m_ip_version(), + .m_ip_ihl(), + .m_ip_dscp(udp_tx_ip_dscp), + .m_ip_ecn(udp_tx_ip_ecn), + .m_ip_length(udp_tx_ip_length), + .m_ip_identification(), + .m_ip_flags(), + .m_ip_fragment_offset(), + .m_ip_ttl(udp_tx_ip_ttl), + .m_ip_protocol(udp_tx_ip_protocol), + .m_ip_header_checksum(), + .m_ip_source_ip(udp_tx_ip_source_ip), + .m_ip_dest_ip(udp_tx_ip_dest_ip), + .m_ip_payload_axis_tdata(udp_tx_ip_payload_axis_tdata), + .m_ip_payload_axis_tkeep(udp_tx_ip_payload_axis_tkeep), + .m_ip_payload_axis_tvalid(udp_tx_ip_payload_axis_tvalid), + .m_ip_payload_axis_tready(udp_tx_ip_payload_axis_tready), + .m_ip_payload_axis_tlast(udp_tx_ip_payload_axis_tlast), + .m_ip_payload_axis_tuser(udp_tx_ip_payload_axis_tuser), + // UDP frame input + .s_udp_hdr_valid(s_udp_hdr_valid), + .s_udp_hdr_ready(s_udp_hdr_ready), + .s_udp_eth_dest_mac(48'd0), + .s_udp_eth_src_mac(48'd0), + .s_udp_eth_type(16'd0), + .s_udp_ip_version(4'd0), + .s_udp_ip_ihl(4'd0), + .s_udp_ip_dscp(s_udp_ip_dscp), + .s_udp_ip_ecn(s_udp_ip_ecn), + .s_udp_ip_identification(16'd0), + .s_udp_ip_flags(3'd0), + .s_udp_ip_fragment_offset(13'd0), + .s_udp_ip_ttl(s_udp_ip_ttl), + .s_udp_ip_header_checksum(16'd0), + .s_udp_ip_source_ip(s_udp_ip_source_ip), + .s_udp_ip_dest_ip(s_udp_ip_dest_ip), + .s_udp_source_port(s_udp_source_port), + .s_udp_dest_port(s_udp_dest_port), + .s_udp_length(s_udp_length), + .s_udp_checksum(s_udp_checksum), + .s_udp_payload_axis_tdata(s_udp_payload_axis_tdata), + .s_udp_payload_axis_tkeep(s_udp_payload_axis_tkeep), + .s_udp_payload_axis_tvalid(s_udp_payload_axis_tvalid), + .s_udp_payload_axis_tready(s_udp_payload_axis_tready), + .s_udp_payload_axis_tlast(s_udp_payload_axis_tlast), + .s_udp_payload_axis_tuser(s_udp_payload_axis_tuser), + // UDP frame output + .m_udp_hdr_valid(m_udp_hdr_valid), + .m_udp_hdr_ready(m_udp_hdr_ready), + .m_udp_eth_dest_mac(m_udp_eth_dest_mac), + .m_udp_eth_src_mac(m_udp_eth_src_mac), + .m_udp_eth_type(m_udp_eth_type), + .m_udp_ip_version(m_udp_ip_version), + .m_udp_ip_ihl(m_udp_ip_ihl), + .m_udp_ip_dscp(m_udp_ip_dscp), + .m_udp_ip_ecn(m_udp_ip_ecn), + .m_udp_ip_length(m_udp_ip_length), + .m_udp_ip_identification(m_udp_ip_identification), + .m_udp_ip_flags(m_udp_ip_flags), + .m_udp_ip_fragment_offset(m_udp_ip_fragment_offset), + .m_udp_ip_ttl(m_udp_ip_ttl), + .m_udp_ip_protocol(m_udp_ip_protocol), + .m_udp_ip_header_checksum(m_udp_ip_header_checksum), + .m_udp_ip_source_ip(m_udp_ip_source_ip), + .m_udp_ip_dest_ip(m_udp_ip_dest_ip), + .m_udp_source_port(m_udp_source_port), + .m_udp_dest_port(m_udp_dest_port), + .m_udp_length(m_udp_length), + .m_udp_checksum(m_udp_checksum), + .m_udp_payload_axis_tdata(m_udp_payload_axis_tdata), + .m_udp_payload_axis_tkeep(m_udp_payload_axis_tkeep), + .m_udp_payload_axis_tvalid(m_udp_payload_axis_tvalid), + .m_udp_payload_axis_tready(m_udp_payload_axis_tready), + .m_udp_payload_axis_tlast(m_udp_payload_axis_tlast), + .m_udp_payload_axis_tuser(m_udp_payload_axis_tuser), + // Status + .rx_busy(udp_rx_busy), + .tx_busy(udp_tx_busy), + .rx_error_header_early_termination(udp_rx_error_header_early_termination), + .rx_error_payload_early_termination(udp_rx_error_payload_early_termination), + .tx_error_payload_early_termination(udp_tx_error_payload_early_termination) +); + +endmodule diff --git a/corundum/lib/eth/rtl/udp_demux.v b/corundum/lib/eth/rtl/udp_demux.v new file mode 100644 index 0000000000000000000000000000000000000000..f4dfb8cc915fee900c2a05acd4d5e7e289d8c607 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_demux.v @@ -0,0 +1,424 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP demultiplexer + */ +module udp_demux # +( + parameter M_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [DATA_WIDTH-1:0] s_udp_payload_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire [ID_WIDTH-1:0] s_udp_payload_axis_tid, + input wire [DEST_WIDTH-1:0] s_udp_payload_axis_tdest, + input wire [USER_WIDTH-1:0] s_udp_payload_axis_tuser, + + /* + * UDP frame outputs + */ + output wire [M_COUNT-1:0] m_udp_hdr_valid, + input wire [M_COUNT-1:0] m_udp_hdr_ready, + output wire [M_COUNT*48-1:0] m_eth_dest_mac, + output wire [M_COUNT*48-1:0] m_eth_src_mac, + output wire [M_COUNT*16-1:0] m_eth_type, + output wire [M_COUNT*4-1:0] m_ip_version, + output wire [M_COUNT*4-1:0] m_ip_ihl, + output wire [M_COUNT*6-1:0] m_ip_dscp, + output wire [M_COUNT*2-1:0] m_ip_ecn, + output wire [M_COUNT*16-1:0] m_ip_length, + output wire [M_COUNT*16-1:0] m_ip_identification, + output wire [M_COUNT*3-1:0] m_ip_flags, + output wire [M_COUNT*13-1:0] m_ip_fragment_offset, + output wire [M_COUNT*8-1:0] m_ip_ttl, + output wire [M_COUNT*8-1:0] m_ip_protocol, + output wire [M_COUNT*16-1:0] m_ip_header_checksum, + output wire [M_COUNT*32-1:0] m_ip_source_ip, + output wire [M_COUNT*32-1:0] m_ip_dest_ip, + output wire [M_COUNT*16-1:0] m_udp_source_port, + output wire [M_COUNT*16-1:0] m_udp_dest_port, + output wire [M_COUNT*16-1:0] m_udp_length, + output wire [M_COUNT*16-1:0] m_udp_checksum, + output wire [M_COUNT*DATA_WIDTH-1:0] m_udp_payload_axis_tdata, + output wire [M_COUNT*KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep, + output wire [M_COUNT-1:0] m_udp_payload_axis_tvalid, + input wire [M_COUNT-1:0] m_udp_payload_axis_tready, + output wire [M_COUNT-1:0] m_udp_payload_axis_tlast, + output wire [M_COUNT*ID_WIDTH-1:0] m_udp_payload_axis_tid, + output wire [M_COUNT*DEST_WIDTH-1:0] m_udp_payload_axis_tdest, + output wire [M_COUNT*USER_WIDTH-1:0] m_udp_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [$clog2(M_COUNT)-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; + +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg [M_COUNT-1:0] m_udp_hdr_valid_reg = 0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; +reg [15:0] m_udp_source_port_reg = 16'd0, m_udp_source_port_next; +reg [15:0] m_udp_dest_port_reg = 16'd0, m_udp_dest_port_next; +reg [15:0] m_udp_length_reg = 16'd0, m_udp_length_next; +reg [15:0] m_udp_checksum_reg = 16'd0, m_udp_checksum_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_int; +reg [M_COUNT-1:0] m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg && enable; + +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg && enable; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = {M_COUNT{m_eth_dest_mac_reg}}; +assign m_eth_src_mac = {M_COUNT{m_eth_src_mac_reg}}; +assign m_eth_type = {M_COUNT{m_eth_type_reg}}; +assign m_ip_version = {M_COUNT{m_ip_version_reg}}; +assign m_ip_ihl = {M_COUNT{m_ip_ihl_reg}}; +assign m_ip_dscp = {M_COUNT{m_ip_dscp_reg}}; +assign m_ip_ecn = {M_COUNT{m_ip_ecn_reg}}; +assign m_ip_length = {M_COUNT{m_ip_length_reg}}; +assign m_ip_identification = {M_COUNT{m_ip_identification_reg}}; +assign m_ip_flags = {M_COUNT{m_ip_flags_reg}}; +assign m_ip_fragment_offset = {M_COUNT{m_ip_fragment_offset_reg}}; +assign m_ip_ttl = {M_COUNT{m_ip_ttl_reg}}; +assign m_ip_protocol = {M_COUNT{m_ip_protocol_reg}}; +assign m_ip_header_checksum = {M_COUNT{m_ip_header_checksum_reg}}; +assign m_ip_source_ip = {M_COUNT{m_ip_source_ip_reg}}; +assign m_ip_dest_ip = {M_COUNT{m_ip_dest_ip_reg}}; +assign m_udp_source_port = {M_COUNT{m_udp_source_port_reg}}; +assign m_udp_dest_port = {M_COUNT{m_udp_dest_port_reg}}; +assign m_udp_length = {M_COUNT{m_udp_length_reg}}; +assign m_udp_checksum = {M_COUNT{m_udp_checksum_reg}}; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_udp_hdr_ready_next = 1'b0; + + s_udp_payload_axis_tready_next = 1'b0; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg & ~m_udp_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + m_udp_source_port_next = m_udp_source_port_reg; + m_udp_dest_port_next = m_udp_dest_port_reg; + m_udp_length_next = m_udp_length_reg; + m_udp_checksum_next = m_udp_checksum_reg; + + if (s_udp_payload_axis_tvalid && s_udp_payload_axis_tready) begin + // end of frame detection + if (s_udp_payload_axis_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_udp_hdr_valid && s_udp_hdr_ready) begin + // start of frame, grab select value + select_ctl = select; + drop_ctl = drop; + frame_ctl = 1'b1; + + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = frame_ctl; + + s_udp_hdr_ready_next = 1'b0; + + m_udp_hdr_valid_next = (!drop_ctl) << select_ctl; + m_eth_dest_mac_next = s_eth_dest_mac; + m_eth_src_mac_next = s_eth_src_mac; + m_eth_type_next = s_eth_type; + m_ip_version_next = s_ip_version; + m_ip_ihl_next = s_ip_ihl; + m_ip_dscp_next = s_ip_dscp; + m_ip_ecn_next = s_ip_ecn; + m_ip_length_next = s_ip_length; + m_ip_identification_next = s_ip_identification; + m_ip_flags_next = s_ip_flags; + m_ip_fragment_offset_next = s_ip_fragment_offset; + m_ip_ttl_next = s_ip_ttl; + m_ip_protocol_next = s_ip_protocol; + m_ip_header_checksum_next = s_ip_header_checksum; + m_ip_source_ip_next = s_ip_source_ip; + m_ip_dest_ip_next = s_ip_dest_ip; + m_udp_source_port_next = s_udp_source_port; + m_udp_dest_port_next = s_udp_dest_port; + m_udp_length_next = s_udp_length; + m_udp_checksum_next = s_udp_checksum; + end + + s_udp_hdr_ready_next = !frame_next && !m_udp_hdr_valid_next; + + s_udp_payload_axis_tready_next = (m_udp_payload_axis_tready_int_early || drop_ctl) && frame_ctl; + + m_udp_payload_axis_tdata_int = s_udp_payload_axis_tdata; + m_udp_payload_axis_tkeep_int = s_udp_payload_axis_tkeep; + m_udp_payload_axis_tvalid_int = (s_udp_payload_axis_tvalid && s_udp_payload_axis_tready && !drop_ctl) << select_ctl; + m_udp_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_udp_payload_axis_tid_int = s_udp_payload_axis_tid; + m_udp_payload_axis_tdest_int = s_udp_payload_axis_tdest; + m_udp_payload_axis_tuser_int = s_udp_payload_axis_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + m_udp_hdr_valid_reg <= 0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; + m_udp_source_port_reg <= m_udp_source_port_next; + m_udp_dest_port_reg <= m_udp_dest_port_next; + m_udp_length_reg <= m_udp_length_next; + m_udp_checksum_reg <= m_udp_checksum_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_udp_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_udp_payload_axis_tvalid_reg = {M_COUNT{1'b0}}, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = {M_COUNT{m_udp_payload_axis_tdata_reg}}; +assign m_udp_payload_axis_tkeep = KEEP_ENABLE ? {M_COUNT{m_udp_payload_axis_tkeep_reg}} : {M_COUNT*KEEP_WIDTH{1'b1}}; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = {M_COUNT{m_udp_payload_axis_tlast_reg}}; +assign m_udp_payload_axis_tid = ID_ENABLE ? {M_COUNT{m_udp_payload_axis_tid_reg}} : {M_COUNT*ID_WIDTH{1'b0}}; +assign m_udp_payload_axis_tdest = DEST_ENABLE ? {M_COUNT{m_udp_payload_axis_tdest_reg}} : {M_COUNT*DEST_WIDTH{1'b0}}; +assign m_udp_payload_axis_tuser = USER_ENABLE ? {M_COUNT{m_udp_payload_axis_tuser_reg}} : {M_COUNT*USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = (m_udp_payload_axis_tready & m_udp_payload_axis_tvalid) || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if ((m_udp_payload_axis_tready & m_udp_payload_axis_tvalid) || !m_udp_payload_axis_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready & m_udp_payload_axis_tvalid) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= {M_COUNT{1'b0}}; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tid_reg <= temp_m_udp_payload_axis_tid_reg; + m_udp_payload_axis_tdest_reg <= temp_m_udp_payload_axis_tdest_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + temp_m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_ip_rx.v b/corundum/lib/eth/rtl/udp_ip_rx.v new file mode 100644 index 0000000000000000000000000000000000000000..609ff09a267ebb80dd44cff2797364b893104f96 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_ip_rx.v @@ -0,0 +1,532 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP ethernet frame receiver (IP frame in, UDP frame out) + */ +module udp_ip_rx +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [7:0] s_ip_payload_axis_tdata, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [7:0] m_udp_payload_axis_tdata, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives an IP frame with header fields in parallel and payload on +an AXI stream interface, decodes and strips the UDP header fields, then +produces the header fields in parallel along with the UDP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_udp_source_port_0; +reg store_udp_source_port_1; +reg store_udp_dest_port_0; +reg store_udp_dest_port_1; +reg store_udp_length_0; +reg store_udp_length_1; +reg store_udp_checksum_0; +reg store_udp_checksum_1; +reg store_last_word; + +reg [2:0] hdr_ptr_reg = 3'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [7:0] m_udp_payload_axis_tdata_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + store_udp_source_port_0 = 1'b0; + store_udp_source_port_1 = 1'b0; + store_udp_dest_port_0 = 1'b0; + store_udp_dest_port_1 = 1'b0; + store_udp_length_0 = 1'b0; + store_udp_length_1 = 1'b0; + store_udp_checksum_0 = 1'b0; + store_udp_checksum_1 = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + + m_udp_payload_axis_tdata_int = 8'd0; + m_udp_payload_axis_tvalid_int = 1'b0; + m_udp_payload_axis_tlast_int = 1'b0; + m_udp_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + hdr_ptr_next = 3'd0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b1; + store_ip_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header state + s_ip_payload_axis_tready_next = 1'b1; + word_count_next = m_udp_length_reg - 16'd8; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer in - store it + hdr_ptr_next = hdr_ptr_reg + 3'd1; + state_next = STATE_READ_HEADER; + + case (hdr_ptr_reg) + 3'h0: store_udp_source_port_1 = 1'b1; + 3'h1: store_udp_source_port_0 = 1'b1; + 3'h2: store_udp_dest_port_1 = 1'b1; + 3'h3: store_udp_dest_port_0 = 1'b1; + 3'h4: store_udp_length_1 = 1'b1; + 3'h5: store_udp_length_0 = 1'b1; + 3'h6: store_udp_checksum_1 = 1'b1; + 3'h7: begin + store_udp_checksum_0 = 1'b1; + m_udp_hdr_valid_next = 1'b1; + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + end + endcase + + if (s_ip_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + m_udp_hdr_valid_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd1; + if (s_ip_payload_axis_tlast) begin + if (word_count_reg != 16'd1) begin + // end of frame, but length does not match + m_udp_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_udp_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = last_word_data_reg; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_ip_payload_axis_tready_next = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_udp_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + + busy_reg <= state_next != STATE_IDLE; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_ip_length; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_udp_payload_axis_tdata_int; + end + + if (store_udp_source_port_0) m_udp_source_port_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_source_port_1) m_udp_source_port_reg[15: 8] <= s_ip_payload_axis_tdata; + if (store_udp_dest_port_0) m_udp_dest_port_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_dest_port_1) m_udp_dest_port_reg[15: 8] <= s_ip_payload_axis_tdata; + if (store_udp_length_0) m_udp_length_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_length_1) m_udp_length_reg[15: 8] <= s_ip_payload_axis_tdata; + if (store_udp_checksum_0) m_udp_checksum_reg[ 7: 0] <= s_ip_payload_axis_tdata; + if (store_udp_checksum_1) m_udp_checksum_reg[15: 8] <= s_ip_payload_axis_tdata; +end + +// output datapath logic +reg [7:0] m_udp_payload_axis_tdata_reg = 8'd0; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg m_udp_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_udp_payload_axis_tdata_reg = 8'd0; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg temp_m_udp_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_udp_payload_int_to_output; +reg store_udp_payload_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tuser = m_udp_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_udp_payload_int_to_output = 1'b0; + store_udp_payload_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_udp_payload_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_udp_payload_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_ip_rx_64.v b/corundum/lib/eth/rtl/udp_ip_rx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..e3244ce2823d097c0bb9804af925739e940d70e9 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_ip_rx_64.v @@ -0,0 +1,560 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP ethernet frame receiver (IP frame in, UDP frame out, 64 bit datapath) + */ +module udp_ip_rx_64 +( + input wire clk, + input wire rst, + + /* + * IP frame input + */ + input wire s_ip_hdr_valid, + output wire s_ip_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_length, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [63:0] s_ip_payload_axis_tdata, + input wire [7:0] s_ip_payload_axis_tkeep, + input wire s_ip_payload_axis_tvalid, + output wire s_ip_payload_axis_tready, + input wire s_ip_payload_axis_tlast, + input wire s_ip_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [63:0] m_udp_payload_axis_tdata, + output wire [7:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire m_udp_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_header_early_termination, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives an IP frame with header fields in parallel and payload on +an AXI stream interface, decodes and strips the UDP header fields, then +produces the header fields in parallel along with the UDP payload in a +separate AXI stream. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_READ_HEADER = 3'd1, + STATE_READ_PAYLOAD = 3'd2, + STATE_READ_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_ip_hdr; +reg store_hdr_word_0; +reg store_last_word; + +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; +reg [15:0] m_udp_source_port_reg = 16'd0; +reg [15:0] m_udp_dest_port_reg = 16'd0; +reg [15:0] m_udp_length_reg = 16'd0; +reg [15:0] m_udp_checksum_reg = 16'd0; + +reg s_ip_hdr_ready_reg = 1'b0, s_ip_hdr_ready_next; +reg s_ip_payload_axis_tready_reg = 1'b0, s_ip_payload_axis_tready_next; + +reg busy_reg = 1'b0; +reg error_header_early_termination_reg = 1'b0, error_header_early_termination_next; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [63:0] m_udp_payload_axis_tdata_int; +reg [7:0] m_udp_payload_axis_tkeep_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_ip_hdr_ready = s_ip_hdr_ready_reg; +assign s_ip_payload_axis_tready = s_ip_payload_axis_tready_reg; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +assign busy = busy_reg; +assign error_header_early_termination = error_header_early_termination_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b0; + + store_ip_hdr = 1'b0; + store_hdr_word_0 = 1'b0; + + store_last_word = 1'b0; + + word_count_next = word_count_reg; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + + error_header_early_termination_next = 1'b0; + error_payload_early_termination_next = 1'b0; + + m_udp_payload_axis_tdata_int = 64'd0; + m_udp_payload_axis_tkeep_int = 8'd0; + m_udp_payload_axis_tvalid_int = 1'b0; + m_udp_payload_axis_tlast_int = 1'b0; + m_udp_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for header + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + + if (s_ip_hdr_ready && s_ip_hdr_valid) begin + s_ip_hdr_ready_next = 1'b0; + s_ip_payload_axis_tready_next = 1'b1; + store_ip_hdr = 1'b1; + state_next = STATE_READ_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_READ_HEADER: begin + // read header state + s_ip_payload_axis_tready_next = 1'b1; + + word_count_next = {s_ip_payload_axis_tdata[39:32], s_ip_payload_axis_tdata[47:40]} - 16'd8; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer in - store it + state_next = STATE_READ_HEADER; + + store_hdr_word_0 = 1'b1; + m_udp_hdr_valid_next = 1'b1; + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + state_next = STATE_READ_PAYLOAD; + + if (s_ip_payload_axis_tlast) begin + error_header_early_termination_next = 1'b1; + m_udp_hdr_valid_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end + + end else begin + state_next = STATE_READ_HEADER; + end + end + STATE_READ_PAYLOAD: begin + // read payload + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = s_ip_payload_axis_tdata; + m_udp_payload_axis_tkeep_int = s_ip_payload_axis_tkeep; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + store_last_word = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + if (word_count_reg <= 8) begin + // have entire payload + m_udp_payload_axis_tkeep_int = s_ip_payload_axis_tkeep & count2keep(word_count_reg); + if (s_ip_payload_axis_tlast) begin + if (keep2count(s_ip_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_udp_payload_axis_tuser_int = 1'b1; + end + s_ip_payload_axis_tready_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + m_udp_payload_axis_tvalid_int = 1'b0; + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + if (s_ip_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_udp_payload_axis_tuser_int = 1'b1; + s_ip_payload_axis_tready_next = 1'b0; + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + end else begin + state_next = STATE_READ_PAYLOAD; + end + end + STATE_READ_PAYLOAD_LAST: begin + // read and discard until end of frame + s_ip_payload_axis_tready_next = m_udp_payload_axis_tready_int_early; + + m_udp_payload_axis_tdata_int = last_word_data_reg; + m_udp_payload_axis_tkeep_int = last_word_keep_reg; + m_udp_payload_axis_tvalid_int = s_ip_payload_axis_tvalid && s_ip_payload_axis_tlast; + m_udp_payload_axis_tlast_int = s_ip_payload_axis_tlast; + m_udp_payload_axis_tuser_int = s_ip_payload_axis_tuser; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end else begin + state_next = STATE_READ_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_ip_payload_axis_tready_next = 1'b1; + + if (s_ip_payload_axis_tready && s_ip_payload_axis_tvalid) begin + if (s_ip_payload_axis_tlast) begin + s_ip_hdr_ready_next = !m_udp_hdr_valid_next; + s_ip_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_ip_hdr_ready_reg <= 1'b0; + s_ip_payload_axis_tready_reg <= 1'b0; + m_udp_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_header_early_termination_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_ip_hdr_ready_reg <= s_ip_hdr_ready_next; + s_ip_payload_axis_tready_reg <= s_ip_payload_axis_tready_next; + + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + + error_header_early_termination_reg <= error_header_early_termination_next; + error_payload_early_termination_reg <= error_payload_early_termination_next; + + busy_reg <= state_next != STATE_IDLE; + end + + word_count_reg <= word_count_next; + + // datapath + if (store_ip_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_ip_length; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + end + + if (store_last_word) begin + last_word_data_reg <= m_udp_payload_axis_tdata_int; + last_word_keep_reg <= m_udp_payload_axis_tkeep_int; + end + + if (store_hdr_word_0) begin + m_udp_source_port_reg[15: 8] <= s_ip_payload_axis_tdata[ 7: 0]; + m_udp_source_port_reg[ 7: 0] <= s_ip_payload_axis_tdata[15: 8]; + m_udp_dest_port_reg[15: 8] <= s_ip_payload_axis_tdata[23:16]; + m_udp_dest_port_reg[ 7: 0] <= s_ip_payload_axis_tdata[31:24]; + m_udp_length_reg[15: 8] <= s_ip_payload_axis_tdata[39:32]; + m_udp_length_reg[ 7: 0] <= s_ip_payload_axis_tdata[47:40]; + m_udp_checksum_reg[15: 8] <= s_ip_payload_axis_tdata[55:48]; + m_udp_checksum_reg[ 7: 0] <= s_ip_payload_axis_tdata[63:56]; + end +end + +// output datapath logic +reg [63:0] m_udp_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_udp_payload_axis_tkeep_reg = 8'd0; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg m_udp_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_udp_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_udp_payload_axis_tkeep_reg = 8'd0; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg temp_m_udp_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_udp_payload_int_to_output; +reg store_udp_payload_int_to_temp; +reg store_udp_payload_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tkeep = m_udp_payload_axis_tkeep_reg; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tuser = m_udp_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_udp_payload_int_to_output = 1'b0; + store_udp_payload_int_to_temp = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_udp_payload_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_udp_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_udp_payload_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_udp_payload_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_udp_payload_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_ip_tx.v b/corundum/lib/eth/rtl/udp_ip_tx.v new file mode 100644 index 0000000000000000000000000000000000000000..23ae0ee47af93c084eeb20283bbbdbefbf6746a4 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_ip_tx.v @@ -0,0 +1,493 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP ethernet frame transmitter (UDP frame in, IP frame out) + */ +module udp_ip_tx +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [7:0] s_udp_payload_axis_tdata, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [7:0] m_ip_payload_axis_tdata, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the IP headers, and transmits the complete IP payload on an AXI interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_PAYLOAD = 3'd2, + STATE_WRITE_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg store_last_word; + +reg [2:0] hdr_ptr_reg = 3'd0, hdr_ptr_next; +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [7:0] last_word_data_reg = 8'd0; + +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; +reg [15:0] udp_length_reg = 16'd0; +reg [15:0] udp_checksum_reg = 16'd0; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [7:0] m_ip_payload_axis_tdata_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + + store_last_word = 1'b0; + + hdr_ptr_next = hdr_ptr_reg; + word_count_next = word_count_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_ip_payload_axis_tdata_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + hdr_ptr_next = 3'd0; + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + s_udp_hdr_ready_next = 1'b0; + m_ip_hdr_valid_next = 1'b1; + if (m_ip_payload_axis_tready_int_reg) begin + m_ip_payload_axis_tvalid_int = 1'b1; + m_ip_payload_axis_tdata_int = s_udp_source_port[15: 8]; + hdr_ptr_next = 3'd1; + end + state_next = STATE_WRITE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header state + word_count_next = udp_length_reg - 16'd8; + + if (m_ip_payload_axis_tready_int_reg) begin + // word transfer out + hdr_ptr_next = hdr_ptr_reg + 3'd1; + m_ip_payload_axis_tvalid_int = 1'b1; + state_next = STATE_WRITE_HEADER; + case (hdr_ptr_reg) + 3'h0: m_ip_payload_axis_tdata_int = udp_source_port_reg[15: 8]; + 3'h1: m_ip_payload_axis_tdata_int = udp_source_port_reg[ 7: 0]; + 3'h2: m_ip_payload_axis_tdata_int = udp_dest_port_reg[15: 8]; + 3'h3: m_ip_payload_axis_tdata_int = udp_dest_port_reg[ 7: 0]; + 3'h4: m_ip_payload_axis_tdata_int = udp_length_reg[15: 8]; + 3'h5: m_ip_payload_axis_tdata_int = udp_length_reg[ 7: 0]; + 3'h6: m_ip_payload_axis_tdata_int = udp_checksum_reg[15: 8]; + 3'h7: begin + m_ip_payload_axis_tdata_int = udp_checksum_reg[ 7: 0]; + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end + endcase + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = s_udp_payload_axis_tdata; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd1; + if (s_udp_payload_axis_tlast) begin + if (word_count_reg != 16'd1) begin + // end of frame, but length does not match + m_ip_payload_axis_tuser_int = 1'b1; + error_payload_early_termination_next = 1'b1; + end + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + if (word_count_reg == 16'd1) begin + store_last_word = 1'b1; + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid && s_udp_payload_axis_tlast; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_udp_payload_axis_tready_next = 1'b1; + + if (s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + end + + hdr_ptr_reg <= hdr_ptr_next; + word_count_reg <= word_count_next; + + // datapath + if (store_udp_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_udp_length + 20; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + udp_length_reg <= s_udp_length; + udp_checksum_reg <= s_udp_checksum; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + end +end + +// output datapath logic +reg [7:0] m_ip_payload_axis_tdata_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [7:0] temp_m_ip_payload_axis_tdata_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_ip_tx_64.v b/corundum/lib/eth/rtl/udp_ip_tx_64.v new file mode 100644 index 0000000000000000000000000000000000000000..fe899b90044858dbccfdf2aa85708558f54964fa --- /dev/null +++ b/corundum/lib/eth/rtl/udp_ip_tx_64.v @@ -0,0 +1,549 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP ethernet frame transmitter (UDP frame in, IP frame out, 64-bit datapath) + */ +module udp_ip_tx_64 +( + input wire clk, + input wire rst, + + /* + * UDP frame input + */ + input wire s_udp_hdr_valid, + output wire s_udp_hdr_ready, + input wire [47:0] s_eth_dest_mac, + input wire [47:0] s_eth_src_mac, + input wire [15:0] s_eth_type, + input wire [3:0] s_ip_version, + input wire [3:0] s_ip_ihl, + input wire [5:0] s_ip_dscp, + input wire [1:0] s_ip_ecn, + input wire [15:0] s_ip_identification, + input wire [2:0] s_ip_flags, + input wire [12:0] s_ip_fragment_offset, + input wire [7:0] s_ip_ttl, + input wire [7:0] s_ip_protocol, + input wire [15:0] s_ip_header_checksum, + input wire [31:0] s_ip_source_ip, + input wire [31:0] s_ip_dest_ip, + input wire [15:0] s_udp_source_port, + input wire [15:0] s_udp_dest_port, + input wire [15:0] s_udp_length, + input wire [15:0] s_udp_checksum, + input wire [63:0] s_udp_payload_axis_tdata, + input wire [7:0] s_udp_payload_axis_tkeep, + input wire s_udp_payload_axis_tvalid, + output wire s_udp_payload_axis_tready, + input wire s_udp_payload_axis_tlast, + input wire s_udp_payload_axis_tuser, + + /* + * IP frame output + */ + output wire m_ip_hdr_valid, + input wire m_ip_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [63:0] m_ip_payload_axis_tdata, + output wire [7:0] m_ip_payload_axis_tkeep, + output wire m_ip_payload_axis_tvalid, + input wire m_ip_payload_axis_tready, + output wire m_ip_payload_axis_tlast, + output wire m_ip_payload_axis_tuser, + + /* + * Status signals + */ + output wire busy, + output wire error_payload_early_termination +); + +/* + +UDP Frame + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + length 2 octets + checksum 2 octets + + payload length octets + +This module receives a UDP frame with header fields in parallel along with the +payload in an AXI stream, combines the header with the payload, passes through +the IP headers, and transmits the complete IP payload on an AXI interface. + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_WRITE_HEADER = 3'd1, + STATE_WRITE_PAYLOAD = 3'd2, + STATE_WRITE_PAYLOAD_LAST = 3'd3, + STATE_WAIT_LAST = 3'd4; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg store_udp_hdr; +reg store_last_word; + +reg [15:0] word_count_reg = 16'd0, word_count_next; + +reg [63:0] last_word_data_reg = 64'd0; +reg [7:0] last_word_keep_reg = 8'd0; + +reg [15:0] udp_source_port_reg = 16'd0; +reg [15:0] udp_dest_port_reg = 16'd0; +reg [15:0] udp_length_reg = 16'd0; +reg [15:0] udp_checksum_reg = 16'd0; + +reg s_udp_hdr_ready_reg = 1'b0, s_udp_hdr_ready_next; +reg s_udp_payload_axis_tready_reg = 1'b0, s_udp_payload_axis_tready_next; + +reg m_ip_hdr_valid_reg = 1'b0, m_ip_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0; +reg [47:0] m_eth_src_mac_reg = 48'd0; +reg [15:0] m_eth_type_reg = 16'd0; +reg [3:0] m_ip_version_reg = 4'd0; +reg [3:0] m_ip_ihl_reg = 4'd0; +reg [5:0] m_ip_dscp_reg = 6'd0; +reg [1:0] m_ip_ecn_reg = 2'd0; +reg [15:0] m_ip_length_reg = 16'd0; +reg [15:0] m_ip_identification_reg = 16'd0; +reg [2:0] m_ip_flags_reg = 3'd0; +reg [12:0] m_ip_fragment_offset_reg = 13'd0; +reg [7:0] m_ip_ttl_reg = 8'd0; +reg [7:0] m_ip_protocol_reg = 8'd0; +reg [15:0] m_ip_header_checksum_reg = 16'd0; +reg [31:0] m_ip_source_ip_reg = 32'd0; +reg [31:0] m_ip_dest_ip_reg = 32'd0; + +reg busy_reg = 1'b0; +reg error_payload_early_termination_reg = 1'b0, error_payload_early_termination_next; + +// internal datapath +reg [63:0] m_ip_payload_axis_tdata_int; +reg [7:0] m_ip_payload_axis_tkeep_int; +reg m_ip_payload_axis_tvalid_int; +reg m_ip_payload_axis_tready_int_reg = 1'b0; +reg m_ip_payload_axis_tlast_int; +reg m_ip_payload_axis_tuser_int; +wire m_ip_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg; + +assign m_ip_hdr_valid = m_ip_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; + +assign busy = busy_reg; +assign error_payload_early_termination = error_payload_early_termination_reg; + +function [3:0] keep2count; + input [7:0] k; + casez (k) + 8'bzzzzzzz0: keep2count = 4'd0; + 8'bzzzzzz01: keep2count = 4'd1; + 8'bzzzzz011: keep2count = 4'd2; + 8'bzzzz0111: keep2count = 4'd3; + 8'bzzz01111: keep2count = 4'd4; + 8'bzz011111: keep2count = 4'd5; + 8'bz0111111: keep2count = 4'd6; + 8'b01111111: keep2count = 4'd7; + 8'b11111111: keep2count = 4'd8; + endcase +endfunction + +function [7:0] count2keep; + input [3:0] k; + case (k) + 4'd0: count2keep = 8'b00000000; + 4'd1: count2keep = 8'b00000001; + 4'd2: count2keep = 8'b00000011; + 4'd3: count2keep = 8'b00000111; + 4'd4: count2keep = 8'b00001111; + 4'd5: count2keep = 8'b00011111; + 4'd6: count2keep = 8'b00111111; + 4'd7: count2keep = 8'b01111111; + 4'd8: count2keep = 8'b11111111; + endcase +endfunction + +always @* begin + state_next = STATE_IDLE; + + s_udp_hdr_ready_next = 1'b0; + s_udp_payload_axis_tready_next = 1'b0; + + store_udp_hdr = 1'b0; + + store_last_word = 1'b0; + + word_count_next = word_count_reg; + + m_ip_hdr_valid_next = m_ip_hdr_valid_reg && !m_ip_hdr_ready; + + error_payload_early_termination_next = 1'b0; + + m_ip_payload_axis_tdata_int = 64'd0; + m_ip_payload_axis_tkeep_int = 8'd0; + m_ip_payload_axis_tvalid_int = 1'b0; + m_ip_payload_axis_tlast_int = 1'b0; + m_ip_payload_axis_tuser_int = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state - wait for data + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + word_count_next = s_udp_length - 16'd8; + + if (s_udp_hdr_ready && s_udp_hdr_valid) begin + store_udp_hdr = 1'b1; + s_udp_hdr_ready_next = 1'b0; + m_ip_hdr_valid_next = 1'b1; + state_next = STATE_WRITE_HEADER; + if (m_ip_payload_axis_tready_int_reg) begin + m_ip_payload_axis_tvalid_int = 1'b1; + m_ip_payload_axis_tdata_int[ 7: 0] = s_udp_source_port[15: 8]; + m_ip_payload_axis_tdata_int[15: 8] = s_udp_source_port[ 7: 0]; + m_ip_payload_axis_tdata_int[23:16] = s_udp_dest_port[15: 8]; + m_ip_payload_axis_tdata_int[31:24] = s_udp_dest_port[ 7: 0]; + m_ip_payload_axis_tdata_int[39:32] = s_udp_length[15: 8]; + m_ip_payload_axis_tdata_int[47:40] = s_udp_length[ 7: 0]; + m_ip_payload_axis_tdata_int[55:48] = s_udp_checksum[15: 8]; + m_ip_payload_axis_tdata_int[63:56] = s_udp_checksum[ 7: 0]; + m_ip_payload_axis_tkeep_int = 8'hff; + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE_HEADER: begin + // write header state + if (m_ip_payload_axis_tready_int_reg) begin + // word transfer out + m_ip_payload_axis_tvalid_int = 1'b1; + m_ip_payload_axis_tdata_int[ 7: 0] = udp_source_port_reg[15: 8]; + m_ip_payload_axis_tdata_int[15: 8] = udp_source_port_reg[ 7: 0]; + m_ip_payload_axis_tdata_int[23:16] = udp_dest_port_reg[15: 8]; + m_ip_payload_axis_tdata_int[31:24] = udp_dest_port_reg[ 7: 0]; + m_ip_payload_axis_tdata_int[39:32] = udp_length_reg[15: 8]; + m_ip_payload_axis_tdata_int[47:40] = udp_length_reg[ 7: 0]; + m_ip_payload_axis_tdata_int[55:48] = udp_checksum_reg[15: 8]; + m_ip_payload_axis_tdata_int[63:56] = udp_checksum_reg[ 7: 0]; + m_ip_payload_axis_tkeep_int = 8'hff; + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + state_next = STATE_WRITE_PAYLOAD; + end else begin + state_next = STATE_WRITE_HEADER; + end + end + STATE_WRITE_PAYLOAD: begin + // write payload + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = s_udp_payload_axis_tdata; + m_ip_payload_axis_tkeep_int = s_udp_payload_axis_tkeep; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + store_last_word = 1'b1; + + if (m_ip_payload_axis_tready_int_reg && s_udp_payload_axis_tvalid) begin + // word transfer through + word_count_next = word_count_reg - 16'd8; + if (word_count_reg <= 8) begin + // have entire payload + m_ip_payload_axis_tkeep_int = count2keep(word_count_reg); + if (s_udp_payload_axis_tlast) begin + if (keep2count(s_udp_payload_axis_tkeep) < word_count_reg[4:0]) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + end + s_udp_payload_axis_tready_next = 1'b0; + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + m_ip_payload_axis_tvalid_int = 1'b0; + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + if (s_udp_payload_axis_tlast) begin + // end of frame, but length does not match + error_payload_early_termination_next = 1'b1; + m_ip_payload_axis_tuser_int = 1'b1; + s_udp_payload_axis_tready_next = 1'b0; + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + end else begin + state_next = STATE_WRITE_PAYLOAD; + end + end + STATE_WRITE_PAYLOAD_LAST: begin + // read and discard until end of frame + s_udp_payload_axis_tready_next = m_ip_payload_axis_tready_int_early; + + m_ip_payload_axis_tdata_int = last_word_data_reg; + m_ip_payload_axis_tkeep_int = last_word_keep_reg; + m_ip_payload_axis_tvalid_int = s_udp_payload_axis_tvalid && s_udp_payload_axis_tlast; + m_ip_payload_axis_tlast_int = s_udp_payload_axis_tlast; + m_ip_payload_axis_tuser_int = s_udp_payload_axis_tuser; + + if (s_udp_payload_axis_tready && s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end else begin + state_next = STATE_WRITE_PAYLOAD_LAST; + end + end + STATE_WAIT_LAST: begin + // wait for end of frame; read and discard + s_udp_payload_axis_tready_next = 1'b1; + + if (s_udp_payload_axis_tvalid) begin + if (s_udp_payload_axis_tlast) begin + s_udp_hdr_ready_next = !m_ip_hdr_valid_next; + s_udp_payload_axis_tready_next = 1'b0; + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_LAST; + end + end else begin + state_next = STATE_WAIT_LAST; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_udp_hdr_ready_reg <= 1'b0; + s_udp_payload_axis_tready_reg <= 1'b0; + m_ip_hdr_valid_reg <= 1'b0; + busy_reg <= 1'b0; + error_payload_early_termination_reg <= 1'b0; + end else begin + state_reg <= state_next; + + + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + + m_ip_hdr_valid_reg <= m_ip_hdr_valid_next; + + busy_reg <= state_next != STATE_IDLE; + + error_payload_early_termination_reg <= error_payload_early_termination_next; + end + + word_count_reg <= word_count_next; + + // datapath + if (store_udp_hdr) begin + m_eth_dest_mac_reg <= s_eth_dest_mac; + m_eth_src_mac_reg <= s_eth_src_mac; + m_eth_type_reg <= s_eth_type; + m_ip_version_reg <= s_ip_version; + m_ip_ihl_reg <= s_ip_ihl; + m_ip_dscp_reg <= s_ip_dscp; + m_ip_ecn_reg <= s_ip_ecn; + m_ip_length_reg <= s_udp_length + 20; + m_ip_identification_reg <= s_ip_identification; + m_ip_flags_reg <= s_ip_flags; + m_ip_fragment_offset_reg <= s_ip_fragment_offset; + m_ip_ttl_reg <= s_ip_ttl; + m_ip_protocol_reg <= s_ip_protocol; + m_ip_header_checksum_reg <= s_ip_header_checksum; + m_ip_source_ip_reg <= s_ip_source_ip; + m_ip_dest_ip_reg <= s_ip_dest_ip; + udp_source_port_reg <= s_udp_source_port; + udp_dest_port_reg <= s_udp_dest_port; + udp_length_reg <= s_udp_length; + udp_checksum_reg <= s_udp_checksum; + end + + if (store_last_word) begin + last_word_data_reg <= m_ip_payload_axis_tdata_int; + last_word_keep_reg <= m_ip_payload_axis_tkeep_int; + end +end + +// output datapath logic +reg [63:0] m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] m_ip_payload_axis_tkeep_reg = 8'd0; +reg m_ip_payload_axis_tvalid_reg = 1'b0, m_ip_payload_axis_tvalid_next; +reg m_ip_payload_axis_tlast_reg = 1'b0; +reg m_ip_payload_axis_tuser_reg = 1'b0; + +reg [63:0] temp_m_ip_payload_axis_tdata_reg = 64'd0; +reg [7:0] temp_m_ip_payload_axis_tkeep_reg = 8'd0; +reg temp_m_ip_payload_axis_tvalid_reg = 1'b0, temp_m_ip_payload_axis_tvalid_next; +reg temp_m_ip_payload_axis_tlast_reg = 1'b0; +reg temp_m_ip_payload_axis_tuser_reg = 1'b0; + +// datapath control +reg store_ip_payload_int_to_output; +reg store_ip_payload_int_to_temp; +reg store_ip_payload_axis_temp_to_output; + +assign m_ip_payload_axis_tdata = m_ip_payload_axis_tdata_reg; +assign m_ip_payload_axis_tkeep = m_ip_payload_axis_tkeep_reg; +assign m_ip_payload_axis_tvalid = m_ip_payload_axis_tvalid_reg; +assign m_ip_payload_axis_tlast = m_ip_payload_axis_tlast_reg; +assign m_ip_payload_axis_tuser = m_ip_payload_axis_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_ip_payload_axis_tready_int_early = m_ip_payload_axis_tready || (!temp_m_ip_payload_axis_tvalid_reg && (!m_ip_payload_axis_tvalid_reg || !m_ip_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + + store_ip_payload_int_to_output = 1'b0; + store_ip_payload_int_to_temp = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b0; + + if (m_ip_payload_axis_tready_int_reg) begin + // input is ready + if (m_ip_payload_axis_tready || !m_ip_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_ip_payload_axis_tvalid_next = m_ip_payload_axis_tvalid_int; + store_ip_payload_int_to_temp = 1'b1; + end + end else if (m_ip_payload_axis_tready) begin + // input is not ready, but output is ready + m_ip_payload_axis_tvalid_next = temp_m_ip_payload_axis_tvalid_reg; + temp_m_ip_payload_axis_tvalid_next = 1'b0; + store_ip_payload_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_ip_payload_axis_tvalid_reg <= 1'b0; + m_ip_payload_axis_tready_int_reg <= 1'b0; + temp_m_ip_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_ip_payload_axis_tvalid_reg <= m_ip_payload_axis_tvalid_next; + m_ip_payload_axis_tready_int_reg <= m_ip_payload_axis_tready_int_early; + temp_m_ip_payload_axis_tvalid_reg <= temp_m_ip_payload_axis_tvalid_next; + end + + // datapath + if (store_ip_payload_int_to_output) begin + m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end else if (store_ip_payload_axis_temp_to_output) begin + m_ip_payload_axis_tdata_reg <= temp_m_ip_payload_axis_tdata_reg; + m_ip_payload_axis_tkeep_reg <= temp_m_ip_payload_axis_tkeep_reg; + m_ip_payload_axis_tlast_reg <= temp_m_ip_payload_axis_tlast_reg; + m_ip_payload_axis_tuser_reg <= temp_m_ip_payload_axis_tuser_reg; + end + + if (store_ip_payload_int_to_temp) begin + temp_m_ip_payload_axis_tdata_reg <= m_ip_payload_axis_tdata_int; + temp_m_ip_payload_axis_tkeep_reg <= m_ip_payload_axis_tkeep_int; + temp_m_ip_payload_axis_tlast_reg <= m_ip_payload_axis_tlast_int; + temp_m_ip_payload_axis_tuser_reg <= m_ip_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/udp_mux.v b/corundum/lib/eth/rtl/udp_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..7d33cedbbf78192769f001e92e5556496a426bb8 --- /dev/null +++ b/corundum/lib/eth/rtl/udp_mux.v @@ -0,0 +1,418 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * UDP multiplexer + */ +module udp_mux # +( + parameter S_COUNT = 4, + parameter DATA_WIDTH = 8, + parameter KEEP_ENABLE = (DATA_WIDTH>8), + parameter KEEP_WIDTH = (DATA_WIDTH/8), + parameter ID_ENABLE = 0, + parameter ID_WIDTH = 8, + parameter DEST_ENABLE = 0, + parameter DEST_WIDTH = 8, + parameter USER_ENABLE = 1, + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * UDP frame inputs + */ + input wire [S_COUNT-1:0] s_udp_hdr_valid, + output wire [S_COUNT-1:0] s_udp_hdr_ready, + input wire [S_COUNT*48-1:0] s_eth_dest_mac, + input wire [S_COUNT*48-1:0] s_eth_src_mac, + input wire [S_COUNT*16-1:0] s_eth_type, + input wire [S_COUNT*4-1:0] s_ip_version, + input wire [S_COUNT*4-1:0] s_ip_ihl, + input wire [S_COUNT*6-1:0] s_ip_dscp, + input wire [S_COUNT*2-1:0] s_ip_ecn, + input wire [S_COUNT*16-1:0] s_ip_length, + input wire [S_COUNT*16-1:0] s_ip_identification, + input wire [S_COUNT*3-1:0] s_ip_flags, + input wire [S_COUNT*13-1:0] s_ip_fragment_offset, + input wire [S_COUNT*8-1:0] s_ip_ttl, + input wire [S_COUNT*8-1:0] s_ip_protocol, + input wire [S_COUNT*16-1:0] s_ip_header_checksum, + input wire [S_COUNT*32-1:0] s_ip_source_ip, + input wire [S_COUNT*32-1:0] s_ip_dest_ip, + input wire [S_COUNT*16-1:0] s_udp_source_port, + input wire [S_COUNT*16-1:0] s_udp_dest_port, + input wire [S_COUNT*16-1:0] s_udp_length, + input wire [S_COUNT*16-1:0] s_udp_checksum, + input wire [S_COUNT*DATA_WIDTH-1:0] s_udp_payload_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_udp_payload_axis_tkeep, + input wire [S_COUNT-1:0] s_udp_payload_axis_tvalid, + output wire [S_COUNT-1:0] s_udp_payload_axis_tready, + input wire [S_COUNT-1:0] s_udp_payload_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_udp_payload_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_udp_payload_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_udp_payload_axis_tuser, + + /* + * UDP frame output + */ + output wire m_udp_hdr_valid, + input wire m_udp_hdr_ready, + output wire [47:0] m_eth_dest_mac, + output wire [47:0] m_eth_src_mac, + output wire [15:0] m_eth_type, + output wire [3:0] m_ip_version, + output wire [3:0] m_ip_ihl, + output wire [5:0] m_ip_dscp, + output wire [1:0] m_ip_ecn, + output wire [15:0] m_ip_length, + output wire [15:0] m_ip_identification, + output wire [2:0] m_ip_flags, + output wire [12:0] m_ip_fragment_offset, + output wire [7:0] m_ip_ttl, + output wire [7:0] m_ip_protocol, + output wire [15:0] m_ip_header_checksum, + output wire [31:0] m_ip_source_ip, + output wire [31:0] m_ip_dest_ip, + output wire [15:0] m_udp_source_port, + output wire [15:0] m_udp_dest_port, + output wire [15:0] m_udp_length, + output wire [15:0] m_udp_checksum, + output wire [DATA_WIDTH-1:0] m_udp_payload_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep, + output wire m_udp_payload_axis_tvalid, + input wire m_udp_payload_axis_tready, + output wire m_udp_payload_axis_tlast, + output wire [ID_WIDTH-1:0] m_udp_payload_axis_tid, + output wire [DEST_WIDTH-1:0] m_udp_payload_axis_tdest, + output wire [USER_WIDTH-1:0] m_udp_payload_axis_tuser, + + /* + * Control + */ + input wire enable, + input wire [$clog2(S_COUNT)-1:0] select +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +reg [CL_S_COUNT-1:0] select_reg = 2'd0, select_next; +reg frame_reg = 1'b0, frame_next; + +reg [S_COUNT-1:0] s_udp_hdr_ready_reg = 0, s_udp_hdr_ready_next; + +reg [S_COUNT-1:0] s_udp_payload_axis_tready_reg = 0, s_udp_payload_axis_tready_next; + +reg m_udp_hdr_valid_reg = 1'b0, m_udp_hdr_valid_next; +reg [47:0] m_eth_dest_mac_reg = 48'd0, m_eth_dest_mac_next; +reg [47:0] m_eth_src_mac_reg = 48'd0, m_eth_src_mac_next; +reg [15:0] m_eth_type_reg = 16'd0, m_eth_type_next; +reg [3:0] m_ip_version_reg = 4'd0, m_ip_version_next; +reg [3:0] m_ip_ihl_reg = 4'd0, m_ip_ihl_next; +reg [5:0] m_ip_dscp_reg = 6'd0, m_ip_dscp_next; +reg [1:0] m_ip_ecn_reg = 2'd0, m_ip_ecn_next; +reg [15:0] m_ip_length_reg = 16'd0, m_ip_length_next; +reg [15:0] m_ip_identification_reg = 16'd0, m_ip_identification_next; +reg [2:0] m_ip_flags_reg = 3'd0, m_ip_flags_next; +reg [12:0] m_ip_fragment_offset_reg = 13'd0, m_ip_fragment_offset_next; +reg [7:0] m_ip_ttl_reg = 8'd0, m_ip_ttl_next; +reg [7:0] m_ip_protocol_reg = 8'd0, m_ip_protocol_next; +reg [15:0] m_ip_header_checksum_reg = 16'd0, m_ip_header_checksum_next; +reg [31:0] m_ip_source_ip_reg = 32'd0, m_ip_source_ip_next; +reg [31:0] m_ip_dest_ip_reg = 32'd0, m_ip_dest_ip_next; +reg [15:0] m_udp_source_port_reg = 16'd0, m_udp_source_port_next; +reg [15:0] m_udp_dest_port_reg = 16'd0, m_udp_dest_port_next; +reg [15:0] m_udp_length_reg = 16'd0, m_udp_length_next; +reg [15:0] m_udp_checksum_reg = 16'd0, m_udp_checksum_next; + +// internal datapath +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_int; +reg m_udp_payload_axis_tvalid_int; +reg m_udp_payload_axis_tready_int_reg = 1'b0; +reg m_udp_payload_axis_tlast_int; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_int; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_int; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_int; +wire m_udp_payload_axis_tready_int_early; + +assign s_udp_hdr_ready = s_udp_hdr_ready_reg; + +assign s_udp_payload_axis_tready = s_udp_payload_axis_tready_reg; + +assign m_udp_hdr_valid = m_udp_hdr_valid_reg; +assign m_eth_dest_mac = m_eth_dest_mac_reg; +assign m_eth_src_mac = m_eth_src_mac_reg; +assign m_eth_type = m_eth_type_reg; +assign m_ip_version = m_ip_version_reg; +assign m_ip_ihl = m_ip_ihl_reg; +assign m_ip_dscp = m_ip_dscp_reg; +assign m_ip_ecn = m_ip_ecn_reg; +assign m_ip_length = m_ip_length_reg; +assign m_ip_identification = m_ip_identification_reg; +assign m_ip_flags = m_ip_flags_reg; +assign m_ip_fragment_offset = m_ip_fragment_offset_reg; +assign m_ip_ttl = m_ip_ttl_reg; +assign m_ip_protocol = m_ip_protocol_reg; +assign m_ip_header_checksum = m_ip_header_checksum_reg; +assign m_ip_source_ip = m_ip_source_ip_reg; +assign m_ip_dest_ip = m_ip_dest_ip_reg; +assign m_udp_source_port = m_udp_source_port_reg; +assign m_udp_dest_port = m_udp_dest_port_reg; +assign m_udp_length = m_udp_length_reg; +assign m_udp_checksum = m_udp_checksum_reg; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_udp_payload_axis_tdata[select_reg*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_udp_payload_axis_tkeep[select_reg*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_udp_payload_axis_tvalid[select_reg]; +wire current_s_tready = s_udp_payload_axis_tready[select_reg]; +wire current_s_tlast = s_udp_payload_axis_tlast[select_reg]; +wire [ID_WIDTH-1:0] current_s_tid = s_udp_payload_axis_tid[select_reg*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_udp_payload_axis_tdest[select_reg*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_udp_payload_axis_tuser[select_reg*USER_WIDTH +: USER_WIDTH]; + +always @* begin + select_next = select_reg; + frame_next = frame_reg; + + s_udp_hdr_ready_next = 0; + + s_udp_payload_axis_tready_next = 0; + + m_udp_hdr_valid_next = m_udp_hdr_valid_reg && !m_udp_hdr_ready; + m_eth_dest_mac_next = m_eth_dest_mac_reg; + m_eth_src_mac_next = m_eth_src_mac_reg; + m_eth_type_next = m_eth_type_reg; + m_ip_version_next = m_ip_version_reg; + m_ip_ihl_next = m_ip_ihl_reg; + m_ip_dscp_next = m_ip_dscp_reg; + m_ip_ecn_next = m_ip_ecn_reg; + m_ip_length_next = m_ip_length_reg; + m_ip_identification_next = m_ip_identification_reg; + m_ip_flags_next = m_ip_flags_reg; + m_ip_fragment_offset_next = m_ip_fragment_offset_reg; + m_ip_ttl_next = m_ip_ttl_reg; + m_ip_protocol_next = m_ip_protocol_reg; + m_ip_header_checksum_next = m_ip_header_checksum_reg; + m_ip_source_ip_next = m_ip_source_ip_reg; + m_ip_dest_ip_next = m_ip_dest_ip_reg; + m_udp_source_port_next = m_udp_source_port_reg; + m_udp_dest_port_next = m_udp_dest_port_reg; + m_udp_length_next = m_udp_length_reg; + m_udp_checksum_next = m_udp_checksum_reg; + + if (current_s_tvalid & current_s_tready) begin + // end of frame detection + if (current_s_tlast) begin + frame_next = 1'b0; + end + end + + if (!frame_reg && enable && !m_udp_hdr_valid && (s_udp_hdr_valid & (1 << select))) begin + // start of frame, grab select value + frame_next = 1'b1; + select_next = select; + + s_udp_hdr_ready_next = (1 << select); + + m_udp_hdr_valid_next = 1'b1; + m_eth_dest_mac_next = s_eth_dest_mac[select*48 +: 48]; + m_eth_src_mac_next = s_eth_src_mac[select*48 +: 48]; + m_eth_type_next = s_eth_type[select*16 +: 16]; + m_ip_version_next = s_ip_version[select*4 +: 4]; + m_ip_ihl_next = s_ip_ihl[select*4 +: 4]; + m_ip_dscp_next = s_ip_dscp[select*6 +: 6]; + m_ip_ecn_next = s_ip_ecn[select*2 +: 2]; + m_ip_length_next = s_ip_length[select*16 +: 16]; + m_ip_identification_next = s_ip_identification[select*16 +: 16]; + m_ip_flags_next = s_ip_flags[select*3 +: 3]; + m_ip_fragment_offset_next = s_ip_fragment_offset[select*13 +: 13]; + m_ip_ttl_next = s_ip_ttl[select*8 +: 8]; + m_ip_protocol_next = s_ip_protocol[select*8 +: 8]; + m_ip_header_checksum_next = s_ip_header_checksum[select*16 +: 16]; + m_ip_source_ip_next = s_ip_source_ip[select*32 +: 32]; + m_ip_dest_ip_next = s_ip_dest_ip[select*32 +: 32]; + m_udp_source_port_next = s_udp_source_port[select*16 +: 16]; + m_udp_dest_port_next = s_udp_dest_port[select*16 +: 16]; + m_udp_length_next = s_udp_length[select*16 +: 16]; + m_udp_checksum_next = s_udp_checksum[select*16 +: 16]; + end + + // generate ready signal on selected port + s_udp_payload_axis_tready_next = (m_udp_payload_axis_tready_int_early && frame_next) << select_next; + + // pass through selected packet data + m_udp_payload_axis_tdata_int = current_s_tdata; + m_udp_payload_axis_tkeep_int = current_s_tkeep; + m_udp_payload_axis_tvalid_int = current_s_tvalid && current_s_tready && frame_reg; + m_udp_payload_axis_tlast_int = current_s_tlast; + m_udp_payload_axis_tid_int = current_s_tid; + m_udp_payload_axis_tdest_int = current_s_tdest; + m_udp_payload_axis_tuser_int = current_s_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 0; + frame_reg <= 1'b0; + s_udp_hdr_ready_reg <= 0; + s_udp_payload_axis_tready_reg <= 0; + m_udp_hdr_valid_reg <= 1'b0; + end else begin + select_reg <= select_next; + frame_reg <= frame_next; + s_udp_hdr_ready_reg <= s_udp_hdr_ready_next; + s_udp_payload_axis_tready_reg <= s_udp_payload_axis_tready_next; + m_udp_hdr_valid_reg <= m_udp_hdr_valid_next; + end + + m_eth_dest_mac_reg <= m_eth_dest_mac_next; + m_eth_src_mac_reg <= m_eth_src_mac_next; + m_eth_type_reg <= m_eth_type_next; + m_ip_version_reg <= m_ip_version_next; + m_ip_ihl_reg <= m_ip_ihl_next; + m_ip_dscp_reg <= m_ip_dscp_next; + m_ip_ecn_reg <= m_ip_ecn_next; + m_ip_length_reg <= m_ip_length_next; + m_ip_identification_reg <= m_ip_identification_next; + m_ip_flags_reg <= m_ip_flags_next; + m_ip_fragment_offset_reg <= m_ip_fragment_offset_next; + m_ip_ttl_reg <= m_ip_ttl_next; + m_ip_protocol_reg <= m_ip_protocol_next; + m_ip_header_checksum_reg <= m_ip_header_checksum_next; + m_ip_source_ip_reg <= m_ip_source_ip_next; + m_ip_dest_ip_reg <= m_ip_dest_ip_next; + m_udp_source_port_reg <= m_udp_source_port_next; + m_udp_dest_port_reg <= m_udp_dest_port_next; + m_udp_length_reg <= m_udp_length_next; + m_udp_checksum_reg <= m_udp_checksum_next; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_udp_payload_axis_tvalid_reg = 1'b0, m_udp_payload_axis_tvalid_next; +reg m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_udp_payload_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_udp_payload_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_udp_payload_axis_tvalid_reg = 1'b0, temp_m_udp_payload_axis_tvalid_next; +reg temp_m_udp_payload_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_udp_payload_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_udp_payload_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_udp_payload_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_udp_payload_axis_tdata = m_udp_payload_axis_tdata_reg; +assign m_udp_payload_axis_tkeep = KEEP_ENABLE ? m_udp_payload_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_udp_payload_axis_tvalid = m_udp_payload_axis_tvalid_reg; +assign m_udp_payload_axis_tlast = m_udp_payload_axis_tlast_reg; +assign m_udp_payload_axis_tid = ID_ENABLE ? m_udp_payload_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_udp_payload_axis_tdest = DEST_ENABLE ? m_udp_payload_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_udp_payload_axis_tuser = USER_ENABLE ? m_udp_payload_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_udp_payload_axis_tready_int_early = m_udp_payload_axis_tready || (!temp_m_udp_payload_axis_tvalid_reg && (!m_udp_payload_axis_tvalid_reg || !m_udp_payload_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_udp_payload_axis_tready_int_reg) begin + // input is ready + if (m_udp_payload_axis_tready || !m_udp_payload_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_udp_payload_axis_tvalid_next = m_udp_payload_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_udp_payload_axis_tready) begin + // input is not ready, but output is ready + m_udp_payload_axis_tvalid_next = temp_m_udp_payload_axis_tvalid_reg; + temp_m_udp_payload_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_udp_payload_axis_tvalid_reg <= 1'b0; + m_udp_payload_axis_tready_int_reg <= 1'b0; + temp_m_udp_payload_axis_tvalid_reg <= 1'b0; + end else begin + m_udp_payload_axis_tvalid_reg <= m_udp_payload_axis_tvalid_next; + m_udp_payload_axis_tready_int_reg <= m_udp_payload_axis_tready_int_early; + temp_m_udp_payload_axis_tvalid_reg <= temp_m_udp_payload_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_udp_payload_axis_tdata_reg <= temp_m_udp_payload_axis_tdata_reg; + m_udp_payload_axis_tkeep_reg <= temp_m_udp_payload_axis_tkeep_reg; + m_udp_payload_axis_tlast_reg <= temp_m_udp_payload_axis_tlast_reg; + m_udp_payload_axis_tid_reg <= temp_m_udp_payload_axis_tid_reg; + m_udp_payload_axis_tdest_reg <= temp_m_udp_payload_axis_tdest_reg; + m_udp_payload_axis_tuser_reg <= temp_m_udp_payload_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_udp_payload_axis_tdata_reg <= m_udp_payload_axis_tdata_int; + temp_m_udp_payload_axis_tkeep_reg <= m_udp_payload_axis_tkeep_int; + temp_m_udp_payload_axis_tlast_reg <= m_udp_payload_axis_tlast_int; + temp_m_udp_payload_axis_tid_reg <= m_udp_payload_axis_tid_int; + temp_m_udp_payload_axis_tdest_reg <= m_udp_payload_axis_tdest_int; + temp_m_udp_payload_axis_tuser_reg <= m_udp_payload_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/eth/rtl/xgmii_baser_dec_64.v b/corundum/lib/eth/rtl/xgmii_baser_dec_64.v new file mode 100644 index 0000000000000000000000000000000000000000..f10c88b7821a1f04f159a73d007f696682f70599 --- /dev/null +++ b/corundum/lib/eth/rtl/xgmii_baser_dec_64.v @@ -0,0 +1,325 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * XGMII 10GBASE-R decoder + */ +module xgmii_baser_dec_64 # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2 +) +( + input wire clk, + input wire rst, + + /* + * 10GBASE-R encoded input + */ + input wire [DATA_WIDTH-1:0] encoded_rx_data, + input wire [HDR_WIDTH-1:0] encoded_rx_hdr, + + /* + * XGMII interface + */ + output wire [DATA_WIDTH-1:0] xgmii_rxd, + output wire [CTRL_WIDTH-1:0] xgmii_rxc, + + /* + * Status + */ + output wire rx_bad_block +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_LPI = 8'h06, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe, + XGMII_SEQ_OS = 8'h9c, + XGMII_RES_0 = 8'h1c, + XGMII_RES_1 = 8'h3c, + XGMII_RES_2 = 8'h7c, + XGMII_RES_3 = 8'hbc, + XGMII_RES_4 = 8'hdc, + XGMII_RES_5 = 8'hf7, + XGMII_SIG_OS = 8'h5c; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +reg [DATA_WIDTH-1:0] decoded_ctrl; +reg [CTRL_WIDTH-1:0] decode_err; + +reg [DATA_WIDTH-1:0] xgmii_rxd_reg = {DATA_WIDTH{1'b0}}, xgmii_rxd_next; +reg [CTRL_WIDTH-1:0] xgmii_rxc_reg = {CTRL_WIDTH{1'b0}}, xgmii_rxc_next; + +reg rx_bad_block_reg = 1'b0, rx_bad_block_next; + +assign xgmii_rxd = xgmii_rxd_reg; +assign xgmii_rxc = xgmii_rxc_reg; + +assign rx_bad_block = rx_bad_block_reg; + +integer i; + +always @* begin + xgmii_rxd_next = {8{XGMII_ERROR}}; + xgmii_rxc_next = 8'hff; + rx_bad_block_next = 1'b0; + + for (i = 0; i < CTRL_WIDTH; i = i + 1) begin + case (encoded_rx_data[7*i+8 +: 7]) + CTRL_IDLE: begin + decoded_ctrl[8*i +: 8] = XGMII_IDLE; + decode_err[i] = 1'b0; + end + CTRL_ERROR: begin + decoded_ctrl[8*i +: 8] = XGMII_ERROR; + decode_err[i] = 1'b0; + end + CTRL_RES_0: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_0; + decode_err[i] = 1'b0; + end + CTRL_RES_1: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_1; + decode_err[i] = 1'b0; + end + CTRL_RES_2: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_2; + decode_err[i] = 1'b0; + end + CTRL_RES_3: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_3; + decode_err[i] = 1'b0; + end + CTRL_RES_4: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_4; + decode_err[i] = 1'b0; + end + CTRL_RES_5: begin + decoded_ctrl[8*i +: 8] = XGMII_RES_5; + decode_err[i] = 1'b0; + end + default: begin + decoded_ctrl[8*i +: 8] = XGMII_ERROR; + decode_err[i] = 1'b1; + end + endcase + end + + if (encoded_rx_hdr == SYNC_DATA) begin + xgmii_rxd_next = encoded_rx_data; + xgmii_rxc_next = 8'h00; + end else if (encoded_rx_hdr == SYNC_CTRL) begin + case (encoded_rx_data[7:0]) + BLOCK_TYPE_CTRL: begin + // C7 C6 C5 C4 C3 C2 C1 C0 BT + xgmii_rxd_next = decoded_ctrl; + xgmii_rxc_next = 8'hff; + end + BLOCK_TYPE_OS_4: begin + // D7 D6 D5 O4 C3 C2 C1 C0 BT + xgmii_rxd_next[31:0] = decoded_ctrl[31:0]; + xgmii_rxc_next[3:0] = 4'hf; + if (encoded_rx_data[39:36] == O_SEQ_OS) begin + xgmii_rxd_next[63:32] = {encoded_rx_data[63:40], XGMII_SEQ_OS}; + xgmii_rxc_next[7:4] = 4'h1; + end else begin + xgmii_rxd_next[63:32] = {4{XGMII_ERROR}}; + xgmii_rxc_next[7:4] = 4'hf; + end + end + BLOCK_TYPE_START_4: begin + // D7 D6 D5 C3 C2 C1 C0 BT + xgmii_rxd_next = {encoded_rx_data[63:40], XGMII_START, decoded_ctrl[31:0]}; + xgmii_rxc_next = 8'h1f; + end + BLOCK_TYPE_OS_START: begin + // D7 D6 D5 O0 D3 D2 D1 BT + if (encoded_rx_data[35:32] == O_SEQ_OS) begin + xgmii_rxd_next[31:0] = {encoded_rx_data[31:8], XGMII_SEQ_OS}; + xgmii_rxc_next[3:0] = 4'h1; + end else begin + xgmii_rxd_next[31:0] = {4{XGMII_ERROR}}; + xgmii_rxc_next[3:0] = 4'hf; + end + xgmii_rxd_next[63:32] = {encoded_rx_data[63:40], XGMII_START}; + xgmii_rxc_next[7:4] = 4'h1; + end + BLOCK_TYPE_OS_04: begin + // D7 D6 D5 O4 O0 D3 D2 D1 BT + if (encoded_rx_data[35:32] == O_SEQ_OS) begin + xgmii_rxd_next[31:0] = {encoded_rx_data[31:8], XGMII_SEQ_OS}; + xgmii_rxc_next[3:0] = 4'h1; + end else begin + xgmii_rxd_next[31:0] = {4{XGMII_ERROR}}; + xgmii_rxc_next[3:0] = 4'hf; + end + if (encoded_rx_data[39:36] == O_SEQ_OS) begin + xgmii_rxd_next[63:32] = {encoded_rx_data[63:40], XGMII_SEQ_OS}; + xgmii_rxc_next[7:4] = 4'h1; + end else begin + xgmii_rxd_next[63:32] = {4{XGMII_ERROR}}; + xgmii_rxc_next[7:4] = 4'hf; + end + end + BLOCK_TYPE_START_0: begin + // D7 D6 D5 D4 D3 D2 D1 BT + xgmii_rxd_next = {encoded_rx_data[63:8], XGMII_START}; + xgmii_rxc_next = 8'h01; + end + BLOCK_TYPE_OS_0: begin + // C7 C6 C5 C4 O0 D3 D2 D1 BT + if (encoded_rx_data[35:32] == O_SEQ_OS) begin + xgmii_rxd_next[31:0] = {encoded_rx_data[31:8], XGMII_SEQ_OS}; + xgmii_rxc_next[3:0] = 4'h1; + end else begin + xgmii_rxd_next[31:0] = {4{XGMII_ERROR}}; + xgmii_rxc_next[3:0] = 4'hf; + end + xgmii_rxd_next[63:32] = decoded_ctrl[63:32]; + xgmii_rxc_next[7:4] = 4'hf; + end + BLOCK_TYPE_TERM_0: begin + // C7 C6 C5 C4 C3 C2 C1 BT + xgmii_rxd_next = {decoded_ctrl[63:8], XGMII_TERM}; + xgmii_rxc_next = 8'hff; + end + BLOCK_TYPE_TERM_1: begin + // C7 C6 C5 C4 C3 C2 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:16], XGMII_TERM, encoded_rx_data[15:8]}; + xgmii_rxc_next = 8'hfe; + end + BLOCK_TYPE_TERM_2: begin + // C7 C6 C5 C4 C3 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:24], XGMII_TERM, encoded_rx_data[23:8]}; + xgmii_rxc_next = 8'hfc; + end + BLOCK_TYPE_TERM_3: begin + // C7 C6 C5 C4 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:32], XGMII_TERM, encoded_rx_data[31:8]}; + xgmii_rxc_next = 8'hf8; + end + BLOCK_TYPE_TERM_4: begin + // C7 C6 C5 D3 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:40], XGMII_TERM, encoded_rx_data[39:8]}; + xgmii_rxc_next = 8'hf0; + end + BLOCK_TYPE_TERM_5: begin + // C7 C6 D4 D3 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:48], XGMII_TERM, encoded_rx_data[47:8]}; + xgmii_rxc_next = 8'he0; + end + BLOCK_TYPE_TERM_6: begin + // C7 D5 D4 D3 D2 D1 D0 BT + xgmii_rxd_next = {decoded_ctrl[63:56], XGMII_TERM, encoded_rx_data[55:8]}; + xgmii_rxc_next = 8'hc0; + end + BLOCK_TYPE_TERM_7: begin + // D6 D5 D4 D3 D2 D1 D0 BT + xgmii_rxd_next = {XGMII_TERM, encoded_rx_data[63:8]}; + xgmii_rxc_next = 8'h80; + end + default: begin + // invalid block type + xgmii_rxd_next = {8{XGMII_ERROR}}; + xgmii_rxc_next = 8'hff; + rx_bad_block_next = 1'b1; + end + endcase + end else begin + // invalid header + xgmii_rxd_next = {8{XGMII_ERROR}}; + xgmii_rxc_next = 8'hff; + rx_bad_block_next = 1'b1; + end +end + +always @(posedge clk) begin + xgmii_rxd_reg <= xgmii_rxd_next; + xgmii_rxc_reg <= xgmii_rxc_next; + + rx_bad_block_reg <= rx_bad_block_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/xgmii_baser_enc_64.v b/corundum/lib/eth/rtl/xgmii_baser_enc_64.v new file mode 100644 index 0000000000000000000000000000000000000000..9cb38d7c00103236a856019fe4b7787a084d71f0 --- /dev/null +++ b/corundum/lib/eth/rtl/xgmii_baser_enc_64.v @@ -0,0 +1,253 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * XGMII 10GBASE-R encoder + */ +module xgmii_baser_enc_64 # +( + parameter DATA_WIDTH = 64, + parameter CTRL_WIDTH = (DATA_WIDTH/8), + parameter HDR_WIDTH = 2 +) +( + input wire clk, + input wire rst, + + /* + * XGMII interface + */ + input wire [DATA_WIDTH-1:0] xgmii_txd, + input wire [CTRL_WIDTH-1:0] xgmii_txc, + + /* + * 10GBASE-R encoded interface + */ + output wire [DATA_WIDTH-1:0] encoded_tx_data, + output wire [HDR_WIDTH-1:0] encoded_tx_hdr +); + +// bus width assertions +initial begin + if (DATA_WIDTH != 64) begin + $error("Error: Interface width must be 64"); + $finish; + end + + if (CTRL_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end + + if (HDR_WIDTH != 2) begin + $error("Error: HDR_WIDTH must be 2"); + $finish; + end +end + +localparam [7:0] + XGMII_IDLE = 8'h07, + XGMII_LPI = 8'h06, + XGMII_START = 8'hfb, + XGMII_TERM = 8'hfd, + XGMII_ERROR = 8'hfe, + XGMII_SEQ_OS = 8'h9c, + XGMII_RES_0 = 8'h1c, + XGMII_RES_1 = 8'h3c, + XGMII_RES_2 = 8'h7c, + XGMII_RES_3 = 8'hbc, + XGMII_RES_4 = 8'hdc, + XGMII_RES_5 = 8'hf7, + XGMII_SIG_OS = 8'h5c; + +localparam [6:0] + CTRL_IDLE = 7'h00, + CTRL_LPI = 7'h06, + CTRL_ERROR = 7'h1e, + CTRL_RES_0 = 7'h2d, + CTRL_RES_1 = 7'h33, + CTRL_RES_2 = 7'h4b, + CTRL_RES_3 = 7'h55, + CTRL_RES_4 = 7'h66, + CTRL_RES_5 = 7'h78; + +localparam [3:0] + O_SEQ_OS = 4'h0, + O_SIG_OS = 4'hf; + +localparam [1:0] + SYNC_DATA = 2'b10, + SYNC_CTRL = 2'b01; + +localparam [7:0] + BLOCK_TYPE_CTRL = 8'h1e, // C7 C6 C5 C4 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_4 = 8'h2d, // D7 D6 D5 O4 C3 C2 C1 C0 BT + BLOCK_TYPE_START_4 = 8'h33, // D7 D6 D5 C3 C2 C1 C0 BT + BLOCK_TYPE_OS_START = 8'h66, // D7 D6 D5 O0 D3 D2 D1 BT + BLOCK_TYPE_OS_04 = 8'h55, // D7 D6 D5 O4 O0 D3 D2 D1 BT + BLOCK_TYPE_START_0 = 8'h78, // D7 D6 D5 D4 D3 D2 D1 BT + BLOCK_TYPE_OS_0 = 8'h4b, // C7 C6 C5 C4 O0 D3 D2 D1 BT + BLOCK_TYPE_TERM_0 = 8'h87, // C7 C6 C5 C4 C3 C2 C1 BT + BLOCK_TYPE_TERM_1 = 8'h99, // C7 C6 C5 C4 C3 C2 D0 BT + BLOCK_TYPE_TERM_2 = 8'haa, // C7 C6 C5 C4 C3 D1 D0 BT + BLOCK_TYPE_TERM_3 = 8'hb4, // C7 C6 C5 C4 D2 D1 D0 BT + BLOCK_TYPE_TERM_4 = 8'hcc, // C7 C6 C5 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_5 = 8'hd2, // C7 C6 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_6 = 8'he1, // C7 D5 D4 D3 D2 D1 D0 BT + BLOCK_TYPE_TERM_7 = 8'hff; // D6 D5 D4 D3 D2 D1 D0 BT + +reg [DATA_WIDTH*7/8-1:0] encoded_ctrl; +reg [CTRL_WIDTH-1:0] encode_err; + +reg [DATA_WIDTH-1:0] encoded_tx_data_reg = {DATA_WIDTH{1'b0}}, encoded_tx_data_next; +reg [HDR_WIDTH-1:0] encoded_tx_hdr_reg = {HDR_WIDTH{1'b0}}, encoded_tx_hdr_next; + +assign encoded_tx_data = encoded_tx_data_reg; +assign encoded_tx_hdr = encoded_tx_hdr_reg; + +integer i; + +always @* begin + + + for (i = 0; i < CTRL_WIDTH; i = i + 1) begin + if (xgmii_txc[i]) begin + // control + case (xgmii_txd[8*i +: 8]) + XGMII_IDLE: begin + encoded_ctrl[7*i +: 7] = CTRL_IDLE; + encode_err[i] = 1'b0; + end + XGMII_ERROR: begin + encoded_ctrl[7*i +: 7] = CTRL_ERROR; + encode_err[i] = 1'b0; + end + XGMII_RES_0: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_0; + encode_err[i] = 1'b0; + end + XGMII_RES_1: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_1; + encode_err[i] = 1'b0; + end + XGMII_RES_2: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_2; + encode_err[i] = 1'b0; + end + XGMII_RES_3: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_3; + encode_err[i] = 1'b0; + end + XGMII_RES_4: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_4; + encode_err[i] = 1'b0; + end + XGMII_RES_5: begin + encoded_ctrl[7*i +: 7] = CTRL_RES_5; + encode_err[i] = 1'b0; + end + default: begin + encoded_ctrl[7*i +: 7] = CTRL_ERROR; + encode_err[i] = 1'b1; + end + endcase + end else begin + // data (always invalid as control) + encoded_ctrl[7*i +: 7] = CTRL_ERROR; + encode_err[i] = 1'b1; + end + end + + if (xgmii_txc == 8'h00) begin + encoded_tx_data_next = xgmii_txd; + encoded_tx_hdr_next = SYNC_DATA; + end else begin + if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_START && !xgmii_txc[7:1]) begin + // start in lane 0 + encoded_tx_data_next = {xgmii_txd[63:8], BLOCK_TYPE_START_0}; + end else if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_START && !xgmii_txc[7:5]) begin + // start in lane 4 + if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_SEQ_OS && !xgmii_txc[3:1]) begin + // ordered set in lane 0 + encoded_tx_data_next[35:0] = {O_SEQ_OS, xgmii_txd[31:8], BLOCK_TYPE_START_4}; + end else begin + encoded_tx_data_next[35:0] = {encoded_ctrl[27:0], BLOCK_TYPE_START_4}; + end + encoded_tx_data_next[63:36] = {xgmii_txd[63:40], 4'd0}; + end else if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_SEQ_OS && !xgmii_txc[3:1]) begin + // ordered set in lane 0 + encoded_tx_data_next[35:8] = {O_SEQ_OS, xgmii_txd[31:8]}; + if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_SEQ_OS && !xgmii_txc[7:5]) begin + // ordered set in lane 4 + encoded_tx_data_next[63:36] = {xgmii_txd[63:40], O_SEQ_OS}; + encoded_tx_data_next[7:0] = BLOCK_TYPE_OS_04; + end else begin + encoded_tx_data_next[63:36] = encoded_ctrl[55:28]; + encoded_tx_data_next[7:0] = BLOCK_TYPE_OS_0; + end + end else if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_SEQ_OS && !xgmii_txc[7:5]) begin + // ordered set in lane 4 + encoded_tx_data_next = {xgmii_txd[63:40], O_SEQ_OS, 4'd0, encoded_ctrl[27:0], BLOCK_TYPE_OS_4}; + end else if (xgmii_txc[0] && xgmii_txd[7:0] == XGMII_TERM) begin + // terminate in lane 0 + encoded_tx_data_next = {encoded_ctrl[55:7], 7'd0, BLOCK_TYPE_TERM_0}; + end else if (xgmii_txc[1] && xgmii_txd[15:8] == XGMII_TERM && !xgmii_txc[0]) begin + // terminate in lane 1 + encoded_tx_data_next = {encoded_ctrl[55:14], 6'd0, xgmii_txd[7:0], BLOCK_TYPE_TERM_1}; + end else if (xgmii_txc[2] && xgmii_txd[23:16] == XGMII_TERM && !xgmii_txc[1:0]) begin + // terminate in lane 2 + encoded_tx_data_next = {encoded_ctrl[55:21], 5'd0, xgmii_txd[15:0], BLOCK_TYPE_TERM_2}; + end else if (xgmii_txc[3] && xgmii_txd[31:24] == XGMII_TERM && !xgmii_txc[2:0]) begin + // terminate in lane 3 + encoded_tx_data_next = {encoded_ctrl[55:28], 4'd0, xgmii_txd[23:0], BLOCK_TYPE_TERM_3}; + end else if (xgmii_txc[4] && xgmii_txd[39:32] == XGMII_TERM && !xgmii_txc[3:0]) begin + // terminate in lane 4 + encoded_tx_data_next = {encoded_ctrl[55:35], 3'd0, xgmii_txd[31:0], BLOCK_TYPE_TERM_4}; + end else if (xgmii_txc[5] && xgmii_txd[47:40] == XGMII_TERM && !xgmii_txc[4:0]) begin + // terminate in lane 5 + encoded_tx_data_next = {encoded_ctrl[55:42], 2'd0, xgmii_txd[39:0], BLOCK_TYPE_TERM_5}; + end else if (xgmii_txc[6] && xgmii_txd[55:48] == XGMII_TERM && !xgmii_txc[5:0]) begin + // terminate in lane 6 + encoded_tx_data_next = {encoded_ctrl[55:49], 1'd0, xgmii_txd[47:0], BLOCK_TYPE_TERM_6}; + end else if (xgmii_txc[7] && xgmii_txd[63:56] == XGMII_TERM && !xgmii_txc[6:0]) begin + // terminate in lane 7 + encoded_tx_data_next = {xgmii_txd[55:0], BLOCK_TYPE_TERM_7}; + end else begin + // all control + encoded_tx_data_next = {encoded_ctrl, BLOCK_TYPE_CTRL}; + end + encoded_tx_hdr_next = SYNC_CTRL; + end +end + +always @(posedge clk) begin + encoded_tx_data_reg <= encoded_tx_data_next; + encoded_tx_hdr_reg <= encoded_tx_hdr_next; +end + +endmodule diff --git a/corundum/lib/eth/rtl/xgmii_deinterleave.v b/corundum/lib/eth/rtl/xgmii_deinterleave.v new file mode 100644 index 0000000000000000000000000000000000000000..5410bc9555c3aaf9bcc43ac7dbc9fdab6cbea00e --- /dev/null +++ b/corundum/lib/eth/rtl/xgmii_deinterleave.v @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * XGMII control/data deinterleave + */ +module xgmii_deinterleave +( + input wire [72:0] input_xgmii_dc, + + output wire [63:0] output_xgmii_d, + output wire [7:0] output_xgmii_c +); + +assign output_xgmii_d[7:0] = input_xgmii_dc[7:0]; +assign output_xgmii_c[0] = input_xgmii_dc[8]; +assign output_xgmii_d[15:8] = input_xgmii_dc[16:9]; +assign output_xgmii_c[1] = input_xgmii_dc[17]; +assign output_xgmii_d[23:16] = input_xgmii_dc[25:18]; +assign output_xgmii_c[2] = input_xgmii_dc[26]; +assign output_xgmii_d[31:24] = input_xgmii_dc[34:27]; +assign output_xgmii_c[3] = input_xgmii_dc[35]; +assign output_xgmii_d[39:32] = input_xgmii_dc[43:36]; +assign output_xgmii_c[4] = input_xgmii_dc[44]; +assign output_xgmii_d[47:40] = input_xgmii_dc[52:45]; +assign output_xgmii_c[5] = input_xgmii_dc[53]; +assign output_xgmii_d[55:48] = input_xgmii_dc[61:54]; +assign output_xgmii_c[6] = input_xgmii_dc[62]; +assign output_xgmii_d[63:56] = input_xgmii_dc[70:63]; +assign output_xgmii_c[7] = input_xgmii_dc[71]; + +endmodule diff --git a/corundum/lib/eth/rtl/xgmii_interleave.v b/corundum/lib/eth/rtl/xgmii_interleave.v new file mode 100644 index 0000000000000000000000000000000000000000..cf365e2f1c8162bbaadd65919d1840656b7b1bc5 --- /dev/null +++ b/corundum/lib/eth/rtl/xgmii_interleave.v @@ -0,0 +1,57 @@ +/* + +Copyright (c) 2016-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * XGMII control/data interleave + */ +module xgmii_interleave +( + input wire [63:0] input_xgmii_d, + input wire [7:0] input_xgmii_c, + + output wire [72:0] output_xgmii_dc +); + +assign output_xgmii_dc[7:0] = input_xgmii_d[7:0]; +assign output_xgmii_dc[8] = input_xgmii_c[0]; +assign output_xgmii_dc[16:9] = input_xgmii_d[15:8]; +assign output_xgmii_dc[17] = input_xgmii_c[1]; +assign output_xgmii_dc[25:18] = input_xgmii_d[23:16]; +assign output_xgmii_dc[26] = input_xgmii_c[2]; +assign output_xgmii_dc[34:27] = input_xgmii_d[31:24]; +assign output_xgmii_dc[35] = input_xgmii_c[3]; +assign output_xgmii_dc[43:36] = input_xgmii_d[39:32]; +assign output_xgmii_dc[44] = input_xgmii_c[4]; +assign output_xgmii_dc[52:45] = input_xgmii_d[47:40]; +assign output_xgmii_dc[53] = input_xgmii_c[5]; +assign output_xgmii_dc[61:54] = input_xgmii_d[55:48]; +assign output_xgmii_dc[62] = input_xgmii_c[6]; +assign output_xgmii_dc[70:63] = input_xgmii_d[63:56]; +assign output_xgmii_dc[71] = input_xgmii_c[7]; + +endmodule diff --git a/corundum/lib/pcie/.gitignore b/corundum/lib/pcie/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..964df4d4afc9fbf5e32167a837ffa1359c80d062 --- /dev/null +++ b/corundum/lib/pcie/.gitignore @@ -0,0 +1,6 @@ +*~ +*.lxt +*.pyc +*.vvp +*.kate-swp + diff --git a/corundum/lib/pcie/AUTHORS b/corundum/lib/pcie/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..7dab2b3a51633a1dd67fd71ceb1c637931f27b85 --- /dev/null +++ b/corundum/lib/pcie/AUTHORS @@ -0,0 +1 @@ +Alex Forencich diff --git a/corundum/lib/pcie/COPYING b/corundum/lib/pcie/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..6923387c7569f15368fefc084c0f3c46e18e3622 --- /dev/null +++ b/corundum/lib/pcie/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/corundum/lib/pcie/README b/corundum/lib/pcie/README new file mode 120000 index 0000000000000000000000000000000000000000..42061c01a1c70097d1e4579f29a5adf40abdec95 --- /dev/null +++ b/corundum/lib/pcie/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/corundum/lib/pcie/README.md b/corundum/lib/pcie/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e62d83d65962d83dc09f62b049a9b0ed6a430c10 --- /dev/null +++ b/corundum/lib/pcie/README.md @@ -0,0 +1,276 @@ +# Verilog PCI Express Components Readme + +For more information and updates: http://alexforencich.com/wiki/en/verilog/pcie/start + +GitHub repository: https://github.com/alexforencich/verilog-pcie + +## Introduction + +Collection of PCI express related components. Includes PCIe to AXI and AXI +lite bridges, a simple PCIe AXI DMA engine, and a flexible, high-performance +DMA subsystem. Currently supports operation with the Xilinx Ultrascale and +Ultrascale Plus PCIe hard IP cores with interfaces between 64 and 512 bits. +Includes full MyHDL testbench with intelligent bus cosimulation endpoints. + +## Documentation + +### PCIe BFM + +A MyHDL transaction layer PCI Express bus functional model (BFM) is included +in pcie.py. This BFM implements an extensive event driven simulation of a +complete PCI express system, including root complex, switches, devices, and +functions, including support for configuration spaces, capabilities and +extended capabilities, and memory and IO operations between devices. The BFM +includes code to enumerate the bus, initialize configuration space registers +and allocate BARs, route messages between devices, perform memory read and +write operations, allocate DMA accessible memory regions in the root complex, +and handle message signaled interrupts. Any module can be connected to a +cosimulated design, enabling testing of not only isolated components and +host-device communication but also communication between multiple components +such as device-to-device DMA and message passing. + +MyHDL models of the Xilinx Ultrascale and Ultrascale Plus PCIe hard cores are +included in pcie_us.py and pcie_usp.py. These modules can be used in +combination with the PCIe BFM to test a MyHDL or Verilog design that targets a +Xilinx Ultrascale or Ultrascale Plus FPGA. The models currently only support +operation as a device, not as a root port. + +### PCIe AXI and AXI lite master + +The pcie_us_axi_master and pcie_us_axil_master modules provide a bridge +between PCIe and AXI. These can be used to implement PCIe BARs. The +pcie_us_axil_master module is a very simple module for providing register +access, supporting only 32 bit operations. The pcie_us_axi_master module is +more complex, converting PCIe operations to AXI bursts. It can be used to +terminate device-to-device DMA operations with reasonable performance. The +pcie_us_axis_cq_demux module can be used to demultiplex PCIe operations based +on the target BAR. + +### PCIe AXI DMA + +The pcie_us_axi_dma module provides a DMA engine with an internal AXI +interface. The AXI interface width must match the PCIe interface width. The +module directly translates AXI operations into PCIe operations. As a result, +it is relatively simple, but the performance is limited due to the constraints +of the AXI interface. Backpressure on the AXI interface is also passed +through to the PCIe interface. + +The pcie_axi_dma_desc_mux module can be used to share the AXI DMA module +between multiple request sources. + +### Flexible DMA subsystem + +The split DMA interface/DMA client modules support highly flexible, highly +performant DMA operations. The DMA interface and DMA client modules are +connected by dual port RAMs with a high performance segmented memory +interface. The segmented memory interface is a better 'impedance match' to +the PCIe hard core interface - data realignment can be done in the same clock +cycle; no bursts, address decoding, arbitration, or reordering simplifies +implementation and provides much higher performance than AXI. The architecture +is also quite flexible as it decouples the DMA interface from the clients with +dual port RAMs, enabling mixing different client interface types and widths +and even supporting clients running in different clock domains without +datapath FIFOs. + +The dma_if_pcie_us module connects the PCIe interface to the segmented memory +interface. Currently, it does not support TLP straddling, but it should be +possible to support this with the segmented interface. + +The dma_psdpram module is a dual clock, parallel simple dual port RAM module +with a segmented interface. The depth is independently adjustable from the +address width, simplifying use of the segmented interface. The module also contains a parametrizable output pipeline register to improve timing. + +The dma_if_mux module enables sharing the DMA interface across several DMA +clients. This module handles the tags and select lines appropriately on both +the descriptor and segmented memory interface for plug-and-play operation +without address assignment - routing is completely determined by component +connections. The module also contains a FIFO to maintain read data ordering +across multiple clients. Make sure to equalize pipeline delay across all +paths for maximum performance. + +DMA client modules connect the segmented memory interface to different +internal interfaces. + +The dma_client_axis_source and dma_client_axis_sink modules provide support +for streaming DMA over AXI stream. The AXI stream width can be any power of +two fraction of the segmented memory interface width. + +### arbiter module + +General-purpose parametrizable arbiter. Supports priority and round-robin +arbitration. Supports blocking until request release or acknowledge. + +### axis_arb_mux module + +Frame-aware AXI stream arbitrated muliplexer with parametrizable data width +and port count. Supports priority and round-robin arbitration. + +### dma_client_axis_sink module + +AXI stream sink DMA client module. Uses a segmented memory interface. + +### dma_client_axis_source module + +AXI stream source DMA client module. Uses a segmented memory interface. + +### dma_if_mux module + +DMA interface mux module. Enables sharing a DMA interface module between +multiple DMA client modules. Wrapper for dma_if_mux_rd and dma_if_mux_wr. + +### dma_if_mux_rd module + +DMA interface mux module. Enables sharing a DMA interface module between +multiple DMA client modules. Muxes descriptors and demuxes memory writes. + +### dma_if_mux_wr module + +DMA interface mux module. Enables sharing a DMA interface module between +multiple DMA client modules. Muxes descriptors, demuxes memory read commands, +and muxes read data. + +### dma_if_pcie_us module + +PCIe DMA interface module for Xilinx Ultrascale series FPGAs. Supports 64, +128, 256, and 512 bit datapaths. Uses a double width segmented memory +interface. Wrapper for dma_if_pcie_us_rd and dma_if_pcie_us_wr. + +### dma_if_pcie_us_rd module + +PCIe DMA interface module for Xilinx Ultrascale series FPGAs. Supports 64, +128, 256, and 512 bit datapaths. Uses a double width segmented memory +interface. + +### dma_if_pcie_us_wr module + +PCIe DMA interface module for Xilinx Ultrascale series FPGAs. Supports 64, +128, 256, and 512 bit datapaths. Uses a double width segmented memory +interface. + +### dma_psdpram module + +DMA RAM module. Segmented simple dual port RAM to connect a DMA interface +module to a DMA client. + +### pcie_axi_dma_desc_mux module + +Descriptor multiplexer/demultiplexer for PCIe AXI DMA module. Enables sharing +the PCIe AXI DMA module between multiple request sources, interleaving +requests and distributing responses. + +### pcie_tag_manager module + +PCIe in-flight tag manager. + +### pcie_us_axi_dma module + +PCIe AXI DMA module for Xilinx Ultrascale series FPGAs. Supports 64, 128, 256, +and 512 bit datapaths. Parametrizable AXI burst length. Wrapper for +pcie_us_axi_dma_rd and pcie_us_axi_dma_wr. + +### pcie_us_axi_dma_rd module + +PCIe AXI DMA module for Xilinx Ultrascale series FPGAs. Supports 64, 128, 256, +and 512 bit datapaths. Parametrizable AXI burst length. + +### pcie_us_axi_dma_wr module + +PCIe AXI DMA module for Xilinx Ultrascale series FPGAs. Supports 64, 128, 256, +and 512 bit datapaths. Parametrizable AXI burst length. + +### pcie_us_axi_master module + +PCIe AXI master module for Xilinx Ultrascale series FPGAs. Supports 64, 128, +256, and 512 bit datapaths. Parametrizable AXI burst length. Wrapper for +pcie_us_axi_master_rd and pcie_us_axi_master_wr. + +### pcie_us_axi_master_rd module + +PCIe AXI master module for Xilinx Ultrascale series FPGAs. Supports 64, 128, +256, and 512 bit datapaths. Parametrizable AXI burst length. + +### pcie_us_axi_master_wr module + +PCIe AXI master module for Xilinx Ultrascale series FPGAs. Supports 64, 128, +256, and 512 bit datapaths. Parametrizable AXI burst length. + +### pcie_us_axil_master module + +PCIe AXI lite master module for Xilinx Ultrascale series FPGAs. Supports 64, +128, 256, and 512 bit PCIe interfaces. + +### pcie_us_axis_cq_demux module + +Demux module for Xilinx Ultrascale CQ interface. Can be used to route +incoming requests based on function, BAR, and other fields. Supports 64, 128, +256, and 512 bit datapaths. + +### pcie_us_axis_rc_demux module + +Demux module for Xilinx Ultrascale RC interface. Can be used to route +incoming completions based on the requester ID (function). Supports 64, 128, +256, and 512 bit datapaths. + +### pcie_us_cfg module + +Configuration shim for Xilinx Ultrascale series FPGAs. + +### pcie_us_msi module + +MSI shim for Xilinx Ultrascale series FPGAs. + +### priority_encoder module + +Parametrizable priority encoder. + +### pulse_merge module + +Parametrizable pulse merge module. Combines several single-cycle pulse status +signals together. + +### Common signals + +### Common parameters + +### Source Files + + arbiter.v : Parametrizable arbiter + axis_arb_mux.v : Parametrizable AXI stream mux + dma_client_axis_sink.v : AXI stream sink DMA client + dma_client_axis_source.v : AXI stream source DMA client + dma_if_mux.v : DMA interface mux + dma_if_mux_rd.v : DMA interface mux (read) + dma_if_mux_wr.v : DMA interface mux (write) + dma_if_pcie_us.v ; DMA interface for Xilinx Ultrascale PCIe + dma_if_pcie_us_rd.v ; DMA interface for Xilinx Ultrascale PCIe (read) + dma_if_pcie_us_wr.v ; DMA interface for Xilinx Ultrascale PCIe (write) + dma_psdpram.v : DMA RAM (segmented simple dual port RAM) + pcie_axi_dma_desc_mux.v : Descriptor mux for DMA engine + pcie_tag_manager.v : PCIe in-flight tag manager + pcie_us_axi_dma.v : PCIe AXI DMA module (Xilinx Ultrascale) + pcie_us_axi_dma_rd.v : PCIe AXI DMA read module (Xilinx Ultrascale) + pcie_us_axi_dma_wr.v : PCIe AXI DMA write module (Xilinx Ultrascale) + pcie_us_axi_master.v : PCIe AXI Master module (Xilinx Ultrascale) + pcie_us_axi_master_rd.v : PCIe AXI Master read module (Xilinx Ultrascale) + pcie_us_axi_master_wr.v : PCIe AXI Master write module (Xilinx Ultrascale) + pcie_us_axil_master.v : PCIe AXI Lite Master module (Xilinx Ultrascale) + pcie_us_axis_cq_demux.v : Parametrizable AXI stream CQ demux + pcie_us_axis_rc_demux.v : Parametrizable AXI stream RC demux + pcie_us_cfg.v : Configuration shim for Xilinx Ultrascale devices + pcie_us_msi.v : MSI shim for Xilinx Ultrascale devices + priority_encoder.v : Parametrizable priority encoder + pulse_merge : Parametrizable pulse merge module + +## Testing + +Running the included testbenches requires MyHDL and Icarus Verilog. Make sure +that myhdl.vpi is installed properly for cosimulation to work correctly. The +testbenches can be run with a Python test runner like nose or py.test, or the +individual test scripts can be run with python directly. + +### Testbench Files + + tb/axis_ep.py : MyHDL AXI Stream endpoints + tb/pcie.py : MyHDL PCI Express BFM + tb/pcie_us.py : MyHDL Xilinx Ultrascale PCIe core model + tb/pcie_usp.py : MyHDL Xilinx Ultrascale Plus PCIe core model diff --git a/corundum/lib/pcie/rtl/arbiter.v b/corundum/lib/pcie/rtl/arbiter.v new file mode 100644 index 0000000000000000000000000000000000000000..8b0443fdbae43c2afc42c074cc93be1603c69964 --- /dev/null +++ b/corundum/lib/pcie/rtl/arbiter.v @@ -0,0 +1,153 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Arbiter module + */ +module arbiter # +( + parameter PORTS = 4, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter TYPE = "PRIORITY", + // block type: "NONE", "REQUEST", "ACKNOWLEDGE" + parameter BLOCK = "NONE", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire clk, + input wire rst, + + input wire [PORTS-1:0] request, + input wire [PORTS-1:0] acknowledge, + + output wire [PORTS-1:0] grant, + output wire grant_valid, + output wire [$clog2(PORTS)-1:0] grant_encoded +); + +reg [PORTS-1:0] grant_reg = 0, grant_next; +reg grant_valid_reg = 0, grant_valid_next; +reg [$clog2(PORTS)-1:0] grant_encoded_reg = 0, grant_encoded_next; + +assign grant_valid = grant_valid_reg; +assign grant = grant_reg; +assign grant_encoded = grant_encoded_reg; + +wire request_valid; +wire [$clog2(PORTS)-1:0] request_index; +wire [PORTS-1:0] request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_inst ( + .input_unencoded(request), + .output_valid(request_valid), + .output_encoded(request_index), + .output_unencoded(request_mask) +); + +reg [PORTS-1:0] mask_reg = 0, mask_next; + +wire masked_request_valid; +wire [$clog2(PORTS)-1:0] masked_request_index; +wire [PORTS-1:0] masked_request_mask; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY(LSB_PRIORITY) +) +priority_encoder_masked ( + .input_unencoded(request & mask_reg), + .output_valid(masked_request_valid), + .output_encoded(masked_request_index), + .output_unencoded(masked_request_mask) +); + +always @* begin + grant_next = 0; + grant_valid_next = 0; + grant_encoded_next = 0; + mask_next = mask_reg; + + if (BLOCK == "REQUEST" && grant_reg & request) begin + // granted request still asserted; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (BLOCK == "ACKNOWLEDGE" && grant_valid && !(grant_reg & acknowledge)) begin + // granted request not yet acknowledged; hold it + grant_valid_next = grant_valid_reg; + grant_next = grant_reg; + grant_encoded_next = grant_encoded_reg; + end else if (request_valid) begin + if (TYPE == "PRIORITY") begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + end else if (TYPE == "ROUND_ROBIN") begin + if (masked_request_valid) begin + grant_valid_next = 1; + grant_next = masked_request_mask; + grant_encoded_next = masked_request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - masked_request_index); + end else begin + mask_next = {PORTS{1'b1}} << (masked_request_index + 1); + end + end else begin + grant_valid_next = 1; + grant_next = request_mask; + grant_encoded_next = request_index; + if (LSB_PRIORITY == "LOW") begin + mask_next = {PORTS{1'b1}} >> (PORTS - request_index); + end else begin + mask_next = {PORTS{1'b1}} << (request_index + 1); + end + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + grant_reg <= 0; + grant_valid_reg <= 0; + grant_encoded_reg <= 0; + mask_reg <= 0; + end else begin + grant_reg <= grant_next; + grant_valid_reg <= grant_valid_next; + grant_encoded_reg <= grant_encoded_next; + mask_reg <= mask_next; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/axis_arb_mux.v b/corundum/lib/pcie/rtl/axis_arb_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..72a3de5e1b7f27e3ff8d7c664853fe23d85792b1 --- /dev/null +++ b/corundum/lib/pcie/rtl/axis_arb_mux.v @@ -0,0 +1,249 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI4-Stream arbitrated multiplexer + */ +module axis_arb_mux # +( + // Number of AXI stream inputs + parameter S_COUNT = 4, + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 8, + // Propagate tkeep signal + parameter KEEP_ENABLE = (DATA_WIDTH>8), + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * AXI Stream inputs + */ + input wire [S_COUNT*DATA_WIDTH-1:0] s_axis_tdata, + input wire [S_COUNT*KEEP_WIDTH-1:0] s_axis_tkeep, + input wire [S_COUNT-1:0] s_axis_tvalid, + output wire [S_COUNT-1:0] s_axis_tready, + input wire [S_COUNT-1:0] s_axis_tlast, + input wire [S_COUNT*ID_WIDTH-1:0] s_axis_tid, + input wire [S_COUNT*DEST_WIDTH-1:0] s_axis_tdest, + input wire [S_COUNT*USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI Stream output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +parameter CL_S_COUNT = $clog2(S_COUNT); + +wire [S_COUNT-1:0] request; +wire [S_COUNT-1:0] acknowledge; +wire [S_COUNT-1:0] grant; +wire grant_valid; +wire [CL_S_COUNT-1:0] grant_encoded; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +assign s_axis_tready = (m_axis_tready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [DATA_WIDTH-1:0] current_s_tdata = s_axis_tdata[grant_encoded*DATA_WIDTH +: DATA_WIDTH]; +wire [KEEP_WIDTH-1:0] current_s_tkeep = s_axis_tkeep[grant_encoded*KEEP_WIDTH +: KEEP_WIDTH]; +wire current_s_tvalid = s_axis_tvalid[grant_encoded]; +wire current_s_tready = s_axis_tready[grant_encoded]; +wire current_s_tlast = s_axis_tlast[grant_encoded]; +wire [ID_WIDTH-1:0] current_s_tid = s_axis_tid[grant_encoded*ID_WIDTH +: ID_WIDTH]; +wire [DEST_WIDTH-1:0] current_s_tdest = s_axis_tdest[grant_encoded*DEST_WIDTH +: DEST_WIDTH]; +wire [USER_WIDTH-1:0] current_s_tuser = s_axis_tuser[grant_encoded*USER_WIDTH +: USER_WIDTH]; + +// arbiter instance +arbiter #( + .PORTS(S_COUNT), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_tvalid & ~grant; +assign acknowledge = grant & s_axis_tvalid & s_axis_tready & s_axis_tlast; + +always @* begin + // pass through selected packet data + m_axis_tdata_int = current_s_tdata; + m_axis_tkeep_int = current_s_tkeep; + m_axis_tvalid_int = current_s_tvalid && m_axis_tready_int_reg && grant_valid; + m_axis_tlast_int = current_s_tlast; + m_axis_tid_int = current_s_tid; + m_axis_tdest_int = current_s_tdest; + m_axis_tuser_int = current_s_tuser; +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = KEEP_ENABLE ? m_axis_tkeep_reg : {KEEP_WIDTH{1'b1}}; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_client_axis_sink.v b/corundum/lib/pcie/rtl/dma_client_axis_sink.v new file mode 100644 index 0000000000000000000000000000000000000000..8721052105b67f887ab8ab0d0445aa47c6ec8bf9 --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_client_axis_sink.v @@ -0,0 +1,515 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI stream sink DMA client + */ +module dma_client_axis_sink # +( + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // Width of AXI stream interfaces in bits + parameter AXIS_DATA_WIDTH = SEG_DATA_WIDTH*SEG_COUNT/2, + // Use AXI stream tkeep signal + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + // Use AXI stream tlast signal + parameter AXIS_LAST_ENABLE = 1, + // Propagate AXI stream tid signal + parameter AXIS_ID_ENABLE = 0, + // AXI stream tid signal width + parameter AXIS_ID_WIDTH = 8, + // Propagate AXI stream tdest signal + parameter AXIS_DEST_ENABLE = 0, + // AXI stream tdest signal width + parameter AXIS_DEST_WIDTH = 8, + // Propagate AXI stream tuser signal + parameter AXIS_USER_ENABLE = 1, + // AXI stream tuser signal width + parameter AXIS_USER_WIDTH = 1, + // Width of length field + parameter LEN_WIDTH = 16, + // Width of tag field + parameter TAG_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI write descriptor input + */ + input wire [RAM_ADDR_WIDTH-1:0] s_axis_write_desc_ram_addr, + input wire [LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire s_axis_write_desc_valid, + output wire s_axis_write_desc_ready, + + /* + * AXI write descriptor status output + */ + output wire [LEN_WIDTH-1:0] m_axis_write_desc_status_len, + output wire [TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id, + output wire [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest, + output wire [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user, + output wire m_axis_write_desc_status_valid, + + /* + * AXI stream write data input + */ + input wire [AXIS_DATA_WIDTH-1:0] s_axis_write_data_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] s_axis_write_data_tkeep, + input wire s_axis_write_data_tvalid, + output wire s_axis_write_data_tready, + input wire s_axis_write_data_tlast, + input wire [AXIS_ID_WIDTH-1:0] s_axis_write_data_tid, + input wire [AXIS_DEST_WIDTH-1:0] s_axis_write_data_tdest, + input wire [AXIS_USER_WIDTH-1:0] s_axis_write_data_tuser, + + /* + * RAM interface + */ + output wire [SEG_COUNT*SEG_BE_WIDTH-1:0] ram_wr_cmd_be, + output wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_wr_cmd_data, + output wire [SEG_COUNT-1:0] ram_wr_cmd_valid, + input wire [SEG_COUNT-1:0] ram_wr_cmd_ready, + + /* + * Configuration + */ + input wire enable, + input wire abort +); + +parameter RAM_WORD_WIDTH = SEG_BE_WIDTH; +parameter RAM_WORD_SIZE = SEG_DATA_WIDTH/RAM_WORD_WIDTH; + +parameter AXIS_KEEP_WIDTH_INT = AXIS_KEEP_ENABLE ? AXIS_KEEP_WIDTH : 1; +parameter AXIS_WORD_WIDTH = AXIS_KEEP_WIDTH_INT; +parameter AXIS_WORD_SIZE = AXIS_DATA_WIDTH/AXIS_WORD_WIDTH; + +parameter PART_COUNT_ = SEG_COUNT*SEG_BE_WIDTH / AXIS_KEEP_WIDTH_INT; +parameter PART_COUNT = PART_COUNT_ == 0 ? 1 : PART_COUNT_; +parameter PART_COUNT_WIDTH = PART_COUNT > 1 ? $clog2(PART_COUNT) : 1; +parameter PART_OFFSET_WIDTH = AXIS_KEEP_WIDTH_INT > 1 ? $clog2(AXIS_KEEP_WIDTH_INT) : 1; +parameter PARTS_PER_SEG = (SEG_BE_WIDTH + AXIS_KEEP_WIDTH_INT - 1) / AXIS_KEEP_WIDTH_INT; +parameter SEGS_PER_PART = (AXIS_KEEP_WIDTH_INT + SEG_BE_WIDTH - 1) / SEG_BE_WIDTH; + +parameter OFFSET_WIDTH = AXIS_KEEP_WIDTH_INT > 1 ? $clog2(AXIS_KEEP_WIDTH_INT) : 1; +parameter OFFSET_MASK = AXIS_KEEP_WIDTH_INT > 1 ? {OFFSET_WIDTH{1'b1}} : 0; +parameter ADDR_MASK = {RAM_ADDR_WIDTH{1'b1}} << $clog2(AXIS_KEEP_WIDTH_INT); +parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - $clog2(AXIS_KEEP_WIDTH_INT) + 1; + +// bus width assertions +initial begin + if (RAM_WORD_SIZE * SEG_BE_WIDTH != SEG_DATA_WIDTH) begin + $error("Error: RAM data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXIS_WORD_SIZE * AXIS_KEEP_WIDTH_INT != AXIS_DATA_WIDTH) begin + $error("Error: AXI stream data width not evenly divisble (instance %m)"); + $finish; + end + + if (RAM_WORD_SIZE != AXIS_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(RAM_WORD_WIDTH) != RAM_WORD_WIDTH) begin + $error("Error: RAM word width must be even power of two (instance %m)"); + $finish; + end + + if (RAM_ADDR_WIDTH != SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH)) begin + $error("Error: RAM_ADDR_WIDTH does not match RAM configuration (instance %m)"); + $finish; + end + + if (AXIS_DATA_WIDTH > SEG_COUNT*SEG_DATA_WIDTH) begin + $error("Error: AXI stream interface width must not be wider than RAM interface width (instance %m)"); + $finish; + end + + if (AXIS_DATA_WIDTH*2**PART_COUNT_WIDTH != SEG_COUNT*SEG_DATA_WIDTH) begin + $error("Error: AXI stream interface width must be a power of two fraction of RAM interface width (instance %m)"); + $finish; + end +end + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_WRITE = 2'd1, + STATE_DROP_DATA = 2'd2; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +integer i; +reg [OFFSET_WIDTH:0] cycle_size; + +reg [RAM_ADDR_WIDTH-1:0] addr_reg = {RAM_ADDR_WIDTH{1'b0}}, addr_next; +reg [SEG_COUNT-1:0] ram_mask_reg = 0, ram_mask_next; +reg [AXIS_KEEP_WIDTH_INT-1:0] keep_mask_reg = {AXIS_KEEP_WIDTH_INT{1'b0}}, keep_mask_next; +reg [OFFSET_WIDTH-1:0] last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, last_cycle_offset_next; +reg [LEN_WIDTH-1:0] length_reg = {LEN_WIDTH{1'b0}}, length_next; +reg [CYCLE_COUNT_WIDTH-1:0] cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, cycle_count_next; +reg last_cycle_reg = 1'b0, last_cycle_next; + +reg s_axis_write_desc_ready_reg = 1'b0, s_axis_write_desc_ready_next; + +reg [LEN_WIDTH-1:0] m_axis_write_desc_status_len_reg = {LEN_WIDTH{1'b0}}, m_axis_write_desc_status_len_next; +reg [TAG_WIDTH-1:0] m_axis_write_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_write_desc_status_tag_next; +reg [AXIS_ID_WIDTH-1:0] m_axis_write_desc_status_id_reg = {AXIS_ID_WIDTH{1'b0}}, m_axis_write_desc_status_id_next; +reg [AXIS_DEST_WIDTH-1:0] m_axis_write_desc_status_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, m_axis_write_desc_status_dest_next; +reg [AXIS_USER_WIDTH-1:0] m_axis_write_desc_status_user_reg = {AXIS_USER_WIDTH{1'b0}}, m_axis_write_desc_status_user_next; +reg m_axis_write_desc_status_valid_reg = 1'b0, m_axis_write_desc_status_valid_next; + +reg s_axis_write_data_tready_reg = 1'b0, s_axis_write_data_tready_next; + +// internal datapath +reg [SEG_COUNT*SEG_BE_WIDTH-1:0] ram_wr_cmd_be_int; +reg [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr_int; +reg [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_wr_cmd_data_int; +reg [SEG_COUNT-1:0] ram_wr_cmd_valid_int; +reg [SEG_COUNT-1:0] ram_wr_cmd_ready_int_reg = 1'b0; +wire [SEG_COUNT-1:0] ram_wr_cmd_ready_int_early; + +assign s_axis_write_desc_ready = s_axis_write_desc_ready_reg; + +assign m_axis_write_desc_status_len = m_axis_write_desc_status_len_reg; +assign m_axis_write_desc_status_tag = m_axis_write_desc_status_tag_reg; +assign m_axis_write_desc_status_id = m_axis_write_desc_status_id_reg; +assign m_axis_write_desc_status_dest = m_axis_write_desc_status_dest_reg; +assign m_axis_write_desc_status_user = m_axis_write_desc_status_user_reg; +assign m_axis_write_desc_status_valid = m_axis_write_desc_status_valid_reg; + +assign s_axis_write_data_tready = s_axis_write_data_tready_reg; + +always @* begin + state_next = STATE_IDLE; + + s_axis_write_desc_ready_next = 1'b0; + + m_axis_write_desc_status_len_next = m_axis_write_desc_status_len_reg; + m_axis_write_desc_status_tag_next = m_axis_write_desc_status_tag_reg; + m_axis_write_desc_status_id_next = m_axis_write_desc_status_id_reg; + m_axis_write_desc_status_dest_next = m_axis_write_desc_status_dest_reg; + m_axis_write_desc_status_user_next = m_axis_write_desc_status_user_reg; + m_axis_write_desc_status_valid_next = 1'b0; + + s_axis_write_data_tready_next = 1'b0; + + ram_wr_cmd_be_int = (s_axis_write_data_tkeep & keep_mask_reg) << (addr_reg & ({PART_COUNT_WIDTH{1'b1}} << PART_OFFSET_WIDTH)); + ram_wr_cmd_addr_int = {PART_COUNT{addr_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]}}; + ram_wr_cmd_data_int = {PART_COUNT{s_axis_write_data_tdata}}; + ram_wr_cmd_valid_int = {SEG_COUNT{1'b0}}; + + cycle_size = AXIS_KEEP_WIDTH_INT; + + addr_next = addr_reg; + ram_mask_next = ram_mask_reg; + keep_mask_next = keep_mask_reg; + last_cycle_offset_next = last_cycle_offset_reg; + length_next = length_reg; + cycle_count_next = cycle_count_reg; + last_cycle_next = last_cycle_reg; + + case (state_reg) + STATE_IDLE: begin + // idle state - load new descriptor to start operation + s_axis_write_desc_ready_next = enable; + + addr_next = s_axis_write_desc_ram_addr & ADDR_MASK; + last_cycle_offset_next = s_axis_write_desc_len & OFFSET_MASK; + + if (PART_COUNT > 1) begin + ram_mask_next = {SEGS_PER_PART{1'b1}} << ((((addr_next >> PART_OFFSET_WIDTH) & ({PART_COUNT_WIDTH{1'b1}})) / PARTS_PER_SEG) * SEGS_PER_PART); + end else begin + ram_mask_next = {SEG_COUNT{1'b1}}; + end + + m_axis_write_desc_status_tag_next = s_axis_write_desc_tag; + + length_next = 0; + + cycle_count_next = (s_axis_write_desc_len - 1) >> $clog2(AXIS_KEEP_WIDTH_INT); + last_cycle_next = cycle_count_next == 0; + if (cycle_count_next == 0 && last_cycle_offset_next != 0) begin + keep_mask_next = {AXIS_KEEP_WIDTH_INT{1'b1}} >> (AXIS_KEEP_WIDTH_INT - last_cycle_offset_next); + end else begin + keep_mask_next = {AXIS_KEEP_WIDTH_INT{1'b1}}; + end + + if (s_axis_write_desc_ready && s_axis_write_desc_valid) begin + s_axis_write_desc_ready_next = 1'b0; + s_axis_write_data_tready_next = !(~ram_wr_cmd_ready_int_early & ram_mask_next); + state_next = STATE_WRITE; + end else begin + state_next = STATE_IDLE; + end + end + STATE_WRITE: begin + // write state - generate write operations + s_axis_write_data_tready_next = !(~ram_wr_cmd_ready_int_early & ram_mask_reg); + + if (s_axis_write_data_tready && s_axis_write_data_tvalid) begin + m_axis_write_desc_status_id_next = s_axis_write_data_tid; + m_axis_write_desc_status_dest_next = s_axis_write_data_tdest; + m_axis_write_desc_status_user_next = s_axis_write_data_tuser; + + // update counters + addr_next = addr_reg + AXIS_KEEP_WIDTH_INT; + length_next = length_reg + AXIS_KEEP_WIDTH_INT; + cycle_count_next = cycle_count_reg - 1; + last_cycle_next = cycle_count_next == 0; + if (cycle_count_next == 0 && last_cycle_offset_reg != 0) begin + keep_mask_next = {AXIS_KEEP_WIDTH_INT{1'b1}} >> (AXIS_KEEP_WIDTH_INT - last_cycle_offset_reg); + end else begin + keep_mask_next = {AXIS_KEEP_WIDTH_INT{1'b1}}; + end + + if (PART_COUNT > 1) begin + ram_mask_next = {SEGS_PER_PART{1'b1}} << ((((addr_next >> PART_OFFSET_WIDTH) & ({PART_COUNT_WIDTH{1'b1}})) / PARTS_PER_SEG) * SEGS_PER_PART); + end else begin + ram_mask_next = {SEG_COUNT{1'b1}}; + end + + ram_wr_cmd_be_int = (s_axis_write_data_tkeep & keep_mask_reg) << (addr_reg & ({PART_COUNT_WIDTH{1'b1}} << PART_OFFSET_WIDTH)); + ram_wr_cmd_addr_int = {SEG_COUNT{addr_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]}}; + ram_wr_cmd_data_int = {PART_COUNT{s_axis_write_data_tdata}}; + for (i = 0; i < SEG_COUNT; i = i + 1) begin + ram_wr_cmd_valid_int[i] = ram_wr_cmd_be_int[i*SEG_BE_WIDTH +: SEG_BE_WIDTH] != 0; + end + + if (AXIS_LAST_ENABLE && s_axis_write_data_tlast) begin + if (AXIS_KEEP_ENABLE) begin + cycle_size = AXIS_KEEP_WIDTH_INT; + for (i = AXIS_KEEP_WIDTH_INT-1; i >= 0; i = i - 1) begin + if (~(s_axis_write_data_tkeep & keep_mask_reg) & (1 << i)) begin + cycle_size = i; + end + end + end else begin + cycle_size = AXIS_KEEP_WIDTH_INT; + end + + // no more data to transfer, finish operation + if (last_cycle_reg && last_cycle_offset_reg > 0) begin + if (AXIS_KEEP_ENABLE && !(s_axis_write_data_tkeep & keep_mask_reg & ~({AXIS_KEEP_WIDTH_INT{1'b1}} >> (AXIS_KEEP_WIDTH_INT - last_cycle_offset_reg)))) begin + length_next = length_reg + cycle_size; + end else begin + length_next = length_reg + last_cycle_offset_reg; + end + end else begin + if (AXIS_KEEP_ENABLE) begin + length_next = length_reg + cycle_size; + end + end + + m_axis_write_desc_status_len_next = length_next; + m_axis_write_desc_status_valid_next = 1'b1; + + s_axis_write_data_tready_next = 1'b0; + s_axis_write_desc_ready_next = enable; + state_next = STATE_IDLE; + end else if (last_cycle_reg) begin + if (last_cycle_offset_reg > 0) begin + length_next = length_reg + last_cycle_offset_reg; + end + + m_axis_write_desc_status_len_next = length_next; + m_axis_write_desc_status_valid_next = 1'b1; + + if (AXIS_LAST_ENABLE) begin + s_axis_write_data_tready_next = 1'b1; + state_next = STATE_DROP_DATA; + end else begin + s_axis_write_data_tready_next = 1'b0; + s_axis_write_desc_ready_next = enable; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_WRITE; + end + end else begin + state_next = STATE_WRITE; + end + end + STATE_DROP_DATA: begin + // drop excess AXI stream data + s_axis_write_data_tready_next = 1'b1; + + if (s_axis_write_data_tready && s_axis_write_data_tvalid) begin + if (s_axis_write_data_tlast) begin + s_axis_write_data_tready_next = 1'b0; + s_axis_write_desc_ready_next = enable; + state_next = STATE_IDLE; + end else begin + state_next = STATE_DROP_DATA; + end + end else begin + state_next = STATE_DROP_DATA; + end + end + endcase +end + +always @(posedge clk) begin + state_reg <= state_next; + + s_axis_write_desc_ready_reg <= s_axis_write_desc_ready_next; + + m_axis_write_desc_status_len_reg <= m_axis_write_desc_status_len_next; + m_axis_write_desc_status_tag_reg <= m_axis_write_desc_status_tag_next; + m_axis_write_desc_status_id_reg <= m_axis_write_desc_status_id_next; + m_axis_write_desc_status_dest_reg <= m_axis_write_desc_status_dest_next; + m_axis_write_desc_status_user_reg <= m_axis_write_desc_status_user_next; + m_axis_write_desc_status_valid_reg <= m_axis_write_desc_status_valid_next; + + s_axis_write_data_tready_reg <= s_axis_write_data_tready_next; + + addr_reg <= addr_next; + ram_mask_reg <= ram_mask_next; + keep_mask_reg <= keep_mask_next; + last_cycle_offset_reg <= last_cycle_offset_next; + length_reg <= length_next; + cycle_count_reg <= cycle_count_next; + last_cycle_reg <= last_cycle_next; + + if (rst) begin + state_reg <= STATE_IDLE; + s_axis_write_desc_ready_reg <= 1'b0; + m_axis_write_desc_status_valid_reg <= 1'b0; + s_axis_write_data_tready_reg <= 1'b0; + end +end + +// output datapath logic (write data) +generate + +genvar n; + +for (n = 0; n < SEG_COUNT; n = n + 1) begin + + reg [SEG_BE_WIDTH-1:0] ram_wr_cmd_be_reg = {SEG_BE_WIDTH{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [SEG_DATA_WIDTH-1:0] ram_wr_cmd_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg ram_wr_cmd_valid_reg = 1'b0, ram_wr_cmd_valid_next; + + reg [SEG_BE_WIDTH-1:0] temp_ram_wr_cmd_be_reg = {SEG_BE_WIDTH{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] temp_ram_wr_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [SEG_DATA_WIDTH-1:0] temp_ram_wr_cmd_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg temp_ram_wr_cmd_valid_reg = 1'b0, temp_ram_wr_cmd_valid_next; + + // datapath control + reg store_axi_w_int_to_output; + reg store_axi_w_int_to_temp; + reg store_axi_w_temp_to_output; + + assign ram_wr_cmd_be[n*SEG_BE_WIDTH +: SEG_BE_WIDTH] = ram_wr_cmd_be_reg; + assign ram_wr_cmd_addr[n*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = ram_wr_cmd_addr_reg; + assign ram_wr_cmd_data[n*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] = ram_wr_cmd_data_reg; + assign ram_wr_cmd_valid[n +: 1] = ram_wr_cmd_valid_reg; + + // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) + assign ram_wr_cmd_ready_int_early[n +: 1] = ram_wr_cmd_ready[n +: 1] || (!temp_ram_wr_cmd_valid_reg && (!ram_wr_cmd_valid_reg || !ram_wr_cmd_valid_int[n +: 1])); + + always @* begin + // transfer sink ready state to source + ram_wr_cmd_valid_next = ram_wr_cmd_valid_reg; + temp_ram_wr_cmd_valid_next = temp_ram_wr_cmd_valid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (ram_wr_cmd_ready_int_reg[n +: 1]) begin + // input is ready + if (ram_wr_cmd_ready[n +: 1] || !ram_wr_cmd_valid_reg) begin + // output is ready or currently not valid, transfer data to output + ram_wr_cmd_valid_next = ram_wr_cmd_valid_int[n +: 1]; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_ram_wr_cmd_valid_next = ram_wr_cmd_valid_int[n +: 1]; + store_axi_w_int_to_temp = 1'b1; + end + end else if (ram_wr_cmd_ready[n +: 1]) begin + // input is not ready, but output is ready + ram_wr_cmd_valid_next = temp_ram_wr_cmd_valid_reg; + temp_ram_wr_cmd_valid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + ram_wr_cmd_valid_reg <= 1'b0; + ram_wr_cmd_ready_int_reg[n +: 1] <= 1'b0; + temp_ram_wr_cmd_valid_reg <= 1'b0; + end else begin + ram_wr_cmd_valid_reg <= ram_wr_cmd_valid_next; + ram_wr_cmd_ready_int_reg[n +: 1] <= ram_wr_cmd_ready_int_early[n +: 1]; + temp_ram_wr_cmd_valid_reg <= temp_ram_wr_cmd_valid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + ram_wr_cmd_be_reg <= ram_wr_cmd_be_int[n*SEG_BE_WIDTH +: SEG_BE_WIDTH]; + ram_wr_cmd_addr_reg <= ram_wr_cmd_addr_int[n*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH]; + ram_wr_cmd_data_reg <= ram_wr_cmd_data_int[n*SEG_DATA_WIDTH +: SEG_DATA_WIDTH]; + end else if (store_axi_w_temp_to_output) begin + ram_wr_cmd_be_reg <= temp_ram_wr_cmd_be_reg; + ram_wr_cmd_addr_reg <= temp_ram_wr_cmd_addr_reg; + ram_wr_cmd_data_reg <= temp_ram_wr_cmd_data_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_ram_wr_cmd_be_reg <= ram_wr_cmd_be_int[n*SEG_BE_WIDTH +: SEG_BE_WIDTH]; + temp_ram_wr_cmd_addr_reg <= ram_wr_cmd_addr_int[n*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH]; + temp_ram_wr_cmd_data_reg <= ram_wr_cmd_data_int[n*SEG_DATA_WIDTH +: SEG_DATA_WIDTH]; + end + end + +end + +endgenerate + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_client_axis_source.v b/corundum/lib/pcie/rtl/dma_client_axis_source.v new file mode 100644 index 0000000000000000000000000000000000000000..ef8009bbf6e9c46964d05533bb7e46eea165a8d6 --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_client_axis_source.v @@ -0,0 +1,580 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * AXI stream source DMA client + */ +module dma_client_axis_source # +( + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // Width of AXI stream interfaces in bits + parameter AXIS_DATA_WIDTH = SEG_DATA_WIDTH*SEG_COUNT/2, + // Use AXI stream tkeep signal + parameter AXIS_KEEP_ENABLE = (AXIS_DATA_WIDTH>8), + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = (AXIS_DATA_WIDTH/8), + // Use AXI stream tlast signal + parameter AXIS_LAST_ENABLE = 1, + // Propagate AXI stream tid signal + parameter AXIS_ID_ENABLE = 0, + // AXI stream tid signal width + parameter AXIS_ID_WIDTH = 8, + // Propagate AXI stream tdest signal + parameter AXIS_DEST_ENABLE = 0, + // AXI stream tdest signal width + parameter AXIS_DEST_WIDTH = 8, + // Propagate AXI stream tuser signal + parameter AXIS_USER_ENABLE = 1, + // AXI stream tuser signal width + parameter AXIS_USER_WIDTH = 1, + // Width of length field + parameter LEN_WIDTH = 16, + // Width of tag field + parameter TAG_WIDTH = 8 +) +( + input wire clk, + input wire rst, + + /* + * AXI read descriptor input + */ + input wire [RAM_ADDR_WIDTH-1:0] s_axis_read_desc_ram_addr, + input wire [LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire [AXIS_ID_WIDTH-1:0] s_axis_read_desc_id, + input wire [AXIS_DEST_WIDTH-1:0] s_axis_read_desc_dest, + input wire [AXIS_USER_WIDTH-1:0] s_axis_read_desc_user, + input wire s_axis_read_desc_valid, + output wire s_axis_read_desc_ready, + + /* + * AXI read descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire m_axis_read_desc_status_valid, + + /* + * AXI stream read data output + */ + output wire [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep, + output wire m_axis_read_data_tvalid, + input wire m_axis_read_data_tready, + output wire m_axis_read_data_tlast, + output wire [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid, + output wire [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest, + output wire [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser, + + /* + * RAM interface + */ + output wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_rd_cmd_addr, + output wire [SEG_COUNT-1:0] ram_rd_cmd_valid, + input wire [SEG_COUNT-1:0] ram_rd_cmd_ready, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_rd_resp_data, + input wire [SEG_COUNT-1:0] ram_rd_resp_valid, + output wire [SEG_COUNT-1:0] ram_rd_resp_ready, + + /* + * Configuration + */ + input wire enable +); + +parameter RAM_WORD_WIDTH = SEG_BE_WIDTH; +parameter RAM_WORD_SIZE = SEG_DATA_WIDTH/RAM_WORD_WIDTH; + +parameter AXIS_KEEP_WIDTH_INT = AXIS_KEEP_ENABLE ? AXIS_KEEP_WIDTH : 1; +parameter AXIS_WORD_WIDTH = AXIS_KEEP_WIDTH_INT; +parameter AXIS_WORD_SIZE = AXIS_DATA_WIDTH/AXIS_WORD_WIDTH; + +parameter PART_COUNT = SEG_COUNT*SEG_BE_WIDTH / AXIS_KEEP_WIDTH_INT; +parameter PART_COUNT_WIDTH = PART_COUNT > 1 ? $clog2(PART_COUNT) : 1; +parameter PART_OFFSET_WIDTH = AXIS_KEEP_WIDTH_INT > 1 ? $clog2(AXIS_KEEP_WIDTH_INT) : 1; +parameter PARTS_PER_SEG = (SEG_BE_WIDTH + AXIS_KEEP_WIDTH_INT - 1) / AXIS_KEEP_WIDTH_INT; +parameter SEGS_PER_PART = (AXIS_KEEP_WIDTH_INT + SEG_BE_WIDTH - 1) / SEG_BE_WIDTH; + +parameter OFFSET_WIDTH = AXIS_KEEP_WIDTH_INT > 1 ? $clog2(AXIS_KEEP_WIDTH_INT) : 1; +parameter OFFSET_MASK = AXIS_KEEP_WIDTH_INT > 1 ? {OFFSET_WIDTH{1'b1}} : 0; +parameter ADDR_MASK = {RAM_ADDR_WIDTH{1'b1}} << $clog2(AXIS_KEEP_WIDTH_INT); +parameter CYCLE_COUNT_WIDTH = LEN_WIDTH - $clog2(AXIS_KEEP_WIDTH_INT) + 1; + +// bus width assertions +initial begin + if (RAM_WORD_SIZE * SEG_BE_WIDTH != SEG_DATA_WIDTH) begin + $error("Error: RAM data width not evenly divisble (instance %m)"); + $finish; + end + + if (AXIS_WORD_SIZE * AXIS_KEEP_WIDTH_INT != AXIS_DATA_WIDTH) begin + $error("Error: AXI stream data width not evenly divisble (instance %m)"); + $finish; + end + + if (RAM_WORD_SIZE != AXIS_WORD_SIZE) begin + $error("Error: word size mismatch (instance %m)"); + $finish; + end + + if (2**$clog2(RAM_WORD_WIDTH) != RAM_WORD_WIDTH) begin + $error("Error: RAM word width must be even power of two (instance %m)"); + $finish; + end + + if (RAM_ADDR_WIDTH != SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH)) begin + $error("Error: RAM_ADDR_WIDTH does not match RAM configuration (instance %m)"); + $finish; + end + + if (AXIS_DATA_WIDTH > SEG_COUNT*SEG_DATA_WIDTH) begin + $error("Error: AXI stream interface width must not be wider than RAM interface width (instance %m)"); + $finish; + end + + if (AXIS_DATA_WIDTH*2**PART_COUNT_WIDTH != SEG_COUNT*SEG_DATA_WIDTH) begin + $error("Error: AXI stream interface width must be a power of two fraction of RAM interface width (instance %m)"); + $finish; + end +end + +localparam [0:0] + READ_STATE_IDLE = 1'd0, + READ_STATE_READ = 1'd1; + +reg [0:0] read_state_reg = READ_STATE_IDLE, read_state_next; + +localparam [0:0] + AXIS_STATE_IDLE = 1'd0, + AXIS_STATE_READ = 1'd1; + +reg [0:0] axis_state_reg = AXIS_STATE_IDLE, axis_state_next; + +// datapath control signals +reg axis_cmd_ready; + +integer i; + +reg [RAM_ADDR_WIDTH-1:0] read_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, read_addr_next; +reg [SEG_COUNT-1:0] read_ram_mask_reg = 0, read_ram_mask_next; +reg [CYCLE_COUNT_WIDTH-1:0] read_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, read_cycle_count_next; + +reg [RAM_ADDR_WIDTH-1:0] axis_cmd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, axis_cmd_addr_next; +reg [OFFSET_WIDTH-1:0] axis_cmd_last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, axis_cmd_last_cycle_offset_next; +reg [CYCLE_COUNT_WIDTH-1:0] axis_cmd_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, axis_cmd_cycle_count_next; +reg [TAG_WIDTH-1:0] axis_cmd_tag_reg = {TAG_WIDTH{1'b0}}, axis_cmd_tag_next; +reg [AXIS_ID_WIDTH-1:0] axis_cmd_axis_id_reg = {AXIS_ID_WIDTH{1'b0}}, axis_cmd_axis_id_next; +reg [AXIS_DEST_WIDTH-1:0] axis_cmd_axis_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, axis_cmd_axis_dest_next; +reg [AXIS_USER_WIDTH-1:0] axis_cmd_axis_user_reg = {AXIS_USER_WIDTH{1'b0}}, axis_cmd_axis_user_next; +reg axis_cmd_valid_reg = 1'b0, axis_cmd_valid_next; + +reg [RAM_ADDR_WIDTH-1:0] addr_reg = {RAM_ADDR_WIDTH{1'b0}}, addr_next; +reg [SEG_COUNT-1:0] ram_mask_reg = 0, ram_mask_next; +reg [OFFSET_WIDTH-1:0] last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, last_cycle_offset_next; +reg [CYCLE_COUNT_WIDTH-1:0] cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, cycle_count_next; +reg last_cycle_reg = 1'b0, last_cycle_next; + +reg [AXIS_ID_WIDTH-1:0] axis_id_reg = {AXIS_ID_WIDTH{1'b0}}, axis_id_next; +reg [AXIS_DEST_WIDTH-1:0] axis_dest_reg = {AXIS_DEST_WIDTH{1'b0}}, axis_dest_next; +reg [AXIS_USER_WIDTH-1:0] axis_user_reg = {AXIS_USER_WIDTH{1'b0}}, axis_user_next; + +reg s_axis_read_desc_ready_reg = 1'b0, s_axis_read_desc_ready_next; + +reg [TAG_WIDTH-1:0] m_axis_read_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_read_desc_status_tag_next; +reg m_axis_read_desc_status_valid_reg = 1'b0, m_axis_read_desc_status_valid_next; + +reg [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_rd_cmd_addr_reg = 0, ram_rd_cmd_addr_next; +reg [SEG_COUNT-1:0] ram_rd_cmd_valid_reg = 0, ram_rd_cmd_valid_next; +reg [SEG_COUNT-1:0] ram_rd_resp_ready_cmb; + +// internal datapath +reg [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata_int; +reg [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep_int; +reg m_axis_read_data_tvalid_int; +reg m_axis_read_data_tready_int_reg = 1'b0; +reg m_axis_read_data_tlast_int; +reg [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid_int; +reg [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest_int; +reg [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser_int; +wire m_axis_read_data_tready_int_early; + +assign s_axis_read_desc_ready = s_axis_read_desc_ready_reg; + +assign m_axis_read_desc_status_tag = m_axis_read_desc_status_tag_reg; +assign m_axis_read_desc_status_valid = m_axis_read_desc_status_valid_reg; + +assign ram_rd_cmd_addr = ram_rd_cmd_addr_reg; +assign ram_rd_cmd_valid = ram_rd_cmd_valid_reg; +assign ram_rd_resp_ready = ram_rd_resp_ready_cmb; + +always @* begin + read_state_next = READ_STATE_IDLE; + + s_axis_read_desc_ready_next = 1'b0; + + ram_rd_cmd_addr_next = ram_rd_cmd_addr_reg; + ram_rd_cmd_valid_next = ram_rd_cmd_valid_reg & ~ram_rd_cmd_ready; + + read_addr_next = read_addr_reg; + read_ram_mask_next = read_ram_mask_reg; + read_cycle_count_next = read_cycle_count_reg; + + axis_cmd_addr_next = axis_cmd_addr_reg; + axis_cmd_last_cycle_offset_next = axis_cmd_last_cycle_offset_reg; + axis_cmd_cycle_count_next = axis_cmd_cycle_count_reg; + axis_cmd_tag_next = axis_cmd_tag_reg; + axis_cmd_axis_id_next = axis_cmd_axis_id_reg; + axis_cmd_axis_dest_next = axis_cmd_axis_dest_reg; + axis_cmd_axis_user_next = axis_cmd_axis_user_reg; + axis_cmd_valid_next = axis_cmd_valid_reg && !axis_cmd_ready; + + case (read_state_reg) + READ_STATE_IDLE: begin + // idle state - load new descriptor to start operation + s_axis_read_desc_ready_next = !axis_cmd_valid_reg && enable; + + if (s_axis_read_desc_ready && s_axis_read_desc_valid) begin + + read_addr_next = s_axis_read_desc_ram_addr & ADDR_MASK; + + if (PART_COUNT > 1) begin + read_ram_mask_next = {SEGS_PER_PART{1'b1}} << ((((read_addr_next >> PART_OFFSET_WIDTH) & ({PART_COUNT_WIDTH{1'b1}})) / PARTS_PER_SEG) * SEGS_PER_PART); + end else begin + read_ram_mask_next = {SEG_COUNT{1'b1}}; + end + + axis_cmd_addr_next = s_axis_read_desc_ram_addr & ADDR_MASK; + axis_cmd_last_cycle_offset_next = s_axis_read_desc_len & OFFSET_MASK; + + axis_cmd_tag_next = s_axis_read_desc_tag; + + axis_cmd_axis_id_next = s_axis_read_desc_id; + axis_cmd_axis_dest_next = s_axis_read_desc_dest; + axis_cmd_axis_user_next = s_axis_read_desc_user; + + axis_cmd_cycle_count_next = (s_axis_read_desc_len - 1) >> $clog2(AXIS_KEEP_WIDTH_INT); + read_cycle_count_next = (s_axis_read_desc_len - 1) >> $clog2(AXIS_KEEP_WIDTH_INT); + + axis_cmd_valid_next = 1'b1; + + s_axis_read_desc_ready_next = 1'b0; + read_state_next = READ_STATE_READ; + end else begin + read_state_next = READ_STATE_IDLE; + end + end + READ_STATE_READ: begin + // read state - start new read operations + + if (!(ram_rd_cmd_valid & ~ram_rd_cmd_ready & read_ram_mask_reg)) begin + + // update counters + read_addr_next = read_addr_reg + AXIS_KEEP_WIDTH_INT; + read_cycle_count_next = read_cycle_count_reg - 1; + + if (PART_COUNT > 1) begin + read_ram_mask_next = {SEGS_PER_PART{1'b1}} << ((((read_addr_next >> PART_OFFSET_WIDTH) & ({PART_COUNT_WIDTH{1'b1}})) / PARTS_PER_SEG) * SEGS_PER_PART); + end else begin + read_ram_mask_next = {SEG_COUNT{1'b1}}; + end + + for (i = 0; i < SEG_COUNT; i = i + 1) begin + if (read_ram_mask_reg[i]) begin + ram_rd_cmd_addr_next[i*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = read_addr_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]; + ram_rd_cmd_valid_next[i] = 1'b1; + end + end + + if (read_cycle_count_reg == 0) begin + s_axis_read_desc_ready_next = !axis_cmd_valid_reg && enable; + read_state_next = READ_STATE_IDLE; + end else begin + read_state_next = READ_STATE_READ; + end + end else begin + read_state_next = READ_STATE_READ; + end + end + endcase +end + +always @* begin + axis_state_next = AXIS_STATE_IDLE; + + m_axis_read_desc_status_tag_next = m_axis_read_desc_status_tag_reg; + m_axis_read_desc_status_valid_next = 1'b0; + + m_axis_read_data_tdata_int = ram_rd_resp_data >> (((addr_reg >> PART_OFFSET_WIDTH) & {PART_COUNT_WIDTH{1'b1}}) * AXIS_DATA_WIDTH); + m_axis_read_data_tkeep_int = {AXIS_KEEP_WIDTH{1'b1}}; + m_axis_read_data_tlast_int = 1'b0; + m_axis_read_data_tvalid_int = 1'b0; + m_axis_read_data_tid_int = axis_id_reg; + m_axis_read_data_tdest_int = axis_dest_reg; + m_axis_read_data_tuser_int = axis_user_reg; + + ram_rd_resp_ready_cmb = {SEG_COUNT{1'b0}}; + + axis_cmd_ready = 1'b0; + + addr_next = addr_reg; + ram_mask_next = ram_mask_reg; + last_cycle_offset_next = last_cycle_offset_reg; + cycle_count_next = cycle_count_reg; + last_cycle_next = last_cycle_reg; + + axis_id_next = axis_id_reg; + axis_dest_next = axis_dest_reg; + axis_user_next = axis_user_reg; + + case (axis_state_reg) + AXIS_STATE_IDLE: begin + // idle state - load new descriptor to start operation + + // store transfer parameters + addr_next = axis_cmd_addr_reg; + last_cycle_offset_next = axis_cmd_last_cycle_offset_reg; + cycle_count_next = axis_cmd_cycle_count_reg; + last_cycle_next = axis_cmd_cycle_count_reg == 0; + + if (PART_COUNT > 1) begin + ram_mask_next = {SEGS_PER_PART{1'b1}} << ((((addr_next >> PART_OFFSET_WIDTH) & ({PART_COUNT_WIDTH{1'b1}})) / PARTS_PER_SEG) * SEGS_PER_PART); + end else begin + ram_mask_next = {SEG_COUNT{1'b1}}; + end + + m_axis_read_desc_status_tag_next = axis_cmd_tag_reg; + axis_id_next = axis_cmd_axis_id_reg; + axis_dest_next = axis_cmd_axis_dest_reg; + axis_user_next = axis_cmd_axis_user_reg; + + if (axis_cmd_valid_reg) begin + axis_cmd_ready = 1'b1; + axis_state_next = AXIS_STATE_READ; + end + end + AXIS_STATE_READ: begin + // handle read data + ram_rd_resp_ready_cmb = {SEG_COUNT{1'b0}}; + + if (!(ram_mask_reg & ~ram_rd_resp_valid) && m_axis_read_data_tready_int_reg) begin + // transfer in read data + ram_rd_resp_ready_cmb = ram_mask_reg; + + // update counters + addr_next = addr_reg + AXIS_KEEP_WIDTH_INT; + cycle_count_next = cycle_count_reg - 1; + last_cycle_next = cycle_count_next == 0; + + if (PART_COUNT > 1) begin + ram_mask_next = {SEGS_PER_PART{1'b1}} << ((((addr_next >> PART_OFFSET_WIDTH) & ({PART_COUNT_WIDTH{1'b1}})) / PARTS_PER_SEG) * SEGS_PER_PART); + end else begin + ram_mask_next = {SEG_COUNT{1'b1}}; + end + + m_axis_read_data_tdata_int = ram_rd_resp_data >> (((addr_reg >> PART_OFFSET_WIDTH) & {PART_COUNT_WIDTH{1'b1}}) * AXIS_DATA_WIDTH); + m_axis_read_data_tkeep_int = {AXIS_KEEP_WIDTH_INT{1'b1}}; + m_axis_read_data_tvalid_int = 1'b1; + + if (last_cycle_reg) begin + // no more data to transfer, finish operation + if (last_cycle_offset_reg > 0) begin + m_axis_read_data_tkeep_int = {AXIS_KEEP_WIDTH_INT{1'b1}} >> (AXIS_KEEP_WIDTH_INT - last_cycle_offset_reg); + end + m_axis_read_data_tlast_int = 1'b1; + + m_axis_read_desc_status_valid_next = 1'b1; + + axis_state_next = AXIS_STATE_IDLE; + end else begin + // more cycles in AXI transfer + axis_state_next = AXIS_STATE_READ; + end + end else begin + axis_state_next = AXIS_STATE_READ; + end + end + endcase +end + +always @(posedge clk) begin + read_state_reg <= read_state_next; + axis_state_reg <= axis_state_next; + + s_axis_read_desc_ready_reg <= s_axis_read_desc_ready_next; + + m_axis_read_desc_status_tag_reg <= m_axis_read_desc_status_tag_next; + m_axis_read_desc_status_valid_reg <= m_axis_read_desc_status_valid_next; + + ram_rd_cmd_addr_reg <= ram_rd_cmd_addr_next; + ram_rd_cmd_valid_reg <= ram_rd_cmd_valid_next; + + read_addr_reg <= read_addr_next; + read_ram_mask_reg <= read_ram_mask_next; + read_cycle_count_reg <= read_cycle_count_next; + + axis_cmd_addr_reg <= axis_cmd_addr_next; + axis_cmd_last_cycle_offset_reg <= axis_cmd_last_cycle_offset_next; + axis_cmd_cycle_count_reg <= axis_cmd_cycle_count_next; + axis_cmd_tag_reg <= axis_cmd_tag_next; + axis_cmd_axis_id_reg <= axis_cmd_axis_id_next; + axis_cmd_axis_dest_reg <= axis_cmd_axis_dest_next; + axis_cmd_axis_user_reg <= axis_cmd_axis_user_next; + axis_cmd_valid_reg <= axis_cmd_valid_next; + + addr_reg <= addr_next; + ram_mask_reg <= ram_mask_next; + last_cycle_offset_reg <= last_cycle_offset_next; + cycle_count_reg <= cycle_count_next; + last_cycle_reg <= last_cycle_next; + + axis_id_reg <= axis_id_next; + axis_dest_reg <= axis_dest_next; + axis_user_reg <= axis_user_next; + + if (rst) begin + read_state_reg <= READ_STATE_IDLE; + axis_state_reg <= AXIS_STATE_IDLE; + + axis_cmd_valid_reg <= 1'b0; + + s_axis_read_desc_ready_reg <= 1'b0; + m_axis_read_desc_status_valid_reg <= 1'b0; + + ram_rd_cmd_valid_reg <= {SEG_COUNT{1'b0}}; + end +end + +// output datapath logic +reg [AXIS_DATA_WIDTH-1:0] m_axis_read_data_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; +reg [AXIS_KEEP_WIDTH-1:0] m_axis_read_data_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; +reg m_axis_read_data_tvalid_reg = 1'b0, m_axis_read_data_tvalid_next; +reg m_axis_read_data_tlast_reg = 1'b0; +reg [AXIS_ID_WIDTH-1:0] m_axis_read_data_tid_reg = {AXIS_ID_WIDTH{1'b0}}; +reg [AXIS_DEST_WIDTH-1:0] m_axis_read_data_tdest_reg = {AXIS_DEST_WIDTH{1'b0}}; +reg [AXIS_USER_WIDTH-1:0] m_axis_read_data_tuser_reg = {AXIS_USER_WIDTH{1'b0}}; + +reg [AXIS_DATA_WIDTH-1:0] temp_m_axis_read_data_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; +reg [AXIS_KEEP_WIDTH-1:0] temp_m_axis_read_data_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_read_data_tvalid_reg = 1'b0, temp_m_axis_read_data_tvalid_next; +reg temp_m_axis_read_data_tlast_reg = 1'b0; +reg [AXIS_ID_WIDTH-1:0] temp_m_axis_read_data_tid_reg = {AXIS_ID_WIDTH{1'b0}}; +reg [AXIS_DEST_WIDTH-1:0] temp_m_axis_read_data_tdest_reg = {AXIS_DEST_WIDTH{1'b0}}; +reg [AXIS_USER_WIDTH-1:0] temp_m_axis_read_data_tuser_reg = {AXIS_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_read_data_tdata = m_axis_read_data_tdata_reg; +assign m_axis_read_data_tkeep = AXIS_KEEP_ENABLE ? m_axis_read_data_tkeep_reg : {AXIS_KEEP_WIDTH{1'b1}}; +assign m_axis_read_data_tvalid = m_axis_read_data_tvalid_reg; +assign m_axis_read_data_tlast = AXIS_LAST_ENABLE ? m_axis_read_data_tlast_reg : 1'b1; +assign m_axis_read_data_tid = AXIS_ID_ENABLE ? m_axis_read_data_tid_reg : {AXIS_ID_WIDTH{1'b0}}; +assign m_axis_read_data_tdest = AXIS_DEST_ENABLE ? m_axis_read_data_tdest_reg : {AXIS_DEST_WIDTH{1'b0}}; +assign m_axis_read_data_tuser = AXIS_USER_ENABLE ? m_axis_read_data_tuser_reg : {AXIS_USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_read_data_tready_int_early = m_axis_read_data_tready || (!temp_m_axis_read_data_tvalid_reg && (!m_axis_read_data_tvalid_reg || !m_axis_read_data_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_reg; + temp_m_axis_read_data_tvalid_next = temp_m_axis_read_data_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_read_data_tready_int_reg) begin + // input is ready + if (m_axis_read_data_tready || !m_axis_read_data_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_read_data_tvalid_next = m_axis_read_data_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_read_data_tready) begin + // input is not ready, but output is ready + m_axis_read_data_tvalid_next = temp_m_axis_read_data_tvalid_reg; + temp_m_axis_read_data_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_read_data_tvalid_reg <= 1'b0; + m_axis_read_data_tready_int_reg <= 1'b0; + temp_m_axis_read_data_tvalid_reg <= 1'b0; + end else begin + m_axis_read_data_tvalid_reg <= m_axis_read_data_tvalid_next; + m_axis_read_data_tready_int_reg <= m_axis_read_data_tready_int_early; + temp_m_axis_read_data_tvalid_reg <= temp_m_axis_read_data_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_read_data_tdata_reg <= m_axis_read_data_tdata_int; + m_axis_read_data_tkeep_reg <= m_axis_read_data_tkeep_int; + m_axis_read_data_tlast_reg <= m_axis_read_data_tlast_int; + m_axis_read_data_tid_reg <= m_axis_read_data_tid_int; + m_axis_read_data_tdest_reg <= m_axis_read_data_tdest_int; + m_axis_read_data_tuser_reg <= m_axis_read_data_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_read_data_tdata_reg <= temp_m_axis_read_data_tdata_reg; + m_axis_read_data_tkeep_reg <= temp_m_axis_read_data_tkeep_reg; + m_axis_read_data_tlast_reg <= temp_m_axis_read_data_tlast_reg; + m_axis_read_data_tid_reg <= temp_m_axis_read_data_tid_reg; + m_axis_read_data_tdest_reg <= temp_m_axis_read_data_tdest_reg; + m_axis_read_data_tuser_reg <= temp_m_axis_read_data_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_read_data_tdata_reg <= m_axis_read_data_tdata_int; + temp_m_axis_read_data_tkeep_reg <= m_axis_read_data_tkeep_int; + temp_m_axis_read_data_tlast_reg <= m_axis_read_data_tlast_int; + temp_m_axis_read_data_tid_reg <= m_axis_read_data_tid_int; + temp_m_axis_read_data_tdest_reg <= m_axis_read_data_tdest_int; + temp_m_axis_read_data_tuser_reg <= m_axis_read_data_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_if_mux.v b/corundum/lib/pcie/rtl/dma_if_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..85caeaac34485994b2773452bc7cb7fa8e7f66c9 --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_if_mux.v @@ -0,0 +1,324 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * DMA interface mux + */ +module dma_if_mux # +( + // Number of ports + parameter PORTS = 2, + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // Input RAM segment select width + parameter S_RAM_SEL_WIDTH = 2, + // Output RAM segment select width + // Additional bits required for response routing + parameter M_RAM_SEL_WIDTH = S_RAM_SEL_WIDTH+$clog2(PORTS), + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // Length field width + parameter LEN_WIDTH = 16, + // Input tag field width + parameter S_TAG_WIDTH = 8, + // Output tag field width (towards DMA module) + // Additional bits required for response routing + parameter M_TAG_WIDTH = S_TAG_WIDTH+$clog2(PORTS), + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Read descriptor output (to DMA interface) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_read_desc_dma_addr, + output wire [M_RAM_SEL_WIDTH-1:0] m_axis_read_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_read_desc_ram_addr, + output wire [LEN_WIDTH-1:0] m_axis_read_desc_len, + output wire [M_TAG_WIDTH-1:0] m_axis_read_desc_tag, + output wire m_axis_read_desc_valid, + input wire m_axis_read_desc_ready, + + /* + * Read descriptor status input (from DMA interface) + */ + input wire [M_TAG_WIDTH-1:0] s_axis_read_desc_status_tag, + input wire s_axis_read_desc_status_valid, + + /* + * Write descriptor output (to DMA interface) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_write_desc_dma_addr, + output wire [M_RAM_SEL_WIDTH-1:0] m_axis_write_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_write_desc_ram_addr, + output wire [LEN_WIDTH-1:0] m_axis_write_desc_len, + output wire [M_TAG_WIDTH-1:0] m_axis_write_desc_tag, + output wire m_axis_write_desc_valid, + input wire m_axis_write_desc_ready, + + /* + * Write descriptor status input (from DMA interface) + */ + input wire [M_TAG_WIDTH-1:0] s_axis_write_desc_status_tag, + input wire s_axis_write_desc_status_valid, + + /* + * Read descriptor input + */ + input wire [PORTS*DMA_ADDR_WIDTH-1:0] s_axis_read_desc_dma_addr, + input wire [PORTS*S_RAM_SEL_WIDTH-1:0] s_axis_read_desc_ram_sel, + input wire [PORTS*RAM_ADDR_WIDTH-1:0] s_axis_read_desc_ram_addr, + input wire [PORTS*LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [PORTS*S_TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire [PORTS-1:0] s_axis_read_desc_valid, + output wire [PORTS-1:0] s_axis_read_desc_ready, + + /* + * Read descriptor status output + */ + output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire [PORTS-1:0] m_axis_read_desc_status_valid, + + /* + * Write descriptor input + */ + input wire [PORTS*DMA_ADDR_WIDTH-1:0] s_axis_write_desc_dma_addr, + input wire [PORTS*S_RAM_SEL_WIDTH-1:0] s_axis_write_desc_ram_sel, + input wire [PORTS*RAM_ADDR_WIDTH-1:0] s_axis_write_desc_ram_addr, + input wire [PORTS*LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [PORTS*S_TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire [PORTS-1:0] s_axis_write_desc_valid, + output wire [PORTS-1:0] s_axis_write_desc_ready, + + /* + * Write descriptor status output + */ + output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire [PORTS-1:0] m_axis_write_desc_status_valid, + + /* + * RAM interface (from DMA interface) + */ + input wire [SEG_COUNT*M_RAM_SEL_WIDTH-1:0] if_ram_wr_cmd_sel, + input wire [SEG_COUNT*SEG_BE_WIDTH-1:0] if_ram_wr_cmd_be, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] if_ram_wr_cmd_addr, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] if_ram_wr_cmd_data, + input wire [SEG_COUNT-1:0] if_ram_wr_cmd_valid, + output wire [SEG_COUNT-1:0] if_ram_wr_cmd_ready, + input wire [SEG_COUNT*M_RAM_SEL_WIDTH-1:0] if_ram_rd_cmd_sel, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] if_ram_rd_cmd_addr, + input wire [SEG_COUNT-1:0] if_ram_rd_cmd_valid, + output wire [SEG_COUNT-1:0] if_ram_rd_cmd_ready, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] if_ram_rd_resp_data, + output wire [SEG_COUNT-1:0] if_ram_rd_resp_valid, + input wire [SEG_COUNT-1:0] if_ram_rd_resp_ready, + + /* + * RAM interface + */ + output wire [PORTS*SEG_COUNT*S_RAM_SEL_WIDTH-1:0] ram_wr_cmd_sel, + output wire [PORTS*SEG_COUNT*SEG_BE_WIDTH-1:0] ram_wr_cmd_be, + output wire [PORTS*SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr, + output wire [PORTS*SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_wr_cmd_data, + output wire [PORTS*SEG_COUNT-1:0] ram_wr_cmd_valid, + input wire [PORTS*SEG_COUNT-1:0] ram_wr_cmd_ready, + output wire [PORTS*SEG_COUNT*S_RAM_SEL_WIDTH-1:0] ram_rd_cmd_sel, + output wire [PORTS*SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_rd_cmd_addr, + output wire [PORTS*SEG_COUNT-1:0] ram_rd_cmd_valid, + input wire [PORTS*SEG_COUNT-1:0] ram_rd_cmd_ready, + input wire [PORTS*SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_rd_resp_data, + input wire [PORTS*SEG_COUNT-1:0] ram_rd_resp_valid, + output wire [PORTS*SEG_COUNT-1:0] ram_rd_resp_ready +); + +dma_if_mux_rd #( + .PORTS(PORTS), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .S_RAM_SEL_WIDTH(S_RAM_SEL_WIDTH), + .M_RAM_SEL_WIDTH(M_RAM_SEL_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .LEN_WIDTH(LEN_WIDTH), + .S_TAG_WIDTH(S_TAG_WIDTH), + .M_TAG_WIDTH(M_TAG_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +dma_if_mux_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * Descriptor output (to DMA interface) + */ + .m_axis_read_desc_dma_addr(m_axis_read_desc_dma_addr), + .m_axis_read_desc_ram_sel(m_axis_read_desc_ram_sel), + .m_axis_read_desc_ram_addr(m_axis_read_desc_ram_addr), + .m_axis_read_desc_len(m_axis_read_desc_len), + .m_axis_read_desc_tag(m_axis_read_desc_tag), + .m_axis_read_desc_valid(m_axis_read_desc_valid), + .m_axis_read_desc_ready(m_axis_read_desc_ready), + + /* + * Descriptor status input (from DMA interface) + */ + .s_axis_read_desc_status_tag(s_axis_read_desc_status_tag), + .s_axis_read_desc_status_valid(s_axis_read_desc_status_valid), + + /* + * Descriptor input + */ + .s_axis_read_desc_dma_addr(s_axis_read_desc_dma_addr), + .s_axis_read_desc_ram_sel(s_axis_read_desc_ram_sel), + .s_axis_read_desc_ram_addr(s_axis_read_desc_ram_addr), + .s_axis_read_desc_len(s_axis_read_desc_len), + .s_axis_read_desc_tag(s_axis_read_desc_tag), + .s_axis_read_desc_valid(s_axis_read_desc_valid), + .s_axis_read_desc_ready(s_axis_read_desc_ready), + + /* + * Descriptor status output + */ + .m_axis_read_desc_status_tag(m_axis_read_desc_status_tag), + .m_axis_read_desc_status_valid(m_axis_read_desc_status_valid), + + /* + * RAM interface (from DMA interface) + */ + .if_ram_wr_cmd_sel(if_ram_wr_cmd_sel), + .if_ram_wr_cmd_be(if_ram_wr_cmd_be), + .if_ram_wr_cmd_addr(if_ram_wr_cmd_addr), + .if_ram_wr_cmd_data(if_ram_wr_cmd_data), + .if_ram_wr_cmd_valid(if_ram_wr_cmd_valid), + .if_ram_wr_cmd_ready(if_ram_wr_cmd_ready), + + /* + * RAM interface + */ + .ram_wr_cmd_sel(ram_wr_cmd_sel), + .ram_wr_cmd_be(ram_wr_cmd_be), + .ram_wr_cmd_addr(ram_wr_cmd_addr), + .ram_wr_cmd_data(ram_wr_cmd_data), + .ram_wr_cmd_valid(ram_wr_cmd_valid), + .ram_wr_cmd_ready(ram_wr_cmd_ready) +); + +dma_if_mux_wr #( + .PORTS(PORTS), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .S_RAM_SEL_WIDTH(S_RAM_SEL_WIDTH), + .M_RAM_SEL_WIDTH(M_RAM_SEL_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .LEN_WIDTH(LEN_WIDTH), + .S_TAG_WIDTH(S_TAG_WIDTH), + .M_TAG_WIDTH(M_TAG_WIDTH), + .ARB_TYPE(ARB_TYPE), + .LSB_PRIORITY(LSB_PRIORITY) +) +dma_if_mux_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * Descriptor output (to DMA interface) + */ + .m_axis_write_desc_dma_addr(m_axis_write_desc_dma_addr), + .m_axis_write_desc_ram_sel(m_axis_write_desc_ram_sel), + .m_axis_write_desc_ram_addr(m_axis_write_desc_ram_addr), + .m_axis_write_desc_len(m_axis_write_desc_len), + .m_axis_write_desc_tag(m_axis_write_desc_tag), + .m_axis_write_desc_valid(m_axis_write_desc_valid), + .m_axis_write_desc_ready(m_axis_write_desc_ready), + + /* + * Descriptor status input (from DMA interface) + */ + .s_axis_write_desc_status_tag(s_axis_write_desc_status_tag), + .s_axis_write_desc_status_valid(s_axis_write_desc_status_valid), + + /* + * Descriptor input + */ + .s_axis_write_desc_dma_addr(s_axis_write_desc_dma_addr), + .s_axis_write_desc_ram_sel(s_axis_write_desc_ram_sel), + .s_axis_write_desc_ram_addr(s_axis_write_desc_ram_addr), + .s_axis_write_desc_len(s_axis_write_desc_len), + .s_axis_write_desc_tag(s_axis_write_desc_tag), + .s_axis_write_desc_valid(s_axis_write_desc_valid), + .s_axis_write_desc_ready(s_axis_write_desc_ready), + + /* + * Descriptor status output + */ + .m_axis_write_desc_status_tag(m_axis_write_desc_status_tag), + .m_axis_write_desc_status_valid(m_axis_write_desc_status_valid), + + /* + * RAM interface (from DMA interface) + */ + .if_ram_rd_cmd_sel(if_ram_rd_cmd_sel), + .if_ram_rd_cmd_addr(if_ram_rd_cmd_addr), + .if_ram_rd_cmd_valid(if_ram_rd_cmd_valid), + .if_ram_rd_cmd_ready(if_ram_rd_cmd_ready), + .if_ram_rd_resp_data(if_ram_rd_resp_data), + .if_ram_rd_resp_valid(if_ram_rd_resp_valid), + .if_ram_rd_resp_ready(if_ram_rd_resp_ready), + + /* + * RAM interface + */ + .ram_rd_cmd_sel(ram_rd_cmd_sel), + .ram_rd_cmd_addr(ram_rd_cmd_addr), + .ram_rd_cmd_valid(ram_rd_cmd_valid), + .ram_rd_cmd_ready(ram_rd_cmd_ready), + .ram_rd_resp_data(ram_rd_resp_data), + .ram_rd_resp_valid(ram_rd_resp_valid), + .ram_rd_resp_ready(ram_rd_resp_ready) +); + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_if_mux_rd.v b/corundum/lib/pcie/rtl/dma_if_mux_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..ae5a565dcbd2bd94ddcd7a09d3bdcd9151940b16 --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_if_mux_rd.v @@ -0,0 +1,463 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * DMA interface mux (read) + */ +module dma_if_mux_rd # +( + // Number of ports + parameter PORTS = 2, + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // Input RAM segment select width + parameter S_RAM_SEL_WIDTH = 2, + // Output RAM segment select width + // Additional bits required for response routing + parameter M_RAM_SEL_WIDTH = S_RAM_SEL_WIDTH+$clog2(PORTS), + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // Length field width + parameter LEN_WIDTH = 16, + // Input tag field width + parameter S_TAG_WIDTH = 8, + // Output tag field width (towards DMA module) + // Additional bits required for response routing + parameter M_TAG_WIDTH = S_TAG_WIDTH+$clog2(PORTS), + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Read descriptor output (to DMA interface) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_read_desc_dma_addr, + output wire [M_RAM_SEL_WIDTH-1:0] m_axis_read_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_read_desc_ram_addr, + output wire [LEN_WIDTH-1:0] m_axis_read_desc_len, + output wire [M_TAG_WIDTH-1:0] m_axis_read_desc_tag, + output wire m_axis_read_desc_valid, + input wire m_axis_read_desc_ready, + + /* + * Read descriptor status input (from DMA interface) + */ + input wire [M_TAG_WIDTH-1:0] s_axis_read_desc_status_tag, + input wire s_axis_read_desc_status_valid, + + /* + * Read descriptor input + */ + input wire [PORTS*DMA_ADDR_WIDTH-1:0] s_axis_read_desc_dma_addr, + input wire [PORTS*S_RAM_SEL_WIDTH-1:0] s_axis_read_desc_ram_sel, + input wire [PORTS*RAM_ADDR_WIDTH-1:0] s_axis_read_desc_ram_addr, + input wire [PORTS*LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [PORTS*S_TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire [PORTS-1:0] s_axis_read_desc_valid, + output wire [PORTS-1:0] s_axis_read_desc_ready, + + /* + * Read descriptor status output + */ + output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire [PORTS-1:0] m_axis_read_desc_status_valid, + + /* + * RAM interface (from DMA interface) + */ + input wire [SEG_COUNT*M_RAM_SEL_WIDTH-1:0] if_ram_wr_cmd_sel, + input wire [SEG_COUNT*SEG_BE_WIDTH-1:0] if_ram_wr_cmd_be, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] if_ram_wr_cmd_addr, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] if_ram_wr_cmd_data, + input wire [SEG_COUNT-1:0] if_ram_wr_cmd_valid, + output wire [SEG_COUNT-1:0] if_ram_wr_cmd_ready, + + /* + * RAM interface + */ + output wire [PORTS*SEG_COUNT*S_RAM_SEL_WIDTH-1:0] ram_wr_cmd_sel, + output wire [PORTS*SEG_COUNT*SEG_BE_WIDTH-1:0] ram_wr_cmd_be, + output wire [PORTS*SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr, + output wire [PORTS*SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_wr_cmd_data, + output wire [PORTS*SEG_COUNT-1:0] ram_wr_cmd_valid, + input wire [PORTS*SEG_COUNT-1:0] ram_wr_cmd_ready +); + +parameter CL_PORTS = $clog2(PORTS); + +parameter S_RAM_SEL_WIDTH_INT = S_RAM_SEL_WIDTH > 0 ? S_RAM_SEL_WIDTH : 1; + +// check configuration +initial begin + if (M_TAG_WIDTH < S_TAG_WIDTH+$clog2(PORTS)) begin + $error("Error: M_TAG_WIDTH must be at least $clog2(PORTS) larger than S_TAG_WIDTH (instance %m)"); + $finish; + end + + if (M_RAM_SEL_WIDTH < S_RAM_SEL_WIDTH+$clog2(PORTS)) begin + $error("Error: M_RAM_SEL_WIDTH must be at least $clog2(PORTS) larger than S_RAM_SEL_WIDTH (instance %m)"); + $finish; + end +end + +// descriptor mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [DMA_ADDR_WIDTH-1:0] m_axis_read_desc_dma_addr_int; +reg [M_RAM_SEL_WIDTH-1:0] m_axis_read_desc_ram_sel_int; +reg [RAM_ADDR_WIDTH-1:0] m_axis_read_desc_ram_addr_int; +reg [LEN_WIDTH-1:0] m_axis_read_desc_len_int; +reg [M_TAG_WIDTH-1:0] m_axis_read_desc_tag_int; +reg m_axis_read_desc_valid_int; +reg m_axis_read_desc_ready_int_reg = 1'b0; +wire m_axis_read_desc_ready_int_early; + +assign s_axis_read_desc_ready = (m_axis_read_desc_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [DMA_ADDR_WIDTH-1:0] current_s_desc_dma_addr = s_axis_read_desc_dma_addr[grant_encoded*DMA_ADDR_WIDTH +: DMA_ADDR_WIDTH]; +wire [S_RAM_SEL_WIDTH-1:0] current_s_desc_ram_sel = s_axis_read_desc_ram_sel[grant_encoded*S_RAM_SEL_WIDTH +: S_RAM_SEL_WIDTH_INT]; +wire [RAM_ADDR_WIDTH-1:0] current_s_desc_ram_addr = s_axis_read_desc_ram_addr[grant_encoded*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]; +wire [LEN_WIDTH-1:0] current_s_desc_len = s_axis_read_desc_len[grant_encoded*LEN_WIDTH +: LEN_WIDTH]; +wire [S_TAG_WIDTH-1:0] current_s_desc_tag = s_axis_read_desc_tag[grant_encoded*S_TAG_WIDTH +: S_TAG_WIDTH]; +wire current_s_desc_valid = s_axis_read_desc_valid[grant_encoded]; +wire current_s_desc_ready = s_axis_read_desc_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_read_desc_valid & ~grant; +assign acknowledge = grant & s_axis_read_desc_valid & s_axis_read_desc_ready; + +always @* begin + // pass through selected packet data + m_axis_read_desc_dma_addr_int = current_s_desc_dma_addr; + if (S_RAM_SEL_WIDTH > 0) begin + m_axis_read_desc_ram_sel_int = {grant_encoded, current_s_desc_ram_sel}; + end else begin + m_axis_read_desc_ram_sel_int = grant_encoded; + end + m_axis_read_desc_ram_addr_int = current_s_desc_ram_addr; + m_axis_read_desc_len_int = current_s_desc_len; + m_axis_read_desc_tag_int = {grant_encoded, current_s_desc_tag}; + m_axis_read_desc_valid_int = current_s_desc_valid && m_axis_read_desc_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [DMA_ADDR_WIDTH-1:0] m_axis_read_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}; +reg [M_RAM_SEL_WIDTH-1:0] m_axis_read_desc_ram_sel_reg = {M_RAM_SEL_WIDTH{1'b0}}; +reg [RAM_ADDR_WIDTH-1:0] m_axis_read_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] m_axis_read_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] m_axis_read_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg m_axis_read_desc_valid_reg = 1'b0, m_axis_read_desc_valid_next; + +reg [DMA_ADDR_WIDTH-1:0] temp_m_axis_read_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}; +reg [M_RAM_SEL_WIDTH-1:0] temp_m_axis_read_desc_ram_sel_reg = {M_RAM_SEL_WIDTH{1'b0}}; +reg [RAM_ADDR_WIDTH-1:0] temp_m_axis_read_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] temp_m_axis_read_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] temp_m_axis_read_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg temp_m_axis_read_desc_valid_reg = 1'b0, temp_m_axis_read_desc_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_read_desc_dma_addr = m_axis_read_desc_dma_addr_reg; +assign m_axis_read_desc_ram_sel = m_axis_read_desc_ram_sel_reg; +assign m_axis_read_desc_ram_addr = m_axis_read_desc_ram_addr_reg; +assign m_axis_read_desc_len = m_axis_read_desc_len_reg; +assign m_axis_read_desc_tag = m_axis_read_desc_tag_reg; +assign m_axis_read_desc_valid = m_axis_read_desc_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_read_desc_ready_int_early = m_axis_read_desc_ready || (!temp_m_axis_read_desc_valid_reg && (!m_axis_read_desc_valid_reg || !m_axis_read_desc_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_read_desc_valid_next = m_axis_read_desc_valid_reg; + temp_m_axis_read_desc_valid_next = temp_m_axis_read_desc_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_read_desc_ready_int_reg) begin + // input is ready + if (m_axis_read_desc_ready || !m_axis_read_desc_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_read_desc_valid_next = m_axis_read_desc_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_read_desc_valid_next = m_axis_read_desc_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_read_desc_ready) begin + // input is not ready, but output is ready + m_axis_read_desc_valid_next = temp_m_axis_read_desc_valid_reg; + temp_m_axis_read_desc_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_read_desc_valid_reg <= 1'b0; + m_axis_read_desc_ready_int_reg <= 1'b0; + temp_m_axis_read_desc_valid_reg <= 1'b0; + end else begin + m_axis_read_desc_valid_reg <= m_axis_read_desc_valid_next; + m_axis_read_desc_ready_int_reg <= m_axis_read_desc_ready_int_early; + temp_m_axis_read_desc_valid_reg <= temp_m_axis_read_desc_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_read_desc_dma_addr_reg <= m_axis_read_desc_dma_addr_int; + m_axis_read_desc_ram_sel_reg <= m_axis_read_desc_ram_sel_int; + m_axis_read_desc_ram_addr_reg <= m_axis_read_desc_ram_addr_int; + m_axis_read_desc_len_reg <= m_axis_read_desc_len_int; + m_axis_read_desc_tag_reg <= m_axis_read_desc_tag_int; + end else if (store_axis_temp_to_output) begin + m_axis_read_desc_dma_addr_reg <= temp_m_axis_read_desc_dma_addr_reg; + m_axis_read_desc_ram_sel_reg <= temp_m_axis_read_desc_ram_sel_reg; + m_axis_read_desc_ram_addr_reg <= temp_m_axis_read_desc_ram_addr_reg; + m_axis_read_desc_len_reg <= temp_m_axis_read_desc_len_reg; + m_axis_read_desc_tag_reg <= temp_m_axis_read_desc_tag_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_read_desc_dma_addr_reg <= m_axis_read_desc_dma_addr_int; + temp_m_axis_read_desc_ram_sel_reg <= m_axis_read_desc_ram_sel_int; + temp_m_axis_read_desc_ram_addr_reg <= m_axis_read_desc_ram_addr_int; + temp_m_axis_read_desc_len_reg <= m_axis_read_desc_len_int; + temp_m_axis_read_desc_tag_reg <= m_axis_read_desc_tag_int; + end +end + +// descriptor status demux +reg [S_TAG_WIDTH-1:0] m_axis_read_desc_status_tag_reg = {S_TAG_WIDTH{1'b0}}, m_axis_read_desc_status_tag_next; +reg [PORTS-1:0] m_axis_read_desc_status_valid_reg = {PORTS{1'b0}}, m_axis_read_desc_status_valid_next; + +assign m_axis_read_desc_status_tag = {PORTS{m_axis_read_desc_status_tag_reg}}; +assign m_axis_read_desc_status_valid = m_axis_read_desc_status_valid_reg; + +always @* begin + m_axis_read_desc_status_tag_next = s_axis_read_desc_status_tag; + m_axis_read_desc_status_valid_next = s_axis_read_desc_status_valid << (PORTS > 1 ? s_axis_read_desc_status_tag[S_TAG_WIDTH+CL_PORTS-1:S_TAG_WIDTH] : 0); +end + +always @(posedge clk) begin + if (rst) begin + m_axis_read_desc_status_valid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_read_desc_status_valid_reg <= m_axis_read_desc_status_valid_next; + end + + m_axis_read_desc_status_tag_reg <= m_axis_read_desc_status_tag_next; +end + +generate + +genvar n, p; + +for (n = 0; n < SEG_COUNT; n = n + 1) begin + + // RAM write command demux + + wire [M_RAM_SEL_WIDTH-1:0] seg_if_ram_wr_cmd_sel = if_ram_wr_cmd_sel[M_RAM_SEL_WIDTH*n +: M_RAM_SEL_WIDTH]; + wire [SEG_BE_WIDTH-1:0] seg_if_ram_wr_cmd_be = if_ram_wr_cmd_be[SEG_BE_WIDTH*n +: SEG_BE_WIDTH]; + wire [SEG_ADDR_WIDTH-1:0] seg_if_ram_wr_cmd_addr = if_ram_wr_cmd_addr[SEG_ADDR_WIDTH*n +: SEG_ADDR_WIDTH]; + wire [SEG_DATA_WIDTH-1:0] seg_if_ram_wr_cmd_data = if_ram_wr_cmd_data[SEG_DATA_WIDTH*n +: SEG_DATA_WIDTH]; + wire seg_if_ram_wr_cmd_valid = if_ram_wr_cmd_valid[n]; + wire seg_if_ram_wr_cmd_ready; + + assign if_ram_wr_cmd_ready[n] = seg_if_ram_wr_cmd_ready; + + wire [PORTS*S_RAM_SEL_WIDTH-1:0] seg_ram_wr_cmd_sel; + wire [PORTS*SEG_BE_WIDTH-1:0] seg_ram_wr_cmd_be; + wire [PORTS*SEG_ADDR_WIDTH-1:0] seg_ram_wr_cmd_addr; + wire [PORTS*SEG_DATA_WIDTH-1:0] seg_ram_wr_cmd_data; + wire [PORTS-1:0] seg_ram_wr_cmd_valid; + wire [PORTS-1:0] seg_ram_wr_cmd_ready; + + for (p = 0; p < PORTS; p = p + 1) begin + assign ram_wr_cmd_sel[(p*SEG_COUNT+n)*S_RAM_SEL_WIDTH +: S_RAM_SEL_WIDTH_INT] = seg_ram_wr_cmd_sel[p*S_RAM_SEL_WIDTH +: S_RAM_SEL_WIDTH_INT]; + assign ram_wr_cmd_be[(p*SEG_COUNT+n)*SEG_BE_WIDTH +: SEG_BE_WIDTH] = seg_ram_wr_cmd_be[p*SEG_BE_WIDTH +: SEG_BE_WIDTH]; + assign ram_wr_cmd_addr[(p*SEG_COUNT+n)*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = seg_ram_wr_cmd_addr[p*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH]; + assign ram_wr_cmd_data[(p*SEG_COUNT+n)*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] = seg_ram_wr_cmd_data[p*SEG_DATA_WIDTH +: SEG_DATA_WIDTH]; + assign ram_wr_cmd_valid[p*SEG_COUNT+n] = seg_ram_wr_cmd_valid[p]; + assign seg_ram_wr_cmd_ready[p] = ram_wr_cmd_ready[p*SEG_COUNT+n]; + end + + // internal datapath + reg [S_RAM_SEL_WIDTH-1:0] seg_ram_wr_cmd_sel_int; + reg [SEG_BE_WIDTH-1:0] seg_ram_wr_cmd_be_int; + reg [SEG_ADDR_WIDTH-1:0] seg_ram_wr_cmd_addr_int; + reg [SEG_DATA_WIDTH-1:0] seg_ram_wr_cmd_data_int; + reg [PORTS-1:0] seg_ram_wr_cmd_valid_int; + reg seg_ram_wr_cmd_ready_int_reg = 1'b0; + wire seg_ram_wr_cmd_ready_int_early; + + assign seg_if_ram_wr_cmd_ready = seg_ram_wr_cmd_ready_int_reg; + + wire [CL_PORTS-1:0] select = PORTS > 1 ? (seg_if_ram_wr_cmd_sel >> (M_RAM_SEL_WIDTH - CL_PORTS)) : 0; + + always @* begin + seg_ram_wr_cmd_sel_int = seg_if_ram_wr_cmd_sel; + seg_ram_wr_cmd_be_int = seg_if_ram_wr_cmd_be; + seg_ram_wr_cmd_addr_int = seg_if_ram_wr_cmd_addr; + seg_ram_wr_cmd_data_int = seg_if_ram_wr_cmd_data; + seg_ram_wr_cmd_valid_int = (seg_if_ram_wr_cmd_valid && seg_if_ram_wr_cmd_ready) << select; + end + + // output datapath logic + reg [S_RAM_SEL_WIDTH-1:0] seg_ram_wr_cmd_sel_reg = {S_RAM_SEL_WIDTH_INT{1'b0}}; + reg [SEG_BE_WIDTH-1:0] seg_ram_wr_cmd_be_reg = {SEG_BE_WIDTH{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] seg_ram_wr_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [SEG_DATA_WIDTH-1:0] seg_ram_wr_cmd_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg [PORTS-1:0] seg_ram_wr_cmd_valid_reg = {PORTS{1'b0}}, seg_ram_wr_cmd_valid_next; + + reg [S_RAM_SEL_WIDTH-1:0] temp_seg_ram_wr_cmd_sel_reg = {S_RAM_SEL_WIDTH_INT{1'b0}}; + reg [SEG_BE_WIDTH-1:0] temp_seg_ram_wr_cmd_be_reg = {SEG_BE_WIDTH{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] temp_seg_ram_wr_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [SEG_DATA_WIDTH-1:0] temp_seg_ram_wr_cmd_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg [PORTS-1:0] temp_seg_ram_wr_cmd_valid_reg = {PORTS{1'b0}}, temp_seg_ram_wr_cmd_valid_next; + + // datapath control + reg store_axis_resp_int_to_output; + reg store_axis_resp_int_to_temp; + reg store_axis_resp_temp_to_output; + + assign seg_ram_wr_cmd_sel = {PORTS{seg_ram_wr_cmd_sel_reg}}; + assign seg_ram_wr_cmd_be = {PORTS{seg_ram_wr_cmd_be_reg}}; + assign seg_ram_wr_cmd_addr = {PORTS{seg_ram_wr_cmd_addr_reg}}; + assign seg_ram_wr_cmd_data = {PORTS{seg_ram_wr_cmd_data_reg}}; + assign seg_ram_wr_cmd_valid = seg_ram_wr_cmd_valid_reg; + + // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) + assign seg_ram_wr_cmd_ready_int_early = (seg_ram_wr_cmd_ready & seg_ram_wr_cmd_valid_reg) || (!temp_seg_ram_wr_cmd_valid_reg && (!seg_ram_wr_cmd_valid_reg || !seg_ram_wr_cmd_valid_int)); + + always @* begin + // transfer sink ready state to source + seg_ram_wr_cmd_valid_next = seg_ram_wr_cmd_valid_reg; + temp_seg_ram_wr_cmd_valid_next = temp_seg_ram_wr_cmd_valid_reg; + + store_axis_resp_int_to_output = 1'b0; + store_axis_resp_int_to_temp = 1'b0; + store_axis_resp_temp_to_output = 1'b0; + + if (seg_ram_wr_cmd_ready_int_reg) begin + // input is ready + if ((seg_ram_wr_cmd_ready & seg_ram_wr_cmd_valid_reg) || !seg_ram_wr_cmd_valid_reg) begin + // output is ready or currently not valid, transfer data to output + seg_ram_wr_cmd_valid_next = seg_ram_wr_cmd_valid_int; + store_axis_resp_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_seg_ram_wr_cmd_valid_next = seg_ram_wr_cmd_valid_int; + store_axis_resp_int_to_temp = 1'b1; + end + end else if (seg_ram_wr_cmd_ready & seg_ram_wr_cmd_valid_reg) begin + // input is not ready, but output is ready + seg_ram_wr_cmd_valid_next = temp_seg_ram_wr_cmd_valid_reg; + temp_seg_ram_wr_cmd_valid_next = {PORTS{1'b0}}; + store_axis_resp_temp_to_output = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + seg_ram_wr_cmd_valid_reg <= {PORTS{1'b0}}; + seg_ram_wr_cmd_ready_int_reg <= 1'b0; + temp_seg_ram_wr_cmd_valid_reg <= {PORTS{1'b0}}; + end else begin + seg_ram_wr_cmd_valid_reg <= seg_ram_wr_cmd_valid_next; + seg_ram_wr_cmd_ready_int_reg <= seg_ram_wr_cmd_ready_int_early; + temp_seg_ram_wr_cmd_valid_reg <= temp_seg_ram_wr_cmd_valid_next; + end + + // datapath + if (store_axis_resp_int_to_output) begin + seg_ram_wr_cmd_sel_reg <= seg_ram_wr_cmd_sel_int; + seg_ram_wr_cmd_be_reg <= seg_ram_wr_cmd_be_int; + seg_ram_wr_cmd_addr_reg <= seg_ram_wr_cmd_addr_int; + seg_ram_wr_cmd_data_reg <= seg_ram_wr_cmd_data_int; + end else if (store_axis_resp_temp_to_output) begin + seg_ram_wr_cmd_sel_reg <= temp_seg_ram_wr_cmd_sel_reg; + seg_ram_wr_cmd_be_reg <= temp_seg_ram_wr_cmd_be_reg; + seg_ram_wr_cmd_addr_reg <= temp_seg_ram_wr_cmd_addr_reg; + seg_ram_wr_cmd_data_reg <= temp_seg_ram_wr_cmd_data_reg; + end + + if (store_axis_resp_int_to_temp) begin + temp_seg_ram_wr_cmd_sel_reg <= seg_ram_wr_cmd_sel_int; + temp_seg_ram_wr_cmd_be_reg <= seg_ram_wr_cmd_be_int; + temp_seg_ram_wr_cmd_addr_reg <= seg_ram_wr_cmd_addr_int; + temp_seg_ram_wr_cmd_data_reg <= seg_ram_wr_cmd_data_int; + end + end + +end + +endgenerate + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_if_mux_wr.v b/corundum/lib/pcie/rtl/dma_if_mux_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..6c1bd7e88b8606f5cd03bbead030f571324e983b --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_if_mux_wr.v @@ -0,0 +1,591 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * DMA interface mux (write) + */ +module dma_if_mux_wr # +( + // Number of ports + parameter PORTS = 2, + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // Input RAM segment select width + parameter S_RAM_SEL_WIDTH = 2, + // Output RAM segment select width + // Additional bits required for response routing + parameter M_RAM_SEL_WIDTH = S_RAM_SEL_WIDTH+$clog2(PORTS), + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // Length field width + parameter LEN_WIDTH = 16, + // Input tag field width + parameter S_TAG_WIDTH = 8, + // Output tag field width (towards DMA module) + // Additional bits required for response routing + parameter M_TAG_WIDTH = S_TAG_WIDTH+$clog2(PORTS), + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Write descriptor output (to DMA interface) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_write_desc_dma_addr, + output wire [M_RAM_SEL_WIDTH-1:0] m_axis_write_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_write_desc_ram_addr, + output wire [LEN_WIDTH-1:0] m_axis_write_desc_len, + output wire [M_TAG_WIDTH-1:0] m_axis_write_desc_tag, + output wire m_axis_write_desc_valid, + input wire m_axis_write_desc_ready, + + /* + * Write descriptor status input (from DMA interface) + */ + input wire [M_TAG_WIDTH-1:0] s_axis_write_desc_status_tag, + input wire s_axis_write_desc_status_valid, + + /* + * Write descriptor input + */ + input wire [PORTS*DMA_ADDR_WIDTH-1:0] s_axis_write_desc_dma_addr, + input wire [PORTS*S_RAM_SEL_WIDTH-1:0] s_axis_write_desc_ram_sel, + input wire [PORTS*RAM_ADDR_WIDTH-1:0] s_axis_write_desc_ram_addr, + input wire [PORTS*LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [PORTS*S_TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire [PORTS-1:0] s_axis_write_desc_valid, + output wire [PORTS-1:0] s_axis_write_desc_ready, + + /* + * Write descriptor status output + */ + output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire [PORTS-1:0] m_axis_write_desc_status_valid, + + /* + * RAM interface (from DMA interface) + */ + input wire [SEG_COUNT*M_RAM_SEL_WIDTH-1:0] if_ram_rd_cmd_sel, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] if_ram_rd_cmd_addr, + input wire [SEG_COUNT-1:0] if_ram_rd_cmd_valid, + output wire [SEG_COUNT-1:0] if_ram_rd_cmd_ready, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] if_ram_rd_resp_data, + output wire [SEG_COUNT-1:0] if_ram_rd_resp_valid, + input wire [SEG_COUNT-1:0] if_ram_rd_resp_ready, + + /* + * RAM interface + */ + output wire [PORTS*SEG_COUNT*S_RAM_SEL_WIDTH-1:0] ram_rd_cmd_sel, + output wire [PORTS*SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_rd_cmd_addr, + output wire [PORTS*SEG_COUNT-1:0] ram_rd_cmd_valid, + input wire [PORTS*SEG_COUNT-1:0] ram_rd_cmd_ready, + input wire [PORTS*SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_rd_resp_data, + input wire [PORTS*SEG_COUNT-1:0] ram_rd_resp_valid, + output wire [PORTS*SEG_COUNT-1:0] ram_rd_resp_ready +); + +parameter CL_PORTS = $clog2(PORTS); + +parameter S_RAM_SEL_WIDTH_INT = S_RAM_SEL_WIDTH > 0 ? S_RAM_SEL_WIDTH : 1; + +parameter FIFO_ADDR_WIDTH = 5; + +// check configuration +initial begin + if (M_TAG_WIDTH < S_TAG_WIDTH+$clog2(PORTS)) begin + $error("Error: M_TAG_WIDTH must be at least $clog2(PORTS) larger than S_TAG_WIDTH (instance %m)"); + $finish; + end + + if (M_RAM_SEL_WIDTH < S_RAM_SEL_WIDTH+$clog2(PORTS)) begin + $error("Error: M_RAM_SEL_WIDTH must be at least $clog2(PORTS) larger than S_RAM_SEL_WIDTH (instance %m)"); + $finish; + end +end + +// descriptor mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [DMA_ADDR_WIDTH-1:0] m_axis_write_desc_dma_addr_int; +reg [M_RAM_SEL_WIDTH-1:0] m_axis_write_desc_ram_sel_int; +reg [RAM_ADDR_WIDTH-1:0] m_axis_write_desc_ram_addr_int; +reg [LEN_WIDTH-1:0] m_axis_write_desc_len_int; +reg [M_TAG_WIDTH-1:0] m_axis_write_desc_tag_int; +reg m_axis_write_desc_valid_int; +reg m_axis_write_desc_ready_int_reg = 1'b0; +wire m_axis_write_desc_ready_int_early; + +assign s_axis_write_desc_ready = (m_axis_write_desc_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [DMA_ADDR_WIDTH-1:0] current_s_desc_dma_addr = s_axis_write_desc_dma_addr[grant_encoded*DMA_ADDR_WIDTH +: DMA_ADDR_WIDTH]; +wire [S_RAM_SEL_WIDTH-1:0] current_s_desc_ram_sel = s_axis_write_desc_ram_sel[grant_encoded*S_RAM_SEL_WIDTH +: S_RAM_SEL_WIDTH_INT]; +wire [RAM_ADDR_WIDTH-1:0] current_s_desc_ram_addr = s_axis_write_desc_ram_addr[grant_encoded*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]; +wire [LEN_WIDTH-1:0] current_s_desc_len = s_axis_write_desc_len[grant_encoded*LEN_WIDTH +: LEN_WIDTH]; +wire [S_TAG_WIDTH-1:0] current_s_desc_tag = s_axis_write_desc_tag[grant_encoded*S_TAG_WIDTH +: S_TAG_WIDTH]; +wire current_s_desc_valid = s_axis_write_desc_valid[grant_encoded]; +wire current_s_desc_ready = s_axis_write_desc_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_write_desc_valid & ~grant; +assign acknowledge = grant & s_axis_write_desc_valid & s_axis_write_desc_ready; + +always @* begin + // pass through selected packet data + m_axis_write_desc_dma_addr_int = current_s_desc_dma_addr; + if (S_RAM_SEL_WIDTH > 0) begin + m_axis_write_desc_ram_sel_int = {grant_encoded, current_s_desc_ram_sel}; + end else begin + m_axis_write_desc_ram_sel_int = grant_encoded; + end + m_axis_write_desc_ram_addr_int = current_s_desc_ram_addr; + m_axis_write_desc_len_int = current_s_desc_len; + m_axis_write_desc_tag_int = {grant_encoded, current_s_desc_tag}; + m_axis_write_desc_valid_int = current_s_desc_valid && m_axis_write_desc_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [DMA_ADDR_WIDTH-1:0] m_axis_write_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}; +reg [M_RAM_SEL_WIDTH-1:0] m_axis_write_desc_ram_sel_reg = {M_RAM_SEL_WIDTH{1'b0}}; +reg [RAM_ADDR_WIDTH-1:0] m_axis_write_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] m_axis_write_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] m_axis_write_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg m_axis_write_desc_valid_reg = 1'b0, m_axis_write_desc_valid_next; + +reg [DMA_ADDR_WIDTH-1:0] temp_m_axis_write_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}; +reg [M_RAM_SEL_WIDTH-1:0] temp_m_axis_write_desc_ram_sel_reg = {M_RAM_SEL_WIDTH{1'b0}}; +reg [RAM_ADDR_WIDTH-1:0] temp_m_axis_write_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] temp_m_axis_write_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] temp_m_axis_write_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg temp_m_axis_write_desc_valid_reg = 1'b0, temp_m_axis_write_desc_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_write_desc_dma_addr = m_axis_write_desc_dma_addr_reg; +assign m_axis_write_desc_ram_addr = m_axis_write_desc_ram_addr_reg; +assign m_axis_write_desc_ram_sel = m_axis_write_desc_ram_sel_reg; +assign m_axis_write_desc_len = m_axis_write_desc_len_reg; +assign m_axis_write_desc_tag = m_axis_write_desc_tag_reg; +assign m_axis_write_desc_valid = m_axis_write_desc_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_write_desc_ready_int_early = m_axis_write_desc_ready || (!temp_m_axis_write_desc_valid_reg && (!m_axis_write_desc_valid_reg || !m_axis_write_desc_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_write_desc_valid_next = m_axis_write_desc_valid_reg; + temp_m_axis_write_desc_valid_next = temp_m_axis_write_desc_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_write_desc_ready_int_reg) begin + // input is ready + if (m_axis_write_desc_ready || !m_axis_write_desc_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_write_desc_valid_next = m_axis_write_desc_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_write_desc_valid_next = m_axis_write_desc_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_write_desc_ready) begin + // input is not ready, but output is ready + m_axis_write_desc_valid_next = temp_m_axis_write_desc_valid_reg; + temp_m_axis_write_desc_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_write_desc_valid_reg <= 1'b0; + m_axis_write_desc_ready_int_reg <= 1'b0; + temp_m_axis_write_desc_valid_reg <= 1'b0; + end else begin + m_axis_write_desc_valid_reg <= m_axis_write_desc_valid_next; + m_axis_write_desc_ready_int_reg <= m_axis_write_desc_ready_int_early; + temp_m_axis_write_desc_valid_reg <= temp_m_axis_write_desc_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_write_desc_dma_addr_reg <= m_axis_write_desc_dma_addr_int; + m_axis_write_desc_ram_sel_reg <= m_axis_write_desc_ram_sel_int; + m_axis_write_desc_ram_addr_reg <= m_axis_write_desc_ram_addr_int; + m_axis_write_desc_len_reg <= m_axis_write_desc_len_int; + m_axis_write_desc_tag_reg <= m_axis_write_desc_tag_int; + end else if (store_axis_temp_to_output) begin + m_axis_write_desc_dma_addr_reg <= temp_m_axis_write_desc_dma_addr_reg; + m_axis_write_desc_ram_sel_reg <= temp_m_axis_write_desc_ram_sel_reg; + m_axis_write_desc_ram_addr_reg <= temp_m_axis_write_desc_ram_addr_reg; + m_axis_write_desc_len_reg <= temp_m_axis_write_desc_len_reg; + m_axis_write_desc_tag_reg <= temp_m_axis_write_desc_tag_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_write_desc_dma_addr_reg <= m_axis_write_desc_dma_addr_int; + temp_m_axis_write_desc_ram_sel_reg <= m_axis_write_desc_ram_sel_int; + temp_m_axis_write_desc_ram_addr_reg <= m_axis_write_desc_ram_addr_int; + temp_m_axis_write_desc_len_reg <= m_axis_write_desc_len_int; + temp_m_axis_write_desc_tag_reg <= m_axis_write_desc_tag_int; + end +end + +// descriptor status demux +reg [S_TAG_WIDTH-1:0] m_axis_write_desc_status_tag_reg = {S_TAG_WIDTH{1'b0}}, m_axis_write_desc_status_tag_next; +reg [PORTS-1:0] m_axis_write_desc_status_valid_reg = {PORTS{1'b0}}, m_axis_write_desc_status_valid_next; + +assign m_axis_write_desc_status_tag = {PORTS{m_axis_write_desc_status_tag_reg}}; +assign m_axis_write_desc_status_valid = m_axis_write_desc_status_valid_reg; + +always @* begin + m_axis_write_desc_status_tag_next = s_axis_write_desc_status_tag; + m_axis_write_desc_status_valid_next = s_axis_write_desc_status_valid << (PORTS > 1 ? s_axis_write_desc_status_tag[S_TAG_WIDTH+CL_PORTS-1:S_TAG_WIDTH] : 0); +end + +always @(posedge clk) begin + if (rst) begin + m_axis_write_desc_status_valid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_write_desc_status_valid_reg <= m_axis_write_desc_status_valid_next; + end + + m_axis_write_desc_status_tag_reg <= m_axis_write_desc_status_tag_next; +end + +generate + +genvar n, p; + +for (n = 0; n < SEG_COUNT; n = n + 1) begin + + // FIFO to maintain response ordering + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_wr_ptr_reg = 0; + reg [FIFO_ADDR_WIDTH+1-1:0] fifo_rd_ptr_reg = 0; + reg [CL_PORTS-1:0] fifo_sel[(2**FIFO_ADDR_WIDTH)-1:0]; + + wire fifo_empty = fifo_wr_ptr_reg == fifo_rd_ptr_reg; + wire fifo_full = fifo_wr_ptr_reg == (fifo_rd_ptr_reg ^ (1 << FIFO_ADDR_WIDTH)); + + integer i; + + initial begin + for (i = 0; i < 2**FIFO_ADDR_WIDTH; i = i + 1) begin + fifo_sel[i] = 0; + end + end + + // RAM read command demux + + wire [M_RAM_SEL_WIDTH-1:0] seg_if_ram_rd_cmd_sel = if_ram_rd_cmd_sel[M_RAM_SEL_WIDTH*n +: M_RAM_SEL_WIDTH]; + wire [SEG_ADDR_WIDTH-1:0] seg_if_ram_rd_cmd_addr = if_ram_rd_cmd_addr[SEG_ADDR_WIDTH*n +: SEG_ADDR_WIDTH]; + wire seg_if_ram_rd_cmd_valid = if_ram_rd_cmd_valid[n]; + wire seg_if_ram_rd_cmd_ready; + + assign if_ram_rd_cmd_ready[n] = seg_if_ram_rd_cmd_ready; + + wire [PORTS*S_RAM_SEL_WIDTH-1:0] seg_ram_rd_cmd_sel; + wire [PORTS*SEG_ADDR_WIDTH-1:0] seg_ram_rd_cmd_addr; + wire [PORTS-1:0] seg_ram_rd_cmd_valid; + wire [PORTS-1:0] seg_ram_rd_cmd_ready; + + for (p = 0; p < PORTS; p = p + 1) begin + assign ram_rd_cmd_sel[(p*SEG_COUNT+n)*S_RAM_SEL_WIDTH +: S_RAM_SEL_WIDTH_INT] = seg_ram_rd_cmd_sel[p*S_RAM_SEL_WIDTH +: S_RAM_SEL_WIDTH_INT]; + assign ram_rd_cmd_addr[(p*SEG_COUNT+n)*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = seg_ram_rd_cmd_addr[p*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH]; + assign ram_rd_cmd_valid[p*SEG_COUNT+n] = seg_ram_rd_cmd_valid[p]; + assign seg_ram_rd_cmd_ready[p] = ram_rd_cmd_ready[p*SEG_COUNT+n]; + end + + // internal datapath + reg [S_RAM_SEL_WIDTH-1:0] seg_ram_rd_cmd_sel_int; + reg [SEG_ADDR_WIDTH-1:0] seg_ram_rd_cmd_addr_int; + reg [PORTS-1:0] seg_ram_rd_cmd_valid_int; + reg seg_ram_rd_cmd_ready_int_reg = 1'b0; + wire seg_ram_rd_cmd_ready_int_early; + + assign seg_if_ram_rd_cmd_ready = seg_ram_rd_cmd_ready_int_reg && !fifo_full; + + wire [CL_PORTS-1:0] select_cmd = PORTS > 1 ? (seg_if_ram_rd_cmd_sel >> (M_RAM_SEL_WIDTH - CL_PORTS)) : 0; + + always @* begin + seg_ram_rd_cmd_sel_int = seg_if_ram_rd_cmd_sel; + seg_ram_rd_cmd_addr_int = seg_if_ram_rd_cmd_addr; + seg_ram_rd_cmd_valid_int = (seg_if_ram_rd_cmd_valid && seg_if_ram_rd_cmd_ready) << select_cmd; + end + + always @(posedge clk) begin + if (seg_if_ram_rd_cmd_valid && seg_if_ram_rd_cmd_ready) begin + fifo_sel[fifo_wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= select_cmd; + fifo_wr_ptr_reg <= fifo_wr_ptr_reg + 1; + end + + if (rst) begin + fifo_wr_ptr_reg <= 0; + end + end + + // output datapath logic + reg [S_RAM_SEL_WIDTH-1:0] seg_ram_rd_cmd_sel_reg = {S_RAM_SEL_WIDTH_INT{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] seg_ram_rd_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [PORTS-1:0] seg_ram_rd_cmd_valid_reg = {PORTS{1'b0}}, seg_ram_rd_cmd_valid_next; + + reg [S_RAM_SEL_WIDTH-1:0] temp_seg_ram_rd_cmd_sel_reg = {S_RAM_SEL_WIDTH_INT{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] temp_seg_ram_rd_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [PORTS-1:0] temp_seg_ram_rd_cmd_valid_reg = {PORTS{1'b0}}, temp_seg_ram_rd_cmd_valid_next; + + // datapath control + reg store_axis_resp_int_to_output; + reg store_axis_resp_int_to_temp; + reg store_axis_resp_temp_to_output; + + assign seg_ram_rd_cmd_sel = {PORTS{seg_ram_rd_cmd_sel_reg}}; + assign seg_ram_rd_cmd_addr = {PORTS{seg_ram_rd_cmd_addr_reg}}; + assign seg_ram_rd_cmd_valid = seg_ram_rd_cmd_valid_reg; + + // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) + assign seg_ram_rd_cmd_ready_int_early = (seg_ram_rd_cmd_ready & seg_ram_rd_cmd_valid_reg) || (!temp_seg_ram_rd_cmd_valid_reg && (!seg_ram_rd_cmd_valid_reg || !seg_ram_rd_cmd_valid_int)); + + always @* begin + // transfer sink ready state to source + seg_ram_rd_cmd_valid_next = seg_ram_rd_cmd_valid_reg; + temp_seg_ram_rd_cmd_valid_next = temp_seg_ram_rd_cmd_valid_reg; + + store_axis_resp_int_to_output = 1'b0; + store_axis_resp_int_to_temp = 1'b0; + store_axis_resp_temp_to_output = 1'b0; + + if (seg_ram_rd_cmd_ready_int_reg) begin + // input is ready + if ((seg_ram_rd_cmd_ready & seg_ram_rd_cmd_valid_reg) || !seg_ram_rd_cmd_valid_reg) begin + // output is ready or currently not valid, transfer data to output + seg_ram_rd_cmd_valid_next = seg_ram_rd_cmd_valid_int; + store_axis_resp_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_seg_ram_rd_cmd_valid_next = seg_ram_rd_cmd_valid_int; + store_axis_resp_int_to_temp = 1'b1; + end + end else if (seg_ram_rd_cmd_ready & seg_ram_rd_cmd_valid_reg) begin + // input is not ready, but output is ready + seg_ram_rd_cmd_valid_next = temp_seg_ram_rd_cmd_valid_reg; + temp_seg_ram_rd_cmd_valid_next = {PORTS{1'b0}}; + store_axis_resp_temp_to_output = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + seg_ram_rd_cmd_valid_reg <= {PORTS{1'b0}}; + seg_ram_rd_cmd_ready_int_reg <= 1'b0; + temp_seg_ram_rd_cmd_valid_reg <= {PORTS{1'b0}}; + end else begin + seg_ram_rd_cmd_valid_reg <= seg_ram_rd_cmd_valid_next; + seg_ram_rd_cmd_ready_int_reg <= seg_ram_rd_cmd_ready_int_early; + temp_seg_ram_rd_cmd_valid_reg <= temp_seg_ram_rd_cmd_valid_next; + end + + // datapath + if (store_axis_resp_int_to_output) begin + seg_ram_rd_cmd_sel_reg <= seg_ram_rd_cmd_sel_int; + seg_ram_rd_cmd_addr_reg <= seg_ram_rd_cmd_addr_int; + end else if (store_axis_resp_temp_to_output) begin + seg_ram_rd_cmd_sel_reg <= temp_seg_ram_rd_cmd_sel_reg; + seg_ram_rd_cmd_addr_reg <= temp_seg_ram_rd_cmd_addr_reg; + end + + if (store_axis_resp_int_to_temp) begin + temp_seg_ram_rd_cmd_sel_reg <= seg_ram_rd_cmd_sel_int; + temp_seg_ram_rd_cmd_addr_reg <= seg_ram_rd_cmd_addr_int; + end + end + + // RAM read response mux + + wire [PORTS*SEG_DATA_WIDTH-1:0] seg_ram_rd_resp_data; + wire [PORTS-1:0] seg_ram_rd_resp_valid; + wire [PORTS-1:0] seg_ram_rd_resp_ready; + + for (p = 0; p < PORTS; p = p + 1) begin + assign seg_ram_rd_resp_data[p*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] = ram_rd_resp_data[(p*SEG_COUNT+n)*SEG_DATA_WIDTH +: SEG_DATA_WIDTH]; + assign seg_ram_rd_resp_valid[p] = ram_rd_resp_valid[p*SEG_COUNT+n]; + assign ram_rd_resp_ready[p*SEG_COUNT+n] = seg_ram_rd_resp_ready[p]; + end + + wire [SEG_DATA_WIDTH-1:0] seg_if_ram_rd_resp_data; + wire seg_if_ram_rd_resp_valid; + wire seg_if_ram_rd_resp_ready = if_ram_rd_resp_ready[n]; + + assign if_ram_rd_resp_data[n*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] = seg_if_ram_rd_resp_data; + assign if_ram_rd_resp_valid[n] = seg_if_ram_rd_resp_valid; + + // internal datapath + reg [SEG_DATA_WIDTH-1:0] seg_if_ram_rd_resp_data_int; + reg seg_if_ram_rd_resp_valid_int; + reg seg_if_ram_rd_resp_ready_int_reg = 1'b0; + wire seg_if_ram_rd_resp_ready_int_early; + + wire [CL_PORTS-1:0] select_resp = fifo_sel[fifo_rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]]; + + assign seg_ram_rd_resp_ready = (seg_if_ram_rd_resp_ready_int_reg && !fifo_empty) << select_resp; + + // mux for incoming packet + wire [SEG_DATA_WIDTH-1:0] current_resp_data = seg_ram_rd_resp_data[select_resp*SEG_DATA_WIDTH +: SEG_DATA_WIDTH]; + wire current_resp_valid = seg_ram_rd_resp_valid[select_resp]; + wire current_resp_ready = seg_ram_rd_resp_ready[select_resp]; + + always @* begin + // pass through selected packet data + seg_if_ram_rd_resp_data_int = current_resp_data; + seg_if_ram_rd_resp_valid_int = current_resp_valid && seg_if_ram_rd_resp_ready_int_reg && !fifo_empty; + end + + always @(posedge clk) begin + if (current_resp_valid && seg_if_ram_rd_resp_ready_int_reg && !fifo_empty) begin + fifo_rd_ptr_reg <= fifo_rd_ptr_reg + 1; + end + + if (rst) begin + fifo_rd_ptr_reg <= 0; + end + end + + // output datapath logic + reg [SEG_DATA_WIDTH-1:0] seg_if_ram_rd_resp_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg seg_if_ram_rd_resp_valid_reg = 1'b0, seg_if_ram_rd_resp_valid_next; + + reg [SEG_DATA_WIDTH-1:0] temp_seg_if_ram_rd_resp_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg temp_seg_if_ram_rd_resp_valid_reg = 1'b0, temp_seg_if_ram_rd_resp_valid_next; + + // datapath control + reg store_axis_int_to_output; + reg store_axis_int_to_temp; + reg store_axis_temp_to_output; + + assign seg_if_ram_rd_resp_data = seg_if_ram_rd_resp_data_reg; + assign seg_if_ram_rd_resp_valid = seg_if_ram_rd_resp_valid_reg; + + // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) + assign seg_if_ram_rd_resp_ready_int_early = seg_if_ram_rd_resp_ready || (!temp_seg_if_ram_rd_resp_valid_reg && (!seg_if_ram_rd_resp_valid_reg || !seg_if_ram_rd_resp_valid_int)); + + always @* begin + // transfer sink ready state to source + seg_if_ram_rd_resp_valid_next = seg_if_ram_rd_resp_valid_reg; + temp_seg_if_ram_rd_resp_valid_next = temp_seg_if_ram_rd_resp_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (seg_if_ram_rd_resp_ready_int_reg) begin + // input is ready + if (seg_if_ram_rd_resp_ready || !seg_if_ram_rd_resp_valid_reg) begin + // output is ready or currently not valid, transfer data to output + seg_if_ram_rd_resp_valid_next = seg_if_ram_rd_resp_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_seg_if_ram_rd_resp_valid_next = seg_if_ram_rd_resp_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (seg_if_ram_rd_resp_ready) begin + // input is not ready, but output is ready + seg_if_ram_rd_resp_valid_next = temp_seg_if_ram_rd_resp_valid_reg; + temp_seg_if_ram_rd_resp_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + seg_if_ram_rd_resp_valid_reg <= 1'b0; + seg_if_ram_rd_resp_ready_int_reg <= 1'b0; + temp_seg_if_ram_rd_resp_valid_reg <= 1'b0; + end else begin + seg_if_ram_rd_resp_valid_reg <= seg_if_ram_rd_resp_valid_next; + seg_if_ram_rd_resp_ready_int_reg <= seg_if_ram_rd_resp_ready_int_early; + temp_seg_if_ram_rd_resp_valid_reg <= temp_seg_if_ram_rd_resp_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + seg_if_ram_rd_resp_data_reg <= seg_if_ram_rd_resp_data_int; + end else if (store_axis_temp_to_output) begin + seg_if_ram_rd_resp_data_reg <= temp_seg_if_ram_rd_resp_data_reg; + end + + if (store_axis_int_to_temp) begin + temp_seg_if_ram_rd_resp_data_reg <= seg_if_ram_rd_resp_data_int; + end + end + +end + +endgenerate + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_if_pcie_us.v b/corundum/lib/pcie/rtl/dma_if_pcie_us.v new file mode 100644 index 0000000000000000000000000000000000000000..d91cf2be92e178b0a6987a0b24263c17a97ee1ec --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_if_pcie_us.v @@ -0,0 +1,408 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe DMA interface + */ +module dma_if_pcie_us # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream RC tuser signal width + parameter AXIS_PCIE_RC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 75 : 161, + // PCIe AXI stream RQ tuser signal width + parameter AXIS_PCIE_RQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 60 : 137, + // RQ sequence number width + parameter RQ_SEQ_NUM_WIDTH = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 4 : 6, + // RQ sequence number tracking enable + parameter RQ_SEQ_NUM_ENABLE = 0, + // RAM segment count + parameter SEG_COUNT = AXIS_PCIE_DATA_WIDTH > 64 ? AXIS_PCIE_DATA_WIDTH*2 / 128 : 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH*2/SEG_COUNT, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // RAM select width + parameter RAM_SEL_WIDTH = 2, + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // PCIe address width + parameter PCIE_ADDR_WIDTH = 64, + // PCIe tag count + parameter PCIE_TAG_COUNT = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 64 : 256, + // PCIe tag field width + parameter PCIE_TAG_WIDTH = $clog2(PCIE_TAG_COUNT), + // Support PCIe extended tags + parameter PCIE_EXT_TAG_ENABLE = (PCIE_TAG_COUNT>32), + // Length field width + parameter LEN_WIDTH = 16, + // Tag field width + parameter TAG_WIDTH = 8, + // Operation table size (read) + parameter READ_OP_TABLE_SIZE = PCIE_TAG_COUNT, + // In-flight transmit limit (read) + parameter READ_TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control (read) + parameter READ_TX_FC_ENABLE = 0, + // Operation table size (write) + parameter WRITE_OP_TABLE_SIZE = 2**(RQ_SEQ_NUM_WIDTH-1), + // In-flight transmit limit (write) + parameter WRITE_TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control (write) + parameter WRITE_TX_FC_ENABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (RC) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_rc_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_rc_tkeep, + input wire s_axis_rc_tvalid, + output wire s_axis_rc_tready, + input wire s_axis_rc_tlast, + input wire [AXIS_PCIE_RC_USER_WIDTH-1:0] s_axis_rc_tuser, + + /* + * AXI output (RQ) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep, + output wire m_axis_rq_tvalid, + input wire m_axis_rq_tready, + output wire m_axis_rq_tlast, + output wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser, + + /* + * Transmit sequence number input + */ + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_0, + input wire s_axis_rq_seq_num_valid_0, + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_1, + input wire s_axis_rq_seq_num_valid_1, + + /* + * Transmit flow control + */ + input wire [7:0] pcie_tx_fc_nph_av, + input wire [7:0] pcie_tx_fc_ph_av, + input wire [11:0] pcie_tx_fc_pd_av, + + /* + * AXI read descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_read_desc_pcie_addr, + input wire [RAM_SEL_WIDTH-1:0] s_axis_read_desc_ram_sel, + input wire [RAM_ADDR_WIDTH-1:0] s_axis_read_desc_ram_addr, + input wire [LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire s_axis_read_desc_valid, + output wire s_axis_read_desc_ready, + + /* + * AXI read descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire m_axis_read_desc_status_valid, + + /* + * AXI write descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_write_desc_pcie_addr, + input wire [RAM_SEL_WIDTH-1:0] s_axis_write_desc_ram_sel, + input wire [RAM_ADDR_WIDTH-1:0] s_axis_write_desc_ram_addr, + input wire [LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire s_axis_write_desc_valid, + output wire s_axis_write_desc_ready, + + /* + * AXI write descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire m_axis_write_desc_status_valid, + + /* + * RAM interface + */ + output wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] ram_wr_cmd_sel, + output wire [SEG_COUNT*SEG_BE_WIDTH-1:0] ram_wr_cmd_be, + output wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_wr_cmd_data, + output wire [SEG_COUNT-1:0] ram_wr_cmd_valid, + input wire [SEG_COUNT-1:0] ram_wr_cmd_ready, + output wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] ram_rd_cmd_sel, + output wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_rd_cmd_addr, + output wire [SEG_COUNT-1:0] ram_rd_cmd_valid, + input wire [SEG_COUNT-1:0] ram_rd_cmd_ready, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_rd_resp_data, + input wire [SEG_COUNT-1:0] ram_rd_resp_valid, + output wire [SEG_COUNT-1:0] ram_rd_resp_ready, + + /* + * Configuration + */ + input wire read_enable, + input wire write_enable, + input wire ext_tag_enable, + input wire [15:0] requester_id, + input wire requester_id_enable, + input wire [2:0] max_read_request_size, + input wire [2:0] max_payload_size, + + /* + * Status + */ + output wire status_error_cor, + output wire status_error_uncor +); + +wire [AXIS_PCIE_DATA_WIDTH-1:0] axis_rq_tdata_read; +wire [AXIS_PCIE_KEEP_WIDTH-1:0] axis_rq_tkeep_read; +wire axis_rq_tvalid_read; +wire axis_rq_tready_read; +wire axis_rq_tlast_read; +wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] axis_rq_tuser_read; + +wire [RQ_SEQ_NUM_WIDTH-1:0] axis_rq_seq_num_read_0; +wire axis_rq_seq_num_valid_read_0; +wire [RQ_SEQ_NUM_WIDTH-1:0] axis_rq_seq_num_read_1; +wire axis_rq_seq_num_valid_read_1; + +dma_if_pcie_us_rd #( + .AXIS_PCIE_DATA_WIDTH(AXIS_PCIE_DATA_WIDTH), + .AXIS_PCIE_KEEP_WIDTH(AXIS_PCIE_KEEP_WIDTH), + .AXIS_PCIE_RC_USER_WIDTH(AXIS_PCIE_RC_USER_WIDTH), + .AXIS_PCIE_RQ_USER_WIDTH(AXIS_PCIE_RQ_USER_WIDTH), + .RQ_SEQ_NUM_WIDTH(RQ_SEQ_NUM_WIDTH), + .RQ_SEQ_NUM_ENABLE(RQ_SEQ_NUM_ENABLE), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_SEL_WIDTH(RAM_SEL_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .PCIE_ADDR_WIDTH(PCIE_ADDR_WIDTH), + .PCIE_TAG_COUNT(PCIE_TAG_COUNT), + .PCIE_TAG_WIDTH(PCIE_TAG_WIDTH), + .PCIE_EXT_TAG_ENABLE(PCIE_EXT_TAG_ENABLE), + .LEN_WIDTH(LEN_WIDTH), + .TAG_WIDTH(TAG_WIDTH), + .OP_TABLE_SIZE(READ_OP_TABLE_SIZE), + .TX_LIMIT(READ_TX_LIMIT), + .TX_FC_ENABLE(READ_TX_FC_ENABLE) +) +dma_if_pcie_us_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI input (RC) + */ + .s_axis_rc_tdata(s_axis_rc_tdata), + .s_axis_rc_tkeep(s_axis_rc_tkeep), + .s_axis_rc_tvalid(s_axis_rc_tvalid), + .s_axis_rc_tready(s_axis_rc_tready), + .s_axis_rc_tlast(s_axis_rc_tlast), + .s_axis_rc_tuser(s_axis_rc_tuser), + + /* + * AXI output (RQ) + */ + .m_axis_rq_tdata(axis_rq_tdata_read), + .m_axis_rq_tkeep(axis_rq_tkeep_read), + .m_axis_rq_tvalid(axis_rq_tvalid_read), + .m_axis_rq_tready(axis_rq_tready_read), + .m_axis_rq_tlast(axis_rq_tlast_read), + .m_axis_rq_tuser(axis_rq_tuser_read), + + /* + * Transmit sequence number input + */ + .s_axis_rq_seq_num_0(axis_rq_seq_num_read_0), + .s_axis_rq_seq_num_valid_0(axis_rq_seq_num_valid_read_0), + .s_axis_rq_seq_num_1(axis_rq_seq_num_read_1), + .s_axis_rq_seq_num_valid_1(axis_rq_seq_num_valid_read_1), + + /* + * Transmit flow control + */ + .pcie_tx_fc_nph_av(pcie_tx_fc_nph_av), + + /* + * AXI read descriptor input + */ + .s_axis_read_desc_pcie_addr(s_axis_read_desc_pcie_addr), + .s_axis_read_desc_ram_sel(s_axis_read_desc_ram_sel), + .s_axis_read_desc_ram_addr(s_axis_read_desc_ram_addr), + .s_axis_read_desc_len(s_axis_read_desc_len), + .s_axis_read_desc_tag(s_axis_read_desc_tag), + .s_axis_read_desc_valid(s_axis_read_desc_valid), + .s_axis_read_desc_ready(s_axis_read_desc_ready), + + /* + * AXI read descriptor status output + */ + .m_axis_read_desc_status_tag(m_axis_read_desc_status_tag), + .m_axis_read_desc_status_valid(m_axis_read_desc_status_valid), + + /* + * RAM interface + */ + .ram_wr_cmd_sel(ram_wr_cmd_sel), + .ram_wr_cmd_be(ram_wr_cmd_be), + .ram_wr_cmd_addr(ram_wr_cmd_addr), + .ram_wr_cmd_data(ram_wr_cmd_data), + .ram_wr_cmd_valid(ram_wr_cmd_valid), + .ram_wr_cmd_ready(ram_wr_cmd_ready), + + /* + * Configuration + */ + .enable(read_enable), + .ext_tag_enable(ext_tag_enable), + .requester_id(requester_id), + .requester_id_enable(requester_id_enable), + .max_read_request_size(max_read_request_size), + + /* + * Status + */ + .status_error_cor(status_error_cor), + .status_error_uncor(status_error_uncor) +); + +dma_if_pcie_us_wr #( + .AXIS_PCIE_DATA_WIDTH(AXIS_PCIE_DATA_WIDTH), + .AXIS_PCIE_KEEP_WIDTH(AXIS_PCIE_KEEP_WIDTH), + .AXIS_PCIE_RQ_USER_WIDTH(AXIS_PCIE_RQ_USER_WIDTH), + .RQ_SEQ_NUM_WIDTH(RQ_SEQ_NUM_WIDTH), + .RQ_SEQ_NUM_ENABLE(RQ_SEQ_NUM_ENABLE), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_SEL_WIDTH(RAM_SEL_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .PCIE_ADDR_WIDTH(PCIE_ADDR_WIDTH), + .LEN_WIDTH(LEN_WIDTH), + .TAG_WIDTH(TAG_WIDTH), + .OP_TABLE_SIZE(WRITE_OP_TABLE_SIZE), + .TX_LIMIT(WRITE_TX_LIMIT), + .TX_FC_ENABLE(WRITE_TX_FC_ENABLE) +) +dma_if_pcie_us_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI input (RQ from read DMA IF) + */ + .s_axis_rq_tdata(axis_rq_tdata_read), + .s_axis_rq_tkeep(axis_rq_tkeep_read), + .s_axis_rq_tvalid(axis_rq_tvalid_read), + .s_axis_rq_tready(axis_rq_tready_read), + .s_axis_rq_tlast(axis_rq_tlast_read), + .s_axis_rq_tuser(axis_rq_tuser_read), + + /* + * AXI output (RQ) + */ + .m_axis_rq_tdata(m_axis_rq_tdata), + .m_axis_rq_tkeep(m_axis_rq_tkeep), + .m_axis_rq_tvalid(m_axis_rq_tvalid), + .m_axis_rq_tready(m_axis_rq_tready), + .m_axis_rq_tlast(m_axis_rq_tlast), + .m_axis_rq_tuser(m_axis_rq_tuser), + + /* + * Transmit sequence number input + */ + .s_axis_rq_seq_num_0(s_axis_rq_seq_num_0), + .s_axis_rq_seq_num_valid_0(s_axis_rq_seq_num_valid_0), + .s_axis_rq_seq_num_1(s_axis_rq_seq_num_1), + .s_axis_rq_seq_num_valid_1(s_axis_rq_seq_num_valid_1), + + /* + * Transmit sequence number output (to read DMA IF) + */ + .m_axis_rq_seq_num_0(axis_rq_seq_num_read_0), + .m_axis_rq_seq_num_valid_0(axis_rq_seq_num_valid_read_0), + .m_axis_rq_seq_num_1(axis_rq_seq_num_read_1), + .m_axis_rq_seq_num_valid_1(axis_rq_seq_num_valid_read_1), + + /* + * Transmit flow control + */ + .pcie_tx_fc_ph_av(pcie_tx_fc_ph_av), + .pcie_tx_fc_pd_av(pcie_tx_fc_pd_av), + + /* + * AXI write descriptor input + */ + .s_axis_write_desc_pcie_addr(s_axis_write_desc_pcie_addr), + .s_axis_write_desc_ram_sel(s_axis_write_desc_ram_sel), + .s_axis_write_desc_ram_addr(s_axis_write_desc_ram_addr), + .s_axis_write_desc_len(s_axis_write_desc_len), + .s_axis_write_desc_tag(s_axis_write_desc_tag), + .s_axis_write_desc_valid(s_axis_write_desc_valid), + .s_axis_write_desc_ready(s_axis_write_desc_ready), + + /* + * AXI write descriptor status output + */ + .m_axis_write_desc_status_tag(m_axis_write_desc_status_tag), + .m_axis_write_desc_status_valid(m_axis_write_desc_status_valid), + + /* + * RAM interface + */ + .ram_rd_cmd_sel(ram_rd_cmd_sel), + .ram_rd_cmd_addr(ram_rd_cmd_addr), + .ram_rd_cmd_valid(ram_rd_cmd_valid), + .ram_rd_cmd_ready(ram_rd_cmd_ready), + .ram_rd_resp_data(ram_rd_resp_data), + .ram_rd_resp_valid(ram_rd_resp_valid), + .ram_rd_resp_ready(ram_rd_resp_ready), + + /* + * Configuration + */ + .enable(write_enable), + .requester_id(requester_id), + .requester_id_enable(requester_id_enable), + .max_payload_size(max_payload_size) +); + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_if_pcie_us_rd.v b/corundum/lib/pcie/rtl/dma_if_pcie_us_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..439b3cc773efb13e94ff391089c969835f78af12 --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_if_pcie_us_rd.v @@ -0,0 +1,1592 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe DMA read interface + */ +module dma_if_pcie_us_rd # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream RC tuser signal width + parameter AXIS_PCIE_RC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 75 : 161, + // PCIe AXI stream RQ tuser signal width + parameter AXIS_PCIE_RQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 60 : 137, + // RQ sequence number width + parameter RQ_SEQ_NUM_WIDTH = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 4 : 6, + // RQ sequence number tracking enable + parameter RQ_SEQ_NUM_ENABLE = 0, + // RAM segment count + parameter SEG_COUNT = AXIS_PCIE_DATA_WIDTH > 64 ? AXIS_PCIE_DATA_WIDTH*2 / 128 : 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH*2/SEG_COUNT, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // RAM select width + parameter RAM_SEL_WIDTH = 2, + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // PCIe address width + parameter PCIE_ADDR_WIDTH = 64, + // PCIe tag count + parameter PCIE_TAG_COUNT = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 64 : 256, + // PCIe tag field width + parameter PCIE_TAG_WIDTH = $clog2(PCIE_TAG_COUNT), + // Support PCIe extended tags + parameter PCIE_EXT_TAG_ENABLE = (PCIE_TAG_COUNT>32), + // Length field width + parameter LEN_WIDTH = 16, + // Tag field width + parameter TAG_WIDTH = 8, + // Operation table size + parameter OP_TABLE_SIZE = PCIE_TAG_COUNT, + // In-flight transmit limit + parameter TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control + parameter TX_FC_ENABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (RC) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_rc_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_rc_tkeep, + input wire s_axis_rc_tvalid, + output wire s_axis_rc_tready, + input wire s_axis_rc_tlast, + input wire [AXIS_PCIE_RC_USER_WIDTH-1:0] s_axis_rc_tuser, + + /* + * AXI output (RQ) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep, + output wire m_axis_rq_tvalid, + input wire m_axis_rq_tready, + output wire m_axis_rq_tlast, + output wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser, + + /* + * Transmit sequence number input + */ + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_0, + input wire s_axis_rq_seq_num_valid_0, + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_1, + input wire s_axis_rq_seq_num_valid_1, + + /* + * Transmit flow control + */ + input wire [7:0] pcie_tx_fc_nph_av, + + /* + * AXI read descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_read_desc_pcie_addr, + input wire [RAM_SEL_WIDTH-1:0] s_axis_read_desc_ram_sel, + input wire [RAM_ADDR_WIDTH-1:0] s_axis_read_desc_ram_addr, + input wire [LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire s_axis_read_desc_valid, + output wire s_axis_read_desc_ready, + + /* + * AXI read descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire m_axis_read_desc_status_valid, + + /* + * RAM interface + */ + output wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] ram_wr_cmd_sel, + output wire [SEG_COUNT*SEG_BE_WIDTH-1:0] ram_wr_cmd_be, + output wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_wr_cmd_data, + output wire [SEG_COUNT-1:0] ram_wr_cmd_valid, + input wire [SEG_COUNT-1:0] ram_wr_cmd_ready, + + /* + * Configuration + */ + input wire enable, + input wire ext_tag_enable, + input wire [15:0] requester_id, + input wire requester_id_enable, + input wire [2:0] max_read_request_size, + + /* + * Status + */ + output wire status_error_cor, + output wire status_error_uncor +); + +parameter RAM_WORD_WIDTH = SEG_BE_WIDTH; +parameter RAM_WORD_SIZE = SEG_DATA_WIDTH/RAM_WORD_WIDTH; + +parameter AXIS_PCIE_WORD_WIDTH = AXIS_PCIE_KEEP_WIDTH; +parameter AXIS_PCIE_WORD_SIZE = AXIS_PCIE_DATA_WIDTH/AXIS_PCIE_WORD_WIDTH; + +parameter OFFSET_WIDTH = $clog2(AXIS_PCIE_DATA_WIDTH/8); +parameter RAM_OFFSET_WIDTH = $clog2(SEG_COUNT*SEG_DATA_WIDTH/8); + +parameter OP_TAG_WIDTH = $clog2(OP_TABLE_SIZE); +parameter OP_TABLE_READ_COUNT_WIDTH = PCIE_TAG_WIDTH+1; +parameter OP_TABLE_WRITE_COUNT_WIDTH = LEN_WIDTH; + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, or 256 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + if (AXIS_PCIE_RC_USER_WIDTH != 161) begin + $error("Error: PCIe RC tuser width must be 161 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_RQ_USER_WIDTH != 137) begin + $error("Error: PCIe RQ tuser width must be 137 (instance %m)"); + $finish; + end + end else begin + if (AXIS_PCIE_RC_USER_WIDTH != 75) begin + $error("Error: PCIe RC tuser width must be 75 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_RQ_USER_WIDTH != 60 && AXIS_PCIE_RQ_USER_WIDTH != 62) begin + $error("Error: PCIe RQ tuser width must be 60 or 62 (instance %m)"); + $finish; + end + end + + if (AXIS_PCIE_RQ_USER_WIDTH == 60) begin + if (RQ_SEQ_NUM_ENABLE && RQ_SEQ_NUM_WIDTH != 4) begin + $error("Error: RQ sequence number width must be 4 (instance %m)"); + $finish; + end + + if (PCIE_TAG_COUNT > 64) begin + $error("Error: PCIe tag count must be no larger than 64 (instance %m)"); + $finish; + end + end else begin + if (RQ_SEQ_NUM_ENABLE && RQ_SEQ_NUM_WIDTH != 6) begin + $error("Error: RQ sequence number width must be 6 (instance %m)"); + $finish; + end + + if (PCIE_TAG_COUNT > 256) begin + $error("Error: PCIe tag count must be no larger than 256 (instance %m)"); + $finish; + end + end + + if (RQ_SEQ_NUM_ENABLE && TX_LIMIT > 2**(RQ_SEQ_NUM_WIDTH-1)) begin + $error("Error: TX limit out of range (instance %m)"); + $finish; + end + + if (SEG_COUNT < 2) begin + $error("Error: RAM interface requires at least 2 segments (instance %m)"); + $finish; + end + + if (SEG_COUNT*SEG_DATA_WIDTH != AXIS_PCIE_DATA_WIDTH*2) begin + $error("Error: RAM interface width must be double the PCIe interface width (instance %m)"); + $finish; + end + + if (SEG_BE_WIDTH * 8 != SEG_DATA_WIDTH) begin + $error("Error: RAM interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (2**$clog2(RAM_WORD_WIDTH) != RAM_WORD_WIDTH) begin + $error("Error: RAM word width must be even power of two (instance %m)"); + $finish; + end + + if (RAM_ADDR_WIDTH != SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH)) begin + $error("Error: RAM_ADDR_WIDTH does not match RAM configuration (instance %m)"); + $finish; + end +end + +localparam [3:0] + REQ_MEM_READ = 4'b0000, + REQ_MEM_WRITE = 4'b0001, + REQ_IO_READ = 4'b0010, + REQ_IO_WRITE = 4'b0011, + REQ_MEM_FETCH_ADD = 4'b0100, + REQ_MEM_SWAP = 4'b0101, + REQ_MEM_CAS = 4'b0110, + REQ_MEM_READ_LOCKED = 4'b0111, + REQ_CFG_READ_0 = 4'b1000, + REQ_CFG_READ_1 = 4'b1001, + REQ_CFG_WRITE_0 = 4'b1010, + REQ_CFG_WRITE_1 = 4'b1011, + REQ_MSG = 4'b1100, + REQ_MSG_VENDOR = 4'b1101, + REQ_MSG_ATS = 4'b1110; + +localparam [2:0] + CPL_STATUS_SC = 3'b000, // successful completion + CPL_STATUS_UR = 3'b001, // unsupported request + CPL_STATUS_CRS = 3'b010, // configuration request retry status + CPL_STATUS_CA = 3'b100; // completer abort + +localparam [4:0] + RC_ERROR_NORMAL_TERMINATION = 4'b0000, + RC_ERROR_POISONED = 4'b0001, + RC_ERROR_BAD_STATUS = 4'b0010, + RC_ERROR_INVALID_LENGTH = 4'b0011, + RC_ERROR_MISMATCH = 4'b0100, + RC_ERROR_INVALID_ADDRESS = 4'b0101, + RC_ERROR_INVALID_TAG = 4'b0110, + RC_ERROR_TIMEOUT = 4'b1001, + RC_ERROR_FLR = 4'b1000; + +localparam [1:0] + REQ_STATE_IDLE = 2'd0, + REQ_STATE_START = 2'd1, + REQ_STATE_HEADER = 2'd2; + +reg [1:0] req_state_reg = REQ_STATE_IDLE, req_state_next; + +localparam [1:0] + TLP_STATE_IDLE = 3'd0, + TLP_STATE_HEADER = 3'd1, + TLP_STATE_WRITE = 3'd2, + TLP_STATE_WAIT_END = 3'd3; + +reg [1:0] tlp_state_reg = TLP_STATE_IDLE, tlp_state_next; + +// // datapath control signals +reg tag_table_we_req; + +reg tlp_cmd_ready; + +reg last_cycle; + +reg [3:0] first_be; +reg [3:0] last_be; +reg [10:0] dword_count; +reg req_last_tlp; +reg [PCIE_ADDR_WIDTH-1:0] req_pcie_addr; + +reg [PCIE_ADDR_WIDTH-1:0] req_pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, req_pcie_addr_next; +reg [RAM_ADDR_WIDTH-1:0] req_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, req_addr_next; +reg [LEN_WIDTH-1:0] req_op_count_reg = {LEN_WIDTH{1'b0}}, req_op_count_next; +reg [12:0] req_tlp_count_reg = 13'd0, req_tlp_count_next; + +reg [11:0] lower_addr_reg = 12'd0, lower_addr_next; +reg [12:0] byte_count_reg = 13'd0, byte_count_next; +reg [3:0] error_code_reg = 4'd0, error_code_next; +reg [RAM_SEL_WIDTH-1:0] ram_sel_reg = {RAM_SEL_WIDTH{1'b0}}, ram_sel_next; +reg [RAM_ADDR_WIDTH-1:0] addr_reg = {RAM_ADDR_WIDTH{1'b0}}, addr_next; +reg [RAM_ADDR_WIDTH-1:0] addr_delay_reg = {RAM_ADDR_WIDTH{1'b0}}, addr_delay_next; +reg addr_valid_reg = 1'b0, addr_valid_next; +reg [9:0] op_dword_count_reg = 10'd0, op_dword_count_next; +reg [12:0] op_count_reg = 13'd0, op_count_next; +reg [SEG_COUNT-1:0] ram_mask_reg = {SEG_COUNT{1'b0}}, ram_mask_next; +reg [SEG_COUNT-1:0] ram_mask_0_reg = {SEG_COUNT{1'b0}}, ram_mask_0_next; +reg [SEG_COUNT-1:0] ram_mask_1_reg = {SEG_COUNT{1'b0}}, ram_mask_1_next; +reg ram_wrap_reg = 1'b0, ram_wrap_next; +reg [OFFSET_WIDTH+1-1:0] cycle_byte_count_reg = {OFFSET_WIDTH+1{1'b0}}, cycle_byte_count_next; +reg [RAM_OFFSET_WIDTH-1:0] start_offset_reg = {RAM_OFFSET_WIDTH{1'b0}}, start_offset_next; +reg [RAM_OFFSET_WIDTH-1:0] end_offset_reg = {RAM_OFFSET_WIDTH{1'b0}}, end_offset_next; +reg [PCIE_TAG_WIDTH-1:0] pcie_tag_reg = {PCIE_TAG_WIDTH{1'b0}}, pcie_tag_next; +reg [OP_TAG_WIDTH-1:0] op_tag_reg = {OP_TAG_WIDTH{1'b0}}, op_tag_next; +reg final_cpl_reg = 1'b0, final_cpl_next; +reg finish_tag_reg = 1'b0, finish_tag_next; + +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] rc_tdata_int_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}, rc_tdata_int_next; +reg rc_tvalid_int_reg = 1'b0, rc_tvalid_int_next; + +reg [RAM_SEL_WIDTH-1:0] tlp_cmd_ram_sel_reg = {RAM_SEL_WIDTH{1'b0}}, tlp_cmd_ram_sel_next; +reg [RAM_ADDR_WIDTH-1:0] tlp_cmd_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, tlp_cmd_addr_next; +reg [OP_TAG_WIDTH-1:0] tlp_cmd_op_tag_reg = {OP_TAG_WIDTH{1'b0}}, tlp_cmd_op_tag_next; +reg [TAG_WIDTH-1:0] tlp_cmd_tag_reg = {TAG_WIDTH{1'b0}}, tlp_cmd_tag_next; +reg [PCIE_TAG_WIDTH-1:0] tlp_cmd_pcie_tag_reg = {PCIE_TAG_WIDTH{1'b0}}, tlp_cmd_pcie_tag_next; +reg tlp_cmd_last_reg = 1'b0, tlp_cmd_last_next; +reg tlp_cmd_valid_reg = 1'b0, tlp_cmd_valid_next; + +reg [RAM_SEL_WIDTH-1:0] tag_table_sel[(2**PCIE_TAG_WIDTH)-1:0]; +reg [RAM_ADDR_WIDTH-1:0] tag_table_addr[(2**PCIE_TAG_WIDTH)-1:0]; +reg [OP_TAG_WIDTH-1:0] tag_table_op_tag[(2**PCIE_TAG_WIDTH)-1:0]; +reg tag_table_we_tlp_reg = 1'b0, tag_table_we_tlp_next; + +reg [10:0] max_read_request_size_dw_reg = 11'd0; + +reg have_credit_reg = 1'b0; + +reg [RQ_SEQ_NUM_WIDTH-1:0] active_tx_count_reg = {RQ_SEQ_NUM_WIDTH{1'b0}}; +reg active_tx_count_av_reg = 1'b1; +reg inc_active_tx; + +reg s_axis_rc_tready_reg = 1'b0, s_axis_rc_tready_next; +reg s_axis_read_desc_ready_reg = 1'b0, s_axis_read_desc_ready_next; + +reg [TAG_WIDTH-1:0] m_axis_read_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_read_desc_status_tag_next; +reg m_axis_read_desc_status_valid_reg = 1'b0, m_axis_read_desc_status_valid_next; + +reg status_error_cor_reg = 1'b0, status_error_cor_next; +reg status_error_uncor_reg = 1'b0, status_error_uncor_next; + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_int; +reg m_axis_rq_tvalid_int; +reg m_axis_rq_tready_int_reg = 1'b0; +reg m_axis_rq_tlast_int; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_int; +wire m_axis_rq_tready_int_early; + +reg [SEG_COUNT*RAM_SEL_WIDTH-1:0] ram_wr_cmd_sel_int; +reg [SEG_COUNT*SEG_BE_WIDTH-1:0] ram_wr_cmd_be_int; +reg [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr_int; +reg [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_wr_cmd_data_int; +reg [SEG_COUNT-1:0] ram_wr_cmd_valid_int; +reg [SEG_COUNT-1:0] ram_wr_cmd_ready_int_reg = 1'b0; +wire [SEG_COUNT-1:0] ram_wr_cmd_ready_int_early; + +assign s_axis_rc_tready = s_axis_rc_tready_reg; +assign s_axis_read_desc_ready = s_axis_read_desc_ready_reg; + +assign m_axis_read_desc_status_tag = m_axis_read_desc_status_tag_reg; +assign m_axis_read_desc_status_valid = m_axis_read_desc_status_valid_reg; + +assign status_error_cor = status_error_cor_reg; +assign status_error_uncor = status_error_uncor_reg; + +wire [PCIE_ADDR_WIDTH-1:0] req_pcie_addr_plus_max_read_request = req_pcie_addr_reg + {max_read_request_size_dw_reg, 2'b00}; +wire [PCIE_ADDR_WIDTH-1:0] req_pcie_addr_plus_op_count = req_pcie_addr_reg + req_op_count_reg; + +// PCIe tag management +wire [PCIE_TAG_WIDTH-1:0] new_tag; +wire new_tag_valid; +reg new_tag_ready; + +wire [PCIE_TAG_COUNT-1:0] active_tags; + +pcie_tag_manager #( + .PCIE_TAG_COUNT(PCIE_TAG_COUNT), + .PCIE_TAG_WIDTH(PCIE_TAG_WIDTH), + .PCIE_EXT_TAG_ENABLE(PCIE_EXT_TAG_ENABLE) +) +pcie_tag_manager_inst ( + .clk(clk), + .rst(rst), + + .m_axis_tag(new_tag), + .m_axis_tag_valid(new_tag_valid), + .m_axis_tag_ready(new_tag_ready), + + .s_axis_tag(pcie_tag_reg), + .s_axis_tag_valid(finish_tag_reg), + + .ext_tag_enable(ext_tag_enable), + + .active_tags(active_tags) +); + +// operation tag management +wire [OP_TAG_WIDTH-1:0] op_table_start_ptr; +wire op_table_start_ptr_valid; +reg [TAG_WIDTH-1:0] op_table_start_tag; +reg op_table_start_en; +reg [OP_TAG_WIDTH-1:0] op_table_finish_ptr; +reg op_table_finish_en; +reg [OP_TAG_WIDTH-1:0] op_table_read_start_ptr; +reg op_table_read_start_commit; +reg op_table_read_start_en; +reg [OP_TAG_WIDTH-1:0] op_table_read_finish_ptr; +reg op_table_read_finish_en; + +reg [2**OP_TAG_WIDTH-1:0] op_table_active = 0; +reg [TAG_WIDTH-1:0] op_table_tag [2**OP_TAG_WIDTH-1:0]; +reg op_table_init [2**OP_TAG_WIDTH-1:0]; +reg op_table_read_init [2**OP_TAG_WIDTH-1:0]; +reg op_table_read_commit [2**OP_TAG_WIDTH-1:0]; +reg [OP_TABLE_READ_COUNT_WIDTH-1:0] op_table_read_count_start [2**OP_TAG_WIDTH-1:0]; +reg [OP_TABLE_READ_COUNT_WIDTH-1:0] op_table_read_count_finish [2**OP_TAG_WIDTH-1:0]; + +priority_encoder #( + .WIDTH(2**OP_TAG_WIDTH), + .LSB_PRIORITY("HIGH") +) +op_table_start_ptr_enc_inst ( + .input_unencoded(~op_table_active), + .output_valid(op_table_start_ptr_valid), + .output_encoded(op_table_start_ptr), + .output_unencoded() +); + +integer i; + +initial begin + for (i = 0; i < 2**OP_TAG_WIDTH; i = i + 1) begin + op_table_tag[i] = 0; + op_table_init[i] = 0; + op_table_read_init[i] = 0; + op_table_read_commit[i] = 0; + op_table_read_count_start[i] = 0; + op_table_read_count_finish[i] = 0; + end + + for (i = 0; i < 2**PCIE_TAG_WIDTH; i = i + 1) begin + tag_table_addr[i] = 0; + tag_table_op_tag[i] = 0; + end +end + +always @* begin + req_state_next = REQ_STATE_IDLE; + + s_axis_read_desc_ready_next = 1'b0; + + req_pcie_addr_next = req_pcie_addr_reg; + req_addr_next = req_addr_reg; + req_op_count_next = req_op_count_reg; + req_tlp_count_next = req_tlp_count_reg; + + tlp_cmd_ram_sel_next = tlp_cmd_ram_sel_reg; + tlp_cmd_addr_next = tlp_cmd_addr_reg; + tlp_cmd_op_tag_next = tlp_cmd_op_tag_reg; + tlp_cmd_tag_next = tlp_cmd_tag_reg; + tlp_cmd_pcie_tag_next = tlp_cmd_pcie_tag_reg; + tlp_cmd_last_next = tlp_cmd_last_reg; + tlp_cmd_valid_next = tlp_cmd_valid_reg && !tlp_cmd_ready; + + inc_active_tx = 1'b0; + + m_axis_rq_tdata_int = {AXIS_PCIE_DATA_WIDTH{1'b0}}; + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; + m_axis_rq_tvalid_int = 1'b0; + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tlast_int = 1'b1; + end else begin + m_axis_rq_tlast_int = 1'b0; + end + m_axis_rq_tuser_int = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + + m_axis_rq_tdata_int[1:0] = 2'b0; // address type + m_axis_rq_tdata_int[63:2] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:2]; // address + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tdata_int[74:64] = 11'd0; // DWORD count + m_axis_rq_tdata_int[78:75] = REQ_MEM_READ; // request type - memory read + m_axis_rq_tdata_int[79] = 1'b0; // poisoned request + m_axis_rq_tdata_int[95:80] = requester_id; + m_axis_rq_tdata_int[103:96] = new_tag; + m_axis_rq_tdata_int[119:104] = 16'd0; // completer ID + m_axis_rq_tdata_int[120] = requester_id_enable; + m_axis_rq_tdata_int[123:121] = 3'b000; // traffic class + m_axis_rq_tdata_int[126:124] = 3'b000; // attr + m_axis_rq_tdata_int[127] = 1'b0; // force ECRC + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tkeep_int = 16'b0000000000001111; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_rq_tkeep_int = 8'b00001111; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_rq_tkeep_int = 4'b1111; + end else begin + m_axis_rq_tkeep_int = 2'b11; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[3:0] = 4'd0; // first BE 0 + m_axis_rq_tuser_int[7:4] = 4'd0; // first BE 1 + m_axis_rq_tuser_int[11:8] = 4'd0; // last BE 0 + m_axis_rq_tuser_int[15:12] = 4'd0; // last BE 1 + m_axis_rq_tuser_int[19:16] = 3'd0; // addr_offset + m_axis_rq_tuser_int[21:20] = 2'b01; // is_sop + m_axis_rq_tuser_int[23:22] = 2'd0; // is_sop0_ptr + m_axis_rq_tuser_int[25:24] = 2'd0; // is_sop1_ptr + m_axis_rq_tuser_int[27:26] = 2'b01; // is_eop + m_axis_rq_tuser_int[31:28] = 4'd3; // is_eop0_ptr + m_axis_rq_tuser_int[35:32] = 4'd0; // is_eop1_ptr + m_axis_rq_tuser_int[36] = 1'b0; // discontinue + m_axis_rq_tuser_int[38:37] = 2'b00; // tph_present + m_axis_rq_tuser_int[42:39] = 4'b0000; // tph_type + m_axis_rq_tuser_int[44:43] = 2'b00; // tph_indirect_tag_en + m_axis_rq_tuser_int[60:45] = 16'd0; // tph_st_tag + m_axis_rq_tuser_int[66:61] = 6'd0; // seq_num0 + m_axis_rq_tuser_int[72:67] = 6'd0; // seq_num1 + m_axis_rq_tuser_int[136:73] = 64'd0; // parity + end else begin + m_axis_rq_tuser_int[3:0] = 4'd0; // first BE + m_axis_rq_tuser_int[7:4] = 4'd0; // last BE + m_axis_rq_tuser_int[10:8] = 3'd0; // addr_offset + m_axis_rq_tuser_int[11] = 1'b0; // discontinue + m_axis_rq_tuser_int[12] = 1'b0; // tph_present + m_axis_rq_tuser_int[14:13] = 2'b00; // tph_type + m_axis_rq_tuser_int[15] = 1'b0; // tph_indirect_tag_en + m_axis_rq_tuser_int[23:16] = 8'd0; // tph_st_tag + m_axis_rq_tuser_int[27:24] = 4'd0; // seq_num + m_axis_rq_tuser_int[59:28] = 32'd0; // parity + if (AXIS_PCIE_RQ_USER_WIDTH == 62) begin + m_axis_rq_tuser_int[61:60] = 2'd0; // seq_num + end + end + + new_tag_ready = 1'b0; + op_table_start_tag = s_axis_read_desc_tag; + op_table_start_en = 1'b0; + + op_table_read_start_ptr = tlp_cmd_op_tag_reg; + op_table_read_start_commit = 1'b0; + op_table_read_start_en = 1'b0; + + // TLP size computation + if (req_op_count_reg + req_pcie_addr_reg[1:0] <= {max_read_request_size_dw_reg, 2'b00}) begin + // packet smaller than max read request size + if (req_pcie_addr_reg[12] != req_pcie_addr_plus_op_count[12]) begin + // crosses 4k boundary + req_tlp_count_next = 13'h1000 - req_pcie_addr_reg[11:0]; + dword_count = 11'h400 - req_pcie_addr_reg[11:2]; + req_last_tlp = req_pcie_addr_plus_op_count[11:0] == 0; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]+1; + req_pcie_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + req_tlp_count_next = req_op_count_reg; + dword_count = (req_op_count_reg + req_pcie_addr_reg[1:0] + 3) >> 2; + req_last_tlp = 1'b1; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]; + req_pcie_addr[11:0] = req_pcie_addr_reg[11:0] + req_op_count_reg; + end + end else begin + // packet larger than max read request size + if (req_pcie_addr_reg[12] != req_pcie_addr_plus_max_read_request[12]) begin + // crosses 4k boundary + req_tlp_count_next = 13'h1000 - req_pcie_addr_reg[11:0]; + dword_count = 11'h400 - req_pcie_addr_reg[11:2]; + req_last_tlp = 1'b0; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]+1; + req_pcie_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + req_tlp_count_next = {max_read_request_size_dw_reg, 2'b00} - req_pcie_addr_reg[1:0]; + dword_count = max_read_request_size_dw_reg; + req_last_tlp = 1'b0; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]; + req_pcie_addr[11:0] = {req_pcie_addr_reg[11:2] + max_read_request_size_dw_reg, 2'b00}; + end + end + + first_be = 4'b1111 << req_pcie_addr_reg[1:0]; + last_be = 4'b1111 >> (3 - ((req_pcie_addr_reg[1:0] + req_tlp_count_next[1:0] - 1) & 3)); + + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tdata_int[74:64] = dword_count; // DWORD count + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[3:0] = dword_count == 1 ? first_be & last_be : first_be; // first BE 0 + m_axis_rq_tuser_int[7:4] = 4'd0; // first BE 1 + m_axis_rq_tuser_int[11:8] = dword_count == 1 ? 4'b0000 : last_be; // last BE 0 + m_axis_rq_tuser_int[15:12] = 4'd0; // last BE 1 + end else begin + m_axis_rq_tuser_int[3:0] = dword_count == 1 ? first_be & last_be : first_be; // first BE + m_axis_rq_tuser_int[7:4] = dword_count == 1 ? 4'b0000 : last_be; // last BE + end + + // TLP segmentation and request generation + case (req_state_reg) + REQ_STATE_IDLE: begin + s_axis_read_desc_ready_next = enable && !tlp_cmd_valid_reg && op_table_start_ptr_valid; + + if (s_axis_read_desc_ready && s_axis_read_desc_valid) begin + s_axis_read_desc_ready_next = 1'b0; + tlp_cmd_ram_sel_next = s_axis_read_desc_ram_sel; + req_pcie_addr_next = s_axis_read_desc_pcie_addr; + req_addr_next = s_axis_read_desc_ram_addr; + req_op_count_next = s_axis_read_desc_len; + tlp_cmd_tag_next = s_axis_read_desc_tag; + tlp_cmd_op_tag_next = op_table_start_ptr; + op_table_start_tag = s_axis_read_desc_tag; + op_table_start_en = 1'b1; + req_state_next = REQ_STATE_START; + end else begin + req_state_next = REQ_STATE_IDLE; + end + end + REQ_STATE_START: begin + if (m_axis_rq_tready_int_reg && !tlp_cmd_valid_reg && new_tag_valid && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + m_axis_rq_tvalid_int = 1'b1; + + inc_active_tx = 1'b1; + + if (AXIS_PCIE_DATA_WIDTH > 64) begin + req_pcie_addr_next = req_pcie_addr; + req_addr_next = req_addr_reg + req_tlp_count_next; + req_op_count_next = req_op_count_reg - req_tlp_count_next; + + new_tag_ready = 1'b1; + + tlp_cmd_addr_next = req_addr_reg; + tlp_cmd_pcie_tag_next = new_tag; + tlp_cmd_last_next = req_last_tlp; + tlp_cmd_valid_next = 1'b1; + + op_table_read_start_ptr = tlp_cmd_op_tag_reg; + op_table_read_start_commit = req_last_tlp; + op_table_read_start_en = 1'b1; + + if (!req_last_tlp) begin + req_state_next = REQ_STATE_START; + end else begin + s_axis_read_desc_ready_next = 1'b0; + req_state_next = REQ_STATE_IDLE; + end + end else begin + req_state_next = REQ_STATE_HEADER; + end + end else begin + req_state_next = REQ_STATE_START; + end + end + REQ_STATE_HEADER: begin + if (m_axis_rq_tready_int_reg && !tlp_cmd_valid_reg && new_tag_valid) begin + req_pcie_addr_next = req_pcie_addr; + req_addr_next = req_addr_reg + req_tlp_count_next; + req_op_count_next = req_op_count_reg - req_tlp_count_next; + + new_tag_ready = 1'b1; + + m_axis_rq_tdata_int[10:0] = dword_count; // DWORD count + m_axis_rq_tdata_int[14:11] = REQ_MEM_READ; // request type - memory read + m_axis_rq_tdata_int[15] = 1'b0; // poisoned request + m_axis_rq_tdata_int[31:16] = requester_id; + m_axis_rq_tdata_int[40:32] = new_tag; + m_axis_rq_tdata_int[55:41] = 16'd0; // completer ID + m_axis_rq_tdata_int[56] = requester_id_enable; + m_axis_rq_tdata_int[59:57] = 3'b000; // traffic class + m_axis_rq_tdata_int[62:60] = 3'b000; // attr + m_axis_rq_tdata_int[63] = 1'b0; // force ECRC + m_axis_rq_tlast_int = 1'b1; + m_axis_rq_tvalid_int = 1'b1; + + tlp_cmd_addr_next = req_addr_reg; + tlp_cmd_pcie_tag_next = new_tag; + tlp_cmd_last_next = req_last_tlp; + tlp_cmd_valid_next = 1'b1; + + op_table_read_start_ptr = tlp_cmd_op_tag_reg; + op_table_read_start_commit = req_last_tlp; + op_table_read_start_en = 1'b1; + + if (!req_last_tlp) begin + req_state_next = REQ_STATE_START; + end else begin + s_axis_read_desc_ready_next = 1'b0; + req_state_next = REQ_STATE_IDLE; + end + end else begin + req_state_next = REQ_STATE_HEADER; + end + end + endcase +end + +always @* begin + tlp_state_next = TLP_STATE_IDLE; + + last_cycle = 1'b0; + + tag_table_we_tlp_next = 1'b0; + + s_axis_rc_tready_next = 1'b0; + + lower_addr_next = lower_addr_reg; + byte_count_next = byte_count_reg; + error_code_next = error_code_reg; + ram_sel_next = ram_sel_reg; + addr_next = addr_reg; + addr_delay_next = addr_delay_reg; + addr_valid_next = addr_valid_reg; + op_count_next = op_count_reg; + ram_mask_next = ram_mask_reg; + ram_mask_0_next = ram_mask_0_reg; + ram_mask_1_next = ram_mask_1_reg; + ram_wrap_next = ram_wrap_reg; + cycle_byte_count_next = cycle_byte_count_reg; + start_offset_next = start_offset_reg; + end_offset_next = end_offset_reg; + op_dword_count_next = op_dword_count_reg; + pcie_tag_next = pcie_tag_reg; + op_tag_next = op_tag_reg; + final_cpl_next = final_cpl_reg; + finish_tag_next = 1'b0; + offset_next = offset_reg; + + rc_tdata_int_next = rc_tdata_int_reg; + rc_tvalid_int_next = rc_tvalid_int_reg; + + ram_wr_cmd_sel_int = {SEG_COUNT{ram_sel_reg}}; + if (!ram_wrap_reg) begin + ram_wr_cmd_be_int = ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} << start_offset_reg) & ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} >> (SEG_COUNT*SEG_BE_WIDTH-1-end_offset_reg)); + end else begin + ram_wr_cmd_be_int = ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} << start_offset_reg) | ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} >> (SEG_COUNT*SEG_BE_WIDTH-1-end_offset_reg)); + end + for (i = 0; i < SEG_COUNT; i = i + 1) begin + ram_wr_cmd_addr_int[i*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = addr_delay_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]; + if (ram_mask_1_reg[i]) begin + ram_wr_cmd_addr_int[i*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = addr_delay_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]+1; + end + end + ram_wr_cmd_data_int = {3{rc_tdata_int_reg}} >> (AXIS_PCIE_DATA_WIDTH - offset_reg*8); + ram_wr_cmd_valid_int = {SEG_COUNT{1'b0}}; + + status_error_cor_next = 1'b0; + status_error_uncor_next = 1'b0; + + // Write generation + if (rc_tvalid_int_reg && !(~ram_wr_cmd_ready_int_reg & ram_mask_reg)) begin + rc_tvalid_int_next = 1'b0; + + ram_wr_cmd_sel_int = {SEG_COUNT{ram_sel_reg}}; + if (!ram_wrap_reg) begin + ram_wr_cmd_be_int = ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} << start_offset_reg) & ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} >> (SEG_COUNT*SEG_BE_WIDTH-1-end_offset_reg)); + end else begin + ram_wr_cmd_be_int = ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} << start_offset_reg) | ({SEG_COUNT*SEG_BE_WIDTH{1'b1}} >> (SEG_COUNT*SEG_BE_WIDTH-1-end_offset_reg)); + end + for (i = 0; i < SEG_COUNT; i = i + 1) begin + if (ram_mask_0_reg[i]) begin + ram_wr_cmd_addr_int[i*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = addr_delay_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]; + end + if (ram_mask_1_reg[i]) begin + ram_wr_cmd_addr_int[i*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = addr_delay_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]+1; + end + end + ram_wr_cmd_data_int = {3{rc_tdata_int_reg}} >> (AXIS_PCIE_DATA_WIDTH - offset_reg*8); + ram_wr_cmd_valid_int = ram_mask_reg; + end + + // TLP response handling + case (tlp_state_reg) + TLP_STATE_IDLE: begin + // idle state, wait for completion + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_rc_tready_next = !rc_tvalid_int_next; + + if (s_axis_rc_tready && s_axis_rc_tvalid) begin + // header fields + lower_addr_next = s_axis_rc_tdata[11:0]; // lower address + error_code_next = s_axis_rc_tdata[15:12]; // error code + byte_count_next = s_axis_rc_tdata[28:16]; // byte count + //s_axis_rc_tdata[29]; // locked read + //s_axis_rc_tdata[30]; // request completed + op_dword_count_next = s_axis_rc_tdata[42:32]; // DWORD count + //s_axis_rc_tdata[45:43]; // completion status + //s_axis_rc_tdata[46]; // poisoned completion + //s_axis_rc_tdata[63:48]; // requester ID + pcie_tag_next = s_axis_rc_tdata[71:64]; // tag + //s_axis_rc_tdata[87:72]; // completer ID + //s_axis_rc_tdata[91:89]; // attr + //s_axis_rc_tdata[94:92]; // tc + + // tuser fields + //s_axis_rc_tuser[31:0]; // byte enables + //s_axis_rc_tuser[32]; // is_sof_0 + //s_axis_rc_tuser[33]; // is_sof_1 + //s_axis_rc_tuser[37:34]; // is_eof_0 + //s_axis_rc_tuser[41:38]; // is_eof_1 + //s_axis_rc_tuser[42]; // discontinue + //s_axis_rc_tuser[74:43]; // parity + + ram_sel_next = tag_table_sel[pcie_tag_next]; + if (!addr_valid_reg || pcie_tag_reg != pcie_tag_next) begin + // current AXI address not valid, so read it from table + addr_next = tag_table_addr[pcie_tag_next]; + end + + offset_next = addr_next[OFFSET_WIDTH-1:0] - (12+lower_addr_next[1:0]); + + if (byte_count_next > (op_dword_count_next << 2) - lower_addr_next[1:0]) begin + // more completions to follow + op_count_next = (op_dword_count_next << 2) - lower_addr_next[1:0]; + final_cpl_next = 1'b0; + + if (op_dword_count_next > (AXIS_PCIE_DATA_WIDTH/32-3)) begin + // more than one cycle + cycle_byte_count_next = (AXIS_PCIE_DATA_WIDTH/8-12)-lower_addr_next[1:0]; + last_cycle = 1'b0; + + start_offset_next = addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + end else begin + // one cycle + cycle_byte_count_next = op_count_next; + last_cycle = 1'b1; + + start_offset_next = addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + end + end else begin + // last completion + op_count_next = byte_count_next; + final_cpl_next = 1'b1; + + if (op_count_next > (AXIS_PCIE_DATA_WIDTH/8-12)-lower_addr_next[1:0]) begin + // more than one cycle + cycle_byte_count_next = (AXIS_PCIE_DATA_WIDTH/8-12)-lower_addr_next[1:0]; + last_cycle = 1'b0; + + start_offset_next = addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + end else begin + // one cycle + cycle_byte_count_next = op_count_next; + last_cycle = 1'b1; + + start_offset_next = addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + end + end + + ram_mask_0_next = {SEG_COUNT{1'b1}} << (start_offset_next >> $clog2(SEG_BE_WIDTH)); + ram_mask_1_next = {SEG_COUNT{1'b1}} >> (SEG_COUNT-1-(end_offset_next >> $clog2(SEG_BE_WIDTH))); + + if (!ram_wrap_next) begin + ram_mask_next = ram_mask_0_next & ram_mask_1_next; + ram_mask_0_next = ram_mask_0_next & ram_mask_1_next; + ram_mask_1_next = 0; + end else begin + ram_mask_next = ram_mask_0_next | ram_mask_1_next; + end + + addr_delay_next = addr_next; + addr_next = addr_next + cycle_byte_count_next; + op_count_next = op_count_next - cycle_byte_count_next; + + op_tag_next = tag_table_op_tag[pcie_tag_next]; + + if (active_tags[pcie_tag_next] && error_code_next == RC_ERROR_NORMAL_TERMINATION) begin + // no error + addr_valid_next = !final_cpl_next; + rc_tdata_int_next = s_axis_rc_tdata; + rc_tvalid_int_next = 1'b1; + if (last_cycle) begin + if (final_cpl_next) begin + // last completion in current read request (PCIe tag) + + // release tag + finish_tag_next = 1'b1; + end else begin + // more completions to come, store current address + tag_table_we_tlp_next = 1'b1; + end + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WRITE; + end + end else if (error_code_next == RC_ERROR_MISMATCH) begin + // mismatched fields + // Handle as malformed TLP (2.3.2) + // drop TLP and report uncorrectable error + status_error_uncor_next = 1'b1; + addr_valid_next = 1'b0; + if (s_axis_rc_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_WAIT_END; + end + end else if (!active_tags[pcie_tag_next] || error_code_next == RC_ERROR_INVALID_TAG) begin + // invalid tag + // Handle as unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + // drop TLP and report correctable error + status_error_cor_next = 1'b1; + addr_valid_next = 1'b0; + if (s_axis_rc_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_WAIT_END; + end + end else begin + // request terminated by other error (tag valid) + // report error + case (error_code_next) + RC_ERROR_POISONED: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.3) + RC_ERROR_BAD_STATUS: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.1) + RC_ERROR_INVALID_LENGTH: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_MISMATCH: status_error_uncor_next = 1'b1; // malformed TLP (2.3.2) + RC_ERROR_INVALID_ADDRESS: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_INVALID_TAG: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_TIMEOUT: status_error_uncor_next = 1'b1; // uncorrectable (6.2.3.2.4.4) + RC_ERROR_FLR: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + default: status_error_uncor_next = 1'b1; + endcase + // last request in current transfer + addr_valid_next = 1'b0; + + // release tag + finish_tag_next = 1'b1; + + // drop TLP + if (s_axis_rc_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_WAIT_END; + end + end + end else begin + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + s_axis_rc_tready_next = 1'b1; + + if (s_axis_rc_tready && s_axis_rc_tvalid) begin + // header fields + lower_addr_next = s_axis_rc_tdata[11:0]; // lower address + error_code_next = s_axis_rc_tdata[15:12]; // error code + byte_count_next = s_axis_rc_tdata[28:16]; // byte count + //s_axis_rc_tdata[29]; // locked read + //s_axis_rc_tdata[30]; // request completed + op_dword_count_next = s_axis_rc_tdata[42:32]; // DWORD count + //s_axis_rc_tdata[45:43]; // completion status + //s_axis_rc_tdata[46]; // poisoned completion + //s_axis_rc_tdata[63:48]; // requester ID + + // tuser fields + //s_axis_rc_tuser[31:0]; // byte enables + //s_axis_rc_tuser[32]; // is_sof_0 + //s_axis_rc_tuser[33]; // is_sof_1 + //s_axis_rc_tuser[37:34]; // is_eof_0 + //s_axis_rc_tuser[41:38]; // is_eof_1 + //s_axis_rc_tuser[42]; // discontinue + //s_axis_rc_tuser[74:43]; // parity + + if (byte_count_next > (op_dword_count_next << 2) - lower_addr_next[1:0]) begin + // more completions to follow + op_count_next = (op_dword_count_next << 2) - lower_addr_next[1:0]; + final_cpl_next = 1'b0; + end else begin + // last completion + op_count_next = byte_count_next; + final_cpl_next = 1'b1; + end + + if (s_axis_rc_tlast) begin + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_IDLE; + end else begin + s_axis_rc_tready_next = !rc_tvalid_int_next; + tlp_state_next = TLP_STATE_HEADER; + end + end else begin + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_IDLE; + end + end + end + TLP_STATE_HEADER: begin + // header state; process header (64 bit interface only) + s_axis_rc_tready_next = !rc_tvalid_int_next; + + if (s_axis_rc_tready && s_axis_rc_tvalid) begin + pcie_tag_next = s_axis_rc_tdata[7:0]; // tag + //s_axis_rc_tdata[23:8]; // completer ID + //s_axis_rc_tdata[27:25]; // attr + //s_axis_rc_tdata[30:28]; // tc + + ram_sel_next = tag_table_sel[pcie_tag_next]; + if (!addr_valid_reg || pcie_tag_reg != pcie_tag_next) begin + // current AXI address not valid, so read it from table + addr_next = tag_table_addr[pcie_tag_next]; + end + + offset_next = addr_next[OFFSET_WIDTH-1:0] - (4+lower_addr_reg[1:0]); + + if (op_count_next > 4-lower_addr_reg[1:0]) begin + // more than one cycle + cycle_byte_count_next = 4-lower_addr_reg[1:0]; + last_cycle = 1'b0; + end else begin + // one cycle + cycle_byte_count_next = op_count_next; + last_cycle = 1'b1; + end + start_offset_next = addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + + ram_mask_0_next = {SEG_COUNT{1'b1}} << (start_offset_next >> $clog2(SEG_BE_WIDTH)); + ram_mask_1_next = {SEG_COUNT{1'b1}} >> (SEG_COUNT-1-(end_offset_next >> $clog2(SEG_BE_WIDTH))); + + if (!ram_wrap_next) begin + ram_mask_next = ram_mask_0_next & ram_mask_1_next; + ram_mask_0_next = ram_mask_0_next & ram_mask_1_next; + ram_mask_1_next = 0; + end else begin + ram_mask_next = ram_mask_0_next | ram_mask_1_next; + end + + addr_delay_next = addr_next; + addr_next = addr_next + cycle_byte_count_next; + op_count_next = op_count_next - cycle_byte_count_next; + + op_tag_next = tag_table_op_tag[pcie_tag_next]; + + if (active_tags[pcie_tag_next] && error_code_reg == RC_ERROR_NORMAL_TERMINATION) begin + // no error + addr_valid_next = !final_cpl_next; + rc_tdata_int_next = s_axis_rc_tdata; + rc_tvalid_int_next = 1'b1; + if (last_cycle) begin + if (final_cpl_next) begin + // last completion in current read request (PCIe tag) + + // release tag + finish_tag_next = 1'b1; + end else begin + // more completions to come, store current address + tag_table_we_tlp_next = 1'b1; + end + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WRITE; + end + end else if (error_code_next == RC_ERROR_MISMATCH) begin + // mismatched fields + // Handle as malformed TLP (2.3.2) + // drop TLP and report uncorrectable error + status_error_uncor_next = 1'b1; + addr_valid_next = 1'b0; + s_axis_rc_tready_next = 1'b1; + if (s_axis_rc_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end else if (!active_tags[pcie_tag_next] || error_code_next == RC_ERROR_INVALID_TAG) begin + // invalid tag or mismatched fields (tag invalid) + // Handle as unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + // drop TLP and report correctable error + status_error_cor_next = 1'b1; + addr_valid_next = 1'b0; + s_axis_rc_tready_next = 1'b1; + if (s_axis_rc_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end else begin + // request terminated by other error (tag valid) + // report error + case (error_code_next) + RC_ERROR_POISONED: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.3) + RC_ERROR_BAD_STATUS: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.1) + RC_ERROR_INVALID_LENGTH: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_MISMATCH: status_error_uncor_next = 1'b1; // malformed TLP (2.3.2) + RC_ERROR_INVALID_ADDRESS: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_INVALID_TAG: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_TIMEOUT: status_error_uncor_next = 1'b1; // uncorrectable (6.2.3.2.4.4) + RC_ERROR_FLR: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + default: status_error_uncor_next = 1'b1; + endcase + // last request in current transfer + addr_valid_next = 1'b0; + + // release tag + finish_tag_next = 1'b1; + + // drop TLP + s_axis_rc_tready_next = 1'b1; + if (s_axis_rc_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end + end else begin + tlp_state_next = TLP_STATE_HEADER; + end + end + TLP_STATE_WRITE: begin + // write state - generate write operations + s_axis_rc_tready_next = !rc_tvalid_int_next; + + if (s_axis_rc_tready && s_axis_rc_tvalid) begin + rc_tdata_int_next = s_axis_rc_tdata; + rc_tvalid_int_next = 1'b1; + + if (op_count_next > AXIS_PCIE_DATA_WIDTH/8) begin + // more cycles after this one + cycle_byte_count_next = AXIS_PCIE_DATA_WIDTH/8; + last_cycle = 1'b0; + end else begin + // last cycle + cycle_byte_count_next = op_count_next; + last_cycle = 1'b1; + end + start_offset_next = addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + + ram_mask_0_next = {SEG_COUNT{1'b1}} << (start_offset_next >> $clog2(SEG_BE_WIDTH)); + ram_mask_1_next = {SEG_COUNT{1'b1}} >> (SEG_COUNT-1-(end_offset_next >> $clog2(SEG_BE_WIDTH))); + + if (!ram_wrap_next) begin + ram_mask_next = ram_mask_0_next & ram_mask_1_next; + ram_mask_0_next = ram_mask_0_next & ram_mask_1_next; + ram_mask_1_next = 0; + end else begin + ram_mask_next = ram_mask_0_next | ram_mask_1_next; + end + + addr_delay_next = addr_reg; + addr_next = addr_reg + cycle_byte_count_next; + op_count_next = op_count_reg - cycle_byte_count_next; + + s_axis_rc_tready_next = !(~ram_wr_cmd_ready_int_early & ram_mask_next); + + if (last_cycle) begin + if (final_cpl_reg) begin + // last completion in current read request (PCIe tag) + + // release tag + finish_tag_next = 1'b1; + end else begin + // more completions to come, store current address + tag_table_we_tlp_next = 1'b1; + end + + if (AXIS_PCIE_DATA_WIDTH == 64) begin + s_axis_rc_tready_next = 1'b1; + end + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WRITE; + end + end else begin + tlp_state_next = TLP_STATE_WRITE; + end + end + TLP_STATE_WAIT_END: begin + // wait end state, wait for end of TLP + s_axis_rc_tready_next = 1'b1; + + if (s_axis_rc_tready & s_axis_rc_tvalid) begin + if (s_axis_rc_tlast) begin + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_rc_tready_next = !rc_tvalid_int_next; + end else begin + s_axis_rc_tready_next = 1'b1; + end + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end + endcase + + m_axis_read_desc_status_tag_next = m_axis_read_desc_status_tag_reg; + m_axis_read_desc_status_valid_next = 1'b0; + + op_table_finish_ptr = op_tag_reg; + op_table_finish_en = 1'b0; + op_table_read_finish_ptr = op_tag_reg; + op_table_read_finish_en = 1'b0; + + // finish handling + if (finish_tag_reg) begin + // mark done + op_table_read_finish_ptr = op_tag_reg; + op_table_read_finish_en = 1'b1; + + op_table_finish_ptr = op_tag_reg; + + m_axis_read_desc_status_tag_next = op_table_tag[op_tag_reg]; + + if (op_table_read_commit[op_table_read_finish_ptr] && (op_table_read_count_start[op_table_read_finish_ptr] == op_table_read_count_finish[op_table_read_finish_ptr])) begin + op_table_finish_en = 1'b1; + m_axis_read_desc_status_valid_next = 1'b1; + end + end +end + +always @* begin + tag_table_we_req = 1'b0; + tlp_cmd_ready = 1'b0; + + // tag table write management + if (tag_table_we_tlp_reg) begin + + end else if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + tag_table_we_req = 1'b1; + end +end + +always @(posedge clk) begin + req_state_reg <= req_state_next; + tlp_state_reg <= tlp_state_next; + + status_error_cor_reg <= status_error_cor_next; + status_error_uncor_reg <= status_error_uncor_next; + + req_pcie_addr_reg <= req_pcie_addr_next; + req_addr_reg <= req_addr_next; + req_op_count_reg <= req_op_count_next; + req_tlp_count_reg <= req_tlp_count_next; + + lower_addr_reg <= lower_addr_next; + byte_count_reg <= byte_count_next; + error_code_reg <= error_code_next; + ram_sel_reg <= ram_sel_next; + addr_reg <= addr_next; + addr_delay_reg <= addr_delay_next; + addr_valid_reg <= addr_valid_next; + op_count_reg <= op_count_next; + ram_mask_reg <= ram_mask_next; + ram_mask_0_reg <= ram_mask_0_next; + ram_mask_1_reg <= ram_mask_1_next; + ram_wrap_reg <= ram_wrap_next; + cycle_byte_count_reg <= cycle_byte_count_next; + start_offset_reg <= start_offset_next; + end_offset_reg <= end_offset_next; + op_dword_count_reg <= op_dword_count_next; + pcie_tag_reg <= pcie_tag_next; + op_tag_reg <= op_tag_next; + final_cpl_reg <= final_cpl_next; + finish_tag_reg <= finish_tag_next; + + offset_reg <= offset_next; + + tlp_cmd_ram_sel_reg <= tlp_cmd_ram_sel_next; + tlp_cmd_addr_reg <= tlp_cmd_addr_next; + tlp_cmd_op_tag_reg <= tlp_cmd_op_tag_next; + tlp_cmd_tag_reg <= tlp_cmd_tag_next; + tlp_cmd_pcie_tag_reg <= tlp_cmd_pcie_tag_next; + tlp_cmd_last_reg <= tlp_cmd_last_next; + tlp_cmd_valid_reg <= tlp_cmd_valid_next; + + rc_tdata_int_reg <= rc_tdata_int_next; + rc_tvalid_int_reg <= rc_tvalid_int_next; + + s_axis_rc_tready_reg <= s_axis_rc_tready_next; + s_axis_read_desc_ready_reg <= s_axis_read_desc_ready_next; + + m_axis_read_desc_status_tag_reg <= m_axis_read_desc_status_tag_next; + m_axis_read_desc_status_valid_reg <= m_axis_read_desc_status_valid_next; + + max_read_request_size_dw_reg <= 11'd32 << (max_read_request_size > 5 ? 5 : max_read_request_size); + + have_credit_reg <= pcie_tx_fc_nph_av > 4; + + if (active_tx_count_reg < TX_LIMIT && inc_active_tx && !s_axis_rq_seq_num_valid_0 && !s_axis_rq_seq_num_valid_1) begin + // inc by 1 + active_tx_count_reg <= active_tx_count_reg + 1; + active_tx_count_av_reg <= active_tx_count_reg < (TX_LIMIT-1); + end else if (active_tx_count_reg > 0 && ((inc_active_tx && s_axis_rq_seq_num_valid_0 && s_axis_rq_seq_num_valid_1) || (!inc_active_tx && (s_axis_rq_seq_num_valid_0 ^ s_axis_rq_seq_num_valid_1)))) begin + // dec by 1 + active_tx_count_reg <= active_tx_count_reg - 1; + active_tx_count_av_reg <= 1'b1; + end else if (active_tx_count_reg > 1 && !inc_active_tx && s_axis_rq_seq_num_valid_0 && s_axis_rq_seq_num_valid_1) begin + // dec by 2 + active_tx_count_reg <= active_tx_count_reg - 2; + active_tx_count_av_reg <= 1'b1; + end else begin + active_tx_count_av_reg <= active_tx_count_reg < TX_LIMIT; + end + + tag_table_we_tlp_reg <= tag_table_we_tlp_next; + + if (tag_table_we_tlp_reg) begin + tag_table_addr[pcie_tag_reg] <= addr_reg; + end else if (tlp_cmd_valid_reg && tag_table_we_req) begin + tag_table_sel[tlp_cmd_pcie_tag_reg] <= tlp_cmd_ram_sel_reg; + tag_table_addr[tlp_cmd_pcie_tag_reg] <= tlp_cmd_addr_reg; + tag_table_op_tag[tlp_cmd_pcie_tag_reg] <= tlp_cmd_op_tag_reg; + end + + if (op_table_start_en) begin + op_table_active[op_table_start_ptr] <= 1'b1; + op_table_tag[op_table_start_ptr] <= op_table_start_tag; + op_table_init[op_table_start_ptr] <= !op_table_init[op_table_start_ptr]; + end + + if (op_table_finish_en) begin + op_table_active[op_table_finish_ptr] <= 1'b0; + end + + if (op_table_read_start_en) begin + op_table_read_init[op_table_read_start_ptr] <= op_table_init[op_table_read_start_ptr]; + op_table_read_commit[op_table_read_start_ptr] <= op_table_read_start_commit; + if (op_table_read_init[op_table_read_start_ptr] != op_table_init[op_table_read_start_ptr]) begin + op_table_read_count_start[op_table_read_start_ptr] <= op_table_read_count_finish[op_table_read_start_ptr]; + end else begin + op_table_read_count_start[op_table_read_start_ptr] <= op_table_read_count_start[op_table_read_start_ptr] + 1; + end + end + + if (op_table_read_finish_en) begin + op_table_read_count_finish[op_table_read_finish_ptr] <= op_table_read_count_finish[op_table_read_finish_ptr] + 1; + end + + if (rst) begin + req_state_reg <= REQ_STATE_IDLE; + tlp_state_reg <= TLP_STATE_IDLE; + + addr_valid_reg <= 1'b0; + tlp_cmd_valid_reg <= 1'b0; + finish_tag_reg <= 1'b0; + + rc_tvalid_int_reg <= 1'b0; + + s_axis_rc_tready_reg <= 1'b0; + s_axis_read_desc_ready_reg <= 1'b0; + m_axis_read_desc_status_valid_reg <= 1'b0; + + active_tx_count_reg <= {RQ_SEQ_NUM_WIDTH{1'b0}}; + active_tx_count_av_reg <= 1'b1; + + tag_table_we_tlp_reg <= 1'b0; + op_table_active <= 0; + + status_error_cor_reg <= 1'b0; + status_error_uncor_reg <= 1'b0; + end +end + +// output datapath logic (PCIe TLP) +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg m_axis_rq_tvalid_reg = 1'b0, m_axis_rq_tvalid_next; +reg m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_rq_tvalid_reg = 1'b0, temp_m_axis_rq_tvalid_next; +reg temp_m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] temp_m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_rq_int_to_output; +reg store_axis_rq_int_to_temp; +reg store_axis_rq_temp_to_output; + +assign m_axis_rq_tdata = m_axis_rq_tdata_reg; +assign m_axis_rq_tkeep = m_axis_rq_tkeep_reg; +assign m_axis_rq_tvalid = m_axis_rq_tvalid_reg; +assign m_axis_rq_tlast = m_axis_rq_tlast_reg; +assign m_axis_rq_tuser = m_axis_rq_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_rq_tready_int_early = m_axis_rq_tready || (!temp_m_axis_rq_tvalid_reg && (!m_axis_rq_tvalid_reg || !m_axis_rq_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_rq_tvalid_next = m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + + store_axis_rq_int_to_output = 1'b0; + store_axis_rq_int_to_temp = 1'b0; + store_axis_rq_temp_to_output = 1'b0; + + if (m_axis_rq_tready_int_reg) begin + // input is ready + if (m_axis_rq_tready || !m_axis_rq_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_temp = 1'b1; + end + end else if (m_axis_rq_tready) begin + // input is not ready, but output is ready + m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = 1'b0; + store_axis_rq_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_rq_tvalid_reg <= 1'b0; + m_axis_rq_tready_int_reg <= 1'b0; + temp_m_axis_rq_tvalid_reg <= 1'b0; + end else begin + m_axis_rq_tvalid_reg <= m_axis_rq_tvalid_next; + m_axis_rq_tready_int_reg <= m_axis_rq_tready_int_early; + temp_m_axis_rq_tvalid_reg <= temp_m_axis_rq_tvalid_next; + end + + // datapath + if (store_axis_rq_int_to_output) begin + m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end else if (store_axis_rq_temp_to_output) begin + m_axis_rq_tdata_reg <= temp_m_axis_rq_tdata_reg; + m_axis_rq_tkeep_reg <= temp_m_axis_rq_tkeep_reg; + m_axis_rq_tlast_reg <= temp_m_axis_rq_tlast_reg; + m_axis_rq_tuser_reg <= temp_m_axis_rq_tuser_reg; + end + + if (store_axis_rq_int_to_temp) begin + temp_m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + temp_m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + temp_m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + temp_m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end +end + +// output datapath logic (write data) +generate + +genvar n; + +for (n = 0; n < SEG_COUNT; n = n + 1) begin + + reg [RAM_SEL_WIDTH-1:0] ram_wr_cmd_sel_reg = {RAM_SEL_WIDTH{1'b0}}; + reg [SEG_BE_WIDTH-1:0] ram_wr_cmd_be_reg = {SEG_BE_WIDTH{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] ram_wr_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [SEG_DATA_WIDTH-1:0] ram_wr_cmd_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg ram_wr_cmd_valid_reg = 1'b0, ram_wr_cmd_valid_next; + + reg [RAM_SEL_WIDTH-1:0] temp_ram_wr_cmd_sel_reg = {RAM_SEL_WIDTH{1'b0}}; + reg [SEG_BE_WIDTH-1:0] temp_ram_wr_cmd_be_reg = {SEG_BE_WIDTH{1'b0}}; + reg [SEG_ADDR_WIDTH-1:0] temp_ram_wr_cmd_addr_reg = {SEG_ADDR_WIDTH{1'b0}}; + reg [SEG_DATA_WIDTH-1:0] temp_ram_wr_cmd_data_reg = {SEG_DATA_WIDTH{1'b0}}; + reg temp_ram_wr_cmd_valid_reg = 1'b0, temp_ram_wr_cmd_valid_next; + + // datapath control + reg store_axi_w_int_to_output; + reg store_axi_w_int_to_temp; + reg store_axi_w_temp_to_output; + + assign ram_wr_cmd_sel[n*RAM_SEL_WIDTH +: RAM_SEL_WIDTH] = ram_wr_cmd_sel_reg; + assign ram_wr_cmd_be[n*SEG_BE_WIDTH +: SEG_BE_WIDTH] = ram_wr_cmd_be_reg; + assign ram_wr_cmd_addr[n*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = ram_wr_cmd_addr_reg; + assign ram_wr_cmd_data[n*SEG_DATA_WIDTH +: SEG_DATA_WIDTH] = ram_wr_cmd_data_reg; + assign ram_wr_cmd_valid[n +: 1] = ram_wr_cmd_valid_reg; + + // enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) + assign ram_wr_cmd_ready_int_early[n +: 1] = ram_wr_cmd_ready[n +: 1] || (!temp_ram_wr_cmd_valid_reg && (!ram_wr_cmd_valid_reg || !ram_wr_cmd_valid_int[n +: 1])); + + always @* begin + // transfer sink ready state to source + ram_wr_cmd_valid_next = ram_wr_cmd_valid_reg; + temp_ram_wr_cmd_valid_next = temp_ram_wr_cmd_valid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (ram_wr_cmd_ready_int_reg[n +: 1]) begin + // input is ready + if (ram_wr_cmd_ready[n +: 1] || !ram_wr_cmd_valid_reg) begin + // output is ready or currently not valid, transfer data to output + ram_wr_cmd_valid_next = ram_wr_cmd_valid_int[n +: 1]; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_ram_wr_cmd_valid_next = ram_wr_cmd_valid_int[n +: 1]; + store_axi_w_int_to_temp = 1'b1; + end + end else if (ram_wr_cmd_ready[n +: 1]) begin + // input is not ready, but output is ready + ram_wr_cmd_valid_next = temp_ram_wr_cmd_valid_reg; + temp_ram_wr_cmd_valid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end + end + + always @(posedge clk) begin + if (rst) begin + ram_wr_cmd_valid_reg <= 1'b0; + ram_wr_cmd_ready_int_reg[n +: 1] <= 1'b0; + temp_ram_wr_cmd_valid_reg <= 1'b0; + end else begin + ram_wr_cmd_valid_reg <= ram_wr_cmd_valid_next; + ram_wr_cmd_ready_int_reg[n +: 1] <= ram_wr_cmd_ready_int_early[n +: 1]; + temp_ram_wr_cmd_valid_reg <= temp_ram_wr_cmd_valid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + ram_wr_cmd_sel_reg <= ram_wr_cmd_sel_int[n*RAM_SEL_WIDTH +: RAM_SEL_WIDTH]; + ram_wr_cmd_be_reg <= ram_wr_cmd_be_int[n*SEG_BE_WIDTH +: SEG_BE_WIDTH]; + ram_wr_cmd_addr_reg <= ram_wr_cmd_addr_int[n*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH]; + ram_wr_cmd_data_reg <= ram_wr_cmd_data_int[n*SEG_DATA_WIDTH +: SEG_DATA_WIDTH]; + end else if (store_axi_w_temp_to_output) begin + ram_wr_cmd_sel_reg <= temp_ram_wr_cmd_sel_reg; + ram_wr_cmd_be_reg <= temp_ram_wr_cmd_be_reg; + ram_wr_cmd_addr_reg <= temp_ram_wr_cmd_addr_reg; + ram_wr_cmd_data_reg <= temp_ram_wr_cmd_data_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_ram_wr_cmd_sel_reg <= ram_wr_cmd_sel_int[n*RAM_SEL_WIDTH +: RAM_SEL_WIDTH]; + temp_ram_wr_cmd_be_reg <= ram_wr_cmd_be_int[n*SEG_BE_WIDTH +: SEG_BE_WIDTH]; + temp_ram_wr_cmd_addr_reg <= ram_wr_cmd_addr_int[n*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH]; + temp_ram_wr_cmd_data_reg <= ram_wr_cmd_data_int[n*SEG_DATA_WIDTH +: SEG_DATA_WIDTH]; + end + end + +end + +endgenerate + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_if_pcie_us_wr.v b/corundum/lib/pcie/rtl/dma_if_pcie_us_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..56c8b3ef9f3cf64986927a727fb80ce1ace749e5 --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_if_pcie_us_wr.v @@ -0,0 +1,1286 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe DMA write interface + */ +module dma_if_pcie_us_wr # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream RQ tuser signal width + parameter AXIS_PCIE_RQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 60 : 137, + // RQ sequence number width + parameter RQ_SEQ_NUM_WIDTH = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 4 : 6, + // RQ sequence number tracking enable + parameter RQ_SEQ_NUM_ENABLE = 0, + // RAM segment count + parameter SEG_COUNT = AXIS_PCIE_DATA_WIDTH > 64 ? AXIS_PCIE_DATA_WIDTH*2 / 128 : 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH*2/SEG_COUNT, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // RAM select width + parameter RAM_SEL_WIDTH = 2, + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // PCIe address width + parameter PCIE_ADDR_WIDTH = 64, + // Length field width + parameter LEN_WIDTH = 16, + // Tag field width + parameter TAG_WIDTH = 8, + // Operation table size + parameter OP_TABLE_SIZE = 2**(RQ_SEQ_NUM_WIDTH-1), + // In-flight transmit limit + parameter TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control + parameter TX_FC_ENABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (RQ from read DMA IF) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_rq_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_rq_tkeep, + input wire s_axis_rq_tvalid, + output wire s_axis_rq_tready, + input wire s_axis_rq_tlast, + input wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] s_axis_rq_tuser, + + /* + * AXI output (RQ) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep, + output wire m_axis_rq_tvalid, + input wire m_axis_rq_tready, + output wire m_axis_rq_tlast, + output wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser, + + /* + * Transmit sequence number input + */ + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_0, + input wire s_axis_rq_seq_num_valid_0, + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_1, + input wire s_axis_rq_seq_num_valid_1, + + /* + * Transmit sequence number output (to read DMA IF) + */ + output wire [RQ_SEQ_NUM_WIDTH-1:0] m_axis_rq_seq_num_0, + output wire m_axis_rq_seq_num_valid_0, + output wire [RQ_SEQ_NUM_WIDTH-1:0] m_axis_rq_seq_num_1, + output wire m_axis_rq_seq_num_valid_1, + + /* + * Transmit flow control + */ + input wire [7:0] pcie_tx_fc_ph_av, + input wire [11:0] pcie_tx_fc_pd_av, + + /* + * AXI write descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_write_desc_pcie_addr, + input wire [RAM_SEL_WIDTH-1:0] s_axis_write_desc_ram_sel, + input wire [RAM_ADDR_WIDTH-1:0] s_axis_write_desc_ram_addr, + input wire [LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire s_axis_write_desc_valid, + output wire s_axis_write_desc_ready, + + /* + * AXI write descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire m_axis_write_desc_status_valid, + + /* + * RAM interface + */ + output wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] ram_rd_cmd_sel, + output wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_rd_cmd_addr, + output wire [SEG_COUNT-1:0] ram_rd_cmd_valid, + input wire [SEG_COUNT-1:0] ram_rd_cmd_ready, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ram_rd_resp_data, + input wire [SEG_COUNT-1:0] ram_rd_resp_valid, + output wire [SEG_COUNT-1:0] ram_rd_resp_ready, + + /* + * Configuration + */ + input wire enable, + input wire [15:0] requester_id, + input wire requester_id_enable, + input wire [2:0] max_payload_size +); + +parameter RAM_WORD_WIDTH = SEG_BE_WIDTH; +parameter RAM_WORD_SIZE = SEG_DATA_WIDTH/RAM_WORD_WIDTH; + +parameter AXIS_PCIE_WORD_WIDTH = AXIS_PCIE_KEEP_WIDTH; +parameter AXIS_PCIE_WORD_SIZE = AXIS_PCIE_DATA_WIDTH/AXIS_PCIE_WORD_WIDTH; + +parameter OFFSET_WIDTH = $clog2(AXIS_PCIE_DATA_WIDTH/8); +parameter RAM_OFFSET_WIDTH = $clog2(SEG_COUNT*SEG_DATA_WIDTH/8); +parameter WORD_LEN_WIDTH = LEN_WIDTH - $clog2(AXIS_PCIE_KEEP_WIDTH); +parameter CYCLE_COUNT_WIDTH = 13-$clog2(AXIS_PCIE_KEEP_WIDTH*4); + +parameter SEQ_NUM_MASK = {RQ_SEQ_NUM_WIDTH-1{1'b1}}; +parameter SEQ_NUM_FLAG = {1'b1, {RQ_SEQ_NUM_WIDTH-1{1'b0}}}; + +parameter MASK_FIFO_ADDR_WIDTH = $clog2(OP_TABLE_SIZE)+1; + +parameter OP_TAG_WIDTH = $clog2(OP_TABLE_SIZE); + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, or 256 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + if (AXIS_PCIE_RQ_USER_WIDTH != 137) begin + $error("Error: PCIe RQ tuser width must be 137 (instance %m)"); + $finish; + end + end else begin + if (AXIS_PCIE_RQ_USER_WIDTH != 60 && AXIS_PCIE_RQ_USER_WIDTH != 62) begin + $error("Error: PCIe RQ tuser width must be 60 or 62 (instance %m)"); + $finish; + end + end + + if (AXIS_PCIE_RQ_USER_WIDTH == 60) begin + if (RQ_SEQ_NUM_WIDTH != 4) begin + $error("Error: RQ sequence number width must be 4 (instance %m)"); + $finish; + end + end else begin + if (RQ_SEQ_NUM_WIDTH != 6) begin + $error("Error: RQ sequence number width must be 6 (instance %m)"); + $finish; + end + end + + if (RQ_SEQ_NUM_ENABLE && OP_TABLE_SIZE > 2**(RQ_SEQ_NUM_WIDTH-1)) begin + $error("Error: Operation table size of range (instance %m)"); + $finish; + end + + if (RQ_SEQ_NUM_ENABLE && TX_LIMIT > 2**(RQ_SEQ_NUM_WIDTH-1)) begin + $error("Error: TX limit out of range (instance %m)"); + $finish; + end + + if (SEG_COUNT < 2) begin + $error("Error: RAM interface requires at least 2 segments (instance %m)"); + $finish; + end + + if (SEG_COUNT*SEG_DATA_WIDTH != AXIS_PCIE_DATA_WIDTH*2) begin + $error("Error: RAM interface width must be double the PCIe interface width (instance %m)"); + $finish; + end + + if (SEG_BE_WIDTH * 8 != SEG_DATA_WIDTH) begin + $error("Error: RAM interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (2**$clog2(RAM_WORD_WIDTH) != RAM_WORD_WIDTH) begin + $error("Error: RAM word width must be even power of two (instance %m)"); + $finish; + end + + if (RAM_ADDR_WIDTH != SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH)) begin + $error("Error: RAM_ADDR_WIDTH does not match RAM configuration (instance %m)"); + $finish; + end +end + +localparam [3:0] + REQ_MEM_READ = 4'b0000, + REQ_MEM_WRITE = 4'b0001, + REQ_IO_READ = 4'b0010, + REQ_IO_WRITE = 4'b0011, + REQ_MEM_FETCH_ADD = 4'b0100, + REQ_MEM_SWAP = 4'b0101, + REQ_MEM_CAS = 4'b0110, + REQ_MEM_READ_LOCKED = 4'b0111, + REQ_CFG_READ_0 = 4'b1000, + REQ_CFG_READ_1 = 4'b1001, + REQ_CFG_WRITE_0 = 4'b1010, + REQ_CFG_WRITE_1 = 4'b1011, + REQ_MSG = 4'b1100, + REQ_MSG_VENDOR = 4'b1101, + REQ_MSG_ATS = 4'b1110; + +localparam [2:0] + CPL_STATUS_SC = 3'b000, // successful completion + CPL_STATUS_UR = 3'b001, // unsupported request + CPL_STATUS_CRS = 3'b010, // configuration request retry status + CPL_STATUS_CA = 3'b100; // completer abort + +localparam [0:0] + REQ_STATE_IDLE = 1'd0, + REQ_STATE_START = 1'd1; + +reg [0:0] req_state_reg = REQ_STATE_IDLE, req_state_next; + +localparam [0:0] + READ_STATE_IDLE = 1'd0, + READ_STATE_READ = 1'd1; + +reg [0:0] read_state_reg = READ_STATE_IDLE, read_state_next; + +localparam [2:0] + TLP_STATE_IDLE = 3'd0, + TLP_STATE_HEADER_1 = 3'd1, + TLP_STATE_HEADER_2 = 3'd2, + TLP_STATE_TRANSFER = 3'd3, + TLP_STATE_PASSTHROUGH = 3'd4; + +reg [2:0] tlp_state_reg = TLP_STATE_IDLE, tlp_state_next; + +// datapath control signals +reg mask_fifo_we; + +reg read_cmd_ready; + +reg [PCIE_ADDR_WIDTH-1:0] pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, pcie_addr_next; +reg [RAM_SEL_WIDTH-1:0] ram_sel_reg = {RAM_SEL_WIDTH{1'b0}}, ram_sel_next; +reg [RAM_ADDR_WIDTH-1:0] ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, ram_addr_next; +reg [LEN_WIDTH-1:0] op_count_reg = {LEN_WIDTH{1'b0}}, op_count_next; +reg [LEN_WIDTH-1:0] tr_count_reg = {LEN_WIDTH{1'b0}}, tr_count_next; +reg [12:0] tlp_count_reg = 13'd0, tlp_count_next; + +reg [PCIE_ADDR_WIDTH-1:0] read_pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, read_pcie_addr_next; +reg [RAM_SEL_WIDTH-1:0] read_ram_sel_reg = {RAM_SEL_WIDTH{1'b0}}, read_ram_sel_next; +reg [RAM_ADDR_WIDTH-1:0] read_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, read_ram_addr_next; +reg [LEN_WIDTH-1:0] read_len_reg = {LEN_WIDTH{1'b0}}, read_len_next; +reg [SEG_COUNT-1:0] read_ram_mask_reg = {SEG_COUNT{1'b0}}, read_ram_mask_next; +reg [SEG_COUNT-1:0] read_ram_mask_0_reg = {SEG_COUNT{1'b0}}, read_ram_mask_0_next; +reg [SEG_COUNT-1:0] read_ram_mask_1_reg = {SEG_COUNT{1'b0}}, read_ram_mask_1_next; +reg ram_wrap_reg = 1'b0, ram_wrap_next; +reg [CYCLE_COUNT_WIDTH-1:0] read_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, read_cycle_count_next; +reg read_last_cycle_reg = 1'b0, read_last_cycle_next; +reg [OFFSET_WIDTH+1-1:0] cycle_byte_count_reg = {OFFSET_WIDTH+1{1'b0}}, cycle_byte_count_next; +reg [RAM_OFFSET_WIDTH-1:0] start_offset_reg = {RAM_OFFSET_WIDTH{1'b0}}, start_offset_next; +reg [RAM_OFFSET_WIDTH-1:0] end_offset_reg = {RAM_OFFSET_WIDTH{1'b0}}, end_offset_next; + +reg [PCIE_ADDR_WIDTH-1:0] tlp_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, tlp_addr_next; +reg [11:0] tlp_len_reg = 12'd0, tlp_len_next; +reg [RAM_OFFSET_WIDTH-1:0] offset_reg = {RAM_OFFSET_WIDTH{1'b0}}, offset_next; +reg [9:0] dword_count_reg = 10'd0, dword_count_next; +reg [SEG_COUNT-1:0] ram_mask_reg = {SEG_COUNT{1'b0}}, ram_mask_next; +reg ram_mask_valid_reg = 1'b0, ram_mask_valid_next; +reg [CYCLE_COUNT_WIDTH-1:0] cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, cycle_count_next; +reg last_cycle_reg = 1'b0, last_cycle_next; + +reg [TAG_WIDTH-1:0] tlp_cmd_tag_reg = {TAG_WIDTH{1'b0}}, tlp_cmd_tag_next; + +reg [PCIE_ADDR_WIDTH-1:0] read_cmd_pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, read_cmd_pcie_addr_next; +reg [RAM_SEL_WIDTH-1:0] read_cmd_ram_sel_reg = {RAM_SEL_WIDTH{1'b0}}, read_cmd_ram_sel_next; +reg [RAM_ADDR_WIDTH-1:0] read_cmd_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, read_cmd_ram_addr_next; +reg [11:0] read_cmd_len_reg = 12'd0, read_cmd_len_next; +reg [CYCLE_COUNT_WIDTH-1:0] read_cmd_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, read_cmd_cycle_count_next; +reg read_cmd_last_cycle_reg = 1'b0, read_cmd_last_cycle_next; +reg read_cmd_valid_reg = 1'b0, read_cmd_valid_next; + +reg [MASK_FIFO_ADDR_WIDTH+1-1:0] mask_fifo_wr_ptr_reg = 0; +reg [MASK_FIFO_ADDR_WIDTH+1-1:0] mask_fifo_rd_ptr_reg = 0, mask_fifo_rd_ptr_next; +reg [SEG_COUNT-1:0] mask_fifo_mask[(2**MASK_FIFO_ADDR_WIDTH)-1:0]; +reg [SEG_COUNT-1:0] mask_fifo_wr_mask; + +wire mask_fifo_empty = mask_fifo_wr_ptr_reg == mask_fifo_rd_ptr_reg; +wire mask_fifo_full = mask_fifo_wr_ptr_reg == (mask_fifo_rd_ptr_reg ^ (1 << MASK_FIFO_ADDR_WIDTH)); + +reg [10:0] max_payload_size_dw_reg = 11'd0; + +reg have_credit_reg = 1'b0; + +reg [RQ_SEQ_NUM_WIDTH-1:0] active_tx_count_reg = {RQ_SEQ_NUM_WIDTH{1'b0}}; +reg active_tx_count_av_reg = 1'b1; +reg inc_active_tx; + +reg s_axis_rq_tready_reg = 1'b0, s_axis_rq_tready_next; + +reg s_axis_write_desc_ready_reg = 1'b0, s_axis_write_desc_ready_next; + +reg [TAG_WIDTH-1:0] m_axis_write_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_write_desc_status_tag_next; +reg m_axis_write_desc_status_valid_reg = 1'b0, m_axis_write_desc_status_valid_next; + +reg [SEG_COUNT*RAM_SEL_WIDTH-1:0] ram_rd_cmd_sel_reg = 0, ram_rd_cmd_sel_next; +reg [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ram_rd_cmd_addr_reg = 0, ram_rd_cmd_addr_next; +reg [SEG_COUNT-1:0] ram_rd_cmd_valid_reg = 0, ram_rd_cmd_valid_next; +reg [SEG_COUNT-1:0] ram_rd_resp_ready_cmb; + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_int; +reg m_axis_rq_tvalid_int; +reg m_axis_rq_tready_int_reg = 1'b0; +reg m_axis_rq_tlast_int; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_int; +wire m_axis_rq_tready_int_early; + +assign s_axis_rq_tready = s_axis_rq_tready_reg; + +assign m_axis_rq_seq_num_0 = s_axis_rq_seq_num_0 & SEQ_NUM_MASK; +assign m_axis_rq_seq_num_valid_0 = s_axis_rq_seq_num_valid_0 && (s_axis_rq_seq_num_0 & SEQ_NUM_FLAG); +assign m_axis_rq_seq_num_1 = s_axis_rq_seq_num_1 & SEQ_NUM_MASK; +assign m_axis_rq_seq_num_valid_1 = s_axis_rq_seq_num_valid_1 && (s_axis_rq_seq_num_1 & SEQ_NUM_FLAG); + +wire axis_rq_seq_num_valid_0_int = s_axis_rq_seq_num_valid_0 && !(s_axis_rq_seq_num_0 & SEQ_NUM_FLAG); +wire axis_rq_seq_num_valid_1_int = s_axis_rq_seq_num_valid_1 && !(s_axis_rq_seq_num_1 & SEQ_NUM_FLAG); + +assign s_axis_write_desc_ready = s_axis_write_desc_ready_reg; + +assign m_axis_write_desc_status_tag = m_axis_write_desc_status_tag_reg; +assign m_axis_write_desc_status_valid = m_axis_write_desc_status_valid_reg; + +assign ram_rd_cmd_sel = ram_rd_cmd_sel_reg; +assign ram_rd_cmd_addr = ram_rd_cmd_addr_reg; +assign ram_rd_cmd_valid = ram_rd_cmd_valid_reg; +assign ram_rd_resp_ready = ram_rd_resp_ready_cmb; + +wire [PCIE_ADDR_WIDTH-1:0] pcie_addr_plus_max_payload = pcie_addr_reg + {max_payload_size_dw_reg, 2'b00}; +wire [PCIE_ADDR_WIDTH-1:0] pcie_addr_plus_op_count = pcie_addr_reg + op_count_reg; + +// operation tag management +reg [OP_TAG_WIDTH+1-1:0] op_table_start_ptr_reg = 0; +reg [PCIE_ADDR_WIDTH-1:0] op_table_start_pcie_addr; +reg [11:0] op_table_start_len; +reg [9:0] op_table_start_dword_len; +reg [CYCLE_COUNT_WIDTH-1:0] op_table_start_cycle_count; +reg [RAM_OFFSET_WIDTH-1:0] op_table_start_offset; +reg [TAG_WIDTH-1:0] op_table_start_tag; +reg op_table_start_last; +reg op_table_start_en; +reg [OP_TAG_WIDTH+1-1:0] op_table_tx_start_ptr_reg = 0; +reg op_table_tx_start_en; +reg [OP_TAG_WIDTH+1-1:0] op_table_tx_finish_ptr_reg = 0; +reg op_table_tx_finish_en; +reg [OP_TAG_WIDTH+1-1:0] op_table_finish_ptr_reg = 0; +reg op_table_finish_en; + +reg [2**OP_TAG_WIDTH-1:0] op_table_active = 0; +reg [2**OP_TAG_WIDTH-1:0] op_table_tx_done = 0; +reg [PCIE_ADDR_WIDTH-1:0] op_table_pcie_addr[2**OP_TAG_WIDTH-1:0]; +reg [11:0] op_table_len[2**OP_TAG_WIDTH-1:0]; +reg [9:0] op_table_dword_len[2**OP_TAG_WIDTH-1:0]; +reg [CYCLE_COUNT_WIDTH-1:0] op_table_cycle_count[2**OP_TAG_WIDTH-1:0]; +reg [RAM_OFFSET_WIDTH-1:0] op_table_offset[2**OP_TAG_WIDTH-1:0]; +reg [TAG_WIDTH-1:0] op_table_tag[2**OP_TAG_WIDTH-1:0]; +reg op_table_last[2**OP_TAG_WIDTH-1:0]; + +integer i; + +initial begin + for (i = 0; i < 2**OP_TAG_WIDTH; i = i + 1) begin + op_table_pcie_addr[i] = 0; + op_table_len[i] = 0; + op_table_dword_len[i] = 0; + op_table_cycle_count[i] = 0; + op_table_offset[i] = 0; + op_table_tag[i] = 0; + op_table_last[i] = 0; + end +end + +always @* begin + req_state_next = REQ_STATE_IDLE; + + s_axis_write_desc_ready_next = 1'b0; + + pcie_addr_next = pcie_addr_reg; + ram_sel_next = ram_sel_reg; + ram_addr_next = ram_addr_reg; + op_count_next = op_count_reg; + tr_count_next = tr_count_reg; + tlp_count_next = tlp_count_reg; + + tlp_cmd_tag_next = tlp_cmd_tag_reg; + + read_cmd_pcie_addr_next = read_cmd_pcie_addr_reg; + read_cmd_ram_sel_next = read_cmd_ram_sel_reg; + read_cmd_ram_addr_next = read_cmd_ram_addr_reg; + read_cmd_len_next = read_cmd_len_reg; + read_cmd_cycle_count_next = read_cmd_cycle_count_reg; + read_cmd_last_cycle_next = read_cmd_last_cycle_reg; + read_cmd_valid_next = read_cmd_valid_reg && !read_cmd_ready; + + op_table_start_pcie_addr = pcie_addr_reg; + op_table_start_len = 0; + op_table_start_dword_len = 0; + op_table_start_cycle_count = 0; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + op_table_start_offset = 16+pcie_addr_reg[1:0]-ram_addr_reg[RAM_OFFSET_WIDTH-1:0]; + end else begin + op_table_start_offset = pcie_addr_reg[1:0]-ram_addr_reg[RAM_OFFSET_WIDTH-1:0]; + end + op_table_start_tag = tlp_cmd_tag_reg; + op_table_start_last = 0; + op_table_start_en = 1'b0; + + // TLP segmentation + case (req_state_reg) + REQ_STATE_IDLE: begin + // idle state, wait for incoming descriptor + s_axis_write_desc_ready_next = !op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] && ($unsigned(op_table_start_ptr_reg - op_table_finish_ptr_reg) < 2**OP_TAG_WIDTH) && enable; + + pcie_addr_next = s_axis_write_desc_pcie_addr; + ram_sel_next = s_axis_write_desc_ram_sel; + ram_addr_next = s_axis_write_desc_ram_addr; + op_count_next = s_axis_write_desc_len; + + if (op_count_next <= {max_payload_size_dw_reg, 2'b00}-pcie_addr_next[1:0]) begin + // packet smaller than max payload size + if ((pcie_addr_next ^ (pcie_addr_next + op_count_next)) & (1 << 12)) begin + // crosses 4k boundary + tlp_count_next = 13'h1000 - pcie_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one TLP + tlp_count_next = op_count_next; + end + end else begin + // packet larger than max payload size + if ((pcie_addr_next ^ (pcie_addr_next + {max_payload_size_dw_reg, 2'b00})) & (1 << 12)) begin + // crosses 4k boundary + tlp_count_next = 13'h1000 - pcie_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one TLP + tlp_count_next = {max_payload_size_dw_reg, 2'b00}-pcie_addr_next[1:0]; + end + end + + if (s_axis_write_desc_ready & s_axis_write_desc_valid) begin + s_axis_write_desc_ready_next = 1'b0; + tlp_cmd_tag_next = s_axis_write_desc_tag; + req_state_next = REQ_STATE_START; + end else begin + req_state_next = REQ_STATE_IDLE; + end + end + REQ_STATE_START: begin + // start state, compute TLP length + if (!op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] && ($unsigned(op_table_start_ptr_reg - op_table_finish_ptr_reg) < 2**OP_TAG_WIDTH) && (!ram_rd_cmd_valid_reg || ram_rd_cmd_ready) && (!read_cmd_valid_reg || read_cmd_ready)) begin + read_cmd_pcie_addr_next = pcie_addr_reg; + read_cmd_ram_sel_next = ram_sel_reg; + read_cmd_ram_addr_next = ram_addr_reg; + read_cmd_len_next = tlp_count_next; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + read_cmd_cycle_count_next = (tlp_count_next + 16+pcie_addr_reg[1:0] - 1) >> $clog2(AXIS_PCIE_DATA_WIDTH/8); + end else begin + read_cmd_cycle_count_next = (tlp_count_next + pcie_addr_reg[1:0] - 1) >> $clog2(AXIS_PCIE_DATA_WIDTH/8); + end + op_table_start_cycle_count = read_cmd_cycle_count_next; + read_cmd_last_cycle_next = read_cmd_cycle_count_next == 0; + read_cmd_valid_next = 1'b1; + + pcie_addr_next = pcie_addr_reg + tlp_count_next; + ram_addr_next = ram_addr_reg + tlp_count_next; + op_count_next = op_count_reg - tlp_count_next; + + op_table_start_pcie_addr = pcie_addr_reg; + op_table_start_len = tlp_count_next; + op_table_start_dword_len = (tlp_count_next + pcie_addr_reg[1:0] + 3) >> 2; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + op_table_start_offset = 16+pcie_addr_reg[1:0]-ram_addr_reg[RAM_OFFSET_WIDTH-1:0]; + end else begin + op_table_start_offset = pcie_addr_reg[1:0]-ram_addr_reg[RAM_OFFSET_WIDTH-1:0]; + end + op_table_start_last = op_count_reg == tlp_count_next; + + op_table_start_tag = tlp_cmd_tag_reg; + op_table_start_en = 1'b1; + + if (op_count_next <= {max_payload_size_dw_reg, 2'b00}-pcie_addr_next[1:0]) begin + // packet smaller than max payload size + if ((pcie_addr_next ^ (pcie_addr_next + op_count_next)) & (1 << 12)) begin + // crosses 4k boundary + tlp_count_next = 13'h1000 - pcie_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one TLP + tlp_count_next = op_count_next; + end + end else begin + // packet larger than max payload size + if ((pcie_addr_next ^ (pcie_addr_next + {max_payload_size_dw_reg, 2'b00})) & (1 << 12)) begin + // crosses 4k boundary + tlp_count_next = 13'h1000 - pcie_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one TLP + tlp_count_next = {max_payload_size_dw_reg, 2'b00}-pcie_addr_next[1:0]; + end + end + + if (!op_table_start_last) begin + req_state_next = REQ_STATE_START; + end else begin + s_axis_write_desc_ready_next = !op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] && ($unsigned(op_table_start_ptr_reg - op_table_finish_ptr_reg) < 2**OP_TAG_WIDTH) && enable; + req_state_next = REQ_STATE_IDLE; + end + end else begin + req_state_next = REQ_STATE_START; + end + end + endcase +end + +always @* begin + read_state_next = READ_STATE_IDLE; + + read_cmd_ready = 1'b0; + + ram_rd_cmd_sel_next = ram_rd_cmd_sel_reg; + ram_rd_cmd_addr_next = ram_rd_cmd_addr_reg; + ram_rd_cmd_valid_next = ram_rd_cmd_valid_reg & ~ram_rd_cmd_ready; + + read_pcie_addr_next = read_pcie_addr_reg; + read_ram_sel_next = read_ram_sel_reg; + read_ram_addr_next = read_ram_addr_reg; + read_len_next = read_len_reg; + read_ram_mask_next = read_ram_mask_reg; + read_ram_mask_0_next = read_ram_mask_0_reg; + read_ram_mask_1_next = read_ram_mask_1_reg; + ram_wrap_next = ram_wrap_reg; + read_cycle_count_next = read_cycle_count_reg; + read_last_cycle_next = read_last_cycle_reg; + cycle_byte_count_next = cycle_byte_count_reg; + start_offset_next = start_offset_reg; + end_offset_next = end_offset_reg; + + mask_fifo_wr_mask = read_ram_mask_reg; + mask_fifo_we = 1'b0; + + // Read request generation + case (read_state_reg) + READ_STATE_IDLE: begin + // idle state, wait for read command + + read_pcie_addr_next = read_cmd_pcie_addr_reg; + read_ram_sel_next = read_cmd_ram_sel_reg; + read_ram_addr_next = read_cmd_ram_addr_reg; + read_len_next = read_cmd_len_reg; + read_cycle_count_next = read_cmd_cycle_count_reg; + read_last_cycle_next = read_cmd_last_cycle_reg; + + if (AXIS_PCIE_DATA_WIDTH >= 256 && read_len_next > (AXIS_PCIE_DATA_WIDTH/8-16)-read_pcie_addr_next[1:0]) begin + cycle_byte_count_next = (AXIS_PCIE_DATA_WIDTH/8-16)-read_pcie_addr_next[1:0]; + end else if (AXIS_PCIE_DATA_WIDTH <= 128 && read_len_next > AXIS_PCIE_DATA_WIDTH/8-read_pcie_addr_next[1:0]) begin + cycle_byte_count_next = AXIS_PCIE_DATA_WIDTH/8-read_pcie_addr_next[1:0]; + end else begin + cycle_byte_count_next = read_len_next; + end + start_offset_next = read_ram_addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + + read_ram_mask_0_next = {SEG_COUNT{1'b1}} << (start_offset_next >> $clog2(SEG_BE_WIDTH)); + read_ram_mask_1_next = {SEG_COUNT{1'b1}} >> (SEG_COUNT-1-(end_offset_next >> $clog2(SEG_BE_WIDTH))); + + if (!ram_wrap_next) begin + read_ram_mask_next = read_ram_mask_0_next & read_ram_mask_1_next; + read_ram_mask_0_next = read_ram_mask_0_next & read_ram_mask_1_next; + read_ram_mask_1_next = 0; + end else begin + read_ram_mask_next = read_ram_mask_0_next | read_ram_mask_1_next; + end + + if (read_cmd_valid_reg) begin + read_cmd_ready = 1'b1; + read_state_next = READ_STATE_READ; + end else begin + read_state_next = READ_STATE_IDLE; + end + end + READ_STATE_READ: begin + // read state - start new read operations + + if (!(ram_rd_cmd_valid & ~ram_rd_cmd_ready & read_ram_mask_reg) && !mask_fifo_full) begin + + // update counters + read_ram_addr_next = read_ram_addr_reg + cycle_byte_count_reg; + read_len_next = read_len_reg - cycle_byte_count_reg; + read_cycle_count_next = read_cycle_count_reg - 1; + read_last_cycle_next = read_cycle_count_next == 0; + + for (i = 0; i < SEG_COUNT; i = i + 1) begin + if (read_ram_mask_0_reg[i]) begin + ram_rd_cmd_sel_next[i*RAM_SEL_WIDTH +: RAM_SEL_WIDTH] = read_ram_sel_reg; + ram_rd_cmd_addr_next[i*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = read_ram_addr_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]; + ram_rd_cmd_valid_next[i] = 1'b1; + end + if (read_ram_mask_1_reg[i]) begin + ram_rd_cmd_sel_next[i*RAM_SEL_WIDTH +: RAM_SEL_WIDTH] = read_ram_sel_reg; + ram_rd_cmd_addr_next[i*SEG_ADDR_WIDTH +: SEG_ADDR_WIDTH] = read_ram_addr_reg[RAM_ADDR_WIDTH-1:RAM_ADDR_WIDTH-SEG_ADDR_WIDTH]+1; + ram_rd_cmd_valid_next[i] = 1'b1; + end + end + + mask_fifo_wr_mask = read_ram_mask_reg; + mask_fifo_we = 1'b1; + + if (read_len_next > AXIS_PCIE_DATA_WIDTH/8) begin + cycle_byte_count_next = AXIS_PCIE_DATA_WIDTH/8; + end else begin + cycle_byte_count_next = read_len_next; + end + start_offset_next = read_ram_addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + + read_ram_mask_0_next = {SEG_COUNT{1'b1}} << (start_offset_next >> $clog2(SEG_BE_WIDTH)); + read_ram_mask_1_next = {SEG_COUNT{1'b1}} >> (SEG_COUNT-1-(end_offset_next >> $clog2(SEG_BE_WIDTH))); + + if (!ram_wrap_next) begin + read_ram_mask_next = read_ram_mask_0_next & read_ram_mask_1_next; + read_ram_mask_0_next = read_ram_mask_0_next & read_ram_mask_1_next; + read_ram_mask_1_next = 0; + end else begin + read_ram_mask_next = read_ram_mask_0_next | read_ram_mask_1_next; + end + + if (!read_last_cycle_reg) begin + read_state_next = READ_STATE_READ; + end else if (read_cmd_valid_reg) begin + + read_pcie_addr_next = read_cmd_pcie_addr_reg; + read_ram_sel_next = read_cmd_ram_sel_reg; + read_ram_addr_next = read_cmd_ram_addr_reg; + read_len_next = read_cmd_len_reg; + read_cycle_count_next = read_cmd_cycle_count_reg; + read_last_cycle_next = read_cmd_last_cycle_reg; + + if (AXIS_PCIE_DATA_WIDTH >= 256 && read_len_next > (AXIS_PCIE_DATA_WIDTH/8-16)-read_pcie_addr_next[1:0]) begin + cycle_byte_count_next = (AXIS_PCIE_DATA_WIDTH/8-16)-read_pcie_addr_next[1:0]; + end else if (AXIS_PCIE_DATA_WIDTH <= 128 && read_len_next > AXIS_PCIE_DATA_WIDTH/8-read_pcie_addr_next[1:0]) begin + cycle_byte_count_next = AXIS_PCIE_DATA_WIDTH/8-read_pcie_addr_next[1:0]; + end else begin + cycle_byte_count_next = read_len_next; + end + start_offset_next = read_ram_addr_next; + {ram_wrap_next, end_offset_next} = start_offset_next+cycle_byte_count_next-1; + + read_ram_mask_0_next = {SEG_COUNT{1'b1}} << (start_offset_next >> $clog2(SEG_BE_WIDTH)); + read_ram_mask_1_next = {SEG_COUNT{1'b1}} >> (SEG_COUNT-1-(end_offset_next >> $clog2(SEG_BE_WIDTH))); + + if (!ram_wrap_next) begin + read_ram_mask_next = read_ram_mask_0_next & read_ram_mask_1_next; + read_ram_mask_0_next = read_ram_mask_0_next & read_ram_mask_1_next; + read_ram_mask_1_next = 0; + end else begin + read_ram_mask_next = read_ram_mask_0_next | read_ram_mask_1_next; + end + + read_cmd_ready = 1'b1; + + read_state_next = READ_STATE_READ; + end else begin + read_state_next = READ_STATE_IDLE; + end + end else begin + read_state_next = READ_STATE_READ; + end + end + endcase +end + +wire [3:0] first_be = 4'b1111 << tlp_addr_reg[1:0]; +wire [3:0] last_be = 4'b1111 >> (3 - ((tlp_addr_reg[1:0] + tlp_len_reg[1:0] - 1) & 3)); + +always @* begin + tlp_state_next = TLP_STATE_IDLE; + + m_axis_write_desc_status_tag_next = m_axis_write_desc_status_tag_reg; + m_axis_write_desc_status_valid_next = 1'b0; + + ram_rd_resp_ready_cmb = {SEG_COUNT{1'b0}}; + + tlp_addr_next = tlp_addr_reg; + tlp_len_next = tlp_len_reg; + dword_count_next = dword_count_reg; + offset_next = offset_reg; + ram_mask_next = ram_mask_reg; + ram_mask_valid_next = ram_mask_valid_reg; + cycle_count_next = cycle_count_reg; + last_cycle_next = last_cycle_reg; + + mask_fifo_rd_ptr_next = mask_fifo_rd_ptr_reg; + + op_table_tx_start_en = 1'b0; + op_table_tx_finish_en = 1'b0; + + inc_active_tx = 1'b0; + + s_axis_rq_tready_next = 1'b0; + + m_axis_rq_tdata_int = {AXIS_PCIE_DATA_WIDTH{1'b0}}; + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; + m_axis_rq_tvalid_int = 1'b0; + m_axis_rq_tlast_int = 1'b0; + m_axis_rq_tuser_int = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + + m_axis_rq_tdata_int[1:0] = 2'b0; // address type + m_axis_rq_tdata_int[63:2] = tlp_addr_reg[PCIE_ADDR_WIDTH-1:2]; // address + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tdata_int[74:64] = dword_count_reg; // DWORD count + m_axis_rq_tdata_int[78:75] = REQ_MEM_WRITE; // request type - memory write + m_axis_rq_tdata_int[79] = 1'b0; // poisoned request + m_axis_rq_tdata_int[95:80] = requester_id; + m_axis_rq_tdata_int[103:96] = 8'd0; // tag + m_axis_rq_tdata_int[119:104] = 16'd0; // completer ID + m_axis_rq_tdata_int[120] = requester_id_enable; // requester ID enable + m_axis_rq_tdata_int[123:121] = 3'b000; // traffic class + m_axis_rq_tdata_int[126:124] = 3'b000; // attr + m_axis_rq_tdata_int[127] = 1'b0; // force ECRC + end + + if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_rq_tkeep_int = 8'b00001111; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_rq_tkeep_int = 4'b1111; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_rq_tkeep_int = 2'b11; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[3:0] = dword_count_reg == 1 ? first_be & last_be : first_be; // first BE 0 + m_axis_rq_tuser_int[7:4] = 4'd0; // first BE 1 + m_axis_rq_tuser_int[11:8] = dword_count_reg == 1 ? 4'b0000 : last_be; // last BE 0 + m_axis_rq_tuser_int[15:12] = 4'd0; // last BE 1 + m_axis_rq_tuser_int[19:16] = 3'd0; // addr_offset + m_axis_rq_tuser_int[21:20] = 2'b01; // is_sop + m_axis_rq_tuser_int[23:22] = 2'd0; // is_sop0_ptr + m_axis_rq_tuser_int[25:24] = 2'd0; // is_sop1_ptr + m_axis_rq_tuser_int[27:26] = 2'b01; // is_eop + m_axis_rq_tuser_int[31:28] = 4'd3; // is_eop0_ptr + m_axis_rq_tuser_int[35:32] = 4'd0; // is_eop1_ptr + m_axis_rq_tuser_int[36] = 1'b0; // discontinue + m_axis_rq_tuser_int[38:37] = 2'b00; // tph_present + m_axis_rq_tuser_int[42:39] = 4'b0000; // tph_type + m_axis_rq_tuser_int[44:43] = 2'b00; // tph_indirect_tag_en + m_axis_rq_tuser_int[60:45] = 16'd0; // tph_st_tag + m_axis_rq_tuser_int[66:61] = op_table_tx_finish_ptr_reg[OP_TAG_WIDTH-1:0] & SEQ_NUM_MASK; // seq_num0 + m_axis_rq_tuser_int[72:67] = 6'd0; // seq_num1 + m_axis_rq_tuser_int[136:73] = 64'd0; // parity + end else begin + m_axis_rq_tuser_int[3:0] = dword_count_reg == 1 ? first_be & last_be : first_be; // first BE + m_axis_rq_tuser_int[7:4] = dword_count_reg == 1 ? 4'b0000 : last_be; // last BE + m_axis_rq_tuser_int[10:8] = 3'd0; // addr_offset + m_axis_rq_tuser_int[11] = 1'b0; // discontinue + m_axis_rq_tuser_int[12] = 1'b0; // tph_present + m_axis_rq_tuser_int[14:13] = 2'b00; // tph_type + m_axis_rq_tuser_int[15] = 1'b0; // tph_indirect_tag_en + m_axis_rq_tuser_int[23:16] = 8'd0; // tph_st_tag + m_axis_rq_tuser_int[27:24] = op_table_tx_finish_ptr_reg[OP_TAG_WIDTH-1:0] & SEQ_NUM_MASK; // seq_num + m_axis_rq_tuser_int[59:28] = 32'd0; // parity + if (AXIS_PCIE_RQ_USER_WIDTH == 62) begin + m_axis_rq_tuser_int[61:60] = (op_table_tx_finish_ptr_reg[OP_TAG_WIDTH-1:0] & SEQ_NUM_MASK) >> 4; // seq_num + end + end + + // AXI read response processing and TLP generation + case (tlp_state_reg) + TLP_STATE_IDLE: begin + // idle state, wait for command + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + + // pass through read request TLP + m_axis_rq_tdata_int = s_axis_rq_tdata; + m_axis_rq_tkeep_int = s_axis_rq_tkeep; + m_axis_rq_tvalid_int = s_axis_rq_tready && s_axis_rq_tvalid; + m_axis_rq_tlast_int = s_axis_rq_tlast; + m_axis_rq_tuser_int = s_axis_rq_tuser; + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[61+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end else begin + if (RQ_SEQ_NUM_WIDTH > 4) begin + m_axis_rq_tuser_int[60+RQ_SEQ_NUM_WIDTH-4-1] = 1'b1; + end else begin + m_axis_rq_tuser_int[24+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end + end + + ram_rd_resp_ready_cmb = {SEG_COUNT{1'b0}}; + + tlp_addr_next = op_table_pcie_addr[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + tlp_len_next = op_table_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + dword_count_next = op_table_dword_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + offset_next = op_table_offset[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + cycle_count_next = op_table_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + last_cycle_next = op_table_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] == 0; + + if (s_axis_rq_tready && s_axis_rq_tvalid) begin + // pass through read request TLP + if (s_axis_rq_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_PASSTHROUGH; + end + end else if (op_table_active[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_tx_start_ptr_reg != op_table_start_ptr_reg && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + s_axis_rq_tready_next = 1'b0; + op_table_tx_start_en = 1'b1; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + tlp_state_next = TLP_STATE_IDLE; + end + end + TLP_STATE_HEADER_1: begin + // header 1 state, send TLP header + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + + ram_rd_resp_ready_cmb = {SEG_COUNT{1'b0}}; + + if (!(ram_mask_reg & ~ram_rd_resp_valid) && ram_mask_valid_reg && m_axis_rq_tready_int_reg) begin + // transfer in read data + ram_rd_resp_ready_cmb = ram_mask_reg; + ram_mask_valid_next = 1'b0; + + // update counters + dword_count_next = dword_count_reg - (AXIS_PCIE_KEEP_WIDTH-4); + cycle_count_next = cycle_count_reg - 1; + last_cycle_next = cycle_count_next == 0; + offset_next = offset_reg + AXIS_PCIE_DATA_WIDTH/8; + + m_axis_rq_tdata_int[AXIS_PCIE_DATA_WIDTH-1:128] = {2{ram_rd_resp_data}} >> (SEG_COUNT*SEG_DATA_WIDTH-offset_reg*8 + 128); + m_axis_rq_tvalid_int = 1'b1; + if (dword_count_reg >= AXIS_PCIE_KEEP_WIDTH-4) begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}}; + end else begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}} >> (AXIS_PCIE_KEEP_WIDTH-4 - dword_count_reg); + end + + inc_active_tx = 1'b1; + + if (last_cycle_reg) begin + m_axis_rq_tlast_int = 1'b1; + op_table_tx_finish_en = 1'b1; + + // skip idle state if possible + tlp_addr_next = op_table_pcie_addr[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + tlp_len_next = op_table_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + dword_count_next = op_table_dword_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + offset_next = op_table_offset[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + cycle_count_next = op_table_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + last_cycle_next = op_table_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] == 0; + + if (op_table_active[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_tx_start_ptr_reg != op_table_start_ptr_reg && !s_axis_rq_tvalid && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + op_table_tx_start_en = 1'b1; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + tlp_state_next = TLP_STATE_TRANSFER; + end + end else begin + tlp_state_next = TLP_STATE_HEADER_1; + end + end else begin + if (m_axis_rq_tready_int_reg) begin + m_axis_rq_tvalid_int = 1'b1; + + inc_active_tx = 1'b1; + + if (AXIS_PCIE_DATA_WIDTH == 128) begin + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + tlp_state_next = TLP_STATE_HEADER_2; + end + end else begin + tlp_state_next = TLP_STATE_HEADER_1; + end + end + end + TLP_STATE_HEADER_2: begin + // header 2 state, send rest of TLP header (64 bit interface only) + if (m_axis_rq_tready_int_reg) begin + m_axis_rq_tdata_int[10:0] = dword_count_reg; // DWORD count + m_axis_rq_tdata_int[14:11] = 4'b0001; // request type - memory write + m_axis_rq_tdata_int[15] = 1'b0; // poisoned request + m_axis_rq_tdata_int[31:16] = requester_id; + m_axis_rq_tdata_int[39:32] = 8'd0; // tag + m_axis_rq_tdata_int[55:40] = 16'd0; // completer ID + m_axis_rq_tdata_int[56] = requester_id_enable; // requester ID enable + m_axis_rq_tdata_int[59:57] = 3'b000; // traffic class + m_axis_rq_tdata_int[62:60] = 3'b000; // attr + m_axis_rq_tdata_int[63] = 1'b0; // force ECRC + m_axis_rq_tvalid_int = 1'b1; + m_axis_rq_tkeep_int = 2'b11; + + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + tlp_state_next = TLP_STATE_HEADER_2; + end + end + TLP_STATE_TRANSFER: begin + // transfer state, transfer data + + ram_rd_resp_ready_cmb = {SEG_COUNT{1'b0}}; + + if (!(ram_mask_reg & ~ram_rd_resp_valid) && ram_mask_valid_reg && m_axis_rq_tready_int_reg) begin + // transfer in read data + ram_rd_resp_ready_cmb = ram_mask_reg; + ram_mask_valid_next = 1'b0; + + // update counters + dword_count_next = dword_count_reg - AXIS_PCIE_KEEP_WIDTH; + cycle_count_next = cycle_count_reg - 1; + last_cycle_next = cycle_count_next == 0; + offset_next = offset_reg + AXIS_PCIE_DATA_WIDTH/8; + + m_axis_rq_tdata_int = {2{ram_rd_resp_data}} >> (SEG_COUNT*SEG_DATA_WIDTH-offset_reg*8); + m_axis_rq_tvalid_int = 1'b1; + if (dword_count_reg >= AXIS_PCIE_KEEP_WIDTH) begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}}; + end else begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}} >> (AXIS_PCIE_KEEP_WIDTH - dword_count_reg); + end + + if (last_cycle_reg) begin + // no more data to transfer, finish operation + m_axis_rq_tlast_int = 1'b1; + op_table_tx_finish_en = 1'b1; + + // skip idle state if possible + tlp_addr_next = op_table_pcie_addr[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + tlp_len_next = op_table_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + dword_count_next = op_table_dword_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + offset_next = op_table_offset[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + cycle_count_next = op_table_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + last_cycle_next = op_table_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] == 0; + + if (op_table_active[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_tx_start_ptr_reg != op_table_start_ptr_reg && !s_axis_rq_tvalid && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + op_table_tx_start_en = 1'b1; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + tlp_state_next = TLP_STATE_TRANSFER; + end + end else begin + tlp_state_next = TLP_STATE_TRANSFER; + end + end + TLP_STATE_PASSTHROUGH: begin + // passthrough state, pass through read request TLP + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + + // pass through read request TLP + m_axis_rq_tdata_int = s_axis_rq_tdata; + m_axis_rq_tkeep_int = s_axis_rq_tkeep; + m_axis_rq_tvalid_int = s_axis_rq_tready && s_axis_rq_tvalid; + m_axis_rq_tlast_int = s_axis_rq_tlast; + m_axis_rq_tuser_int = s_axis_rq_tuser; + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[61+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end else begin + if (RQ_SEQ_NUM_WIDTH > 4) begin + m_axis_rq_tuser_int[60+RQ_SEQ_NUM_WIDTH-4-1] = 1'b1; + end else begin + m_axis_rq_tuser_int[24+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end + end + + if (s_axis_rq_tready && s_axis_rq_tvalid && s_axis_rq_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_PASSTHROUGH; + end + end + endcase + + if (!ram_mask_valid_next && !mask_fifo_empty) begin + ram_mask_next = mask_fifo_mask[mask_fifo_rd_ptr_reg[MASK_FIFO_ADDR_WIDTH-1:0]]; + ram_mask_valid_next = 1'b1; + mask_fifo_rd_ptr_next = mask_fifo_rd_ptr_reg+1; + end + + op_table_finish_en = 1'b0; + + if (op_table_active[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]] && (!RQ_SEQ_NUM_ENABLE || op_table_tx_done[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]]) && op_table_finish_ptr_reg != op_table_tx_finish_ptr_reg) begin + op_table_finish_en = 1'b1; + + if (op_table_last[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]]) begin + m_axis_write_desc_status_tag_next = op_table_tag[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]]; + m_axis_write_desc_status_valid_next = 1'b1; + end + end +end + +always @(posedge clk) begin + req_state_reg <= req_state_next; + read_state_reg <= read_state_next; + tlp_state_reg <= tlp_state_next; + + pcie_addr_reg <= pcie_addr_next; + ram_sel_reg <= ram_sel_next; + ram_addr_reg <= ram_addr_next; + op_count_reg <= op_count_next; + tr_count_reg <= tr_count_next; + tlp_count_reg <= tlp_count_next; + + read_pcie_addr_reg <= read_pcie_addr_next; + read_ram_sel_reg <= read_ram_sel_next; + read_ram_addr_reg <= read_ram_addr_next; + read_len_reg <= read_len_next; + read_ram_mask_reg <= read_ram_mask_next; + read_ram_mask_0_reg <= read_ram_mask_0_next; + read_ram_mask_1_reg <= read_ram_mask_1_next; + ram_wrap_reg <= ram_wrap_next; + read_cycle_count_reg <= read_cycle_count_next; + read_last_cycle_reg <= read_last_cycle_next; + cycle_byte_count_reg <= cycle_byte_count_next; + start_offset_reg <= start_offset_next; + end_offset_reg <= end_offset_next; + + tlp_addr_reg <= tlp_addr_next; + tlp_len_reg <= tlp_len_next; + dword_count_reg <= dword_count_next; + offset_reg <= offset_next; + ram_mask_reg <= ram_mask_next; + ram_mask_valid_reg <= ram_mask_valid_next; + cycle_count_reg <= cycle_count_next; + last_cycle_reg <= last_cycle_next; + + tlp_cmd_tag_reg <= tlp_cmd_tag_next; + + read_cmd_pcie_addr_reg <= read_cmd_pcie_addr_next; + read_cmd_ram_sel_reg <= read_cmd_ram_sel_next; + read_cmd_ram_addr_reg <= read_cmd_ram_addr_next; + read_cmd_len_reg <= read_cmd_len_next; + read_cmd_cycle_count_reg <= read_cmd_cycle_count_next; + read_cmd_last_cycle_reg <= read_cmd_last_cycle_next; + read_cmd_valid_reg <= read_cmd_valid_next; + + s_axis_rq_tready_reg <= s_axis_rq_tready_next; + + s_axis_write_desc_ready_reg <= s_axis_write_desc_ready_next; + + m_axis_write_desc_status_valid_reg <= m_axis_write_desc_status_valid_next; + m_axis_write_desc_status_tag_reg <= m_axis_write_desc_status_tag_next; + + ram_rd_cmd_sel_reg <= ram_rd_cmd_sel_next; + ram_rd_cmd_addr_reg <= ram_rd_cmd_addr_next; + ram_rd_cmd_valid_reg <= ram_rd_cmd_valid_next; + + max_payload_size_dw_reg <= 11'd32 << (max_payload_size > 5 ? 5 : max_payload_size); + + have_credit_reg <= (pcie_tx_fc_ph_av > 4) && (pcie_tx_fc_pd_av > (max_payload_size_dw_reg >> 1)); + + if (active_tx_count_reg < TX_LIMIT && inc_active_tx && !axis_rq_seq_num_valid_0_int && !axis_rq_seq_num_valid_1_int) begin + // inc by 1 + active_tx_count_reg <= active_tx_count_reg + 1; + active_tx_count_av_reg <= active_tx_count_reg < (TX_LIMIT-1); + end else if (active_tx_count_reg > 0 && ((inc_active_tx && axis_rq_seq_num_valid_0_int && axis_rq_seq_num_valid_1_int) || (!inc_active_tx && (axis_rq_seq_num_valid_0_int ^ axis_rq_seq_num_valid_1_int)))) begin + // dec by 1 + active_tx_count_reg <= active_tx_count_reg - 1; + active_tx_count_av_reg <= 1'b1; + end else if (active_tx_count_reg > 1 && !inc_active_tx && axis_rq_seq_num_valid_0_int && axis_rq_seq_num_valid_1_int) begin + // dec by 2 + active_tx_count_reg <= active_tx_count_reg - 2; + active_tx_count_av_reg <= 1'b1; + end else begin + active_tx_count_av_reg <= active_tx_count_reg < TX_LIMIT; + end + + if (mask_fifo_we) begin + mask_fifo_mask[mask_fifo_wr_ptr_reg[MASK_FIFO_ADDR_WIDTH-1:0]] <= mask_fifo_wr_mask; + mask_fifo_wr_ptr_reg <= mask_fifo_wr_ptr_reg + 1; + end + mask_fifo_rd_ptr_reg <= mask_fifo_rd_ptr_next; + + if (op_table_start_en) begin + op_table_start_ptr_reg <= op_table_start_ptr_reg + 1; + op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= 1'b1; + op_table_tx_done[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= 1'b0; + op_table_pcie_addr[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_pcie_addr; + op_table_len[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_len; + op_table_dword_len[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_dword_len; + op_table_cycle_count[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_cycle_count; + op_table_offset[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_offset; + op_table_tag[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_tag; + op_table_last[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_last; + end + + if (op_table_tx_start_en) begin + op_table_tx_start_ptr_reg <= op_table_tx_start_ptr_reg + 1; + end + + if (op_table_tx_finish_en) begin + op_table_tx_finish_ptr_reg <= op_table_tx_finish_ptr_reg + 1; + end + + if (axis_rq_seq_num_valid_0_int) begin + op_table_tx_done[s_axis_rq_seq_num_0[OP_TAG_WIDTH-1:0]] <= 1'b1; + end + + if (axis_rq_seq_num_valid_1_int) begin + op_table_tx_done[s_axis_rq_seq_num_1[OP_TAG_WIDTH-1:0]] <= 1'b1; + end + + if (op_table_finish_en) begin + op_table_finish_ptr_reg <= op_table_finish_ptr_reg + 1; + op_table_active[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]] <= 1'b0; + end + + if (rst) begin + req_state_reg <= REQ_STATE_IDLE; + read_state_reg <= READ_STATE_IDLE; + tlp_state_reg <= TLP_STATE_IDLE; + + read_cmd_valid_reg <= 1'b0; + + ram_mask_valid_reg <= 1'b0; + + s_axis_rq_tready_reg <= 1'b0; + s_axis_write_desc_ready_reg <= 1'b0; + m_axis_write_desc_status_valid_reg <= 1'b0; + ram_rd_cmd_valid_reg <= {SEG_COUNT{1'b0}}; + + active_tx_count_reg <= {RQ_SEQ_NUM_WIDTH{1'b0}}; + active_tx_count_av_reg <= 1'b1; + + mask_fifo_wr_ptr_reg <= 0; + mask_fifo_rd_ptr_reg <= 0; + + op_table_start_ptr_reg <= 0; + op_table_tx_start_ptr_reg <= 0; + op_table_tx_finish_ptr_reg <= 0; + op_table_finish_ptr_reg <= 0; + op_table_active <= 0; + end +end + +// output datapath logic (PCIe TLP) +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg m_axis_rq_tvalid_reg = 1'b0, m_axis_rq_tvalid_next; +reg m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_rq_tvalid_reg = 1'b0, temp_m_axis_rq_tvalid_next; +reg temp_m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] temp_m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_rq_int_to_output; +reg store_axis_rq_int_to_temp; +reg store_axis_rq_temp_to_output; + +assign m_axis_rq_tdata = m_axis_rq_tdata_reg; +assign m_axis_rq_tkeep = m_axis_rq_tkeep_reg; +assign m_axis_rq_tvalid = m_axis_rq_tvalid_reg; +assign m_axis_rq_tlast = m_axis_rq_tlast_reg; +assign m_axis_rq_tuser = m_axis_rq_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_rq_tready_int_early = m_axis_rq_tready || (!temp_m_axis_rq_tvalid_reg && (!m_axis_rq_tvalid_reg || !m_axis_rq_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_rq_tvalid_next = m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + + store_axis_rq_int_to_output = 1'b0; + store_axis_rq_int_to_temp = 1'b0; + store_axis_rq_temp_to_output = 1'b0; + + if (m_axis_rq_tready_int_reg) begin + // input is ready + if (m_axis_rq_tready || !m_axis_rq_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_temp = 1'b1; + end + end else if (m_axis_rq_tready) begin + // input is not ready, but output is ready + m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = 1'b0; + store_axis_rq_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_rq_tvalid_reg <= 1'b0; + m_axis_rq_tready_int_reg <= 1'b0; + temp_m_axis_rq_tvalid_reg <= 1'b0; + end else begin + m_axis_rq_tvalid_reg <= m_axis_rq_tvalid_next; + m_axis_rq_tready_int_reg <= m_axis_rq_tready_int_early; + temp_m_axis_rq_tvalid_reg <= temp_m_axis_rq_tvalid_next; + end + + // datapath + if (store_axis_rq_int_to_output) begin + m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end else if (store_axis_rq_temp_to_output) begin + m_axis_rq_tdata_reg <= temp_m_axis_rq_tdata_reg; + m_axis_rq_tkeep_reg <= temp_m_axis_rq_tkeep_reg; + m_axis_rq_tlast_reg <= temp_m_axis_rq_tlast_reg; + m_axis_rq_tuser_reg <= temp_m_axis_rq_tuser_reg; + end + + if (store_axis_rq_int_to_temp) begin + temp_m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + temp_m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + temp_m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + temp_m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/dma_psdpram.v b/corundum/lib/pcie/rtl/dma_psdpram.v new file mode 100644 index 0000000000000000000000000000000000000000..52ba477a804c5f6dec034ad6b3a68959c42b0f2b --- /dev/null +++ b/corundum/lib/pcie/rtl/dma_psdpram.v @@ -0,0 +1,150 @@ +/* + +Copyright (c) 2019 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * DMA parallel simple dual port RAM + */ +module dma_psdpram # +( + // RAM size + parameter SIZE = 4096, + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 128, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // Read data output pipeline stages + parameter PIPELINE = 2 +) +( + /* + * Write port + */ + input wire clk_wr, + input wire rst_wr, + input wire [SEG_COUNT*SEG_BE_WIDTH-1:0] wr_cmd_be, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] wr_cmd_addr, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] wr_cmd_data, + input wire [SEG_COUNT-1:0] wr_cmd_valid, + output wire [SEG_COUNT-1:0] wr_cmd_ready, + + /* + * Read port + */ + input wire clk_rd, + input wire rst_rd, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] rd_cmd_addr, + input wire [SEG_COUNT-1:0] rd_cmd_valid, + output wire [SEG_COUNT-1:0] rd_cmd_ready, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] rd_resp_data, + output wire [SEG_COUNT-1:0] rd_resp_valid, + input wire [SEG_COUNT-1:0] rd_resp_ready +); + +parameter INT_ADDR_WIDTH = $clog2(SIZE/(SEG_COUNT*SEG_BE_WIDTH)); + +// check configuration +initial begin + if (SEG_ADDR_WIDTH < INT_ADDR_WIDTH) begin + $error("Error: SEG_ADDR_WIDTH not sufficient for requested size (min %d for size %d) (instance %m)", INT_ADDR_WIDTH, SIZE); + $finish; + end +end + +generate + + genvar n; + + for (n = 0; n < SEG_COUNT; n = n + 1) begin + + reg [SEG_DATA_WIDTH-1:0] mem_reg[2**INT_ADDR_WIDTH-1:0]; + + reg [PIPELINE-1:0] rd_resp_valid_pipe_reg = 0; + reg [SEG_DATA_WIDTH-1:0] rd_resp_data_pipe_reg[PIPELINE-1:0]; + + integer i, j; + + initial begin + // two nested loops for smaller number of iterations per loop + // workaround for synthesizer complaints about large loop counts + for (i = 0; i < 2**INT_ADDR_WIDTH; i = i + 2**(INT_ADDR_WIDTH/2)) begin + for (j = i; j < i + 2**(INT_ADDR_WIDTH/2); j = j + 1) begin + mem_reg[j] = 0; + end + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + rd_resp_data_pipe_reg[i] = 0; + end + end + + always @(posedge clk_wr) begin + for (i = 0; i < SEG_BE_WIDTH; i = i + 1) begin + if (wr_cmd_valid[n] && wr_cmd_be[n*SEG_BE_WIDTH+i]) begin + mem_reg[wr_cmd_addr[SEG_ADDR_WIDTH*n +: INT_ADDR_WIDTH]][i*8 +: 8] <= wr_cmd_data[SEG_DATA_WIDTH*n+i*8 +: 8]; + end + end + end + + assign wr_cmd_ready[n] = 1'b1; + + always @(posedge clk_rd) begin + if (rd_resp_ready[n]) begin + rd_resp_valid_pipe_reg[PIPELINE-1] <= 1'b0; + end + + for (j = PIPELINE-1; j > 0; j = j - 1) begin + if (rd_resp_ready[n] || ((~rd_resp_valid_pipe_reg) >> j)) begin + rd_resp_valid_pipe_reg[j] <= rd_resp_valid_pipe_reg[j-1]; + rd_resp_data_pipe_reg[j] <= rd_resp_data_pipe_reg[j-1]; + rd_resp_valid_pipe_reg[j-1] <= 1'b0; + end + end + + if (rd_cmd_valid[n] && rd_cmd_ready[n]) begin + rd_resp_valid_pipe_reg[0] <= 1'b1; + rd_resp_data_pipe_reg[0] <= mem_reg[rd_cmd_addr[SEG_ADDR_WIDTH*n +: INT_ADDR_WIDTH]]; + end + + if (rst_rd) begin + rd_resp_valid_pipe_reg <= 0; + end + end + + assign rd_cmd_ready[n] = rd_resp_ready[n] || ~rd_resp_valid_pipe_reg; + + assign rd_resp_valid[n] = rd_resp_valid_pipe_reg[PIPELINE-1]; + assign rd_resp_data[SEG_DATA_WIDTH*n +: SEG_DATA_WIDTH] = rd_resp_data_pipe_reg[PIPELINE-1]; + end + +endgenerate + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_axi_dma_desc_mux.v b/corundum/lib/pcie/rtl/pcie_axi_dma_desc_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..51c4ceadc86eafd0aaca99e3a1981eb89c27be29 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_axi_dma_desc_mux.v @@ -0,0 +1,263 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PCIe AXI DMA descriptor mux + */ +module pcie_axi_dma_desc_mux # +( + // Number of ports + parameter PORTS = 2, + // PCIe address width + parameter PCIE_ADDR_WIDTH = 64, + // AXI address width + parameter AXI_ADDR_WIDTH = 16, + // Length field width + parameter LEN_WIDTH = 20, + // Input tag field width + parameter S_TAG_WIDTH = 8, + // Output tag field width (towards DMA module) + // Additional bits required for response routing + parameter M_TAG_WIDTH = S_TAG_WIDTH+$clog2(PORTS), + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Descriptor output (to PCIe DMA core) + */ + output wire [PCIE_ADDR_WIDTH-1:0] m_axis_desc_pcie_addr, + output wire [AXI_ADDR_WIDTH-1:0] m_axis_desc_axi_addr, + output wire [LEN_WIDTH-1:0] m_axis_desc_len, + output wire [M_TAG_WIDTH-1:0] m_axis_desc_tag, + output wire m_axis_desc_valid, + input wire m_axis_desc_ready, + + /* + * Descriptor status input (from PCIe DMA core) + */ + input wire [M_TAG_WIDTH-1:0] s_axis_desc_status_tag, + input wire s_axis_desc_status_valid, + + /* + * Descriptor input + */ + input wire [PORTS*PCIE_ADDR_WIDTH-1:0] s_axis_desc_pcie_addr, + input wire [PORTS*AXI_ADDR_WIDTH-1:0] s_axis_desc_axi_addr, + input wire [PORTS*LEN_WIDTH-1:0] s_axis_desc_len, + input wire [PORTS*S_TAG_WIDTH-1:0] s_axis_desc_tag, + input wire [PORTS-1:0] s_axis_desc_valid, + output wire [PORTS-1:0] s_axis_desc_ready, + + /* + * Descriptor status output + */ + output wire [PORTS*S_TAG_WIDTH-1:0] m_axis_desc_status_tag, + output wire [PORTS-1:0] m_axis_desc_status_valid +); + +parameter CL_PORTS = $clog2(PORTS); + +// check configuration +initial begin + if (M_TAG_WIDTH < S_TAG_WIDTH+$clog2(PORTS)) begin + $error("Error: M_TAG_WIDTH must be at least $clog2(PORTS) larger than S_TAG_WIDTH (instance %m)"); + $finish; + end +end + +// descriptor mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [PCIE_ADDR_WIDTH-1:0] m_axis_desc_pcie_addr_int; +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_axi_addr_int; +reg [LEN_WIDTH-1:0] m_axis_desc_len_int; +reg [M_TAG_WIDTH-1:0] m_axis_desc_tag_int; +reg m_axis_desc_valid_int; +reg m_axis_desc_ready_int_reg = 1'b0; +wire m_axis_desc_ready_int_early; + +assign s_axis_desc_ready = (m_axis_desc_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [PCIE_ADDR_WIDTH-1:0] current_s_desc_pcie_addr = s_axis_desc_pcie_addr[grant_encoded*PCIE_ADDR_WIDTH +: PCIE_ADDR_WIDTH]; +wire [AXI_ADDR_WIDTH-1:0] current_s_desc_axi_addr = s_axis_desc_axi_addr[grant_encoded*AXI_ADDR_WIDTH +: AXI_ADDR_WIDTH]; +wire [LEN_WIDTH-1:0] current_s_desc_len = s_axis_desc_len[grant_encoded*LEN_WIDTH +: LEN_WIDTH]; +wire [S_TAG_WIDTH-1:0] current_s_desc_tag = s_axis_desc_tag[grant_encoded*S_TAG_WIDTH +: S_TAG_WIDTH]; +wire current_s_desc_valid = s_axis_desc_valid[grant_encoded]; +wire current_s_desc_ready = s_axis_desc_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_desc_valid & ~grant; +assign acknowledge = grant & s_axis_desc_valid & s_axis_desc_ready; + +always @* begin + // pass through selected packet data + m_axis_desc_pcie_addr_int = current_s_desc_pcie_addr; + m_axis_desc_axi_addr_int = current_s_desc_axi_addr; + m_axis_desc_len_int = current_s_desc_len; + m_axis_desc_tag_int = {grant_encoded, current_s_desc_tag}; + m_axis_desc_valid_int = current_s_desc_valid && m_axis_desc_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [PCIE_ADDR_WIDTH-1:0] m_axis_desc_pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}; +reg [AXI_ADDR_WIDTH-1:0] m_axis_desc_axi_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] m_axis_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] m_axis_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg m_axis_desc_valid_reg = 1'b0, m_axis_desc_valid_next; + +reg [PCIE_ADDR_WIDTH-1:0] temp_m_axis_desc_pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}; +reg [AXI_ADDR_WIDTH-1:0] temp_m_axis_desc_axi_addr_reg = {AXI_ADDR_WIDTH{1'b0}}; +reg [LEN_WIDTH-1:0] temp_m_axis_desc_len_reg = {LEN_WIDTH{1'b0}}; +reg [M_TAG_WIDTH-1:0] temp_m_axis_desc_tag_reg = {M_TAG_WIDTH{1'b0}}; +reg temp_m_axis_desc_valid_reg = 1'b0, temp_m_axis_desc_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_desc_pcie_addr = m_axis_desc_pcie_addr_reg; +assign m_axis_desc_axi_addr = m_axis_desc_axi_addr_reg; +assign m_axis_desc_len = m_axis_desc_len_reg; +assign m_axis_desc_tag = m_axis_desc_tag_reg; +assign m_axis_desc_valid = m_axis_desc_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_desc_ready_int_early = m_axis_desc_ready || (!temp_m_axis_desc_valid_reg && (!m_axis_desc_valid_reg || !m_axis_desc_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_desc_valid_next = m_axis_desc_valid_reg; + temp_m_axis_desc_valid_next = temp_m_axis_desc_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_desc_ready_int_reg) begin + // input is ready + if (m_axis_desc_ready || !m_axis_desc_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_desc_valid_next = m_axis_desc_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_desc_valid_next = m_axis_desc_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_desc_ready) begin + // input is not ready, but output is ready + m_axis_desc_valid_next = temp_m_axis_desc_valid_reg; + temp_m_axis_desc_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_desc_valid_reg <= 1'b0; + m_axis_desc_ready_int_reg <= 1'b0; + temp_m_axis_desc_valid_reg <= 1'b0; + end else begin + m_axis_desc_valid_reg <= m_axis_desc_valid_next; + m_axis_desc_ready_int_reg <= m_axis_desc_ready_int_early; + temp_m_axis_desc_valid_reg <= temp_m_axis_desc_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_desc_pcie_addr_reg <= m_axis_desc_pcie_addr_int; + m_axis_desc_axi_addr_reg <= m_axis_desc_axi_addr_int; + m_axis_desc_len_reg <= m_axis_desc_len_int; + m_axis_desc_tag_reg <= m_axis_desc_tag_int; + end else if (store_axis_temp_to_output) begin + m_axis_desc_pcie_addr_reg <= temp_m_axis_desc_pcie_addr_reg; + m_axis_desc_axi_addr_reg <= temp_m_axis_desc_axi_addr_reg; + m_axis_desc_len_reg <= temp_m_axis_desc_len_reg; + m_axis_desc_tag_reg <= temp_m_axis_desc_tag_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_desc_pcie_addr_reg <= m_axis_desc_pcie_addr_int; + temp_m_axis_desc_axi_addr_reg <= m_axis_desc_axi_addr_int; + temp_m_axis_desc_len_reg <= m_axis_desc_len_int; + temp_m_axis_desc_tag_reg <= m_axis_desc_tag_int; + end +end + +// descriptor status demux +reg [S_TAG_WIDTH-1:0] m_axis_desc_status_tag_reg = {S_TAG_WIDTH{1'b0}}, m_axis_desc_status_tag_next; +reg [PORTS-1:0] m_axis_desc_status_valid_reg = {PORTS{1'b0}}, m_axis_desc_status_valid_next; + +assign m_axis_desc_status_tag = {PORTS{m_axis_desc_status_tag_reg}}; +assign m_axis_desc_status_valid = m_axis_desc_status_valid_reg; + +always @* begin + m_axis_desc_status_tag_next = s_axis_desc_status_tag; + m_axis_desc_status_valid_next = s_axis_desc_status_valid << (PORTS > 1 ? s_axis_desc_status_tag[S_TAG_WIDTH+CL_PORTS-1:S_TAG_WIDTH] : 0); +end + +always @(posedge clk) begin + if (rst) begin + m_axis_desc_status_valid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_desc_status_valid_reg <= m_axis_desc_status_valid_next; + end + + m_axis_desc_status_tag_reg <= m_axis_desc_status_tag_next; +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_tag_manager.v b/corundum/lib/pcie/rtl/pcie_tag_manager.v new file mode 100644 index 0000000000000000000000000000000000000000..fe702697c0d0bb294068aca121f384299ec4f34e --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_tag_manager.v @@ -0,0 +1,168 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * PCIe tag manager + */ +module pcie_tag_manager # +( + parameter PCIE_TAG_COUNT = 256, + parameter PCIE_TAG_WIDTH = $clog2(PCIE_TAG_COUNT), + parameter PCIE_EXT_TAG_ENABLE = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXIS tag output + */ + output wire [PCIE_TAG_WIDTH-1:0] m_axis_tag, + output wire m_axis_tag_valid, + input wire m_axis_tag_ready, + + /* + * AXIS tag output + */ + input wire [PCIE_TAG_WIDTH-1:0] s_axis_tag, + input wire s_axis_tag_valid, + + /* + * Configuration + */ + input wire ext_tag_enable, + + /* + * Status + */ + output wire [PCIE_TAG_COUNT-1:0] active_tags +); + +// parameter assertions +initial begin + if (PCIE_TAG_WIDTH < $clog2(PCIE_TAG_COUNT)) begin + $error("Error: PCIe tag width insufficient for requested tag count (instance %m)"); + $finish; + end + + if (PCIE_TAG_COUNT < 1 || PCIE_TAG_COUNT > 256) begin + $error("Error: PCIe tag count must be between 1 and 256 (instance %m)"); + $finish; + end + + if (PCIE_TAG_COUNT > 32 && !PCIE_EXT_TAG_ENABLE) begin + $warning("Warning: PCIe tag count set larger than 32, but extended tag support is disabled (instance %m)"); + end + + if (PCIE_TAG_COUNT <= 32 && PCIE_EXT_TAG_ENABLE) begin + $warning("Warning: PCIe tag count set to 32 or less, but extended tag support is enabled (instance %m)"); + end +end + +reg [PCIE_TAG_COUNT-1:0] tag_active_reg = {PCIE_TAG_COUNT{1'b0}}, tag_active_next; +reg [PCIE_TAG_COUNT-1:0] tag_mask_reg = {PCIE_TAG_COUNT{1'b0}}, tag_mask_next; + +reg [PCIE_TAG_WIDTH-1:0] m_axis_tag_reg = {PCIE_TAG_WIDTH{1'b0}}, m_axis_tag_next; +reg m_axis_tag_valid_reg = 1'b0, m_axis_tag_valid_next; + +assign m_axis_tag = m_axis_tag_reg; +assign m_axis_tag_valid = m_axis_tag_valid_reg; + +assign active_tags = tag_active_reg; + +wire tag_valid; +wire [PCIE_TAG_WIDTH-1:0] tag_index; + +priority_encoder #( + .WIDTH(PCIE_TAG_COUNT), + .LSB_PRIORITY("HIGH") +) +priority_encoder_inst ( + .input_unencoded(~tag_active_reg & (ext_tag_enable && PCIE_EXT_TAG_ENABLE ? {256{1'b1}} : {32{1'b1}})), + .output_valid(tag_valid), + .output_encoded(tag_index), + .output_unencoded() +); + +wire masked_tag_valid; +wire [PCIE_TAG_WIDTH-1:0] masked_tag_index; + +priority_encoder #( + .WIDTH(PCIE_TAG_COUNT), + .LSB_PRIORITY("HIGH") +) +priority_encoder_masked ( + .input_unencoded(~tag_active_reg & tag_mask_reg & (ext_tag_enable && PCIE_EXT_TAG_ENABLE ? {256{1'b1}} : {32{1'b1}})), + .output_valid(masked_tag_valid), + .output_encoded(masked_tag_index), + .output_unencoded() +); + +always @* begin + tag_active_next = tag_active_reg; + tag_mask_next = tag_mask_reg; + + m_axis_tag_next = m_axis_tag_reg; + m_axis_tag_valid_next = m_axis_tag_valid_reg && !m_axis_tag_ready; + + if (s_axis_tag_valid) begin + tag_active_next[s_axis_tag] = 1'b0; + end + + if (!m_axis_tag_valid || m_axis_tag_ready) begin + if (tag_valid) begin + if (masked_tag_valid) begin + m_axis_tag_next = masked_tag_index; + m_axis_tag_valid_next = 1'b1; + tag_active_next[masked_tag_index] = 1'b1; + tag_mask_next = {PCIE_TAG_COUNT{1'b1}} << (masked_tag_index + 1); + end else begin + m_axis_tag_next = tag_index; + m_axis_tag_valid_next = 1'b1; + tag_active_next[tag_index] = 1'b1; + tag_mask_next = {PCIE_TAG_COUNT{1'b1}} << (tag_index + 1); + end + end + end +end + +always @(posedge clk) begin + if (rst) begin + tag_active_reg <= {PCIE_TAG_COUNT{1'b0}}; + tag_mask_reg <= {PCIE_TAG_COUNT{1'b0}}; + m_axis_tag_valid_reg <= 1'b0; + end else begin + tag_active_reg <= tag_active_next; + tag_mask_reg <= tag_mask_next; + m_axis_tag_valid_reg <= m_axis_tag_valid_next; + end + + m_axis_tag_reg <= m_axis_tag_next; +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axi_dma.v b/corundum/lib/pcie/rtl/pcie_us_axi_dma.v new file mode 100644 index 0000000000000000000000000000000000000000..68406a20935e6ac4b614ed1f80e67fecb99be5ba --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axi_dma.v @@ -0,0 +1,444 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe AXI DMA + */ +module pcie_us_axi_dma # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream RC tuser signal width + parameter AXIS_PCIE_RC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 75 : 161, + // PCIe AXI stream RQ tuser signal width + parameter AXIS_PCIE_RQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 60 : 137, + // RQ sequence number width + parameter RQ_SEQ_NUM_WIDTH = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 4 : 6, + // RQ sequence number tracking enable + parameter RQ_SEQ_NUM_ENABLE = 0, + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 64, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 256, + // PCIe address width + parameter PCIE_ADDR_WIDTH = 64, + // PCIe tag count + parameter PCIE_TAG_COUNT = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 64 : 256, + // PCIe tag field width + parameter PCIE_TAG_WIDTH = $clog2(PCIE_TAG_COUNT), + // Support PCIe extended tags + parameter PCIE_EXT_TAG_ENABLE = (PCIE_TAG_COUNT>32), + // Length field width + parameter LEN_WIDTH = 20, + // Tag field width + parameter TAG_WIDTH = 8, + // Operation table size (read) + parameter READ_OP_TABLE_SIZE = 2**(AXI_ID_WIDTH < PCIE_TAG_WIDTH ? AXI_ID_WIDTH : PCIE_TAG_WIDTH), + // In-flight transmit limit (read) + parameter READ_TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control (read) + parameter READ_TX_FC_ENABLE = 0, + // Operation table size (write) + parameter WRITE_OP_TABLE_SIZE = 2**(RQ_SEQ_NUM_WIDTH-1), + // In-flight transmit limit (write) + parameter WRITE_TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control (write) + parameter WRITE_TX_FC_ENABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (RC) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_rc_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_rc_tkeep, + input wire s_axis_rc_tvalid, + output wire s_axis_rc_tready, + input wire s_axis_rc_tlast, + input wire [AXIS_PCIE_RC_USER_WIDTH-1:0] s_axis_rc_tuser, + + /* + * AXI output (RQ) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep, + output wire m_axis_rq_tvalid, + input wire m_axis_rq_tready, + output wire m_axis_rq_tlast, + output wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser, + + /* + * Transmit sequence number input + */ + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_0, + input wire s_axis_rq_seq_num_valid_0, + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_1, + input wire s_axis_rq_seq_num_valid_1, + + /* + * Transmit flow control + */ + input wire [7:0] pcie_tx_fc_nph_av, + input wire [7:0] pcie_tx_fc_ph_av, + input wire [11:0] pcie_tx_fc_pd_av, + + /* + * AXI read descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_read_desc_pcie_addr, + input wire [AXI_ADDR_WIDTH-1:0] s_axis_read_desc_axi_addr, + input wire [LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire s_axis_read_desc_valid, + output wire s_axis_read_desc_ready, + + /* + * AXI read descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire m_axis_read_desc_status_valid, + + /* + * AXI write descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_write_desc_pcie_addr, + input wire [AXI_ADDR_WIDTH-1:0] s_axis_write_desc_axi_addr, + input wire [LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire s_axis_write_desc_valid, + output wire s_axis_write_desc_ready, + + /* + * AXI write descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire m_axis_write_desc_status_valid, + + /* + * AXI master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready, + + /* + * Configuration + */ + input wire read_enable, + input wire write_enable, + input wire ext_tag_enable, + input wire [15:0] requester_id, + input wire requester_id_enable, + input wire [2:0] max_read_request_size, + input wire [2:0] max_payload_size, + + /* + * Status + */ + output wire status_error_cor, + output wire status_error_uncor +); + +wire [AXIS_PCIE_DATA_WIDTH-1:0] axis_rq_tdata_read; +wire [AXIS_PCIE_KEEP_WIDTH-1:0] axis_rq_tkeep_read; +wire axis_rq_tvalid_read; +wire axis_rq_tready_read; +wire axis_rq_tlast_read; +wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] axis_rq_tuser_read; + +wire [RQ_SEQ_NUM_WIDTH-1:0] axis_rq_seq_num_read_0; +wire axis_rq_seq_num_valid_read_0; +wire [RQ_SEQ_NUM_WIDTH-1:0] axis_rq_seq_num_read_1; +wire axis_rq_seq_num_valid_read_1; + +pcie_us_axi_dma_rd #( + .AXIS_PCIE_DATA_WIDTH(AXIS_PCIE_DATA_WIDTH), + .AXIS_PCIE_KEEP_WIDTH(AXIS_PCIE_KEEP_WIDTH), + .AXIS_PCIE_RC_USER_WIDTH(AXIS_PCIE_RC_USER_WIDTH), + .AXIS_PCIE_RQ_USER_WIDTH(AXIS_PCIE_RQ_USER_WIDTH), + .RQ_SEQ_NUM_WIDTH(RQ_SEQ_NUM_WIDTH), + .RQ_SEQ_NUM_ENABLE(RQ_SEQ_NUM_ENABLE), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN), + .PCIE_ADDR_WIDTH(PCIE_ADDR_WIDTH), + .PCIE_TAG_COUNT(PCIE_TAG_COUNT), + .PCIE_TAG_WIDTH(PCIE_TAG_WIDTH), + .PCIE_EXT_TAG_ENABLE(PCIE_EXT_TAG_ENABLE), + .LEN_WIDTH(LEN_WIDTH), + .TAG_WIDTH(TAG_WIDTH), + .OP_TABLE_SIZE(READ_OP_TABLE_SIZE), + .TX_LIMIT(READ_TX_LIMIT), + .TX_FC_ENABLE(READ_TX_FC_ENABLE) +) +pcie_us_axi_dma_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI input (RC) + */ + .s_axis_rc_tdata(s_axis_rc_tdata), + .s_axis_rc_tkeep(s_axis_rc_tkeep), + .s_axis_rc_tvalid(s_axis_rc_tvalid), + .s_axis_rc_tready(s_axis_rc_tready), + .s_axis_rc_tlast(s_axis_rc_tlast), + .s_axis_rc_tuser(s_axis_rc_tuser), + + /* + * AXI output (RQ) + */ + .m_axis_rq_tdata(axis_rq_tdata_read), + .m_axis_rq_tkeep(axis_rq_tkeep_read), + .m_axis_rq_tvalid(axis_rq_tvalid_read), + .m_axis_rq_tready(axis_rq_tready_read), + .m_axis_rq_tlast(axis_rq_tlast_read), + .m_axis_rq_tuser(axis_rq_tuser_read), + + /* + * Transmit sequence number input + */ + .s_axis_rq_seq_num_0(axis_rq_seq_num_read_0), + .s_axis_rq_seq_num_valid_0(axis_rq_seq_num_valid_read_0), + .s_axis_rq_seq_num_1(axis_rq_seq_num_read_1), + .s_axis_rq_seq_num_valid_1(axis_rq_seq_num_valid_read_1), + + /* + * Transmit flow control + */ + .pcie_tx_fc_nph_av(pcie_tx_fc_nph_av), + + /* + * AXI read descriptor input + */ + .s_axis_read_desc_pcie_addr(s_axis_read_desc_pcie_addr), + .s_axis_read_desc_axi_addr(s_axis_read_desc_axi_addr), + .s_axis_read_desc_len(s_axis_read_desc_len), + .s_axis_read_desc_tag(s_axis_read_desc_tag), + .s_axis_read_desc_valid(s_axis_read_desc_valid), + .s_axis_read_desc_ready(s_axis_read_desc_ready), + + /* + * AXI read descriptor status output + */ + .m_axis_read_desc_status_tag(m_axis_read_desc_status_tag), + .m_axis_read_desc_status_valid(m_axis_read_desc_status_valid), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready), + + /* + * Configuration + */ + .enable(read_enable), + .ext_tag_enable(ext_tag_enable), + .requester_id(requester_id), + .requester_id_enable(requester_id_enable), + .max_read_request_size(max_read_request_size), + + /* + * Status + */ + .status_error_cor(status_error_cor), + .status_error_uncor(status_error_uncor) +); + +pcie_us_axi_dma_wr #( + .AXIS_PCIE_DATA_WIDTH(AXIS_PCIE_DATA_WIDTH), + .AXIS_PCIE_KEEP_WIDTH(AXIS_PCIE_KEEP_WIDTH), + .AXIS_PCIE_RQ_USER_WIDTH(AXIS_PCIE_RQ_USER_WIDTH), + .RQ_SEQ_NUM_WIDTH(RQ_SEQ_NUM_WIDTH), + .RQ_SEQ_NUM_ENABLE(RQ_SEQ_NUM_ENABLE), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN), + .PCIE_ADDR_WIDTH(PCIE_ADDR_WIDTH), + .LEN_WIDTH(LEN_WIDTH), + .TAG_WIDTH(TAG_WIDTH), + .OP_TABLE_SIZE(WRITE_OP_TABLE_SIZE), + .TX_LIMIT(WRITE_TX_LIMIT), + .TX_FC_ENABLE(WRITE_TX_FC_ENABLE) +) +pcie_us_axi_dma_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI input (RQ from read DMA) + */ + .s_axis_rq_tdata(axis_rq_tdata_read), + .s_axis_rq_tkeep(axis_rq_tkeep_read), + .s_axis_rq_tvalid(axis_rq_tvalid_read), + .s_axis_rq_tready(axis_rq_tready_read), + .s_axis_rq_tlast(axis_rq_tlast_read), + .s_axis_rq_tuser(axis_rq_tuser_read), + + /* + * AXI output (RQ) + */ + .m_axis_rq_tdata(m_axis_rq_tdata), + .m_axis_rq_tkeep(m_axis_rq_tkeep), + .m_axis_rq_tvalid(m_axis_rq_tvalid), + .m_axis_rq_tready(m_axis_rq_tready), + .m_axis_rq_tlast(m_axis_rq_tlast), + .m_axis_rq_tuser(m_axis_rq_tuser), + + /* + * Transmit sequence number input + */ + .s_axis_rq_seq_num_0(s_axis_rq_seq_num_0), + .s_axis_rq_seq_num_valid_0(s_axis_rq_seq_num_valid_0), + .s_axis_rq_seq_num_1(s_axis_rq_seq_num_1), + .s_axis_rq_seq_num_valid_1(s_axis_rq_seq_num_valid_1), + + /* + * Transmit sequence number output (to read DMA) + */ + .m_axis_rq_seq_num_0(axis_rq_seq_num_read_0), + .m_axis_rq_seq_num_valid_0(axis_rq_seq_num_valid_read_0), + .m_axis_rq_seq_num_1(axis_rq_seq_num_read_1), + .m_axis_rq_seq_num_valid_1(axis_rq_seq_num_valid_read_1), + + /* + * Transmit flow control + */ + .pcie_tx_fc_ph_av(pcie_tx_fc_ph_av), + .pcie_tx_fc_pd_av(pcie_tx_fc_pd_av), + + /* + * AXI write descriptor input + */ + .s_axis_write_desc_pcie_addr(s_axis_write_desc_pcie_addr), + .s_axis_write_desc_axi_addr(s_axis_write_desc_axi_addr), + .s_axis_write_desc_len(s_axis_write_desc_len), + .s_axis_write_desc_tag(s_axis_write_desc_tag), + .s_axis_write_desc_valid(s_axis_write_desc_valid), + .s_axis_write_desc_ready(s_axis_write_desc_ready), + + /* + * AXI write descriptor status output + */ + .m_axis_write_desc_status_tag(m_axis_write_desc_status_tag), + .m_axis_write_desc_status_valid(m_axis_write_desc_status_valid), + + /* + * AXI master interface + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready), + + /* + * Configuration + */ + .enable(write_enable), + .requester_id(requester_id), + .requester_id_enable(requester_id_enable), + .max_payload_size(max_payload_size) +); + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axi_dma_rd.v b/corundum/lib/pcie/rtl/pcie_us_axi_dma_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..bd03d5cd42fb68bea46c392112f3b654998903dc --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axi_dma_rd.v @@ -0,0 +1,1656 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe AXI DMA Read + */ +module pcie_us_axi_dma_rd # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream RC tuser signal width + parameter AXIS_PCIE_RC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 75 : 161, + // PCIe AXI stream RQ tuser signal width + parameter AXIS_PCIE_RQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 60 : 137, + // RQ sequence number width + parameter RQ_SEQ_NUM_WIDTH = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 4 : 6, + // RQ sequence number tracking enable + parameter RQ_SEQ_NUM_ENABLE = 0, + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 64, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 256, + // PCIe address width + parameter PCIE_ADDR_WIDTH = 64, + // PCIe tag count + parameter PCIE_TAG_COUNT = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 64 : 256, + // PCIe tag field width + parameter PCIE_TAG_WIDTH = $clog2(PCIE_TAG_COUNT), + // Support PCIe extended tags + parameter PCIE_EXT_TAG_ENABLE = (PCIE_TAG_COUNT>32), + // Length field width + parameter LEN_WIDTH = 20, + // Tag field width + parameter TAG_WIDTH = 8, + // Operation table size + parameter OP_TABLE_SIZE = 2**(AXI_ID_WIDTH < PCIE_TAG_WIDTH ? AXI_ID_WIDTH : PCIE_TAG_WIDTH), + // In-flight transmit limit + parameter TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control + parameter TX_FC_ENABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (RC) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_rc_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_rc_tkeep, + input wire s_axis_rc_tvalid, + output wire s_axis_rc_tready, + input wire s_axis_rc_tlast, + input wire [AXIS_PCIE_RC_USER_WIDTH-1:0] s_axis_rc_tuser, + + /* + * AXI output (RQ) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep, + output wire m_axis_rq_tvalid, + input wire m_axis_rq_tready, + output wire m_axis_rq_tlast, + output wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser, + + /* + * Transmit sequence number input + */ + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_0, + input wire s_axis_rq_seq_num_valid_0, + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_1, + input wire s_axis_rq_seq_num_valid_1, + + /* + * Transmit flow control + */ + input wire [7:0] pcie_tx_fc_nph_av, + + /* + * AXI read descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_read_desc_pcie_addr, + input wire [AXI_ADDR_WIDTH-1:0] s_axis_read_desc_axi_addr, + input wire [LEN_WIDTH-1:0] s_axis_read_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_read_desc_tag, + input wire s_axis_read_desc_valid, + output wire s_axis_read_desc_ready, + + /* + * AXI read descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_read_desc_status_tag, + output wire m_axis_read_desc_status_valid, + + /* + * AXI master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + + /* + * Configuration + */ + input wire enable, + input wire ext_tag_enable, + input wire [15:0] requester_id, + input wire requester_id_enable, + input wire [2:0] max_read_request_size, + + /* + * Status + */ + output wire status_error_cor, + output wire status_error_uncor +); + +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXI_MAX_BURST_SIZE = AXI_MAX_BURST_LEN*AXI_WORD_WIDTH; + +parameter AXIS_PCIE_WORD_WIDTH = AXIS_PCIE_KEEP_WIDTH; +parameter AXIS_PCIE_WORD_SIZE = AXIS_PCIE_DATA_WIDTH/AXIS_PCIE_WORD_WIDTH; + +parameter OFFSET_WIDTH = $clog2(AXIS_PCIE_DATA_WIDTH/8); +parameter CYCLE_COUNT_WIDTH = 13-AXI_BURST_SIZE; + +parameter OP_TAG_WIDTH = $clog2(OP_TABLE_SIZE); +parameter OP_TABLE_READ_COUNT_WIDTH = PCIE_TAG_WIDTH+1; +parameter OP_TABLE_WRITE_COUNT_WIDTH = LEN_WIDTH; + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, 256, or 512 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + if (AXIS_PCIE_RC_USER_WIDTH != 161) begin + $error("Error: PCIe RC tuser width must be 161 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_RQ_USER_WIDTH != 137) begin + $error("Error: PCIe RQ tuser width must be 137 (instance %m)"); + $finish; + end + end else begin + if (AXIS_PCIE_RC_USER_WIDTH != 75) begin + $error("Error: PCIe RC tuser width must be 75 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_RQ_USER_WIDTH != 60 && AXIS_PCIE_RQ_USER_WIDTH != 62) begin + $error("Error: PCIe RQ tuser width must be 60 or 62 (instance %m)"); + $finish; + end + end + + if (AXIS_PCIE_RQ_USER_WIDTH == 60) begin + if (RQ_SEQ_NUM_ENABLE && RQ_SEQ_NUM_WIDTH != 4) begin + $error("Error: RQ sequence number width must be 4 (instance %m)"); + $finish; + end + + if (PCIE_TAG_COUNT > 64) begin + $error("Error: PCIe tag count must be no larger than 64 (instance %m)"); + $finish; + end + end else begin + if (RQ_SEQ_NUM_ENABLE && RQ_SEQ_NUM_WIDTH != 6) begin + $error("Error: RQ sequence number width must be 6 (instance %m)"); + $finish; + end + + if (PCIE_TAG_COUNT > 256) begin + $error("Error: PCIe tag count must be no larger than 256 (instance %m)"); + $finish; + end + end + + if (RQ_SEQ_NUM_ENABLE && TX_LIMIT > 2**(RQ_SEQ_NUM_WIDTH-1)) begin + $error("Error: TX limit out of range (instance %m)"); + $finish; + end + + if (AXI_DATA_WIDTH != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: AXI interface width must match PCIe interface width (instance %m)"); + $finish; + end + + if (AXI_STRB_WIDTH * 8 != AXI_DATA_WIDTH) begin + $error("Error: AXI interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXI_MAX_BURST_LEN < 1 || AXI_MAX_BURST_LEN > 256) begin + $error("Error: AXI_MAX_BURST_LEN must be between 1 and 256 (instance %m)"); + $finish; + end + + if (AXI_ID_WIDTH < OP_TAG_WIDTH) begin + $error("Error: AXI_ID_WIDTH must be at least OP_TAG_WIDTH (instance %m)"); + $finish; + end +end + +localparam [3:0] + REQ_MEM_READ = 4'b0000, + REQ_MEM_WRITE = 4'b0001, + REQ_IO_READ = 4'b0010, + REQ_IO_WRITE = 4'b0011, + REQ_MEM_FETCH_ADD = 4'b0100, + REQ_MEM_SWAP = 4'b0101, + REQ_MEM_CAS = 4'b0110, + REQ_MEM_READ_LOCKED = 4'b0111, + REQ_CFG_READ_0 = 4'b1000, + REQ_CFG_READ_1 = 4'b1001, + REQ_CFG_WRITE_0 = 4'b1010, + REQ_CFG_WRITE_1 = 4'b1011, + REQ_MSG = 4'b1100, + REQ_MSG_VENDOR = 4'b1101, + REQ_MSG_ATS = 4'b1110; + +localparam [2:0] + CPL_STATUS_SC = 3'b000, // successful completion + CPL_STATUS_UR = 3'b001, // unsupported request + CPL_STATUS_CRS = 3'b010, // configuration request retry status + CPL_STATUS_CA = 3'b100; // completer abort + +localparam [4:0] + RC_ERROR_NORMAL_TERMINATION = 4'b0000, + RC_ERROR_POISONED = 4'b0001, + RC_ERROR_BAD_STATUS = 4'b0010, + RC_ERROR_INVALID_LENGTH = 4'b0011, + RC_ERROR_MISMATCH = 4'b0100, + RC_ERROR_INVALID_ADDRESS = 4'b0101, + RC_ERROR_INVALID_TAG = 4'b0110, + RC_ERROR_TIMEOUT = 4'b1001, + RC_ERROR_FLR = 4'b1000; + +localparam [1:0] + REQ_STATE_IDLE = 2'd0, + REQ_STATE_START = 2'd1, + REQ_STATE_HEADER = 2'd2; + +reg [1:0] req_state_reg = REQ_STATE_IDLE, req_state_next; + +localparam [2:0] + TLP_STATE_IDLE = 3'd0, + TLP_STATE_HEADER = 3'd1, + TLP_STATE_START = 3'd2, + TLP_STATE_TRANSFER = 3'd3, + TLP_STATE_DROP_TAG = 3'd4, + TLP_STATE_WAIT_END = 3'd5; + +reg [2:0] tlp_state_reg = TLP_STATE_IDLE, tlp_state_next; + +// datapath control signals +reg transfer_in_save; + +reg tag_table_we_req; + +reg tlp_cmd_ready; + +reg finish_tag; + +reg [3:0] first_be; +reg [3:0] last_be; +reg [10:0] dword_count; +reg req_last_tlp; +reg [PCIE_ADDR_WIDTH-1:0] req_pcie_addr; + +reg [PCIE_ADDR_WIDTH-1:0] req_pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, req_pcie_addr_next; +reg [AXI_ADDR_WIDTH-1:0] req_axi_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, req_axi_addr_next; +reg [LEN_WIDTH-1:0] req_op_count_reg = {LEN_WIDTH{1'b0}}, req_op_count_next; +reg [12:0] req_tlp_count_reg = 13'd0, req_tlp_count_next; + +reg [11:0] lower_addr_reg = 12'd0, lower_addr_next; +reg [12:0] byte_count_reg = 13'd0, byte_count_next; +reg [3:0] error_code_reg = 4'd0, error_code_next; +reg [AXI_ADDR_WIDTH-1:0] axi_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, axi_addr_next; +reg axi_addr_valid_reg = 1'b0, axi_addr_valid_next; +reg [9:0] op_dword_count_reg = 10'd0, op_dword_count_next; +reg [12:0] op_count_reg = 13'd0, op_count_next; +reg [12:0] tr_count_reg = 13'd0, tr_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] input_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, input_cycle_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] output_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, output_cycle_count_next; +reg input_active_reg = 1'b0, input_active_next; +reg bubble_cycle_reg = 1'b0, bubble_cycle_next; +reg first_cycle_reg = 1'b0, first_cycle_next; +reg last_cycle_reg = 1'b0, last_cycle_next; +reg [PCIE_TAG_WIDTH-1:0] pcie_tag_reg = {PCIE_TAG_WIDTH{1'b0}}, pcie_tag_next; +reg [OP_TAG_WIDTH-1:0] op_tag_reg = {OP_TAG_WIDTH{1'b0}}, op_tag_next; +reg final_cpl_reg = 1'b0, final_cpl_next; + +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; +reg [OFFSET_WIDTH-1:0] first_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, first_cycle_offset_next; +reg [OFFSET_WIDTH-1:0] last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, last_cycle_offset_next; + +reg [AXI_ADDR_WIDTH-1:0] tlp_cmd_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, tlp_cmd_addr_next; +reg [OP_TAG_WIDTH-1:0] tlp_cmd_op_tag_reg = {OP_TAG_WIDTH{1'b0}}, tlp_cmd_op_tag_next; +reg [TAG_WIDTH-1:0] tlp_cmd_tag_reg = {TAG_WIDTH{1'b0}}, tlp_cmd_tag_next; +reg [PCIE_TAG_WIDTH-1:0] tlp_cmd_pcie_tag_reg = {PCIE_TAG_WIDTH{1'b0}}, tlp_cmd_pcie_tag_next; +reg tlp_cmd_last_reg = 1'b0, tlp_cmd_last_next; +reg tlp_cmd_valid_reg = 1'b0, tlp_cmd_valid_next; + +reg [AXI_ADDR_WIDTH-1:0] tag_table_axi_addr[(2**PCIE_TAG_WIDTH)-1:0]; +reg [OP_TAG_WIDTH-1:0] tag_table_op_tag[(2**PCIE_TAG_WIDTH)-1:0]; +reg tag_table_we_tlp_reg = 1'b0, tag_table_we_tlp_next; + +reg [10:0] max_read_request_size_dw_reg = 11'd0; + +reg have_credit_reg = 1'b0; + +reg [RQ_SEQ_NUM_WIDTH-1:0] active_tx_count_reg = {RQ_SEQ_NUM_WIDTH{1'b0}}; +reg active_tx_count_av_reg = 1'b1; +reg inc_active_tx; + +reg s_axis_rc_tready_reg = 1'b0, s_axis_rc_tready_next; +reg s_axis_read_desc_ready_reg = 1'b0, s_axis_read_desc_ready_next; + +reg [TAG_WIDTH-1:0] m_axis_read_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_read_desc_status_tag_next; +reg m_axis_read_desc_status_valid_reg = 1'b0, m_axis_read_desc_status_valid_next; + +reg [AXI_ID_WIDTH-1:0] m_axi_awid_reg = {AXI_ID_WIDTH{1'b0}}, m_axi_awid_next; +reg [AXI_ADDR_WIDTH-1:0] m_axi_awaddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_awaddr_next; +reg [7:0] m_axi_awlen_reg = 8'd0, m_axi_awlen_next; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; +reg m_axi_bready_reg = 1'b0, m_axi_bready_next; + +reg status_error_cor_reg = 1'b0, status_error_cor_next; +reg status_error_uncor_reg = 1'b0, status_error_uncor_next; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] save_axis_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; + +wire [AXI_DATA_WIDTH-1:0] shift_axis_tdata = {s_axis_rc_tdata, save_axis_tdata_reg} >> ((AXI_STRB_WIDTH-offset_reg)*AXI_WORD_SIZE); + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_int; +reg m_axis_rq_tvalid_int; +reg m_axis_rq_tready_int_reg = 1'b0; +reg m_axis_rq_tlast_int; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_int; +wire m_axis_rq_tready_int_early; + +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_int; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +reg m_axi_wlast_int; +wire m_axi_wready_int_early; + +assign s_axis_rc_tready = s_axis_rc_tready_reg; +assign s_axis_read_desc_ready = s_axis_read_desc_ready_reg; + +assign m_axis_read_desc_status_tag = m_axis_read_desc_status_tag_reg; +assign m_axis_read_desc_status_valid = m_axis_read_desc_status_valid_reg; + +assign m_axi_awid = m_axi_awid_reg; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = $clog2(AXI_STRB_WIDTH); +assign m_axi_awburst = 2'b01; +assign m_axi_awlock = 1'b0; +assign m_axi_awcache = 4'b0011; +assign m_axi_awprot = 3'b010; +assign m_axi_awvalid = m_axi_awvalid_reg; +assign m_axi_bready = m_axi_bready_reg; + +assign status_error_cor = status_error_cor_reg; +assign status_error_uncor = status_error_uncor_reg; + +wire [PCIE_ADDR_WIDTH-1:0] req_pcie_addr_plus_max_read_request = req_pcie_addr_reg + {max_read_request_size_dw_reg, 2'b00}; +wire [PCIE_ADDR_WIDTH-1:0] req_pcie_addr_plus_op_count = req_pcie_addr_reg + req_op_count_reg; +wire [PCIE_ADDR_WIDTH-1:0] req_pcie_addr_plus_tlp_count = req_pcie_addr_reg + req_tlp_count_reg; + +// PCIe tag management +wire [PCIE_TAG_WIDTH-1:0] new_tag; +wire new_tag_valid; +reg new_tag_ready; + +wire [PCIE_TAG_COUNT-1:0] active_tags; + +pcie_tag_manager #( + .PCIE_TAG_COUNT(PCIE_TAG_COUNT), + .PCIE_TAG_WIDTH(PCIE_TAG_WIDTH), + .PCIE_EXT_TAG_ENABLE(PCIE_EXT_TAG_ENABLE) +) +pcie_tag_manager_inst ( + .clk(clk), + .rst(rst), + + .m_axis_tag(new_tag), + .m_axis_tag_valid(new_tag_valid), + .m_axis_tag_ready(new_tag_ready), + + .s_axis_tag(pcie_tag_reg), + .s_axis_tag_valid(finish_tag), + + .ext_tag_enable(ext_tag_enable), + + .active_tags(active_tags) +); + +// operation tag management +wire [OP_TAG_WIDTH-1:0] op_table_start_ptr; +wire op_table_start_ptr_valid; +reg [TAG_WIDTH-1:0] op_table_start_tag; +reg op_table_start_en; +reg [OP_TAG_WIDTH-1:0] op_table_finish_ptr; +reg op_table_finish_en; +reg [OP_TAG_WIDTH-1:0] op_table_read_start_ptr; +reg op_table_read_start_commit; +reg op_table_read_start_en; +reg [OP_TAG_WIDTH-1:0] op_table_read_finish_ptr; +reg op_table_read_finish_en; +reg [OP_TAG_WIDTH-1:0] op_table_write_start_ptr; +reg op_table_write_start_commit; +reg op_table_write_start_en; +reg [OP_TAG_WIDTH-1:0] op_table_write_finish_ptr; +reg op_table_write_finish_en; + +reg [2**OP_TAG_WIDTH-1:0] op_table_active = 0; +reg [TAG_WIDTH-1:0] op_table_tag [2**OP_TAG_WIDTH-1:0]; +reg op_table_init [2**OP_TAG_WIDTH-1:0]; +reg op_table_read_init [2**OP_TAG_WIDTH-1:0]; +reg op_table_read_commit [2**OP_TAG_WIDTH-1:0]; +reg op_table_read_error [2**OP_TAG_WIDTH-1:0]; +reg [OP_TABLE_READ_COUNT_WIDTH-1:0] op_table_read_count_start [2**OP_TAG_WIDTH-1:0]; +reg [OP_TABLE_READ_COUNT_WIDTH-1:0] op_table_read_count_finish [2**OP_TAG_WIDTH-1:0]; +reg op_table_write_init [2**OP_TAG_WIDTH-1:0]; +reg op_table_write_commit [2**OP_TAG_WIDTH-1:0]; +reg [OP_TABLE_WRITE_COUNT_WIDTH-1:0] op_table_write_count_start [2**OP_TAG_WIDTH-1:0]; +reg [OP_TABLE_WRITE_COUNT_WIDTH-1:0] op_table_write_count_finish [2**OP_TAG_WIDTH-1:0]; + +priority_encoder #( + .WIDTH(2**OP_TAG_WIDTH), + .LSB_PRIORITY("HIGH") +) +op_table_start_ptr_enc_inst ( + .input_unencoded(~op_table_active), + .output_valid(op_table_start_ptr_valid), + .output_encoded(op_table_start_ptr), + .output_unencoded() +); + +integer i; + +initial begin + for (i = 0; i < 2**OP_TAG_WIDTH; i = i + 1) begin + op_table_tag[i] = 0; + op_table_init[i] = 0; + op_table_read_init[i] = 0; + op_table_read_commit[i] = 0; + op_table_read_count_start[i] = 0; + op_table_read_count_finish[i] = 0; + op_table_write_init[i] = 0; + op_table_write_commit[i] = 0; + op_table_write_count_start[i] = 0; + op_table_write_count_finish[i] = 0; + end + + for (i = 0; i < 2**PCIE_TAG_WIDTH; i = i + 1) begin + tag_table_axi_addr[i] = 0; + tag_table_op_tag[i] = 0; + end +end + +always @* begin + req_state_next = REQ_STATE_IDLE; + + s_axis_read_desc_ready_next = 1'b0; + + req_pcie_addr_next = req_pcie_addr_reg; + req_axi_addr_next = req_axi_addr_reg; + req_op_count_next = req_op_count_reg; + req_tlp_count_next = req_tlp_count_reg; + + tlp_cmd_addr_next = tlp_cmd_addr_reg; + tlp_cmd_op_tag_next = tlp_cmd_op_tag_reg; + tlp_cmd_tag_next = tlp_cmd_tag_reg; + tlp_cmd_pcie_tag_next = tlp_cmd_pcie_tag_reg; + tlp_cmd_last_next = tlp_cmd_last_reg; + tlp_cmd_valid_next = tlp_cmd_valid_reg && !tlp_cmd_ready; + + inc_active_tx = 1'b0; + + m_axis_rq_tdata_int = {AXIS_PCIE_DATA_WIDTH{1'b0}}; + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; + m_axis_rq_tvalid_int = 1'b0; + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tlast_int = 1'b1; + end else begin + m_axis_rq_tlast_int = 1'b0; + end + m_axis_rq_tuser_int = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + + m_axis_rq_tdata_int[1:0] = 2'b0; // address type + m_axis_rq_tdata_int[63:2] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:2]; // address + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tdata_int[74:64] = 11'd0; // DWORD count + m_axis_rq_tdata_int[78:75] = REQ_MEM_READ; // request type - memory read + m_axis_rq_tdata_int[79] = 1'b0; // poisoned request + m_axis_rq_tdata_int[95:80] = requester_id; + m_axis_rq_tdata_int[103:96] = new_tag; + m_axis_rq_tdata_int[119:104] = 16'd0; // completer ID + m_axis_rq_tdata_int[120] = requester_id_enable; + m_axis_rq_tdata_int[123:121] = 3'b000; // traffic class + m_axis_rq_tdata_int[126:124] = 3'b000; // attr + m_axis_rq_tdata_int[127] = 1'b0; // force ECRC + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tkeep_int = 16'b0000000000001111; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_rq_tkeep_int = 8'b00001111; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_rq_tkeep_int = 4'b1111; + end else begin + m_axis_rq_tkeep_int = 2'b11; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[3:0] = 4'd0; // first BE 0 + m_axis_rq_tuser_int[7:4] = 4'd0; // first BE 1 + m_axis_rq_tuser_int[11:8] = 4'd0; // last BE 0 + m_axis_rq_tuser_int[15:12] = 4'd0; // last BE 1 + m_axis_rq_tuser_int[19:16] = 3'd0; // addr_offset + m_axis_rq_tuser_int[21:20] = 2'b01; // is_sop + m_axis_rq_tuser_int[23:22] = 2'd0; // is_sop0_ptr + m_axis_rq_tuser_int[25:24] = 2'd0; // is_sop1_ptr + m_axis_rq_tuser_int[27:26] = 2'b01; // is_eop + m_axis_rq_tuser_int[31:28] = 4'd3; // is_eop0_ptr + m_axis_rq_tuser_int[35:32] = 4'd0; // is_eop1_ptr + m_axis_rq_tuser_int[36] = 1'b0; // discontinue + m_axis_rq_tuser_int[38:37] = 2'b00; // tph_present + m_axis_rq_tuser_int[42:39] = 4'b0000; // tph_type + m_axis_rq_tuser_int[44:43] = 2'b00; // tph_indirect_tag_en + m_axis_rq_tuser_int[60:45] = 16'd0; // tph_st_tag + m_axis_rq_tuser_int[66:61] = 6'd0; // seq_num0 + m_axis_rq_tuser_int[72:67] = 6'd0; // seq_num1 + m_axis_rq_tuser_int[136:73] = 64'd0; // parity + end else begin + m_axis_rq_tuser_int[3:0] = 4'd0; // first BE + m_axis_rq_tuser_int[7:4] = 4'd0; // last BE + m_axis_rq_tuser_int[10:8] = 3'd0; // addr_offset + m_axis_rq_tuser_int[11] = 1'b0; // discontinue + m_axis_rq_tuser_int[12] = 1'b0; // tph_present + m_axis_rq_tuser_int[14:13] = 2'b00; // tph_type + m_axis_rq_tuser_int[15] = 1'b0; // tph_indirect_tag_en + m_axis_rq_tuser_int[23:16] = 8'd0; // tph_st_tag + m_axis_rq_tuser_int[27:24] = 4'd0; // seq_num + m_axis_rq_tuser_int[59:28] = 32'd0; // parity + if (AXIS_PCIE_RQ_USER_WIDTH == 62) begin + m_axis_rq_tuser_int[61:60] = 2'd0; // seq_num + end + end + + new_tag_ready = 1'b0; + op_table_start_tag = s_axis_read_desc_tag; + op_table_start_en = 1'b0; + + op_table_read_start_ptr = tlp_cmd_op_tag_reg; + op_table_read_start_commit = 1'b0; + op_table_read_start_en = 1'b0; + + // TLP size computation + if (req_op_count_reg + req_pcie_addr_reg[1:0] <= {max_read_request_size_dw_reg, 2'b00}) begin + // packet smaller than max read request size + if (req_pcie_addr_reg[12] != req_pcie_addr_plus_op_count[12]) begin + // crosses 4k boundary + req_tlp_count_next = 13'h1000 - req_pcie_addr_reg[11:0]; + dword_count = 11'h400 - req_pcie_addr_reg[11:2]; + req_last_tlp = req_pcie_addr_plus_op_count[11:0] == 0; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]+1; + req_pcie_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + req_tlp_count_next = req_op_count_reg; + dword_count = (req_op_count_reg + req_pcie_addr_reg[1:0] + 3) >> 2; + req_last_tlp = 1'b1; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]; + req_pcie_addr[11:0] = req_pcie_addr_reg[11:0] + req_op_count_reg; + end + end else begin + // packet larger than max read request size + if (req_pcie_addr_reg[12] != req_pcie_addr_plus_max_read_request[12]) begin + // crosses 4k boundary + req_tlp_count_next = 13'h1000 - req_pcie_addr_reg[11:0]; + dword_count = 11'h400 - req_pcie_addr_reg[11:2]; + req_last_tlp = 1'b0; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]+1; + req_pcie_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + req_tlp_count_next = {max_read_request_size_dw_reg, 2'b00}-req_pcie_addr_reg[1:0]; + dword_count = max_read_request_size_dw_reg; + req_last_tlp = 1'b0; + // optimized req_pcie_addr = req_addr_reg + req_tlp_count_next + req_pcie_addr[PCIE_ADDR_WIDTH-1:12] = req_pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]; + req_pcie_addr[11:0] = {req_pcie_addr_reg[11:2] + max_read_request_size_dw_reg, 2'b00}; + end + end + + first_be = 4'b1111 << req_pcie_addr_reg[1:0]; + last_be = 4'b1111 >> (3 - ((req_pcie_addr_reg[1:0] + req_tlp_count_next[1:0] - 1) & 3)); + + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tdata_int[74:64] = dword_count; // DWORD count + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[3:0] = dword_count == 1 ? first_be & last_be : first_be; // first BE 0 + m_axis_rq_tuser_int[7:4] = 4'd0; // first BE 1 + m_axis_rq_tuser_int[11:8] = dword_count == 1 ? 4'b0000 : last_be; // last BE 0 + m_axis_rq_tuser_int[15:12] = 4'd0; // last BE 1 + end else begin + m_axis_rq_tuser_int[3:0] = dword_count == 1 ? first_be & last_be : first_be; // first BE + m_axis_rq_tuser_int[7:4] = dword_count == 1 ? 4'b0000 : last_be; // last BE + end + + // TLP segmentation and request generation + case (req_state_reg) + REQ_STATE_IDLE: begin + s_axis_read_desc_ready_next = enable && !tlp_cmd_valid_reg && op_table_start_ptr_valid; + + if (s_axis_read_desc_ready && s_axis_read_desc_valid) begin + s_axis_read_desc_ready_next = 1'b0; + req_pcie_addr_next = s_axis_read_desc_pcie_addr; + req_axi_addr_next = s_axis_read_desc_axi_addr; + req_op_count_next = s_axis_read_desc_len; + tlp_cmd_tag_next = s_axis_read_desc_tag; + tlp_cmd_op_tag_next = op_table_start_ptr; + op_table_start_tag = s_axis_read_desc_tag; + op_table_start_en = 1'b1; + req_state_next = REQ_STATE_START; + end else begin + req_state_next = REQ_STATE_IDLE; + end + end + REQ_STATE_START: begin + if (m_axis_rq_tready_int_reg && !tlp_cmd_valid_reg && new_tag_valid && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + + m_axis_rq_tvalid_int = 1'b1; + + inc_active_tx = 1'b1; + + if (AXIS_PCIE_DATA_WIDTH > 64) begin + req_pcie_addr_next = req_pcie_addr; + req_axi_addr_next = req_axi_addr_reg + req_tlp_count_next; + req_op_count_next = req_op_count_reg - req_tlp_count_next; + + new_tag_ready = 1'b1; + + tlp_cmd_addr_next = req_axi_addr_reg; + tlp_cmd_pcie_tag_next = new_tag; + tlp_cmd_last_next = req_last_tlp; + tlp_cmd_valid_next = 1'b1; + + op_table_read_start_ptr = tlp_cmd_op_tag_reg; + op_table_read_start_commit = req_last_tlp; + op_table_read_start_en = 1'b1; + + if (!req_last_tlp) begin + req_state_next = REQ_STATE_START; + end else begin + s_axis_read_desc_ready_next = 1'b0; + req_state_next = REQ_STATE_IDLE; + end + end else begin + req_state_next = REQ_STATE_HEADER; + end + end else begin + req_state_next = REQ_STATE_START; + end + end + REQ_STATE_HEADER: begin + if (m_axis_rq_tready_int_reg && !tlp_cmd_valid_reg && new_tag_valid) begin + req_pcie_addr_next = req_pcie_addr; + req_axi_addr_next = req_axi_addr_reg + req_tlp_count_next; + req_op_count_next = req_op_count_reg - req_tlp_count_next; + + new_tag_ready = 1'b1; + + m_axis_rq_tdata_int[10:0] = dword_count; // DWORD count + m_axis_rq_tdata_int[14:11] = REQ_MEM_READ; // request type - memory read + m_axis_rq_tdata_int[15] = 1'b0; // poisoned request + m_axis_rq_tdata_int[31:16] = requester_id; + m_axis_rq_tdata_int[40:32] = new_tag; + m_axis_rq_tdata_int[55:41] = 16'd0; // completer ID + m_axis_rq_tdata_int[56] = requester_id_enable; + m_axis_rq_tdata_int[59:57] = 3'b000; // traffic class + m_axis_rq_tdata_int[62:60] = 3'b000; // attr + m_axis_rq_tdata_int[63] = 1'b0; // force ECRC + m_axis_rq_tlast_int = 1'b1; + m_axis_rq_tvalid_int = 1'b1; + + tlp_cmd_addr_next = req_axi_addr_reg; + tlp_cmd_pcie_tag_next = new_tag; + tlp_cmd_last_next = req_last_tlp; + tlp_cmd_valid_next = 1'b1; + + op_table_read_start_ptr = tlp_cmd_op_tag_reg; + op_table_read_start_commit = req_last_tlp; + op_table_read_start_en = 1'b1; + + if (!req_last_tlp) begin + req_state_next = REQ_STATE_START; + end else begin + s_axis_read_desc_ready_next = 1'b0; + req_state_next = REQ_STATE_IDLE; + end + end else begin + req_state_next = REQ_STATE_HEADER; + end + end + endcase +end + +always @* begin + tlp_state_next = TLP_STATE_IDLE; + + transfer_in_save = 1'b0; + + finish_tag = 1'b0; + + tag_table_we_tlp_next = 1'b0; + + s_axis_rc_tready_next = 1'b0; + + m_axis_read_desc_status_tag_next = m_axis_read_desc_status_tag_reg; + m_axis_read_desc_status_valid_next = 1'b0; + + lower_addr_next = lower_addr_reg; + byte_count_next = byte_count_reg; + error_code_next = error_code_reg; + axi_addr_next = axi_addr_reg; + axi_addr_valid_next = axi_addr_valid_reg; + op_count_next = op_count_reg; + tr_count_next = tr_count_reg; + op_dword_count_next = op_dword_count_reg; + input_cycle_count_next = input_cycle_count_reg; + output_cycle_count_next = output_cycle_count_reg; + input_active_next = input_active_reg; + bubble_cycle_next = bubble_cycle_reg; + first_cycle_next = first_cycle_reg; + last_cycle_next = last_cycle_reg; + pcie_tag_next = pcie_tag_reg; + op_tag_next = op_tag_reg; + final_cpl_next = final_cpl_reg; + offset_next = offset_reg; + first_cycle_offset_next = first_cycle_offset_reg; + last_cycle_offset_next = last_cycle_offset_reg; + + m_axi_awid_next = m_axi_awid_reg; + m_axi_awaddr_next = m_axi_awaddr_reg; + m_axi_awlen_next = m_axi_awlen_reg; + m_axi_awvalid_next = m_axi_awvalid_reg && !m_axi_awready; + m_axi_bready_next = 1'b0; + + m_axi_wdata_int = shift_axis_tdata; + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b1}}; + m_axi_wvalid_int = 1'b0; + m_axi_wlast_int = 1'b0; + + status_error_cor_next = 1'b0; + status_error_uncor_next = 1'b0; + + op_table_finish_ptr = m_axi_bid; + op_table_finish_en = 1'b0; + op_table_read_finish_ptr = op_tag_reg; + op_table_read_finish_en = 1'b0; + op_table_write_start_ptr = op_tag_reg; + op_table_write_start_commit = 1'b0; + op_table_write_start_en = 1'b0; + op_table_write_finish_ptr = m_axi_bid; + op_table_write_finish_en = 1'b0; + + // TLP response handling and AXI operation generation + case (tlp_state_reg) + TLP_STATE_IDLE: begin + // idle state, wait for completion + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_rc_tready_next = 1'b0; + + if (s_axis_rc_tvalid) begin + // header fields + lower_addr_next = s_axis_rc_tdata[11:0]; // lower address + error_code_next = s_axis_rc_tdata[15:12]; // error code + byte_count_next = s_axis_rc_tdata[28:16]; // byte count + //s_axis_rc_tdata[29]; // locked read + //s_axis_rc_tdata[30]; // request completed + op_dword_count_next = s_axis_rc_tdata[42:32]; // DWORD count + //s_axis_rc_tdata[45:43]; // completion status + //s_axis_rc_tdata[46]; // poisoned completion + //s_axis_rc_tdata[63:48]; // requester ID + pcie_tag_next = s_axis_rc_tdata[71:64]; // tag + //s_axis_rc_tdata[87:72]; // completer ID + //s_axis_rc_tdata[91:89]; // attr + //s_axis_rc_tdata[94:92]; // tc + + // tuser fields + //s_axis_rc_tuser[31:0]; // byte enables + //s_axis_rc_tuser[32]; // is_sof_0 + //s_axis_rc_tuser[33]; // is_sof_1 + //s_axis_rc_tuser[37:34]; // is_eof_0 + //s_axis_rc_tuser[41:38]; // is_eof_1 + //s_axis_rc_tuser[42]; // discontinue + //s_axis_rc_tuser[74:43]; // parity + + if (byte_count_next > (op_dword_count_next << 2) - lower_addr_next[1:0]) begin + // more completions to follow + op_count_next = (op_dword_count_next << 2) - lower_addr_next[1:0]; + final_cpl_next = 1'b0; + end else begin + // last completion + op_count_next = byte_count_next; + final_cpl_next = 1'b1; + end + + if (!axi_addr_valid_reg || pcie_tag_reg != pcie_tag_next) begin + // current AXI address not valid, so read it from table + axi_addr_next = tag_table_axi_addr[pcie_tag_next]; + end + + offset_next = axi_addr_next[OFFSET_WIDTH-1:0] - (12+lower_addr_next[1:0]); + bubble_cycle_next = axi_addr_next[OFFSET_WIDTH-1:0] < 12+lower_addr_next[1:0]; + first_cycle_offset_next = axi_addr_next[OFFSET_WIDTH-1:0]; + first_cycle_next = 1'b1; + + // AXI transfer size computation + if (op_count_next <= AXI_MAX_BURST_SIZE-axi_addr_next[OFFSET_WIDTH-1:0] || AXI_MAX_BURST_SIZE >= 4096) begin + // packet smaller than max burst size + if ((axi_addr_next ^ (axi_addr_next + op_count_next)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = op_count_next; + end + end else begin + // packet larger than max burst size + if ((axi_addr_next ^ (axi_addr_next + AXI_MAX_BURST_SIZE)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = AXI_MAX_BURST_SIZE - axi_addr_next[OFFSET_WIDTH-1:0]; + end + end + + op_tag_next = tag_table_op_tag[pcie_tag_next]; + + if (active_tags[pcie_tag_next] && error_code_next == RC_ERROR_NORMAL_TERMINATION) begin + // no error + axi_addr_valid_next = !final_cpl_next; + s_axis_rc_tready_next = !m_axi_awvalid || m_axi_awready; + tlp_state_next = TLP_STATE_START; + end else if (error_code_next == RC_ERROR_MISMATCH) begin + // mismatched fields + // Handle as malformed TLP (2.3.2) + // drop TLP and report uncorrectable error + status_error_uncor_next = 1'b1; + axi_addr_valid_next = 1'b0; + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_WAIT_END; + end else if (!active_tags[pcie_tag_next] || error_code_next == RC_ERROR_INVALID_TAG) begin + // invalid tag + // Handle as unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + // drop TLP and report correctable error + status_error_cor_next = 1'b1; + axi_addr_valid_next = 1'b0; + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_WAIT_END; + end else begin + // request terminated by other error (tag valid) + // report error + case (error_code_next) + RC_ERROR_POISONED: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.3) + RC_ERROR_BAD_STATUS: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.1) + RC_ERROR_INVALID_LENGTH: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_MISMATCH: status_error_uncor_next = 1'b1; // malformed TLP (2.3.2) + RC_ERROR_INVALID_ADDRESS: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_INVALID_TAG: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_TIMEOUT: status_error_uncor_next = 1'b1; // uncorrectable (6.2.3.2.4.4) + RC_ERROR_FLR: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + default: status_error_uncor_next = 1'b1; + endcase + // last request in current transfer + axi_addr_valid_next = 1'b0; + // drop TLP + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_DROP_TAG; + end + end else begin + s_axis_rc_tready_next = 1'b0; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + s_axis_rc_tready_next = 1'b1; + + if (s_axis_rc_tready && s_axis_rc_tvalid) begin + // header fields + lower_addr_next = s_axis_rc_tdata[11:0]; // lower address + error_code_next = s_axis_rc_tdata[15:12]; // error code + byte_count_next = s_axis_rc_tdata[28:16]; // byte count + //s_axis_rc_tdata[29]; // locked read + //s_axis_rc_tdata[30]; // request completed + op_dword_count_next = s_axis_rc_tdata[42:32]; // DWORD count + //s_axis_rc_tdata[45:43]; // completion status + //s_axis_rc_tdata[46]; // poisoned completion + //s_axis_rc_tdata[63:48]; // requester ID + + // tuser fields + //s_axis_rc_tuser[31:0]; // byte enables + //s_axis_rc_tuser[32]; // is_sof_0 + //s_axis_rc_tuser[33]; // is_sof_1 + //s_axis_rc_tuser[37:34]; // is_eof_0 + //s_axis_rc_tuser[41:38]; // is_eof_1 + //s_axis_rc_tuser[42]; // discontinue + //s_axis_rc_tuser[74:43]; // parity + + if (byte_count_next > (op_dword_count_next << 2) - lower_addr_next[1:0]) begin + // more completions to follow + op_count_next = (op_dword_count_next << 2) - lower_addr_next[1:0]; + final_cpl_next = 1'b0; + end else begin + // last completion + op_count_next = byte_count_next; + final_cpl_next = 1'b1; + end + + if (s_axis_rc_tlast) begin + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_IDLE; + end else begin + s_axis_rc_tready_next = 1'b0; + tlp_state_next = TLP_STATE_HEADER; + end + end else begin + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_IDLE; + end + end + end + TLP_STATE_HEADER: begin + // header state; process header (64 bit interface only) + s_axis_rc_tready_next = 1'b0; + + if (s_axis_rc_tvalid) begin + pcie_tag_next = s_axis_rc_tdata[7:0]; // tag + //s_axis_rc_tdata[23:8]; // completer ID + //s_axis_rc_tdata[27:25]; // attr + //s_axis_rc_tdata[30:28]; // tc + + if (!axi_addr_valid_reg || pcie_tag_reg != pcie_tag_next) begin + // current AXI address not valid, so read it from table + axi_addr_next = tag_table_axi_addr[pcie_tag_next]; + end + + offset_next = axi_addr_next[OFFSET_WIDTH-1:0] - (4+lower_addr_reg[1:0]); + bubble_cycle_next = axi_addr_next[OFFSET_WIDTH-1:0] < 4+lower_addr_reg[1:0]; + first_cycle_offset_next = axi_addr_next[OFFSET_WIDTH-1:0]; + first_cycle_next = 1'b1; + + // AXI transfer size computation + if (op_count_next <= AXI_MAX_BURST_SIZE-axi_addr_next[OFFSET_WIDTH-1:0] || AXI_MAX_BURST_SIZE >= 4096) begin + // packet smaller than max burst size + if ((axi_addr_next ^ (axi_addr_next + op_count_next)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = op_count_next; + end + end else begin + // packet larger than max burst size + if ((axi_addr_next ^ (axi_addr_next + AXI_MAX_BURST_SIZE)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = AXI_MAX_BURST_SIZE - axi_addr_next[OFFSET_WIDTH-1:0]; + end + end + + op_tag_next = tag_table_op_tag[pcie_tag_next]; + + if (active_tags[pcie_tag_next] && error_code_reg == RC_ERROR_NORMAL_TERMINATION) begin + // no error + axi_addr_valid_next = !final_cpl_next; + s_axis_rc_tready_next = !m_axi_awvalid || m_axi_awready; + tlp_state_next = TLP_STATE_START; + end else if (error_code_next == RC_ERROR_MISMATCH) begin + // mismatched fields + // Handle as malformed TLP (2.3.2) + // drop TLP and report uncorrectable error + status_error_uncor_next = 1'b1; + axi_addr_valid_next = 1'b0; + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_WAIT_END; + end else if (!active_tags[pcie_tag_next] || error_code_next == RC_ERROR_INVALID_TAG) begin + // invalid tag or mismatched fields (tag invalid) + // Handle as unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + // drop TLP and report correctable error + status_error_cor_next = 1'b1; + axi_addr_valid_next = 1'b0; + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_WAIT_END; + end else begin + // request terminated by other error (tag valid) + // report error + case (error_code_next) + RC_ERROR_POISONED: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.3) + RC_ERROR_BAD_STATUS: status_error_cor_next = 1'b1; // advisory non-fatal (6.2.3.2.4.1) + RC_ERROR_INVALID_LENGTH: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_MISMATCH: status_error_uncor_next = 1'b1; // malformed TLP (2.3.2) + RC_ERROR_INVALID_ADDRESS: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_INVALID_TAG: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + RC_ERROR_TIMEOUT: status_error_uncor_next = 1'b1; // uncorrectable (6.2.3.2.4.4) + RC_ERROR_FLR: status_error_cor_next = 1'b1; // unexpected completion (2.3.2), advisory non-fatal (6.2.3.2.4.5) + default: status_error_uncor_next = 1'b1; + endcase + // last request in current transfer + axi_addr_valid_next = 1'b0; + // drop TLP + s_axis_rc_tready_next = 1'b1; + tlp_state_next = TLP_STATE_DROP_TAG; + end + end else begin + tlp_state_next = TLP_STATE_HEADER; + end + end + TLP_STATE_START: begin + s_axis_rc_tready_next = !m_axi_awvalid || m_axi_awready; + + if (s_axis_rc_tready && s_axis_rc_tvalid) begin + transfer_in_save = 1'b1; + + if (AXIS_PCIE_DATA_WIDTH == 64) begin + input_cycle_count_next = (tr_count_next + 4+lower_addr_reg[1:0] - 1) >> (AXI_BURST_SIZE); + end else begin + input_cycle_count_next = (tr_count_next + 12+lower_addr_reg[1:0] - 1) >> (AXI_BURST_SIZE); + end + output_cycle_count_next = (tr_count_next + axi_addr_reg[OFFSET_WIDTH-1:0] - 1) >> (AXI_BURST_SIZE); + last_cycle_offset_next = axi_addr_reg[OFFSET_WIDTH-1:0] + tr_count_next; + last_cycle_next = output_cycle_count_next == 0; + input_active_next = 1'b1; + + m_axi_awid_next = op_tag_reg; + m_axi_awaddr_next = axi_addr_reg; + m_axi_awlen_next = output_cycle_count_next; + m_axi_awvalid_next = 1'b1; + + axi_addr_next = axi_addr_reg + tr_count_next; + op_count_next = op_count_reg - tr_count_next; + + // AXI transfer size computation + if (op_count_next <= AXI_MAX_BURST_SIZE-axi_addr_next[OFFSET_WIDTH-1:0] || AXI_MAX_BURST_SIZE >= 4096) begin + // packet smaller than max burst size + if ((axi_addr_next ^ (axi_addr_next + op_count_next)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = op_count_next; + end + end else begin + // packet larger than max burst size + if ((axi_addr_next ^ (axi_addr_next + AXI_MAX_BURST_SIZE)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = AXI_MAX_BURST_SIZE - axi_addr_next[OFFSET_WIDTH-1:0]; + end + end + + op_table_write_start_ptr = op_tag_reg; + op_table_write_start_commit = op_count_next == 0 && final_cpl_reg && op_table_read_commit[op_table_write_start_ptr] && (op_table_read_count_start[op_table_write_start_ptr] == op_table_read_count_finish[op_table_write_start_ptr]); + op_table_write_start_en = 1'b1; + + input_active_next = input_cycle_count_next != 0; + input_cycle_count_next = input_cycle_count_next - 1; + s_axis_rc_tready_next = m_axi_wready_int_early && input_active_next && bubble_cycle_reg && (!last_cycle_next || op_count_next == 0 || !m_axi_awvalid || m_axi_awready); + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + tlp_state_next = TLP_STATE_START; + end + end + TLP_STATE_TRANSFER: begin + s_axis_rc_tready_next = m_axi_wready_int_early && input_active_reg && !(first_cycle_reg && !bubble_cycle_reg) && (!last_cycle_reg || op_count_reg == 0 || !m_axi_awvalid || m_axi_awready); + + if (m_axi_wready_int_reg && ((s_axis_rc_tready && s_axis_rc_tvalid) || !input_active_reg || (first_cycle_reg && !bubble_cycle_reg)) && (!last_cycle_reg || op_count_reg == 0 || !m_axi_awvalid || m_axi_awready)) begin + transfer_in_save = s_axis_rc_tready && s_axis_rc_tvalid; + + if (first_cycle_reg && !bubble_cycle_reg) begin + m_axi_wdata_int = {save_axis_tdata_reg, {AXIS_PCIE_DATA_WIDTH{1'b0}}} >> ((AXI_STRB_WIDTH-offset_reg)*8); + end else begin + m_axi_wdata_int = shift_axis_tdata; + end + if (first_cycle_reg) begin + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b1}} << first_cycle_offset_reg; + end else begin + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b1}}; + end + + if (input_active_reg && !(first_cycle_reg && !bubble_cycle_reg)) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg != 0; + end + output_cycle_count_next = output_cycle_count_reg - 1; + last_cycle_next = output_cycle_count_next == 0; + + if (last_cycle_reg) begin + if (last_cycle_offset_reg != 0 && op_count_reg == 0) begin + m_axi_wstrb_int = m_axi_wstrb_int & {AXI_STRB_WIDTH{1'b1}} >> (AXI_STRB_WIDTH-last_cycle_offset_reg); + end + m_axi_wlast_int = 1'b1; + end + m_axi_wvalid_int = 1'b1; + first_cycle_next = 1'b0; + if (!last_cycle_reg) begin + // current transfer not finished yet + s_axis_rc_tready_next = m_axi_wready_int_early && input_active_next && (!last_cycle_next || op_count_reg == 0 || !m_axi_awvalid || m_axi_awready); + tlp_state_next = TLP_STATE_TRANSFER; + end else if (op_count_reg != 0) begin + // current transfer done, but operation not finished yet + + // keep offset, no bubble cycles, not first cycle + bubble_cycle_next = 1'b0; + first_cycle_next = 1'b0; + + input_cycle_count_next = (tr_count_next - offset_reg - 1) >> (AXI_BURST_SIZE); + output_cycle_count_next = (tr_count_next + axi_addr_reg[OFFSET_WIDTH-1:0] - 1) >> (AXI_BURST_SIZE); + last_cycle_offset_next = axi_addr_reg[OFFSET_WIDTH-1:0] + tr_count_next; + last_cycle_next = output_cycle_count_next == 0; + input_active_next = tr_count_next > offset_reg; + + m_axi_awaddr_next = axi_addr_reg; + m_axi_awlen_next = output_cycle_count_next; + m_axi_awvalid_next = 1'b1; + + axi_addr_next = axi_addr_reg + tr_count_next; + op_count_next = op_count_reg - tr_count_next; + + // AXI transfer size computation + if (op_count_next <= AXI_MAX_BURST_SIZE-axi_addr_next[OFFSET_WIDTH-1:0] || AXI_MAX_BURST_SIZE >= 4096) begin + // packet smaller than max burst size + if ((axi_addr_next ^ (axi_addr_next + op_count_next)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = op_count_next; + end + end else begin + // packet larger than max burst size + if ((axi_addr_next ^ (axi_addr_next + AXI_MAX_BURST_SIZE)) & (1 << 12)) begin + // crosses 4k boundary + tr_count_next = 13'h1000 - axi_addr_next[11:0]; + end else begin + // does not cross 4k boundary, send one request + tr_count_next = AXI_MAX_BURST_SIZE - axi_addr_next[OFFSET_WIDTH-1:0]; + end + end + + op_table_write_start_ptr = op_tag_reg; + op_table_write_start_commit = op_count_next == 0 && final_cpl_reg && op_table_read_commit[op_table_write_start_ptr] && (op_table_read_count_start[op_table_write_start_ptr] == op_table_read_count_finish[op_table_write_start_ptr]); + op_table_write_start_en = 1'b1; + + s_axis_rc_tready_next = m_axi_wready_int_early && input_active_next && (!last_cycle_next || op_count_next == 0 || !m_axi_awvalid || m_axi_awready); + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + if (final_cpl_reg) begin + // last completion in current read request (PCIe tag) + finish_tag = 1'b1; // release tag + // mark done + op_table_read_finish_ptr = op_tag_reg; + op_table_read_finish_en = 1'b1; + end else begin + // more completions to come, store current address + tag_table_we_tlp_next = 1'b1; + end + + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_rc_tready_next = 1'b0; + end else begin + s_axis_rc_tready_next = 1'b1; + end + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + tlp_state_next = TLP_STATE_TRANSFER; + end + end + TLP_STATE_DROP_TAG: begin + // drop tag and TLP + s_axis_rc_tready_next = 1'b1; + + // release tag + finish_tag = 1'b1; + + // mark done + op_table_read_finish_ptr = op_tag_reg; + op_table_read_finish_en = 1'b1; + + // commit writes if we're done + op_table_write_start_ptr = op_tag_reg; + op_table_write_start_commit = op_table_read_commit[op_table_write_start_ptr] && (op_table_read_count_start[op_table_write_start_ptr] == op_table_read_count_finish[op_table_write_start_ptr]); + + if (s_axis_rc_tready & s_axis_rc_tvalid) begin + if (s_axis_rc_tlast) begin + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_rc_tready_next = 1'b0; + end else begin + s_axis_rc_tready_next = 1'b1; + end + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end + TLP_STATE_WAIT_END: begin + // wait end state, wait for end of TLP + s_axis_rc_tready_next = 1'b1; + + if (s_axis_rc_tready & s_axis_rc_tvalid) begin + if (s_axis_rc_tlast) begin + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_rc_tready_next = 1'b0; + end else begin + s_axis_rc_tready_next = 1'b1; + end + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end else begin + tlp_state_next = TLP_STATE_WAIT_END; + end + end + endcase + + m_axi_bready_next = op_table_active != 0; + + if (m_axi_bready && m_axi_bvalid) begin + op_table_finish_ptr = m_axi_bid; + + op_table_write_finish_ptr = m_axi_bid; + op_table_write_finish_en = 1'b1; + + m_axis_read_desc_status_tag_next = op_table_tag[op_table_write_finish_ptr]; + + if (op_table_write_commit[op_table_write_finish_ptr] && (op_table_write_count_start[op_table_write_finish_ptr] == op_table_write_count_finish[op_table_write_finish_ptr])) begin + op_table_finish_en = 1'b1; + m_axis_read_desc_status_valid_next = 1'b1; + end + end +end + +always @* begin + tag_table_we_req = 1'b0; + tlp_cmd_ready = 1'b0; + + // tag table write management + if (tag_table_we_tlp_reg) begin + + end else if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + tag_table_we_req = 1'b1; + end +end + +always @(posedge clk) begin + req_state_reg <= req_state_next; + tlp_state_reg <= tlp_state_next; + + status_error_cor_reg <= status_error_cor_next; + status_error_uncor_reg <= status_error_uncor_next; + + req_pcie_addr_reg <= req_pcie_addr_next; + req_axi_addr_reg <= req_axi_addr_next; + req_op_count_reg <= req_op_count_next; + req_tlp_count_reg <= req_tlp_count_next; + + lower_addr_reg <= lower_addr_next; + byte_count_reg <= byte_count_next; + error_code_reg <= error_code_next; + axi_addr_reg <= axi_addr_next; + axi_addr_valid_reg <= axi_addr_valid_next; + op_count_reg <= op_count_next; + tr_count_reg <= tr_count_next; + op_dword_count_reg <= op_dword_count_next; + input_cycle_count_reg <= input_cycle_count_next; + output_cycle_count_reg <= output_cycle_count_next; + input_active_reg <= input_active_next; + bubble_cycle_reg <= bubble_cycle_next; + first_cycle_reg <= first_cycle_next; + last_cycle_reg <= last_cycle_next; + pcie_tag_reg <= pcie_tag_next; + op_tag_reg <= op_tag_next; + final_cpl_reg <= final_cpl_next; + + offset_reg <= offset_next; + first_cycle_offset_reg <= first_cycle_offset_next; + last_cycle_offset_reg <= last_cycle_offset_next; + + tlp_cmd_addr_reg <= tlp_cmd_addr_next; + tlp_cmd_op_tag_reg <= tlp_cmd_op_tag_next; + tlp_cmd_tag_reg <= tlp_cmd_tag_next; + tlp_cmd_pcie_tag_reg <= tlp_cmd_pcie_tag_next; + tlp_cmd_last_reg <= tlp_cmd_last_next; + tlp_cmd_valid_reg <= tlp_cmd_valid_next; + + s_axis_rc_tready_reg <= s_axis_rc_tready_next; + + s_axis_read_desc_ready_reg <= s_axis_read_desc_ready_next; + + m_axis_read_desc_status_tag_reg <= m_axis_read_desc_status_tag_next; + m_axis_read_desc_status_valid_reg <= m_axis_read_desc_status_valid_next; + + m_axi_awid_reg <= m_axi_awid_next; + m_axi_awaddr_reg <= m_axi_awaddr_next; + m_axi_awlen_reg <= m_axi_awlen_next; + m_axi_awvalid_reg <= m_axi_awvalid_next; + m_axi_bready_reg <= m_axi_bready_next; + + max_read_request_size_dw_reg <= 11'd32 << (max_read_request_size > 5 ? 5 : max_read_request_size); + + have_credit_reg <= pcie_tx_fc_nph_av > 4; + + if (inc_active_tx && !s_axis_rq_seq_num_valid_0 && !s_axis_rq_seq_num_valid_1) begin + // inc by 1 + active_tx_count_reg <= active_tx_count_reg + 1; + active_tx_count_av_reg <= active_tx_count_reg < (TX_LIMIT-1); + end else if ((inc_active_tx && s_axis_rq_seq_num_valid_0 && s_axis_rq_seq_num_valid_1) || (!inc_active_tx && (s_axis_rq_seq_num_valid_0 ^ s_axis_rq_seq_num_valid_1))) begin + // dec by 1 + active_tx_count_reg <= active_tx_count_reg - 1; + active_tx_count_av_reg <= 1'b1; + end else if (!inc_active_tx && s_axis_rq_seq_num_valid_0 && s_axis_rq_seq_num_valid_1) begin + // dec by 2 + active_tx_count_reg <= active_tx_count_reg - 2; + active_tx_count_av_reg <= 1'b1; + end else begin + active_tx_count_av_reg <= active_tx_count_reg < TX_LIMIT; + end + + if (transfer_in_save) begin + save_axis_tdata_reg <= s_axis_rc_tdata; + end + + tag_table_we_tlp_reg <= tag_table_we_tlp_next; + + if (tag_table_we_tlp_reg) begin + tag_table_axi_addr[pcie_tag_reg] <= axi_addr_reg; + end else if (tlp_cmd_valid_reg && tag_table_we_req) begin + tag_table_axi_addr[tlp_cmd_pcie_tag_reg] <= tlp_cmd_addr_reg; + tag_table_op_tag[tlp_cmd_pcie_tag_reg] <= tlp_cmd_op_tag_reg; + end + + if (op_table_start_en) begin + op_table_active[op_table_start_ptr] <= 1'b1; + op_table_tag[op_table_start_ptr] <= op_table_start_tag; + op_table_init[op_table_start_ptr] <= !op_table_init[op_table_start_ptr]; + end + + if (op_table_finish_en) begin + op_table_active[op_table_finish_ptr] <= 1'b0; + end + + if (op_table_read_start_en) begin + op_table_read_init[op_table_read_start_ptr] <= op_table_init[op_table_read_start_ptr]; + op_table_read_commit[op_table_read_start_ptr] <= op_table_read_start_commit; + if (op_table_read_init[op_table_read_start_ptr] != op_table_init[op_table_read_start_ptr]) begin + op_table_read_count_start[op_table_read_start_ptr] <= op_table_read_count_finish[op_table_read_start_ptr]; + end else begin + op_table_read_count_start[op_table_read_start_ptr] <= op_table_read_count_start[op_table_read_start_ptr] + 1; + end + end + + if (op_table_read_finish_en) begin + op_table_read_count_finish[op_table_read_finish_ptr] <= op_table_read_count_finish[op_table_read_finish_ptr] + 1; + end + + if (op_table_write_start_en) begin + op_table_write_init[op_table_write_start_ptr] <= op_table_init[op_table_write_start_ptr]; + op_table_write_commit[op_table_write_start_ptr] <= op_table_write_start_commit; + if (op_table_write_init[op_table_write_start_ptr] != op_table_init[op_table_write_start_ptr]) begin + op_table_write_count_start[op_table_write_start_ptr] <= op_table_write_count_finish[op_table_write_start_ptr]; + end else begin + op_table_write_count_start[op_table_write_start_ptr] <= op_table_write_count_start[op_table_write_start_ptr] + 1; + end + end else if (op_table_write_start_commit) begin + op_table_write_commit[op_table_write_start_ptr] <= op_table_write_start_commit; + end + + if (op_table_write_finish_en) begin + op_table_write_count_finish[op_table_write_finish_ptr] <= op_table_write_count_finish[op_table_write_finish_ptr] + 1; + end + + if (rst) begin + req_state_reg <= REQ_STATE_IDLE; + tlp_state_reg <= TLP_STATE_IDLE; + axi_addr_valid_reg <= 1'b0; + tlp_cmd_valid_reg <= 1'b0; + s_axis_rc_tready_reg <= 1'b0; + s_axis_read_desc_ready_reg <= 1'b0; + m_axis_read_desc_status_valid_reg <= 1'b0; + m_axi_awvalid_reg <= 1'b0; + m_axi_bready_reg <= 1'b0; + + active_tx_count_reg <= {RQ_SEQ_NUM_WIDTH{1'b0}}; + active_tx_count_av_reg = 1'b1; + + tag_table_we_tlp_reg <= 1'b0; + op_table_active <= 0; + + status_error_cor_reg <= 1'b0; + status_error_uncor_reg <= 1'b0; + end +end + +// output datapath logic (PCIe TLP) +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg m_axis_rq_tvalid_reg = 1'b0, m_axis_rq_tvalid_next; +reg m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_rq_tvalid_reg = 1'b0, temp_m_axis_rq_tvalid_next; +reg temp_m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] temp_m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_rq_int_to_output; +reg store_axis_rq_int_to_temp; +reg store_axis_rq_temp_to_output; + +assign m_axis_rq_tdata = m_axis_rq_tdata_reg; +assign m_axis_rq_tkeep = m_axis_rq_tkeep_reg; +assign m_axis_rq_tvalid = m_axis_rq_tvalid_reg; +assign m_axis_rq_tlast = m_axis_rq_tlast_reg; +assign m_axis_rq_tuser = m_axis_rq_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_rq_tready_int_early = m_axis_rq_tready || (!temp_m_axis_rq_tvalid_reg && (!m_axis_rq_tvalid_reg || !m_axis_rq_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_rq_tvalid_next = m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + + store_axis_rq_int_to_output = 1'b0; + store_axis_rq_int_to_temp = 1'b0; + store_axis_rq_temp_to_output = 1'b0; + + if (m_axis_rq_tready_int_reg) begin + // input is ready + if (m_axis_rq_tready || !m_axis_rq_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_temp = 1'b1; + end + end else if (m_axis_rq_tready) begin + // input is not ready, but output is ready + m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = 1'b0; + store_axis_rq_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_rq_tvalid_reg <= 1'b0; + m_axis_rq_tready_int_reg <= 1'b0; + temp_m_axis_rq_tvalid_reg <= 1'b0; + end else begin + m_axis_rq_tvalid_reg <= m_axis_rq_tvalid_next; + m_axis_rq_tready_int_reg <= m_axis_rq_tready_int_early; + temp_m_axis_rq_tvalid_reg <= temp_m_axis_rq_tvalid_next; + end + + // datapath + if (store_axis_rq_int_to_output) begin + m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end else if (store_axis_rq_temp_to_output) begin + m_axis_rq_tdata_reg <= temp_m_axis_rq_tdata_reg; + m_axis_rq_tkeep_reg <= temp_m_axis_rq_tkeep_reg; + m_axis_rq_tlast_reg <= temp_m_axis_rq_tlast_reg; + m_axis_rq_tuser_reg <= temp_m_axis_rq_tuser_reg; + end + + if (store_axis_rq_int_to_temp) begin + temp_m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + temp_m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + temp_m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + temp_m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end +end + +// output datapath logic (AXI write data) +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; +reg m_axi_wlast_reg = 1'b0; + +reg [AXI_DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; +reg temp_m_axi_wlast_reg = 1'b0; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wvalid = m_axi_wvalid_reg; +assign m_axi_wlast = m_axi_wlast_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = m_axi_wready || (!temp_m_axi_wvalid_reg && (!m_axi_wvalid_reg || !m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (m_axi_wready || !m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axi_dma_wr.v b/corundum/lib/pcie/rtl/pcie_us_axi_dma_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..3f3adc55c1e115d88ae9cfb1177a348620359fc2 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axi_dma_wr.v @@ -0,0 +1,1216 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe AXI DMA Write + */ +module pcie_us_axi_dma_wr # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream RQ tuser signal width + parameter AXIS_PCIE_RQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 60 : 137, + // RQ sequence number width + parameter RQ_SEQ_NUM_WIDTH = AXIS_PCIE_RQ_USER_WIDTH == 60 ? 4 : 6, + // RQ sequence number tracking enable + parameter RQ_SEQ_NUM_ENABLE = 0, + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 64, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 256, + // PCIe address width + parameter PCIE_ADDR_WIDTH = 64, + // Length field width + parameter LEN_WIDTH = 20, + // Tag field width + parameter TAG_WIDTH = 8, + // Operation table size + parameter OP_TABLE_SIZE = 2**(RQ_SEQ_NUM_WIDTH-1), + // In-flight transmit limit + parameter TX_LIMIT = 2**(RQ_SEQ_NUM_WIDTH-1), + // Transmit flow control + parameter TX_FC_ENABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (RQ from read DMA) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_rq_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_rq_tkeep, + input wire s_axis_rq_tvalid, + output wire s_axis_rq_tready, + input wire s_axis_rq_tlast, + input wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] s_axis_rq_tuser, + + /* + * AXI output (RQ) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep, + output wire m_axis_rq_tvalid, + input wire m_axis_rq_tready, + output wire m_axis_rq_tlast, + output wire [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser, + + /* + * Transmit sequence number input + */ + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_0, + input wire s_axis_rq_seq_num_valid_0, + input wire [RQ_SEQ_NUM_WIDTH-1:0] s_axis_rq_seq_num_1, + input wire s_axis_rq_seq_num_valid_1, + + /* + * Transmit sequence number output (to read DMA) + */ + output wire [RQ_SEQ_NUM_WIDTH-1:0] m_axis_rq_seq_num_0, + output wire m_axis_rq_seq_num_valid_0, + output wire [RQ_SEQ_NUM_WIDTH-1:0] m_axis_rq_seq_num_1, + output wire m_axis_rq_seq_num_valid_1, + + /* + * Transmit flow control + */ + input wire [7:0] pcie_tx_fc_ph_av, + input wire [11:0] pcie_tx_fc_pd_av, + + /* + * AXI write descriptor input + */ + input wire [PCIE_ADDR_WIDTH-1:0] s_axis_write_desc_pcie_addr, + input wire [AXI_ADDR_WIDTH-1:0] s_axis_write_desc_axi_addr, + input wire [LEN_WIDTH-1:0] s_axis_write_desc_len, + input wire [TAG_WIDTH-1:0] s_axis_write_desc_tag, + input wire s_axis_write_desc_valid, + output wire s_axis_write_desc_ready, + + /* + * AXI write descriptor status output + */ + output wire [TAG_WIDTH-1:0] m_axis_write_desc_status_tag, + output wire m_axis_write_desc_status_valid, + + /* + * AXI master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready, + + /* + * Configuration + */ + input wire enable, + input wire [15:0] requester_id, + input wire requester_id_enable, + input wire [2:0] max_payload_size +); + +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXI_MAX_BURST_SIZE = AXI_MAX_BURST_LEN*AXI_WORD_WIDTH; + +parameter AXIS_PCIE_WORD_WIDTH = AXIS_PCIE_KEEP_WIDTH; +parameter AXIS_PCIE_WORD_SIZE = AXIS_PCIE_DATA_WIDTH/AXIS_PCIE_WORD_WIDTH; + +parameter OFFSET_WIDTH = $clog2(AXI_DATA_WIDTH/8); +parameter WORD_LEN_WIDTH = LEN_WIDTH - $clog2(AXIS_PCIE_KEEP_WIDTH); +parameter CYCLE_COUNT_WIDTH = 13-AXI_BURST_SIZE; + +parameter SEQ_NUM_MASK = {RQ_SEQ_NUM_WIDTH-1{1'b1}}; +parameter SEQ_NUM_FLAG = {1'b1, {RQ_SEQ_NUM_WIDTH-1{1'b0}}}; + +parameter OP_TAG_WIDTH = $clog2(OP_TABLE_SIZE); + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, 256, or 512 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + if (AXIS_PCIE_RQ_USER_WIDTH != 137) begin + $error("Error: PCIe RQ tuser width must be 137 (instance %m)"); + $finish; + end + end else begin + if (AXIS_PCIE_RQ_USER_WIDTH != 60 && AXIS_PCIE_RQ_USER_WIDTH != 62) begin + $error("Error: PCIe RQ tuser width must be 60 or 62 (instance %m)"); + $finish; + end + end + + if (AXIS_PCIE_RQ_USER_WIDTH == 60) begin + if (RQ_SEQ_NUM_ENABLE && RQ_SEQ_NUM_WIDTH != 4) begin + $error("Error: RQ sequence number width must be 4 (instance %m)"); + $finish; + end + end else begin + if (RQ_SEQ_NUM_ENABLE && RQ_SEQ_NUM_WIDTH != 6) begin + $error("Error: RQ sequence number width must be 6 (instance %m)"); + $finish; + end + end + + if (RQ_SEQ_NUM_ENABLE && OP_TABLE_SIZE > 2**(RQ_SEQ_NUM_WIDTH-1)) begin + $error("Error: Operation table size of range (instance %m)"); + $finish; + end + + if (RQ_SEQ_NUM_ENABLE && TX_LIMIT > 2**(RQ_SEQ_NUM_WIDTH-1)) begin + $error("Error: TX limit out of range (instance %m)"); + $finish; + end + + if (AXI_DATA_WIDTH != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: AXI interface width must match PCIe interface width (instance %m)"); + $finish; + end + + if (AXI_STRB_WIDTH * 8 != AXI_DATA_WIDTH) begin + $error("Error: AXI interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXI_MAX_BURST_LEN < 1 || AXI_MAX_BURST_LEN > 256) begin + $error("Error: AXI_MAX_BURST_LEN must be between 1 and 256 (instance %m)"); + $finish; + end +end + +localparam [3:0] + REQ_MEM_READ = 4'b0000, + REQ_MEM_WRITE = 4'b0001, + REQ_IO_READ = 4'b0010, + REQ_IO_WRITE = 4'b0011, + REQ_MEM_FETCH_ADD = 4'b0100, + REQ_MEM_SWAP = 4'b0101, + REQ_MEM_CAS = 4'b0110, + REQ_MEM_READ_LOCKED = 4'b0111, + REQ_CFG_READ_0 = 4'b1000, + REQ_CFG_READ_1 = 4'b1001, + REQ_CFG_WRITE_0 = 4'b1010, + REQ_CFG_WRITE_1 = 4'b1011, + REQ_MSG = 4'b1100, + REQ_MSG_VENDOR = 4'b1101, + REQ_MSG_ATS = 4'b1110; + +localparam [2:0] + CPL_STATUS_SC = 3'b000, // successful completion + CPL_STATUS_UR = 3'b001, // unsupported request + CPL_STATUS_CRS = 3'b010, // configuration request retry status + CPL_STATUS_CA = 3'b100; // completer abort + +localparam [1:0] + AXI_STATE_IDLE = 2'd0, + AXI_STATE_START = 2'd1, + AXI_STATE_REQ = 2'd2; + +reg [1:0] axi_state_reg = AXI_STATE_IDLE, axi_state_next; + +localparam [2:0] + TLP_STATE_IDLE = 3'd0, + TLP_STATE_HEADER_1 = 3'd1, + TLP_STATE_HEADER_2 = 3'd2, + TLP_STATE_TRANSFER = 3'd3, + TLP_STATE_PASSTHROUGH = 3'd4; + +reg [2:0] tlp_state_reg = TLP_STATE_IDLE, tlp_state_next; + +// datapath control signals +reg transfer_in_save; + +reg [12:0] tlp_count; +reg [10:0] dword_count; +reg last_tlp; +reg [PCIE_ADDR_WIDTH-1:0] pcie_addr; + +reg [12:0] tr_count; +reg last_tr; +reg [AXI_ADDR_WIDTH-1:0] axi_addr; + +reg [PCIE_ADDR_WIDTH-1:0] pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, pcie_addr_next; +reg [AXI_ADDR_WIDTH-1:0] axi_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, axi_addr_next; +reg [LEN_WIDTH-1:0] op_count_reg = {LEN_WIDTH{1'b0}}, op_count_next; +reg [LEN_WIDTH-1:0] tr_count_reg = {LEN_WIDTH{1'b0}}, tr_count_next; +reg [12:0] tlp_count_reg = 13'd0, tlp_count_next; + +reg [PCIE_ADDR_WIDTH-1:0] tlp_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, tlp_addr_next; +reg [11:0] tlp_len_reg = 12'd0, tlp_len_next; +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; +reg [9:0] dword_count_reg = 10'd0, dword_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] input_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, input_cycle_count_next; +reg [CYCLE_COUNT_WIDTH-1:0] output_cycle_count_reg = {CYCLE_COUNT_WIDTH{1'b0}}, output_cycle_count_next; +reg input_active_reg = 1'b0, input_active_next; +reg bubble_cycle_reg = 1'b0, bubble_cycle_next; +reg last_cycle_reg = 1'b0, last_cycle_next; + +reg [TAG_WIDTH-1:0] tlp_cmd_tag_reg = {TAG_WIDTH{1'b0}}, tlp_cmd_tag_next; +reg tlp_cmd_last_reg = 1'b0, tlp_cmd_last_next; + +reg [10:0] max_payload_size_dw_reg = 11'd0; + +reg have_credit_reg = 1'b0; + +reg [RQ_SEQ_NUM_WIDTH-1:0] active_tx_count_reg = {RQ_SEQ_NUM_WIDTH{1'b0}}; +reg active_tx_count_av_reg = 1'b1; +reg inc_active_tx; + +reg s_axis_rq_tready_reg = 1'b0, s_axis_rq_tready_next; + +reg s_axis_write_desc_ready_reg = 1'b0, s_axis_write_desc_ready_next; + +reg [TAG_WIDTH-1:0] m_axis_write_desc_status_tag_reg = {TAG_WIDTH{1'b0}}, m_axis_write_desc_status_tag_next; +reg m_axis_write_desc_status_valid_reg = 1'b0, m_axis_write_desc_status_valid_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axi_araddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_araddr_next; +reg [7:0] m_axi_arlen_reg = 8'd0, m_axi_arlen_next; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; +reg m_axi_rready_reg = 1'b0, m_axi_rready_next; + +reg [AXI_DATA_WIDTH-1:0] save_axi_rdata_reg = {AXI_DATA_WIDTH{1'b0}}; + +wire [AXI_DATA_WIDTH-1:0] shift_axi_rdata = {m_axi_rdata, save_axi_rdata_reg} >> ((AXI_STRB_WIDTH-offset_reg)*AXI_WORD_SIZE); + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_int; +reg m_axis_rq_tvalid_int; +reg m_axis_rq_tready_int_reg = 1'b0; +reg m_axis_rq_tlast_int; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_int; +wire m_axis_rq_tready_int_early; + +assign s_axis_rq_tready = s_axis_rq_tready_reg; + +assign m_axis_rq_seq_num_0 = s_axis_rq_seq_num_0 & SEQ_NUM_MASK; +assign m_axis_rq_seq_num_valid_0 = s_axis_rq_seq_num_valid_0 && (s_axis_rq_seq_num_0 & SEQ_NUM_FLAG); +assign m_axis_rq_seq_num_1 = s_axis_rq_seq_num_1 & SEQ_NUM_MASK; +assign m_axis_rq_seq_num_valid_1 = s_axis_rq_seq_num_valid_1 && (s_axis_rq_seq_num_1 & SEQ_NUM_FLAG); + +wire axis_rq_seq_num_valid_0_int = s_axis_rq_seq_num_valid_0 && !(s_axis_rq_seq_num_0 & SEQ_NUM_FLAG); +wire axis_rq_seq_num_valid_1_int = s_axis_rq_seq_num_valid_1 && !(s_axis_rq_seq_num_1 & SEQ_NUM_FLAG); + +assign s_axis_write_desc_ready = s_axis_write_desc_ready_reg; + +assign m_axis_write_desc_status_tag = m_axis_write_desc_status_tag_reg; +assign m_axis_write_desc_status_valid = m_axis_write_desc_status_valid_reg; + +assign m_axi_arid = {AXI_ID_WIDTH{1'b0}}; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = AXI_BURST_SIZE; +assign m_axi_arburst = 2'b01; +assign m_axi_arlock = 1'b0; +assign m_axi_arcache = 4'b0011; +assign m_axi_arprot = 3'b010; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +wire [PCIE_ADDR_WIDTH-1:0] pcie_addr_plus_max_payload = pcie_addr_reg + {max_payload_size_dw_reg, 2'b00}; +wire [PCIE_ADDR_WIDTH-1:0] pcie_addr_plus_op_count = pcie_addr_reg + op_count_reg; +wire [PCIE_ADDR_WIDTH-1:0] pcie_addr_plus_tlp_count = pcie_addr_reg + tlp_count_reg; + +wire [AXI_ADDR_WIDTH-1:0] axi_addr_plus_max_burst = axi_addr_reg + AXI_MAX_BURST_SIZE; +wire [AXI_ADDR_WIDTH-1:0] axi_addr_plus_op_count = axi_addr_reg + op_count_reg; +wire [AXI_ADDR_WIDTH-1:0] axi_addr_plus_tlp_count = axi_addr_reg + tlp_count_reg; + +// operation tag management +reg [OP_TAG_WIDTH+1-1:0] op_table_start_ptr_reg = 0; +reg [PCIE_ADDR_WIDTH-1:0] op_table_start_pcie_addr; +reg [11:0] op_table_start_len; +reg [9:0] op_table_start_dword_len; +reg [CYCLE_COUNT_WIDTH-1:0] op_table_start_input_cycle_count; +reg [CYCLE_COUNT_WIDTH-1:0] op_table_start_output_cycle_count; +reg [OFFSET_WIDTH-1:0] op_table_start_offset; +reg op_table_start_bubble_cycle; +reg [TAG_WIDTH-1:0] op_table_start_tag; +reg op_table_start_last; +reg op_table_start_en; +reg [OP_TAG_WIDTH+1-1:0] op_table_tx_start_ptr_reg = 0; +reg op_table_tx_start_en; +reg [OP_TAG_WIDTH+1-1:0] op_table_tx_finish_ptr_reg = 0; +reg op_table_tx_finish_en; +reg [OP_TAG_WIDTH+1-1:0] op_table_finish_ptr_reg = 0; +reg op_table_finish_en; + +reg [2**OP_TAG_WIDTH-1:0] op_table_active = 0; +reg [2**OP_TAG_WIDTH-1:0] op_table_tx_done = 0; +reg [PCIE_ADDR_WIDTH-1:0] op_table_pcie_addr[2**OP_TAG_WIDTH-1:0]; +reg [11:0] op_table_len[2**OP_TAG_WIDTH-1:0]; +reg [9:0] op_table_dword_len[2**OP_TAG_WIDTH-1:0]; +reg [CYCLE_COUNT_WIDTH-1:0] op_table_input_cycle_count[2**OP_TAG_WIDTH-1:0]; +reg [CYCLE_COUNT_WIDTH-1:0] op_table_output_cycle_count[2**OP_TAG_WIDTH-1:0]; +reg [OFFSET_WIDTH-1:0] op_table_offset[2**OP_TAG_WIDTH-1:0]; +reg op_table_bubble_cycle[2**OP_TAG_WIDTH-1:0]; +reg [TAG_WIDTH-1:0] op_table_tag[2**OP_TAG_WIDTH-1:0]; +reg op_table_last[2**OP_TAG_WIDTH-1:0]; + +integer i; + +initial begin + for (i = 0; i < 2**OP_TAG_WIDTH; i = i + 1) begin + op_table_pcie_addr[i] = 0; + op_table_len[i] = 0; + op_table_dword_len[i] = 0; + op_table_input_cycle_count[i] = 0; + op_table_output_cycle_count[i] = 0; + op_table_offset[i] = 0; + op_table_tag[i] = 0; + op_table_bubble_cycle[i] = 0; + op_table_last[i] = 0; + end +end + +always @* begin + axi_state_next = AXI_STATE_IDLE; + + s_axis_write_desc_ready_next = 1'b0; + + m_axi_araddr_next = m_axi_araddr_reg; + m_axi_arlen_next = m_axi_arlen_reg; + m_axi_arvalid_next = m_axi_arvalid_reg && !m_axi_arready; + + pcie_addr_next = pcie_addr_reg; + axi_addr_next = axi_addr_reg; + op_count_next = op_count_reg; + tr_count_next = tr_count_reg; + tlp_count_next = tlp_count_reg; + + tlp_cmd_tag_next = tlp_cmd_tag_reg; + tlp_cmd_last_next = tlp_cmd_last_reg; + + // TLP size computation + if (op_count_reg <= {max_payload_size_dw_reg, 2'b00}-pcie_addr_reg[1:0]) begin + // packet smaller than max read request size + if (pcie_addr_reg[12] != pcie_addr_plus_op_count[12]) begin + // crosses 4k boundary + tlp_count = 13'h1000 - pcie_addr_reg[11:0]; + dword_count = 11'h400 - pcie_addr_reg[11:2]; + last_tlp = pcie_addr_plus_op_count[11:0] == 0; + // optimized pcie_addr = pcie_addr_reg + tlp_count + pcie_addr[PCIE_ADDR_WIDTH-1:12] = pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]+1; + pcie_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + tlp_count = op_count_reg; + dword_count = (op_count_reg + pcie_addr_reg[1:0] + 3) >> 2; + last_tlp = 1'b1; + // optimized pcie_addr = pcie_addr_reg + tlp_count + pcie_addr[PCIE_ADDR_WIDTH-1:12] = pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]; + pcie_addr[11:0] = pcie_addr_reg[11:0] + op_count_reg; + end + end else begin + // packet larger than max read request size + if (pcie_addr_reg[12] != pcie_addr_plus_max_payload[12]) begin + // crosses 4k boundary + tlp_count = 13'h1000 - pcie_addr_reg[11:0]; + dword_count = 11'h400 - pcie_addr_reg[11:2]; + last_tlp = 1'b0; + // optimized pcie_addr = pcie_addr_reg + tlp_count + pcie_addr[PCIE_ADDR_WIDTH-1:12] = pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]+1; + pcie_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + tlp_count = {max_payload_size_dw_reg, 2'b00}-pcie_addr_reg[1:0]; + dword_count = max_payload_size_dw_reg; + last_tlp = 1'b0; + // optimized pcie_addr = pcie_addr_reg + tlp_count + pcie_addr[PCIE_ADDR_WIDTH-1:12] = pcie_addr_reg[PCIE_ADDR_WIDTH-1:12]; + pcie_addr[11:0] = {pcie_addr_reg[11:2] + max_payload_size_dw_reg, 2'b00}; + end + end + + // AXI transfer size computation + if (tlp_count_reg <= AXI_MAX_BURST_SIZE-axi_addr_reg[OFFSET_WIDTH-1:0] || AXI_MAX_BURST_SIZE >= 4096) begin + // packet smaller than max read request size + if (axi_addr_reg[12] != axi_addr_plus_tlp_count[12]) begin + // crosses 4k boundary + tr_count = 13'h1000 - axi_addr_reg[11:0]; + last_tr = axi_addr_plus_tlp_count[11:0] == 0; + // optimized axi_addr = axi_addr_reg + tr_count + axi_addr[AXI_ADDR_WIDTH-1:12] = axi_addr_reg[AXI_ADDR_WIDTH-1:12]+1; + axi_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + tr_count = tlp_count_reg; + last_tr = 1'b1; + // optimized axi_addr = axi_addr_reg + tr_count + axi_addr[AXI_ADDR_WIDTH-1:12] = axi_addr_reg[AXI_ADDR_WIDTH-1:12]; + axi_addr[11:0] = axi_addr_reg[11:0] + tlp_count_reg; + end + end else begin + // packet larger than max read request size + if (axi_addr_reg[12] != axi_addr_plus_max_burst[12]) begin + // crosses 4k boundary + tr_count = 13'h1000 - axi_addr_reg[11:0]; + last_tr = 1'b0; + // optimized axi_addr = axi_addr_reg + tr_count + axi_addr[AXI_ADDR_WIDTH-1:12] = axi_addr_reg[AXI_ADDR_WIDTH-1:12]+1; + axi_addr[11:0] = 12'd0; + end else begin + // does not cross 4k boundary, send one TLP + tr_count = AXI_MAX_BURST_SIZE-axi_addr_reg[1:0]; + last_tr = 1'b0; + // optimized axi_addr = axi_addr_reg + tr_count + axi_addr[AXI_ADDR_WIDTH-1:12] = axi_addr_reg[AXI_ADDR_WIDTH-1:12]; + axi_addr[11:0] = {axi_addr_reg[11:2], 2'b00} + AXI_MAX_BURST_SIZE; + end + end + + op_table_start_pcie_addr = pcie_addr_reg; + op_table_start_len = tlp_count; + op_table_start_dword_len = dword_count; + op_table_start_input_cycle_count = (tlp_count + axi_addr_reg[OFFSET_WIDTH-1:0] - 1) >> AXI_BURST_SIZE; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + op_table_start_output_cycle_count = (tlp_count + 16+pcie_addr_reg[1:0] - 1) >> AXI_BURST_SIZE; + end else begin + op_table_start_output_cycle_count = (tlp_count + pcie_addr_reg[1:0] - 1) >> AXI_BURST_SIZE; + end + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + op_table_start_offset = 16+pcie_addr_reg[1:0]-axi_addr_reg[OFFSET_WIDTH-1:0]; + op_table_start_bubble_cycle = axi_addr_reg[OFFSET_WIDTH-1:0] > 16+pcie_addr_reg[1:0]; + end else begin + op_table_start_offset = pcie_addr_reg[1:0]-axi_addr_reg[OFFSET_WIDTH-1:0]; + op_table_start_bubble_cycle = axi_addr_reg[OFFSET_WIDTH-1:0] > pcie_addr_reg[1:0]; + end + op_table_start_tag = tlp_cmd_tag_reg; + op_table_start_last = last_tlp; + op_table_start_en = 1'b0; + + // TLP segmentation and AXI read request generation + case (axi_state_reg) + AXI_STATE_IDLE: begin + // idle state, wait for incoming descriptor + s_axis_write_desc_ready_next = !op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] && ($unsigned(op_table_start_ptr_reg - op_table_finish_ptr_reg) < 2**OP_TAG_WIDTH) && enable; + + pcie_addr_next = s_axis_write_desc_pcie_addr; + axi_addr_next = s_axis_write_desc_axi_addr; + op_count_next = s_axis_write_desc_len; + + if (s_axis_write_desc_ready & s_axis_write_desc_valid) begin + s_axis_write_desc_ready_next = 1'b0; + tlp_cmd_tag_next = s_axis_write_desc_tag; + axi_state_next = AXI_STATE_START; + end else begin + axi_state_next = AXI_STATE_IDLE; + end + end + AXI_STATE_START: begin + // start state, compute TLP length + tlp_count_next = tlp_count; + + op_table_start_pcie_addr = pcie_addr_reg; + op_table_start_len = tlp_count; + op_table_start_dword_len = dword_count; + op_table_start_input_cycle_count = (tlp_count + axi_addr_reg[OFFSET_WIDTH-1:0] - 1) >> AXI_BURST_SIZE; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + op_table_start_output_cycle_count = (tlp_count + 16+pcie_addr_reg[1:0] - 1) >> AXI_BURST_SIZE; + end else begin + op_table_start_output_cycle_count = (tlp_count + pcie_addr_reg[1:0] - 1) >> AXI_BURST_SIZE; + end + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + op_table_start_offset = 16+pcie_addr_reg[1:0]-axi_addr_reg[OFFSET_WIDTH-1:0]; + op_table_start_bubble_cycle = axi_addr_reg[OFFSET_WIDTH-1:0] > 16+pcie_addr_reg[1:0]; + end else begin + op_table_start_offset = pcie_addr_reg[1:0]-axi_addr_reg[OFFSET_WIDTH-1:0]; + op_table_start_bubble_cycle = axi_addr_reg[OFFSET_WIDTH-1:0] > pcie_addr_reg[1:0]; + end + op_table_start_tag = tlp_cmd_tag_reg; + op_table_start_last = last_tlp; + + if (!op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] && ($unsigned(op_table_start_ptr_reg - op_table_finish_ptr_reg) < 2**OP_TAG_WIDTH)) begin + pcie_addr_next = pcie_addr; + op_count_next = op_count_reg - tlp_count_next; + + tlp_cmd_last_next = last_tlp; + + op_table_start_en = 1'b1; + + axi_state_next = AXI_STATE_REQ; + end else begin + axi_state_next = AXI_STATE_START; + end + end + AXI_STATE_REQ: begin + // request state, generate AXI read requests + if (!m_axi_arvalid) begin + tr_count_next = tr_count; + + m_axi_araddr_next = axi_addr_reg; + m_axi_arlen_next = (tr_count_next + axi_addr_reg[OFFSET_WIDTH-1:0] - 1) >> AXI_BURST_SIZE; + m_axi_arvalid_next = 1; + + axi_addr_next = axi_addr; + tlp_count_next = tlp_count_reg - tr_count_next; + + if (!last_tr) begin + axi_state_next = AXI_STATE_REQ; + end else if (!tlp_cmd_last_reg) begin + axi_state_next = AXI_STATE_START; + end else begin + s_axis_write_desc_ready_next = !op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] && ($unsigned(op_table_start_ptr_reg - op_table_finish_ptr_reg) < 2**OP_TAG_WIDTH) && enable; + axi_state_next = AXI_STATE_IDLE; + end + end else begin + axi_state_next = AXI_STATE_REQ; + end + end + endcase +end + +wire [3:0] first_be = 4'b1111 << tlp_addr_reg[1:0]; +wire [3:0] last_be = 4'b1111 >> (3 - ((tlp_addr_reg[1:0] + tlp_len_reg[1:0] - 1) & 3)); + +always @* begin + tlp_state_next = TLP_STATE_IDLE; + + transfer_in_save = 1'b0; + + s_axis_rq_tready_next = 1'b0; + + m_axis_write_desc_status_tag_next = m_axis_write_desc_status_tag_reg; + m_axis_write_desc_status_valid_next = 1'b0; + + m_axi_rready_next = 1'b0; + + tlp_addr_next = tlp_addr_reg; + tlp_len_next = tlp_len_reg; + dword_count_next = dword_count_reg; + offset_next = offset_reg; + input_cycle_count_next = input_cycle_count_reg; + output_cycle_count_next = output_cycle_count_reg; + input_active_next = input_active_reg; + bubble_cycle_next = bubble_cycle_reg; + last_cycle_next = last_cycle_reg; + + op_table_tx_start_en = 1'b0; + op_table_tx_finish_en = 1'b0; + + inc_active_tx = 1'b0; + + m_axis_rq_tdata_int = {AXIS_PCIE_DATA_WIDTH{1'b0}}; + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; + m_axis_rq_tvalid_int = 1'b0; + m_axis_rq_tlast_int = 1'b0; + m_axis_rq_tuser_int = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + + m_axis_rq_tdata_int[1:0] = 2'b0; // address type + m_axis_rq_tdata_int[63:2] = tlp_addr_reg[PCIE_ADDR_WIDTH-1:2]; // address + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_rq_tdata_int[74:64] = dword_count_reg; // DWORD count + m_axis_rq_tdata_int[78:75] = REQ_MEM_WRITE; // request type - memory write + m_axis_rq_tdata_int[79] = 1'b0; // poisoned request + m_axis_rq_tdata_int[95:80] = requester_id; + m_axis_rq_tdata_int[103:96] = 8'd0; // tag + m_axis_rq_tdata_int[119:104] = 16'd0; // completer ID + m_axis_rq_tdata_int[120] = requester_id_enable; // requester ID enable + m_axis_rq_tdata_int[123:121] = 3'b000; // traffic class + m_axis_rq_tdata_int[126:124] = 3'b000; // attr + m_axis_rq_tdata_int[127] = 1'b0; // force ECRC + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tkeep_int = 16'b0000000000001111; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_rq_tkeep_int = 8'b00001111; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_rq_tkeep_int = 4'b1111; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_rq_tkeep_int = 2'b11; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[3:0] = dword_count_reg == 1 ? first_be & last_be : first_be; // first BE 0 + m_axis_rq_tuser_int[7:4] = 4'd0; // first BE 1 + m_axis_rq_tuser_int[11:8] = dword_count_reg == 1 ? 4'b0000 : last_be; // last BE 0 + m_axis_rq_tuser_int[15:12] = 4'd0; // last BE 1 + m_axis_rq_tuser_int[19:16] = 3'd0; // addr_offset + m_axis_rq_tuser_int[21:20] = 2'b01; // is_sop + m_axis_rq_tuser_int[23:22] = 2'd0; // is_sop0_ptr + m_axis_rq_tuser_int[25:24] = 2'd0; // is_sop1_ptr + m_axis_rq_tuser_int[27:26] = 2'b01; // is_eop + m_axis_rq_tuser_int[31:28] = 4'd3; // is_eop0_ptr + m_axis_rq_tuser_int[35:32] = 4'd0; // is_eop1_ptr + m_axis_rq_tuser_int[36] = 1'b0; // discontinue + m_axis_rq_tuser_int[38:37] = 2'b00; // tph_present + m_axis_rq_tuser_int[42:39] = 4'b0000; // tph_type + m_axis_rq_tuser_int[44:43] = 2'b00; // tph_indirect_tag_en + m_axis_rq_tuser_int[60:45] = 16'd0; // tph_st_tag + m_axis_rq_tuser_int[66:61] = op_table_tx_finish_ptr_reg[OP_TAG_WIDTH-1:0] & SEQ_NUM_MASK; // seq_num0 + m_axis_rq_tuser_int[72:67] = 6'd0; // seq_num1 + m_axis_rq_tuser_int[136:73] = 64'd0; // parity + end else begin + m_axis_rq_tuser_int[3:0] = dword_count_reg == 1 ? first_be & last_be : first_be; // first BE + m_axis_rq_tuser_int[7:4] = dword_count_reg == 1 ? 4'b0000 : last_be; // last BE + m_axis_rq_tuser_int[10:8] = 3'd0; // addr_offset + m_axis_rq_tuser_int[11] = 1'b0; // discontinue + m_axis_rq_tuser_int[12] = 1'b0; // tph_present + m_axis_rq_tuser_int[14:13] = 2'b00; // tph_type + m_axis_rq_tuser_int[15] = 1'b0; // tph_indirect_tag_en + m_axis_rq_tuser_int[23:16] = 8'd0; // tph_st_tag + m_axis_rq_tuser_int[27:24] = op_table_tx_finish_ptr_reg[OP_TAG_WIDTH-1:0] & SEQ_NUM_MASK; // seq_num + m_axis_rq_tuser_int[59:28] = 32'd0; // parity + if (AXIS_PCIE_RQ_USER_WIDTH == 62) begin + m_axis_rq_tuser_int[61:60] = (op_table_tx_finish_ptr_reg[OP_TAG_WIDTH-1:0] & SEQ_NUM_MASK) >> 4; // seq_num + end + end + + // AXI read response processing and TLP generation + case (tlp_state_reg) + TLP_STATE_IDLE: begin + // idle state, wait for command + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + + // pass through read request TLP + m_axis_rq_tdata_int = s_axis_rq_tdata; + m_axis_rq_tkeep_int = s_axis_rq_tkeep; + m_axis_rq_tvalid_int = s_axis_rq_tready && s_axis_rq_tvalid; + m_axis_rq_tlast_int = s_axis_rq_tlast; + m_axis_rq_tuser_int = s_axis_rq_tuser; + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[61+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end else begin + if (RQ_SEQ_NUM_WIDTH > 4) begin + m_axis_rq_tuser_int[60+RQ_SEQ_NUM_WIDTH-4-1] = 1'b1; + end else begin + m_axis_rq_tuser_int[24+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end + end + + m_axi_rready_next = 1'b0; + + tlp_addr_next = op_table_pcie_addr[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + tlp_len_next = op_table_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + dword_count_next = op_table_dword_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + offset_next = op_table_offset[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + input_cycle_count_next = op_table_input_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + output_cycle_count_next = op_table_output_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + input_active_next = 1'b1; + bubble_cycle_next = op_table_bubble_cycle[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + last_cycle_next = op_table_output_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] == 0; + + if (s_axis_rq_tready && s_axis_rq_tvalid) begin + // pass through read request TLP + if (s_axis_rq_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_PASSTHROUGH; + end + end else if (op_table_active[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_tx_start_ptr_reg != op_table_start_ptr_reg && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + s_axis_rq_tready_next = 1'b0; + op_table_tx_start_en = 1'b1; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + m_axi_rready_next = m_axis_rq_tready_int_early; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axi_rready_next = m_axis_rq_tready_int_early && bubble_cycle_next; + end else begin + m_axi_rready_next = 1'b0; + end + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + tlp_state_next = TLP_STATE_IDLE; + end + end + TLP_STATE_HEADER_1: begin + // header 1 state, send TLP header + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_reg; + + if (m_axis_rq_tready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + transfer_in_save = m_axi_rready && m_axi_rvalid; + + if (bubble_cycle_reg) begin + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg != 0; + end + bubble_cycle_next = 1'b0; + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + dword_count_next = dword_count_reg - (AXIS_PCIE_KEEP_WIDTH-4); + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg != 0; + end + output_cycle_count_next = output_cycle_count_reg - 1; + last_cycle_next = output_cycle_count_next == 0; + + m_axis_rq_tdata_int[AXIS_PCIE_DATA_WIDTH-1:128] = shift_axi_rdata[AXIS_PCIE_DATA_WIDTH-1:128]; + m_axis_rq_tvalid_int = 1'b1; + if (dword_count_reg >= AXIS_PCIE_KEEP_WIDTH-4) begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}}; + end else begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}} >> (AXIS_PCIE_KEEP_WIDTH-4 - dword_count_reg); + end + + inc_active_tx = 1'b1; + + if (last_cycle_reg) begin + m_axis_rq_tlast_int = 1'b1; + op_table_tx_finish_en = 1'b1; + + // skip idle state if possible + tlp_addr_next = op_table_pcie_addr[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + tlp_len_next = op_table_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + dword_count_next = op_table_dword_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + offset_next = op_table_offset[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + input_cycle_count_next = op_table_input_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + output_cycle_count_next = op_table_output_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + input_active_next = 1'b1; + bubble_cycle_next = op_table_bubble_cycle[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + last_cycle_next = op_table_output_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] == 0; + + if (op_table_active[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_tx_start_ptr_reg != op_table_start_ptr_reg && !s_axis_rq_tvalid && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + op_table_tx_start_en = 1'b1; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + m_axi_rready_next = m_axis_rq_tready_int_early; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axi_rready_next = m_axis_rq_tready_int_early && bubble_cycle_next; + end else begin + m_axi_rready_next = 1'b0; + end + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + m_axi_rready_next = 0; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_TRANSFER; + end + end + end else begin + tlp_state_next = TLP_STATE_HEADER_1; + end + end else begin + if (m_axis_rq_tready_int_reg) begin + m_axis_rq_tvalid_int = 1'b1; + + inc_active_tx = 1'b1; + + if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axi_rready_next = m_axis_rq_tready_int_early; + if ((m_axi_rready && m_axi_rvalid) && bubble_cycle_reg) begin + transfer_in_save = 1'b1; + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg != 0; + end + bubble_cycle_next = 1'b0; + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_next; + end + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + m_axi_rready_next = m_axis_rq_tready_int_early && bubble_cycle_reg; + tlp_state_next = TLP_STATE_HEADER_2; + end + end else begin + tlp_state_next = TLP_STATE_HEADER_1; + end + end + end + TLP_STATE_HEADER_2: begin + // header 2 state, send rest of TLP header (64 bit interface only) + if (m_axis_rq_tready_int_reg) begin + m_axis_rq_tdata_int[10:0] = dword_count_reg; // DWORD count + m_axis_rq_tdata_int[14:11] = 4'b0001; // request type - memory write + m_axis_rq_tdata_int[15] = 1'b0; // poisoned request + m_axis_rq_tdata_int[31:16] = requester_id; + m_axis_rq_tdata_int[39:32] = 8'd0; // tag + m_axis_rq_tdata_int[55:40] = 16'd0; // completer ID + m_axis_rq_tdata_int[56] = requester_id_enable; // requester ID enable + m_axis_rq_tdata_int[59:57] = 3'b000; // traffic class + m_axis_rq_tdata_int[62:60] = 3'b000; // attr + m_axis_rq_tdata_int[63] = 1'b0; // force ECRC + m_axis_rq_tvalid_int = 1'b1; + m_axis_rq_tkeep_int = 2'b11; + + m_axi_rready_next = m_axis_rq_tready_int_early; + if ((m_axi_rready && m_axi_rvalid) && bubble_cycle_reg) begin + transfer_in_save = 1'b1; + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg != 0; + end + bubble_cycle_next = 1'b0; + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_next; + end + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + tlp_state_next = TLP_STATE_HEADER_2; + end + end + TLP_STATE_TRANSFER: begin + // transfer state, transfer data + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_reg; + + if (m_axis_rq_tready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + transfer_in_save = 1'b1; + + if (bubble_cycle_reg) begin + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg != 0; + end + bubble_cycle_next = 1'b0; + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + dword_count_next = dword_count_reg - AXIS_PCIE_KEEP_WIDTH; + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg != 0; + end + output_cycle_count_next = output_cycle_count_reg - 1; + last_cycle_next = output_cycle_count_next == 0; + + m_axis_rq_tdata_int = shift_axi_rdata; + m_axis_rq_tvalid_int = 1'b1; + if (dword_count_reg >= AXIS_PCIE_KEEP_WIDTH) begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}}; + end else begin + m_axis_rq_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}} >> (AXIS_PCIE_KEEP_WIDTH - dword_count_reg); + end + + if (last_cycle_reg) begin + m_axis_rq_tlast_int = 1'b1; + op_table_tx_finish_en = 1'b1; + + // skip idle state if possible + tlp_addr_next = op_table_pcie_addr[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + tlp_len_next = op_table_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + dword_count_next = op_table_dword_len[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + offset_next = op_table_offset[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + input_cycle_count_next = op_table_input_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + output_cycle_count_next = op_table_output_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + input_active_next = 1'b1; + bubble_cycle_next = op_table_bubble_cycle[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]]; + last_cycle_next = op_table_output_cycle_count[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] == 0; + + if (op_table_active[op_table_tx_start_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_tx_start_ptr_reg != op_table_start_ptr_reg && !s_axis_rq_tvalid && (!TX_FC_ENABLE || have_credit_reg) && (!RQ_SEQ_NUM_ENABLE || active_tx_count_av_reg)) begin + op_table_tx_start_en = 1'b1; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + m_axi_rready_next = m_axis_rq_tready_int_early; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axi_rready_next = m_axis_rq_tready_int_early && bubble_cycle_next; + end else begin + m_axi_rready_next = 1'b0; + end + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + m_axi_rready_next = 0; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + m_axi_rready_next = m_axis_rq_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_TRANSFER; + end + end + end else begin + tlp_state_next = TLP_STATE_TRANSFER; + end + end + TLP_STATE_PASSTHROUGH: begin + // passthrough state, pass through read request TLP + s_axis_rq_tready_next = m_axis_rq_tready_int_early; + + // pass through read request TLP + m_axis_rq_tdata_int = s_axis_rq_tdata; + m_axis_rq_tkeep_int = s_axis_rq_tkeep; + m_axis_rq_tvalid_int = s_axis_rq_tready && s_axis_rq_tvalid; + m_axis_rq_tlast_int = s_axis_rq_tlast; + m_axis_rq_tuser_int = s_axis_rq_tuser; + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_rq_tuser_int[61+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end else begin + if (RQ_SEQ_NUM_WIDTH > 4) begin + m_axis_rq_tuser_int[60+RQ_SEQ_NUM_WIDTH-4-1] = 1'b1; + end else begin + m_axis_rq_tuser_int[24+RQ_SEQ_NUM_WIDTH-1] = 1'b1; + end + end + + if (s_axis_rq_tready && s_axis_rq_tvalid && s_axis_rq_tlast) begin + tlp_state_next = TLP_STATE_IDLE; + end else begin + tlp_state_next = TLP_STATE_PASSTHROUGH; + end + end + endcase + + op_table_finish_en = 1'b0; + + if (op_table_active[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_tx_done[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]] && op_table_finish_ptr_reg != op_table_start_ptr_reg) begin + op_table_finish_en = 1'b1; + + if (op_table_last[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]]) begin + m_axis_write_desc_status_tag_next = op_table_tag[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]]; + m_axis_write_desc_status_valid_next = 1'b1; + end + end +end + +always @(posedge clk) begin + axi_state_reg <= axi_state_next; + tlp_state_reg <= tlp_state_next; + + pcie_addr_reg <= pcie_addr_next; + axi_addr_reg <= axi_addr_next; + op_count_reg <= op_count_next; + tr_count_reg <= tr_count_next; + tlp_count_reg <= tlp_count_next; + + tlp_addr_reg <= tlp_addr_next; + tlp_len_reg <= tlp_len_next; + dword_count_reg <= dword_count_next; + offset_reg <= offset_next; + input_cycle_count_reg <= input_cycle_count_next; + output_cycle_count_reg <= output_cycle_count_next; + input_active_reg <= input_active_next; + bubble_cycle_reg <= bubble_cycle_next; + last_cycle_reg <= last_cycle_next; + + tlp_cmd_tag_reg <= tlp_cmd_tag_next; + tlp_cmd_last_reg <= tlp_cmd_last_next; + + s_axis_rq_tready_reg <= s_axis_rq_tready_next; + + s_axis_write_desc_ready_reg <= s_axis_write_desc_ready_next; + + m_axis_write_desc_status_tag_reg <= m_axis_write_desc_status_tag_next; + m_axis_write_desc_status_valid_reg <= m_axis_write_desc_status_valid_next; + + m_axi_araddr_reg <= m_axi_araddr_next; + m_axi_arlen_reg <= m_axi_arlen_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + + max_payload_size_dw_reg <= 11'd32 << (max_payload_size > 5 ? 5 : max_payload_size); + + have_credit_reg <= (pcie_tx_fc_ph_av > 4) && (pcie_tx_fc_pd_av > (max_payload_size_dw_reg >> 1)); + + if (active_tx_count_reg < TX_LIMIT && inc_active_tx && !axis_rq_seq_num_valid_0_int && !axis_rq_seq_num_valid_1_int) begin + // inc by 1 + active_tx_count_reg <= active_tx_count_reg + 1; + active_tx_count_av_reg <= active_tx_count_reg < (TX_LIMIT-1); + end else if (active_tx_count_reg > 0 && ((inc_active_tx && axis_rq_seq_num_valid_0_int && axis_rq_seq_num_valid_1_int) || (!inc_active_tx && (axis_rq_seq_num_valid_0_int ^ axis_rq_seq_num_valid_1_int)))) begin + // dec by 1 + active_tx_count_reg <= active_tx_count_reg - 1; + active_tx_count_av_reg <= 1'b1; + end else if (active_tx_count_reg > 1 && !inc_active_tx && axis_rq_seq_num_valid_0_int && axis_rq_seq_num_valid_1_int) begin + // dec by 2 + active_tx_count_reg <= active_tx_count_reg - 2; + active_tx_count_av_reg <= 1'b1; + end else begin + active_tx_count_av_reg <= active_tx_count_reg < TX_LIMIT; + end + + if (op_table_start_en) begin + op_table_start_ptr_reg <= op_table_start_ptr_reg + 1; + op_table_active[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= 1'b1; + op_table_tx_done[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= 1'b0; + op_table_pcie_addr[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_pcie_addr; + op_table_len[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_len; + op_table_dword_len[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_dword_len; + op_table_input_cycle_count[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_input_cycle_count; + op_table_output_cycle_count[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_output_cycle_count; + op_table_offset[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_offset; + op_table_bubble_cycle[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_bubble_cycle; + op_table_tag[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_tag; + op_table_last[op_table_start_ptr_reg[OP_TAG_WIDTH-1:0]] <= op_table_start_last; + end + + if (op_table_tx_start_en) begin + op_table_tx_start_ptr_reg <= op_table_tx_start_ptr_reg + 1; + end + + if (op_table_tx_finish_en) begin + op_table_tx_finish_ptr_reg <= op_table_tx_finish_ptr_reg + 1; + end + + if (axis_rq_seq_num_valid_0_int) begin + op_table_tx_done[s_axis_rq_seq_num_0[OP_TAG_WIDTH-1:0]] <= 1'b1; + end + + if (axis_rq_seq_num_valid_1_int) begin + op_table_tx_done[s_axis_rq_seq_num_1[OP_TAG_WIDTH-1:0]] <= 1'b1; + end + + if (op_table_finish_en) begin + op_table_finish_ptr_reg <= op_table_finish_ptr_reg + 1; + op_table_active[op_table_finish_ptr_reg[OP_TAG_WIDTH-1:0]] <= 1'b0; + end + + if (transfer_in_save) begin + save_axi_rdata_reg <= m_axi_rdata; + end + + if (rst) begin + axi_state_reg <= AXI_STATE_IDLE; + tlp_state_reg <= TLP_STATE_IDLE; + + s_axis_rq_tready_reg <= 1'b0; + s_axis_write_desc_ready_reg <= 1'b0; + m_axis_write_desc_status_valid_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + m_axi_rready_reg <= 1'b0; + + active_tx_count_reg <= {RQ_SEQ_NUM_WIDTH{1'b0}}; + active_tx_count_av_reg <= 1'b1; + + op_table_start_ptr_reg <= 0; + op_table_tx_start_ptr_reg <= 0; + op_table_tx_finish_ptr_reg <= 0; + op_table_finish_ptr_reg <= 0; + op_table_active <= 0; + end +end + +// output datapath logic (PCIe TLP) +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg m_axis_rq_tvalid_reg = 1'b0, m_axis_rq_tvalid_next; +reg m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_rq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_rq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_rq_tvalid_reg = 1'b0, temp_m_axis_rq_tvalid_next; +reg temp_m_axis_rq_tlast_reg = 1'b0; +reg [AXIS_PCIE_RQ_USER_WIDTH-1:0] temp_m_axis_rq_tuser_reg = {AXIS_PCIE_RQ_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_rq_int_to_output; +reg store_axis_rq_int_to_temp; +reg store_axis_rq_temp_to_output; + +assign m_axis_rq_tdata = m_axis_rq_tdata_reg; +assign m_axis_rq_tkeep = m_axis_rq_tkeep_reg; +assign m_axis_rq_tvalid = m_axis_rq_tvalid_reg; +assign m_axis_rq_tlast = m_axis_rq_tlast_reg; +assign m_axis_rq_tuser = m_axis_rq_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_rq_tready_int_early = m_axis_rq_tready || (!temp_m_axis_rq_tvalid_reg && (!m_axis_rq_tvalid_reg || !m_axis_rq_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_rq_tvalid_next = m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + + store_axis_rq_int_to_output = 1'b0; + store_axis_rq_int_to_temp = 1'b0; + store_axis_rq_temp_to_output = 1'b0; + + if (m_axis_rq_tready_int_reg) begin + // input is ready + if (m_axis_rq_tready || !m_axis_rq_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_rq_tvalid_next = m_axis_rq_tvalid_int; + store_axis_rq_int_to_temp = 1'b1; + end + end else if (m_axis_rq_tready) begin + // input is not ready, but output is ready + m_axis_rq_tvalid_next = temp_m_axis_rq_tvalid_reg; + temp_m_axis_rq_tvalid_next = 1'b0; + store_axis_rq_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_rq_tvalid_reg <= 1'b0; + m_axis_rq_tready_int_reg <= 1'b0; + temp_m_axis_rq_tvalid_reg <= 1'b0; + end else begin + m_axis_rq_tvalid_reg <= m_axis_rq_tvalid_next; + m_axis_rq_tready_int_reg <= m_axis_rq_tready_int_early; + temp_m_axis_rq_tvalid_reg <= temp_m_axis_rq_tvalid_next; + end + + // datapath + if (store_axis_rq_int_to_output) begin + m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end else if (store_axis_rq_temp_to_output) begin + m_axis_rq_tdata_reg <= temp_m_axis_rq_tdata_reg; + m_axis_rq_tkeep_reg <= temp_m_axis_rq_tkeep_reg; + m_axis_rq_tlast_reg <= temp_m_axis_rq_tlast_reg; + m_axis_rq_tuser_reg <= temp_m_axis_rq_tuser_reg; + end + + if (store_axis_rq_int_to_temp) begin + temp_m_axis_rq_tdata_reg <= m_axis_rq_tdata_int; + temp_m_axis_rq_tkeep_reg <= m_axis_rq_tkeep_int; + temp_m_axis_rq_tlast_reg <= m_axis_rq_tlast_int; + temp_m_axis_rq_tuser_reg <= m_axis_rq_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axi_master.v b/corundum/lib/pcie/rtl/pcie_us_axi_master.v new file mode 100644 index 0000000000000000000000000000000000000000..708fb17bc487ce5dd3c1f5fd749b07a576f08d5b --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axi_master.v @@ -0,0 +1,322 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe AXI Master + */ +module pcie_us_axi_master # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream CQ tuser signal width + parameter AXIS_PCIE_CQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 85 : 183, + // PCIe AXI stream CC tuser signal width + parameter AXIS_PCIE_CC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 33 : 81, + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 64, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 256 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (CQ) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_cq_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_cq_tkeep, + input wire s_axis_cq_tvalid, + output wire s_axis_cq_tready, + input wire s_axis_cq_tlast, + input wire [AXIS_PCIE_CQ_USER_WIDTH-1:0] s_axis_cq_tuser, + + /* + * AXI output (CC) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cc_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cc_tkeep, + output wire m_axis_cc_tvalid, + input wire m_axis_cc_tready, + output wire m_axis_cc_tlast, + output wire [AXIS_PCIE_CC_USER_WIDTH-1:0] m_axis_cc_tuser, + + /* + * AXI Master output + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready, + + /* + * Configuration + */ + input wire [15:0] completer_id, + input wire completer_id_enable, + input wire [2:0] max_payload_size, + + /* + * Status + */ + output wire status_error_cor, + output wire status_error_uncor +); + +wire [AXIS_PCIE_DATA_WIDTH-1:0] axis_cq_tdata_read; +wire [AXIS_PCIE_KEEP_WIDTH-1:0] axis_cq_tkeep_read; +wire axis_cq_tvalid_read; +wire axis_cq_tready_read; +wire axis_cq_tlast_read; +wire [AXIS_PCIE_CQ_USER_WIDTH-1:0] axis_cq_tuser_read; + +wire [AXIS_PCIE_DATA_WIDTH-1:0] axis_cq_tdata_write; +wire [AXIS_PCIE_KEEP_WIDTH-1:0] axis_cq_tkeep_write; +wire axis_cq_tvalid_write; +wire axis_cq_tready_write; +wire axis_cq_tlast_write; +wire [AXIS_PCIE_CQ_USER_WIDTH-1:0] axis_cq_tuser_write; + +wire [3:0] req_type; +wire [1:0] select; + +wire [1:0] status_error_uncor_int; + +pcie_us_axis_cq_demux #( + .M_COUNT(2), + .AXIS_PCIE_DATA_WIDTH(AXIS_PCIE_DATA_WIDTH), + .AXIS_PCIE_KEEP_WIDTH(AXIS_PCIE_KEEP_WIDTH), + .AXIS_PCIE_CQ_USER_WIDTH(AXIS_PCIE_CQ_USER_WIDTH) +) +cq_demux_inst ( + .clk(clk), + .rst(rst), + + .s_axis_cq_tdata(s_axis_cq_tdata), + .s_axis_cq_tkeep(s_axis_cq_tkeep), + .s_axis_cq_tvalid(s_axis_cq_tvalid), + .s_axis_cq_tready(s_axis_cq_tready), + .s_axis_cq_tlast(s_axis_cq_tlast), + .s_axis_cq_tuser(s_axis_cq_tuser), + + .m_axis_cq_tdata({axis_cq_tdata_write, axis_cq_tdata_read}), + .m_axis_cq_tkeep({axis_cq_tkeep_write, axis_cq_tkeep_read}), + .m_axis_cq_tvalid({axis_cq_tvalid_write, axis_cq_tvalid_read}), + .m_axis_cq_tready({axis_cq_tready_write, axis_cq_tready_read}), + .m_axis_cq_tlast({axis_cq_tlast_write, axis_cq_tlast_read}), + .m_axis_cq_tuser({axis_cq_tuser_write, axis_cq_tuser_read}), + + .req_type(req_type), + .target_function(), + .bar_id(), + .msg_code(), + .msg_routing(), + + .enable(1'b1), + .drop(1'b0), + .select(select) +); + +assign select[1] = req_type == 4'b0001; +assign select[0] = ~select[1]; + +pcie_us_axi_master_rd #( + .AXIS_PCIE_DATA_WIDTH(AXIS_PCIE_DATA_WIDTH), + .AXIS_PCIE_KEEP_WIDTH(AXIS_PCIE_KEEP_WIDTH), + .AXIS_PCIE_CC_USER_WIDTH(AXIS_PCIE_CC_USER_WIDTH), + .AXIS_PCIE_CQ_USER_WIDTH(AXIS_PCIE_CQ_USER_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN) +) +pcie_us_axi_master_rd_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI input (CQ) + */ + .s_axis_cq_tdata(axis_cq_tdata_read), + .s_axis_cq_tkeep(axis_cq_tkeep_read), + .s_axis_cq_tvalid(axis_cq_tvalid_read), + .s_axis_cq_tready(axis_cq_tready_read), + .s_axis_cq_tlast(axis_cq_tlast_read), + .s_axis_cq_tuser(axis_cq_tuser_read), + + /* + * AXI output (CC) + */ + .m_axis_cc_tdata(m_axis_cc_tdata), + .m_axis_cc_tkeep(m_axis_cc_tkeep), + .m_axis_cc_tvalid(m_axis_cc_tvalid), + .m_axis_cc_tready(m_axis_cc_tready), + .m_axis_cc_tlast(m_axis_cc_tlast), + .m_axis_cc_tuser(m_axis_cc_tuser), + + /* + * AXI master interface + */ + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready), + + /* + * Configuration + */ + .completer_id(completer_id), + .completer_id_enable(completer_id_enable), + .max_payload_size(max_payload_size), + + /* + * Status + */ + .status_error_cor(status_error_cor), + .status_error_uncor(status_error_uncor_int[0]) +); + +pcie_us_axi_master_wr #( + .AXIS_PCIE_DATA_WIDTH(AXIS_PCIE_DATA_WIDTH), + .AXIS_PCIE_KEEP_WIDTH(AXIS_PCIE_KEEP_WIDTH), + .AXIS_PCIE_CQ_USER_WIDTH(AXIS_PCIE_CQ_USER_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN) +) +pcie_us_axi_master_wr_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI input (CQ) + */ + .s_axis_cq_tdata(axis_cq_tdata_write), + .s_axis_cq_tkeep(axis_cq_tkeep_write), + .s_axis_cq_tvalid(axis_cq_tvalid_write), + .s_axis_cq_tready(axis_cq_tready_write), + .s_axis_cq_tlast(axis_cq_tlast_write), + .s_axis_cq_tuser(axis_cq_tuser_write), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready), + + /* + * Status + */ + .status_error_uncor(status_error_uncor_int[1]) +); + +pulse_merge #( + .INPUT_WIDTH(2), + .COUNT_WIDTH(4) +) +status_error_uncor_pm_inst ( + .clk(clk), + .rst(rst), + + .pulse_in(status_error_uncor_int), + .count_out(), + .pulse_out(status_error_uncor) +); + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axi_master_rd.v b/corundum/lib/pcie/rtl/pcie_us_axi_master_rd.v new file mode 100644 index 0000000000000000000000000000000000000000..046bcb80462b58082d854aff1dbead80106fe4e5 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axi_master_rd.v @@ -0,0 +1,1223 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe AXI Master (read) + */ +module pcie_us_axi_master_rd # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream CQ tuser signal width + parameter AXIS_PCIE_CQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 85 : 183, + // PCIe AXI stream CC tuser signal width + parameter AXIS_PCIE_CC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 33 : 81, + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 64, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 256 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (CQ) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_cq_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_cq_tkeep, + input wire s_axis_cq_tvalid, + output wire s_axis_cq_tready, + input wire s_axis_cq_tlast, + input wire [AXIS_PCIE_CQ_USER_WIDTH-1:0] s_axis_cq_tuser, + + /* + * AXI output (CC) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cc_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cc_tkeep, + output wire m_axis_cc_tvalid, + input wire m_axis_cc_tready, + output wire m_axis_cc_tlast, + output wire [AXIS_PCIE_CC_USER_WIDTH-1:0] m_axis_cc_tuser, + + /* + * AXI master interface + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready, + + /* + * Configuration + */ + input wire [15:0] completer_id, + input wire completer_id_enable, + input wire [2:0] max_payload_size, + + /* + * Status + */ + output wire status_error_cor, + output wire status_error_uncor +); + +parameter PCIE_ADDR_WIDTH = 64; + +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXI_MAX_BURST_SIZE = AXI_MAX_BURST_LEN*AXI_WORD_WIDTH; + +parameter AXIS_PCIE_WORD_WIDTH = AXIS_PCIE_KEEP_WIDTH; +parameter AXIS_PCIE_WORD_SIZE = AXIS_PCIE_DATA_WIDTH/AXIS_PCIE_WORD_WIDTH; + +parameter OFFSET_WIDTH = $clog2(AXI_DATA_WIDTH/32); + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, 256, or 512 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + if (AXIS_PCIE_CQ_USER_WIDTH != 183) begin + $error("Error: PCIe CQ tuser width must be 183 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_CC_USER_WIDTH != 81) begin + $error("Error: PCIe CC tuser width must be 81 (instance %m)"); + $finish; + end + end else begin + if (AXIS_PCIE_CQ_USER_WIDTH != 85 && AXIS_PCIE_CQ_USER_WIDTH != 88) begin + $error("Error: PCIe CQ tuser width must be 85 or 88 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_CC_USER_WIDTH != 33) begin + $error("Error: PCIe CC tuser width must be 33 (instance %m)"); + $finish; + end + end + + if (AXI_DATA_WIDTH != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: AXI interface width must match PCIe interface width (instance %m)"); + $finish; + end + + if (AXI_STRB_WIDTH * 8 != AXI_DATA_WIDTH) begin + $error("Error: AXI interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXI_MAX_BURST_LEN < 1 || AXI_MAX_BURST_LEN > 256) begin + $error("Error: AXI_MAX_BURST_LEN must be between 1 and 256 (instance %m)"); + $finish; + end +end + +localparam [3:0] + REQ_MEM_READ = 4'b0000, + REQ_MEM_WRITE = 4'b0001, + REQ_IO_READ = 4'b0010, + REQ_IO_WRITE = 4'b0011, + REQ_MEM_FETCH_ADD = 4'b0100, + REQ_MEM_SWAP = 4'b0101, + REQ_MEM_CAS = 4'b0110, + REQ_MEM_READ_LOCKED = 4'b0111, + REQ_CFG_READ_0 = 4'b1000, + REQ_CFG_READ_1 = 4'b1001, + REQ_CFG_WRITE_0 = 4'b1010, + REQ_CFG_WRITE_1 = 4'b1011, + REQ_MSG = 4'b1100, + REQ_MSG_VENDOR = 4'b1101, + REQ_MSG_ATS = 4'b1110; + +localparam [2:0] + CPL_STATUS_SC = 3'b000, // successful completion + CPL_STATUS_UR = 3'b001, // unsupported request + CPL_STATUS_CRS = 3'b010, // configuration request retry status + CPL_STATUS_CA = 3'b100; // completer abort + +localparam [2:0] + AXI_STATE_IDLE = 3'd0, + AXI_STATE_HEADER = 3'd1, + AXI_STATE_START = 3'd2, + AXI_STATE_REQ = 3'd3, + AXI_STATE_WAIT_END = 3'd4; + +reg [2:0] axi_state_reg = AXI_STATE_IDLE, axi_state_next; + +localparam [2:0] + TLP_STATE_IDLE = 3'd0, + TLP_STATE_HEADER_1 = 3'd1, + TLP_STATE_HEADER_2 = 3'd2, + TLP_STATE_TRANSFER = 3'd3, + TLP_STATE_CPL_1 = 3'd4, + TLP_STATE_CPL_2 = 3'd5; + +reg [2:0] tlp_state_reg = TLP_STATE_IDLE, tlp_state_next; + +// datapath control signals +reg transfer_in_save; + +reg tlp_cmd_ready; + +reg [1:0] first_be_offset; +reg [1:0] last_be_offset; +reg [2:0] single_dword_len; + +reg [PCIE_ADDR_WIDTH-1:0] pcie_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, pcie_addr_next; +reg [AXI_ADDR_WIDTH-1:0] axi_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, axi_addr_next; +reg [12:0] op_count_reg = 13'd0, op_count_next; +reg [10:0] op_dword_count_reg = 11'd0, op_dword_count_next; +reg [10:0] tr_dword_count_reg = 11'd0, tr_dword_count_next; +reg [10:0] tlp_dword_count_reg = 11'd0, tlp_dword_count_next; +reg [3:0] first_be_reg = 4'd0, first_be_next; +reg [3:0] last_be_reg = 4'd0, last_be_next; + +reg [1:0] at_reg = 2'd0, at_next; +reg [PCIE_ADDR_WIDTH-1:0] tlp_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, tlp_addr_next; +reg [12:0] tlp_len_reg = 13'd0, tlp_len_next; +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; +reg [10:0] dword_count_reg = 11'd0, dword_count_next; +reg [9:0] input_cycle_count_reg = 10'd0, input_cycle_count_next; +reg [9:0] output_cycle_count_reg = 10'd0, output_cycle_count_next; +reg input_active_reg = 1'b0, input_active_next; +reg bubble_cycle_reg = 1'b0, bubble_cycle_next; +reg last_cycle_reg = 1'b0, last_cycle_next; +reg last_tlp_reg = 1'b0, last_tlp_next; +reg [2:0] status_reg = 3'd0, status_next; +reg [15:0] requester_id_reg = 16'd0, requester_id_next; +reg [7:0] tag_reg = 8'd0, tag_next; +reg [2:0] tc_reg = 3'd0, tc_next; +reg [2:0] attr_reg = 3'd0, attr_next; + +reg [1:0] tlp_cmd_at_reg = 2'd0, tlp_cmd_at_next; +reg [PCIE_ADDR_WIDTH-1:0] tlp_cmd_addr_reg = {PCIE_ADDR_WIDTH{1'b0}}, tlp_cmd_addr_next; +reg [12:0] tlp_cmd_byte_len_reg = 13'd0, tlp_cmd_byte_len_next; +reg [10:0] tlp_cmd_dword_len_reg = 11'd0, tlp_cmd_dword_len_next; +reg [9:0] tlp_cmd_input_cycle_len_reg = 10'd0, tlp_cmd_input_cycle_len_next; +reg [9:0] tlp_cmd_output_cycle_len_reg = 10'd0, tlp_cmd_output_cycle_len_next; +reg [OFFSET_WIDTH-1:0] tlp_cmd_offset_reg = {OFFSET_WIDTH{1'b0}}, tlp_cmd_offset_next; +reg [2:0] tlp_cmd_status_reg = 3'd0, tlp_cmd_status_next; +reg [15:0] tlp_cmd_requester_id_reg = 16'd0, tlp_cmd_requester_id_next; +reg [7:0] tlp_cmd_tag_reg = 8'd0, tlp_cmd_tag_next; +reg [2:0] tlp_cmd_tc_reg = 3'd0, tlp_cmd_tc_next; +reg [2:0] tlp_cmd_attr_reg = 3'd0, tlp_cmd_attr_next; +reg tlp_cmd_bubble_cycle_reg = 1'b0, tlp_cmd_bubble_cycle_next; +reg tlp_cmd_last_reg = 1'b0, tlp_cmd_last_next; +reg tlp_cmd_valid_reg = 1'b0, tlp_cmd_valid_next; + +reg [10:0] max_payload_size_dw_reg = 11'd0; + +reg s_axis_cq_tready_reg = 1'b0, s_axis_cq_tready_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axi_araddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_araddr_next; +reg [7:0] m_axi_arlen_reg = 8'd0, m_axi_arlen_next; +reg m_axi_arvalid_reg = 1'b0, m_axi_arvalid_next; +reg m_axi_rready_reg = 1'b0, m_axi_rready_next; + +reg [AXI_DATA_WIDTH-1:0] save_axi_rdata_reg = {AXI_DATA_WIDTH{1'b0}}; + +wire [AXI_DATA_WIDTH-1:0] shift_axi_rdata = {m_axi_rdata, save_axi_rdata_reg} >> ((AXI_STRB_WIDTH/4-offset_reg)*32); + +reg status_error_cor_reg = 1'b0, status_error_cor_next; +reg status_error_uncor_reg = 1'b0, status_error_uncor_next; + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cc_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cc_tkeep_int; +reg m_axis_cc_tvalid_int; +reg m_axis_cc_tready_int_reg = 1'b0; +reg m_axis_cc_tlast_int; +reg [AXIS_PCIE_CC_USER_WIDTH-1:0] m_axis_cc_tuser_int; +wire m_axis_cc_tready_int_early; + +assign s_axis_cq_tready = s_axis_cq_tready_reg; + +assign m_axi_arid = {AXI_ID_WIDTH{1'b0}}; +assign m_axi_araddr = m_axi_araddr_reg; +assign m_axi_arlen = m_axi_arlen_reg; +assign m_axi_arsize = AXI_BURST_SIZE; +assign m_axi_arburst = 2'b01; +assign m_axi_arlock = 1'b0; +assign m_axi_arcache = 4'b0011; +assign m_axi_arprot = 3'b010; +assign m_axi_arvalid = m_axi_arvalid_reg; +assign m_axi_rready = m_axi_rready_reg; + +assign status_error_cor = status_error_cor_reg; +assign status_error_uncor = status_error_uncor_reg; + +always @* begin + casez (first_be_next) + 4'b0000: single_dword_len = 3'd0; + 4'b0001: single_dword_len = 3'd1; + 4'b0010: single_dword_len = 3'd1; + 4'b0100: single_dword_len = 3'd1; + 4'b1000: single_dword_len = 3'd1; + 4'b0011: single_dword_len = 3'd2; + 4'b0110: single_dword_len = 3'd2; + 4'b1100: single_dword_len = 3'd2; + 4'b01z1: single_dword_len = 3'd3; + 4'b1z10: single_dword_len = 3'd3; + 4'b1zz1: single_dword_len = 3'd4; + endcase + + casez (first_be_next) + 4'b0000: first_be_offset = 2'b00; + 4'bzzz1: first_be_offset = 2'b00; + 4'bzz10: first_be_offset = 2'b01; + 4'bz100: first_be_offset = 2'b10; + 4'b1000: first_be_offset = 2'b11; + endcase + + casez (last_be_next) + 4'b0000: last_be_offset = 2'b00; + 4'b1zzz: last_be_offset = 2'b00; + 4'b01zz: last_be_offset = 2'b01; + 4'b001z: last_be_offset = 2'b10; + 4'b0001: last_be_offset = 2'b11; + endcase +end + +always @* begin + axi_state_next = AXI_STATE_IDLE; + + s_axis_cq_tready_next = 1'b0; + + m_axi_araddr_next = m_axi_araddr_reg; + m_axi_arlen_next = m_axi_arlen_reg; + m_axi_arvalid_next = m_axi_arvalid_reg && !m_axi_arready; + + pcie_addr_next = pcie_addr_reg; + axi_addr_next = axi_addr_reg; + op_count_next = op_count_reg; + op_dword_count_next = op_dword_count_reg; + tr_dword_count_next = tr_dword_count_reg; + tlp_dword_count_next = tlp_dword_count_reg; + first_be_next = first_be_reg; + last_be_next = last_be_reg; + + tlp_cmd_at_next = tlp_cmd_at_reg; + tlp_cmd_addr_next = tlp_cmd_addr_reg; + tlp_cmd_byte_len_next = tlp_cmd_byte_len_reg; + tlp_cmd_dword_len_next = tlp_cmd_dword_len_reg; + tlp_cmd_input_cycle_len_next = tlp_cmd_input_cycle_len_reg; + tlp_cmd_output_cycle_len_next = tlp_cmd_output_cycle_len_reg; + tlp_cmd_offset_next = tlp_cmd_offset_reg; + tlp_cmd_status_next = tlp_cmd_status_reg; + tlp_cmd_requester_id_next = tlp_cmd_requester_id_reg; + tlp_cmd_tag_next = tlp_cmd_tag_reg; + tlp_cmd_attr_next = tlp_cmd_attr_reg; + tlp_cmd_tc_next = tlp_cmd_tc_reg; + tlp_cmd_bubble_cycle_next = tlp_cmd_bubble_cycle_reg; + tlp_cmd_last_next = tlp_cmd_last_reg; + tlp_cmd_valid_next = tlp_cmd_valid_reg && !tlp_cmd_ready; + + status_error_cor_next = 1'b0; + status_error_uncor_next = 1'b0; + + // TLP segmentation and AXI read request generation + case (axi_state_reg) + AXI_STATE_IDLE: begin + // idle state, wait for completion request + s_axis_cq_tready_next = !tlp_cmd_valid_reg; + + if (s_axis_cq_tready & s_axis_cq_tvalid) begin + // header fields + tlp_cmd_at_next = s_axis_cq_tdata[1:0]; + pcie_addr_next = {s_axis_cq_tdata[63:2], first_be_offset}; + tlp_cmd_status_next = CPL_STATUS_SC; // successful completion + if (AXIS_PCIE_DATA_WIDTH > 64) begin + op_dword_count_next = s_axis_cq_tdata[74:64]; + if (op_dword_count_next == 1) begin + op_count_next = single_dword_len; + end else begin + op_count_next = (op_dword_count_next << 2) - first_be_offset - last_be_offset; + end + tlp_cmd_requester_id_next = s_axis_cq_tdata[95:80]; + tlp_cmd_tag_next = s_axis_cq_tdata[103:96]; + tlp_cmd_tc_next = s_axis_cq_tdata[123:121]; + tlp_cmd_attr_next = s_axis_cq_tdata[126:124]; + end + + // tuser fields + if (AXIS_PCIE_DATA_WIDTH == 512) begin + first_be_next = s_axis_cq_tuser[3:0]; + last_be_next = s_axis_cq_tuser[11:8]; + end else begin + first_be_next = s_axis_cq_tuser[3:0]; + last_be_next = s_axis_cq_tuser[7:4]; + end + + if (AXIS_PCIE_DATA_WIDTH == 64) begin + // 64 bit interface hasn't processed the whole header yet + s_axis_cq_tready_next = 1'b1; + if (s_axis_cq_tlast) begin + // truncated packet + // report uncorrectable error + status_error_uncor_next = 1'b1; + axi_state_next = AXI_STATE_IDLE; + end else begin + axi_state_next = AXI_STATE_HEADER; + end + end else begin + // processed whole header; check request type + if (s_axis_cq_tdata[78:75] == REQ_MEM_READ) begin + // read request + s_axis_cq_tready_next = 1'b0; + axi_state_next = AXI_STATE_START; + end else if (s_axis_cq_tdata[78:75] == REQ_MEM_WRITE || (s_axis_cq_tdata[78:75] & 4'b1100) == 4'b1100) begin + // posted request (memory write or message), drop and report uncorrectable error + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + axi_state_next = AXI_STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + axi_state_next = AXI_STATE_WAIT_END; + end + end else begin + // invalid request, send UR completion + tlp_cmd_status_next = CPL_STATUS_UR; // unsupported request + tlp_cmd_valid_next = 1'b1; + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + axi_state_next = AXI_STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + axi_state_next = AXI_STATE_WAIT_END; + end + end + end + end else begin + axi_state_next = AXI_STATE_IDLE; + end + end + AXI_STATE_HEADER: begin + // header state, store rest of header (64 bit interface only) + s_axis_cq_tready_next = 1'b1; + + if (s_axis_cq_tready & s_axis_cq_tvalid) begin + // header fields + op_dword_count_next = s_axis_cq_tdata[10:0]; + if (op_dword_count_next == 1) begin + op_count_next = single_dword_len; + end else begin + op_count_next = (op_dword_count_next << 2) - first_be_offset - last_be_offset; + end + tlp_cmd_requester_id_next = s_axis_cq_tdata[31:16]; + tlp_cmd_tag_next = s_axis_cq_tdata[39:32]; + tlp_cmd_tc_next = s_axis_cq_tdata[59:57]; + tlp_cmd_attr_next = s_axis_cq_tdata[62:60]; + + // processed whole header; check request type + if (s_axis_cq_tdata[14:11] == REQ_MEM_READ) begin + // read request + s_axis_cq_tready_next = 1'b0; + axi_state_next = AXI_STATE_START; + end else if (s_axis_cq_tdata[14:11] == REQ_MEM_WRITE || (s_axis_cq_tdata[14:11] & 4'b1100) == 4'b1100) begin + // posted request (memory write or message), drop and report uncorrectable error + // write request - drop + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + axi_state_next = AXI_STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + axi_state_next = AXI_STATE_WAIT_END; + end + end else begin + // invalid request, send UR completion + tlp_cmd_status_next = CPL_STATUS_UR; // unsupported request + tlp_cmd_valid_next = 1'b1; + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + axi_state_next = AXI_STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + axi_state_next = AXI_STATE_WAIT_END; + end + end + end else begin + axi_state_next = AXI_STATE_HEADER; + end + end + AXI_STATE_START: begin + // start state, compute TLP length + if (!tlp_cmd_valid_reg) begin + if (op_dword_count_reg <= max_payload_size_dw_reg) begin + // packet smaller than max payload size + // assumed to not cross 4k boundary, send one TLP + tlp_dword_count_next = op_dword_count_reg; + end else begin + // packet larger than max payload size + // assumed to not cross 4k boundary, send one TLP, align to 128 byte RCB + tlp_dword_count_next = max_payload_size_dw_reg - pcie_addr_reg[6:2]; + end + + // read completion TLP will transfer DWORD count minus offset into first DWORD + op_count_next = op_count_reg - (tlp_dword_count_next << 2) + pcie_addr_reg[1:0]; + op_dword_count_next = op_dword_count_reg - tlp_dword_count_next; + + // number of bus transfers from AXI, DWORD count plus DWORD offset, divided by bus width in DWORDS + tlp_cmd_input_cycle_len_next = (tlp_dword_count_next + pcie_addr_reg[OFFSET_WIDTH+2-1:2] - 1) >> (AXI_BURST_SIZE-2); + // number of bus transfers in TLP, DOWRD count plus payload start DWORD offset, divided by bus width in DWORDS + if (AXIS_PCIE_DATA_WIDTH == 64) begin + tlp_cmd_output_cycle_len_next = (tlp_dword_count_next + 1 - 1) >> (AXI_BURST_SIZE-2); + end else begin + tlp_cmd_output_cycle_len_next = (tlp_dword_count_next + 3 - 1) >> (AXI_BURST_SIZE-2); + end + + tlp_cmd_addr_next = pcie_addr_reg; + tlp_cmd_byte_len_next = op_count_reg; + tlp_cmd_dword_len_next = tlp_dword_count_next; + // required DWORD shift to place first DWORD read from AXI into proper position in payload + // bubble cycle required if first AXI transfer does not fill first payload transfer + if (AXIS_PCIE_DATA_WIDTH == 64) begin + tlp_cmd_offset_next = 1-pcie_addr_reg[OFFSET_WIDTH+2-1:2]; + tlp_cmd_bubble_cycle_next = 1'b0; + end else begin + tlp_cmd_offset_next = 3-pcie_addr_reg[OFFSET_WIDTH+2-1:2]; + tlp_cmd_bubble_cycle_next = pcie_addr_reg[OFFSET_WIDTH+2-1:2] > 3; + end + tlp_cmd_last_next = op_dword_count_next == 0; + tlp_cmd_valid_next = 1'b1; + + axi_state_next = AXI_STATE_REQ; + end else begin + axi_state_next = AXI_STATE_START; + end + end + AXI_STATE_REQ: begin + // request state, generate AXI read requests + if (!m_axi_arvalid) begin + if (tlp_dword_count_reg <= AXI_MAX_BURST_SIZE/4) begin + // packet smaller than max burst size + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = tlp_dword_count_reg; + end else begin + // packet larger than max burst size + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = AXI_MAX_BURST_SIZE/4 - pcie_addr_reg[OFFSET_WIDTH+2-1:2]; + end + + m_axi_araddr_next = pcie_addr_reg; + m_axi_arlen_next = (tr_dword_count_next + pcie_addr_reg[OFFSET_WIDTH+2-1:2] - 1) >> (AXI_BURST_SIZE-2); + m_axi_arvalid_next = 1; + + // increment address by transfer size + pcie_addr_next = pcie_addr_reg + (tr_dword_count_next << 2); + // first transfer will end on DWORD boundary, so subsequent transfers will be DWORD aligned + pcie_addr_next[1:0] = 2'b0; + // keep track of how much more needs to be read to fill the TLP + tlp_dword_count_next = tlp_dword_count_reg - tr_dword_count_next; + + if (tlp_dword_count_next > 0) begin + axi_state_next = AXI_STATE_REQ; + end else if (op_dword_count_next > 0) begin + axi_state_next = AXI_STATE_START; + end else begin + axi_state_next = AXI_STATE_IDLE; + end + end else begin + axi_state_next = AXI_STATE_REQ; + end + end + AXI_STATE_WAIT_END: begin + // wait end state, wait for end of TLP + s_axis_cq_tready_next = 1'b1; + + if (s_axis_cq_tready & s_axis_cq_tvalid) begin + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = !tlp_cmd_valid_reg; + axi_state_next = AXI_STATE_IDLE; + end else begin + axi_state_next = AXI_STATE_WAIT_END; + end + end else begin + axi_state_next = AXI_STATE_WAIT_END; + end + end + endcase +end + +always @* begin + tlp_state_next = TLP_STATE_IDLE; + + transfer_in_save = 1'b0; + + tlp_cmd_ready = 1'b0; + + m_axi_rready_next = 1'b0; + + at_next = at_reg; + tlp_addr_next = tlp_addr_reg; + tlp_len_next = tlp_len_reg; + dword_count_next = dword_count_reg; + offset_next = offset_reg; + input_cycle_count_next = input_cycle_count_reg; + output_cycle_count_next = output_cycle_count_reg; + input_active_next = input_active_reg; + bubble_cycle_next = bubble_cycle_reg; + last_cycle_next = last_cycle_reg; + last_tlp_next = last_tlp_reg; + status_next = status_reg; + requester_id_next = requester_id_reg; + tag_next = tag_reg; + tc_next = tc_reg; + attr_next = attr_reg; + + m_axis_cc_tdata_int = {AXIS_PCIE_DATA_WIDTH{1'b0}}; + m_axis_cc_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; + m_axis_cc_tvalid_int = 1'b0; + m_axis_cc_tlast_int = 1'b0; + m_axis_cc_tuser_int = {AXIS_PCIE_CC_USER_WIDTH{1'b0}}; + + m_axis_cc_tdata_int[6:0] = tlp_addr_reg; // lower address + m_axis_cc_tdata_int[9:8] = at_reg; + m_axis_cc_tdata_int[28:16] = tlp_len_reg; // byte count + m_axis_cc_tdata_int[42:32] = dword_count_reg; + m_axis_cc_tdata_int[45:43] = status_reg; + m_axis_cc_tdata_int[63:48] = requester_id_reg; + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_cc_tdata_int[71:64] = tag_reg; + m_axis_cc_tdata_int[87:72] = completer_id; + m_axis_cc_tdata_int[88] = completer_id_enable; + m_axis_cc_tdata_int[91:89] = tc_reg; + m_axis_cc_tdata_int[94:92] = attr_reg; + m_axis_cc_tdata_int[95] = 1'b0; // force ECRC + if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tdata_int[255:96] = shift_axi_rdata[255:96]; + end else begin + m_axis_cc_tdata_int[127:96] = shift_axi_rdata[127:96]; + end + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tkeep_int = 16'b0000000000000111; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tkeep_int = 8'b00000111; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_cc_tkeep_int = 4'b0111; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cc_tkeep_int = 2'b11; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tuser_int[1:0] = 2'b01; // is_sop + m_axis_cc_tuser_int[3:2] = 2'd0; // is_sop0_ptr + m_axis_cc_tuser_int[5:4] = 2'd0; // is_sop1_ptr + m_axis_cc_tuser_int[7:6] = 2'b01; // is_eop + m_axis_cc_tuser_int[11:8] = 4'd3; // is_eop0_ptr + m_axis_cc_tuser_int[15:12] = 4'd0; // is_eop1_ptr + m_axis_cc_tuser_int[16] = 1'b0; // discontinue + m_axis_cc_tuser_int[80:17] = 64'd0; // parity + end else begin + m_axis_cc_tuser_int[0] = 1'b0; // discontinue + m_axis_cc_tuser_int[32:1] = 32'd0; // parity + end + + // AXI read response processing and TLP generation + case (tlp_state_reg) + TLP_STATE_IDLE: begin + // idle state, wait for command + m_axi_rready_next = 1'b0; + + // store TLP fields and transfer parameters + at_next = tlp_cmd_at_reg; + tlp_addr_next = tlp_cmd_addr_reg; + tlp_len_next = tlp_cmd_byte_len_reg; + dword_count_next = tlp_cmd_dword_len_reg; + offset_next = tlp_cmd_offset_reg; + input_cycle_count_next = tlp_cmd_input_cycle_len_reg; + output_cycle_count_next = tlp_cmd_output_cycle_len_reg; + input_active_next = 1'b1; + bubble_cycle_next = tlp_cmd_bubble_cycle_reg; + last_cycle_next = tlp_cmd_output_cycle_len_reg == 0; + last_tlp_next = tlp_cmd_last_reg; + status_next = tlp_cmd_status_reg; + requester_id_next = tlp_cmd_requester_id_reg; + tag_next = tlp_cmd_tag_reg; + tc_next = tlp_cmd_tc_reg; + attr_next = tlp_cmd_attr_reg; + + if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + if (status_next == CPL_STATUS_SC) begin + // SC status, output TLP header + if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axi_rready_next = 1'b0; + end else begin + m_axi_rready_next = m_axis_cc_tready_int_early; + end + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + // status other than SC + tlp_state_next = TLP_STATE_CPL_1; + end + end else begin + tlp_state_next = TLP_STATE_IDLE; + end + end + TLP_STATE_HEADER_1: begin + // header 1 state, send TLP header + if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axi_rready_next = 1'b0; + + if (m_axis_cc_tready_int_reg) begin + // output first part of header + m_axis_cc_tvalid_int = 1'b1; + + m_axi_rready_next = m_axis_cc_tready_int_early; + + tlp_state_next = TLP_STATE_HEADER_2; + end else begin + tlp_state_next = TLP_STATE_HEADER_1; + end + end else begin + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_reg; + + if (m_axis_cc_tready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + transfer_in_save = m_axi_rready && m_axi_rvalid; + + if (AXIS_PCIE_DATA_WIDTH >= 256 && bubble_cycle_reg) begin + // bubble cycle; store input data and update input cycle count + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + bubble_cycle_next = 1'b0; + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + // some data is transferred with header + dword_count_next = dword_count_reg - (AXIS_PCIE_KEEP_WIDTH-3); + // update cycle counters + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + output_cycle_count_next = output_cycle_count_reg - 1; + last_cycle_next = output_cycle_count_next == 0; + + // transfer data + m_axis_cc_tdata_int[AXIS_PCIE_DATA_WIDTH-1:96] = shift_axi_rdata[AXIS_PCIE_DATA_WIDTH-1:96]; + + // generate tvalid and tkeep signals for header and data + m_axis_cc_tvalid_int = 1'b1; + if (dword_count_reg >= AXIS_PCIE_KEEP_WIDTH-3) begin + m_axis_cc_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}}; + end else begin + m_axis_cc_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b1}} >> (AXIS_PCIE_KEEP_WIDTH-3 - dword_count_reg); + end + + if (last_cycle_reg) begin + m_axis_cc_tlast_int = 1'b1; + + // skip idle state if possible + at_next = tlp_cmd_at_reg; + tlp_addr_next = tlp_cmd_addr_reg; + tlp_len_next = tlp_cmd_byte_len_reg; + dword_count_next = tlp_cmd_dword_len_reg; + offset_next = tlp_cmd_offset_reg; + input_cycle_count_next = tlp_cmd_input_cycle_len_reg; + output_cycle_count_next = tlp_cmd_output_cycle_len_reg; + input_active_next = 1'b1; + bubble_cycle_next = tlp_cmd_bubble_cycle_reg; + last_cycle_next = tlp_cmd_output_cycle_len_reg == 0; + last_tlp_next = tlp_cmd_last_reg; + status_next = tlp_cmd_status_reg; + requester_id_next = tlp_cmd_requester_id_reg; + tag_next = tlp_cmd_tag_reg; + tc_next = tlp_cmd_tc_reg; + attr_next = tlp_cmd_attr_reg; + + if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + m_axi_rready_next = m_axis_cc_tready_int_early; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + m_axi_rready_next = 1'b0; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_TRANSFER; + end + end + end else begin + tlp_state_next = TLP_STATE_HEADER_1; + end + end + end + TLP_STATE_HEADER_2: begin + // header 2 state, send rest of TLP header (64 bit interface only) + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_reg; + + m_axis_cc_tdata_int[7:0] = tag_reg; + m_axis_cc_tdata_int[23:8] = completer_id; + m_axis_cc_tdata_int[24] = completer_id_enable; + m_axis_cc_tdata_int[27:25] = tc_reg; + m_axis_cc_tdata_int[30:28] = attr_reg; + m_axis_cc_tdata_int[31] = 1'b0; // force ECRC + m_axis_cc_tdata_int[63:32] = shift_axi_rdata[63:32]; + + if (m_axis_cc_tready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + transfer_in_save = m_axi_rready && m_axi_rvalid; + + // some data is transferred with header + dword_count_next = dword_count_reg - 1; + // update cycle counters + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + output_cycle_count_next = output_cycle_count_reg - 1; + last_cycle_next = output_cycle_count_next == 0; + + // generate tvalid and tkeep signals for header and data + m_axis_cc_tvalid_int = 1'b1; + if (dword_count_reg >= 1) begin + m_axis_cc_tkeep_int = 2'b11; + end else begin + m_axis_cc_tkeep_int = 2'b11 >> (1 - dword_count_reg); + end + + if (last_cycle_reg) begin + m_axis_cc_tlast_int = 1'b1; + + // skip idle state if possible + at_next = tlp_cmd_at_reg; + tlp_addr_next = tlp_cmd_addr_reg; + tlp_len_next = tlp_cmd_byte_len_reg; + dword_count_next = tlp_cmd_dword_len_reg; + offset_next = tlp_cmd_offset_reg; + input_cycle_count_next = tlp_cmd_input_cycle_len_reg; + output_cycle_count_next = tlp_cmd_output_cycle_len_reg; + input_active_next = 1'b1; + bubble_cycle_next = tlp_cmd_bubble_cycle_reg; + last_cycle_next = tlp_cmd_output_cycle_len_reg == 0; + last_tlp_next = tlp_cmd_last_reg; + status_next = tlp_cmd_status_reg; + requester_id_next = tlp_cmd_requester_id_reg; + tag_next = tlp_cmd_tag_reg; + tc_next = tlp_cmd_tc_reg; + attr_next = tlp_cmd_attr_reg; + + if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + m_axi_rready_next = 1'b0; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + m_axi_rready_next = 1'b0; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_TRANSFER; + end + end else begin + tlp_state_next = TLP_STATE_HEADER_2; + end + end + TLP_STATE_TRANSFER: begin + // transfer state, transfer data + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_reg; + + if (m_axis_cc_tready_int_reg && ((m_axi_rready && m_axi_rvalid) || !input_active_reg)) begin + transfer_in_save = 1'b1; + + if (bubble_cycle_reg) begin + // bubble cycle; store input data and update input cycle count + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + bubble_cycle_next = 1'b0; + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_TRANSFER; + end else begin + // update DWORD count + dword_count_next = dword_count_reg - AXI_STRB_WIDTH/4; + // update cycle counters + if (input_active_reg) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + output_cycle_count_next = output_cycle_count_reg - 1; + last_cycle_next = output_cycle_count_next == 0; + + // output data and generate tvalid and tkeep signals + m_axis_cc_tdata_int = shift_axi_rdata; + m_axis_cc_tvalid_int = 1'b1; + if (dword_count_reg >= AXI_STRB_WIDTH/4) begin + m_axis_cc_tkeep_int = {AXI_STRB_WIDTH{1'b1}}; + end else begin + m_axis_cc_tkeep_int = {AXI_STRB_WIDTH{1'b1}} >> (AXI_STRB_WIDTH - dword_count_reg); + end + + if (last_cycle_reg) begin + m_axis_cc_tlast_int = 1'b1; + + // skip idle state if possible + at_next = tlp_cmd_at_reg; + tlp_addr_next = tlp_cmd_addr_reg; + tlp_len_next = tlp_cmd_byte_len_reg; + dword_count_next = tlp_cmd_dword_len_reg; + offset_next = tlp_cmd_offset_reg; + input_cycle_count_next = tlp_cmd_input_cycle_len_reg; + output_cycle_count_next = tlp_cmd_output_cycle_len_reg; + input_active_next = 1'b1; + bubble_cycle_next = tlp_cmd_bubble_cycle_reg; + last_cycle_next = tlp_cmd_output_cycle_len_reg == 0; + last_tlp_next = tlp_cmd_last_reg; + status_next = tlp_cmd_status_reg; + requester_id_next = tlp_cmd_requester_id_reg; + tag_next = tlp_cmd_tag_reg; + tc_next = tlp_cmd_tc_reg; + attr_next = tlp_cmd_attr_reg; + + if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axi_rready_next = 1'b0; + end else begin + m_axi_rready_next = m_axis_cc_tready_int_early; + end + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + m_axi_rready_next = 1'b0; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + m_axi_rready_next = m_axis_cc_tready_int_early && input_active_next; + tlp_state_next = TLP_STATE_TRANSFER; + end + end + end else begin + tlp_state_next = TLP_STATE_TRANSFER; + end + end + TLP_STATE_CPL_1: begin + // send completion + m_axis_cc_tvalid_int = 1'b1; + m_axis_cc_tdata_int[28:16] = 13'd0; // byte count + m_axis_cc_tdata_int[42:32] = 11'd0; // DWORD count + m_axis_cc_tdata_int[45:43] = status_reg; + + // generate tvalid and tkeep signals for completion + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tkeep_int = 16'b0000000000000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tkeep_int = 8'b00000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_cc_tkeep_int = 4'b0111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cc_tkeep_int = 2'b11; + m_axis_cc_tlast_int = 1'b0; + end + + if (m_axis_cc_tready_int_reg) begin + if (AXIS_PCIE_DATA_WIDTH == 64) begin + tlp_state_next = TLP_STATE_CPL_2; + end else begin + // skip idle state if possible + at_next = tlp_cmd_at_reg; + tlp_addr_next = tlp_cmd_addr_reg; + tlp_len_next = tlp_cmd_byte_len_reg; + dword_count_next = tlp_cmd_dword_len_reg; + offset_next = tlp_cmd_offset_reg; + input_cycle_count_next = tlp_cmd_input_cycle_len_reg; + output_cycle_count_next = tlp_cmd_output_cycle_len_reg; + input_active_next = 1'b1; + bubble_cycle_next = tlp_cmd_bubble_cycle_reg; + last_cycle_next = tlp_cmd_output_cycle_len_reg == 0; + last_tlp_next = tlp_cmd_last_reg; + status_next = tlp_cmd_status_reg; + requester_id_next = tlp_cmd_requester_id_reg; + tag_next = tlp_cmd_tag_reg; + tc_next = tlp_cmd_tc_reg; + attr_next = tlp_cmd_attr_reg; + + if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + m_axi_rready_next = m_axis_cc_tready_int_early; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + m_axi_rready_next = 1'b0; + tlp_state_next = TLP_STATE_IDLE; + end + end + end else begin + tlp_state_next = TLP_STATE_CPL_1; + end + end + TLP_STATE_CPL_2: begin + // send rest of completion + m_axis_cc_tvalid_int = 1'b1; + m_axis_cc_tdata_int[7:0] = tag_reg; + m_axis_cc_tdata_int[23:8] = completer_id; + m_axis_cc_tdata_int[24] = completer_id_enable; + m_axis_cc_tdata_int[27:25] = tc_reg; + m_axis_cc_tdata_int[30:28] = attr_reg; + m_axis_cc_tdata_int[31] = 1'b0; // force ECRC + m_axis_cc_tdata_int[63:32] = 32'd0; + m_axis_cc_tkeep_int = 2'b01; + m_axis_cc_tlast_int = 1'b1; + + if (m_axis_cc_tready_int_reg) begin + // skip idle state if possible + at_next = tlp_cmd_at_reg; + tlp_addr_next = tlp_cmd_addr_reg; + tlp_len_next = tlp_cmd_byte_len_reg; + dword_count_next = tlp_cmd_dword_len_reg; + offset_next = tlp_cmd_offset_reg; + input_cycle_count_next = tlp_cmd_input_cycle_len_reg; + output_cycle_count_next = tlp_cmd_output_cycle_len_reg; + input_active_next = 1'b1; + bubble_cycle_next = tlp_cmd_bubble_cycle_reg; + last_cycle_next = tlp_cmd_output_cycle_len_reg == 0; + last_tlp_next = tlp_cmd_last_reg; + status_next = tlp_cmd_status_reg; + requester_id_next = tlp_cmd_requester_id_reg; + tag_next = tlp_cmd_tag_reg; + tc_next = tlp_cmd_tc_reg; + attr_next = tlp_cmd_attr_reg; + + if (tlp_cmd_valid_reg) begin + tlp_cmd_ready = 1'b1; + m_axi_rready_next = 1'b0; + tlp_state_next = TLP_STATE_HEADER_1; + end else begin + m_axi_rready_next = 1'b0; + tlp_state_next = TLP_STATE_IDLE; + end + end else begin + tlp_state_next = TLP_STATE_CPL_2; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + axi_state_reg <= AXI_STATE_IDLE; + tlp_state_reg <= TLP_STATE_IDLE; + tlp_cmd_valid_reg <= 1'b0; + s_axis_cq_tready_reg <= 1'b0; + m_axi_arvalid_reg <= 1'b0; + m_axi_rready_reg <= 1'b0; + + status_error_cor_reg <= 1'b0; + status_error_uncor_reg <= 1'b0; + end else begin + axi_state_reg <= axi_state_next; + tlp_state_reg <= tlp_state_next; + tlp_cmd_valid_reg <= tlp_cmd_valid_next; + s_axis_cq_tready_reg <= s_axis_cq_tready_next; + m_axi_arvalid_reg <= m_axi_arvalid_next; + m_axi_rready_reg <= m_axi_rready_next; + + status_error_cor_reg <= status_error_cor_next; + status_error_uncor_reg <= status_error_uncor_next; + end + + pcie_addr_reg <= pcie_addr_next; + axi_addr_reg <= axi_addr_next; + op_count_reg <= op_count_next; + op_dword_count_reg <= op_dword_count_next; + tr_dword_count_reg <= tr_dword_count_next; + tlp_dword_count_reg <= tlp_dword_count_next; + first_be_reg <= first_be_next; + last_be_reg <= last_be_next; + + at_reg <= at_next; + tlp_addr_reg <= tlp_addr_next; + tlp_len_reg <= tlp_len_next; + dword_count_reg <= dword_count_next; + offset_reg <= offset_next; + input_cycle_count_reg <= input_cycle_count_next; + output_cycle_count_reg <= output_cycle_count_next; + input_active_reg <= input_active_next; + bubble_cycle_reg <= bubble_cycle_next; + last_cycle_reg <= last_cycle_next; + last_tlp_reg <= last_tlp_next; + status_reg <= status_next; + requester_id_reg <= requester_id_next; + tag_reg <= tag_next; + tc_reg <= tc_next; + attr_reg <= attr_next; + + tlp_cmd_at_reg <= tlp_cmd_at_next; + tlp_cmd_addr_reg <= tlp_cmd_addr_next; + tlp_cmd_byte_len_reg <= tlp_cmd_byte_len_next; + tlp_cmd_dword_len_reg <= tlp_cmd_dword_len_next; + tlp_cmd_input_cycle_len_reg <= tlp_cmd_input_cycle_len_next; + tlp_cmd_output_cycle_len_reg <= tlp_cmd_output_cycle_len_next; + tlp_cmd_offset_reg <= tlp_cmd_offset_next; + tlp_cmd_status_reg <= tlp_cmd_status_next; + tlp_cmd_requester_id_reg <= tlp_cmd_requester_id_next; + tlp_cmd_tag_reg <= tlp_cmd_tag_next; + tlp_cmd_tc_reg <= tlp_cmd_tc_next; + tlp_cmd_attr_reg <= tlp_cmd_attr_next; + tlp_cmd_bubble_cycle_reg <= tlp_cmd_bubble_cycle_next; + tlp_cmd_last_reg <= tlp_cmd_last_next; + + m_axi_araddr_reg <= m_axi_araddr_next; + m_axi_arlen_reg <= m_axi_arlen_next; + + max_payload_size_dw_reg <= 11'd32 << (max_payload_size > 5 ? 5 : max_payload_size); + + if (transfer_in_save) begin + save_axi_rdata_reg <= m_axi_rdata; + end +end + +// output datapath logic (PCIe TLP) +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cc_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cc_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg m_axis_cc_tvalid_reg = 1'b0, m_axis_cc_tvalid_next; +reg m_axis_cc_tlast_reg = 1'b0; +reg [AXIS_PCIE_CC_USER_WIDTH-1:0] m_axis_cc_tuser_reg = {AXIS_PCIE_CC_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_cc_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_cc_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_cc_tvalid_reg = 1'b0, temp_m_axis_cc_tvalid_next; +reg temp_m_axis_cc_tlast_reg = 1'b0; +reg [AXIS_PCIE_CC_USER_WIDTH-1:0] temp_m_axis_cc_tuser_reg = {AXIS_PCIE_CC_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_cc_int_to_output; +reg store_axis_cc_int_to_temp; +reg store_axis_cc_temp_to_output; + +assign m_axis_cc_tdata = m_axis_cc_tdata_reg; +assign m_axis_cc_tkeep = m_axis_cc_tkeep_reg; +assign m_axis_cc_tvalid = m_axis_cc_tvalid_reg; +assign m_axis_cc_tlast = m_axis_cc_tlast_reg; +assign m_axis_cc_tuser = m_axis_cc_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_cc_tready_int_early = m_axis_cc_tready || (!temp_m_axis_cc_tvalid_reg && (!m_axis_cc_tvalid_reg || !m_axis_cc_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_cc_tvalid_next = m_axis_cc_tvalid_reg; + temp_m_axis_cc_tvalid_next = temp_m_axis_cc_tvalid_reg; + + store_axis_cc_int_to_output = 1'b0; + store_axis_cc_int_to_temp = 1'b0; + store_axis_cc_temp_to_output = 1'b0; + + if (m_axis_cc_tready_int_reg) begin + // input is ready + if (m_axis_cc_tready || !m_axis_cc_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_cc_tvalid_next = m_axis_cc_tvalid_int; + store_axis_cc_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_cc_tvalid_next = m_axis_cc_tvalid_int; + store_axis_cc_int_to_temp = 1'b1; + end + end else if (m_axis_cc_tready) begin + // input is not ready, but output is ready + m_axis_cc_tvalid_next = temp_m_axis_cc_tvalid_reg; + temp_m_axis_cc_tvalid_next = 1'b0; + store_axis_cc_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_cc_tvalid_reg <= 1'b0; + m_axis_cc_tready_int_reg <= 1'b0; + temp_m_axis_cc_tvalid_reg <= 1'b0; + end else begin + m_axis_cc_tvalid_reg <= m_axis_cc_tvalid_next; + m_axis_cc_tready_int_reg <= m_axis_cc_tready_int_early; + temp_m_axis_cc_tvalid_reg <= temp_m_axis_cc_tvalid_next; + end + + // datapath + if (store_axis_cc_int_to_output) begin + m_axis_cc_tdata_reg <= m_axis_cc_tdata_int; + m_axis_cc_tkeep_reg <= m_axis_cc_tkeep_int; + m_axis_cc_tlast_reg <= m_axis_cc_tlast_int; + m_axis_cc_tuser_reg <= m_axis_cc_tuser_int; + end else if (store_axis_cc_temp_to_output) begin + m_axis_cc_tdata_reg <= temp_m_axis_cc_tdata_reg; + m_axis_cc_tkeep_reg <= temp_m_axis_cc_tkeep_reg; + m_axis_cc_tlast_reg <= temp_m_axis_cc_tlast_reg; + m_axis_cc_tuser_reg <= temp_m_axis_cc_tuser_reg; + end + + if (store_axis_cc_int_to_temp) begin + temp_m_axis_cc_tdata_reg <= m_axis_cc_tdata_int; + temp_m_axis_cc_tkeep_reg <= m_axis_cc_tkeep_int; + temp_m_axis_cc_tlast_reg <= m_axis_cc_tlast_int; + temp_m_axis_cc_tuser_reg <= m_axis_cc_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axi_master_wr.v b/corundum/lib/pcie/rtl/pcie_us_axi_master_wr.v new file mode 100644 index 0000000000000000000000000000000000000000..2fbae9f02852fb2c2dabad2289b63c54106bd6f5 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axi_master_wr.v @@ -0,0 +1,676 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe AXI Master (write) + */ +module pcie_us_axi_master_wr # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream CQ tuser signal width + parameter AXIS_PCIE_CQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 85 : 183, + // Width of AXI data bus in bits + parameter AXI_DATA_WIDTH = AXIS_PCIE_DATA_WIDTH, + // Width of AXI address bus in bits + parameter AXI_ADDR_WIDTH = 64, + // Width of AXI wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Width of AXI ID signal + parameter AXI_ID_WIDTH = 8, + // Maximum AXI burst length to generate + parameter AXI_MAX_BURST_LEN = 256 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (CQ) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_cq_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_cq_tkeep, + input wire s_axis_cq_tvalid, + output wire s_axis_cq_tready, + input wire s_axis_cq_tlast, + input wire [AXIS_PCIE_CQ_USER_WIDTH-1:0] s_axis_cq_tuser, + + /* + * AXI Master output + */ + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + + /* + * Status + */ + output wire status_error_uncor +); + +parameter AXI_WORD_WIDTH = AXI_STRB_WIDTH; +parameter AXI_WORD_SIZE = AXI_DATA_WIDTH/AXI_WORD_WIDTH; +parameter AXI_BURST_SIZE = $clog2(AXI_STRB_WIDTH); +parameter AXI_MAX_BURST_SIZE = AXI_MAX_BURST_LEN*AXI_WORD_WIDTH; + +parameter AXIS_PCIE_WORD_WIDTH = AXIS_PCIE_KEEP_WIDTH; +parameter AXIS_PCIE_WORD_SIZE = AXIS_PCIE_DATA_WIDTH/AXIS_PCIE_WORD_WIDTH; + +parameter OFFSET_WIDTH = $clog2(AXIS_PCIE_DATA_WIDTH/32); + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, 256, or 512 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + if (AXIS_PCIE_CQ_USER_WIDTH != 183) begin + $error("Error: PCIe CQ tuser width must be 183 (instance %m)"); + $finish; + end + end else begin + if (AXIS_PCIE_CQ_USER_WIDTH != 85 && AXIS_PCIE_CQ_USER_WIDTH != 88) begin + $error("Error: PCIe CQ tuser width must be 85 or 88 (instance %m)"); + $finish; + end + end + + if (AXI_DATA_WIDTH != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: AXI interface width must match PCIe interface width (instance %m)"); + $finish; + end + + if (AXI_STRB_WIDTH * 8 != AXI_DATA_WIDTH) begin + $error("Error: AXI interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXI_MAX_BURST_LEN < 1 || AXI_MAX_BURST_LEN > 256) begin + $error("Error: AXI_MAX_BURST_LEN must be between 1 and 256 (instance %m)"); + $finish; + end +end + +localparam [3:0] + REQ_MEM_READ = 4'b0000, + REQ_MEM_WRITE = 4'b0001, + REQ_IO_READ = 4'b0010, + REQ_IO_WRITE = 4'b0011, + REQ_MEM_FETCH_ADD = 4'b0100, + REQ_MEM_SWAP = 4'b0101, + REQ_MEM_CAS = 4'b0110, + REQ_MEM_READ_LOCKED = 4'b0111, + REQ_CFG_READ_0 = 4'b1000, + REQ_CFG_READ_1 = 4'b1001, + REQ_CFG_WRITE_0 = 4'b1010, + REQ_CFG_WRITE_1 = 4'b1011, + REQ_MSG = 4'b1100, + REQ_MSG_VENDOR = 4'b1101, + REQ_MSG_ATS = 4'b1110; + +localparam [2:0] + CPL_STATUS_SC = 3'b000, // successful completion + CPL_STATUS_UR = 3'b001, // unsupported request + CPL_STATUS_CRS = 3'b010, // configuration request retry status + CPL_STATUS_CA = 3'b100; // completer abort + +localparam [1:0] + STATE_IDLE = 2'd0, + STATE_HEADER = 3'd1, + STATE_TRANSFER = 2'd2, + STATE_WAIT_END = 2'd3; + +reg [1:0] state_reg = STATE_IDLE, state_next; + +// datapath control signals +reg transfer_in_save; +reg flush_save; + +reg [AXI_ADDR_WIDTH-1:0] axi_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, axi_addr_next; +reg [9:0] op_dword_count_reg = 10'd0, op_dword_count_next; +reg [9:0] tr_dword_count_reg = 10'd0, tr_dword_count_next; +reg [11:0] input_cycle_count_reg = 12'd0, input_cycle_count_next; +reg [11:0] output_cycle_count_reg = 12'd0, output_cycle_count_next; +reg input_active_reg = 1'b0, input_active_next; +reg bubble_cycle_reg = 1'b0, bubble_cycle_next; +reg first_cycle_reg = 1'b0, first_cycle_next; +reg last_cycle_reg = 1'b0, last_cycle_next; + +reg [3:0] type_reg = 4'd0, type_next; +reg [3:0] first_be_reg = 4'd0, first_be_next; +reg [3:0] last_be_reg = 4'd0, last_be_next; +reg [OFFSET_WIDTH-1:0] offset_reg = {OFFSET_WIDTH{1'b0}}, offset_next; +reg [OFFSET_WIDTH-1:0] first_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, first_cycle_offset_next; +reg [OFFSET_WIDTH-1:0] last_cycle_offset_reg = {OFFSET_WIDTH{1'b0}}, last_cycle_offset_next; + +reg s_axis_cq_tready_reg = 1'b0, s_axis_cq_tready_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axi_awaddr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axi_awaddr_next; +reg [7:0] m_axi_awlen_reg = 8'd0, m_axi_awlen_next; +reg m_axi_awvalid_reg = 1'b0, m_axi_awvalid_next; + +reg [AXI_DATA_WIDTH-1:0] save_axis_tdata_reg = {AXI_DATA_WIDTH{1'b0}}; + +wire [AXI_DATA_WIDTH-1:0] shift_axis_tdata = {s_axis_cq_tdata, save_axis_tdata_reg} >> ((AXI_STRB_WIDTH/4-offset_reg)*32); + +reg status_error_uncor_reg = 1'b0, status_error_uncor_next; + +// internal datapath +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_int; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_int; +reg m_axi_wvalid_int; +reg m_axi_wready_int_reg = 1'b0; +reg m_axi_wlast_int; +wire m_axi_wready_int_early; + +assign s_axis_cq_tready = s_axis_cq_tready_reg; + +assign m_axi_awid = {AXI_ID_WIDTH{1'b0}}; +assign m_axi_awaddr = m_axi_awaddr_reg; +assign m_axi_awlen = m_axi_awlen_reg; +assign m_axi_awsize = $clog2(AXI_STRB_WIDTH); +assign m_axi_awburst = 2'b01; +assign m_axi_awlock = 1'b0; +assign m_axi_awcache = 4'b0011; +assign m_axi_awprot = 3'b010; +assign m_axi_awvalid = m_axi_awvalid_reg; + +assign m_axi_bready = 1'b1; + +assign status_error_uncor = status_error_uncor_reg; + +always @* begin + state_next = STATE_IDLE; + + transfer_in_save = 1'b0; + + s_axis_cq_tready_next = 1'b0; + + type_next = type_reg; + axi_addr_next = axi_addr_reg; + op_dword_count_next = op_dword_count_reg; + tr_dword_count_next = tr_dword_count_reg; + input_cycle_count_next = input_cycle_count_reg; + output_cycle_count_next = output_cycle_count_reg; + input_active_next = input_active_reg; + bubble_cycle_next = bubble_cycle_reg; + first_cycle_next = first_cycle_reg; + last_cycle_next = last_cycle_reg; + first_be_next = first_be_reg; + last_be_next = last_be_reg; + offset_next = offset_reg; + first_cycle_offset_next = first_cycle_offset_reg; + last_cycle_offset_next = last_cycle_offset_reg; + + m_axi_awaddr_next = m_axi_awaddr_reg; + m_axi_awlen_next = m_axi_awlen_reg; + m_axi_awvalid_next = m_axi_awvalid_reg && !m_axi_awready; + + m_axi_wdata_int = shift_axis_tdata; + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b1}}; + m_axi_wvalid_int = 1'b0; + m_axi_wlast_int = 1'b0; + + status_error_uncor_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state, wait for completion request + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_cq_tready_next = m_axi_wready_int_early && (!m_axi_awvalid || m_axi_awready); + + if (s_axis_cq_tready && s_axis_cq_tvalid) begin + transfer_in_save = 1'b1; + + // header fields + axi_addr_next = {s_axis_cq_tdata[63:2], 2'b00}; + op_dword_count_next = s_axis_cq_tdata[74:64]; + type_next = s_axis_cq_tdata[78:75]; + + // tuser fields + if (AXIS_PCIE_DATA_WIDTH == 512) begin + first_be_next = s_axis_cq_tuser[3:0]; + last_be_next = s_axis_cq_tuser[11:8]; + end else begin + first_be_next = s_axis_cq_tuser[3:0]; + last_be_next = s_axis_cq_tuser[7:4]; + end + + if (op_dword_count_next == 1) begin + // use first_be for both byte enables for single DWORD transfers + last_be_next = first_be_next; + end + + if (op_dword_count_next <= AXI_MAX_BURST_SIZE/4) begin + // packet smaller than max burst size + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = op_dword_count_next; + m_axi_awlen_next = (tr_dword_count_next + axi_addr_next[OFFSET_WIDTH+2-1:2] - 1) >> (AXI_BURST_SIZE-2); + end else begin + // packet larger than max burst size + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = AXI_MAX_BURST_SIZE/4 - axi_addr_next[OFFSET_WIDTH+2-1:2]; + m_axi_awlen_next = (tr_dword_count_next - 1) >> (AXI_BURST_SIZE-2); + end + + m_axi_awaddr_next = axi_addr_next; + + // required DWORD shift to place first DWORD from the TLP payload into proper position on AXI interface + // bubble cycle required if first TLP payload transfer does not fill first AXI transfer + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + offset_next = axi_addr_next[OFFSET_WIDTH+2-1:2] - 4; + bubble_cycle_next = axi_addr_next[OFFSET_WIDTH+2-1:2] < 4; + end else begin + offset_next = axi_addr_next[OFFSET_WIDTH+2-1:2]; + bubble_cycle_next = 1'b0; + end + first_cycle_offset_next = axi_addr_next[OFFSET_WIDTH+2-1:2]; + first_cycle_next = 1'b1; + + // number of bus transfers in TLP, DOWRD count plus payload start DWORD offset, divided by bus width in DWORDS + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + input_cycle_count_next = (tr_dword_count_next + 4 - 1) >> (AXI_BURST_SIZE-2); + end else begin + input_cycle_count_next = (tr_dword_count_next - 1) >> (AXI_BURST_SIZE-2); + end + // number of bus transfers to AXI, DWORD count plus DWORD offset, divided by bus width in DWORDS + output_cycle_count_next = (tr_dword_count_next + axi_addr_next[OFFSET_WIDTH+2-1:2] - 1) >> (AXI_BURST_SIZE-2); + last_cycle_offset_next = axi_addr_next[OFFSET_WIDTH+2-1:2] + tr_dword_count_next; + last_cycle_next = output_cycle_count_next == 0; + input_active_next = 1'b1; + + axi_addr_next = axi_addr_next + (tr_dword_count_next << 2); + op_dword_count_next = op_dword_count_next - tr_dword_count_next; + + if (type_next == REQ_MEM_WRITE) begin + // write request + m_axi_awvalid_next = 1'b1; + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + // some data is transferred with header + input_active_next = input_cycle_count_next > 0; + input_cycle_count_next = input_cycle_count_next - 1; + s_axis_cq_tready_next = 1'b0; + state_next = STATE_TRANSFER; + end else begin + s_axis_cq_tready_next = m_axi_wready_int_early; + state_next = STATE_TRANSFER; + end + end else begin + // invalid request + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + state_next = STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end else begin + state_next = STATE_IDLE; + end + end else begin + s_axis_cq_tready_next = !m_axi_awvalid || m_axi_awready; + + if (s_axis_cq_tready & s_axis_cq_tvalid) begin + // header fields + axi_addr_next = {s_axis_cq_tdata[63:2], 2'b00}; + + // tuser fields + first_be_next = s_axis_cq_tuser[3:0]; + last_be_next = s_axis_cq_tuser[7:4]; + + state_next = STATE_HEADER; + end else begin + state_next = STATE_IDLE; + end + end + end + STATE_HEADER: begin + // header state, store rest of header (64 bit interface only) + s_axis_cq_tready_next = m_axi_wready_int_early; + + if (s_axis_cq_tready && s_axis_cq_tvalid) begin + transfer_in_save = 1'b1; + + // header fields + op_dword_count_next = s_axis_cq_tdata[10:0]; + type_next = s_axis_cq_tdata[14:11]; + + if (op_dword_count_next == 1) begin + // use first_be for both byte enables for single DWORD transfers + last_be_next = first_be_reg; + end + + if (op_dword_count_next <= AXI_MAX_BURST_SIZE/4) begin + // packet smaller than max burst size (only for 64 bits) + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = op_dword_count_next; + end else begin + // packet larger than max burst size + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = AXI_MAX_BURST_SIZE/4 - axi_addr_reg[OFFSET_WIDTH+2-1:2]; + end + + // required DWORD shift to place first DWORD from the TLP payload into proper position on AXI interface + // bubble cycle required if first TLP payload transfer does not fill first AXI transfer + offset_next = axi_addr_reg[OFFSET_WIDTH+2-1:2]; + bubble_cycle_next = 1'b0; + first_cycle_offset_next = axi_addr_reg[OFFSET_WIDTH+2-1:2]; + first_cycle_next = 1'b1; + + // number of bus transfers in TLP, DOWRD count plus payload start DWORD offset, divided by bus width in DWORDS + input_cycle_count_next = (tr_dword_count_next - 1) >> (AXI_BURST_SIZE-2); + // number of bus transfers to AXI, DWORD count plus DWORD offset, divided by bus width in DWORDS + output_cycle_count_next = (tr_dword_count_next + axi_addr_reg[OFFSET_WIDTH+2-1:2] - 1) >> (AXI_BURST_SIZE-2); + last_cycle_offset_next = axi_addr_reg[OFFSET_WIDTH+2-1:2] + tr_dword_count_next; + last_cycle_next = output_cycle_count_next == 0; + input_active_next = 1'b1; + + m_axi_awaddr_next = axi_addr_reg; + m_axi_awlen_next = output_cycle_count_next; + + axi_addr_next = axi_addr_reg + (tr_dword_count_next << 2); + op_dword_count_next = op_dword_count_next - tr_dword_count_next; + + if (type_next == REQ_MEM_WRITE) begin + // write request + m_axi_awvalid_next = 1'b1; + s_axis_cq_tready_next = m_axi_wready_int_early; + state_next = STATE_TRANSFER; + end else begin + // invalid request + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + state_next = STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end else begin + state_next = STATE_HEADER; + end + end + STATE_TRANSFER: begin + // transfer state, transfer data + s_axis_cq_tready_next = m_axi_wready_int_early && input_active_reg && !(AXIS_PCIE_DATA_WIDTH >= 256 && first_cycle_reg && !bubble_cycle_reg); + + if (m_axi_wready_int_reg && ((s_axis_cq_tready && s_axis_cq_tvalid) || !input_active_reg || (AXIS_PCIE_DATA_WIDTH >= 256 && first_cycle_reg && !bubble_cycle_reg))) begin + transfer_in_save = s_axis_cq_tready && s_axis_cq_tvalid; + + // transfer data + if (AXIS_PCIE_DATA_WIDTH >= 256 && first_cycle_reg && !bubble_cycle_reg) begin + m_axi_wdata_int = {save_axis_tdata_reg, {AXIS_PCIE_DATA_WIDTH{1'b0}}} >> ((AXI_STRB_WIDTH/4-offset_reg)*32); + s_axis_cq_tready_next = m_axi_wready_int_early && input_active_reg; + end else begin + m_axi_wdata_int = shift_axis_tdata; + end + // generate strb signal + if (first_cycle_reg) begin + m_axi_wstrb_int = {{AXI_STRB_WIDTH-4{1'b1}}, first_be_reg} << (first_cycle_offset_reg*4); + end else begin + m_axi_wstrb_int = {AXI_STRB_WIDTH{1'b1}}; + end + + // update cycle counters + if (input_active_reg && !(AXIS_PCIE_DATA_WIDTH >= 256 && first_cycle_reg && !bubble_cycle_reg)) begin + input_cycle_count_next = input_cycle_count_reg - 1; + input_active_next = input_cycle_count_reg > 0; + end + output_cycle_count_next = output_cycle_count_reg - 1; + last_cycle_next = output_cycle_count_next == 0; + + // modify strb signal at end of transfer + if (last_cycle_reg) begin + if (op_dword_count_reg == 0) begin + if (last_cycle_offset_reg > 0) begin + m_axi_wstrb_int = m_axi_wstrb_int & {last_be_reg, {AXI_STRB_WIDTH-4{1'b1}}} >> (AXI_STRB_WIDTH-last_cycle_offset_reg*4); + end else begin + m_axi_wstrb_int = m_axi_wstrb_int & {last_be_reg, {AXI_STRB_WIDTH-4{1'b1}}}; + end + end + m_axi_wlast_int = 1'b1; + end + m_axi_wvalid_int = 1'b1; + first_cycle_next = 1'b0; + if (!last_cycle_reg) begin + s_axis_cq_tready_next = m_axi_wready_int_early && input_active_next; + state_next = STATE_TRANSFER; + end else if (op_dword_count_reg > 0) begin + // current transfer done, but operation not finished yet + if (op_dword_count_reg <= AXI_MAX_BURST_SIZE/4) begin + // packet smaller than max burst size + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = op_dword_count_reg; + m_axi_awlen_next = (tr_dword_count_next + axi_addr_reg[OFFSET_WIDTH+2-1:2] - 1) >> (AXI_BURST_SIZE-2); + end else begin + // packet larger than max burst size + // assumed to not cross 4k boundary, send one request + tr_dword_count_next = AXI_MAX_BURST_SIZE/4 - axi_addr_reg[OFFSET_WIDTH+2-1:2]; + m_axi_awlen_next = (tr_dword_count_next - 1) >> (AXI_BURST_SIZE-2); + end + + m_axi_awaddr_next = axi_addr_reg; + + // keep offset, no bubble cycles, not first cycle + bubble_cycle_next = 1'b0; + first_cycle_next = 1'b0; + + // number of bus transfers in TLP, DOWRD count minus payload start DWORD offset, divided by bus width in DWORDS + input_cycle_count_next = (tr_dword_count_next - offset_reg - 1) >> (AXI_BURST_SIZE-2); + // number of bus transfers to AXI, DWORD count plus DWORD offset, divided by bus width in DWORDS + output_cycle_count_next = (tr_dword_count_next + axi_addr_reg[OFFSET_WIDTH+2-1:2] - 1) >> (AXI_BURST_SIZE-2); + last_cycle_offset_next = axi_addr_reg[OFFSET_WIDTH+2-1:2] + tr_dword_count_next; + last_cycle_next = output_cycle_count_next == 0; + input_active_next = tr_dword_count_next > offset_reg; + + axi_addr_next = axi_addr_reg + (tr_dword_count_next << 2); + op_dword_count_next = op_dword_count_reg - tr_dword_count_next; + + m_axi_awvalid_next = 1'b1; + s_axis_cq_tready_next = m_axi_wready_int_early && input_active_next; + state_next = STATE_TRANSFER; + end else begin + s_axis_cq_tready_next = m_axi_wready_int_early && (!m_axi_awvalid || m_axi_awready); + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_TRANSFER; + end + end + STATE_WAIT_END: begin + // wait end state, wait for end of TLP + s_axis_cq_tready_next = 1'b1; + + if (s_axis_cq_tready & s_axis_cq_tvalid) begin + if (s_axis_cq_tlast) begin + if (AXIS_PCIE_DATA_WIDTH > 64) begin + s_axis_cq_tready_next = m_axi_wready_int_early && (!m_axi_awvalid || m_axi_awready); + end else begin + s_axis_cq_tready_next = 1'b1; + end + state_next = STATE_IDLE; + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axis_cq_tready_reg <= 1'b0; + + m_axi_awvalid_reg <= 1'b0; + + status_error_uncor_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axis_cq_tready_reg <= s_axis_cq_tready_next; + + m_axi_awvalid_reg <= m_axi_awvalid_next; + + status_error_uncor_reg <= status_error_uncor_next; + end + + axi_addr_reg <= axi_addr_next; + op_dword_count_reg <= op_dword_count_next; + tr_dword_count_reg <= tr_dword_count_next; + input_cycle_count_reg <= input_cycle_count_next; + output_cycle_count_reg <= output_cycle_count_next; + input_active_reg <= input_active_next; + bubble_cycle_reg <= bubble_cycle_next; + first_cycle_reg <= first_cycle_next; + last_cycle_reg <= last_cycle_next; + + type_reg <= type_next; + first_be_reg <= first_be_next; + last_be_reg <= last_be_next; + offset_reg <= offset_next; + first_cycle_offset_reg <= first_cycle_offset_next; + last_cycle_offset_reg <= last_cycle_offset_next; + + m_axi_awaddr_reg <= m_axi_awaddr_next; + m_axi_awlen_reg <= m_axi_awlen_next; + + if (transfer_in_save) begin + save_axis_tdata_reg <= s_axis_cq_tdata; + end +end + +// output datapath logic (AXI write data) +reg [AXI_DATA_WIDTH-1:0] m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg m_axi_wvalid_reg = 1'b0, m_axi_wvalid_next; +reg m_axi_wlast_reg = 1'b0; + +reg [AXI_DATA_WIDTH-1:0] temp_m_axi_wdata_reg = {AXI_DATA_WIDTH{1'b0}}; +reg [AXI_STRB_WIDTH-1:0] temp_m_axi_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}; +reg temp_m_axi_wvalid_reg = 1'b0, temp_m_axi_wvalid_next; +reg temp_m_axi_wlast_reg = 1'b0; + +// datapath control +reg store_axi_w_int_to_output; +reg store_axi_w_int_to_temp; +reg store_axi_w_temp_to_output; + +assign m_axi_wdata = m_axi_wdata_reg; +assign m_axi_wstrb = m_axi_wstrb_reg; +assign m_axi_wvalid = m_axi_wvalid_reg; +assign m_axi_wlast = m_axi_wlast_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axi_wready_int_early = m_axi_wready || (!temp_m_axi_wvalid_reg && (!m_axi_wvalid_reg || !m_axi_wvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axi_wvalid_next = m_axi_wvalid_reg; + temp_m_axi_wvalid_next = temp_m_axi_wvalid_reg; + + store_axi_w_int_to_output = 1'b0; + store_axi_w_int_to_temp = 1'b0; + store_axi_w_temp_to_output = 1'b0; + + if (m_axi_wready_int_reg) begin + // input is ready + if (m_axi_wready || !m_axi_wvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axi_wvalid_next = m_axi_wvalid_int; + store_axi_w_int_to_temp = 1'b1; + end + end else if (m_axi_wready) begin + // input is not ready, but output is ready + m_axi_wvalid_next = temp_m_axi_wvalid_reg; + temp_m_axi_wvalid_next = 1'b0; + store_axi_w_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axi_wvalid_reg <= 1'b0; + m_axi_wready_int_reg <= 1'b0; + temp_m_axi_wvalid_reg <= 1'b0; + end else begin + m_axi_wvalid_reg <= m_axi_wvalid_next; + m_axi_wready_int_reg <= m_axi_wready_int_early; + temp_m_axi_wvalid_reg <= temp_m_axi_wvalid_next; + end + + // datapath + if (store_axi_w_int_to_output) begin + m_axi_wdata_reg <= m_axi_wdata_int; + m_axi_wstrb_reg <= m_axi_wstrb_int; + m_axi_wlast_reg <= m_axi_wlast_int; + end else if (store_axi_w_temp_to_output) begin + m_axi_wdata_reg <= temp_m_axi_wdata_reg; + m_axi_wstrb_reg <= temp_m_axi_wstrb_reg; + m_axi_wlast_reg <= temp_m_axi_wlast_reg; + end + + if (store_axi_w_int_to_temp) begin + temp_m_axi_wdata_reg <= m_axi_wdata_int; + temp_m_axi_wstrb_reg <= m_axi_wstrb_int; + temp_m_axi_wlast_reg <= m_axi_wlast_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axil_master.v b/corundum/lib/pcie/rtl/pcie_us_axil_master.v new file mode 100644 index 0000000000000000000000000000000000000000..df543f3b6a38e365d6f3c325b763e59c762da525 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axil_master.v @@ -0,0 +1,933 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe AXI Lite Master + */ +module pcie_us_axil_master # +( + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream CQ tuser signal width + parameter AXIS_PCIE_CQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 85 : 183, + // PCIe AXI stream CC tuser signal width + parameter AXIS_PCIE_CC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 33 : 81, + // Width of AXI lite data bus in bits + parameter AXI_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXI_ADDR_WIDTH = 64, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + // Enable parity + parameter ENABLE_PARITY = 0 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (CQ) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_cq_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_cq_tkeep, + input wire s_axis_cq_tvalid, + output wire s_axis_cq_tready, + input wire s_axis_cq_tlast, + input wire [AXIS_PCIE_CQ_USER_WIDTH-1:0] s_axis_cq_tuser, + + /* + * AXI output (CC) + */ + output wire [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cc_tdata, + output wire [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cc_tkeep, + output wire m_axis_cc_tvalid, + input wire m_axis_cc_tready, + output wire m_axis_cc_tlast, + output wire [AXIS_PCIE_CC_USER_WIDTH-1:0] m_axis_cc_tuser, + + /* + * AXI Lite Master output + */ + output wire [AXI_ADDR_WIDTH-1:0] m_axil_awaddr, + output wire [2:0] m_axil_awprot, + output wire m_axil_awvalid, + input wire m_axil_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axil_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axil_wstrb, + output wire m_axil_wvalid, + input wire m_axil_wready, + input wire [1:0] m_axil_bresp, + input wire m_axil_bvalid, + output wire m_axil_bready, + output wire [AXI_ADDR_WIDTH-1:0] m_axil_araddr, + output wire [2:0] m_axil_arprot, + output wire m_axil_arvalid, + input wire m_axil_arready, + input wire [AXI_DATA_WIDTH-1:0] m_axil_rdata, + input wire [1:0] m_axil_rresp, + input wire m_axil_rvalid, + output wire m_axil_rready, + + /* + * Configuration + */ + input wire [15:0] completer_id, + input wire completer_id_enable, + + /* + * Status + */ + output wire status_error_cor, + output wire status_error_uncor +); + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, 256, or 512 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + if (AXIS_PCIE_CQ_USER_WIDTH != 183) begin + $error("Error: PCIe CQ tuser width must be 183 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_CC_USER_WIDTH != 81) begin + $error("Error: PCIe CC tuser width must be 81 (instance %m)"); + $finish; + end + end else begin + if (AXIS_PCIE_CQ_USER_WIDTH != 85 && AXIS_PCIE_CQ_USER_WIDTH != 88) begin + $error("Error: PCIe CQ tuser width must be 85 or 88 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_CC_USER_WIDTH != 33) begin + $error("Error: PCIe CC tuser width must be 33 (instance %m)"); + $finish; + end + end + + if (AXI_DATA_WIDTH != 32) begin + $error("Error: AXI interface width must be 32 (instance %m)"); + $finish; + end + + if (AXI_STRB_WIDTH * 8 != AXI_DATA_WIDTH) begin + $error("Error: AXI interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +localparam [3:0] + REQ_MEM_READ = 4'b0000, + REQ_MEM_WRITE = 4'b0001, + REQ_IO_READ = 4'b0010, + REQ_IO_WRITE = 4'b0011, + REQ_MEM_FETCH_ADD = 4'b0100, + REQ_MEM_SWAP = 4'b0101, + REQ_MEM_CAS = 4'b0110, + REQ_MEM_READ_LOCKED = 4'b0111, + REQ_CFG_READ_0 = 4'b1000, + REQ_CFG_READ_1 = 4'b1001, + REQ_CFG_WRITE_0 = 4'b1010, + REQ_CFG_WRITE_1 = 4'b1011, + REQ_MSG = 4'b1100, + REQ_MSG_VENDOR = 4'b1101, + REQ_MSG_ATS = 4'b1110; + +localparam [2:0] + CPL_STATUS_SC = 3'b000, // successful completion + CPL_STATUS_UR = 3'b001, // unsupported request + CPL_STATUS_CRS = 3'b010, // configuration request retry status + CPL_STATUS_CA = 3'b100; // completer abort + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_HEADER = 3'd1, + STATE_READ = 3'd2, + STATE_WRITE_1 = 3'd3, + STATE_WRITE_2 = 3'd4, + STATE_WAIT_END = 3'd5, + STATE_CPL_1 = 3'd6, + STATE_CPL_2 = 3'd7; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg [1:0] at_reg = 2'd0, at_next; +reg [10:0] dword_count_reg = 11'd0, dword_count_next; +reg [3:0] type_reg = 4'd0, type_next; +reg [2:0] status_reg = 3'b000, status_next; +reg [15:0] requester_id_reg = 16'd0, requester_id_next; +reg [7:0] tag_reg = 7'd0, tag_next; +reg [2:0] tc_reg = 3'd0, tc_next; +reg [2:0] attr_reg = 3'd0, attr_next; +reg [3:0] first_be_reg = 4'd0, first_be_next; +reg [3:0] last_be_reg = 4'd0, last_be_next; +reg cpl_data_reg = 1'b0, cpl_data_next; + +reg s_axis_cq_tready_reg = 1'b0, s_axis_cq_tready_next; + +reg [AXI_ADDR_WIDTH-1:0] m_axil_addr_reg = {AXI_ADDR_WIDTH{1'b0}}, m_axil_addr_next; +reg m_axil_awvalid_reg = 1'b0, m_axil_awvalid_next; +reg [AXI_DATA_WIDTH-1:0] m_axil_wdata_reg = {AXI_DATA_WIDTH{1'b0}}, m_axil_wdata_next; +reg [AXI_STRB_WIDTH-1:0] m_axil_wstrb_reg = {AXI_STRB_WIDTH{1'b0}}, m_axil_wstrb_next; +reg m_axil_wvalid_reg = 1'b0, m_axil_wvalid_next; +reg m_axil_bready_reg = 1'b0, m_axil_bready_next; +reg m_axil_arvalid_reg = 1'b0, m_axil_arvalid_next; +reg m_axil_rready_reg = 1'b0, m_axil_rready_next; + +reg status_error_cor_reg = 1'b0, status_error_cor_next; +reg status_error_uncor_reg = 1'b0, status_error_uncor_next; + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cc_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cc_tkeep_int; +reg m_axis_cc_tvalid_int; +reg m_axis_cc_tready_int_reg = 1'b0; +reg m_axis_cc_tlast_int; +reg [AXIS_PCIE_CC_USER_WIDTH-1:0] m_axis_cc_tuser_int; +wire m_axis_cc_tready_int_early; + +assign s_axis_cq_tready = s_axis_cq_tready_reg; + +assign m_axil_awaddr = m_axil_addr_reg; +assign m_axil_awprot = 3'b010; +assign m_axil_awvalid = m_axil_awvalid_reg; +assign m_axil_wdata = m_axil_wdata_reg; +assign m_axil_wstrb = m_axil_wstrb_reg; +assign m_axil_wvalid = m_axil_wvalid_reg; +assign m_axil_bready = m_axil_bready_reg; +assign m_axil_araddr = m_axil_addr_reg; +assign m_axil_arprot = 3'b010; +assign m_axil_arvalid = m_axil_arvalid_reg; +assign m_axil_rready = m_axil_rready_reg; + +assign status_error_cor = status_error_cor_reg; +assign status_error_uncor = status_error_uncor_reg; + +always @* begin + state_next = STATE_IDLE; + + s_axis_cq_tready_next = 1'b0; + + at_next = at_reg; + dword_count_next = dword_count_reg; + type_next = type_reg; + status_next = status_reg; + requester_id_next = requester_id_reg; + tag_next = tag_reg; + tc_next = tc_reg; + attr_next = attr_reg; + first_be_next = first_be_reg; + last_be_next = last_be_reg; + cpl_data_next = cpl_data_reg; + + m_axis_cc_tdata_int = {AXIS_PCIE_DATA_WIDTH{1'b0}}; + m_axis_cc_tkeep_int = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; + m_axis_cc_tvalid_int = 1'b0; + m_axis_cc_tlast_int = 1'b0; + m_axis_cc_tuser_int = {AXIS_PCIE_CC_USER_WIDTH{1'b0}}; + + casez (first_be_reg) + 4'b0000: m_axis_cc_tdata_int[6:0] = {m_axil_addr_reg[6:2], 2'b00}; // lower address + 4'bzzz1: m_axis_cc_tdata_int[6:0] = {m_axil_addr_reg[6:2], 2'b00}; // lower address + 4'bzz10: m_axis_cc_tdata_int[6:0] = {m_axil_addr_reg[6:2], 2'b01}; // lower address + 4'bz100: m_axis_cc_tdata_int[6:0] = {m_axil_addr_reg[6:2], 2'b10}; // lower address + 4'b1000: m_axis_cc_tdata_int[6:0] = {m_axil_addr_reg[6:2], 2'b11}; // lower address + endcase + m_axis_cc_tdata_int[9:8] = at_reg; + casez (first_be_reg) + 4'b0000: m_axis_cc_tdata_int[28:16] = 13'd0; // Byte count + 4'b0001: m_axis_cc_tdata_int[28:16] = 13'd1; // Byte count + 4'b0010: m_axis_cc_tdata_int[28:16] = 13'd1; // Byte count + 4'b0100: m_axis_cc_tdata_int[28:16] = 13'd1; // Byte count + 4'b1000: m_axis_cc_tdata_int[28:16] = 13'd1; // Byte count + 4'b0011: m_axis_cc_tdata_int[28:16] = 13'd2; // Byte count + 4'b0110: m_axis_cc_tdata_int[28:16] = 13'd2; // Byte count + 4'b1100: m_axis_cc_tdata_int[28:16] = 13'd2; // Byte count + 4'b01z1: m_axis_cc_tdata_int[28:16] = 13'd3; // Byte count + 4'b1z10: m_axis_cc_tdata_int[28:16] = 13'd3; // Byte count + 4'b1zz1: m_axis_cc_tdata_int[28:16] = 13'd4; // Byte count + endcase + m_axis_cc_tdata_int[42:32] = 11'd1; // DWORD count + m_axis_cc_tdata_int[45:43] = status_reg; + m_axis_cc_tdata_int[63:48] = requester_id_reg; + if (AXIS_PCIE_DATA_WIDTH > 64) begin + m_axis_cc_tdata_int[71:64] = tag_reg; + m_axis_cc_tdata_int[87:72] = completer_id; + m_axis_cc_tdata_int[88] = completer_id_enable; + m_axis_cc_tdata_int[91:89] = tc_reg; + m_axis_cc_tdata_int[94:92] = attr_reg; + m_axis_cc_tdata_int[95] = 1'b0; // force ECRC + m_axis_cc_tdata_int[127:96] = m_axil_rdata; + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tuser_int[1:0] = 2'b01; // is_sop + m_axis_cc_tuser_int[3:2] = 2'd0; // is_sop0_ptr + m_axis_cc_tuser_int[5:4] = 2'd0; // is_sop1_ptr + m_axis_cc_tuser_int[7:6] = 2'b01; // is_eop + m_axis_cc_tuser_int[11:8] = 4'd3; // is_eop0_ptr + m_axis_cc_tuser_int[15:12] = 4'd0; // is_eop1_ptr + m_axis_cc_tuser_int[16] = 1'b0; // discontinue + m_axis_cc_tuser_int[80:17] = 64'd0; // parity + end else begin + m_axis_cc_tuser_int[0] = 1'b0; // discontinue + m_axis_cc_tuser_int[32:1] = 32'd0; // parity + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tkeep_int = 16'b0000000000001111; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tkeep_int = 8'b00001111; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_cc_tkeep_int = 4'b1111; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cc_tkeep_int = 2'b11; + end + + m_axil_addr_next = m_axil_addr_reg; + m_axil_awvalid_next = m_axil_awvalid_reg && !m_axil_awready; + m_axil_wdata_next = m_axil_wdata_reg; + m_axil_wstrb_next = m_axil_wstrb_reg; + m_axil_wvalid_next = m_axil_wvalid_reg && !m_axil_wready; + m_axil_bready_next = 1'b0; + m_axil_arvalid_next = m_axil_arvalid_reg && !m_axil_arready; + m_axil_rready_next = 1'b0; + + status_error_cor_next = 1'b0; + status_error_uncor_next = 1'b0; + + case (state_reg) + STATE_IDLE: begin + // idle state, wait for completion request + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + + if (s_axis_cq_tready && s_axis_cq_tvalid) begin + // header fields + at_next = s_axis_cq_tdata[1:0]; + m_axil_addr_next = {s_axis_cq_tdata[63:2], 2'b00}; + if (AXIS_PCIE_DATA_WIDTH > 64) begin + dword_count_next = s_axis_cq_tdata[74:64]; + type_next = s_axis_cq_tdata[78:75]; + requester_id_next = s_axis_cq_tdata[95:80]; + tag_next = s_axis_cq_tdata[103:96]; + tc_next = s_axis_cq_tdata[123:121]; + attr_next = s_axis_cq_tdata[126:124]; + + // data + if (AXIS_PCIE_DATA_WIDTH >= 256) begin + m_axil_wdata_next = s_axis_cq_tdata[159:128]; + end + end + + // tuser fields + if (AXIS_PCIE_DATA_WIDTH == 512) begin + first_be_next = s_axis_cq_tuser[3:0]; + last_be_next = s_axis_cq_tuser[11:8]; + end else begin + first_be_next = s_axis_cq_tuser[3:0]; + last_be_next = s_axis_cq_tuser[7:4]; + end + + m_axil_wstrb_next = first_be_next; + + status_next = CPL_STATUS_SC; // successful completion + + if (AXIS_PCIE_DATA_WIDTH == 64) begin + if (s_axis_cq_tlast) begin + // truncated packet + // report uncorrectable error + status_error_uncor_next = 1'b1; + state_next = STATE_IDLE; + end else begin + state_next = STATE_HEADER; + end + end else begin + if (type_next == REQ_MEM_READ || type_next == REQ_IO_READ) begin + // read request + if (s_axis_cq_tlast && dword_count_next == 11'd1) begin + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = m_axis_cc_tready_int_early; + s_axis_cq_tready_next = 1'b0; + state_next = STATE_READ; + end else begin + // bad length + status_next = CPL_STATUS_CA; // completer abort + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = 1'b0; + state_next = STATE_CPL_1; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end else if (type_next == REQ_MEM_WRITE || type_next == REQ_IO_WRITE) begin + // write request + if (AXIS_PCIE_DATA_WIDTH >= 256 && s_axis_cq_tlast && dword_count_next == 11'd1) begin + m_axil_awvalid_next = 1'b1; + m_axil_wvalid_next = 1'b1; + m_axil_bready_next = 1'b1; + s_axis_cq_tready_next = 1'b0; + state_next = STATE_WRITE_2; + end else if (AXIS_PCIE_DATA_WIDTH < 256 && dword_count_next == 11'd1) begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WRITE_1; + end else begin + // bad length + status_next = CPL_STATUS_CA; // completer abort + if (type_next == REQ_MEM_WRITE) begin + // memory write - posted, no completion + // report uncorrectable error + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end else begin + // IO write - non-posted, send completion + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = 1'b0; + state_next = STATE_CPL_1; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end + end else begin + // other request + status_next = CPL_STATUS_UR; // unsupported request + if (type_next == REQ_MEM_WRITE || (type_next & 4'b1100) == 4'b1100) begin + // memory write or message - posted, no completion + // report uncorrectable error + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end else begin + // other non-posted request, send UR completion + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = 1'b0; + state_next = STATE_CPL_1; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end + end + end else begin + state_next = STATE_IDLE; + end + end + STATE_HEADER: begin + // header state, handle header (64 bit interface only) + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + + // header fields + dword_count_next = s_axis_cq_tdata[10:0]; + type_next = s_axis_cq_tdata[14:11]; + requester_id_next = s_axis_cq_tdata[31:16]; + tag_next = s_axis_cq_tdata[39:32]; + tc_next = s_axis_cq_tdata[59:57]; + attr_next = s_axis_cq_tdata[62:60]; + + // data + m_axil_wstrb_next = first_be_reg; + + if (s_axis_cq_tready && s_axis_cq_tvalid) begin + if (type_next == REQ_MEM_READ || type_next == REQ_IO_READ) begin + // read request + if (s_axis_cq_tlast && dword_count_next == 11'd1) begin + m_axil_arvalid_next = 1'b1; + m_axil_rready_next = m_axis_cc_tready_int_early; + s_axis_cq_tready_next = 1'b0; + state_next = STATE_READ; + end else begin + // bad length + status_next = CPL_STATUS_CA; // completer abort + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = 1'b0; + state_next = STATE_CPL_1; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end else if (type_next == REQ_MEM_WRITE || type_next == REQ_IO_WRITE) begin + // write request + if (dword_count_next == 11'd1) begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WRITE_1; + end else begin + // bad length + status_next = CPL_STATUS_CA; // completer abort + if (type_next == REQ_MEM_WRITE) begin + // memory write - posted, no completion + // report uncorrectable error + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end else begin + // other non-posted request, send UR completion + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = 1'b0; + state_next = STATE_CPL_1; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end + end else begin + // other request + status_next = CPL_STATUS_UR; // unsupported request + if (type_next == REQ_MEM_WRITE || (type_next & 4'b1100) == 4'b1100) begin + // memory write or message - posted, no completion + // report uncorrectable error + status_error_uncor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end else begin + // other non-posted request, send UR completion + // report correctable error + status_error_cor_next = 1'b1; + if (s_axis_cq_tlast) begin + s_axis_cq_tready_next = 1'b0; + state_next = STATE_CPL_1; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end + end + end else begin + state_next = STATE_HEADER; + end + end + STATE_READ: begin + // read state, wait for read response + m_axil_rready_next = m_axis_cc_tready_int_early; + + m_axis_cc_tdata_int[42:32] = 11'd1; // DWORD count + m_axis_cc_tdata_int[45:43] = CPL_STATUS_SC; // status: successful completion + m_axis_cc_tdata_int[127:96] = m_axil_rdata; + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tuser_int[7:6] = 2'b01; // is_eop + m_axis_cc_tuser_int[11:8] = 4'd3; // is_eop0_ptr + m_axis_cc_tuser_int[15:12] = 4'd0; // is_eop1_ptr + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tkeep_int = 16'b0000000000001111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tkeep_int = 8'b00001111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_cc_tkeep_int = 4'b1111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cc_tkeep_int = 2'b11; + m_axis_cc_tlast_int = 1'b0; + end + + if (m_axil_rready && m_axil_rvalid) begin + // send completion + m_axis_cc_tvalid_int = 1'b1; + + m_axil_rready_next = 1'b0; + if (AXIS_PCIE_DATA_WIDTH == 64) begin + cpl_data_next = 1'b1; + state_next = STATE_CPL_2; + end else begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_READ; + end + end + STATE_WRITE_1: begin + // write 1 state, store write data and initiate write + s_axis_cq_tready_next = 1'b1; + + // data + m_axil_wdata_next = s_axis_cq_tdata[31:0]; + + if (s_axis_cq_tready && s_axis_cq_tvalid) begin + if (s_axis_cq_tlast) begin + m_axil_awvalid_next = 1'b1; + m_axil_wvalid_next = 1'b1; + m_axil_bready_next = m_axis_cc_tready_int_early; + s_axis_cq_tready_next = 1'b0; + state_next = STATE_WRITE_2; + end else begin + s_axis_cq_tready_next = 1'b1; + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WRITE_1; + end + end + STATE_WRITE_2: begin + // write 2 state, handle write response + m_axil_bready_next = m_axis_cc_tready_int_early; + + if (m_axil_bready && m_axil_bvalid) begin + m_axil_bready_next = 1'b0; + if (type_reg == REQ_MEM_WRITE) begin + // memory write - posted, no completion + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end else begin + // IO write - non-posted, send completion + m_axis_cc_tvalid_int = 1'b1; + m_axis_cc_tdata_int[42:32] = 11'd0; // DWORD count + m_axis_cc_tdata_int[45:43] = CPL_STATUS_SC; // status: successful completion + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tuser_int[7:6] = 2'b01; // is_eop + m_axis_cc_tuser_int[11:8] = 4'd2; // is_eop0_ptr + m_axis_cc_tuser_int[15:12] = 4'd0; // is_eop1_ptr + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tkeep_int = 16'b0000000000000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tkeep_int = 8'b00000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_cc_tkeep_int = 4'b0111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cc_tkeep_int = 2'b11; + m_axis_cc_tlast_int = 1'b0; + end + + if (AXIS_PCIE_DATA_WIDTH == 64) begin + cpl_data_next = 1'b0; + state_next = STATE_CPL_2; + end else begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end + end + end else begin + state_next = STATE_WRITE_2; + end + end + STATE_WAIT_END: begin + // wait end state, wait for end of completion request + s_axis_cq_tready_next = 1'b1; + + if (s_axis_cq_tready && s_axis_cq_tvalid) begin + if (s_axis_cq_tlast) begin + // completion + if (type_reg == REQ_MEM_WRITE || (type_reg & 4'b1100) == 4'b1100) begin + // memory write or message - posted, no completion + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end else begin + // IO write - non-posted, send completion + m_axis_cc_tvalid_int = 1'b1; + m_axis_cc_tdata_int[42:32] = 11'd0; // DWORD count + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tuser_int[7:6] = 2'b01; // is_eop + m_axis_cc_tuser_int[11:8] = 4'd2; // is_eop0_ptr + m_axis_cc_tuser_int[15:12] = 4'd0; // is_eop1_ptr + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tkeep_int = 16'b0000000000000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tkeep_int = 8'b00000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_cc_tkeep_int = 4'b0111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cc_tkeep_int = 2'b11; + m_axis_cc_tlast_int = 1'b0; + end + + if (m_axis_cc_tready_int_reg) begin + if (AXIS_PCIE_DATA_WIDTH == 64) begin + cpl_data_next = 1'b0; + state_next = STATE_CPL_2; + end else begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_CPL_1; + end + end + end else begin + state_next = STATE_WAIT_END; + end + end else begin + state_next = STATE_WAIT_END; + end + end + STATE_CPL_1: begin + // send completion + m_axis_cc_tvalid_int = 1'b1; + m_axis_cc_tdata_int[28:16] = 13'd0; // byte count + m_axis_cc_tdata_int[42:32] = 11'd0; // DWORD count + m_axis_cc_tdata_int[45:43] = status_reg; + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tuser_int[7:6] = 2'b01; // is_eop + m_axis_cc_tuser_int[11:8] = 4'd2; // is_eop0_ptr + m_axis_cc_tuser_int[15:12] = 4'd0; // is_eop1_ptr + end + + if (AXIS_PCIE_DATA_WIDTH == 512) begin + m_axis_cc_tkeep_int = 16'b0000000000000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 256) begin + m_axis_cc_tkeep_int = 8'b00000111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 128) begin + m_axis_cc_tkeep_int = 4'b0111; + m_axis_cc_tlast_int = 1'b1; + end else if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cc_tkeep_int = 2'b11; + m_axis_cc_tlast_int = 1'b0; + end + + if (m_axis_cc_tready_int_reg) begin + if (AXIS_PCIE_DATA_WIDTH == 64) begin + cpl_data_next = 1'b0; + state_next = STATE_CPL_2; + end else begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end + end else begin + state_next = STATE_CPL_1; + end + end + STATE_CPL_2: begin + // send rest of completion + m_axis_cc_tvalid_int = 1'b1; + m_axis_cc_tdata_int[7:0] = tag_reg; + m_axis_cc_tdata_int[23:8] = completer_id; + m_axis_cc_tdata_int[24] = completer_id_enable; + m_axis_cc_tdata_int[27:25] = tc_reg; + m_axis_cc_tdata_int[30:28] = attr_reg; + m_axis_cc_tdata_int[31] = 1'b0; // force ECRC + m_axis_cc_tdata_int[63:32] = m_axil_rdata; + m_axis_cc_tkeep_int = {cpl_data_reg, 1'b1}; + m_axis_cc_tlast_int = 1'b1; + + if (m_axis_cc_tready_int_reg) begin + s_axis_cq_tready_next = m_axis_cc_tready_int_early; + state_next = STATE_IDLE; + end else begin + state_next = STATE_CPL_2; + end + end + endcase +end + +always @(posedge clk) begin + if (rst) begin + state_reg <= STATE_IDLE; + s_axis_cq_tready_reg <= 1'b0; + + m_axil_awvalid_reg <= 1'b0; + m_axil_wvalid_reg <= 1'b0; + m_axil_bready_reg <= 1'b0; + m_axil_arvalid_reg <= 1'b0; + m_axil_rready_reg <= 1'b0; + + status_error_cor_reg <= 1'b0; + status_error_uncor_reg <= 1'b0; + end else begin + state_reg <= state_next; + s_axis_cq_tready_reg <= s_axis_cq_tready_next; + + m_axil_awvalid_reg <= m_axil_awvalid_next; + m_axil_wvalid_reg <= m_axil_wvalid_next; + m_axil_bready_reg <= m_axil_bready_next; + m_axil_arvalid_reg <= m_axil_arvalid_next; + m_axil_rready_reg <= m_axil_rready_next; + + status_error_cor_reg <= status_error_cor_next; + status_error_uncor_reg <= status_error_uncor_next; + end + + at_reg <= at_next; + dword_count_reg <= dword_count_next; + type_reg <= type_next; + tag_reg <= tag_next; + status_reg <= status_next; + requester_id_reg <= requester_id_next; + tc_reg <= tc_next; + attr_reg <= attr_next; + first_be_reg <= first_be_next; + last_be_reg <= last_be_next; + cpl_data_reg <= cpl_data_next; + + m_axil_addr_reg <= m_axil_addr_next; + m_axil_wdata_reg <= m_axil_wdata_next; + m_axil_wstrb_reg <= m_axil_wstrb_next; +end + +// output datapath logic +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cc_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cc_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg m_axis_cc_tvalid_reg = 1'b0, m_axis_cc_tvalid_next; +reg m_axis_cc_tlast_reg = 1'b0; +reg [AXIS_PCIE_CC_USER_WIDTH-1:0] m_axis_cc_tuser_reg = {AXIS_PCIE_CC_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_cc_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_cc_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg temp_m_axis_cc_tvalid_reg = 1'b0, temp_m_axis_cc_tvalid_next; +reg temp_m_axis_cc_tlast_reg = 1'b0; +reg [AXIS_PCIE_CC_USER_WIDTH-1:0] temp_m_axis_cc_tuser_reg = {AXIS_PCIE_CC_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_cc_tdata = m_axis_cc_tdata_reg; +assign m_axis_cc_tkeep = m_axis_cc_tkeep_reg; +assign m_axis_cc_tvalid = m_axis_cc_tvalid_reg; +assign m_axis_cc_tlast = m_axis_cc_tlast_reg; +assign m_axis_cc_tuser = m_axis_cc_tuser_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_cc_tready_int_early = m_axis_cc_tready || (!temp_m_axis_cc_tvalid_reg && (!m_axis_cc_tvalid_reg || !m_axis_cc_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_cc_tvalid_next = m_axis_cc_tvalid_reg; + temp_m_axis_cc_tvalid_next = temp_m_axis_cc_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_cc_tready_int_reg) begin + // input is ready + if (m_axis_cc_tready || !m_axis_cc_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_cc_tvalid_next = m_axis_cc_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_cc_tvalid_next = m_axis_cc_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_cc_tready) begin + // input is not ready, but output is ready + m_axis_cc_tvalid_next = temp_m_axis_cc_tvalid_reg; + temp_m_axis_cc_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_cc_tvalid_reg <= 1'b0; + m_axis_cc_tready_int_reg <= 1'b0; + temp_m_axis_cc_tvalid_reg <= 1'b0; + end else begin + m_axis_cc_tvalid_reg <= m_axis_cc_tvalid_next; + m_axis_cc_tready_int_reg <= m_axis_cc_tready_int_early; + temp_m_axis_cc_tvalid_reg <= temp_m_axis_cc_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_cc_tdata_reg <= m_axis_cc_tdata_int; + m_axis_cc_tkeep_reg <= m_axis_cc_tkeep_int; + m_axis_cc_tlast_reg <= m_axis_cc_tlast_int; + m_axis_cc_tuser_reg <= m_axis_cc_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_cc_tdata_reg <= temp_m_axis_cc_tdata_reg; + m_axis_cc_tkeep_reg <= temp_m_axis_cc_tkeep_reg; + m_axis_cc_tlast_reg <= temp_m_axis_cc_tlast_reg; + m_axis_cc_tuser_reg <= temp_m_axis_cc_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_cc_tdata_reg <= m_axis_cc_tdata_int; + temp_m_axis_cc_tkeep_reg <= m_axis_cc_tkeep_int; + temp_m_axis_cc_tlast_reg <= m_axis_cc_tlast_int; + temp_m_axis_cc_tuser_reg <= m_axis_cc_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axis_cq_demux.v b/corundum/lib/pcie/rtl/pcie_us_axis_cq_demux.v new file mode 100644 index 0000000000000000000000000000000000000000..9c11df3c6c0818a1d35525f133b69941a3c829b4 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axis_cq_demux.v @@ -0,0 +1,311 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe CQ demultiplexer + */ +module pcie_us_axis_cq_demux # +( + // Output count + parameter M_COUNT = 2, + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream CQ tuser signal width + parameter AXIS_PCIE_CQ_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 85 : 183 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (CQ) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_cq_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_cq_tkeep, + input wire s_axis_cq_tvalid, + output wire s_axis_cq_tready, + input wire s_axis_cq_tlast, + input wire [AXIS_PCIE_CQ_USER_WIDTH-1:0] s_axis_cq_tuser, + + /* + * AXI output (CQ) + */ + output wire [M_COUNT*AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cq_tdata, + output wire [M_COUNT*AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cq_tkeep, + output wire [M_COUNT-1:0] m_axis_cq_tvalid, + input wire [M_COUNT-1:0] m_axis_cq_tready, + output wire [M_COUNT-1:0] m_axis_cq_tlast, + output wire [M_COUNT*AXIS_PCIE_CQ_USER_WIDTH-1:0] m_axis_cq_tuser, + + /* + * Fields + */ + output wire [3:0] req_type, + output wire [7:0] target_function, + output wire [2:0] bar_id, + output wire [7:0] msg_code, + output wire [2:0] msg_routing, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [M_COUNT-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, 256, or 512 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end +end + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_axis_cq_tready_reg = 1'b0, s_axis_cq_tready_next; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_s_axis_cq_tdata = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_s_axis_cq_tkeep = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg temp_s_axis_cq_tvalid = 1'b0; +reg temp_s_axis_cq_tlast = 1'b0; +reg [AXIS_PCIE_CQ_USER_WIDTH-1:0] temp_s_axis_cq_tuser = {AXIS_PCIE_CQ_USER_WIDTH{1'b0}}; + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cq_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cq_tkeep_int; +reg [M_COUNT-1:0] m_axis_cq_tvalid_int; +reg m_axis_cq_tready_int_reg = 1'b0; +reg m_axis_cq_tlast_int; +reg [AXIS_PCIE_CQ_USER_WIDTH-1:0] m_axis_cq_tuser_int; +wire m_axis_cq_tready_int_early; + +assign s_axis_cq_tready = (s_axis_cq_tready_reg || (AXIS_PCIE_DATA_WIDTH == 64 && !temp_s_axis_cq_tvalid)) && enable; + +assign req_type = AXIS_PCIE_DATA_WIDTH > 64 ? s_axis_cq_tdata[78:75] : s_axis_cq_tdata[14:11]; +assign target_function = AXIS_PCIE_DATA_WIDTH > 64 ? s_axis_cq_tdata[111:104] : s_axis_cq_tdata[47:40]; +assign bar_id = AXIS_PCIE_DATA_WIDTH > 64 ? s_axis_cq_tdata[114:112] : s_axis_cq_tdata[50:48]; +assign msg_code = AXIS_PCIE_DATA_WIDTH > 64 ? s_axis_cq_tdata[111:104] : s_axis_cq_tdata[47:40]; +assign msg_routing = AXIS_PCIE_DATA_WIDTH > 64 ? s_axis_cq_tdata[114:112] : s_axis_cq_tdata[50:48]; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_axis_cq_tready_next = 1'b0; + + if (AXIS_PCIE_DATA_WIDTH == 64) begin + if (temp_s_axis_cq_tvalid && s_axis_cq_tready) begin + // end of frame detection + if (temp_s_axis_cq_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + end else begin + if (s_axis_cq_tvalid && s_axis_cq_tready) begin + // end of frame detection + if (s_axis_cq_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + end + + if (!frame_reg && (AXIS_PCIE_DATA_WIDTH == 64 ? temp_s_axis_cq_tvalid : s_axis_cq_tvalid) && s_axis_cq_tready) begin + // start of frame, grab select value + select_ctl = 0; + drop_ctl = 1'b1; + frame_ctl = 1'b1; + for (i = M_COUNT-1; i >= 0; i = i - 1) begin + if (select[i]) begin + select_ctl = i; + drop_ctl = 1'b0; + end + end + drop_ctl = drop_ctl || drop; + if (AXIS_PCIE_DATA_WIDTH == 64) begin + if (!(s_axis_cq_tready && temp_s_axis_cq_tvalid && temp_s_axis_cq_tlast)) begin + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = 1'b1; + end + end else begin + if (!(s_axis_cq_tready && s_axis_cq_tvalid && s_axis_cq_tlast)) begin + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = 1'b1; + end + end + end + + s_axis_cq_tready_next = m_axis_cq_tready_int_early || drop_ctl; + + if (AXIS_PCIE_DATA_WIDTH == 64) begin + m_axis_cq_tdata_int = temp_s_axis_cq_tdata; + m_axis_cq_tkeep_int = temp_s_axis_cq_tkeep; + m_axis_cq_tvalid_int = (temp_s_axis_cq_tvalid && s_axis_cq_tready && !drop_ctl) << select_ctl; + m_axis_cq_tlast_int = temp_s_axis_cq_tlast; + m_axis_cq_tuser_int = temp_s_axis_cq_tuser; + end else begin + m_axis_cq_tdata_int = s_axis_cq_tdata; + m_axis_cq_tkeep_int = s_axis_cq_tkeep; + m_axis_cq_tvalid_int = (s_axis_cq_tvalid && s_axis_cq_tready && !drop_ctl) << select_ctl; + m_axis_cq_tlast_int = s_axis_cq_tlast; + m_axis_cq_tuser_int = s_axis_cq_tuser; + end +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_axis_cq_tready_reg <= 1'b0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_axis_cq_tready_reg <= s_axis_cq_tready_next; + end + + if (s_axis_cq_tready && AXIS_PCIE_DATA_WIDTH == 64) begin + temp_s_axis_cq_tdata <= s_axis_cq_tdata; + temp_s_axis_cq_tkeep <= s_axis_cq_tkeep; + temp_s_axis_cq_tvalid <= s_axis_cq_tvalid; + temp_s_axis_cq_tlast <= s_axis_cq_tlast; + temp_s_axis_cq_tuser <= s_axis_cq_tuser; + end +end + +// output datapath logic +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_cq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_cq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_cq_tvalid_reg = {M_COUNT{1'b0}}, m_axis_cq_tvalid_next; +reg m_axis_cq_tlast_reg = 1'b0; +reg [AXIS_PCIE_CQ_USER_WIDTH-1:0] m_axis_cq_tuser_reg = {AXIS_PCIE_CQ_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_cq_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_cq_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_axis_cq_tvalid_reg = {M_COUNT{1'b0}}, temp_m_axis_cq_tvalid_next; +reg temp_m_axis_cq_tlast_reg = 1'b0; +reg [AXIS_PCIE_CQ_USER_WIDTH-1:0] temp_m_axis_cq_tuser_reg = {AXIS_PCIE_CQ_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_cq_temp_to_output; + +assign m_axis_cq_tdata = {M_COUNT{m_axis_cq_tdata_reg}}; +assign m_axis_cq_tkeep = {M_COUNT{m_axis_cq_tkeep_reg}}; +assign m_axis_cq_tvalid = m_axis_cq_tvalid_reg; +assign m_axis_cq_tlast = {M_COUNT{m_axis_cq_tlast_reg}}; +assign m_axis_cq_tuser = {M_COUNT{m_axis_cq_tuser_reg}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_cq_tready_int_early = (m_axis_cq_tready & m_axis_cq_tvalid) || (!temp_m_axis_cq_tvalid_reg && (!m_axis_cq_tvalid || !m_axis_cq_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_cq_tvalid_next = m_axis_cq_tvalid_reg; + temp_m_axis_cq_tvalid_next = temp_m_axis_cq_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_cq_temp_to_output = 1'b0; + + if (m_axis_cq_tready_int_reg) begin + // input is ready + if ((m_axis_cq_tready & m_axis_cq_tvalid) || !m_axis_cq_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_axis_cq_tvalid_next = m_axis_cq_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_cq_tvalid_next = m_axis_cq_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_cq_tready & m_axis_cq_tvalid) begin + // input is not ready, but output is ready + m_axis_cq_tvalid_next = temp_m_axis_cq_tvalid_reg; + temp_m_axis_cq_tvalid_next = 1'b0; + store_axis_cq_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_cq_tvalid_reg <= {M_COUNT{1'b0}}; + m_axis_cq_tready_int_reg <= 1'b0; + temp_m_axis_cq_tvalid_reg <= 1'b0; + end else begin + m_axis_cq_tvalid_reg <= m_axis_cq_tvalid_next; + m_axis_cq_tready_int_reg <= m_axis_cq_tready_int_early; + temp_m_axis_cq_tvalid_reg <= temp_m_axis_cq_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_cq_tdata_reg <= m_axis_cq_tdata_int; + m_axis_cq_tkeep_reg <= m_axis_cq_tkeep_int; + m_axis_cq_tlast_reg <= m_axis_cq_tlast_int; + m_axis_cq_tuser_reg <= m_axis_cq_tuser_int; + end else if (store_axis_cq_temp_to_output) begin + m_axis_cq_tdata_reg <= temp_m_axis_cq_tdata_reg; + m_axis_cq_tkeep_reg <= temp_m_axis_cq_tkeep_reg; + m_axis_cq_tlast_reg <= temp_m_axis_cq_tlast_reg; + m_axis_cq_tuser_reg <= temp_m_axis_cq_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_cq_tdata_reg <= m_axis_cq_tdata_int; + temp_m_axis_cq_tkeep_reg <= m_axis_cq_tkeep_int; + temp_m_axis_cq_tlast_reg <= m_axis_cq_tlast_int; + temp_m_axis_cq_tuser_reg <= m_axis_cq_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_axis_rc_demux.v b/corundum/lib/pcie/rtl/pcie_us_axis_rc_demux.v new file mode 100644 index 0000000000000000000000000000000000000000..9dce0692428dec36dff3450cfed998e730bee381 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_axis_rc_demux.v @@ -0,0 +1,263 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe RC demultiplexer + */ +module pcie_us_axis_rc_demux # +( + // Output count + parameter M_COUNT = 2, + // Width of PCIe AXI stream interfaces in bits + parameter AXIS_PCIE_DATA_WIDTH = 256, + // PCIe AXI stream tkeep signal width (words per cycle) + parameter AXIS_PCIE_KEEP_WIDTH = (AXIS_PCIE_DATA_WIDTH/32), + // PCIe AXI stream RC tuser signal width + parameter AXIS_PCIE_RC_USER_WIDTH = AXIS_PCIE_DATA_WIDTH < 512 ? 75 : 161 +) +( + input wire clk, + input wire rst, + + /* + * AXI input (RC) + */ + input wire [AXIS_PCIE_DATA_WIDTH-1:0] s_axis_rc_tdata, + input wire [AXIS_PCIE_KEEP_WIDTH-1:0] s_axis_rc_tkeep, + input wire s_axis_rc_tvalid, + output wire s_axis_rc_tready, + input wire s_axis_rc_tlast, + input wire [AXIS_PCIE_RC_USER_WIDTH-1:0] s_axis_rc_tuser, + + /* + * AXI output (RC) + */ + output wire [M_COUNT*AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rc_tdata, + output wire [M_COUNT*AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rc_tkeep, + output wire [M_COUNT-1:0] m_axis_rc_tvalid, + input wire [M_COUNT-1:0] m_axis_rc_tready, + output wire [M_COUNT-1:0] m_axis_rc_tlast, + output wire [M_COUNT*AXIS_PCIE_RC_USER_WIDTH-1:0] m_axis_rc_tuser, + + /* + * Fields + */ + output wire [15:0] requester_id, + + /* + * Control + */ + input wire enable, + input wire drop, + input wire [M_COUNT-1:0] select +); + +parameter CL_M_COUNT = $clog2(M_COUNT); + +// bus width assertions +initial begin + if (AXIS_PCIE_DATA_WIDTH != 64 && AXIS_PCIE_DATA_WIDTH != 128 && AXIS_PCIE_DATA_WIDTH != 256 && AXIS_PCIE_DATA_WIDTH != 512) begin + $error("Error: PCIe interface width must be 64, 128, 256, or 512 (instance %m)"); + $finish; + end + + if (AXIS_PCIE_KEEP_WIDTH * 32 != AXIS_PCIE_DATA_WIDTH) begin + $error("Error: PCIe interface requires dword (32-bit) granularity (instance %m)"); + $finish; + end +end + +reg [CL_M_COUNT-1:0] select_reg = {CL_M_COUNT{1'b0}}, select_ctl, select_next; +reg drop_reg = 1'b0, drop_ctl, drop_next; +reg frame_reg = 1'b0, frame_ctl, frame_next; + +reg s_axis_rc_tready_reg = 1'b0, s_axis_rc_tready_next; + +// internal datapath +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rc_tdata_int; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rc_tkeep_int; +reg [M_COUNT-1:0] m_axis_rc_tvalid_int; +reg m_axis_rc_tready_int_reg = 1'b0; +reg m_axis_rc_tlast_int; +reg [AXIS_PCIE_RC_USER_WIDTH-1:0] m_axis_rc_tuser_int; +wire m_axis_rc_tready_int_early; + +assign s_axis_rc_tready = s_axis_rc_tready_reg && enable; + +assign requester_id = s_axis_rc_tdata[63:48]; + +integer i; + +always @* begin + select_next = select_reg; + select_ctl = select_reg; + drop_next = drop_reg; + drop_ctl = drop_reg; + frame_next = frame_reg; + frame_ctl = frame_reg; + + s_axis_rc_tready_next = 1'b0; + + if (s_axis_rc_tvalid && s_axis_rc_tready) begin + // end of frame detection + if (s_axis_rc_tlast) begin + frame_next = 1'b0; + drop_next = 1'b0; + end + end + + if (!frame_reg && s_axis_rc_tvalid && s_axis_rc_tready) begin + // start of frame, grab select value + select_ctl = 0; + drop_ctl = 1'b1; + frame_ctl = 1'b1; + for (i = M_COUNT-1; i >= 0; i = i - 1) begin + if (select[i]) begin + select_ctl = i; + drop_ctl = 1'b0; + end + end + drop_ctl = drop_ctl || drop; + if (!(s_axis_rc_tready && s_axis_rc_tvalid && s_axis_rc_tlast)) begin + select_next = select_ctl; + drop_next = drop_ctl; + frame_next = 1'b1; + end + end + + s_axis_rc_tready_next = m_axis_rc_tready_int_early || drop_ctl; + + m_axis_rc_tdata_int = s_axis_rc_tdata; + m_axis_rc_tkeep_int = s_axis_rc_tkeep; + m_axis_rc_tvalid_int = (s_axis_rc_tvalid && s_axis_rc_tready && !drop_ctl) << select_ctl; + m_axis_rc_tlast_int = s_axis_rc_tlast; + m_axis_rc_tuser_int = s_axis_rc_tuser; +end + +always @(posedge clk) begin + if (rst) begin + select_reg <= 2'd0; + drop_reg <= 1'b0; + frame_reg <= 1'b0; + s_axis_rc_tready_reg <= 1'b0; + end else begin + select_reg <= select_next; + drop_reg <= drop_next; + frame_reg <= frame_next; + s_axis_rc_tready_reg <= s_axis_rc_tready_next; + end +end + +// output datapath logic +reg [AXIS_PCIE_DATA_WIDTH-1:0] m_axis_rc_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] m_axis_rc_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] m_axis_rc_tvalid_reg = {M_COUNT{1'b0}}, m_axis_rc_tvalid_next; +reg m_axis_rc_tlast_reg = 1'b0; +reg [AXIS_PCIE_RC_USER_WIDTH-1:0] m_axis_rc_tuser_reg = {AXIS_PCIE_RC_USER_WIDTH{1'b0}}; + +reg [AXIS_PCIE_DATA_WIDTH-1:0] temp_m_axis_rc_tdata_reg = {AXIS_PCIE_DATA_WIDTH{1'b0}}; +reg [AXIS_PCIE_KEEP_WIDTH-1:0] temp_m_axis_rc_tkeep_reg = {AXIS_PCIE_KEEP_WIDTH{1'b0}}; +reg [M_COUNT-1:0] temp_m_axis_rc_tvalid_reg = {M_COUNT{1'b0}}, temp_m_axis_rc_tvalid_next; +reg temp_m_axis_rc_tlast_reg = 1'b0; +reg [AXIS_PCIE_RC_USER_WIDTH-1:0] temp_m_axis_rc_tuser_reg = {AXIS_PCIE_RC_USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_rc_temp_to_output; + +assign m_axis_rc_tdata = {M_COUNT{m_axis_rc_tdata_reg}}; +assign m_axis_rc_tkeep = {M_COUNT{m_axis_rc_tkeep_reg}}; +assign m_axis_rc_tvalid = m_axis_rc_tvalid_reg; +assign m_axis_rc_tlast = {M_COUNT{m_axis_rc_tlast_reg}}; +assign m_axis_rc_tuser = {M_COUNT{m_axis_rc_tuser_reg}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_rc_tready_int_early = (m_axis_rc_tready & m_axis_rc_tvalid) || (!temp_m_axis_rc_tvalid_reg && (!m_axis_rc_tvalid || !m_axis_rc_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_rc_tvalid_next = m_axis_rc_tvalid_reg; + temp_m_axis_rc_tvalid_next = temp_m_axis_rc_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_rc_temp_to_output = 1'b0; + + if (m_axis_rc_tready_int_reg) begin + // input is ready + if ((m_axis_rc_tready & m_axis_rc_tvalid) || !m_axis_rc_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_axis_rc_tvalid_next = m_axis_rc_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_rc_tvalid_next = m_axis_rc_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_rc_tready & m_axis_rc_tvalid) begin + // input is not ready, but output is ready + m_axis_rc_tvalid_next = temp_m_axis_rc_tvalid_reg; + temp_m_axis_rc_tvalid_next = 1'b0; + store_axis_rc_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_rc_tvalid_reg <= {M_COUNT{1'b0}}; + m_axis_rc_tready_int_reg <= 1'b0; + temp_m_axis_rc_tvalid_reg <= 1'b0; + end else begin + m_axis_rc_tvalid_reg <= m_axis_rc_tvalid_next; + m_axis_rc_tready_int_reg <= m_axis_rc_tready_int_early; + temp_m_axis_rc_tvalid_reg <= temp_m_axis_rc_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_rc_tdata_reg <= m_axis_rc_tdata_int; + m_axis_rc_tkeep_reg <= m_axis_rc_tkeep_int; + m_axis_rc_tlast_reg <= m_axis_rc_tlast_int; + m_axis_rc_tuser_reg <= m_axis_rc_tuser_int; + end else if (store_axis_rc_temp_to_output) begin + m_axis_rc_tdata_reg <= temp_m_axis_rc_tdata_reg; + m_axis_rc_tkeep_reg <= temp_m_axis_rc_tkeep_reg; + m_axis_rc_tlast_reg <= temp_m_axis_rc_tlast_reg; + m_axis_rc_tuser_reg <= temp_m_axis_rc_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_rc_tdata_reg <= m_axis_rc_tdata_int; + temp_m_axis_rc_tkeep_reg <= m_axis_rc_tkeep_int; + temp_m_axis_rc_tlast_reg <= m_axis_rc_tlast_int; + temp_m_axis_rc_tuser_reg <= m_axis_rc_tuser_int; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_cfg.v b/corundum/lib/pcie/rtl/pcie_us_cfg.v new file mode 100644 index 0000000000000000000000000000000000000000..9640a682509248f35246de894c1fd5e7d940facd --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_cfg.v @@ -0,0 +1,171 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe configuration shim + */ +module pcie_us_cfg # +( + parameter PF_COUNT = 1, + parameter VF_COUNT = 0, + parameter VF_OFFSET = 64, + parameter F_COUNT = PF_COUNT+VF_COUNT, + parameter READ_EXT_TAG_ENABLE = 1, + parameter READ_MAX_READ_REQ_SIZE = 1, + parameter READ_MAX_PAYLOAD_SIZE = 1, + parameter PCIE_CAP_OFFSET = 12'h0C0 +) +( + input wire clk, + input wire rst, + + /* + * Configuration outputs + */ + output wire [F_COUNT-1:0] ext_tag_enable, + output wire [F_COUNT*3-1:0] max_read_request_size, + output wire [F_COUNT*3-1:0] max_payload_size, + + /* + * Interface to Ultrascale PCIe IP core + */ + output wire [9:0] cfg_mgmt_addr, + output wire [7:0] cfg_mgmt_function_number, + output wire cfg_mgmt_write, + output wire [31:0] cfg_mgmt_write_data, + output wire [3:0] cfg_mgmt_byte_enable, + output wire cfg_mgmt_read, + input wire [31:0] cfg_mgmt_read_data, + input wire cfg_mgmt_read_write_done +); + +localparam READ_REV_CTRL = READ_EXT_TAG_ENABLE || READ_MAX_READ_REQ_SIZE || READ_MAX_PAYLOAD_SIZE; + +localparam DEV_CTRL_OFFSET = PCIE_CAP_OFFSET + 12'h008; + +reg [F_COUNT-1:0] ext_tag_enable_reg = {F_COUNT{1'b0}}, ext_tag_enable_next; +reg [F_COUNT*3-1:0] max_read_request_size_reg = {F_COUNT{3'd0}}, max_read_request_size_next; +reg [F_COUNT*3-1:0] max_payload_size_reg = {F_COUNT{3'd0}}, max_payload_size_next; + +reg [9:0] cfg_mgmt_addr_reg = 10'd0, cfg_mgmt_addr_next; +reg [7:0] cfg_mgmt_function_number_reg = 8'd0, cfg_mgmt_function_number_next; +reg cfg_mgmt_write_reg = 1'b0, cfg_mgmt_write_next; +reg [31:0] cfg_mgmt_write_data_reg = 32'd0, cfg_mgmt_write_data_next; +reg [3:0] cfg_mgmt_byte_enable_reg = 4'd0, cfg_mgmt_byte_enable_next; +reg cfg_mgmt_read_reg = 1'b0, cfg_mgmt_read_next; + +reg [7:0] delay_reg = 8'hff, delay_next; +reg [7:0] func_cnt_reg = 8'd0, func_cnt_next; + +assign ext_tag_enable = ext_tag_enable_reg; +assign max_read_request_size = max_read_request_size_reg; +assign max_payload_size = max_payload_size_reg; + +assign cfg_mgmt_addr = cfg_mgmt_addr_reg; +assign cfg_mgmt_function_number = cfg_mgmt_function_number_reg; +assign cfg_mgmt_write = cfg_mgmt_write_reg; +assign cfg_mgmt_write_data = cfg_mgmt_write_data_reg; +assign cfg_mgmt_byte_enable = cfg_mgmt_byte_enable_reg; +assign cfg_mgmt_read = cfg_mgmt_read_reg; + +always @* begin + ext_tag_enable_next = ext_tag_enable_reg; + max_read_request_size_next = max_read_request_size_reg; + max_payload_size_next = max_payload_size_reg; + + cfg_mgmt_addr_next = cfg_mgmt_addr_reg; + cfg_mgmt_function_number_next = cfg_mgmt_function_number_reg; + cfg_mgmt_write_next = cfg_mgmt_write_reg && !cfg_mgmt_read_write_done; + cfg_mgmt_write_data_next = cfg_mgmt_write_data_reg; + cfg_mgmt_byte_enable_next = cfg_mgmt_byte_enable_reg; + cfg_mgmt_read_next = cfg_mgmt_read_reg && !cfg_mgmt_read_write_done; + + delay_next = delay_reg; + func_cnt_next = func_cnt_reg; + + if (delay_reg > 0) begin + delay_next = delay_reg - 1; + end else begin + cfg_mgmt_addr_next = DEV_CTRL_OFFSET >> 2; + cfg_mgmt_read_next = 1'b1; + if (cfg_mgmt_read_write_done) begin + cfg_mgmt_read_next = 1'b0; + + ext_tag_enable_next[func_cnt_reg] = cfg_mgmt_read_data[8]; + max_read_request_size_next[func_cnt_reg*3 +: 3] = cfg_mgmt_read_data[14:12]; + max_payload_size_next[func_cnt_reg*3 +: 3] = cfg_mgmt_read_data[7:5]; + + if (func_cnt_reg == F_COUNT-1) begin + func_cnt_next = 0; + cfg_mgmt_function_number_next = 0; + end else if (func_cnt_reg == PF_COUNT-1) begin + func_cnt_next = func_cnt_reg + 1; + cfg_mgmt_function_number_next = VF_OFFSET; + end else begin + func_cnt_next = func_cnt_reg + 1; + cfg_mgmt_function_number_next = cfg_mgmt_function_number_reg + 1; + end + + delay_next = 8'hff; + end + end +end + +always @(posedge clk) begin + if (rst) begin + ext_tag_enable_reg <= {F_COUNT{1'b0}}; + max_read_request_size_reg <= {F_COUNT{3'd0}}; + max_payload_size_reg <= {F_COUNT{3'd0}}; + + cfg_mgmt_addr_reg <= 10'd0; + cfg_mgmt_function_number_reg <= 8'd0; + cfg_mgmt_write_reg <= 1'b0; + cfg_mgmt_read_reg <= 1'b0; + + delay_reg <= 8'hff; + func_cnt_reg <= 8'd0; + end else begin + ext_tag_enable_reg <= ext_tag_enable_next; + max_read_request_size_reg <= max_read_request_size_next; + max_payload_size_reg <= max_payload_size_next; + + cfg_mgmt_addr_reg <= cfg_mgmt_addr_next; + cfg_mgmt_function_number_reg <= cfg_mgmt_function_number_next; + cfg_mgmt_write_reg <= cfg_mgmt_write_next; + cfg_mgmt_read_reg <= cfg_mgmt_read_next; + + delay_reg <= delay_next; + func_cnt_reg <= func_cnt_next; + end + + cfg_mgmt_write_data_reg <= cfg_mgmt_write_data_next; + cfg_mgmt_byte_enable_reg <= cfg_mgmt_byte_enable_next; + +end + +endmodule diff --git a/corundum/lib/pcie/rtl/pcie_us_msi.v b/corundum/lib/pcie/rtl/pcie_us_msi.v new file mode 100644 index 0000000000000000000000000000000000000000..2c1be8c219c5c15505ad9b38a8d4e89240309b10 --- /dev/null +++ b/corundum/lib/pcie/rtl/pcie_us_msi.v @@ -0,0 +1,156 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Ultrascale PCIe MSI shim + */ +module pcie_us_msi # +( + parameter MSI_COUNT = 32 +) +( + input wire clk, + input wire rst, + + /* + * Interrupt request inputs + */ + input wire [MSI_COUNT-1:0] msi_irq, + + /* + * Interface to Ultrascale PCIe IP core + */ + input wire [3:0] cfg_interrupt_msi_enable, + input wire [7:0] cfg_interrupt_msi_vf_enable, + input wire [11:0] cfg_interrupt_msi_mmenable, + input wire cfg_interrupt_msi_mask_update, + input wire [31:0] cfg_interrupt_msi_data, + output wire [3:0] cfg_interrupt_msi_select, + output wire [31:0] cfg_interrupt_msi_int, + output wire [31:0] cfg_interrupt_msi_pending_status, + output wire cfg_interrupt_msi_pending_status_data_enable, + output wire [3:0] cfg_interrupt_msi_pending_status_function_num, + input wire cfg_interrupt_msi_sent, + input wire cfg_interrupt_msi_fail, + output wire [2:0] cfg_interrupt_msi_attr, + output wire cfg_interrupt_msi_tph_present, + output wire [1:0] cfg_interrupt_msi_tph_type, + output wire [8:0] cfg_interrupt_msi_tph_st_tag, + output wire [3:0] cfg_interrupt_msi_function_number +); + +reg active_reg = 1'b0, active_next; + +reg [MSI_COUNT-1:0] msi_irq_reg = {MSI_COUNT{1'b0}}; +reg [MSI_COUNT-1:0] msi_irq_last_reg = {MSI_COUNT{1'b0}}; +reg [MSI_COUNT-1:0] msi_irq_active_reg = {MSI_COUNT{1'b0}}, msi_irq_active_next; + +reg [MSI_COUNT-1:0] msi_irq_mask_reg = {MSI_COUNT{1'b0}}, msi_irq_mask_next; + +reg [MSI_COUNT-1:0] msi_int_reg = {MSI_COUNT{1'b0}}, msi_int_next; + +assign cfg_interrupt_msi_select = 4'd0; // request PF0 mask on cfg_interrupt_msi_data +assign cfg_interrupt_msi_int = msi_int_reg; +assign cfg_interrupt_msi_pending_status = msi_irq_reg; +assign cfg_interrupt_msi_pending_status_data_enable = 1'b1; // set PF0 pending status +assign cfg_interrupt_msi_pending_status_function_num = 4'd0; // set PF0 pending status +assign cfg_interrupt_msi_attr = 3'd0; +assign cfg_interrupt_msi_tph_present = 1'b0; // no TPH +assign cfg_interrupt_msi_tph_type = 2'd0; +assign cfg_interrupt_msi_tph_st_tag = 9'd0; +assign cfg_interrupt_msi_function_number = 4'd0; // send MSI for PF0 + +wire [MSI_COUNT-1:0] message_enable_mask = cfg_interrupt_msi_mmenable[2:0] > 3'd4 ? {32{1'b1}} : {32{1'b1}} >> (32 - (1 << cfg_interrupt_msi_mmenable[2:0])); + +reg [MSI_COUNT-1:0] acknowledge; +wire [MSI_COUNT-1:0] grant; +wire grant_valid; + +// arbiter instance +arbiter #( + .PORTS(MSI_COUNT), + .TYPE("ROUND_ROBIN"), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY("HIGH") +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(msi_irq_active_reg & msi_irq_mask_reg & ~grant), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded() +); + +always @* begin + active_next = active_reg; + + msi_irq_active_next = (msi_irq_active_reg | (msi_irq_reg & ~msi_irq_last_reg)); + + msi_irq_mask_next = ~cfg_interrupt_msi_data & message_enable_mask & {32{cfg_interrupt_msi_enable[0]}}; + + msi_int_next = {MSI_COUNT{1'b0}}; + + acknowledge = {MSI_COUNT{1'b0}}; + + if (!active_reg) begin + if (cfg_interrupt_msi_enable && grant_valid) begin + msi_int_next = grant; + active_next = 1'b1; + end + end else begin + if (cfg_interrupt_msi_sent || cfg_interrupt_msi_fail) begin + if (cfg_interrupt_msi_sent) begin + msi_irq_active_next = msi_irq_active_next & ~grant; + end + acknowledge = grant; + active_next = 1'b0; + end + end +end + +always @(posedge clk) begin + if (rst) begin + active_reg <= 1'b0; + msi_irq_reg <= {MSI_COUNT{1'b0}}; + msi_irq_last_reg <= {MSI_COUNT{1'b0}}; + msi_irq_active_reg <= {MSI_COUNT{1'b0}}; + msi_irq_mask_reg <= {MSI_COUNT{1'b0}}; + msi_int_reg <= {MSI_COUNT{1'b0}}; + end else begin + active_reg <= active_next; + msi_irq_reg <= msi_irq; + msi_irq_last_reg <= msi_irq_reg; + msi_irq_active_reg <= msi_irq_active_next; + msi_irq_mask_reg <= msi_irq_mask_next; + msi_int_reg <= msi_int_next; + end +end + +endmodule diff --git a/corundum/lib/pcie/rtl/priority_encoder.v b/corundum/lib/pcie/rtl/priority_encoder.v new file mode 100644 index 0000000000000000000000000000000000000000..73030630244adccf1ab3be09177c060f3f6cfb7e --- /dev/null +++ b/corundum/lib/pcie/rtl/priority_encoder.v @@ -0,0 +1,98 @@ +/* + +Copyright (c) 2014-2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Priority encoder module + */ +module priority_encoder # +( + parameter WIDTH = 4, + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "LOW" +) +( + input wire [WIDTH-1:0] input_unencoded, + output wire output_valid, + output wire [$clog2(WIDTH)-1:0] output_encoded, + output wire [WIDTH-1:0] output_unencoded +); + +// power-of-two width +parameter W1 = 2**$clog2(WIDTH); +parameter W2 = W1/2; + +generate + if (WIDTH == 1) begin + // one input + assign output_valid = input_unencoded; + assign output_encoded = 0; + end else if (WIDTH == 2) begin + // two inputs - just an OR gate + assign output_valid = |input_unencoded; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = input_unencoded[1]; + end else begin + assign output_encoded = ~input_unencoded[0]; + end + end else begin + // more than two inputs - split into two parts and recurse + // also pad input to correct power-of-two width + wire [$clog2(W2)-1:0] out1, out2; + wire valid1, valid2; + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst1 ( + .input_unencoded(input_unencoded[W2-1:0]), + .output_valid(valid1), + .output_encoded(out1) + ); + priority_encoder #( + .WIDTH(W2), + .LSB_PRIORITY(LSB_PRIORITY) + ) + priority_encoder_inst2 ( + .input_unencoded({{W1-WIDTH{1'b0}}, input_unencoded[WIDTH-1:W2]}), + .output_valid(valid2), + .output_encoded(out2) + ); + // multiplexer to select part + assign output_valid = valid1 | valid2; + if (LSB_PRIORITY == "LOW") begin + assign output_encoded = valid2 ? {1'b1, out2} : {1'b0, out1}; + end else begin + assign output_encoded = valid1 ? {1'b0, out1} : {1'b1, out2}; + end + end +endgenerate + +// unencoded output +assign output_unencoded = 1 << output_encoded; + +endmodule diff --git a/corundum/lib/pcie/rtl/pulse_merge.v b/corundum/lib/pcie/rtl/pulse_merge.v new file mode 100644 index 0000000000000000000000000000000000000000..0c79092f00fcd46392595696e06a26f7be21a6e5 --- /dev/null +++ b/corundum/lib/pcie/rtl/pulse_merge.v @@ -0,0 +1,77 @@ +/* + +Copyright (c) 2018 Alex Forencich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Pulse merge module + */ +module pulse_merge # +( + parameter INPUT_WIDTH = 2, + parameter COUNT_WIDTH = 4 +) +( + input wire clk, + input wire rst, + + input wire [INPUT_WIDTH-1:0] pulse_in, + output wire [COUNT_WIDTH-1:0] count_out, + output wire pulse_out +); + +reg [COUNT_WIDTH-1:0] count_reg = {COUNT_WIDTH{1'b0}}, count_next; +reg pulse_reg = 1'b0, pulse_next; + +assign count_out = count_reg; +assign pulse_out = pulse_reg; + +integer i; + +always @* begin + count_next = count_reg; + pulse_next = count_reg > 0; + + if (count_reg > 0) begin + count_next = count_reg - 1; + end + + for (i = 0; i < INPUT_WIDTH; i = i + 1) begin + count_next = count_next + pulse_in[i]; + end +end + +always @(posedge clk) begin + if (rst) begin + count_reg <= {COUNT_WIDTH{1'b0}}; + pulse_reg <= 1'b0; + end else begin + count_reg <= count_next; + pulse_reg <= pulse_next; + end +end + +endmodule diff --git a/corundum/rtl/cmac_pad.v b/corundum/rtl/cmac_pad.v new file mode 100644 index 0000000000000000000000000000000000000000..13ce789d40dc0b7c8b37ce07ac441e85837c776e --- /dev/null +++ b/corundum/rtl/cmac_pad.v @@ -0,0 +1,114 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * CMAC frame pad module + */ +module cmac_pad # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 512, + // tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // tuser signal width + parameter USER_WIDTH = 1 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [USER_WIDTH-1:0] m_axis_tuser +); + +// check configuration +initial begin + if (DATA_WIDTH != 512) begin + $error("Error: AXI stream data width must be 512 (instance %m)"); + $finish; + end + + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +reg frame_reg = 1'b0; + +generate + genvar k; + + for (k = 0; k < KEEP_WIDTH; k = k + 1) begin + assign m_axis_tdata[k*8 +: 8] = s_axis_tkeep[k] ? s_axis_tdata[k*8 +: 8] : 8'd0; + end +endgenerate + +assign m_axis_tkeep = (frame_reg ? {KEEP_WIDTH{1'b0}} : {60{1'b1}}) | s_axis_tkeep; +assign m_axis_tvalid = s_axis_tvalid; +assign s_axis_tready = m_axis_tready; +assign m_axis_tlast = s_axis_tlast; +assign m_axis_tuser = s_axis_tuser; + +always @(posedge clk) begin + if (s_axis_tvalid && s_axis_tready) begin + frame_reg <= !s_axis_tlast; + end + + if (rst) begin + frame_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/rtl/cpl_op_mux.v b/corundum/rtl/cpl_op_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..d1a430848e94aa84a4a8352edcdebac47aa0d36e --- /dev/null +++ b/corundum/rtl/cpl_op_mux.v @@ -0,0 +1,284 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Completion operation mux + */ +module cpl_op_mux # +( + // Number of ports + parameter PORTS = 2, + // Select field width + parameter SELECT_WIDTH = 1, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 4, + // Input request tag field width + parameter S_REQ_TAG_WIDTH = 8, + // Output request tag field width (towards descriptor module) + // Additional bits required for response routing + parameter M_REQ_TAG_WIDTH = S_REQ_TAG_WIDTH+$clog2(PORTS), + // Completion size (bytes) + parameter CPL_SIZE = 32, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Completion request output (to completion module) + */ + output wire [SELECT_WIDTH-1:0] m_axis_req_sel, + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_req_queue, + output wire [M_REQ_TAG_WIDTH-1:0] m_axis_req_tag, + output wire [CPL_SIZE*8-1:0] m_axis_req_data, + output wire m_axis_req_valid, + input wire m_axis_req_ready, + + /* + * Completion request status input (from completion module) + */ + input wire [M_REQ_TAG_WIDTH-1:0] s_axis_req_status_tag, + input wire s_axis_req_status_full, + input wire s_axis_req_status_error, + input wire s_axis_req_status_valid, + + /* + * Completion request input + */ + input wire [PORTS*SELECT_WIDTH-1:0] s_axis_req_sel, + input wire [PORTS*QUEUE_INDEX_WIDTH-1:0] s_axis_req_queue, + input wire [PORTS*S_REQ_TAG_WIDTH-1:0] s_axis_req_tag, + input wire [PORTS*CPL_SIZE*8-1:0] s_axis_req_data, + input wire [PORTS-1:0] s_axis_req_valid, + output wire [PORTS-1:0] s_axis_req_ready, + + /* + * Completion request status output + */ + output wire [PORTS*S_REQ_TAG_WIDTH-1:0] m_axis_req_status_tag, + output wire [PORTS-1:0] m_axis_req_status_full, + output wire [PORTS-1:0] m_axis_req_status_error, + output wire [PORTS-1:0] m_axis_req_status_valid +); + +parameter CL_PORTS = $clog2(PORTS); + +// check configuration +initial begin + if (M_REQ_TAG_WIDTH < S_REQ_TAG_WIDTH+$clog2(PORTS)) begin + $error("Error: M_REQ_TAG_WIDTH must be at least $clog2(PORTS) larger than S_REQ_TAG_WIDTH (instance %m)"); + $finish; + end +end + +// request mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [SELECT_WIDTH-1:0] m_axis_req_sel_int; +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_req_queue_int; +reg [M_REQ_TAG_WIDTH-1:0] m_axis_req_tag_int; +reg [CPL_SIZE*8-1:0] m_axis_req_data_int; +reg m_axis_req_valid_int; +reg m_axis_req_ready_int_reg = 1'b0; +wire m_axis_req_ready_int_early; + +assign s_axis_req_ready = (m_axis_req_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [SELECT_WIDTH-1:0] current_s_desc_sel = s_axis_req_sel[grant_encoded*SELECT_WIDTH +: SELECT_WIDTH]; +wire [QUEUE_INDEX_WIDTH-1:0] current_s_desc_queue = s_axis_req_queue[grant_encoded*QUEUE_INDEX_WIDTH +: QUEUE_INDEX_WIDTH]; +wire [S_REQ_TAG_WIDTH-1:0] current_s_desc_tag = s_axis_req_tag[grant_encoded*S_REQ_TAG_WIDTH +: S_REQ_TAG_WIDTH]; +wire [CPL_SIZE*8-1:0] current_s_desc_data = s_axis_req_data[grant_encoded*CPL_SIZE*8 +: CPL_SIZE*8]; +wire current_s_desc_valid = s_axis_req_valid[grant_encoded]; +wire current_s_desc_ready = s_axis_req_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_req_valid & ~grant; +assign acknowledge = grant & s_axis_req_valid & s_axis_req_ready; + +always @* begin + // pass through selected packet data + m_axis_req_sel_int = current_s_desc_sel; + m_axis_req_queue_int = current_s_desc_queue; + m_axis_req_tag_int = {grant_encoded, current_s_desc_tag}; + m_axis_req_data_int = current_s_desc_data; + m_axis_req_valid_int = current_s_desc_valid && m_axis_req_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [SELECT_WIDTH-1:0] m_axis_req_sel_reg = {SELECT_WIDTH{1'b0}}; +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}; +reg [M_REQ_TAG_WIDTH-1:0] m_axis_req_tag_reg = {M_REQ_TAG_WIDTH{1'b0}}; +reg [CPL_SIZE*8-1:0] m_axis_req_data_reg = {CPL_SIZE*8{1'b0}}; +reg m_axis_req_valid_reg = 1'b0, m_axis_req_valid_next; + +reg [SELECT_WIDTH-1:0] temp_m_axis_req_sel_reg = {SELECT_WIDTH{1'b0}}; +reg [QUEUE_INDEX_WIDTH-1:0] temp_m_axis_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}; +reg [M_REQ_TAG_WIDTH-1:0] temp_m_axis_req_tag_reg = {M_REQ_TAG_WIDTH{1'b0}}; +reg [CPL_SIZE*8-1:0] temp_m_axis_req_data_reg = {CPL_SIZE*8{1'b0}}; +reg temp_m_axis_req_valid_reg = 1'b0, temp_m_axis_req_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_req_sel = m_axis_req_sel_reg; +assign m_axis_req_queue = m_axis_req_queue_reg; +assign m_axis_req_tag = m_axis_req_tag_reg; +assign m_axis_req_data = m_axis_req_data_reg; +assign m_axis_req_valid = m_axis_req_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_req_ready_int_early = m_axis_req_ready || (!temp_m_axis_req_valid_reg && (!m_axis_req_valid_reg || !m_axis_req_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_req_valid_next = m_axis_req_valid_reg; + temp_m_axis_req_valid_next = temp_m_axis_req_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_req_ready_int_reg) begin + // input is ready + if (m_axis_req_ready || !m_axis_req_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_req_valid_next = m_axis_req_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_req_valid_next = m_axis_req_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_req_ready) begin + // input is not ready, but output is ready + m_axis_req_valid_next = temp_m_axis_req_valid_reg; + temp_m_axis_req_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_req_valid_reg <= 1'b0; + m_axis_req_ready_int_reg <= 1'b0; + temp_m_axis_req_valid_reg <= 1'b0; + end else begin + m_axis_req_valid_reg <= m_axis_req_valid_next; + m_axis_req_ready_int_reg <= m_axis_req_ready_int_early; + temp_m_axis_req_valid_reg <= temp_m_axis_req_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_req_sel_reg <= m_axis_req_sel_int; + m_axis_req_queue_reg <= m_axis_req_queue_int; + m_axis_req_tag_reg <= m_axis_req_tag_int; + m_axis_req_data_reg <= m_axis_req_data_int; + end else if (store_axis_temp_to_output) begin + m_axis_req_sel_reg <= temp_m_axis_req_sel_reg; + m_axis_req_queue_reg <= temp_m_axis_req_queue_reg; + m_axis_req_tag_reg <= temp_m_axis_req_tag_reg; + m_axis_req_data_reg <= temp_m_axis_req_data_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_req_sel_reg <= m_axis_req_sel_int; + temp_m_axis_req_queue_reg <= m_axis_req_queue_int; + temp_m_axis_req_tag_reg <= m_axis_req_tag_int; + temp_m_axis_req_data_reg <= m_axis_req_data_int; + end +end + +// request status demux +reg [S_REQ_TAG_WIDTH-1:0] m_axis_req_status_tag_reg = {S_REQ_TAG_WIDTH{1'b0}}, m_axis_req_status_tag_next; +reg m_axis_req_status_full_reg = 1'b0, m_axis_req_status_full_next; +reg m_axis_req_status_error_reg = 1'b0, m_axis_req_status_error_next; +reg [PORTS-1:0] m_axis_req_status_valid_reg = {PORTS{1'b0}}, m_axis_req_status_valid_next; + +assign m_axis_req_status_tag = {PORTS{m_axis_req_status_tag_reg}}; +assign m_axis_req_status_full = {PORTS{m_axis_req_status_full_reg}}; +assign m_axis_req_status_error = {PORTS{m_axis_req_status_error_reg}}; +assign m_axis_req_status_valid = m_axis_req_status_valid_reg; + +always @* begin + m_axis_req_status_tag_next = s_axis_req_status_tag; + m_axis_req_status_full_next = s_axis_req_status_full; + m_axis_req_status_error_next = s_axis_req_status_error; + m_axis_req_status_valid_next = s_axis_req_status_valid << (PORTS > 1 ? (s_axis_req_status_tag >> S_REQ_TAG_WIDTH) : 0); +end + +always @(posedge clk) begin + if (rst) begin + m_axis_req_status_valid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_req_status_valid_reg <= m_axis_req_status_valid_next; + end + + m_axis_req_status_tag_reg <= m_axis_req_status_tag_next; + m_axis_req_status_full_reg <= m_axis_req_status_full_next; + m_axis_req_status_error_reg <= m_axis_req_status_error_next; +end + +endmodule diff --git a/corundum/rtl/cpl_queue_manager.v b/corundum/rtl/cpl_queue_manager.v new file mode 100644 index 0000000000000000000000000000000000000000..082ff2cb3a107fc7400087bb3e4314fe5baacb09 --- /dev/null +++ b/corundum/rtl/cpl_queue_manager.v @@ -0,0 +1,717 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Completion queue manager + */ +module cpl_queue_manager # +( + // Base address width + parameter ADDR_WIDTH = 64, + // Request tag field width + parameter REQ_TAG_WIDTH = 8, + // Number of outstanding operations + parameter OP_TABLE_SIZE = 16, + // Operation tag field width + parameter OP_TAG_WIDTH = 8, + // Queue index width (log2 of number of queues) + parameter QUEUE_INDEX_WIDTH = 8, + // Event index width + parameter EVENT_WIDTH = 8, + // Queue element pointer width (log2 of number of elements) + parameter QUEUE_PTR_WIDTH = 16, + // Log queue size field width + parameter LOG_QUEUE_SIZE_WIDTH = $clog2(QUEUE_PTR_WIDTH), + // Queue element size + parameter CPL_SIZE = 16, + // Pipeline stages + parameter PIPELINE = 2, + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = 16, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * Enqueue request input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_enqueue_req_queue, + input wire [REQ_TAG_WIDTH-1:0] s_axis_enqueue_req_tag, + input wire s_axis_enqueue_req_valid, + output wire s_axis_enqueue_req_ready, + + /* + * Enqueue response output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_enqueue_resp_queue, + output wire [QUEUE_PTR_WIDTH-1:0] m_axis_enqueue_resp_ptr, + output wire [ADDR_WIDTH-1:0] m_axis_enqueue_resp_addr, + output wire [EVENT_WIDTH-1:0] m_axis_enqueue_resp_event, + output wire [REQ_TAG_WIDTH-1:0] m_axis_enqueue_resp_tag, + output wire [OP_TAG_WIDTH-1:0] m_axis_enqueue_resp_op_tag, + output wire m_axis_enqueue_resp_full, + output wire m_axis_enqueue_resp_error, + output wire m_axis_enqueue_resp_valid, + input wire m_axis_enqueue_resp_ready, + + /* + * Enqueue commit input + */ + input wire [OP_TAG_WIDTH-1:0] s_axis_enqueue_commit_op_tag, + input wire s_axis_enqueue_commit_valid, + output wire s_axis_enqueue_commit_ready, + + /* + * Event output + */ + output wire [EVENT_WIDTH-1:0] m_axis_event, + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_event_source, + output wire m_axis_event_valid, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * Configuration + */ + input wire enable +); + +parameter QUEUE_COUNT = 2**QUEUE_INDEX_WIDTH; + +parameter CL_OP_TABLE_SIZE = $clog2(OP_TABLE_SIZE); + +parameter CL_CPL_SIZE = $clog2(CPL_SIZE); + +parameter QUEUE_RAM_BE_WIDTH = 16; +parameter QUEUE_RAM_WIDTH = QUEUE_RAM_BE_WIDTH*8; + +// bus width assertions +initial begin + if (OP_TAG_WIDTH < CL_OP_TABLE_SIZE) begin + $error("Error: OP_TAG_WIDTH insufficient for OP_TABLE_SIZE (instance %m)"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI lite interface width must be 32 (instance %m)"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: AXI lite interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXIL_ADDR_WIDTH < QUEUE_INDEX_WIDTH+5) begin + $error("Error: AXI lite address width too narrow (instance %m)"); + $finish; + end + + if (2**$clog2(CPL_SIZE) != CPL_SIZE) begin + $error("Error: Completion size must be even power of two (instance %m)"); + $finish; + end + + if (PIPELINE < 2) begin + $error("Error: PIPELINE must be at least 2 (instance %m)"); + $finish; + end +end + +reg op_axil_write_pipe_hazard; +reg op_axil_read_pipe_hazard; +reg op_req_pipe_hazard; +reg op_commit_pipe_hazard; +reg stage_active; + +reg [PIPELINE-1:0] op_axil_write_pipe_reg = {PIPELINE{1'b0}}, op_axil_write_pipe_next; +reg [PIPELINE-1:0] op_axil_read_pipe_reg = {PIPELINE{1'b0}}, op_axil_read_pipe_next; +reg [PIPELINE-1:0] op_req_pipe_reg = {PIPELINE{1'b0}}, op_req_pipe_next; +reg [PIPELINE-1:0] op_commit_pipe_reg = {PIPELINE{1'b0}}, op_commit_pipe_next; + +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_addr_pipeline_reg[PIPELINE-1:0], queue_ram_addr_pipeline_next[PIPELINE-1:0]; +reg [2:0] axil_reg_pipeline_reg[PIPELINE-1:0], axil_reg_pipeline_next[PIPELINE-1:0]; +reg [AXIL_DATA_WIDTH-1:0] write_data_pipeline_reg[PIPELINE-1:0], write_data_pipeline_next[PIPELINE-1:0]; +reg [AXIL_STRB_WIDTH-1:0] write_strobe_pipeline_reg[PIPELINE-1:0], write_strobe_pipeline_next[PIPELINE-1:0]; +reg [REQ_TAG_WIDTH-1:0] req_tag_pipeline_reg[PIPELINE-1:0], req_tag_pipeline_next[PIPELINE-1:0]; + +reg s_axis_enqueue_req_ready_reg = 1'b0, s_axis_enqueue_req_ready_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_enqueue_resp_queue_reg = 0, m_axis_enqueue_resp_queue_next; +reg [QUEUE_PTR_WIDTH-1:0] m_axis_enqueue_resp_ptr_reg = 0, m_axis_enqueue_resp_ptr_next; +reg [ADDR_WIDTH-1:0] m_axis_enqueue_resp_addr_reg = 0, m_axis_enqueue_resp_addr_next; +reg [EVENT_WIDTH-1:0] m_axis_enqueue_resp_event_reg = 0, m_axis_enqueue_resp_event_next; +reg [REQ_TAG_WIDTH-1:0] m_axis_enqueue_resp_tag_reg = 0, m_axis_enqueue_resp_tag_next; +reg [OP_TAG_WIDTH-1:0] m_axis_enqueue_resp_op_tag_reg = 0, m_axis_enqueue_resp_op_tag_next; +reg m_axis_enqueue_resp_full_reg = 1'b0, m_axis_enqueue_resp_full_next; +reg m_axis_enqueue_resp_error_reg = 1'b0, m_axis_enqueue_resp_error_next; +reg m_axis_enqueue_resp_valid_reg = 1'b0, m_axis_enqueue_resp_valid_next; + +reg s_axis_enqueue_commit_ready_reg = 1'b0, s_axis_enqueue_commit_ready_next; + +reg [EVENT_WIDTH-1:0] m_axis_event_reg = 0, m_axis_event_next; +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_event_source_reg = 0, m_axis_event_source_next; +reg m_axis_event_valid_reg = 1'b0, m_axis_event_valid_next; + +reg s_axil_awready_reg = 0, s_axil_awready_next; +reg s_axil_wready_reg = 0, s_axil_wready_next; +reg s_axil_bvalid_reg = 0, s_axil_bvalid_next; +reg s_axil_arready_reg = 0, s_axil_arready_next; +reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = 0, s_axil_rdata_next; +reg s_axil_rvalid_reg = 0, s_axil_rvalid_next; + +reg [QUEUE_RAM_WIDTH-1:0] queue_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_read_ptr; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_write_ptr; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_write_data; +reg queue_ram_wr_en; +reg [QUEUE_RAM_BE_WIDTH-1:0] queue_ram_be; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_read_data_reg = 0; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_read_data_pipeline_reg[PIPELINE-1:1]; + +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_data_head_ptr = queue_ram_read_data_pipeline_reg[PIPELINE-1][15:0]; +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_data_tail_ptr = queue_ram_read_data_pipeline_reg[PIPELINE-1][31:16]; +wire [EVENT_WIDTH-1:0] queue_ram_read_data_event = queue_ram_read_data_pipeline_reg[PIPELINE-1][47:32]; +wire [LOG_QUEUE_SIZE_WIDTH-1:0] queue_ram_read_data_log_size = queue_ram_read_data_pipeline_reg[PIPELINE-1][51:48]; +wire queue_ram_read_data_continuous = queue_ram_read_data_pipeline_reg[PIPELINE-1][53]; +wire queue_ram_read_data_armed = queue_ram_read_data_pipeline_reg[PIPELINE-1][54]; +wire queue_ram_read_data_active = queue_ram_read_data_pipeline_reg[PIPELINE-1][55]; +wire [CL_OP_TABLE_SIZE-1:0] queue_ram_read_data_op_index = queue_ram_read_data_pipeline_reg[PIPELINE-1][63:56]; +wire [ADDR_WIDTH-1:0] queue_ram_read_data_base_addr = queue_ram_read_data_pipeline_reg[PIPELINE-1][127:64]; + +reg [OP_TABLE_SIZE-1:0] op_table_active = 0; +reg [OP_TABLE_SIZE-1:0] op_table_commit = 0; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_queue[OP_TABLE_SIZE-1:0]; +reg [QUEUE_PTR_WIDTH-1:0] op_table_queue_ptr[OP_TABLE_SIZE-1:0]; +reg [CL_OP_TABLE_SIZE-1:0] op_table_start_ptr_reg = 0; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_start_queue; +reg [QUEUE_PTR_WIDTH-1:0] op_table_start_queue_ptr; +reg op_table_start_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_commit_ptr; +reg op_table_commit_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_finish_ptr_reg = 0; +reg op_table_finish_en; + +assign s_axis_enqueue_req_ready = s_axis_enqueue_req_ready_reg; + +assign m_axis_enqueue_resp_queue = m_axis_enqueue_resp_queue_reg; +assign m_axis_enqueue_resp_ptr = m_axis_enqueue_resp_ptr_reg; +assign m_axis_enqueue_resp_addr = m_axis_enqueue_resp_addr_reg; +assign m_axis_enqueue_resp_event = m_axis_enqueue_resp_event_reg; +assign m_axis_enqueue_resp_tag = m_axis_enqueue_resp_tag_reg; +assign m_axis_enqueue_resp_op_tag = m_axis_enqueue_resp_op_tag_reg; +assign m_axis_enqueue_resp_full = m_axis_enqueue_resp_full_reg; +assign m_axis_enqueue_resp_error = m_axis_enqueue_resp_error_reg; +assign m_axis_enqueue_resp_valid = m_axis_enqueue_resp_valid_reg; + +assign s_axis_enqueue_commit_ready = s_axis_enqueue_commit_ready_reg; + +assign m_axis_event = m_axis_event_reg; +assign m_axis_event_source = m_axis_event_source_reg; +assign m_axis_event_valid = m_axis_event_valid_reg; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = 2'b00; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = 2'b00; +assign s_axil_rvalid = s_axil_rvalid_reg; + +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_awaddr_queue = s_axil_awaddr >> 5; +wire [2:0] s_axil_awaddr_reg = s_axil_awaddr >> 2; +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_araddr_queue = s_axil_araddr >> 5; +wire [2:0] s_axil_araddr_reg = s_axil_araddr >> 2; + +wire queue_active = op_table_active[queue_ram_read_data_op_index] && op_table_queue[queue_ram_read_data_op_index] == queue_ram_addr_pipeline_reg[PIPELINE-1]; +wire queue_full_idle = ($unsigned(queue_ram_read_data_head_ptr - queue_ram_read_data_tail_ptr) & ({QUEUE_PTR_WIDTH{1'b1}} << queue_ram_read_data_log_size)) != 0; +wire queue_full_active = ($unsigned(op_table_queue_ptr[queue_ram_read_data_op_index] - queue_ram_read_data_tail_ptr) & ({QUEUE_PTR_WIDTH{1'b1}} << queue_ram_read_data_log_size)) != 0; +wire queue_full = queue_active ? queue_full_active : queue_full_idle; +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_active_head_ptr = queue_active ? op_table_queue_ptr[queue_ram_read_data_op_index] : queue_ram_read_data_head_ptr; + +integer i; + +initial begin + for (i = 0; i < QUEUE_COUNT; i = i + 1) begin + queue_ram[i] = 0; + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] = 0; + axil_reg_pipeline_reg[i] = 0; + write_data_pipeline_reg[i] = 0; + write_strobe_pipeline_reg[i] = 0; + req_tag_pipeline_reg[i] = 0; + end + + for (i = 0; i < OP_TABLE_SIZE; i = i + 1) begin + op_table_queue[i] = 0; + op_table_queue_ptr[i] = 0; + end +end + +integer j; + +always @* begin + op_axil_write_pipe_next = {op_axil_write_pipe_reg, 1'b0}; + op_axil_read_pipe_next = {op_axil_read_pipe_reg, 1'b0}; + op_req_pipe_next = {op_req_pipe_reg, 1'b0}; + op_commit_pipe_next = {op_commit_pipe_reg, 1'b0}; + + queue_ram_addr_pipeline_next[0] = 0; + axil_reg_pipeline_next[0] = 0; + write_data_pipeline_next[0] = 0; + write_strobe_pipeline_next[0] = 0; + req_tag_pipeline_next[0] = 0; + for (j = 1; j < PIPELINE; j = j + 1) begin + queue_ram_addr_pipeline_next[j] = queue_ram_addr_pipeline_reg[j-1]; + axil_reg_pipeline_next[j] = axil_reg_pipeline_reg[j-1]; + write_data_pipeline_next[j] = write_data_pipeline_reg[j-1]; + write_strobe_pipeline_next[j] = write_strobe_pipeline_reg[j-1]; + req_tag_pipeline_next[j] = req_tag_pipeline_reg[j-1]; + end + + s_axis_enqueue_req_ready_next = 1'b0; + + m_axis_enqueue_resp_queue_next = m_axis_enqueue_resp_queue_reg; + m_axis_enqueue_resp_ptr_next = m_axis_enqueue_resp_ptr_reg; + m_axis_enqueue_resp_addr_next = m_axis_enqueue_resp_addr_reg; + m_axis_enqueue_resp_event_next = m_axis_enqueue_resp_event_reg; + m_axis_enqueue_resp_tag_next = m_axis_enqueue_resp_tag_reg; + m_axis_enqueue_resp_op_tag_next = m_axis_enqueue_resp_op_tag_reg; + m_axis_enqueue_resp_full_next = m_axis_enqueue_resp_full_reg; + m_axis_enqueue_resp_error_next = m_axis_enqueue_resp_error_reg; + m_axis_enqueue_resp_valid_next = m_axis_enqueue_resp_valid_reg && !m_axis_enqueue_resp_ready; + + s_axis_enqueue_commit_ready_next = 1'b0; + + m_axis_event_next = m_axis_event_reg; + m_axis_event_source_next = m_axis_event_source_reg; + m_axis_event_valid_next = 1'b0; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + s_axil_arready_next = 1'b0; + s_axil_rdata_next = s_axil_rdata_reg; + s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready; + + queue_ram_read_ptr = 0; + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data = queue_ram_read_data_pipeline_reg[PIPELINE-1]; + queue_ram_wr_en = 0; + queue_ram_be = 0; + + op_table_start_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + op_table_start_queue_ptr = queue_ram_read_active_head_ptr + 1; + op_table_start_en = 1'b0; + op_table_commit_ptr = s_axis_enqueue_commit_op_tag; + op_table_commit_en = 1'b0; + op_table_finish_en = 1'b0; + + op_axil_write_pipe_hazard = 1'b0; + op_axil_read_pipe_hazard = 1'b0; + op_req_pipe_hazard = 1'b0; + op_commit_pipe_hazard = 1'b0; + stage_active = 1'b0; + + for (j = 0; j < PIPELINE; j = j + 1) begin + stage_active = op_axil_write_pipe_reg[j] || op_axil_read_pipe_reg[j] || op_req_pipe_reg[j] || op_commit_pipe_reg[j]; + op_axil_write_pipe_hazard = op_axil_write_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_awaddr_queue); + op_axil_read_pipe_hazard = op_axil_read_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_araddr_queue); + op_req_pipe_hazard = op_req_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axis_enqueue_req_queue); + op_commit_pipe_hazard = op_commit_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == op_table_queue[op_table_finish_ptr_reg]); + end + + // pipeline stage 0 - receive request + if (s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && !op_axil_write_pipe_reg[0] && !op_axil_write_pipe_hazard) begin + // AXIL write + op_axil_write_pipe_next[0] = 1'b1; + + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + + write_data_pipeline_next[0] = s_axil_wdata; + write_strobe_pipeline_next[0] = s_axil_wstrb; + + queue_ram_read_ptr = s_axil_awaddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_awaddr_queue; + axil_reg_pipeline_next[0] = s_axil_awaddr_reg; + end else if (s_axil_arvalid && (!s_axil_rvalid || s_axil_rready) && !op_axil_read_pipe_reg[0] && !op_axil_read_pipe_hazard) begin + // AXIL read + op_axil_read_pipe_next[0] = 1'b1; + + s_axil_arready_next = 1'b1; + + queue_ram_read_ptr = s_axil_araddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_araddr_queue; + axil_reg_pipeline_next[0] = s_axil_araddr_reg; + end else if (op_table_active[op_table_finish_ptr_reg] && op_table_commit[op_table_finish_ptr_reg] && !op_commit_pipe_reg[0] && !op_commit_pipe_hazard) begin + // enqueue commit finalize (update pointer) + op_commit_pipe_next[0] = 1'b1; + + op_table_finish_en = 1'b1; + + write_data_pipeline_next[0] = op_table_queue_ptr[op_table_finish_ptr_reg]; + + queue_ram_read_ptr = op_table_queue[op_table_finish_ptr_reg]; + queue_ram_addr_pipeline_next[0] = op_table_queue[op_table_finish_ptr_reg]; + end else if (enable && !op_table_active[op_table_start_ptr_reg] && s_axis_enqueue_req_valid && (!m_axis_enqueue_resp_valid || m_axis_enqueue_resp_ready) && !op_req_pipe_reg && !op_req_pipe_hazard) begin + // enqueue request + op_req_pipe_next[0] = 1'b1; + + s_axis_enqueue_req_ready_next = 1'b1; + + req_tag_pipeline_next[0] = s_axis_enqueue_req_tag; + + queue_ram_read_ptr = s_axis_enqueue_req_queue; + queue_ram_addr_pipeline_next[0] = s_axis_enqueue_req_queue; + end + + // read complete, perform operation + if (op_req_pipe_reg[PIPELINE-1]) begin + // request + m_axis_enqueue_resp_queue_next = queue_ram_addr_pipeline_reg[PIPELINE-1]; + m_axis_enqueue_resp_ptr_next = queue_ram_read_active_head_ptr; + m_axis_enqueue_resp_addr_next = queue_ram_read_data_base_addr + ((queue_ram_read_active_head_ptr & ({QUEUE_PTR_WIDTH{1'b1}} >> (QUEUE_PTR_WIDTH - queue_ram_read_data_log_size))) * CPL_SIZE); + m_axis_enqueue_resp_event_next = queue_ram_read_data_event; + m_axis_enqueue_resp_tag_next = req_tag_pipeline_reg[PIPELINE-1]; + m_axis_enqueue_resp_op_tag_next = op_table_start_ptr_reg; + m_axis_enqueue_resp_full_next = 1'b0; + m_axis_enqueue_resp_error_next = 1'b0; + + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data[63:56] = op_table_start_ptr_reg; + queue_ram_wr_en = 1'b1; + + op_table_start_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + op_table_start_queue_ptr = queue_ram_read_active_head_ptr + 1; + + if (!queue_ram_read_data_active) begin + // queue inactive + m_axis_enqueue_resp_error_next = 1'b1; + m_axis_enqueue_resp_valid_next = 1'b1; + end else if (queue_full) begin + // queue full + m_axis_enqueue_resp_full_next = 1'b1; + m_axis_enqueue_resp_valid_next = 1'b1; + end else begin + // start enqueue + m_axis_enqueue_resp_valid_next = 1'b1; + + queue_ram_be[7] = 1'b1; + + op_table_start_en = 1'b1; + end + end else if (op_commit_pipe_reg[PIPELINE-1]) begin + // commit + + // update head pointer + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data[15:0] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[1:0] = 2'b11; + queue_ram_wr_en = 1'b1; + + queue_ram_write_data[55:48] = queue_ram_read_data_pipeline_reg[PIPELINE-1][55:48]; + // generate event on head pointer update + if (queue_ram_read_data_armed) begin + m_axis_event_next = queue_ram_read_data_event; + m_axis_event_source_next = queue_ram_addr_pipeline_reg[PIPELINE-1]; + m_axis_event_valid_next = 1'b1; + + if (!queue_ram_read_data_continuous) begin + queue_ram_write_data[54] = 1'b0; + queue_ram_be[6] = 1'b1; + end + end + end else if (op_axil_write_pipe_reg[PIPELINE-1]) begin + // AXIL write + s_axil_bvalid_next = 1'b1; + + queue_ram_write_data = queue_ram_read_data_pipeline_reg[PIPELINE-1]; + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_wr_en = 1'b1; + + // TODO parametrize + case (axil_reg_pipeline_reg[PIPELINE-1]) + 3'd0: begin + // base address lower 32 + // base address is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[95:64] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[11:8] = write_strobe_pipeline_reg[PIPELINE-1]; + end + end + 3'd1: begin + // base address upper 32 + // base address is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[127:96] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[15:12] = write_strobe_pipeline_reg[PIPELINE-1]; + end + end + 3'd2: begin + queue_ram_write_data[55:48] = queue_ram_read_data_pipeline_reg[PIPELINE-1][55:48]; + // log size + // log size is read-only when queue is active + if (!queue_ram_read_data_active) begin + if (write_strobe_pipeline_reg[PIPELINE-1][0]) begin + queue_ram_write_data[51:48] = write_data_pipeline_reg[PIPELINE-1][3:0]; + queue_ram_be[6] = 1'b1; + end + end + // active + if (write_strobe_pipeline_reg[PIPELINE-1][3]) begin + queue_ram_write_data[55] = write_data_pipeline_reg[PIPELINE-1][31]; + queue_ram_be[6] = 1'b1; + end + end + 3'd3: begin + // event index + // event index is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[47:32] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[5:4] = write_strobe_pipeline_reg[PIPELINE-1]; + end + + queue_ram_write_data[55:48] = queue_ram_read_data_pipeline_reg[PIPELINE-1][55:48]; + // continuous + if (write_strobe_pipeline_reg[PIPELINE-1][3]) begin + queue_ram_write_data[53] = write_data_pipeline_reg[PIPELINE-1][30]; + queue_ram_be[6] = 1'b1; + end + // armed + if (write_strobe_pipeline_reg[PIPELINE-1][3]) begin + queue_ram_write_data[54] = write_data_pipeline_reg[PIPELINE-1][31]; + queue_ram_be[6] = 1'b1; + + if (write_data_pipeline_reg[PIPELINE-1][31] && (queue_ram_read_data_head_ptr != queue_ram_read_data_tail_ptr)) begin + // armed and queue not empty + // so generate event + m_axis_event_next = queue_ram_read_data_event; + m_axis_event_source_next = queue_ram_addr_pipeline_reg[PIPELINE-1]; + m_axis_event_valid_next = 1'b1; + + if (!write_data_pipeline_reg[PIPELINE-1][30]) begin + queue_ram_write_data[54] = 1'b0; + queue_ram_be[6] = 1'b1; + end + end + end + end + 3'd4: begin + // head pointer + // tail pointer is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[15:0] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[1:0] = write_strobe_pipeline_reg[PIPELINE-1]; + end + end + 3'd6: begin + // tail pointer + queue_ram_write_data[31:16] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[3:2] = write_strobe_pipeline_reg[PIPELINE-1]; + end + endcase + end else if (op_axil_read_pipe_reg[PIPELINE-1]) begin + // AXIL read + s_axil_rvalid_next = 1'b1; + s_axil_rdata_next = 0; + + // TODO parametrize + case (axil_reg_pipeline_reg[PIPELINE-1]) + 3'd0: begin + // base address lower 32 + s_axil_rdata_next = queue_ram_read_data_base_addr[31:0]; + end + 3'd1: begin + // base address upper 32 + s_axil_rdata_next = queue_ram_read_data_base_addr[63:32]; + end + 3'd2: begin + // log size + s_axil_rdata_next[3:0] = queue_ram_read_data_log_size; + // active + s_axil_rdata_next[31] = queue_ram_read_data_active; + end + 3'd3: begin + // event index + s_axil_rdata_next[29:0] = queue_ram_read_data_event; + s_axil_rdata_next[30] = queue_ram_read_data_continuous; + s_axil_rdata_next[31] = queue_ram_read_data_armed; + end + 3'd4: begin + // head pointer + s_axil_rdata_next = queue_ram_read_data_head_ptr; + end + 3'd6: begin + // tail pointer + s_axil_rdata_next = queue_ram_read_data_tail_ptr; + end + endcase + end + + // enqueue commit (record in table) + s_axis_enqueue_commit_ready_next = enable; + if (s_axis_enqueue_commit_ready && s_axis_enqueue_commit_valid) begin + op_table_commit_ptr = s_axis_enqueue_commit_op_tag; + op_table_commit_en = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + op_axil_write_pipe_reg <= {PIPELINE{1'b0}}; + op_axil_read_pipe_reg <= {PIPELINE{1'b0}}; + op_req_pipe_reg <= {PIPELINE{1'b0}}; + op_commit_pipe_reg <= {PIPELINE{1'b0}}; + + s_axis_enqueue_req_ready_reg <= 1'b0; + m_axis_enqueue_resp_valid_reg <= 1'b0; + s_axis_enqueue_commit_ready_reg <= 1'b0; + m_axis_event_valid_reg <= 1'b0; + + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + + op_table_active <= 0; + + op_table_start_ptr_reg <= 0; + op_table_finish_ptr_reg <= 0; + end else begin + op_axil_write_pipe_reg <= op_axil_write_pipe_next; + op_axil_read_pipe_reg <= op_axil_read_pipe_next; + op_req_pipe_reg <= op_req_pipe_next; + op_commit_pipe_reg <= op_commit_pipe_next; + + s_axis_enqueue_req_ready_reg <= s_axis_enqueue_req_ready_next; + m_axis_enqueue_resp_valid_reg <= m_axis_enqueue_resp_valid_next; + s_axis_enqueue_commit_ready_reg <= s_axis_enqueue_commit_ready_next; + m_axis_event_valid_reg <= m_axis_event_valid_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + if (op_table_start_en) begin + op_table_start_ptr_reg <= op_table_start_ptr_reg + 1; + op_table_active[op_table_start_ptr_reg] <= 1'b1; + end + if (op_table_finish_en) begin + op_table_finish_ptr_reg <= op_table_finish_ptr_reg + 1; + op_table_active[op_table_finish_ptr_reg] <= 1'b0; + end + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] <= queue_ram_addr_pipeline_next[i]; + axil_reg_pipeline_reg[i] <= axil_reg_pipeline_next[i]; + write_data_pipeline_reg[i] <= write_data_pipeline_next[i]; + write_strobe_pipeline_reg[i] <= write_strobe_pipeline_next[i]; + req_tag_pipeline_reg[i] <= req_tag_pipeline_next[i]; + end + + m_axis_enqueue_resp_queue_reg <= m_axis_enqueue_resp_queue_next; + m_axis_enqueue_resp_ptr_reg <= m_axis_enqueue_resp_ptr_next; + m_axis_enqueue_resp_addr_reg <= m_axis_enqueue_resp_addr_next; + m_axis_enqueue_resp_event_reg <= m_axis_enqueue_resp_event_next; + m_axis_enqueue_resp_tag_reg <= m_axis_enqueue_resp_tag_next; + m_axis_enqueue_resp_op_tag_reg <= m_axis_enqueue_resp_op_tag_next; + m_axis_enqueue_resp_full_reg <= m_axis_enqueue_resp_full_next; + m_axis_enqueue_resp_error_reg <= m_axis_enqueue_resp_error_next; + m_axis_event_reg <= m_axis_event_next; + m_axis_event_source_reg <= m_axis_event_source_next; + + s_axil_rdata_reg <= s_axil_rdata_next; + + if (queue_ram_wr_en) begin + for (i = 0; i < QUEUE_RAM_BE_WIDTH; i = i + 1) begin + if (queue_ram_be[i]) begin + queue_ram[queue_ram_write_ptr][i*8 +: 8] <= queue_ram_write_data[i*8 +: 8]; + end + end + end + queue_ram_read_data_reg <= queue_ram[queue_ram_read_ptr]; + queue_ram_read_data_pipeline_reg[1] <= queue_ram_read_data_reg; + for (i = 2; i < PIPELINE; i = i + 1) begin + queue_ram_read_data_pipeline_reg[i] <= queue_ram_read_data_pipeline_reg[i-1]; + end + + if (op_table_start_en) begin + op_table_commit[op_table_start_ptr_reg] <= 1'b0; + op_table_queue[op_table_start_ptr_reg] <= op_table_start_queue; + op_table_queue_ptr[op_table_start_ptr_reg] <= op_table_start_queue_ptr; + end + if (op_table_commit_en) begin + op_table_commit[op_table_commit_ptr] <= 1'b1; + end +end + +endmodule diff --git a/corundum/rtl/cpl_write.v b/corundum/rtl/cpl_write.v new file mode 100644 index 0000000000000000000000000000000000000000..114472db8e3b5dbae0463a7ed24d7d8fa1e26614 --- /dev/null +++ b/corundum/rtl/cpl_write.v @@ -0,0 +1,587 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Completion write module + */ +module cpl_write # +( + // Number of ports + parameter PORTS = 2, + // Select field width + parameter SELECT_WIDTH = $clog2(PORTS), + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // DMA RAM pipeline stages + parameter RAM_PIPELINE = 2, + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // DMA length field width + parameter DMA_LEN_WIDTH = 20, + // DMA tag field width + parameter DMA_TAG_WIDTH = 8, + // Transmit request tag field width + parameter REQ_TAG_WIDTH = 8, + // Queue request tag field width + parameter QUEUE_REQ_TAG_WIDTH = 8, + // Queue operation tag field width + parameter QUEUE_OP_TAG_WIDTH = 8, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 4, + // Completion size (in bytes) + parameter CPL_SIZE = 32, + // Descriptor table size (number of in-flight operations) + parameter DESC_TABLE_SIZE = 8 +) +( + input wire clk, + input wire rst, + + /* + * Completion write request input + */ + input wire [SELECT_WIDTH-1:0] s_axis_req_sel, + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_req_queue, + input wire [REQ_TAG_WIDTH-1:0] s_axis_req_tag, + input wire [CPL_SIZE*8-1:0] s_axis_req_data, + input wire s_axis_req_valid, + output wire s_axis_req_ready, + + /* + * Completion write request status output + */ + output wire [REQ_TAG_WIDTH-1:0] m_axis_req_status_tag, + output wire m_axis_req_status_full, + output wire m_axis_req_status_error, + output wire m_axis_req_status_valid, + + /* + * Completion enqueue request output + */ + output wire [PORTS*QUEUE_INDEX_WIDTH-1:0] m_axis_cpl_enqueue_req_queue, + output wire [PORTS*REQ_TAG_WIDTH-1:0] m_axis_cpl_enqueue_req_tag, + output wire [PORTS-1:0] m_axis_cpl_enqueue_req_valid, + input wire [PORTS-1:0] m_axis_cpl_enqueue_req_ready, + + /* + * Completion enqueue response input + */ + input wire [PORTS*DMA_ADDR_WIDTH-1:0] s_axis_cpl_enqueue_resp_addr, + input wire [PORTS*QUEUE_REQ_TAG_WIDTH-1:0] s_axis_cpl_enqueue_resp_tag, + input wire [PORTS*QUEUE_OP_TAG_WIDTH-1:0] s_axis_cpl_enqueue_resp_op_tag, + input wire [PORTS-1:0] s_axis_cpl_enqueue_resp_full, + input wire [PORTS-1:0] s_axis_cpl_enqueue_resp_error, + input wire [PORTS-1:0] s_axis_cpl_enqueue_resp_valid, + output wire [PORTS-1:0] s_axis_cpl_enqueue_resp_ready, + + /* + * Completion enqueue commit output + */ + output wire [PORTS*QUEUE_OP_TAG_WIDTH-1:0] m_axis_cpl_enqueue_commit_op_tag, + output wire [PORTS-1:0] m_axis_cpl_enqueue_commit_valid, + input wire [PORTS-1:0] m_axis_cpl_enqueue_commit_ready, + + /* + * DMA write descriptor output + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_dma_write_desc_dma_addr, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_dma_write_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_dma_write_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_dma_write_desc_tag, + output wire m_axis_dma_write_desc_valid, + input wire m_axis_dma_write_desc_ready, + + /* + * DMA write descriptor status input + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_dma_write_desc_status_tag, + input wire s_axis_dma_write_desc_status_valid, + + /* + * RAM interface + */ + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_rd_cmd_addr, + input wire [SEG_COUNT-1:0] dma_ram_rd_cmd_valid, + output wire [SEG_COUNT-1:0] dma_ram_rd_cmd_ready, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_rd_resp_data, + output wire [SEG_COUNT-1:0] dma_ram_rd_resp_valid, + input wire [SEG_COUNT-1:0] dma_ram_rd_resp_ready, + + /* + * Configuration + */ + input wire enable +); + +parameter CL_DESC_TABLE_SIZE = $clog2(DESC_TABLE_SIZE); +parameter DESC_PTR_MASK = {CL_DESC_TABLE_SIZE{1'b1}}; + +parameter CL_PORTS = $clog2(PORTS); + +// bus width assertions +initial begin + if (DMA_TAG_WIDTH < CL_DESC_TABLE_SIZE+1) begin + $error("Error: DMA tag width insufficient for descriptor table size (instance %m)"); + $finish; + end + + if (QUEUE_REQ_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: Queue request tag width insufficient for descriptor table size (instance %m)"); + $finish; + end +end + +reg s_axis_req_ready_reg = 1'b0, s_axis_req_ready_next; + +reg [REQ_TAG_WIDTH-1:0] m_axis_req_status_tag_reg = {REQ_TAG_WIDTH{1'b0}}, m_axis_req_status_tag_next; +reg m_axis_req_status_full_reg = 1'b0, m_axis_req_status_full_next; +reg m_axis_req_status_error_reg = 1'b0, m_axis_req_status_error_next; +reg m_axis_req_status_valid_reg = 1'b0, m_axis_req_status_valid_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_cpl_enqueue_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}, m_axis_cpl_enqueue_req_queue_next; +reg [QUEUE_REQ_TAG_WIDTH-1:0] m_axis_cpl_enqueue_req_tag_reg = {QUEUE_REQ_TAG_WIDTH{1'b0}}, m_axis_cpl_enqueue_req_tag_next; +reg [PORTS-1:0] m_axis_cpl_enqueue_req_valid_reg = {PORTS{1'b0}}, m_axis_cpl_enqueue_req_valid_next; + +reg [PORTS-1:0] s_axis_cpl_enqueue_resp_ready_reg = {PORTS{1'b0}}, s_axis_cpl_enqueue_resp_ready_next; + +reg [QUEUE_OP_TAG_WIDTH-1:0] m_axis_cpl_enqueue_commit_op_tag_reg = {QUEUE_OP_TAG_WIDTH{1'b0}}, m_axis_cpl_enqueue_commit_op_tag_next; +reg [PORTS-1:0] m_axis_cpl_enqueue_commit_valid_reg = {PORTS{1'b0}}, m_axis_cpl_enqueue_commit_valid_next; + +reg [DMA_ADDR_WIDTH-1:0] m_axis_dma_write_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}, m_axis_dma_write_desc_dma_addr_next; +reg [RAM_ADDR_WIDTH-1:0] m_axis_dma_write_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, m_axis_dma_write_desc_ram_addr_next; +reg [DMA_LEN_WIDTH-1:0] m_axis_dma_write_desc_len_reg = {DMA_LEN_WIDTH{1'b0}}, m_axis_dma_write_desc_len_next; +reg [DMA_TAG_WIDTH-1:0] m_axis_dma_write_desc_tag_reg = {DMA_TAG_WIDTH{1'b0}}, m_axis_dma_write_desc_tag_next; +reg m_axis_dma_write_desc_valid_reg = 1'b0, m_axis_dma_write_desc_valid_next; + +reg [DESC_TABLE_SIZE-1:0] desc_table_active = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_invalid = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_cpl_write_done = 0; +reg [CL_PORTS-1:0] desc_table_sel[DESC_TABLE_SIZE-1:0]; +reg [REQ_TAG_WIDTH-1:0] desc_table_tag[DESC_TABLE_SIZE-1:0]; +reg [QUEUE_OP_TAG_WIDTH-1:0] desc_table_queue_op_tag[DESC_TABLE_SIZE-1:0]; + +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_start_ptr_reg = 0; +reg [CL_PORTS-1:0] desc_table_start_sel; +reg [REQ_TAG_WIDTH-1:0] desc_table_start_tag; +reg [QUEUE_INDEX_WIDTH-1:0] desc_table_start_cpl_queue; +reg [QUEUE_OP_TAG_WIDTH-1:0] desc_table_start_queue_op_tag; +reg desc_table_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_enqueue_ptr; +reg [QUEUE_OP_TAG_WIDTH-1:0] desc_table_enqueue_queue_op_tag; +reg desc_table_enqueue_invalid; +reg desc_table_enqueue_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_cpl_write_done_ptr; +reg desc_table_cpl_write_done_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_finish_ptr_reg = 0; +reg desc_table_finish_en; + +reg [RAM_ADDR_WIDTH-1:0] dma_write_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, dma_write_desc_ram_addr_next; +reg [7:0] dma_write_desc_len_reg = 8'd0, dma_write_desc_len_next; +reg [CL_DESC_TABLE_SIZE-1:0] dma_write_desc_tag_reg = {CL_DESC_TABLE_SIZE{1'b0}}, dma_write_desc_tag_next; +reg dma_write_desc_user_reg = 1'b0, dma_write_desc_user_next; +reg dma_write_desc_valid_reg = 1'b0, dma_write_desc_valid_next; +wire dma_write_desc_ready; + +wire [CL_DESC_TABLE_SIZE-1:0] dma_write_desc_status_tag; +wire dma_write_desc_status_valid; + +reg [CPL_SIZE*8-1:0] cpl_data_reg = 0, cpl_data_next; +reg cpl_data_valid_reg = 1'b0, cpl_data_valid_next; +wire cpl_data_ready; + +assign s_axis_req_ready = s_axis_req_ready_reg; + +assign m_axis_req_status_tag = m_axis_req_status_tag_reg; +assign m_axis_req_status_full = m_axis_req_status_full_reg; +assign m_axis_req_status_error = m_axis_req_status_error_reg; +assign m_axis_req_status_valid = m_axis_req_status_valid_reg; + +assign m_axis_cpl_enqueue_req_queue = {PORTS{m_axis_cpl_enqueue_req_queue_reg}}; +assign m_axis_cpl_enqueue_req_tag = {PORTS{m_axis_cpl_enqueue_req_tag_reg}}; +assign m_axis_cpl_enqueue_req_valid = m_axis_cpl_enqueue_req_valid_reg; + +assign s_axis_cpl_enqueue_resp_ready = s_axis_cpl_enqueue_resp_ready_reg; + +assign m_axis_cpl_enqueue_commit_op_tag = {PORTS{m_axis_cpl_enqueue_commit_op_tag_reg}}; +assign m_axis_cpl_enqueue_commit_valid = m_axis_cpl_enqueue_commit_valid_reg; + +assign m_axis_dma_write_desc_dma_addr = m_axis_dma_write_desc_dma_addr_reg; +assign m_axis_dma_write_desc_ram_addr = m_axis_dma_write_desc_ram_addr_reg; +assign m_axis_dma_write_desc_len = m_axis_dma_write_desc_len_reg; +assign m_axis_dma_write_desc_tag = m_axis_dma_write_desc_tag_reg; +assign m_axis_dma_write_desc_valid = m_axis_dma_write_desc_valid_reg; + +wire [CL_PORTS-1:0] enqueue_resp_enc; +wire enqueue_resp_enc_valid; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY("HIGH") +) +op_table_start_enc_inst ( + .input_unencoded(s_axis_cpl_enqueue_resp_valid & ~s_axis_cpl_enqueue_resp_ready), + .output_valid(enqueue_resp_enc_valid), + .output_encoded(enqueue_resp_enc), + .output_unencoded() +); + +wire [SEG_COUNT*SEG_BE_WIDTH-1:0] dma_ram_wr_cmd_be_int; +wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_wr_cmd_addr_int; +wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_wr_cmd_data_int; +wire [SEG_COUNT-1:0] dma_ram_wr_cmd_valid_int; +wire [SEG_COUNT-1:0] dma_ram_wr_cmd_ready_int; + +dma_psdpram #( + .SIZE(DESC_TABLE_SIZE*SEG_COUNT*SEG_BE_WIDTH), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .PIPELINE(RAM_PIPELINE) +) +dma_psdpram_inst ( + /* + * Write port + */ + .clk_wr(clk), + .rst_wr(rst), + .wr_cmd_be(dma_ram_wr_cmd_be_int), + .wr_cmd_addr(dma_ram_wr_cmd_addr_int), + .wr_cmd_data(dma_ram_wr_cmd_data_int), + .wr_cmd_valid(dma_ram_wr_cmd_valid_int), + .wr_cmd_ready(dma_ram_wr_cmd_ready_int), + + /* + * Read port + */ + .clk_rd(clk), + .rst_rd(rst), + .rd_cmd_addr(dma_ram_rd_cmd_addr), + .rd_cmd_valid(dma_ram_rd_cmd_valid), + .rd_cmd_ready(dma_ram_rd_cmd_ready), + .rd_resp_data(dma_ram_rd_resp_data), + .rd_resp_valid(dma_ram_rd_resp_valid), + .rd_resp_ready(dma_ram_rd_resp_ready) +); + +dma_client_axis_sink #( + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .AXIS_DATA_WIDTH(CPL_SIZE*8), + .AXIS_KEEP_ENABLE(CPL_SIZE > 1), + .AXIS_KEEP_WIDTH(CPL_SIZE), + .AXIS_LAST_ENABLE(1), + .AXIS_ID_ENABLE(0), + .AXIS_DEST_ENABLE(0), + .AXIS_USER_ENABLE(1), + .AXIS_USER_WIDTH(1), + .LEN_WIDTH(8), + .TAG_WIDTH(CL_DESC_TABLE_SIZE) +) +dma_client_axis_sink_inst ( + .clk(clk), + .rst(rst), + + /* + * DMA write descriptor input + */ + .s_axis_write_desc_ram_addr(dma_write_desc_ram_addr_reg), + .s_axis_write_desc_len(dma_write_desc_len_reg), + .s_axis_write_desc_tag(dma_write_desc_tag_reg), + .s_axis_write_desc_valid(dma_write_desc_valid_reg), + .s_axis_write_desc_ready(dma_write_desc_ready), + + /* + * DMA write descriptor status output + */ + .m_axis_write_desc_status_len(), + .m_axis_write_desc_status_tag(dma_write_desc_status_tag), + .m_axis_write_desc_status_id(), + .m_axis_write_desc_status_dest(), + .m_axis_write_desc_status_user(), + .m_axis_write_desc_status_valid(dma_write_desc_status_valid), + + /* + * AXI stream write data input + */ + .s_axis_write_data_tdata(cpl_data_reg), + .s_axis_write_data_tkeep({CPL_SIZE{1'b1}}), + .s_axis_write_data_tvalid(cpl_data_valid_reg), + .s_axis_write_data_tready(cpl_data_ready), + .s_axis_write_data_tlast(1'b1), + .s_axis_write_data_tid(0), + .s_axis_write_data_tdest(0), + .s_axis_write_data_tuser(1'b0), + + /* + * RAM interface + */ + .ram_wr_cmd_be(dma_ram_wr_cmd_be_int), + .ram_wr_cmd_addr(dma_ram_wr_cmd_addr_int), + .ram_wr_cmd_data(dma_ram_wr_cmd_data_int), + .ram_wr_cmd_valid(dma_ram_wr_cmd_valid_int), + .ram_wr_cmd_ready(dma_ram_wr_cmd_ready_int), + + /* + * Configuration + */ + .enable(1'b1), + .abort(1'b0) +); + +always @* begin + s_axis_req_ready_next = 1'b0; + + m_axis_req_status_tag_next = m_axis_req_status_tag_reg; + m_axis_req_status_full_next = m_axis_req_status_full_reg; + m_axis_req_status_error_next = m_axis_req_status_error_reg; + m_axis_req_status_valid_next = 1'b0; + + m_axis_cpl_enqueue_req_queue_next = m_axis_cpl_enqueue_req_queue_reg; + m_axis_cpl_enqueue_req_tag_next = m_axis_cpl_enqueue_req_tag_reg; + m_axis_cpl_enqueue_req_valid_next = m_axis_cpl_enqueue_req_valid_reg & ~m_axis_cpl_enqueue_req_ready; + + s_axis_cpl_enqueue_resp_ready_next = 1'b0; + + m_axis_cpl_enqueue_commit_op_tag_next = m_axis_cpl_enqueue_commit_op_tag_reg; + m_axis_cpl_enqueue_commit_valid_next = m_axis_cpl_enqueue_commit_valid_reg & ~m_axis_cpl_enqueue_commit_ready; + + m_axis_dma_write_desc_dma_addr_next = m_axis_dma_write_desc_dma_addr_reg; + m_axis_dma_write_desc_ram_addr_next = m_axis_dma_write_desc_ram_addr_reg; + m_axis_dma_write_desc_len_next = m_axis_dma_write_desc_len_reg; + m_axis_dma_write_desc_tag_next = m_axis_dma_write_desc_tag_reg; + m_axis_dma_write_desc_valid_next = m_axis_dma_write_desc_valid_reg && !m_axis_dma_write_desc_ready; + + dma_write_desc_ram_addr_next = dma_write_desc_ram_addr_reg; + dma_write_desc_len_next = dma_write_desc_len_reg; + dma_write_desc_tag_next = dma_write_desc_tag_reg; + dma_write_desc_user_next = dma_write_desc_user_reg; + dma_write_desc_valid_next = dma_write_desc_valid_reg && !dma_write_desc_ready; + + cpl_data_next = cpl_data_reg; + cpl_data_valid_next = cpl_data_valid_reg && !cpl_data_ready; + + desc_table_start_sel = s_axis_req_sel; + desc_table_start_tag = s_axis_req_tag; + desc_table_start_en = 1'b0; + desc_table_enqueue_ptr = s_axis_cpl_enqueue_resp_tag[enqueue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH] & DESC_PTR_MASK; + desc_table_enqueue_queue_op_tag = s_axis_cpl_enqueue_resp_op_tag[enqueue_resp_enc*QUEUE_OP_TAG_WIDTH +: QUEUE_OP_TAG_WIDTH]; + desc_table_enqueue_invalid = 1'b0; + desc_table_enqueue_en = 1'b0; + desc_table_cpl_write_done_ptr = s_axis_dma_write_desc_status_tag & DESC_PTR_MASK; + desc_table_cpl_write_done_en = 1'b0; + desc_table_finish_en = 1'b0; + + // queue query + // wait for descriptor request + s_axis_req_ready_next = enable && !desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] && ($unsigned(desc_table_start_ptr_reg - desc_table_finish_ptr_reg) < DESC_TABLE_SIZE) && (!m_axis_cpl_enqueue_req_valid || (m_axis_cpl_enqueue_req_valid & m_axis_cpl_enqueue_req_ready)) && (!dma_write_desc_valid_reg) && (!cpl_data_valid_reg); + if (s_axis_req_ready && s_axis_req_valid) begin + s_axis_req_ready_next = 1'b0; + + // store in descriptor table + desc_table_start_sel = s_axis_req_sel; + desc_table_start_tag = s_axis_req_tag; + desc_table_start_en = 1'b1; + + // initiate queue query + m_axis_cpl_enqueue_req_queue_next = s_axis_req_queue; + m_axis_cpl_enqueue_req_tag_next = desc_table_start_ptr_reg & DESC_PTR_MASK; + m_axis_cpl_enqueue_req_valid_next = 1 << s_axis_req_sel; + + // initiate completion write to DMA RAM + cpl_data_next = s_axis_req_data; + cpl_data_valid_next = 1'b1; + + dma_write_desc_ram_addr_next = (desc_table_start_ptr_reg & DESC_PTR_MASK) << 5; + dma_write_desc_len_next = CPL_SIZE; + dma_write_desc_tag_next = (desc_table_start_ptr_reg & DESC_PTR_MASK); + dma_write_desc_valid_next = 1'b1; + end + + // finish completion write to DMA RAM + if (dma_write_desc_status_valid) begin + // update entry in descriptor table + // desc_table_cpl_write_done_ptr = s_axis_dma_write_desc_status_tag & DESC_PTR_MASK; + // desc_table_cpl_write_done_en = 1'b1; + end + + // start completion write + // wait for queue query response + if (enqueue_resp_enc_valid && !m_axis_dma_write_desc_valid_reg) begin + s_axis_cpl_enqueue_resp_ready_next = 1 << enqueue_resp_enc; + + // update entry in descriptor table + desc_table_enqueue_ptr = s_axis_cpl_enqueue_resp_tag[enqueue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH] & DESC_PTR_MASK; + desc_table_enqueue_queue_op_tag = s_axis_cpl_enqueue_resp_op_tag[enqueue_resp_enc*QUEUE_OP_TAG_WIDTH +: QUEUE_OP_TAG_WIDTH]; + desc_table_enqueue_invalid = 1'b0; + desc_table_enqueue_en = 1'b1; + + // return descriptor request completion + m_axis_req_status_tag_next = desc_table_tag[s_axis_cpl_enqueue_resp_tag[enqueue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH] & DESC_PTR_MASK]; + m_axis_req_status_full_next = s_axis_cpl_enqueue_resp_full[enqueue_resp_enc*1 +: 1]; + m_axis_req_status_error_next = s_axis_cpl_enqueue_resp_error[enqueue_resp_enc*1 +: 1]; + m_axis_req_status_valid_next = 1'b1; + + // initiate completion write + m_axis_dma_write_desc_dma_addr_next = s_axis_cpl_enqueue_resp_addr[enqueue_resp_enc*DMA_ADDR_WIDTH +: DMA_ADDR_WIDTH]; + m_axis_dma_write_desc_ram_addr_next = (s_axis_cpl_enqueue_resp_tag[enqueue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH] & DESC_PTR_MASK) << 5; + m_axis_dma_write_desc_len_next = CPL_SIZE; + m_axis_dma_write_desc_tag_next = (s_axis_cpl_enqueue_resp_tag[enqueue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH] & DESC_PTR_MASK); + + if (s_axis_cpl_enqueue_resp_error[enqueue_resp_enc*1 +: 1] || s_axis_cpl_enqueue_resp_full[enqueue_resp_enc*1 +: 1]) begin + // queue empty or not active + + // invalidate entry + desc_table_enqueue_invalid = 1'b1; + end else begin + // descriptor available to enqueue + + // initiate completion write + m_axis_dma_write_desc_valid_next = 1'b1; + end + end + + // finish completion write + if (s_axis_dma_write_desc_status_valid) begin + // update entry in descriptor table + desc_table_cpl_write_done_ptr = s_axis_dma_write_desc_status_tag & DESC_PTR_MASK; + desc_table_cpl_write_done_en = 1'b1; + end + + // operation complete + if (desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] && desc_table_finish_ptr_reg != desc_table_start_ptr_reg) begin + if (desc_table_invalid[desc_table_finish_ptr_reg & DESC_PTR_MASK]) begin + // invalidate entry in descriptor table + desc_table_finish_en = 1'b1; + + end else if (desc_table_cpl_write_done[desc_table_finish_ptr_reg & DESC_PTR_MASK] && !m_axis_cpl_enqueue_commit_valid) begin + // invalidate entry in descriptor table + desc_table_finish_en = 1'b1; + + // commit enqueue operation + m_axis_cpl_enqueue_commit_op_tag_next = desc_table_queue_op_tag[desc_table_finish_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_enqueue_commit_valid_next = 1 << desc_table_sel[desc_table_finish_ptr_reg & DESC_PTR_MASK]; + end + end +end + +always @(posedge clk) begin + s_axis_req_ready_reg <= s_axis_req_ready_next; + + m_axis_req_status_tag_reg <= m_axis_req_status_tag_next; + m_axis_req_status_full_reg <= m_axis_req_status_full_next; + m_axis_req_status_error_reg <= m_axis_req_status_error_next; + m_axis_req_status_valid_reg <= m_axis_req_status_valid_next; + + m_axis_cpl_enqueue_req_queue_reg <= m_axis_cpl_enqueue_req_queue_next; + m_axis_cpl_enqueue_req_tag_reg <= m_axis_cpl_enqueue_req_tag_next; + m_axis_cpl_enqueue_req_valid_reg <= m_axis_cpl_enqueue_req_valid_next; + + s_axis_cpl_enqueue_resp_ready_reg <= s_axis_cpl_enqueue_resp_ready_next; + + m_axis_cpl_enqueue_commit_op_tag_reg <= m_axis_cpl_enqueue_commit_op_tag_next; + m_axis_cpl_enqueue_commit_valid_reg <= m_axis_cpl_enqueue_commit_valid_next; + + m_axis_dma_write_desc_dma_addr_reg <= m_axis_dma_write_desc_dma_addr_next; + m_axis_dma_write_desc_ram_addr_reg <= m_axis_dma_write_desc_ram_addr_next; + m_axis_dma_write_desc_len_reg <= m_axis_dma_write_desc_len_next; + m_axis_dma_write_desc_tag_reg <= m_axis_dma_write_desc_tag_next; + m_axis_dma_write_desc_valid_reg <= m_axis_dma_write_desc_valid_next; + + dma_write_desc_ram_addr_reg <= dma_write_desc_ram_addr_next; + dma_write_desc_len_reg <= dma_write_desc_len_next; + dma_write_desc_tag_reg <= dma_write_desc_tag_next; + dma_write_desc_user_reg <= dma_write_desc_user_next; + dma_write_desc_valid_reg <= dma_write_desc_valid_next; + + cpl_data_reg <= cpl_data_next; + cpl_data_valid_reg <= cpl_data_valid_next; + + if (desc_table_start_en) begin + desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b1; + desc_table_invalid[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_cpl_write_done[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_sel[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_sel; + desc_table_tag[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_tag; + desc_table_start_ptr_reg <= desc_table_start_ptr_reg + 1; + end + + if (desc_table_enqueue_en) begin + desc_table_queue_op_tag[desc_table_enqueue_ptr & DESC_PTR_MASK] <= desc_table_enqueue_queue_op_tag; + desc_table_invalid[desc_table_enqueue_ptr & DESC_PTR_MASK] <= desc_table_enqueue_invalid; + end + + if (desc_table_cpl_write_done_en) begin + desc_table_cpl_write_done[desc_table_cpl_write_done_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_finish_en) begin + desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_finish_ptr_reg <= desc_table_finish_ptr_reg + 1; + end + + if (rst) begin + s_axis_req_ready_reg <= 1'b0; + m_axis_req_status_valid_reg <= 1'b0; + m_axis_cpl_enqueue_req_valid_reg <= 1'b0; + s_axis_cpl_enqueue_resp_ready_reg <= 1'b0; + m_axis_cpl_enqueue_commit_valid_reg <= 1'b0; + m_axis_dma_write_desc_valid_reg <= 1'b0; + + dma_write_desc_valid_reg <= 1'b0; + cpl_data_valid_reg <= 1'b0; + + desc_table_active <= 0; + desc_table_invalid <= 0; + + desc_table_start_ptr_reg <= 0; + desc_table_finish_ptr_reg <= 0; + end +end + +endmodule diff --git a/corundum/rtl/desc_fetch.v b/corundum/rtl/desc_fetch.v new file mode 100644 index 0000000000000000000000000000000000000000..e9b8a8298f42908c42de4cd33633838b8da83b8e --- /dev/null +++ b/corundum/rtl/desc_fetch.v @@ -0,0 +1,668 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Descriptor fetch module + */ +module desc_fetch # +( + // Number of ports + parameter PORTS = 2, + // Select field width + parameter SELECT_WIDTH = $clog2(PORTS), + // RAM segment count + parameter SEG_COUNT = 2, + // RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // DMA RAM pipeline stages + parameter RAM_PIPELINE = 2, + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // DMA length field width + parameter DMA_LEN_WIDTH = 20, + // DMA tag field width + parameter DMA_TAG_WIDTH = 8, + // Transmit request tag field width + parameter REQ_TAG_WIDTH = 8, + // Queue request tag field width + parameter QUEUE_REQ_TAG_WIDTH = 8, + // Queue operation tag field width + parameter QUEUE_OP_TAG_WIDTH = 8, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 4, + // Completion queue index width + parameter CPL_QUEUE_INDEX_WIDTH = 4, + // Queue element pointer width + parameter QUEUE_PTR_WIDTH = 16, + // Descriptor size (in bytes) + parameter DESC_SIZE = 16, + // Log desc block size field width + parameter LOG_BLOCK_SIZE_WIDTH = 2, + // Descriptor table size (number of in-flight operations) + parameter DESC_TABLE_SIZE = 8, + // Width of AXI stream interface in bits + parameter AXIS_DATA_WIDTH = DESC_SIZE*8, + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH/8 +) +( + input wire clk, + input wire rst, + + /* + * Descriptor read request input + */ + input wire [SELECT_WIDTH-1:0] s_axis_req_sel, + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_req_queue, + input wire [REQ_TAG_WIDTH-1:0] s_axis_req_tag, + input wire s_axis_req_valid, + output wire s_axis_req_ready, + + /* + * Descriptor read request status output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_queue, + output wire [QUEUE_PTR_WIDTH-1:0] m_axis_req_status_ptr, + output wire [CPL_QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_cpl, + output wire [REQ_TAG_WIDTH-1:0] m_axis_req_status_tag, + output wire m_axis_req_status_empty, + output wire m_axis_req_status_error, + output wire m_axis_req_status_valid, + + /* + * Descriptor data output + */ + output wire [AXIS_DATA_WIDTH-1:0] m_axis_desc_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] m_axis_desc_tkeep, + output wire m_axis_desc_tvalid, + input wire m_axis_desc_tready, + output wire m_axis_desc_tlast, + output wire [REQ_TAG_WIDTH-1:0] m_axis_desc_tid, + output wire m_axis_desc_tuser, + + /* + * Descriptor dequeue request output + */ + output wire [PORTS*QUEUE_INDEX_WIDTH-1:0] m_axis_desc_dequeue_req_queue, + output wire [PORTS*REQ_TAG_WIDTH-1:0] m_axis_desc_dequeue_req_tag, + output wire [PORTS-1:0] m_axis_desc_dequeue_req_valid, + input wire [PORTS-1:0] m_axis_desc_dequeue_req_ready, + + /* + * Descriptor dequeue response input + */ + input wire [PORTS*QUEUE_INDEX_WIDTH-1:0] s_axis_desc_dequeue_resp_queue, + input wire [PORTS*QUEUE_PTR_WIDTH-1:0] s_axis_desc_dequeue_resp_ptr, + input wire [PORTS*DMA_ADDR_WIDTH-1:0] s_axis_desc_dequeue_resp_addr, + input wire [PORTS*LOG_BLOCK_SIZE_WIDTH-1:0] s_axis_desc_dequeue_resp_block_size, + input wire [PORTS*CPL_QUEUE_INDEX_WIDTH-1:0] s_axis_desc_dequeue_resp_cpl, + input wire [PORTS*QUEUE_REQ_TAG_WIDTH-1:0] s_axis_desc_dequeue_resp_tag, + input wire [PORTS*QUEUE_OP_TAG_WIDTH-1:0] s_axis_desc_dequeue_resp_op_tag, + input wire [PORTS-1:0] s_axis_desc_dequeue_resp_empty, + input wire [PORTS-1:0] s_axis_desc_dequeue_resp_error, + input wire [PORTS-1:0] s_axis_desc_dequeue_resp_valid, + output wire [PORTS-1:0] s_axis_desc_dequeue_resp_ready, + + /* + * Descriptor dequeue commit output + */ + output wire [PORTS*QUEUE_OP_TAG_WIDTH-1:0] m_axis_desc_dequeue_commit_op_tag, + output wire [PORTS-1:0] m_axis_desc_dequeue_commit_valid, + input wire [PORTS-1:0] m_axis_desc_dequeue_commit_ready, + + /* + * DMA read descriptor output + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_dma_read_desc_dma_addr, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_dma_read_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_dma_read_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_dma_read_desc_tag, + output wire m_axis_dma_read_desc_valid, + input wire m_axis_dma_read_desc_ready, + + /* + * DMA read descriptor status input + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_dma_read_desc_status_tag, + input wire s_axis_dma_read_desc_status_valid, + + /* + * RAM interface + */ + input wire [SEG_COUNT*SEG_BE_WIDTH-1:0] dma_ram_wr_cmd_be, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_wr_cmd_addr, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_wr_cmd_data, + input wire [SEG_COUNT-1:0] dma_ram_wr_cmd_valid, + output wire [SEG_COUNT-1:0] dma_ram_wr_cmd_ready, + + /* + * Configuration + */ + input wire enable +); + +parameter CL_DESC_TABLE_SIZE = $clog2(DESC_TABLE_SIZE); +parameter DESC_PTR_MASK = {CL_DESC_TABLE_SIZE{1'b1}}; + +parameter CL_PORTS = $clog2(PORTS); + +parameter CL_DESC_SIZE = $clog2(DESC_SIZE); + +// bus width assertions +initial begin + if (DMA_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: DMA tag width insufficient for descriptor table size (instance %m)"); + $finish; + end + + if (QUEUE_REQ_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: Queue request tag width insufficient for descriptor table size (instance %m)"); + $finish; + end + + if (QUEUE_REQ_TAG_WIDTH < REQ_TAG_WIDTH) begin + $error("Error: QUEUE_REQ_TAG_WIDTH must be at least REQ_TAG_WIDTH (instance %m)"); + $finish; + end + + if (AXIS_KEEP_WIDTH * 8 != AXIS_DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (2**CL_DESC_SIZE != DESC_SIZE) begin + $error("Error: Descriptor size must be even power of two (instance %m)"); + $finish; + end +end + +reg s_axis_req_ready_reg = 1'b0, s_axis_req_ready_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}, m_axis_req_status_queue_next; +reg [QUEUE_PTR_WIDTH-1:0] m_axis_req_status_ptr_reg = {QUEUE_PTR_WIDTH{1'b0}}, m_axis_req_status_ptr_next; +reg [CPL_QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_cpl_reg = {CPL_QUEUE_INDEX_WIDTH{1'b0}}, m_axis_req_status_cpl_next; +reg [REQ_TAG_WIDTH-1:0] m_axis_req_status_tag_reg = {REQ_TAG_WIDTH{1'b0}}, m_axis_req_status_tag_next; +reg m_axis_req_status_empty_reg = 1'b0, m_axis_req_status_empty_next; +reg m_axis_req_status_error_reg = 1'b0, m_axis_req_status_error_next; +reg m_axis_req_status_valid_reg = 1'b0, m_axis_req_status_valid_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_desc_dequeue_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}, m_axis_desc_dequeue_req_queue_next; +reg [QUEUE_REQ_TAG_WIDTH-1:0] m_axis_desc_dequeue_req_tag_reg = {QUEUE_REQ_TAG_WIDTH{1'b0}}, m_axis_desc_dequeue_req_tag_next; +reg [PORTS-1:0] m_axis_desc_dequeue_req_valid_reg = {PORTS{1'b0}}, m_axis_desc_dequeue_req_valid_next; + +reg [PORTS-1:0] s_axis_desc_dequeue_resp_ready_reg = {PORTS{1'b0}}, s_axis_desc_dequeue_resp_ready_next; + +reg [QUEUE_OP_TAG_WIDTH-1:0] m_axis_desc_dequeue_commit_op_tag_reg = {QUEUE_OP_TAG_WIDTH{1'b0}}, m_axis_desc_dequeue_commit_op_tag_next; +reg [PORTS-1:0] m_axis_desc_dequeue_commit_valid_reg = {PORTS{1'b0}}, m_axis_desc_dequeue_commit_valid_next; + +reg [DMA_ADDR_WIDTH-1:0] m_axis_dma_read_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}, m_axis_dma_read_desc_dma_addr_next; +reg [RAM_ADDR_WIDTH-1:0] m_axis_dma_read_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, m_axis_dma_read_desc_ram_addr_next; +reg [DMA_LEN_WIDTH-1:0] m_axis_dma_read_desc_len_reg = {DMA_LEN_WIDTH{1'b0}}, m_axis_dma_read_desc_len_next; +reg [DMA_TAG_WIDTH-1:0] m_axis_dma_read_desc_tag_reg = {DMA_TAG_WIDTH{1'b0}}, m_axis_dma_read_desc_tag_next; +reg m_axis_dma_read_desc_valid_reg = 1'b0, m_axis_dma_read_desc_valid_next; + +reg [CL_DESC_TABLE_SIZE+1-1:0] active_count_reg = 0; +reg inc_active; +reg dec_active_1; +reg dec_active_2; + +reg [DESC_TABLE_SIZE-1:0] desc_table_active = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_desc_fetched = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_desc_read_done = 0; +reg [CL_PORTS-1:0] desc_table_sel[DESC_TABLE_SIZE-1:0]; +reg [LOG_BLOCK_SIZE_WIDTH-1:0] desc_table_log_desc_block_size[DESC_TABLE_SIZE-1:0]; +reg [REQ_TAG_WIDTH-1:0] desc_table_tag[DESC_TABLE_SIZE-1:0]; +reg [QUEUE_OP_TAG_WIDTH-1:0] desc_table_queue_op_tag[DESC_TABLE_SIZE-1:0]; + +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_start_ptr_reg = 0; +reg [CL_PORTS-1:0] desc_table_start_sel; +reg [LOG_BLOCK_SIZE_WIDTH-1:0] desc_table_start_log_desc_block_size; +reg [REQ_TAG_WIDTH-1:0] desc_table_start_tag; +reg [QUEUE_OP_TAG_WIDTH-1:0] desc_table_start_queue_op_tag; +reg desc_table_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_desc_fetched_ptr; +reg desc_table_desc_fetched_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_desc_read_ptr_reg = 0; +reg desc_table_desc_read_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_desc_read_done_ptr; +reg desc_table_desc_read_done_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_finish_ptr_reg = 0; +reg desc_table_finish_en; + +reg [RAM_ADDR_WIDTH-1:0] dma_read_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, dma_read_desc_ram_addr_next; +reg [7:0] dma_read_desc_len_reg = 8'd0, dma_read_desc_len_next; +reg [CL_DESC_TABLE_SIZE-1:0] dma_read_desc_tag_reg = {CL_DESC_TABLE_SIZE{1'b0}}, dma_read_desc_tag_next; +reg [REQ_TAG_WIDTH-1:0] dma_read_desc_id_reg = {REQ_TAG_WIDTH{1'b0}}, dma_read_desc_id_next; +reg dma_read_desc_user_reg = 1'b0, dma_read_desc_user_next; +reg dma_read_desc_valid_reg = 1'b0, dma_read_desc_valid_next; +wire dma_read_desc_ready; + +wire [CL_DESC_TABLE_SIZE-1:0] dma_read_desc_status_tag; +wire dma_read_desc_status_valid; + +assign s_axis_req_ready = s_axis_req_ready_reg; + +assign m_axis_req_status_queue = m_axis_req_status_queue_reg; +assign m_axis_req_status_ptr = m_axis_req_status_ptr_reg; +assign m_axis_req_status_cpl = m_axis_req_status_cpl_reg; +assign m_axis_req_status_tag = m_axis_req_status_tag_reg; +assign m_axis_req_status_empty = m_axis_req_status_empty_reg; +assign m_axis_req_status_error = m_axis_req_status_error_reg; +assign m_axis_req_status_valid = m_axis_req_status_valid_reg; + +assign m_axis_desc_dequeue_req_queue = {PORTS{m_axis_desc_dequeue_req_queue_reg}}; +assign m_axis_desc_dequeue_req_tag = {PORTS{m_axis_desc_dequeue_req_tag_reg}}; +assign m_axis_desc_dequeue_req_valid = m_axis_desc_dequeue_req_valid_reg; + +assign s_axis_desc_dequeue_resp_ready = s_axis_desc_dequeue_resp_ready_reg; + +assign m_axis_desc_dequeue_commit_op_tag = {PORTS{m_axis_desc_dequeue_commit_op_tag_reg}}; +assign m_axis_desc_dequeue_commit_valid = m_axis_desc_dequeue_commit_valid_reg; + +assign m_axis_dma_read_desc_dma_addr = m_axis_dma_read_desc_dma_addr_reg; +assign m_axis_dma_read_desc_ram_addr = m_axis_dma_read_desc_ram_addr_reg; +assign m_axis_dma_read_desc_len = m_axis_dma_read_desc_len_reg; +assign m_axis_dma_read_desc_tag = m_axis_dma_read_desc_tag_reg; +assign m_axis_dma_read_desc_valid = m_axis_dma_read_desc_valid_reg; + +wire [CL_PORTS-1:0] dequeue_resp_enc; +wire dequeue_resp_enc_valid; + +priority_encoder #( + .WIDTH(PORTS), + .LSB_PRIORITY("HIGH") +) +op_table_start_enc_inst ( + .input_unencoded(s_axis_desc_dequeue_resp_valid & ~s_axis_desc_dequeue_resp_ready), + .output_valid(dequeue_resp_enc_valid), + .output_encoded(dequeue_resp_enc), + .output_unencoded() +); + +wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_rd_cmd_addr_int; +wire [SEG_COUNT-1:0] dma_ram_rd_cmd_valid_int; +wire [SEG_COUNT-1:0] dma_ram_rd_cmd_ready_int; +wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_rd_resp_data_int; +wire [SEG_COUNT-1:0] dma_ram_rd_resp_valid_int; +wire [SEG_COUNT-1:0] dma_ram_rd_resp_ready_int; + +dma_psdpram #( + .SIZE(DESC_TABLE_SIZE*DESC_SIZE*(2**((2**LOG_BLOCK_SIZE_WIDTH)-1))), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .PIPELINE(RAM_PIPELINE) +) +dma_psdpram_inst ( + /* + * Write port + */ + .clk_wr(clk), + .rst_wr(rst), + .wr_cmd_be(dma_ram_wr_cmd_be), + .wr_cmd_addr(dma_ram_wr_cmd_addr), + .wr_cmd_data(dma_ram_wr_cmd_data), + .wr_cmd_valid(dma_ram_wr_cmd_valid), + .wr_cmd_ready(dma_ram_wr_cmd_ready), + + /* + * Read port + */ + .clk_rd(clk), + .rst_rd(rst), + .rd_cmd_addr(dma_ram_rd_cmd_addr_int), + .rd_cmd_valid(dma_ram_rd_cmd_valid_int), + .rd_cmd_ready(dma_ram_rd_cmd_ready_int), + .rd_resp_data(dma_ram_rd_resp_data_int), + .rd_resp_valid(dma_ram_rd_resp_valid_int), + .rd_resp_ready(dma_ram_rd_resp_ready_int) +); + +dma_client_axis_source #( + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH), + .AXIS_KEEP_ENABLE(AXIS_KEEP_WIDTH > 1), + .AXIS_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .AXIS_LAST_ENABLE(1), + .AXIS_ID_ENABLE(1), + .AXIS_ID_WIDTH(REQ_TAG_WIDTH), + .AXIS_DEST_ENABLE(0), + .AXIS_USER_ENABLE(1), + .AXIS_USER_WIDTH(1), + .LEN_WIDTH(8), + .TAG_WIDTH(CL_DESC_TABLE_SIZE) +) +dma_client_axis_source_inst ( + .clk(clk), + .rst(rst), + + /* + * DMA read descriptor input + */ + .s_axis_read_desc_ram_addr(dma_read_desc_ram_addr_reg), + .s_axis_read_desc_len(dma_read_desc_len_reg), + .s_axis_read_desc_tag(dma_read_desc_tag_reg), + .s_axis_read_desc_id(dma_read_desc_id_reg), + .s_axis_read_desc_dest(0), + .s_axis_read_desc_user(dma_read_desc_user_reg), + .s_axis_read_desc_valid(dma_read_desc_valid_reg), + .s_axis_read_desc_ready(dma_read_desc_ready), + + /* + * DMA read descriptor status output + */ + .m_axis_read_desc_status_tag(dma_read_desc_status_tag), + .m_axis_read_desc_status_valid(dma_read_desc_status_valid), + + /* + * AXI stream read data output + */ + .m_axis_read_data_tdata(m_axis_desc_tdata), + .m_axis_read_data_tkeep(m_axis_desc_tkeep), + .m_axis_read_data_tvalid(m_axis_desc_tvalid), + .m_axis_read_data_tready(m_axis_desc_tready), + .m_axis_read_data_tlast(m_axis_desc_tlast), + .m_axis_read_data_tid(m_axis_desc_tid), + .m_axis_read_data_tdest(), + .m_axis_read_data_tuser(m_axis_desc_tuser), + + /* + * RAM interface + */ + .ram_rd_cmd_addr(dma_ram_rd_cmd_addr_int), + .ram_rd_cmd_valid(dma_ram_rd_cmd_valid_int), + .ram_rd_cmd_ready(dma_ram_rd_cmd_ready_int), + .ram_rd_resp_data(dma_ram_rd_resp_data_int), + .ram_rd_resp_valid(dma_ram_rd_resp_valid_int), + .ram_rd_resp_ready(dma_ram_rd_resp_ready_int), + + /* + * Configuration + */ + .enable(1'b1) +); + +always @* begin + s_axis_req_ready_next = 1'b0; + + m_axis_req_status_queue_next = m_axis_req_status_queue_reg; + m_axis_req_status_ptr_next = m_axis_req_status_ptr_reg; + m_axis_req_status_cpl_next = m_axis_req_status_cpl_reg; + m_axis_req_status_tag_next = m_axis_req_status_tag_reg; + m_axis_req_status_empty_next = m_axis_req_status_empty_reg; + m_axis_req_status_error_next = m_axis_req_status_error_reg; + m_axis_req_status_valid_next = 1'b0; + + m_axis_desc_dequeue_req_queue_next = m_axis_desc_dequeue_req_queue_reg; + m_axis_desc_dequeue_req_tag_next = m_axis_desc_dequeue_req_tag_reg; + m_axis_desc_dequeue_req_valid_next = m_axis_desc_dequeue_req_valid_reg & ~m_axis_desc_dequeue_req_ready; + + s_axis_desc_dequeue_resp_ready_next = {PORTS{1'b0}}; + + m_axis_desc_dequeue_commit_op_tag_next = m_axis_desc_dequeue_commit_op_tag_reg; + m_axis_desc_dequeue_commit_valid_next = m_axis_desc_dequeue_commit_valid_reg & ~m_axis_desc_dequeue_commit_ready; + + m_axis_dma_read_desc_dma_addr_next = m_axis_dma_read_desc_dma_addr_reg; + m_axis_dma_read_desc_ram_addr_next = m_axis_dma_read_desc_ram_addr_reg; + m_axis_dma_read_desc_len_next = m_axis_dma_read_desc_len_reg; + m_axis_dma_read_desc_tag_next = m_axis_dma_read_desc_tag_reg; + m_axis_dma_read_desc_valid_next = m_axis_dma_read_desc_valid_reg && !m_axis_dma_read_desc_ready; + + dma_read_desc_ram_addr_next = dma_read_desc_ram_addr_reg; + dma_read_desc_len_next = dma_read_desc_len_reg; + dma_read_desc_tag_next = dma_read_desc_tag_reg; + dma_read_desc_id_next = dma_read_desc_id_reg; + dma_read_desc_user_next = dma_read_desc_user_reg; + dma_read_desc_valid_next = dma_read_desc_valid_reg && !dma_read_desc_ready; + + inc_active = 1'b0; + dec_active_1 = 1'b0; + dec_active_2 = 1'b0; + + desc_table_start_sel = dequeue_resp_enc; + desc_table_start_log_desc_block_size = s_axis_desc_dequeue_resp_block_size[dequeue_resp_enc*LOG_BLOCK_SIZE_WIDTH +: LOG_BLOCK_SIZE_WIDTH]; + desc_table_start_tag = s_axis_desc_dequeue_resp_tag[dequeue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH]; + desc_table_start_queue_op_tag = s_axis_desc_dequeue_resp_op_tag[dequeue_resp_enc*QUEUE_OP_TAG_WIDTH +: QUEUE_OP_TAG_WIDTH]; + desc_table_start_en = 1'b0; + desc_table_desc_fetched_ptr = s_axis_dma_read_desc_status_tag & DESC_PTR_MASK; + desc_table_desc_fetched_en = 1'b0; + desc_table_desc_read_en = 1'b0; + desc_table_desc_read_done_ptr = dma_read_desc_status_tag & DESC_PTR_MASK; + desc_table_desc_read_done_en = 1'b0; + desc_table_finish_en = 1'b0; + + // queue query + // wait for descriptor request + s_axis_req_ready_next = enable && active_count_reg < DESC_TABLE_SIZE && !desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] && ($unsigned(desc_table_start_ptr_reg - desc_table_finish_ptr_reg) < DESC_TABLE_SIZE) && (!m_axis_desc_dequeue_req_valid_reg || (m_axis_desc_dequeue_req_valid_reg & m_axis_desc_dequeue_req_ready)); + if (s_axis_req_ready && s_axis_req_valid) begin + s_axis_req_ready_next = 1'b0; + + // initiate queue query + m_axis_desc_dequeue_req_queue_next = s_axis_req_queue; + m_axis_desc_dequeue_req_tag_next = s_axis_req_tag; + m_axis_desc_dequeue_req_valid_next = 1 << s_axis_req_sel; + + inc_active = 1'b1; + end + + // descriptor fetch + // wait for queue query response + if (dequeue_resp_enc_valid && !m_axis_dma_read_desc_valid_reg && !desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] && ($unsigned(desc_table_start_ptr_reg - desc_table_finish_ptr_reg) < DESC_TABLE_SIZE)) begin + s_axis_desc_dequeue_resp_ready_next = 1 << dequeue_resp_enc; + + // store in descriptor table + desc_table_start_sel = dequeue_resp_enc; + desc_table_start_log_desc_block_size = s_axis_desc_dequeue_resp_block_size[dequeue_resp_enc*LOG_BLOCK_SIZE_WIDTH +: LOG_BLOCK_SIZE_WIDTH]; + desc_table_start_tag = s_axis_desc_dequeue_resp_tag[dequeue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH]; + desc_table_start_queue_op_tag = s_axis_desc_dequeue_resp_op_tag[dequeue_resp_enc*QUEUE_OP_TAG_WIDTH +: QUEUE_OP_TAG_WIDTH]; + + // return descriptor request completion + m_axis_req_status_queue_next = s_axis_desc_dequeue_resp_queue[dequeue_resp_enc*QUEUE_INDEX_WIDTH +: QUEUE_INDEX_WIDTH]; + m_axis_req_status_ptr_next = s_axis_desc_dequeue_resp_ptr[dequeue_resp_enc*QUEUE_PTR_WIDTH +: QUEUE_PTR_WIDTH]; + m_axis_req_status_cpl_next = s_axis_desc_dequeue_resp_cpl[dequeue_resp_enc*CPL_QUEUE_INDEX_WIDTH +: CPL_QUEUE_INDEX_WIDTH]; + m_axis_req_status_tag_next = s_axis_desc_dequeue_resp_tag[dequeue_resp_enc*QUEUE_REQ_TAG_WIDTH +: QUEUE_REQ_TAG_WIDTH]; + m_axis_req_status_empty_next = s_axis_desc_dequeue_resp_empty[dequeue_resp_enc*1 +: 1]; + m_axis_req_status_error_next = s_axis_desc_dequeue_resp_error[dequeue_resp_enc*1 +: 1]; + m_axis_req_status_valid_next = 1'b1; + + // initiate descriptor fetch + m_axis_dma_read_desc_dma_addr_next = s_axis_desc_dequeue_resp_addr[dequeue_resp_enc*DMA_ADDR_WIDTH +: DMA_ADDR_WIDTH]; + m_axis_dma_read_desc_ram_addr_next = (desc_table_start_ptr_reg & DESC_PTR_MASK) << (CL_DESC_SIZE+(2**LOG_BLOCK_SIZE_WIDTH)-1); + m_axis_dma_read_desc_len_next = DESC_SIZE << s_axis_desc_dequeue_resp_block_size[dequeue_resp_enc*LOG_BLOCK_SIZE_WIDTH +: LOG_BLOCK_SIZE_WIDTH]; + m_axis_dma_read_desc_tag_next = (desc_table_start_ptr_reg & DESC_PTR_MASK); + + if (s_axis_desc_dequeue_resp_error[dequeue_resp_enc*1 +: 1] || s_axis_desc_dequeue_resp_empty[dequeue_resp_enc*1 +: 1]) begin + // queue empty or not active + + dec_active_1 = 1'b1; + end else begin + // descriptor available to dequeue + + // store in descriptor table + desc_table_start_en = 1'b1; + + // initiate descriptor fetch + m_axis_dma_read_desc_valid_next = 1'b1; + end + end + + // descriptor fetch completion + // wait for descriptor fetch completion + if (s_axis_dma_read_desc_status_valid) begin + // update entry in descriptor table + desc_table_desc_fetched_ptr = s_axis_dma_read_desc_status_tag & DESC_PTR_MASK; + desc_table_desc_fetched_en = 1'b1; + end + + // return descriptor + // wait for descriptor fetch completion + // TODO descriptor validation? + if (desc_table_active[desc_table_desc_read_ptr_reg & DESC_PTR_MASK] && desc_table_desc_read_ptr_reg != desc_table_start_ptr_reg) begin + if (desc_table_desc_fetched[desc_table_desc_read_ptr_reg & DESC_PTR_MASK] && !(m_axis_desc_dequeue_commit_valid & 1 << desc_table_sel[desc_table_desc_read_ptr_reg & DESC_PTR_MASK]) && !dma_read_desc_valid_reg) begin + // update entry in descriptor table + desc_table_desc_read_en = 1'b1; + + // commit dequeue operation + m_axis_desc_dequeue_commit_op_tag_next = desc_table_queue_op_tag[desc_table_desc_read_ptr_reg & DESC_PTR_MASK]; + m_axis_desc_dequeue_commit_valid_next = 1 << desc_table_sel[desc_table_desc_read_ptr_reg & DESC_PTR_MASK]; + + // initiate descriptor read from DMA RAM + dma_read_desc_ram_addr_next = (desc_table_desc_read_ptr_reg & DESC_PTR_MASK) << (CL_DESC_SIZE+(2**LOG_BLOCK_SIZE_WIDTH)-1); + dma_read_desc_len_next = DESC_SIZE << desc_table_log_desc_block_size[desc_table_desc_read_ptr_reg & DESC_PTR_MASK]; + dma_read_desc_tag_next = (desc_table_desc_read_ptr_reg & DESC_PTR_MASK); + dma_read_desc_id_next = desc_table_tag[desc_table_desc_read_ptr_reg & DESC_PTR_MASK]; + dma_read_desc_user_next = 1'b0; + dma_read_desc_valid_next = 1'b1; + end + end + + // descriptor read completion + // wait for descriptor read completion + if (dma_read_desc_status_valid) begin + // update entry in descriptor table + desc_table_desc_read_done_ptr = dma_read_desc_status_tag & DESC_PTR_MASK; + desc_table_desc_read_done_en = 1'b1; + end + + // finish operation + // wait for descriptor read completion + if (desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] && desc_table_finish_ptr_reg != desc_table_start_ptr_reg) begin + if (desc_table_desc_read_done[desc_table_finish_ptr_reg & DESC_PTR_MASK]) begin + // invalidate entry in descriptor table + desc_table_finish_en = 1'b1; + + dec_active_2 = 1'b1; + end + end +end + +always @(posedge clk) begin + s_axis_req_ready_reg <= s_axis_req_ready_next; + + m_axis_req_status_queue_reg <= m_axis_req_status_queue_next; + m_axis_req_status_ptr_reg <= m_axis_req_status_ptr_next; + m_axis_req_status_cpl_reg <= m_axis_req_status_cpl_next; + m_axis_req_status_tag_reg <= m_axis_req_status_tag_next; + m_axis_req_status_empty_reg <= m_axis_req_status_empty_next; + m_axis_req_status_error_reg <= m_axis_req_status_error_next; + m_axis_req_status_valid_reg <= m_axis_req_status_valid_next; + + m_axis_desc_dequeue_req_queue_reg <= m_axis_desc_dequeue_req_queue_next; + m_axis_desc_dequeue_req_tag_reg <= m_axis_desc_dequeue_req_tag_next; + m_axis_desc_dequeue_req_valid_reg <= m_axis_desc_dequeue_req_valid_next; + + s_axis_desc_dequeue_resp_ready_reg <= s_axis_desc_dequeue_resp_ready_next; + + m_axis_desc_dequeue_commit_op_tag_reg <= m_axis_desc_dequeue_commit_op_tag_next; + m_axis_desc_dequeue_commit_valid_reg <= m_axis_desc_dequeue_commit_valid_next; + + m_axis_dma_read_desc_dma_addr_reg <= m_axis_dma_read_desc_dma_addr_next; + m_axis_dma_read_desc_ram_addr_reg <= m_axis_dma_read_desc_ram_addr_next; + m_axis_dma_read_desc_len_reg <= m_axis_dma_read_desc_len_next; + m_axis_dma_read_desc_tag_reg <= m_axis_dma_read_desc_tag_next; + m_axis_dma_read_desc_valid_reg <= m_axis_dma_read_desc_valid_next; + + dma_read_desc_ram_addr_reg <= dma_read_desc_ram_addr_next; + dma_read_desc_len_reg <= dma_read_desc_len_next; + dma_read_desc_tag_reg <= dma_read_desc_tag_next; + dma_read_desc_id_reg <= dma_read_desc_id_next; + dma_read_desc_user_reg <= dma_read_desc_user_next; + dma_read_desc_valid_reg <= dma_read_desc_valid_next; + + active_count_reg <= active_count_reg + inc_active - dec_active_1 - dec_active_2; + + if (desc_table_start_en) begin + desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b1; + desc_table_desc_fetched[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_desc_read_done[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_sel[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_sel; + desc_table_log_desc_block_size[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_log_desc_block_size; + desc_table_tag[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_tag; + desc_table_queue_op_tag[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_queue_op_tag; + desc_table_start_ptr_reg <= desc_table_start_ptr_reg + 1; + end + + if (desc_table_desc_fetched_en) begin + desc_table_desc_fetched[desc_table_desc_fetched_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_desc_read_en) begin + desc_table_desc_read_ptr_reg <= desc_table_desc_read_ptr_reg + 1; + end + + if (desc_table_desc_read_done_en) begin + desc_table_desc_read_done[desc_table_desc_read_done_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_finish_en) begin + desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_finish_ptr_reg <= desc_table_finish_ptr_reg + 1; + end + + if (rst) begin + s_axis_req_ready_reg <= 1'b0; + m_axis_req_status_valid_reg <= 1'b0; + m_axis_desc_dequeue_req_valid_reg <= {PORTS{1'b0}}; + s_axis_desc_dequeue_resp_ready_reg <= {PORTS{1'b0}}; + m_axis_desc_dequeue_commit_valid_reg <= {PORTS{1'b0}}; + m_axis_dma_read_desc_valid_reg <= 1'b0; + + dma_read_desc_valid_reg <= 1'b0; + + active_count_reg <= 0; + + desc_table_active <= 0; + desc_table_desc_fetched <= 0; + + desc_table_start_ptr_reg <= 0; + desc_table_desc_read_ptr_reg <= 0; + desc_table_finish_ptr_reg <= 0; + end +end + +endmodule diff --git a/corundum/rtl/desc_op_mux.v b/corundum/rtl/desc_op_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..f8948995cfd9df7b29cfdeae8927896f30b3fbe9 --- /dev/null +++ b/corundum/rtl/desc_op_mux.v @@ -0,0 +1,435 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Descriptor operation mux + */ +module desc_op_mux # +( + // Number of ports + parameter PORTS = 2, + // Select field width + parameter SELECT_WIDTH = 1, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 4, + // Queue element pointer width + parameter QUEUE_PTR_WIDTH = 16, + // Completion queue index width + parameter CPL_QUEUE_INDEX_WIDTH = 4, + // Input request tag field width + parameter S_REQ_TAG_WIDTH = 8, + // Output request tag field width (towards descriptor module) + // Additional bits required for response routing + parameter M_REQ_TAG_WIDTH = S_REQ_TAG_WIDTH+$clog2(PORTS), + // Width of AXI stream interface in bits + parameter AXIS_DATA_WIDTH = 256, + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH/8, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Descriptor request output (to descriptor module) + */ + output wire [SELECT_WIDTH-1:0] m_axis_req_sel, + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_req_queue, + output wire [M_REQ_TAG_WIDTH-1:0] m_axis_req_tag, + output wire m_axis_req_valid, + input wire m_axis_req_ready, + + /* + * Descriptor request status input (from descriptor module) + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_req_status_queue, + input wire [QUEUE_PTR_WIDTH-1:0] s_axis_req_status_ptr, + input wire [CPL_QUEUE_INDEX_WIDTH-1:0] s_axis_req_status_cpl, + input wire [M_REQ_TAG_WIDTH-1:0] s_axis_req_status_tag, + input wire s_axis_req_status_empty, + input wire s_axis_req_status_error, + input wire s_axis_req_status_valid, + + /* + * Descriptor data input (from descriptor module) + */ + input wire [AXIS_DATA_WIDTH-1:0] s_axis_desc_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] s_axis_desc_tkeep, + input wire s_axis_desc_tvalid, + output wire s_axis_desc_tready, + input wire s_axis_desc_tlast, + input wire [M_REQ_TAG_WIDTH-1:0] s_axis_desc_tid, + input wire s_axis_desc_tuser, + + /* + * Descriptor request input + */ + input wire [PORTS*SELECT_WIDTH-1:0] s_axis_req_sel, + input wire [PORTS*QUEUE_INDEX_WIDTH-1:0] s_axis_req_queue, + input wire [PORTS*S_REQ_TAG_WIDTH-1:0] s_axis_req_tag, + input wire [PORTS-1:0] s_axis_req_valid, + output wire [PORTS-1:0] s_axis_req_ready, + + /* + * Descriptor request status output + */ + output wire [PORTS*QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_queue, + output wire [PORTS*QUEUE_PTR_WIDTH-1:0] m_axis_req_status_ptr, + output wire [PORTS*CPL_QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_cpl, + output wire [PORTS*S_REQ_TAG_WIDTH-1:0] m_axis_req_status_tag, + output wire [PORTS-1:0] m_axis_req_status_empty, + output wire [PORTS-1:0] m_axis_req_status_error, + output wire [PORTS-1:0] m_axis_req_status_valid, + + /* + * Descriptor data output + */ + output wire [PORTS*AXIS_DATA_WIDTH-1:0] m_axis_desc_tdata, + output wire [PORTS*AXIS_KEEP_WIDTH-1:0] m_axis_desc_tkeep, + output wire [PORTS-1:0] m_axis_desc_tvalid, + input wire [PORTS-1:0] m_axis_desc_tready, + output wire [PORTS-1:0] m_axis_desc_tlast, + output wire [PORTS*S_REQ_TAG_WIDTH-1:0] m_axis_desc_tid, + output wire [PORTS-1:0] m_axis_desc_tuser +); + +parameter CL_PORTS = $clog2(PORTS); + +// check configuration +initial begin + if (M_REQ_TAG_WIDTH < S_REQ_TAG_WIDTH+$clog2(PORTS)) begin + $error("Error: M_REQ_TAG_WIDTH must be at least $clog2(PORTS) larger than S_REQ_TAG_WIDTH (instance %m)"); + $finish; + end +end + +// request mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [SELECT_WIDTH-1:0] m_axis_req_sel_int; +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_req_queue_int; +reg [M_REQ_TAG_WIDTH-1:0] m_axis_req_tag_int; +reg m_axis_req_valid_int; +reg m_axis_req_ready_int_reg = 1'b0; +wire m_axis_req_ready_int_early; + +assign s_axis_req_ready = (m_axis_req_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [SELECT_WIDTH-1:0] current_s_desc_sel = s_axis_req_sel[grant_encoded*SELECT_WIDTH +: SELECT_WIDTH]; +wire [QUEUE_INDEX_WIDTH-1:0] current_s_desc_queue = s_axis_req_queue[grant_encoded*QUEUE_INDEX_WIDTH +: QUEUE_INDEX_WIDTH]; +wire [S_REQ_TAG_WIDTH-1:0] current_s_desc_tag = s_axis_req_tag[grant_encoded*S_REQ_TAG_WIDTH +: S_REQ_TAG_WIDTH]; +wire current_s_desc_valid = s_axis_req_valid[grant_encoded]; +wire current_s_desc_ready = s_axis_req_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_req_valid & ~grant; +assign acknowledge = grant & s_axis_req_valid & s_axis_req_ready; + +always @* begin + // pass through selected packet data + m_axis_req_sel_int = current_s_desc_sel; + m_axis_req_queue_int = current_s_desc_queue; + m_axis_req_tag_int = {grant_encoded, current_s_desc_tag}; + m_axis_req_valid_int = current_s_desc_valid && m_axis_req_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [SELECT_WIDTH-1:0] m_axis_req_sel_reg = {SELECT_WIDTH{1'b0}}; +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}; +reg [M_REQ_TAG_WIDTH-1:0] m_axis_req_tag_reg = {M_REQ_TAG_WIDTH{1'b0}}; +reg m_axis_req_valid_reg = 1'b0, m_axis_req_valid_next; + +reg [SELECT_WIDTH-1:0] temp_m_axis_req_sel_reg = {SELECT_WIDTH{1'b0}}; +reg [QUEUE_INDEX_WIDTH-1:0] temp_m_axis_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}; +reg [M_REQ_TAG_WIDTH-1:0] temp_m_axis_req_tag_reg = {M_REQ_TAG_WIDTH{1'b0}}; +reg temp_m_axis_req_valid_reg = 1'b0, temp_m_axis_req_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_req_sel = m_axis_req_sel_reg; +assign m_axis_req_queue = m_axis_req_queue_reg; +assign m_axis_req_tag = m_axis_req_tag_reg; +assign m_axis_req_valid = m_axis_req_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_req_ready_int_early = m_axis_req_ready || (!temp_m_axis_req_valid_reg && (!m_axis_req_valid_reg || !m_axis_req_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_req_valid_next = m_axis_req_valid_reg; + temp_m_axis_req_valid_next = temp_m_axis_req_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_req_ready_int_reg) begin + // input is ready + if (m_axis_req_ready || !m_axis_req_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_req_valid_next = m_axis_req_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_req_valid_next = m_axis_req_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_req_ready) begin + // input is not ready, but output is ready + m_axis_req_valid_next = temp_m_axis_req_valid_reg; + temp_m_axis_req_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_req_valid_reg <= 1'b0; + m_axis_req_ready_int_reg <= 1'b0; + temp_m_axis_req_valid_reg <= 1'b0; + end else begin + m_axis_req_valid_reg <= m_axis_req_valid_next; + m_axis_req_ready_int_reg <= m_axis_req_ready_int_early; + temp_m_axis_req_valid_reg <= temp_m_axis_req_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_req_sel_reg <= m_axis_req_sel_int; + m_axis_req_queue_reg <= m_axis_req_queue_int; + m_axis_req_tag_reg <= m_axis_req_tag_int; + end else if (store_axis_temp_to_output) begin + m_axis_req_sel_reg <= temp_m_axis_req_sel_reg; + m_axis_req_queue_reg <= temp_m_axis_req_queue_reg; + m_axis_req_tag_reg <= temp_m_axis_req_tag_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_req_sel_reg <= m_axis_req_sel_int; + temp_m_axis_req_queue_reg <= m_axis_req_queue_int; + temp_m_axis_req_tag_reg <= m_axis_req_tag_int; + end +end + +// request status demux +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}, m_axis_req_status_queue_next; +reg [QUEUE_PTR_WIDTH-1:0] m_axis_req_status_ptr_reg = {QUEUE_PTR_WIDTH{1'b0}}, m_axis_req_status_ptr_next; +reg [CPL_QUEUE_INDEX_WIDTH-1:0] m_axis_req_status_cpl_reg = {CPL_QUEUE_INDEX_WIDTH{1'b0}}, m_axis_req_status_cpl_next; +reg [S_REQ_TAG_WIDTH-1:0] m_axis_req_status_tag_reg = {S_REQ_TAG_WIDTH{1'b0}}, m_axis_req_status_tag_next; +reg m_axis_req_status_empty_reg = 1'b0, m_axis_req_status_empty_next; +reg m_axis_req_status_error_reg = 1'b0, m_axis_req_status_error_next; +reg [PORTS-1:0] m_axis_req_status_valid_reg = {PORTS{1'b0}}, m_axis_req_status_valid_next; + +assign m_axis_req_status_queue = {PORTS{m_axis_req_status_queue_reg}}; +assign m_axis_req_status_ptr = {PORTS{m_axis_req_status_ptr_reg}}; +assign m_axis_req_status_cpl = {PORTS{m_axis_req_status_cpl_reg}}; +assign m_axis_req_status_tag = {PORTS{m_axis_req_status_tag_reg}}; +assign m_axis_req_status_empty = {PORTS{m_axis_req_status_empty_reg}}; +assign m_axis_req_status_error = {PORTS{m_axis_req_status_error_reg}}; +assign m_axis_req_status_valid = m_axis_req_status_valid_reg; + +always @* begin + m_axis_req_status_queue_next = s_axis_req_status_queue; + m_axis_req_status_ptr_next = s_axis_req_status_ptr; + m_axis_req_status_cpl_next = s_axis_req_status_cpl; + m_axis_req_status_tag_next = s_axis_req_status_tag; + m_axis_req_status_empty_next = s_axis_req_status_empty; + m_axis_req_status_error_next = s_axis_req_status_error; + m_axis_req_status_valid_next = s_axis_req_status_valid << (PORTS > 1 ? (s_axis_req_status_tag >> S_REQ_TAG_WIDTH) : 0); +end + +always @(posedge clk) begin + if (rst) begin + m_axis_req_status_valid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_req_status_valid_reg <= m_axis_req_status_valid_next; + end + + m_axis_req_status_queue_reg <= m_axis_req_status_queue_next; + m_axis_req_status_ptr_reg <= m_axis_req_status_ptr_next; + m_axis_req_status_cpl_reg <= m_axis_req_status_cpl_next; + m_axis_req_status_tag_reg <= m_axis_req_status_tag_next; + m_axis_req_status_empty_reg <= m_axis_req_status_empty_next; + m_axis_req_status_error_reg <= m_axis_req_status_error_next; +end + +// descriptor data demux + +// internal datapath +reg [AXIS_DATA_WIDTH-1:0] m_axis_desc_tdata_int; +reg [AXIS_KEEP_WIDTH-1:0] m_axis_desc_tkeep_int; +reg [PORTS-1:0] m_axis_desc_tvalid_int; +reg m_axis_desc_tready_int_reg = 1'b0; +wire m_axis_desc_tready_int_early; +reg m_axis_desc_tlast_int; +reg [S_REQ_TAG_WIDTH-1:0] m_axis_desc_tid_int; +reg m_axis_desc_tuser_int; + +assign s_axis_desc_tready = m_axis_desc_tready_int_reg; + +always @* begin + m_axis_desc_tdata_int = s_axis_desc_tdata; + m_axis_desc_tkeep_int = s_axis_desc_tkeep; + m_axis_desc_tvalid_int = (s_axis_desc_tvalid && s_axis_desc_tready) << (PORTS > 1 ? (s_axis_desc_tid >> S_REQ_TAG_WIDTH) : 0); + m_axis_desc_tlast_int = s_axis_desc_tlast; + m_axis_desc_tid_int = s_axis_desc_tid; + m_axis_desc_tuser_int = s_axis_desc_tuser; +end + +// output datapath logic +reg [AXIS_DATA_WIDTH-1:0] m_axis_desc_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; +reg [AXIS_KEEP_WIDTH-1:0] m_axis_desc_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; +reg [PORTS-1:0] m_axis_desc_tvalid_reg = {PORTS{1'b0}}, m_axis_desc_tvalid_next; +reg m_axis_desc_tlast_reg = 1'b0; +reg [S_REQ_TAG_WIDTH-1:0] m_axis_desc_tid_reg = {S_REQ_TAG_WIDTH{1'b0}}; +reg m_axis_desc_tuser_reg = 1'b0; + +reg [AXIS_DATA_WIDTH-1:0] temp_m_axis_desc_tdata_reg = {AXIS_DATA_WIDTH{1'b0}}; +reg [AXIS_KEEP_WIDTH-1:0] temp_m_axis_desc_tkeep_reg = {AXIS_KEEP_WIDTH{1'b0}}; +reg [PORTS-1:0] temp_m_axis_desc_tvalid_reg = {PORTS{1'b0}}, temp_m_axis_desc_tvalid_next; +reg temp_m_axis_desc_tlast_reg = 1'b0; +reg [S_REQ_TAG_WIDTH-1:0] temp_m_axis_desc_tid_reg = {S_REQ_TAG_WIDTH{1'b0}}; +reg temp_m_axis_desc_tuser_reg = 1'b0; + +// datapath control +reg store_axis_req_int_to_output; +reg store_axis_req_int_to_temp; +reg store_axis_req_temp_to_output; + +assign m_axis_desc_tdata = {PORTS{m_axis_desc_tdata_reg}}; +assign m_axis_desc_tkeep = {PORTS{m_axis_desc_tkeep_reg}}; +assign m_axis_desc_tlast = {PORTS{m_axis_desc_tlast_reg}}; +assign m_axis_desc_tid = {PORTS{m_axis_desc_tid_reg}}; +assign m_axis_desc_tuser = {PORTS{m_axis_desc_tuser_reg}}; +assign m_axis_desc_tvalid = m_axis_desc_tvalid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_desc_tready_int_early = (m_axis_desc_tready & m_axis_desc_tvalid) || (!temp_m_axis_desc_tvalid_reg && (!m_axis_desc_tvalid || !m_axis_desc_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_desc_tvalid_next = m_axis_desc_tvalid_reg; + temp_m_axis_desc_tvalid_next = temp_m_axis_desc_tvalid_reg; + + store_axis_req_int_to_output = 1'b0; + store_axis_req_int_to_temp = 1'b0; + store_axis_req_temp_to_output = 1'b0; + + if (m_axis_desc_tready_int_reg) begin + // input is ready + if ((m_axis_desc_tready & m_axis_desc_tvalid) || !m_axis_desc_tvalid) begin + // output is ready or currently not valid, transfer data to output + m_axis_desc_tvalid_next = m_axis_desc_tvalid_int; + store_axis_req_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_desc_tvalid_next = m_axis_desc_tvalid_int; + store_axis_req_int_to_temp = 1'b1; + end + end else if (m_axis_desc_tready & m_axis_desc_tvalid) begin + // input is not ready, but output is ready + m_axis_desc_tvalid_next = temp_m_axis_desc_tvalid_reg; + temp_m_axis_desc_tvalid_next = {PORTS{1'b0}}; + store_axis_req_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_desc_tvalid_reg <= {PORTS{1'b0}}; + m_axis_desc_tready_int_reg <= 1'b0; + temp_m_axis_desc_tvalid_reg <= {PORTS{1'b0}}; + end else begin + m_axis_desc_tvalid_reg <= m_axis_desc_tvalid_next; + m_axis_desc_tready_int_reg <= m_axis_desc_tready_int_early; + temp_m_axis_desc_tvalid_reg <= temp_m_axis_desc_tvalid_next; + end + + // datapath + if (store_axis_req_int_to_output) begin + m_axis_desc_tdata_reg <= m_axis_desc_tdata_int; + m_axis_desc_tkeep_reg <= m_axis_desc_tkeep_int; + m_axis_desc_tlast_reg <= m_axis_desc_tlast_int; + m_axis_desc_tid_reg <= m_axis_desc_tid_int; + m_axis_desc_tuser_reg <= m_axis_desc_tuser_int; + end else if (store_axis_req_temp_to_output) begin + m_axis_desc_tdata_reg <= temp_m_axis_desc_tdata_reg; + m_axis_desc_tkeep_reg <= temp_m_axis_desc_tkeep_reg; + m_axis_desc_tlast_reg <= temp_m_axis_desc_tlast_reg; + m_axis_desc_tid_reg <= temp_m_axis_desc_tid_reg; + m_axis_desc_tuser_reg <= temp_m_axis_desc_tuser_reg; + end + + if (store_axis_req_int_to_temp) begin + temp_m_axis_desc_tdata_reg <= m_axis_desc_tdata_int; + temp_m_axis_desc_tkeep_reg <= m_axis_desc_tkeep_int; + temp_m_axis_desc_tlast_reg <= m_axis_desc_tlast_int; + temp_m_axis_desc_tid_reg <= m_axis_desc_tid_int; + temp_m_axis_desc_tuser_reg <= m_axis_desc_tuser_int; + end +end + +endmodule diff --git a/corundum/rtl/event_mux.v b/corundum/rtl/event_mux.v new file mode 100644 index 0000000000000000000000000000000000000000..0bdcc48a9ec42ea77e1a175320300d892911b8bf --- /dev/null +++ b/corundum/rtl/event_mux.v @@ -0,0 +1,213 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Event mux + */ +module event_mux # +( + // Number of ports + parameter PORTS = 2, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 4, + // Event type field width + parameter EVENT_TYPE_WIDTH = 16, + // Event source field width + parameter EVENT_SOURCE_WIDTH = 16, + // arbitration type: "PRIORITY" or "ROUND_ROBIN" + parameter ARB_TYPE = "PRIORITY", + // LSB priority: "LOW", "HIGH" + parameter LSB_PRIORITY = "HIGH" +) +( + input wire clk, + input wire rst, + + /* + * Event output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_event_queue, + output wire [EVENT_TYPE_WIDTH-1:0] m_axis_event_type, + output wire [EVENT_SOURCE_WIDTH-1:0] m_axis_event_source, + output wire m_axis_event_valid, + input wire m_axis_event_ready, + + /* + * Event input + */ + input wire [PORTS*QUEUE_INDEX_WIDTH-1:0] s_axis_event_queue, + input wire [PORTS*EVENT_TYPE_WIDTH-1:0] s_axis_event_type, + input wire [PORTS*EVENT_SOURCE_WIDTH-1:0] s_axis_event_source, + input wire [PORTS-1:0] s_axis_event_valid, + output wire [PORTS-1:0] s_axis_event_ready +); + +parameter CL_PORTS = $clog2(PORTS); + +// eventriptor mux +wire [PORTS-1:0] request; +wire [PORTS-1:0] acknowledge; +wire [PORTS-1:0] grant; +wire grant_valid; +wire [CL_PORTS-1:0] grant_encoded; + +// internal datapath +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_event_queue_int; +reg [EVENT_TYPE_WIDTH-1:0] m_axis_event_type_int; +reg [EVENT_SOURCE_WIDTH-1:0] m_axis_event_source_int; +reg m_axis_event_valid_int; +reg m_axis_event_ready_int_reg = 1'b0; +wire m_axis_event_ready_int_early; + +assign s_axis_event_ready = (m_axis_event_ready_int_reg && grant_valid) << grant_encoded; + +// mux for incoming packet +wire [QUEUE_INDEX_WIDTH-1:0] current_s_event_queue = s_axis_event_queue[grant_encoded*QUEUE_INDEX_WIDTH +: QUEUE_INDEX_WIDTH]; +wire [EVENT_TYPE_WIDTH-1:0] current_s_event_type = s_axis_event_type[grant_encoded*EVENT_TYPE_WIDTH +: EVENT_TYPE_WIDTH]; +wire [EVENT_SOURCE_WIDTH-1:0] current_s_event_source = s_axis_event_source[grant_encoded*EVENT_SOURCE_WIDTH +: EVENT_SOURCE_WIDTH]; +wire current_s_event_valid = s_axis_event_valid[grant_encoded]; +wire current_s_event_ready = s_axis_event_ready[grant_encoded]; + +// arbiter instance +arbiter #( + .PORTS(PORTS), + .TYPE(ARB_TYPE), + .BLOCK("ACKNOWLEDGE"), + .LSB_PRIORITY(LSB_PRIORITY) +) +arb_inst ( + .clk(clk), + .rst(rst), + .request(request), + .acknowledge(acknowledge), + .grant(grant), + .grant_valid(grant_valid), + .grant_encoded(grant_encoded) +); + +assign request = s_axis_event_valid & ~grant; +assign acknowledge = grant & s_axis_event_valid & s_axis_event_ready; + +always @* begin + m_axis_event_queue_int = current_s_event_queue; + m_axis_event_type_int = current_s_event_type; + m_axis_event_source_int = current_s_event_source; + m_axis_event_valid_int = current_s_event_valid && m_axis_event_ready_int_reg && grant_valid; +end + +// output datapath logic +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_event_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}; +reg [EVENT_TYPE_WIDTH-1:0] m_axis_event_type_reg = {EVENT_TYPE_WIDTH{1'b0}}; +reg [EVENT_SOURCE_WIDTH-1:0] m_axis_event_source_reg = {EVENT_SOURCE_WIDTH{1'b0}}; +reg m_axis_event_valid_reg = 1'b0, m_axis_event_valid_next; + +reg [QUEUE_INDEX_WIDTH-1:0] temp_m_axis_event_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}; +reg [EVENT_TYPE_WIDTH-1:0] temp_m_axis_event_type_reg = {EVENT_TYPE_WIDTH{1'b0}}; +reg [EVENT_SOURCE_WIDTH-1:0] temp_m_axis_event_source_reg = {EVENT_SOURCE_WIDTH{1'b0}}; +reg temp_m_axis_event_valid_reg = 1'b0, temp_m_axis_event_valid_next; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_event_queue = m_axis_event_queue_reg; +assign m_axis_event_type = m_axis_event_type_reg; +assign m_axis_event_source = m_axis_event_source_reg; +assign m_axis_event_valid = m_axis_event_valid_reg; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_event_ready_int_early = m_axis_event_ready || (!temp_m_axis_event_valid_reg && (!m_axis_event_valid_reg || !m_axis_event_valid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_event_valid_next = m_axis_event_valid_reg; + temp_m_axis_event_valid_next = temp_m_axis_event_valid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_event_ready_int_reg) begin + // input is ready + if (m_axis_event_ready || !m_axis_event_valid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_event_valid_next = m_axis_event_valid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_event_valid_next = m_axis_event_valid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_event_ready) begin + // input is not ready, but output is ready + m_axis_event_valid_next = temp_m_axis_event_valid_reg; + temp_m_axis_event_valid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_event_valid_reg <= 1'b0; + m_axis_event_ready_int_reg <= 1'b0; + temp_m_axis_event_valid_reg <= 1'b0; + end else begin + m_axis_event_valid_reg <= m_axis_event_valid_next; + m_axis_event_ready_int_reg <= m_axis_event_ready_int_early; + temp_m_axis_event_valid_reg <= temp_m_axis_event_valid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_event_queue_reg <= m_axis_event_queue_int; + m_axis_event_type_reg <= m_axis_event_type_int; + m_axis_event_source_reg <= m_axis_event_source_int; + end else if (store_axis_temp_to_output) begin + m_axis_event_queue_reg <= temp_m_axis_event_queue_reg; + m_axis_event_type_reg <= temp_m_axis_event_type_reg; + m_axis_event_source_reg <= temp_m_axis_event_source_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_event_queue_reg <= m_axis_event_queue_int; + temp_m_axis_event_type_reg <= m_axis_event_type_int; + temp_m_axis_event_source_reg <= m_axis_event_source_int; + end +end + +endmodule diff --git a/corundum/rtl/interface.v b/corundum/rtl/interface.v new file mode 100644 index 0000000000000000000000000000000000000000..0b7343bf2e072dd61da1900c8d31fd17b9beec07 --- /dev/null +++ b/corundum/rtl/interface.v @@ -0,0 +1,2242 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * NIC Interface + */ +module interface # +( + // Number of ports + parameter PORTS = 1, + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // DMA length field width + parameter DMA_LEN_WIDTH = 16, + // DMA tag field width + parameter DMA_TAG_WIDTH = 8, + // Number of outstanding operations (event queue) + parameter EVENT_QUEUE_OP_TABLE_SIZE = 16, + // Number of outstanding operations (transmit queue) + parameter TX_QUEUE_OP_TABLE_SIZE = 16, + // Number of outstanding operations (receive queue) + parameter RX_QUEUE_OP_TABLE_SIZE = 16, + // Number of outstanding operations (transmit completion queue) + parameter TX_CPL_QUEUE_OP_TABLE_SIZE = 16, + // Number of outstanding operations (receive completion queue) + parameter RX_CPL_QUEUE_OP_TABLE_SIZE = 16, + // Event queue index width + parameter EVENT_QUEUE_INDEX_WIDTH = 5, + // Transmit queue index width + parameter TX_QUEUE_INDEX_WIDTH = 8, + // Receive queue index width + parameter RX_QUEUE_INDEX_WIDTH = 8, + // Transmit completion queue index width + parameter TX_CPL_QUEUE_INDEX_WIDTH = 8, + // Receive completion queue index width + parameter RX_CPL_QUEUE_INDEX_WIDTH = 8, + // Pipeline setting (event queue) + parameter EVENT_QUEUE_PIPELINE = 3, + // Pipeline setting (transmit queue) + parameter TX_QUEUE_PIPELINE = 3, + // Pipeline setting (receive queue) + parameter RX_QUEUE_PIPELINE = 3, + // Pipeline setting (transmit completion queue) + parameter TX_CPL_QUEUE_PIPELINE = 3, + // Pipeline setting (receive completion queue) + parameter RX_CPL_QUEUE_PIPELINE = 3, + // Transmit descriptor table size (number of in-flight operations) + parameter TX_DESC_TABLE_SIZE = 16, + // Transmit packet table size (number of in-progress packets) + parameter TX_PKT_TABLE_SIZE = 8, + // Receive descriptor table size (number of in-flight operations) + parameter RX_DESC_TABLE_SIZE = 16, + // Receive packet table size (number of in-progress packets) + parameter RX_PKT_TABLE_SIZE = 8, + // Transmit scheduler type + parameter TX_SCHEDULER = "RR", + // Scheduler operation table size + parameter TX_SCHEDULER_OP_TABLE_SIZE = 32, + // Scheduler pipeline setting + parameter TX_SCHEDULER_PIPELINE = 3, + // Scheduler TDMA index width + parameter TDMA_INDEX_WIDTH = 8, + // Interrupt number width + parameter INT_WIDTH = 8, + // Queue element pointer width + parameter QUEUE_PTR_WIDTH = 16, + // Queue log size field width + parameter LOG_QUEUE_SIZE_WIDTH = 4, + // Log desc block size field width + parameter LOG_BLOCK_SIZE_WIDTH = 2, + // Enable PTP timestamping + parameter PTP_TS_ENABLE = 1, + // PTP timestamp width + parameter PTP_TS_WIDTH = 96, + // Enable TX checksum offload + parameter TX_CHECKSUM_ENABLE = 1, + // Enable RX RSS + parameter RX_RSS_ENABLE = 1, + // Enable RX hashing + parameter RX_HASH_ENABLE = 1, + // Enable RX checksum offload + parameter RX_CHECKSUM_ENABLE = 1, + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = 16, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // DMA RAM segment count + parameter SEG_COUNT = 2, + // DMA RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // DMA RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // DMA RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // DMA RAM segment select width + parameter RAM_SEL_WIDTH = $clog2(PORTS), + // DMA RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // DMA RAM pipeline stages + parameter RAM_PIPELINE = 2, + // Width of AXI stream interfaces in bits + parameter AXIS_DATA_WIDTH = 256, + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH/8, + // Max transmit packet size + parameter MAX_TX_SIZE = 2048, + // Max receive packet size + parameter MAX_RX_SIZE = 2048, + // DMA TX RAM size + parameter TX_RAM_SIZE = TX_PKT_TABLE_SIZE*MAX_TX_SIZE, + // DMA RX RAM size + parameter RX_RAM_SIZE = RX_PKT_TABLE_SIZE*MAX_RX_SIZE +) +( + input wire clk, + input wire rst, + + /* + * DMA read descriptor output (control) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_ctrl_dma_read_desc_dma_addr, + output wire [RAM_SEL_WIDTH-1:0] m_axis_ctrl_dma_read_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_ctrl_dma_read_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_ctrl_dma_read_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_ctrl_dma_read_desc_tag, + output wire m_axis_ctrl_dma_read_desc_valid, + input wire m_axis_ctrl_dma_read_desc_ready, + + /* + * DMA read descriptor status input (control) + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_ctrl_dma_read_desc_status_tag, + input wire s_axis_ctrl_dma_read_desc_status_valid, + + /* + * DMA write descriptor output (control) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_ctrl_dma_write_desc_dma_addr, + output wire [RAM_SEL_WIDTH-1:0] m_axis_ctrl_dma_write_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_ctrl_dma_write_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_ctrl_dma_write_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_ctrl_dma_write_desc_tag, + output wire m_axis_ctrl_dma_write_desc_valid, + input wire m_axis_ctrl_dma_write_desc_ready, + + /* + * DMA write descriptor status input (control) + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_ctrl_dma_write_desc_status_tag, + input wire s_axis_ctrl_dma_write_desc_status_valid, + + /* + * DMA read descriptor output (data) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_data_dma_read_desc_dma_addr, + output wire [RAM_SEL_WIDTH-1:0] m_axis_data_dma_read_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_data_dma_read_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_data_dma_read_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_data_dma_read_desc_tag, + output wire m_axis_data_dma_read_desc_valid, + input wire m_axis_data_dma_read_desc_ready, + + /* + * DMA read descriptor status input (data) + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_data_dma_read_desc_status_tag, + input wire s_axis_data_dma_read_desc_status_valid, + + /* + * DMA write descriptor output (data) + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_data_dma_write_desc_dma_addr, + output wire [RAM_SEL_WIDTH-1:0] m_axis_data_dma_write_desc_ram_sel, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_data_dma_write_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_data_dma_write_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_data_dma_write_desc_tag, + output wire m_axis_data_dma_write_desc_valid, + input wire m_axis_data_dma_write_desc_ready, + + /* + * DMA write descriptor status input (data) + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_data_dma_write_desc_status_tag, + input wire s_axis_data_dma_write_desc_status_valid, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * AXI-Lite master interface (passthrough for NIC control and status) + */ + output wire [AXIL_ADDR_WIDTH-1:0] m_axil_csr_awaddr, + output wire [2:0] m_axil_csr_awprot, + output wire m_axil_csr_awvalid, + input wire m_axil_csr_awready, + output wire [AXIL_DATA_WIDTH-1:0] m_axil_csr_wdata, + output wire [AXIL_STRB_WIDTH-1:0] m_axil_csr_wstrb, + output wire m_axil_csr_wvalid, + input wire m_axil_csr_wready, + input wire [1:0] m_axil_csr_bresp, + input wire m_axil_csr_bvalid, + output wire m_axil_csr_bready, + output wire [AXIL_ADDR_WIDTH-1:0] m_axil_csr_araddr, + output wire [2:0] m_axil_csr_arprot, + output wire m_axil_csr_arvalid, + input wire m_axil_csr_arready, + input wire [AXIL_DATA_WIDTH-1:0] m_axil_csr_rdata, + input wire [1:0] m_axil_csr_rresp, + input wire m_axil_csr_rvalid, + output wire m_axil_csr_rready, + + /* + * RAM interface (control) + */ + input wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] ctrl_dma_ram_wr_cmd_sel, + input wire [SEG_COUNT*SEG_BE_WIDTH-1:0] ctrl_dma_ram_wr_cmd_be, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ctrl_dma_ram_wr_cmd_addr, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ctrl_dma_ram_wr_cmd_data, + input wire [SEG_COUNT-1:0] ctrl_dma_ram_wr_cmd_valid, + output wire [SEG_COUNT-1:0] ctrl_dma_ram_wr_cmd_ready, + input wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] ctrl_dma_ram_rd_cmd_sel, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] ctrl_dma_ram_rd_cmd_addr, + input wire [SEG_COUNT-1:0] ctrl_dma_ram_rd_cmd_valid, + output wire [SEG_COUNT-1:0] ctrl_dma_ram_rd_cmd_ready, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] ctrl_dma_ram_rd_resp_data, + output wire [SEG_COUNT-1:0] ctrl_dma_ram_rd_resp_valid, + input wire [SEG_COUNT-1:0] ctrl_dma_ram_rd_resp_ready, + + /* + * RAM interface (data) + */ + input wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] data_dma_ram_wr_cmd_sel, + input wire [SEG_COUNT*SEG_BE_WIDTH-1:0] data_dma_ram_wr_cmd_be, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] data_dma_ram_wr_cmd_addr, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] data_dma_ram_wr_cmd_data, + input wire [SEG_COUNT-1:0] data_dma_ram_wr_cmd_valid, + output wire [SEG_COUNT-1:0] data_dma_ram_wr_cmd_ready, + input wire [SEG_COUNT*RAM_SEL_WIDTH-1:0] data_dma_ram_rd_cmd_sel, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] data_dma_ram_rd_cmd_addr, + input wire [SEG_COUNT-1:0] data_dma_ram_rd_cmd_valid, + output wire [SEG_COUNT-1:0] data_dma_ram_rd_cmd_ready, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] data_dma_ram_rd_resp_data, + output wire [SEG_COUNT-1:0] data_dma_ram_rd_resp_valid, + input wire [SEG_COUNT-1:0] data_dma_ram_rd_resp_ready, + + /* + * Transmit data output + */ + output wire [PORTS*AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + output wire [PORTS*AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + output wire [PORTS-1:0] tx_axis_tvalid, + input wire [PORTS-1:0] tx_axis_tready, + output wire [PORTS-1:0] tx_axis_tlast, + output wire [PORTS-1:0] tx_axis_tuser, + + /* + * Transmit timestamp input + */ + input wire [PORTS*PTP_TS_WIDTH-1:0] s_axis_tx_ptp_ts_96, + input wire [PORTS-1:0] s_axis_tx_ptp_ts_valid, + output wire [PORTS-1:0] s_axis_tx_ptp_ts_ready, + + /* + * Receive data input + */ + input wire [PORTS*AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + input wire [PORTS*AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + input wire [PORTS-1:0] rx_axis_tvalid, + output wire [PORTS-1:0] rx_axis_tready, + input wire [PORTS-1:0] rx_axis_tlast, + input wire [PORTS-1:0] rx_axis_tuser, + + /* + * Receive timestamp input + */ + input wire [PORTS*PTP_TS_WIDTH-1:0] s_axis_rx_ptp_ts_96, + input wire [PORTS-1:0] s_axis_rx_ptp_ts_valid, + output wire [PORTS-1:0] s_axis_rx_ptp_ts_ready, + + /* + * PTP clock + */ + input wire [95:0] ptp_ts_96, + input wire ptp_ts_step, + + /* + * MSI interrupts + */ + output wire [31:0] msi_irq +); + +parameter DESC_SIZE = 16; +parameter CPL_SIZE = 32; +parameter EVENT_SIZE = 32; + +parameter AXIS_DESC_DATA_WIDTH = DESC_SIZE*8; +parameter AXIS_DESC_KEEP_WIDTH = AXIS_DESC_DATA_WIDTH/8; + +parameter EVENT_SOURCE_WIDTH = 16; +parameter EVENT_TYPE_WIDTH = 16; + +parameter MAX_DESC_TABLE_SIZE = TX_DESC_TABLE_SIZE > RX_DESC_TABLE_SIZE ? TX_DESC_TABLE_SIZE : RX_DESC_TABLE_SIZE; + +parameter REQ_TAG_WIDTH = $clog2(MAX_DESC_TABLE_SIZE) + 1 + $clog2(PORTS+1); +parameter DESC_REQ_TAG_WIDTH = $clog2(MAX_DESC_TABLE_SIZE) + 1 + $clog2(PORTS+1); + +parameter QUEUE_REQ_TAG_WIDTH = $clog2(MAX_DESC_TABLE_SIZE) + 1 + $clog2(PORTS+1); +parameter QUEUE_OP_TAG_WIDTH = 6; + +parameter DMA_TAG_WIDTH_INT = DMA_TAG_WIDTH - $clog2(PORTS); + +parameter QUEUE_INDEX_WIDTH = TX_QUEUE_INDEX_WIDTH > RX_QUEUE_INDEX_WIDTH ? TX_QUEUE_INDEX_WIDTH : RX_QUEUE_INDEX_WIDTH; +parameter CPL_QUEUE_INDEX_WIDTH = TX_CPL_QUEUE_INDEX_WIDTH > RX_CPL_QUEUE_INDEX_WIDTH ? TX_CPL_QUEUE_INDEX_WIDTH : RX_CPL_QUEUE_INDEX_WIDTH; + +parameter PORT_DESC_REQ_TAG_WIDTH = DESC_REQ_TAG_WIDTH - $clog2(PORTS+1); + +parameter AXIL_CSR_ADDR_WIDTH = AXIL_ADDR_WIDTH-5-$clog2((PORTS+3)/8); +parameter AXIL_CTRL_ADDR_WIDTH = AXIL_ADDR_WIDTH-5-$clog2((PORTS+3)/8); +parameter AXIL_EQM_ADDR_WIDTH = AXIL_ADDR_WIDTH-4-$clog2((PORTS+3)/8); +parameter AXIL_TX_QM_ADDR_WIDTH = AXIL_ADDR_WIDTH-3-$clog2((PORTS+3)/8); +parameter AXIL_TX_CQM_ADDR_WIDTH = AXIL_ADDR_WIDTH-3-$clog2((PORTS+3)/8); +parameter AXIL_RX_QM_ADDR_WIDTH = AXIL_ADDR_WIDTH-4-$clog2((PORTS+3)/8); +parameter AXIL_RX_CQM_ADDR_WIDTH = AXIL_ADDR_WIDTH-4-$clog2((PORTS+3)/8); +parameter AXIL_PORT_ADDR_WIDTH = AXIL_ADDR_WIDTH-3-$clog2((PORTS+3)/8); + +parameter AXIL_CSR_BASE_ADDR = 0; +parameter AXIL_CTRL_BASE_ADDR = AXIL_CSR_BASE_ADDR + 2**AXIL_CSR_ADDR_WIDTH; +parameter AXIL_EQM_BASE_ADDR = AXIL_CTRL_BASE_ADDR + 2**AXIL_CTRL_ADDR_WIDTH; +parameter AXIL_TX_QM_BASE_ADDR = AXIL_EQM_BASE_ADDR + 2**AXIL_EQM_ADDR_WIDTH; +parameter AXIL_TX_CQM_BASE_ADDR = AXIL_TX_QM_BASE_ADDR + 2**AXIL_TX_QM_ADDR_WIDTH; +parameter AXIL_RX_QM_BASE_ADDR = AXIL_TX_CQM_BASE_ADDR + 2**AXIL_TX_CQM_ADDR_WIDTH; +parameter AXIL_RX_CQM_BASE_ADDR = AXIL_RX_QM_BASE_ADDR + 2**AXIL_RX_QM_ADDR_WIDTH; +parameter AXIL_PORT_BASE_ADDR = AXIL_RX_CQM_BASE_ADDR + 2**AXIL_RX_CQM_ADDR_WIDTH; + +// parameter sizing helpers +function [31:0] w_32(input [31:0] val); + w_32 = val; +endfunction + +// AXI lite connections +wire [AXIL_ADDR_WIDTH-1:0] axil_ctrl_awaddr; +wire [2:0] axil_ctrl_awprot; +wire axil_ctrl_awvalid; +wire axil_ctrl_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_ctrl_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_ctrl_wstrb; +wire axil_ctrl_wvalid; +wire axil_ctrl_wready; +wire [1:0] axil_ctrl_bresp; +wire axil_ctrl_bvalid; +wire axil_ctrl_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_ctrl_araddr; +wire [2:0] axil_ctrl_arprot; +wire axil_ctrl_arvalid; +wire axil_ctrl_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_ctrl_rdata; +wire [1:0] axil_ctrl_rresp; +wire axil_ctrl_rvalid; +wire axil_ctrl_rready; + +wire [AXIL_ADDR_WIDTH-1:0] axil_event_queue_manager_awaddr; +wire [2:0] axil_event_queue_manager_awprot; +wire axil_event_queue_manager_awvalid; +wire axil_event_queue_manager_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_event_queue_manager_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_event_queue_manager_wstrb; +wire axil_event_queue_manager_wvalid; +wire axil_event_queue_manager_wready; +wire [1:0] axil_event_queue_manager_bresp; +wire axil_event_queue_manager_bvalid; +wire axil_event_queue_manager_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_event_queue_manager_araddr; +wire [2:0] axil_event_queue_manager_arprot; +wire axil_event_queue_manager_arvalid; +wire axil_event_queue_manager_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_event_queue_manager_rdata; +wire [1:0] axil_event_queue_manager_rresp; +wire axil_event_queue_manager_rvalid; +wire axil_event_queue_manager_rready; + +wire [AXIL_ADDR_WIDTH-1:0] axil_tx_queue_manager_awaddr; +wire [2:0] axil_tx_queue_manager_awprot; +wire axil_tx_queue_manager_awvalid; +wire axil_tx_queue_manager_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_tx_queue_manager_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_tx_queue_manager_wstrb; +wire axil_tx_queue_manager_wvalid; +wire axil_tx_queue_manager_wready; +wire [1:0] axil_tx_queue_manager_bresp; +wire axil_tx_queue_manager_bvalid; +wire axil_tx_queue_manager_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_tx_queue_manager_araddr; +wire [2:0] axil_tx_queue_manager_arprot; +wire axil_tx_queue_manager_arvalid; +wire axil_tx_queue_manager_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_tx_queue_manager_rdata; +wire [1:0] axil_tx_queue_manager_rresp; +wire axil_tx_queue_manager_rvalid; +wire axil_tx_queue_manager_rready; + +wire [AXIL_ADDR_WIDTH-1:0] axil_tx_cpl_queue_manager_awaddr; +wire [2:0] axil_tx_cpl_queue_manager_awprot; +wire axil_tx_cpl_queue_manager_awvalid; +wire axil_tx_cpl_queue_manager_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_tx_cpl_queue_manager_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_tx_cpl_queue_manager_wstrb; +wire axil_tx_cpl_queue_manager_wvalid; +wire axil_tx_cpl_queue_manager_wready; +wire [1:0] axil_tx_cpl_queue_manager_bresp; +wire axil_tx_cpl_queue_manager_bvalid; +wire axil_tx_cpl_queue_manager_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_tx_cpl_queue_manager_araddr; +wire [2:0] axil_tx_cpl_queue_manager_arprot; +wire axil_tx_cpl_queue_manager_arvalid; +wire axil_tx_cpl_queue_manager_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_tx_cpl_queue_manager_rdata; +wire [1:0] axil_tx_cpl_queue_manager_rresp; +wire axil_tx_cpl_queue_manager_rvalid; +wire axil_tx_cpl_queue_manager_rready; + +wire [AXIL_ADDR_WIDTH-1:0] axil_rx_queue_manager_awaddr; +wire [2:0] axil_rx_queue_manager_awprot; +wire axil_rx_queue_manager_awvalid; +wire axil_rx_queue_manager_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_rx_queue_manager_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_rx_queue_manager_wstrb; +wire axil_rx_queue_manager_wvalid; +wire axil_rx_queue_manager_wready; +wire [1:0] axil_rx_queue_manager_bresp; +wire axil_rx_queue_manager_bvalid; +wire axil_rx_queue_manager_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_rx_queue_manager_araddr; +wire [2:0] axil_rx_queue_manager_arprot; +wire axil_rx_queue_manager_arvalid; +wire axil_rx_queue_manager_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_rx_queue_manager_rdata; +wire [1:0] axil_rx_queue_manager_rresp; +wire axil_rx_queue_manager_rvalid; +wire axil_rx_queue_manager_rready; + +wire [AXIL_ADDR_WIDTH-1:0] axil_rx_cpl_queue_manager_awaddr; +wire [2:0] axil_rx_cpl_queue_manager_awprot; +wire axil_rx_cpl_queue_manager_awvalid; +wire axil_rx_cpl_queue_manager_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_rx_cpl_queue_manager_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_rx_cpl_queue_manager_wstrb; +wire axil_rx_cpl_queue_manager_wvalid; +wire axil_rx_cpl_queue_manager_wready; +wire [1:0] axil_rx_cpl_queue_manager_bresp; +wire axil_rx_cpl_queue_manager_bvalid; +wire axil_rx_cpl_queue_manager_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_rx_cpl_queue_manager_araddr; +wire [2:0] axil_rx_cpl_queue_manager_arprot; +wire axil_rx_cpl_queue_manager_arvalid; +wire axil_rx_cpl_queue_manager_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_rx_cpl_queue_manager_rdata; +wire [1:0] axil_rx_cpl_queue_manager_rresp; +wire axil_rx_cpl_queue_manager_rvalid; +wire axil_rx_cpl_queue_manager_rready; + +wire [PORTS*AXIL_ADDR_WIDTH-1:0] axil_port_awaddr; +wire [PORTS*3-1:0] axil_port_awprot; +wire [PORTS-1:0] axil_port_awvalid; +wire [PORTS-1:0] axil_port_awready; +wire [PORTS*AXIL_DATA_WIDTH-1:0] axil_port_wdata; +wire [PORTS*AXIL_STRB_WIDTH-1:0] axil_port_wstrb; +wire [PORTS-1:0] axil_port_wvalid; +wire [PORTS-1:0] axil_port_wready; +wire [PORTS*2-1:0] axil_port_bresp; +wire [PORTS-1:0] axil_port_bvalid; +wire [PORTS-1:0] axil_port_bready; +wire [PORTS*AXIL_ADDR_WIDTH-1:0] axil_port_araddr; +wire [PORTS*3-1:0] axil_port_arprot; +wire [PORTS-1:0] axil_port_arvalid; +wire [PORTS-1:0] axil_port_arready; +wire [PORTS*AXIL_DATA_WIDTH-1:0] axil_port_rdata; +wire [PORTS*2-1:0] axil_port_rresp; +wire [PORTS-1:0] axil_port_rvalid; +wire [PORTS-1:0] axil_port_rready; + +// DMA +wire [PORTS*DMA_ADDR_WIDTH-1:0] port_dma_read_desc_dma_addr; +wire [PORTS*RAM_ADDR_WIDTH-1:0] port_dma_read_desc_ram_addr; +wire [PORTS*DMA_LEN_WIDTH-1:0] port_dma_read_desc_len; +wire [PORTS*DMA_TAG_WIDTH_INT-1:0] port_dma_read_desc_tag; +wire [PORTS-1:0] port_dma_read_desc_valid; +wire [PORTS-1:0] port_dma_read_desc_ready; + +wire [PORTS*DMA_TAG_WIDTH_INT-1:0] port_dma_read_desc_status_tag; +wire [PORTS-1:0] port_dma_read_desc_status_valid; + +wire [PORTS*DMA_ADDR_WIDTH-1:0] port_dma_write_desc_dma_addr; +wire [PORTS*RAM_ADDR_WIDTH-1:0] port_dma_write_desc_ram_addr; +wire [PORTS*DMA_LEN_WIDTH-1:0] port_dma_write_desc_len; +wire [PORTS*DMA_TAG_WIDTH_INT-1:0] port_dma_write_desc_tag; +wire [PORTS-1:0] port_dma_write_desc_valid; +wire [PORTS-1:0] port_dma_write_desc_ready; + +wire [PORTS*DMA_TAG_WIDTH_INT-1:0] port_dma_write_desc_status_tag; +wire [PORTS-1:0] port_dma_write_desc_status_valid; + +wire [PORTS*SEG_COUNT*SEG_BE_WIDTH-1:0] port_dma_ram_wr_cmd_be; +wire [PORTS*SEG_COUNT*SEG_ADDR_WIDTH-1:0] port_dma_ram_wr_cmd_addr; +wire [PORTS*SEG_COUNT*SEG_DATA_WIDTH-1:0] port_dma_ram_wr_cmd_data; +wire [PORTS*SEG_COUNT-1:0] port_dma_ram_wr_cmd_valid; +wire [PORTS*SEG_COUNT-1:0] port_dma_ram_wr_cmd_ready; +wire [PORTS*SEG_COUNT*SEG_ADDR_WIDTH-1:0] port_dma_ram_rd_cmd_addr; +wire [PORTS*SEG_COUNT-1:0] port_dma_ram_rd_cmd_valid; +wire [PORTS*SEG_COUNT-1:0] port_dma_ram_rd_cmd_ready; +wire [PORTS*SEG_COUNT*SEG_DATA_WIDTH-1:0] port_dma_ram_rd_resp_data; +wire [PORTS*SEG_COUNT-1:0] port_dma_ram_rd_resp_valid; +wire [PORTS*SEG_COUNT-1:0] port_dma_ram_rd_resp_ready; + +// Queue management +wire [CPL_QUEUE_INDEX_WIDTH-1:0] event_enqueue_req_queue; +wire [QUEUE_REQ_TAG_WIDTH-1:0] event_enqueue_req_tag; +wire event_enqueue_req_valid; +wire event_enqueue_req_ready; + +wire [DMA_ADDR_WIDTH-1:0] event_enqueue_resp_addr; +wire [QUEUE_REQ_TAG_WIDTH-1:0] event_enqueue_resp_tag; +wire [QUEUE_OP_TAG_WIDTH-1:0] event_enqueue_resp_op_tag; +wire event_enqueue_resp_full; +wire event_enqueue_resp_error; +wire event_enqueue_resp_valid; +wire event_enqueue_resp_ready; + +wire [QUEUE_OP_TAG_WIDTH-1:0] event_enqueue_commit_op_tag; +wire event_enqueue_commit_valid; +wire event_enqueue_commit_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] tx_desc_dequeue_req_queue; +wire [QUEUE_REQ_TAG_WIDTH-1:0] tx_desc_dequeue_req_tag; +wire tx_desc_dequeue_req_valid; +wire tx_desc_dequeue_req_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] tx_desc_dequeue_resp_queue; +wire [QUEUE_PTR_WIDTH-1:0] tx_desc_dequeue_resp_ptr; +wire [DMA_ADDR_WIDTH-1:0] tx_desc_dequeue_resp_addr; +wire [LOG_BLOCK_SIZE_WIDTH-1:0] tx_desc_dequeue_resp_block_size; +wire [CPL_QUEUE_INDEX_WIDTH-1:0] tx_desc_dequeue_resp_cpl; +wire [QUEUE_REQ_TAG_WIDTH-1:0] tx_desc_dequeue_resp_tag; +wire [QUEUE_OP_TAG_WIDTH-1:0] tx_desc_dequeue_resp_op_tag; +wire tx_desc_dequeue_resp_empty; +wire tx_desc_dequeue_resp_error; +wire tx_desc_dequeue_resp_valid; +wire tx_desc_dequeue_resp_ready; + +wire [QUEUE_OP_TAG_WIDTH-1:0] tx_desc_dequeue_commit_op_tag; +wire tx_desc_dequeue_commit_valid; +wire tx_desc_dequeue_commit_ready; + +wire [TX_QUEUE_INDEX_WIDTH-1:0] tx_doorbell_queue; +wire tx_doorbell_valid; + +wire [CPL_QUEUE_INDEX_WIDTH-1:0] tx_cpl_enqueue_req_queue; +wire [QUEUE_REQ_TAG_WIDTH-1:0] tx_cpl_enqueue_req_tag; +wire tx_cpl_enqueue_req_valid; +wire tx_cpl_enqueue_req_ready; + +wire [DMA_ADDR_WIDTH-1:0] tx_cpl_enqueue_resp_addr; +wire [QUEUE_REQ_TAG_WIDTH-1:0] tx_cpl_enqueue_resp_tag; +wire [QUEUE_OP_TAG_WIDTH-1:0] tx_cpl_enqueue_resp_op_tag; +wire tx_cpl_enqueue_resp_full; +wire tx_cpl_enqueue_resp_error; +wire tx_cpl_enqueue_resp_valid; +wire tx_cpl_enqueue_resp_ready; + +wire [QUEUE_OP_TAG_WIDTH-1:0] tx_cpl_enqueue_commit_op_tag; +wire tx_cpl_enqueue_commit_valid; +wire tx_cpl_enqueue_commit_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] rx_desc_dequeue_req_queue; +wire [QUEUE_REQ_TAG_WIDTH-1:0] rx_desc_dequeue_req_tag; +wire rx_desc_dequeue_req_valid; +wire rx_desc_dequeue_req_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] rx_desc_dequeue_resp_queue; +wire [QUEUE_PTR_WIDTH-1:0] rx_desc_dequeue_resp_ptr; +wire [DMA_ADDR_WIDTH-1:0] rx_desc_dequeue_resp_addr; +wire [LOG_BLOCK_SIZE_WIDTH-1:0] rx_desc_dequeue_resp_block_size; +wire [CPL_QUEUE_INDEX_WIDTH-1:0] rx_desc_dequeue_resp_cpl; +wire [QUEUE_REQ_TAG_WIDTH-1:0] rx_desc_dequeue_resp_tag; +wire [QUEUE_OP_TAG_WIDTH-1:0] rx_desc_dequeue_resp_op_tag; +wire rx_desc_dequeue_resp_empty; +wire rx_desc_dequeue_resp_error; +wire rx_desc_dequeue_resp_valid; +wire rx_desc_dequeue_resp_ready; + +wire [QUEUE_OP_TAG_WIDTH-1:0] rx_desc_dequeue_commit_op_tag; +wire rx_desc_dequeue_commit_valid; +wire rx_desc_dequeue_commit_ready; + +wire [CPL_QUEUE_INDEX_WIDTH-1:0] rx_cpl_enqueue_req_queue; +wire [QUEUE_REQ_TAG_WIDTH-1:0] rx_cpl_enqueue_req_tag; +wire rx_cpl_enqueue_req_valid; +wire rx_cpl_enqueue_req_ready; + +wire [DMA_ADDR_WIDTH-1:0] rx_cpl_enqueue_resp_addr; +wire [QUEUE_REQ_TAG_WIDTH-1:0] rx_cpl_enqueue_resp_tag; +wire [QUEUE_OP_TAG_WIDTH-1:0] rx_cpl_enqueue_resp_op_tag; +wire rx_cpl_enqueue_resp_full; +wire rx_cpl_enqueue_resp_error; +wire rx_cpl_enqueue_resp_valid; +wire rx_cpl_enqueue_resp_ready; + +wire [QUEUE_OP_TAG_WIDTH-1:0] rx_cpl_enqueue_commit_op_tag; +wire rx_cpl_enqueue_commit_valid; +wire rx_cpl_enqueue_commit_ready; + +// descriptor and completion +wire [0:0] desc_req_sel; +wire [QUEUE_INDEX_WIDTH-1:0] desc_req_queue; +wire [DESC_REQ_TAG_WIDTH-1:0] desc_req_tag; +wire desc_req_valid; +wire desc_req_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] desc_req_status_queue; +wire [QUEUE_PTR_WIDTH-1:0] desc_req_status_ptr; +wire [CPL_QUEUE_INDEX_WIDTH-1:0] desc_req_status_cpl; +wire [DESC_REQ_TAG_WIDTH-1:0] desc_req_status_tag; +wire desc_req_status_empty; +wire desc_req_status_error; +wire desc_req_status_valid; + +wire [AXIS_DESC_DATA_WIDTH-1:0] axis_desc_tdata; +wire [AXIS_DESC_KEEP_WIDTH-1:0] axis_desc_tkeep; +wire axis_desc_tvalid; +wire axis_desc_tready; +wire axis_desc_tlast; +wire [DESC_REQ_TAG_WIDTH-1:0] axis_desc_tid; +wire axis_desc_tuser; + +wire [PORTS*1-1:0] port_desc_req_sel; +wire [PORTS*QUEUE_INDEX_WIDTH-1:0] port_desc_req_queue; +wire [PORTS*PORT_DESC_REQ_TAG_WIDTH-1:0] port_desc_req_tag; +wire [PORTS-1:0] port_desc_req_valid; +wire [PORTS-1:0] port_desc_req_ready; + +wire [PORTS*QUEUE_INDEX_WIDTH-1:0] port_desc_req_status_queue; +wire [PORTS*QUEUE_PTR_WIDTH-1:0] port_desc_req_status_ptr; +wire [PORTS*CPL_QUEUE_INDEX_WIDTH-1:0] port_desc_req_status_cpl; +wire [PORTS*PORT_DESC_REQ_TAG_WIDTH-1:0] port_desc_req_status_tag; +wire [PORTS-1:0] port_desc_req_status_empty; +wire [PORTS-1:0] port_desc_req_status_error; +wire [PORTS-1:0] port_desc_req_status_valid; + +wire [PORTS*AXIS_DESC_DATA_WIDTH-1:0] port_axis_desc_tdata; +wire [PORTS*AXIS_DESC_KEEP_WIDTH-1:0] port_axis_desc_tkeep; +wire [PORTS-1:0] port_axis_desc_tvalid; +wire [PORTS-1:0] port_axis_desc_tready; +wire [PORTS-1:0] port_axis_desc_tlast; +wire [PORTS*PORT_DESC_REQ_TAG_WIDTH-1:0] port_axis_desc_tid; +wire [PORTS-1:0] port_axis_desc_tuser; + +wire [1:0] cpl_req_sel; +wire [QUEUE_INDEX_WIDTH-1:0] cpl_req_queue; +wire [DESC_REQ_TAG_WIDTH-1:0] cpl_req_tag; +wire [CPL_SIZE*8-1:0] cpl_req_data; +wire cpl_req_valid; +wire cpl_req_ready; + +wire [DESC_REQ_TAG_WIDTH-1:0] cpl_req_status_tag; +wire cpl_req_status_full; +wire cpl_req_status_error; +wire cpl_req_status_valid; + +wire [1:0] event_cpl_req_sel = 2'd2; +wire [QUEUE_INDEX_WIDTH-1:0] event_cpl_req_queue; +wire [PORT_DESC_REQ_TAG_WIDTH-1:0] event_cpl_req_tag; +wire [CPL_SIZE*8-1:0] event_cpl_req_data; +wire event_cpl_req_valid; +wire event_cpl_req_ready; + +wire [PORT_DESC_REQ_TAG_WIDTH-1:0] event_cpl_req_status_tag; +wire event_cpl_req_status_full; +wire event_cpl_req_status_error; +wire event_cpl_req_status_valid; + +wire [PORTS*2-1:0] port_cpl_req_sel; +wire [PORTS*QUEUE_INDEX_WIDTH-1:0] port_cpl_req_queue; +wire [PORTS*PORT_DESC_REQ_TAG_WIDTH-1:0] port_cpl_req_tag; +wire [PORTS*CPL_SIZE*8-1:0] port_cpl_req_data; +wire [PORTS-1:0] port_cpl_req_valid; +wire [PORTS-1:0] port_cpl_req_ready; + +wire [PORTS*PORT_DESC_REQ_TAG_WIDTH-1:0] port_cpl_req_status_tag; +wire [PORTS-1:0] port_cpl_req_status_full; +wire [PORTS-1:0] port_cpl_req_status_error; +wire [PORTS-1:0] port_cpl_req_status_valid; + +// events +wire [EVENT_QUEUE_INDEX_WIDTH-1:0] axis_event_queue; +wire [EVENT_TYPE_WIDTH-1:0] axis_event_type; +wire [EVENT_SOURCE_WIDTH-1:0] axis_event_source; +wire axis_event_valid; +wire axis_event_ready; + +wire [EVENT_QUEUE_INDEX_WIDTH-1:0] tx_fifo_event; +wire [EVENT_TYPE_WIDTH-1:0] tx_fifo_event_type; +wire [EVENT_SOURCE_WIDTH-1:0] tx_fifo_event_source; +wire tx_fifo_event_valid; +wire tx_fifo_event_ready; + +wire [EVENT_QUEUE_INDEX_WIDTH-1:0] rx_fifo_event; +wire [EVENT_TYPE_WIDTH-1:0] rx_fifo_event_type; +wire [EVENT_SOURCE_WIDTH-1:0] rx_fifo_event_source; +wire rx_fifo_event_valid; +wire rx_fifo_event_ready; + +wire [EVENT_QUEUE_INDEX_WIDTH-1:0] tx_event; +wire [EVENT_TYPE_WIDTH-1:0] tx_event_type = 16'd0; +wire [EVENT_SOURCE_WIDTH-1:0] tx_event_source; +wire tx_event_valid; + +wire [EVENT_QUEUE_INDEX_WIDTH-1:0] rx_event; +wire [EVENT_TYPE_WIDTH-1:0] rx_event_type = 16'd1; +wire [EVENT_SOURCE_WIDTH-1:0] rx_event_source; +wire rx_event_valid; + +// interrupts +wire [INT_WIDTH-1:0] event_int; +wire event_int_valid; + +assign msi_irq = (event_int_valid << event_int); + +// Interface control registers +reg axil_ctrl_awready_reg = 1'b0; +reg axil_ctrl_wready_reg = 1'b0; +reg [1:0] axil_ctrl_bresp_reg = 2'b00; +reg axil_ctrl_bvalid_reg = 1'b0; +reg axil_ctrl_arready_reg = 1'b0; +reg [AXIL_DATA_WIDTH-1:0] axil_ctrl_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}; +reg [1:0] axil_ctrl_rresp_reg = 2'b00; +reg axil_ctrl_rvalid_reg = 1'b0; + +assign axil_ctrl_awready = axil_ctrl_awready_reg; +assign axil_ctrl_wready = axil_ctrl_wready_reg; +assign axil_ctrl_bresp = axil_ctrl_bresp_reg; +assign axil_ctrl_bvalid = axil_ctrl_bvalid_reg; +assign axil_ctrl_arready = axil_ctrl_arready_reg; +assign axil_ctrl_rdata = axil_ctrl_rdata_reg; +assign axil_ctrl_rresp = axil_ctrl_rresp_reg; +assign axil_ctrl_rvalid = axil_ctrl_rvalid_reg; + +always @(posedge clk) begin + axil_ctrl_awready_reg <= 1'b0; + axil_ctrl_wready_reg <= 1'b0; + axil_ctrl_bresp_reg <= 2'b00; + axil_ctrl_bvalid_reg <= axil_ctrl_bvalid_reg && !axil_ctrl_bready; + axil_ctrl_arready_reg <= 1'b0; + axil_ctrl_rresp_reg <= 2'b00; + axil_ctrl_rvalid_reg <= axil_ctrl_rvalid_reg && !axil_ctrl_rready; + + if (axil_ctrl_awvalid && axil_ctrl_wvalid && !axil_ctrl_bvalid) begin + // write operation + axil_ctrl_awready_reg <= 1'b1; + axil_ctrl_wready_reg <= 1'b1; + axil_ctrl_bresp_reg <= 2'b00; + axil_ctrl_bvalid_reg <= 1'b1; + + // case ({axil_ctrl_awaddr[15:2], 2'b00}) + // 16'h0000: + // endcase + end + + if (axil_ctrl_arvalid && !axil_ctrl_rvalid) begin + // read operation + axil_ctrl_arready_reg <= 1'b1; + axil_ctrl_rresp_reg <= 2'b00; + axil_ctrl_rvalid_reg <= 1'b1; + axil_ctrl_rdata_reg <= {AXIL_DATA_WIDTH{1'b0}}; + + case ({axil_ctrl_araddr[15:2], 2'b00}) + 16'h0000: axil_ctrl_rdata_reg <= 32'd0; // if_id + 16'h0004: begin + // if_features + axil_ctrl_rdata_reg[0] <= RX_RSS_ENABLE && RX_HASH_ENABLE; + axil_ctrl_rdata_reg[4] <= PTP_TS_ENABLE; + axil_ctrl_rdata_reg[8] <= TX_CHECKSUM_ENABLE; + axil_ctrl_rdata_reg[9] <= RX_CHECKSUM_ENABLE; + axil_ctrl_rdata_reg[10] <= RX_HASH_ENABLE; + end + 16'h0010: axil_ctrl_rdata_reg <= 2**EVENT_QUEUE_INDEX_WIDTH; // event_queue_count + 16'h0014: axil_ctrl_rdata_reg <= AXIL_EQM_BASE_ADDR; // event_queue_offset + 16'h0020: axil_ctrl_rdata_reg <= 2**TX_QUEUE_INDEX_WIDTH; // tx_queue_count + 16'h0024: axil_ctrl_rdata_reg <= AXIL_TX_QM_BASE_ADDR; // tx_queue_offset + 16'h0028: axil_ctrl_rdata_reg <= 2**TX_CPL_QUEUE_INDEX_WIDTH; // tx_cpl_queue_count + 16'h002C: axil_ctrl_rdata_reg <= AXIL_TX_CQM_BASE_ADDR; // tx_cpl_queue_offset + 16'h0030: axil_ctrl_rdata_reg <= 2**RX_QUEUE_INDEX_WIDTH; // rx_queue_count + 16'h0034: axil_ctrl_rdata_reg <= AXIL_RX_QM_BASE_ADDR; // rx_queue_offset + 16'h0038: axil_ctrl_rdata_reg <= 2**RX_CPL_QUEUE_INDEX_WIDTH; // rx_cpl_queue_count + 16'h003C: axil_ctrl_rdata_reg <= AXIL_RX_CQM_BASE_ADDR; // rx_cpl_queue_offset + 16'h0040: axil_ctrl_rdata_reg <= PORTS; // port_count + 16'h0044: axil_ctrl_rdata_reg <= AXIL_PORT_BASE_ADDR; // port_offset + 16'h0048: axil_ctrl_rdata_reg <= 2**AXIL_PORT_ADDR_WIDTH; // port_stride + endcase + end + + if (rst) begin + axil_ctrl_awready_reg <= 1'b0; + axil_ctrl_wready_reg <= 1'b0; + axil_ctrl_bvalid_reg <= 1'b0; + axil_ctrl_arready_reg <= 1'b0; + axil_ctrl_rvalid_reg <= 1'b0; + end +end + +// AXI lite interconnect +parameter AXIL_S_COUNT = 1; +parameter AXIL_M_COUNT = 7+PORTS; + +axil_interconnect #( + .DATA_WIDTH(AXIL_DATA_WIDTH), + .ADDR_WIDTH(AXIL_ADDR_WIDTH), + .STRB_WIDTH(AXIL_STRB_WIDTH), + .S_COUNT(AXIL_S_COUNT), + .M_COUNT(AXIL_M_COUNT), + .M_ADDR_WIDTH({{PORTS{w_32(AXIL_PORT_ADDR_WIDTH)}}, w_32(AXIL_RX_CQM_ADDR_WIDTH), w_32(AXIL_RX_QM_ADDR_WIDTH), w_32(AXIL_TX_CQM_ADDR_WIDTH), w_32(AXIL_TX_QM_ADDR_WIDTH), w_32(AXIL_EQM_ADDR_WIDTH), w_32(AXIL_CTRL_ADDR_WIDTH), w_32(AXIL_CSR_ADDR_WIDTH)}), + .M_CONNECT_READ({AXIL_M_COUNT{{AXIL_S_COUNT{1'b1}}}}), + .M_CONNECT_WRITE({AXIL_M_COUNT{{AXIL_S_COUNT{1'b1}}}}) +) +axil_interconnect_inst ( + .clk(clk), + .rst(rst), + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + .m_axil_awaddr( {axil_port_awaddr, axil_rx_cpl_queue_manager_awaddr, axil_rx_queue_manager_awaddr, axil_tx_cpl_queue_manager_awaddr, axil_tx_queue_manager_awaddr, axil_event_queue_manager_awaddr, axil_ctrl_awaddr, m_axil_csr_awaddr}), + .m_axil_awprot( {axil_port_awprot, axil_rx_cpl_queue_manager_awprot, axil_rx_queue_manager_awprot, axil_tx_cpl_queue_manager_awprot, axil_tx_queue_manager_awprot, axil_event_queue_manager_awprot, axil_ctrl_awprot, m_axil_csr_awprot}), + .m_axil_awvalid({axil_port_awvalid, axil_rx_cpl_queue_manager_awvalid, axil_rx_queue_manager_awvalid, axil_tx_cpl_queue_manager_awvalid, axil_tx_queue_manager_awvalid, axil_event_queue_manager_awvalid, axil_ctrl_awvalid, m_axil_csr_awvalid}), + .m_axil_awready({axil_port_awready, axil_rx_cpl_queue_manager_awready, axil_rx_queue_manager_awready, axil_tx_cpl_queue_manager_awready, axil_tx_queue_manager_awready, axil_event_queue_manager_awready, axil_ctrl_awready, m_axil_csr_awready}), + .m_axil_wdata( {axil_port_wdata, axil_rx_cpl_queue_manager_wdata, axil_rx_queue_manager_wdata, axil_tx_cpl_queue_manager_wdata, axil_tx_queue_manager_wdata, axil_event_queue_manager_wdata, axil_ctrl_wdata, m_axil_csr_wdata}), + .m_axil_wstrb( {axil_port_wstrb, axil_rx_cpl_queue_manager_wstrb, axil_rx_queue_manager_wstrb, axil_tx_cpl_queue_manager_wstrb, axil_tx_queue_manager_wstrb, axil_event_queue_manager_wstrb, axil_ctrl_wstrb, m_axil_csr_wstrb}), + .m_axil_wvalid( {axil_port_wvalid, axil_rx_cpl_queue_manager_wvalid, axil_rx_queue_manager_wvalid, axil_tx_cpl_queue_manager_wvalid, axil_tx_queue_manager_wvalid, axil_event_queue_manager_wvalid, axil_ctrl_wvalid, m_axil_csr_wvalid}), + .m_axil_wready( {axil_port_wready, axil_rx_cpl_queue_manager_wready, axil_rx_queue_manager_wready, axil_tx_cpl_queue_manager_wready, axil_tx_queue_manager_wready, axil_event_queue_manager_wready, axil_ctrl_wready, m_axil_csr_wready}), + .m_axil_bresp( {axil_port_bresp, axil_rx_cpl_queue_manager_bresp, axil_rx_queue_manager_bresp, axil_tx_cpl_queue_manager_bresp, axil_tx_queue_manager_bresp, axil_event_queue_manager_bresp, axil_ctrl_bresp, m_axil_csr_bresp}), + .m_axil_bvalid( {axil_port_bvalid, axil_rx_cpl_queue_manager_bvalid, axil_rx_queue_manager_bvalid, axil_tx_cpl_queue_manager_bvalid, axil_tx_queue_manager_bvalid, axil_event_queue_manager_bvalid, axil_ctrl_bvalid, m_axil_csr_bvalid}), + .m_axil_bready( {axil_port_bready, axil_rx_cpl_queue_manager_bready, axil_rx_queue_manager_bready, axil_tx_cpl_queue_manager_bready, axil_tx_queue_manager_bready, axil_event_queue_manager_bready, axil_ctrl_bready, m_axil_csr_bready}), + .m_axil_araddr( {axil_port_araddr, axil_rx_cpl_queue_manager_araddr, axil_rx_queue_manager_araddr, axil_tx_cpl_queue_manager_araddr, axil_tx_queue_manager_araddr, axil_event_queue_manager_araddr, axil_ctrl_araddr, m_axil_csr_araddr}), + .m_axil_arprot( {axil_port_arprot, axil_rx_cpl_queue_manager_arprot, axil_rx_queue_manager_arprot, axil_tx_cpl_queue_manager_arprot, axil_tx_queue_manager_arprot, axil_event_queue_manager_arprot, axil_ctrl_arprot, m_axil_csr_arprot}), + .m_axil_arvalid({axil_port_arvalid, axil_rx_cpl_queue_manager_arvalid, axil_rx_queue_manager_arvalid, axil_tx_cpl_queue_manager_arvalid, axil_tx_queue_manager_arvalid, axil_event_queue_manager_arvalid, axil_ctrl_arvalid, m_axil_csr_arvalid}), + .m_axil_arready({axil_port_arready, axil_rx_cpl_queue_manager_arready, axil_rx_queue_manager_arready, axil_tx_cpl_queue_manager_arready, axil_tx_queue_manager_arready, axil_event_queue_manager_arready, axil_ctrl_arready, m_axil_csr_arready}), + .m_axil_rdata( {axil_port_rdata, axil_rx_cpl_queue_manager_rdata, axil_rx_queue_manager_rdata, axil_tx_cpl_queue_manager_rdata, axil_tx_queue_manager_rdata, axil_event_queue_manager_rdata, axil_ctrl_rdata, m_axil_csr_rdata}), + .m_axil_rresp( {axil_port_rresp, axil_rx_cpl_queue_manager_rresp, axil_rx_queue_manager_rresp, axil_tx_cpl_queue_manager_rresp, axil_tx_queue_manager_rresp, axil_event_queue_manager_rresp, axil_ctrl_rresp, m_axil_csr_rresp}), + .m_axil_rvalid( {axil_port_rvalid, axil_rx_cpl_queue_manager_rvalid, axil_rx_queue_manager_rvalid, axil_tx_cpl_queue_manager_rvalid, axil_tx_queue_manager_rvalid, axil_event_queue_manager_rvalid, axil_ctrl_rvalid, m_axil_csr_rvalid}), + .m_axil_rready( {axil_port_rready, axil_rx_cpl_queue_manager_rready, axil_rx_queue_manager_rready, axil_tx_cpl_queue_manager_rready, axil_tx_queue_manager_rready, axil_event_queue_manager_rready, axil_ctrl_rready, m_axil_csr_rready}) +); + +// Queue managers + +cpl_queue_manager #( + .ADDR_WIDTH(DMA_ADDR_WIDTH), + .REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .OP_TABLE_SIZE(EVENT_QUEUE_OP_TABLE_SIZE), + .OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(EVENT_QUEUE_INDEX_WIDTH), + .EVENT_WIDTH(INT_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .LOG_QUEUE_SIZE_WIDTH(LOG_QUEUE_SIZE_WIDTH), + .CPL_SIZE(EVENT_SIZE), + .PIPELINE(EVENT_QUEUE_PIPELINE), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_EQM_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) +) +event_queue_manager_inst ( + .clk(clk), + .rst(rst), + + /* + * Enqueue request input + */ + .s_axis_enqueue_req_queue(event_enqueue_req_queue), + .s_axis_enqueue_req_tag(event_enqueue_req_tag), + .s_axis_enqueue_req_valid(event_enqueue_req_valid), + .s_axis_enqueue_req_ready(event_enqueue_req_ready), + + /* + * Enqueue response output + */ + .m_axis_enqueue_resp_queue(), + .m_axis_enqueue_resp_ptr(), + .m_axis_enqueue_resp_addr(event_enqueue_resp_addr), + .m_axis_enqueue_resp_event(), + .m_axis_enqueue_resp_tag(event_enqueue_resp_tag), + .m_axis_enqueue_resp_op_tag(event_enqueue_resp_op_tag), + .m_axis_enqueue_resp_full(event_enqueue_resp_full), + .m_axis_enqueue_resp_error(event_enqueue_resp_error), + .m_axis_enqueue_resp_valid(event_enqueue_resp_valid), + .m_axis_enqueue_resp_ready(event_enqueue_resp_ready), + + /* + * Enqueue commit input + */ + .s_axis_enqueue_commit_op_tag(event_enqueue_commit_op_tag), + .s_axis_enqueue_commit_valid(event_enqueue_commit_valid), + .s_axis_enqueue_commit_ready(event_enqueue_commit_ready), + + /* + * Event output + */ + .m_axis_event(event_int), + .m_axis_event_source(), + .m_axis_event_valid(event_int_valid), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_event_queue_manager_awaddr), + .s_axil_awprot(axil_event_queue_manager_awprot), + .s_axil_awvalid(axil_event_queue_manager_awvalid), + .s_axil_awready(axil_event_queue_manager_awready), + .s_axil_wdata(axil_event_queue_manager_wdata), + .s_axil_wstrb(axil_event_queue_manager_wstrb), + .s_axil_wvalid(axil_event_queue_manager_wvalid), + .s_axil_wready(axil_event_queue_manager_wready), + .s_axil_bresp(axil_event_queue_manager_bresp), + .s_axil_bvalid(axil_event_queue_manager_bvalid), + .s_axil_bready(axil_event_queue_manager_bready), + .s_axil_araddr(axil_event_queue_manager_araddr), + .s_axil_arprot(axil_event_queue_manager_arprot), + .s_axil_arvalid(axil_event_queue_manager_arvalid), + .s_axil_arready(axil_event_queue_manager_arready), + .s_axil_rdata(axil_event_queue_manager_rdata), + .s_axil_rresp(axil_event_queue_manager_rresp), + .s_axil_rvalid(axil_event_queue_manager_rvalid), + .s_axil_rready(axil_event_queue_manager_rready), + + /* + * Configuration + */ + .enable(1'b1) +); + +queue_manager #( + .ADDR_WIDTH(DMA_ADDR_WIDTH), + .REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .OP_TABLE_SIZE(TX_QUEUE_OP_TABLE_SIZE), + .OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(TX_QUEUE_INDEX_WIDTH), + .CPL_INDEX_WIDTH(TX_CPL_QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .LOG_QUEUE_SIZE_WIDTH(LOG_QUEUE_SIZE_WIDTH), + .DESC_SIZE(DESC_SIZE), + .LOG_BLOCK_SIZE_WIDTH(LOG_BLOCK_SIZE_WIDTH), + .PIPELINE(TX_QUEUE_PIPELINE), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_TX_QM_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) +) +tx_queue_manager_inst ( + .clk(clk), + .rst(rst), + + /* + * Dequeue request input + */ + .s_axis_dequeue_req_queue(tx_desc_dequeue_req_queue), + .s_axis_dequeue_req_tag(tx_desc_dequeue_req_tag), + .s_axis_dequeue_req_valid(tx_desc_dequeue_req_valid), + .s_axis_dequeue_req_ready(tx_desc_dequeue_req_ready), + + /* + * Dequeue response output + */ + .m_axis_dequeue_resp_queue(tx_desc_dequeue_resp_queue), + .m_axis_dequeue_resp_ptr(tx_desc_dequeue_resp_ptr), + .m_axis_dequeue_resp_addr(tx_desc_dequeue_resp_addr), + .m_axis_dequeue_resp_block_size(tx_desc_dequeue_resp_block_size), + .m_axis_dequeue_resp_cpl(tx_desc_dequeue_resp_cpl), + .m_axis_dequeue_resp_tag(tx_desc_dequeue_resp_tag), + .m_axis_dequeue_resp_op_tag(tx_desc_dequeue_resp_op_tag), + .m_axis_dequeue_resp_empty(tx_desc_dequeue_resp_empty), + .m_axis_dequeue_resp_error(tx_desc_dequeue_resp_error), + .m_axis_dequeue_resp_valid(tx_desc_dequeue_resp_valid), + .m_axis_dequeue_resp_ready(tx_desc_dequeue_resp_ready), + + /* + * Dequeue commit input + */ + .s_axis_dequeue_commit_op_tag(tx_desc_dequeue_commit_op_tag), + .s_axis_dequeue_commit_valid(tx_desc_dequeue_commit_valid), + .s_axis_dequeue_commit_ready(tx_desc_dequeue_commit_ready), + + /* + * Doorbell output + */ + .m_axis_doorbell_queue(tx_doorbell_queue), + .m_axis_doorbell_valid(tx_doorbell_valid), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_tx_queue_manager_awaddr), + .s_axil_awprot(axil_tx_queue_manager_awprot), + .s_axil_awvalid(axil_tx_queue_manager_awvalid), + .s_axil_awready(axil_tx_queue_manager_awready), + .s_axil_wdata(axil_tx_queue_manager_wdata), + .s_axil_wstrb(axil_tx_queue_manager_wstrb), + .s_axil_wvalid(axil_tx_queue_manager_wvalid), + .s_axil_wready(axil_tx_queue_manager_wready), + .s_axil_bresp(axil_tx_queue_manager_bresp), + .s_axil_bvalid(axil_tx_queue_manager_bvalid), + .s_axil_bready(axil_tx_queue_manager_bready), + .s_axil_araddr(axil_tx_queue_manager_araddr), + .s_axil_arprot(axil_tx_queue_manager_arprot), + .s_axil_arvalid(axil_tx_queue_manager_arvalid), + .s_axil_arready(axil_tx_queue_manager_arready), + .s_axil_rdata(axil_tx_queue_manager_rdata), + .s_axil_rresp(axil_tx_queue_manager_rresp), + .s_axil_rvalid(axil_tx_queue_manager_rvalid), + .s_axil_rready(axil_tx_queue_manager_rready), + + /* + * Configuration + */ + .enable(1'b1) +); + +cpl_queue_manager #( + .ADDR_WIDTH(DMA_ADDR_WIDTH), + .REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .OP_TABLE_SIZE(TX_QUEUE_OP_TABLE_SIZE), + .OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(TX_CPL_QUEUE_INDEX_WIDTH), + .EVENT_WIDTH(EVENT_QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .LOG_QUEUE_SIZE_WIDTH(LOG_QUEUE_SIZE_WIDTH), + .CPL_SIZE(CPL_SIZE), + .PIPELINE(TX_CPL_QUEUE_PIPELINE), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_TX_CQM_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) +) +tx_cpl_queue_manager_inst ( + .clk(clk), + .rst(rst), + + /* + * Enqueue request input + */ + .s_axis_enqueue_req_queue(tx_cpl_enqueue_req_queue), + .s_axis_enqueue_req_tag(tx_cpl_enqueue_req_tag), + .s_axis_enqueue_req_valid(tx_cpl_enqueue_req_valid), + .s_axis_enqueue_req_ready(tx_cpl_enqueue_req_ready), + + /* + * Enqueue response output + */ + .m_axis_enqueue_resp_queue(), + .m_axis_enqueue_resp_ptr(), + .m_axis_enqueue_resp_addr(tx_cpl_enqueue_resp_addr), + .m_axis_enqueue_resp_event(), + .m_axis_enqueue_resp_tag(tx_cpl_enqueue_resp_tag), + .m_axis_enqueue_resp_op_tag(tx_cpl_enqueue_resp_op_tag), + .m_axis_enqueue_resp_full(tx_cpl_enqueue_resp_full), + .m_axis_enqueue_resp_error(tx_cpl_enqueue_resp_error), + .m_axis_enqueue_resp_valid(tx_cpl_enqueue_resp_valid), + .m_axis_enqueue_resp_ready(tx_cpl_enqueue_resp_ready), + + /* + * Enqueue commit input + */ + .s_axis_enqueue_commit_op_tag(tx_cpl_enqueue_commit_op_tag), + .s_axis_enqueue_commit_valid(tx_cpl_enqueue_commit_valid), + .s_axis_enqueue_commit_ready(tx_cpl_enqueue_commit_ready), + + /* + * Event output + */ + .m_axis_event(tx_event), + .m_axis_event_source(tx_event_source), + .m_axis_event_valid(tx_event_valid), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_tx_cpl_queue_manager_awaddr), + .s_axil_awprot(axil_tx_cpl_queue_manager_awprot), + .s_axil_awvalid(axil_tx_cpl_queue_manager_awvalid), + .s_axil_awready(axil_tx_cpl_queue_manager_awready), + .s_axil_wdata(axil_tx_cpl_queue_manager_wdata), + .s_axil_wstrb(axil_tx_cpl_queue_manager_wstrb), + .s_axil_wvalid(axil_tx_cpl_queue_manager_wvalid), + .s_axil_wready(axil_tx_cpl_queue_manager_wready), + .s_axil_bresp(axil_tx_cpl_queue_manager_bresp), + .s_axil_bvalid(axil_tx_cpl_queue_manager_bvalid), + .s_axil_bready(axil_tx_cpl_queue_manager_bready), + .s_axil_araddr(axil_tx_cpl_queue_manager_araddr), + .s_axil_arprot(axil_tx_cpl_queue_manager_arprot), + .s_axil_arvalid(axil_tx_cpl_queue_manager_arvalid), + .s_axil_arready(axil_tx_cpl_queue_manager_arready), + .s_axil_rdata(axil_tx_cpl_queue_manager_rdata), + .s_axil_rresp(axil_tx_cpl_queue_manager_rresp), + .s_axil_rvalid(axil_tx_cpl_queue_manager_rvalid), + .s_axil_rready(axil_tx_cpl_queue_manager_rready), + + /* + * Configuration + */ + .enable(1'b1) +); + +queue_manager #( + .ADDR_WIDTH(DMA_ADDR_WIDTH), + .REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .OP_TABLE_SIZE(RX_QUEUE_OP_TABLE_SIZE), + .OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(RX_QUEUE_INDEX_WIDTH), + .CPL_INDEX_WIDTH(RX_CPL_QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .LOG_QUEUE_SIZE_WIDTH(LOG_QUEUE_SIZE_WIDTH), + .DESC_SIZE(DESC_SIZE), + .LOG_BLOCK_SIZE_WIDTH(LOG_BLOCK_SIZE_WIDTH), + .PIPELINE(RX_QUEUE_PIPELINE), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_RX_QM_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) +) +rx_queue_manager_inst ( + .clk(clk), + .rst(rst), + + /* + * Dequeue request input + */ + .s_axis_dequeue_req_queue(rx_desc_dequeue_req_queue), + .s_axis_dequeue_req_tag(rx_desc_dequeue_req_tag), + .s_axis_dequeue_req_valid(rx_desc_dequeue_req_valid), + .s_axis_dequeue_req_ready(rx_desc_dequeue_req_ready), + + /* + * Dequeue response output + */ + .m_axis_dequeue_resp_queue(rx_desc_dequeue_resp_queue), + .m_axis_dequeue_resp_ptr(rx_desc_dequeue_resp_ptr), + .m_axis_dequeue_resp_addr(rx_desc_dequeue_resp_addr), + .m_axis_dequeue_resp_block_size(rx_desc_dequeue_resp_block_size), + .m_axis_dequeue_resp_cpl(rx_desc_dequeue_resp_cpl), + .m_axis_dequeue_resp_tag(rx_desc_dequeue_resp_tag), + .m_axis_dequeue_resp_op_tag(rx_desc_dequeue_resp_op_tag), + .m_axis_dequeue_resp_empty(rx_desc_dequeue_resp_empty), + .m_axis_dequeue_resp_error(rx_desc_dequeue_resp_error), + .m_axis_dequeue_resp_valid(rx_desc_dequeue_resp_valid), + .m_axis_dequeue_resp_ready(rx_desc_dequeue_resp_ready), + + /* + * Dequeue commit input + */ + .s_axis_dequeue_commit_op_tag(rx_desc_dequeue_commit_op_tag), + .s_axis_dequeue_commit_valid(rx_desc_dequeue_commit_valid), + .s_axis_dequeue_commit_ready(rx_desc_dequeue_commit_ready), + + /* + * Doorbell output + */ + .m_axis_doorbell_queue(), + .m_axis_doorbell_valid(), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_rx_queue_manager_awaddr), + .s_axil_awprot(axil_rx_queue_manager_awprot), + .s_axil_awvalid(axil_rx_queue_manager_awvalid), + .s_axil_awready(axil_rx_queue_manager_awready), + .s_axil_wdata(axil_rx_queue_manager_wdata), + .s_axil_wstrb(axil_rx_queue_manager_wstrb), + .s_axil_wvalid(axil_rx_queue_manager_wvalid), + .s_axil_wready(axil_rx_queue_manager_wready), + .s_axil_bresp(axil_rx_queue_manager_bresp), + .s_axil_bvalid(axil_rx_queue_manager_bvalid), + .s_axil_bready(axil_rx_queue_manager_bready), + .s_axil_araddr(axil_rx_queue_manager_araddr), + .s_axil_arprot(axil_rx_queue_manager_arprot), + .s_axil_arvalid(axil_rx_queue_manager_arvalid), + .s_axil_arready(axil_rx_queue_manager_arready), + .s_axil_rdata(axil_rx_queue_manager_rdata), + .s_axil_rresp(axil_rx_queue_manager_rresp), + .s_axil_rvalid(axil_rx_queue_manager_rvalid), + .s_axil_rready(axil_rx_queue_manager_rready), + + /* + * Configuration + */ + .enable(1'b1) +); + +cpl_queue_manager #( + .ADDR_WIDTH(DMA_ADDR_WIDTH), + .REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .OP_TABLE_SIZE(RX_QUEUE_OP_TABLE_SIZE), + .OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(RX_CPL_QUEUE_INDEX_WIDTH), + .EVENT_WIDTH(EVENT_QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .LOG_QUEUE_SIZE_WIDTH(LOG_QUEUE_SIZE_WIDTH), + .CPL_SIZE(CPL_SIZE), + .PIPELINE(RX_CPL_QUEUE_PIPELINE), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_RX_CQM_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) +) +rx_cpl_queue_manager_inst ( + .clk(clk), + .rst(rst), + + /* + * Enqueue request input + */ + .s_axis_enqueue_req_queue(rx_cpl_enqueue_req_queue), + .s_axis_enqueue_req_tag(rx_cpl_enqueue_req_tag), + .s_axis_enqueue_req_valid(rx_cpl_enqueue_req_valid), + .s_axis_enqueue_req_ready(rx_cpl_enqueue_req_ready), + + /* + * Enqueue response output + */ + .m_axis_enqueue_resp_queue(), + .m_axis_enqueue_resp_ptr(), + .m_axis_enqueue_resp_addr(rx_cpl_enqueue_resp_addr), + .m_axis_enqueue_resp_event(), + .m_axis_enqueue_resp_tag(rx_cpl_enqueue_resp_tag), + .m_axis_enqueue_resp_op_tag(rx_cpl_enqueue_resp_op_tag), + .m_axis_enqueue_resp_full(rx_cpl_enqueue_resp_full), + .m_axis_enqueue_resp_error(rx_cpl_enqueue_resp_error), + .m_axis_enqueue_resp_valid(rx_cpl_enqueue_resp_valid), + .m_axis_enqueue_resp_ready(rx_cpl_enqueue_resp_ready), + + /* + * Enqueue commit input + */ + .s_axis_enqueue_commit_op_tag(rx_cpl_enqueue_commit_op_tag), + .s_axis_enqueue_commit_valid(rx_cpl_enqueue_commit_valid), + .s_axis_enqueue_commit_ready(rx_cpl_enqueue_commit_ready), + + /* + * Event output + */ + .m_axis_event(rx_event), + .m_axis_event_source(rx_event_source), + .m_axis_event_valid(rx_event_valid), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_rx_cpl_queue_manager_awaddr), + .s_axil_awprot(axil_rx_cpl_queue_manager_awprot), + .s_axil_awvalid(axil_rx_cpl_queue_manager_awvalid), + .s_axil_awready(axil_rx_cpl_queue_manager_awready), + .s_axil_wdata(axil_rx_cpl_queue_manager_wdata), + .s_axil_wstrb(axil_rx_cpl_queue_manager_wstrb), + .s_axil_wvalid(axil_rx_cpl_queue_manager_wvalid), + .s_axil_wready(axil_rx_cpl_queue_manager_wready), + .s_axil_bresp(axil_rx_cpl_queue_manager_bresp), + .s_axil_bvalid(axil_rx_cpl_queue_manager_bvalid), + .s_axil_bready(axil_rx_cpl_queue_manager_bready), + .s_axil_araddr(axil_rx_cpl_queue_manager_araddr), + .s_axil_arprot(axil_rx_cpl_queue_manager_arprot), + .s_axil_arvalid(axil_rx_cpl_queue_manager_arvalid), + .s_axil_arready(axil_rx_cpl_queue_manager_arready), + .s_axil_rdata(axil_rx_cpl_queue_manager_rdata), + .s_axil_rresp(axil_rx_cpl_queue_manager_rresp), + .s_axil_rvalid(axil_rx_cpl_queue_manager_rvalid), + .s_axil_rready(axil_rx_cpl_queue_manager_rready), + + /* + * Configuration + */ + .enable(1'b1) +); + +if (PORTS > 1) begin + + desc_op_mux #( + .PORTS(PORTS), + .SELECT_WIDTH(1), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .CPL_QUEUE_INDEX_WIDTH(CPL_QUEUE_INDEX_WIDTH), + .S_REQ_TAG_WIDTH(PORT_DESC_REQ_TAG_WIDTH), + .M_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH), + .AXIS_DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .AXIS_KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .ARB_TYPE("ROUND_ROBIN"), + .LSB_PRIORITY("HIGH") + ) + desc_op_mux_inst ( + .clk(clk), + .rst(rst), + + /* + * Descriptor request output + */ + .m_axis_req_sel(desc_req_sel), + .m_axis_req_queue(desc_req_queue), + .m_axis_req_tag(desc_req_tag), + .m_axis_req_valid(desc_req_valid), + .m_axis_req_ready(desc_req_ready), + + /* + * Descriptor request status input + */ + .s_axis_req_status_queue(desc_req_status_queue), + .s_axis_req_status_ptr(desc_req_status_ptr), + .s_axis_req_status_cpl(desc_req_status_cpl), + .s_axis_req_status_tag(desc_req_status_tag), + .s_axis_req_status_empty(desc_req_status_empty), + .s_axis_req_status_error(desc_req_status_error), + .s_axis_req_status_valid(desc_req_status_valid), + + /* + * Descriptor data input + */ + .s_axis_desc_tdata(axis_desc_tdata), + .s_axis_desc_tkeep(axis_desc_tkeep), + .s_axis_desc_tvalid(axis_desc_tvalid), + .s_axis_desc_tready(axis_desc_tready), + .s_axis_desc_tlast(axis_desc_tlast), + .s_axis_desc_tid(axis_desc_tid), + .s_axis_desc_tuser(axis_desc_tuser), + + /* + * Descriptor request input + */ + .s_axis_req_sel(port_desc_req_sel), + .s_axis_req_queue(port_desc_req_queue), + .s_axis_req_tag(port_desc_req_tag), + .s_axis_req_valid(port_desc_req_valid), + .s_axis_req_ready(port_desc_req_ready), + + /* + * Descriptor request status output + */ + .m_axis_req_status_queue(port_desc_req_status_queue), + .m_axis_req_status_ptr(port_desc_req_status_ptr), + .m_axis_req_status_cpl(port_desc_req_status_cpl), + .m_axis_req_status_tag(port_desc_req_status_tag), + .m_axis_req_status_empty(port_desc_req_status_empty), + .m_axis_req_status_error(port_desc_req_status_error), + .m_axis_req_status_valid(port_desc_req_status_valid), + + /* + * Descriptor data output + */ + .m_axis_desc_tdata(port_axis_desc_tdata), + .m_axis_desc_tkeep(port_axis_desc_tkeep), + .m_axis_desc_tvalid(port_axis_desc_tvalid), + .m_axis_desc_tready(port_axis_desc_tready), + .m_axis_desc_tlast(port_axis_desc_tlast), + .m_axis_desc_tid(port_axis_desc_tid), + .m_axis_desc_tuser(port_axis_desc_tuser) + ); + +end else begin + + assign desc_req_sel = port_desc_req_sel; + assign desc_req_queue = port_desc_req_queue; + assign desc_req_tag = port_desc_req_tag; + assign desc_req_valid = port_desc_req_valid; + assign port_desc_req_ready = desc_req_ready; + + assign port_desc_req_status_queue = desc_req_status_queue; + assign port_desc_req_status_ptr = desc_req_status_ptr; + assign port_desc_req_status_cpl = desc_req_status_cpl; + assign port_desc_req_status_tag = desc_req_status_tag; + assign port_desc_req_status_empty = desc_req_status_empty; + assign port_desc_req_status_error = desc_req_status_error; + assign port_desc_req_status_valid = desc_req_status_valid; + + assign port_axis_desc_tdata = axis_desc_tdata; + assign port_axis_desc_tkeep = axis_desc_tkeep; + assign port_axis_desc_tvalid = axis_desc_tvalid; + assign axis_desc_tready = port_axis_desc_tready; + assign port_axis_desc_tlast = axis_desc_tlast; + assign port_axis_desc_tid = axis_desc_tid; + assign port_axis_desc_tuser = axis_desc_tuser; + +end + +desc_fetch #( + .PORTS(2), + .SELECT_WIDTH(1), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .RAM_PIPELINE(RAM_PIPELINE), + .AXIS_DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .AXIS_KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .DMA_LEN_WIDTH(DMA_LEN_WIDTH), + .DMA_TAG_WIDTH(DMA_TAG_WIDTH), + .REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH), + .QUEUE_REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .QUEUE_OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .CPL_QUEUE_INDEX_WIDTH(CPL_QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .DESC_SIZE(DESC_SIZE), + .LOG_BLOCK_SIZE_WIDTH(LOG_BLOCK_SIZE_WIDTH), + .DESC_TABLE_SIZE(32) +) +desc_fetch_inst ( + .clk(clk), + .rst(rst), + + /* + * Descriptor read request input + */ + .s_axis_req_sel(desc_req_sel), + .s_axis_req_queue(desc_req_queue), + .s_axis_req_tag(desc_req_tag), + .s_axis_req_valid(desc_req_valid), + .s_axis_req_ready(desc_req_ready), + + /* + * Descriptor read request status output + */ + .m_axis_req_status_queue(desc_req_status_queue), + .m_axis_req_status_ptr(desc_req_status_ptr), + .m_axis_req_status_cpl(desc_req_status_cpl), + .m_axis_req_status_tag(desc_req_status_tag), + .m_axis_req_status_empty(desc_req_status_empty), + .m_axis_req_status_error(desc_req_status_error), + .m_axis_req_status_valid(desc_req_status_valid), + + /* + * Descriptor data output + */ + .m_axis_desc_tdata(axis_desc_tdata), + .m_axis_desc_tkeep(axis_desc_tkeep), + .m_axis_desc_tvalid(axis_desc_tvalid), + .m_axis_desc_tready(axis_desc_tready), + .m_axis_desc_tlast(axis_desc_tlast), + .m_axis_desc_tid(axis_desc_tid), + .m_axis_desc_tuser(axis_desc_tuser), + + /* + * Descriptor dequeue request output + */ + .m_axis_desc_dequeue_req_queue({rx_desc_dequeue_req_queue, tx_desc_dequeue_req_queue}), + .m_axis_desc_dequeue_req_tag({rx_desc_dequeue_req_tag, tx_desc_dequeue_req_tag}), + .m_axis_desc_dequeue_req_valid({rx_desc_dequeue_req_valid, tx_desc_dequeue_req_valid}), + .m_axis_desc_dequeue_req_ready({rx_desc_dequeue_req_ready, tx_desc_dequeue_req_ready}), + + /* + * Descriptor dequeue response input + */ + .s_axis_desc_dequeue_resp_queue({rx_desc_dequeue_resp_queue, tx_desc_dequeue_resp_queue}), + .s_axis_desc_dequeue_resp_ptr({rx_desc_dequeue_resp_ptr, tx_desc_dequeue_resp_ptr}), + .s_axis_desc_dequeue_resp_addr({rx_desc_dequeue_resp_addr, tx_desc_dequeue_resp_addr}), + .s_axis_desc_dequeue_resp_block_size({rx_desc_dequeue_resp_block_size, tx_desc_dequeue_resp_block_size}), + .s_axis_desc_dequeue_resp_cpl({rx_desc_dequeue_resp_cpl, tx_desc_dequeue_resp_cpl}), + .s_axis_desc_dequeue_resp_tag({rx_desc_dequeue_resp_tag, tx_desc_dequeue_resp_tag}), + .s_axis_desc_dequeue_resp_op_tag({rx_desc_dequeue_resp_op_tag, tx_desc_dequeue_resp_op_tag}), + .s_axis_desc_dequeue_resp_empty({rx_desc_dequeue_resp_empty, tx_desc_dequeue_resp_empty}), + .s_axis_desc_dequeue_resp_error({rx_desc_dequeue_resp_error, tx_desc_dequeue_resp_error}), + .s_axis_desc_dequeue_resp_valid({rx_desc_dequeue_resp_valid, tx_desc_dequeue_resp_valid}), + .s_axis_desc_dequeue_resp_ready({rx_desc_dequeue_resp_ready, tx_desc_dequeue_resp_ready}), + + /* + * Descriptor dequeue commit output + */ + .m_axis_desc_dequeue_commit_op_tag({rx_desc_dequeue_commit_op_tag, tx_desc_dequeue_commit_op_tag}), + .m_axis_desc_dequeue_commit_valid({rx_desc_dequeue_commit_valid, tx_desc_dequeue_commit_valid}), + .m_axis_desc_dequeue_commit_ready({rx_desc_dequeue_commit_ready, tx_desc_dequeue_commit_ready}), + + /* + * DMA read descriptor output + */ + .m_axis_dma_read_desc_dma_addr(m_axis_ctrl_dma_read_desc_dma_addr), + .m_axis_dma_read_desc_ram_addr(m_axis_ctrl_dma_read_desc_ram_addr), + .m_axis_dma_read_desc_len(m_axis_ctrl_dma_read_desc_len), + .m_axis_dma_read_desc_tag(m_axis_ctrl_dma_read_desc_tag), + .m_axis_dma_read_desc_valid(m_axis_ctrl_dma_read_desc_valid), + .m_axis_dma_read_desc_ready(m_axis_ctrl_dma_read_desc_ready), + + /* + * DMA read descriptor status input + */ + .s_axis_dma_read_desc_status_tag(s_axis_ctrl_dma_read_desc_status_tag), + .s_axis_dma_read_desc_status_valid(s_axis_ctrl_dma_read_desc_status_valid), + + /* + * RAM interface + */ + .dma_ram_wr_cmd_be(ctrl_dma_ram_wr_cmd_be), + .dma_ram_wr_cmd_addr(ctrl_dma_ram_wr_cmd_addr), + .dma_ram_wr_cmd_data(ctrl_dma_ram_wr_cmd_data), + .dma_ram_wr_cmd_valid(ctrl_dma_ram_wr_cmd_valid), + .dma_ram_wr_cmd_ready(ctrl_dma_ram_wr_cmd_ready), + + /* + * Configuration + */ + .enable(1'b1) +); + +assign m_axis_ctrl_dma_read_desc_ram_sel = 0; + +cpl_op_mux #( + .PORTS(PORTS+1), + .SELECT_WIDTH(2), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .S_REQ_TAG_WIDTH(PORT_DESC_REQ_TAG_WIDTH), + .M_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH), + .CPL_SIZE(CPL_SIZE), + .ARB_TYPE("ROUND_ROBIN"), + .LSB_PRIORITY("HIGH") +) +cpl_op_mux_inst ( + .clk(clk), + .rst(rst), + + /* + * Completion request output + */ + .m_axis_req_sel(cpl_req_sel), + .m_axis_req_queue(cpl_req_queue), + .m_axis_req_tag(cpl_req_tag), + .m_axis_req_data(cpl_req_data), + .m_axis_req_valid(cpl_req_valid), + .m_axis_req_ready(cpl_req_ready), + + /* + * Completion request status input + */ + .s_axis_req_status_tag(cpl_req_status_tag), + .s_axis_req_status_full(cpl_req_status_full), + .s_axis_req_status_error(cpl_req_status_error), + .s_axis_req_status_valid(cpl_req_status_valid), + + /* + * Completion request input + */ + .s_axis_req_sel({port_cpl_req_sel, event_cpl_req_sel}), + .s_axis_req_queue({port_cpl_req_queue, event_cpl_req_queue}), + .s_axis_req_tag({port_cpl_req_tag, event_cpl_req_tag}), + .s_axis_req_data({port_cpl_req_data, event_cpl_req_data}), + .s_axis_req_valid({port_cpl_req_valid, event_cpl_req_valid}), + .s_axis_req_ready({port_cpl_req_ready, event_cpl_req_ready}), + + /* + * Completion response output + */ + .m_axis_req_status_tag({port_cpl_req_status_tag, event_cpl_req_status_tag}), + .m_axis_req_status_full({port_cpl_req_status_full, event_cpl_req_status_full}), + .m_axis_req_status_error({port_cpl_req_status_error, event_cpl_req_status_error}), + .m_axis_req_status_valid({port_cpl_req_status_valid, event_cpl_req_status_valid}) +); + +cpl_write #( + .PORTS(3), + .SELECT_WIDTH(2), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_PIPELINE(RAM_PIPELINE), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .DMA_LEN_WIDTH(DMA_LEN_WIDTH), + .DMA_TAG_WIDTH(DMA_TAG_WIDTH), + .REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH), + .QUEUE_REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .QUEUE_OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .CPL_SIZE(CPL_SIZE), + .DESC_TABLE_SIZE(32) +) +cpl_write_inst ( + .clk(clk), + .rst(rst), + + /* + * Completion read request input + */ + .s_axis_req_sel(cpl_req_sel), + .s_axis_req_queue(cpl_req_queue), + .s_axis_req_tag(cpl_req_tag), + .s_axis_req_data(cpl_req_data), + .s_axis_req_valid(cpl_req_valid), + .s_axis_req_ready(cpl_req_ready), + + /* + * Completion read request status output + */ + .m_axis_req_status_tag(cpl_req_status_tag), + .m_axis_req_status_full(cpl_req_status_full), + .m_axis_req_status_error(cpl_req_status_error), + .m_axis_req_status_valid(cpl_req_status_valid), + + /* + * Completion enqueue request output + */ + .m_axis_cpl_enqueue_req_queue({event_enqueue_req_queue, rx_cpl_enqueue_req_queue, tx_cpl_enqueue_req_queue}), + .m_axis_cpl_enqueue_req_tag({event_enqueue_req_tag, rx_cpl_enqueue_req_tag, tx_cpl_enqueue_req_tag}), + .m_axis_cpl_enqueue_req_valid({event_enqueue_req_valid, rx_cpl_enqueue_req_valid, tx_cpl_enqueue_req_valid}), + .m_axis_cpl_enqueue_req_ready({event_enqueue_req_ready, rx_cpl_enqueue_req_ready, tx_cpl_enqueue_req_ready}), + + /* + * Completion enqueue response input + */ + .s_axis_cpl_enqueue_resp_addr({event_enqueue_resp_addr, rx_cpl_enqueue_resp_addr, tx_cpl_enqueue_resp_addr}), + .s_axis_cpl_enqueue_resp_tag({event_enqueue_resp_tag, rx_cpl_enqueue_resp_tag, tx_cpl_enqueue_resp_tag}), + .s_axis_cpl_enqueue_resp_op_tag({event_enqueue_resp_op_tag, rx_cpl_enqueue_resp_op_tag, tx_cpl_enqueue_resp_op_tag}), + .s_axis_cpl_enqueue_resp_full({event_enqueue_resp_full, rx_cpl_enqueue_resp_full, tx_cpl_enqueue_resp_full}), + .s_axis_cpl_enqueue_resp_error({event_enqueue_resp_error, rx_cpl_enqueue_resp_error, tx_cpl_enqueue_resp_error}), + .s_axis_cpl_enqueue_resp_valid({event_enqueue_resp_valid, rx_cpl_enqueue_resp_valid, tx_cpl_enqueue_resp_valid}), + .s_axis_cpl_enqueue_resp_ready({event_enqueue_resp_ready, rx_cpl_enqueue_resp_ready, tx_cpl_enqueue_resp_ready}), + + /* + * Completion enqueue commit output + */ + .m_axis_cpl_enqueue_commit_op_tag({event_enqueue_commit_op_tag, rx_cpl_enqueue_commit_op_tag, tx_cpl_enqueue_commit_op_tag}), + .m_axis_cpl_enqueue_commit_valid({event_enqueue_commit_valid, rx_cpl_enqueue_commit_valid, tx_cpl_enqueue_commit_valid}), + .m_axis_cpl_enqueue_commit_ready({event_enqueue_commit_ready, rx_cpl_enqueue_commit_ready, tx_cpl_enqueue_commit_ready}), + + /* + * DMA write descriptor output + */ + .m_axis_dma_write_desc_dma_addr(m_axis_ctrl_dma_write_desc_dma_addr), + .m_axis_dma_write_desc_ram_addr(m_axis_ctrl_dma_write_desc_ram_addr), + .m_axis_dma_write_desc_len(m_axis_ctrl_dma_write_desc_len), + .m_axis_dma_write_desc_tag(m_axis_ctrl_dma_write_desc_tag), + .m_axis_dma_write_desc_valid(m_axis_ctrl_dma_write_desc_valid), + .m_axis_dma_write_desc_ready(m_axis_ctrl_dma_write_desc_ready), + + /* + * DMA write descriptor status input + */ + .s_axis_dma_write_desc_status_tag(s_axis_ctrl_dma_write_desc_status_tag), + .s_axis_dma_write_desc_status_valid(s_axis_ctrl_dma_write_desc_status_valid), + + /* + * RAM interface + */ + .dma_ram_rd_cmd_addr(ctrl_dma_ram_rd_cmd_addr), + .dma_ram_rd_cmd_valid(ctrl_dma_ram_rd_cmd_valid), + .dma_ram_rd_cmd_ready(ctrl_dma_ram_rd_cmd_ready), + .dma_ram_rd_resp_data(ctrl_dma_ram_rd_resp_data), + .dma_ram_rd_resp_valid(ctrl_dma_ram_rd_resp_valid), + .dma_ram_rd_resp_ready(ctrl_dma_ram_rd_resp_ready), + + /* + * Configuration + */ + .enable(1'b1) +); + +assign m_axis_ctrl_dma_write_desc_ram_sel = 0; + +if (PORTS > 1) begin + + dma_if_mux # + ( + .PORTS(PORTS), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .S_RAM_SEL_WIDTH(0), + .M_RAM_SEL_WIDTH(RAM_SEL_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .LEN_WIDTH(DMA_LEN_WIDTH), + .S_TAG_WIDTH(DMA_TAG_WIDTH_INT), + .M_TAG_WIDTH(DMA_TAG_WIDTH), + .ARB_TYPE("ROUND_ROBIN"), + .LSB_PRIORITY("HIGH") + ) + dma_if_mux_inst ( + .clk(clk), + .rst(rst), + + /* + * Read descriptor output (to DMA interface) + */ + .m_axis_read_desc_dma_addr(m_axis_data_dma_read_desc_dma_addr), + .m_axis_read_desc_ram_sel(m_axis_data_dma_read_desc_ram_sel), + .m_axis_read_desc_ram_addr(m_axis_data_dma_read_desc_ram_addr), + .m_axis_read_desc_len(m_axis_data_dma_read_desc_len), + .m_axis_read_desc_tag(m_axis_data_dma_read_desc_tag), + .m_axis_read_desc_valid(m_axis_data_dma_read_desc_valid), + .m_axis_read_desc_ready(m_axis_data_dma_read_desc_ready), + + /* + * Read descriptor status input (from DMA interface) + */ + .s_axis_read_desc_status_tag(s_axis_data_dma_read_desc_status_tag), + .s_axis_read_desc_status_valid(s_axis_data_dma_read_desc_status_valid), + + /* + * Read descriptor input + */ + .s_axis_read_desc_dma_addr(port_dma_read_desc_dma_addr), + .s_axis_read_desc_ram_sel(0), + .s_axis_read_desc_ram_addr(port_dma_read_desc_ram_addr), + .s_axis_read_desc_len(port_dma_read_desc_len), + .s_axis_read_desc_tag(port_dma_read_desc_tag), + .s_axis_read_desc_valid(port_dma_read_desc_valid), + .s_axis_read_desc_ready(port_dma_read_desc_ready), + + /* + * Read descriptor status output + */ + .m_axis_read_desc_status_tag(port_dma_read_desc_status_tag), + .m_axis_read_desc_status_valid(port_dma_read_desc_status_valid), + + /* + * Write descriptor output (to DMA interface) + */ + .m_axis_write_desc_dma_addr(m_axis_data_dma_write_desc_dma_addr), + .m_axis_write_desc_ram_sel(m_axis_data_dma_write_desc_ram_sel), + .m_axis_write_desc_ram_addr(m_axis_data_dma_write_desc_ram_addr), + .m_axis_write_desc_len(m_axis_data_dma_write_desc_len), + .m_axis_write_desc_tag(m_axis_data_dma_write_desc_tag), + .m_axis_write_desc_valid(m_axis_data_dma_write_desc_valid), + .m_axis_write_desc_ready(m_axis_data_dma_write_desc_ready), + + /* + * Write descriptor status input (from DMA interface) + */ + .s_axis_write_desc_status_tag(s_axis_data_dma_write_desc_status_tag), + .s_axis_write_desc_status_valid(s_axis_data_dma_write_desc_status_valid), + + /* + * Write descriptor input + */ + .s_axis_write_desc_dma_addr(port_dma_write_desc_dma_addr), + .s_axis_write_desc_ram_sel(0), + .s_axis_write_desc_ram_addr(port_dma_write_desc_ram_addr), + .s_axis_write_desc_len(port_dma_write_desc_len), + .s_axis_write_desc_tag(port_dma_write_desc_tag), + .s_axis_write_desc_valid(port_dma_write_desc_valid), + .s_axis_write_desc_ready(port_dma_write_desc_ready), + + /* + * Write descriptor status output + */ + .m_axis_write_desc_status_tag(port_dma_write_desc_status_tag), + .m_axis_write_desc_status_valid(port_dma_write_desc_status_valid), + + /* + * RAM interface (from DMA interface) + */ + .if_ram_wr_cmd_sel(data_dma_ram_wr_cmd_sel), + .if_ram_wr_cmd_be(data_dma_ram_wr_cmd_be), + .if_ram_wr_cmd_addr(data_dma_ram_wr_cmd_addr), + .if_ram_wr_cmd_data(data_dma_ram_wr_cmd_data), + .if_ram_wr_cmd_valid(data_dma_ram_wr_cmd_valid), + .if_ram_wr_cmd_ready(data_dma_ram_wr_cmd_ready), + .if_ram_rd_cmd_sel(data_dma_ram_rd_cmd_sel), + .if_ram_rd_cmd_addr(data_dma_ram_rd_cmd_addr), + .if_ram_rd_cmd_valid(data_dma_ram_rd_cmd_valid), + .if_ram_rd_cmd_ready(data_dma_ram_rd_cmd_ready), + .if_ram_rd_resp_data(data_dma_ram_rd_resp_data), + .if_ram_rd_resp_valid(data_dma_ram_rd_resp_valid), + .if_ram_rd_resp_ready(data_dma_ram_rd_resp_ready), + + /* + * RAM interface + */ + .ram_wr_cmd_sel(), + .ram_wr_cmd_be(port_dma_ram_wr_cmd_be), + .ram_wr_cmd_addr(port_dma_ram_wr_cmd_addr), + .ram_wr_cmd_data(port_dma_ram_wr_cmd_data), + .ram_wr_cmd_valid(port_dma_ram_wr_cmd_valid), + .ram_wr_cmd_ready(port_dma_ram_wr_cmd_ready), + .ram_rd_cmd_sel(), + .ram_rd_cmd_addr(port_dma_ram_rd_cmd_addr), + .ram_rd_cmd_valid(port_dma_ram_rd_cmd_valid), + .ram_rd_cmd_ready(port_dma_ram_rd_cmd_ready), + .ram_rd_resp_data(port_dma_ram_rd_resp_data), + .ram_rd_resp_valid(port_dma_ram_rd_resp_valid), + .ram_rd_resp_ready(port_dma_ram_rd_resp_ready) + ); + +end else begin + + assign m_axis_data_dma_read_desc_dma_addr = port_dma_read_desc_dma_addr; + assign m_axis_data_dma_read_desc_ram_sel = 0; + assign m_axis_data_dma_read_desc_ram_addr = port_dma_read_desc_ram_addr; + assign m_axis_data_dma_read_desc_len = port_dma_read_desc_len; + assign m_axis_data_dma_read_desc_tag = port_dma_read_desc_tag; + assign m_axis_data_dma_read_desc_valid = port_dma_read_desc_valid; + assign port_dma_read_desc_ready = m_axis_data_dma_read_desc_ready; + + assign port_dma_read_desc_status_tag = s_axis_data_dma_read_desc_status_tag; + assign port_dma_read_desc_status_valid = s_axis_data_dma_read_desc_status_valid; + + assign m_axis_data_dma_write_desc_dma_addr = port_dma_write_desc_dma_addr; + assign m_axis_data_dma_write_desc_ram_sel = 0; + assign m_axis_data_dma_write_desc_ram_addr = port_dma_write_desc_ram_addr; + assign m_axis_data_dma_write_desc_len = port_dma_write_desc_len; + assign m_axis_data_dma_write_desc_tag = port_dma_write_desc_tag; + assign m_axis_data_dma_write_desc_valid = port_dma_write_desc_valid; + assign port_dma_write_desc_ready = m_axis_data_dma_write_desc_ready; + + assign port_dma_write_desc_status_tag = s_axis_data_dma_write_desc_status_tag; + assign port_dma_write_desc_status_valid = s_axis_data_dma_write_desc_status_valid; + + assign port_dma_ram_wr_cmd_be = data_dma_ram_wr_cmd_be; + assign port_dma_ram_wr_cmd_addr = data_dma_ram_wr_cmd_addr; + assign port_dma_ram_wr_cmd_data = data_dma_ram_wr_cmd_data; + assign port_dma_ram_wr_cmd_valid = data_dma_ram_wr_cmd_valid; + assign data_dma_ram_wr_cmd_ready = port_dma_ram_wr_cmd_ready; + assign port_dma_ram_rd_cmd_addr = data_dma_ram_rd_cmd_addr; + assign port_dma_ram_rd_cmd_valid = data_dma_ram_rd_cmd_valid; + assign data_dma_ram_rd_cmd_ready = port_dma_ram_rd_cmd_ready; + assign data_dma_ram_rd_resp_data = port_dma_ram_rd_resp_data; + assign data_dma_ram_rd_resp_valid = port_dma_ram_rd_resp_valid; + assign port_dma_ram_rd_resp_ready = data_dma_ram_rd_resp_ready; + +end + +event_mux #( + .PORTS(2), + .QUEUE_INDEX_WIDTH(EVENT_QUEUE_INDEX_WIDTH), + .EVENT_TYPE_WIDTH(EVENT_TYPE_WIDTH), + .EVENT_SOURCE_WIDTH(EVENT_SOURCE_WIDTH), + .ARB_TYPE("ROUND_ROBIN"), + .LSB_PRIORITY("HIGH") +) +event_mux_inst ( + .clk(clk), + .rst(rst), + + /* + * Event output + */ + .m_axis_event_queue(axis_event_queue), + .m_axis_event_type(axis_event_type), + .m_axis_event_source(axis_event_source), + .m_axis_event_valid(axis_event_valid), + .m_axis_event_ready(axis_event_ready), + + /* + * Event input + */ + .s_axis_event_queue({rx_fifo_event, tx_fifo_event}), + .s_axis_event_type({rx_fifo_event_type, tx_fifo_event_type}), + .s_axis_event_source({rx_fifo_event_source, tx_fifo_event_source}), + .s_axis_event_valid({rx_fifo_event_valid, tx_fifo_event_valid}), + .s_axis_event_ready({rx_fifo_event_ready, tx_fifo_event_ready}) +); + +assign event_cpl_req_queue = axis_event_queue; +assign event_cpl_req_tag = 0; +assign event_cpl_req_data[15:0] = axis_event_type; +assign event_cpl_req_data[31:16] = axis_event_source; +assign event_cpl_req_data[255:32] = 0; +assign event_cpl_req_valid = axis_event_valid; +assign axis_event_ready = event_cpl_req_ready; + +axis_fifo #( + .DEPTH(16), + .DATA_WIDTH(EVENT_SOURCE_WIDTH+EVENT_TYPE_WIDTH+EVENT_QUEUE_INDEX_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +tx_event_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata({tx_event_source, tx_event_type, tx_event}), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_event_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata({tx_fifo_event_source, tx_fifo_event_type, tx_fifo_event}), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_event_valid), + .m_axis_tready(tx_fifo_event_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +axis_fifo #( + .DEPTH(16), + .DATA_WIDTH(EVENT_SOURCE_WIDTH+EVENT_TYPE_WIDTH+EVENT_QUEUE_INDEX_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +rx_event_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata({rx_event_source, rx_event_type, rx_event}), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_event_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata({rx_fifo_event_source, rx_fifo_event_type, rx_fifo_event}), + .m_axis_tkeep(), + .m_axis_tvalid(rx_fifo_event_valid), + .m_axis_tready(rx_fifo_event_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +generate + genvar n; + + for (n = 0; n < PORTS; n = n + 1) begin : port + + assign port_cpl_req_sel[n*2+1 +: 1] = 1'b0; + + port #( + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .DMA_LEN_WIDTH(DMA_LEN_WIDTH), + .DMA_TAG_WIDTH(DMA_TAG_WIDTH_INT), + .REQ_TAG_WIDTH(REQ_TAG_WIDTH), + .DESC_REQ_TAG_WIDTH(PORT_DESC_REQ_TAG_WIDTH), + .QUEUE_REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .QUEUE_OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .TX_QUEUE_INDEX_WIDTH(TX_QUEUE_INDEX_WIDTH), + .RX_QUEUE_INDEX_WIDTH(RX_QUEUE_INDEX_WIDTH), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .TX_CPL_QUEUE_INDEX_WIDTH(TX_CPL_QUEUE_INDEX_WIDTH), + .RX_CPL_QUEUE_INDEX_WIDTH(RX_CPL_QUEUE_INDEX_WIDTH), + .CPL_QUEUE_INDEX_WIDTH(CPL_QUEUE_INDEX_WIDTH), + .TX_DESC_TABLE_SIZE(TX_DESC_TABLE_SIZE), + .TX_PKT_TABLE_SIZE(TX_PKT_TABLE_SIZE), + .RX_DESC_TABLE_SIZE(RX_DESC_TABLE_SIZE), + .RX_PKT_TABLE_SIZE(RX_PKT_TABLE_SIZE), + .DESC_TABLE_DMA_OP_COUNT_WIDTH(((2**LOG_BLOCK_SIZE_WIDTH)-1)+1), + .TX_SCHEDULER(TX_SCHEDULER), + .TX_SCHEDULER_OP_TABLE_SIZE(TX_SCHEDULER_OP_TABLE_SIZE), + .TX_SCHEDULER_PIPELINE(TX_SCHEDULER_PIPELINE), + .TDMA_INDEX_WIDTH(TDMA_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .PTP_TS_WIDTH(PTP_TS_WIDTH), + .TX_CHECKSUM_ENABLE(TX_CHECKSUM_ENABLE), + .RX_RSS_ENABLE(RX_RSS_ENABLE), + .RX_HASH_ENABLE(RX_HASH_ENABLE), + .RX_CHECKSUM_ENABLE(RX_CHECKSUM_ENABLE), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_PORT_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .RAM_PIPELINE(RAM_PIPELINE), + .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH), + .AXIS_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .MAX_TX_SIZE(MAX_TX_SIZE), + .MAX_RX_SIZE(MAX_RX_SIZE), + .DESC_SIZE(DESC_SIZE), + .CPL_SIZE(CPL_SIZE), + .AXIS_DESC_DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .AXIS_DESC_KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .TX_RAM_SIZE(TX_RAM_SIZE), + .RX_RAM_SIZE(RX_RAM_SIZE) + ) + port_inst ( + .clk(clk), + .rst(rst), + + /* + * Descriptor request output + */ + .m_axis_desc_req_sel(port_desc_req_sel[n*1 +: 1]), + .m_axis_desc_req_queue(port_desc_req_queue[n*QUEUE_INDEX_WIDTH +: QUEUE_INDEX_WIDTH]), + .m_axis_desc_req_tag(port_desc_req_tag[n*PORT_DESC_REQ_TAG_WIDTH +: PORT_DESC_REQ_TAG_WIDTH]), + .m_axis_desc_req_valid(port_desc_req_valid[n +: 1]), + .m_axis_desc_req_ready(port_desc_req_ready[n +: 1]), + + /* + * Descriptor response input + */ + .s_axis_desc_req_status_queue(port_desc_req_status_queue[n*QUEUE_INDEX_WIDTH +: QUEUE_INDEX_WIDTH]), + .s_axis_desc_req_status_ptr(port_desc_req_status_ptr[n*QUEUE_PTR_WIDTH +: QUEUE_PTR_WIDTH]), + .s_axis_desc_req_status_cpl(port_desc_req_status_cpl[n*CPL_QUEUE_INDEX_WIDTH +: CPL_QUEUE_INDEX_WIDTH]), + .s_axis_desc_req_status_tag(port_desc_req_status_tag[n*PORT_DESC_REQ_TAG_WIDTH +: PORT_DESC_REQ_TAG_WIDTH]), + .s_axis_desc_req_status_empty(port_desc_req_status_empty[n +: 1]), + .s_axis_desc_req_status_error(port_desc_req_status_error[n +: 1]), + .s_axis_desc_req_status_valid(port_desc_req_status_valid[n +: 1]), + + /* + * Descriptor data input + */ + .s_axis_desc_tdata(port_axis_desc_tdata[n*AXIS_DESC_DATA_WIDTH +: AXIS_DESC_DATA_WIDTH]), + .s_axis_desc_tkeep(port_axis_desc_tkeep[n*AXIS_DESC_KEEP_WIDTH +: AXIS_DESC_KEEP_WIDTH]), + .s_axis_desc_tvalid(port_axis_desc_tvalid[n +: 1]), + .s_axis_desc_tready(port_axis_desc_tready[n +: 1]), + .s_axis_desc_tlast(port_axis_desc_tlast[n +: 1]), + .s_axis_desc_tid(port_axis_desc_tid[n*PORT_DESC_REQ_TAG_WIDTH +: PORT_DESC_REQ_TAG_WIDTH]), + .s_axis_desc_tuser(port_axis_desc_tuser[n +: 1]), + + /* + * Completion request output + */ + .m_axis_cpl_req_sel(port_cpl_req_sel[n*2 +: 1]), + .m_axis_cpl_req_queue(port_cpl_req_queue[n*QUEUE_INDEX_WIDTH +: QUEUE_INDEX_WIDTH]), + .m_axis_cpl_req_tag(port_cpl_req_tag[n*PORT_DESC_REQ_TAG_WIDTH +: PORT_DESC_REQ_TAG_WIDTH]), + .m_axis_cpl_req_data(port_cpl_req_data[n*CPL_SIZE*8 +: CPL_SIZE*8]), + .m_axis_cpl_req_valid(port_cpl_req_valid[n +: 1]), + .m_axis_cpl_req_ready(port_cpl_req_ready[n +: 1]), + + /* + * Completion response input + */ + .s_axis_cpl_req_status_tag(port_cpl_req_status_tag[n*PORT_DESC_REQ_TAG_WIDTH +: PORT_DESC_REQ_TAG_WIDTH]), + .s_axis_cpl_req_status_full(port_cpl_req_status_full[n +: 1]), + .s_axis_cpl_req_status_error(port_cpl_req_status_error[n +: 1]), + .s_axis_cpl_req_status_valid(port_cpl_req_status_valid[n +: 1]), + + /* + * TX doorbell input + */ + .s_axis_tx_doorbell_queue(tx_doorbell_queue), + .s_axis_tx_doorbell_valid(tx_doorbell_valid), + + /* + * DMA read descriptor output + */ + .m_axis_dma_read_desc_dma_addr(port_dma_read_desc_dma_addr[n*DMA_ADDR_WIDTH +: DMA_ADDR_WIDTH]), + .m_axis_dma_read_desc_ram_addr(port_dma_read_desc_ram_addr[n*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]), + .m_axis_dma_read_desc_len(port_dma_read_desc_len[n*DMA_LEN_WIDTH +: DMA_LEN_WIDTH]), + .m_axis_dma_read_desc_tag(port_dma_read_desc_tag[n*DMA_TAG_WIDTH_INT +: DMA_TAG_WIDTH_INT]), + .m_axis_dma_read_desc_valid(port_dma_read_desc_valid[n +: 1]), + .m_axis_dma_read_desc_ready(port_dma_read_desc_ready[n +: 1]), + + /* + * DMA read descriptor status input + */ + .s_axis_dma_read_desc_status_tag(port_dma_read_desc_status_tag[n*DMA_TAG_WIDTH_INT +: DMA_TAG_WIDTH_INT]), + .s_axis_dma_read_desc_status_valid(port_dma_read_desc_status_valid[n +: 1]), + + /* + * DMA write descriptor output + */ + .m_axis_dma_write_desc_dma_addr(port_dma_write_desc_dma_addr[n*DMA_ADDR_WIDTH +: DMA_ADDR_WIDTH]), + .m_axis_dma_write_desc_ram_addr(port_dma_write_desc_ram_addr[n*RAM_ADDR_WIDTH +: RAM_ADDR_WIDTH]), + .m_axis_dma_write_desc_len(port_dma_write_desc_len[n*DMA_LEN_WIDTH +: DMA_LEN_WIDTH]), + .m_axis_dma_write_desc_tag(port_dma_write_desc_tag[n*DMA_TAG_WIDTH_INT +: DMA_TAG_WIDTH_INT]), + .m_axis_dma_write_desc_valid(port_dma_write_desc_valid[n +: 1]), + .m_axis_dma_write_desc_ready(port_dma_write_desc_ready[n +: 1]), + + /* + * DMA write descriptor status input + */ + .s_axis_dma_write_desc_status_tag(port_dma_write_desc_status_tag[n*DMA_TAG_WIDTH_INT +: DMA_TAG_WIDTH_INT]), + .s_axis_dma_write_desc_status_valid(port_dma_write_desc_status_valid[n +: 1]), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_port_awaddr[n*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_awprot(axil_port_awprot[n*3 +: 3]), + .s_axil_awvalid(axil_port_awvalid[n +: 1]), + .s_axil_awready(axil_port_awready[n +: 1]), + .s_axil_wdata(axil_port_wdata[n*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_wstrb(axil_port_wstrb[n*AXIL_STRB_WIDTH +: AXIL_STRB_WIDTH]), + .s_axil_wvalid(axil_port_wvalid[n +: 1]), + .s_axil_wready(axil_port_wready[n +: 1]), + .s_axil_bresp(axil_port_bresp[n*2 +: 2]), + .s_axil_bvalid(axil_port_bvalid[n +: 1]), + .s_axil_bready(axil_port_bready[n +: 1]), + .s_axil_araddr(axil_port_araddr[n*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_arprot(axil_port_arprot[n*3 +: 3]), + .s_axil_arvalid(axil_port_arvalid[n +: 1]), + .s_axil_arready(axil_port_arready[n +: 1]), + .s_axil_rdata(axil_port_rdata[n*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_rresp(axil_port_rresp[n*2 +: 2]), + .s_axil_rvalid(axil_port_rvalid[n +: 1]), + .s_axil_rready(axil_port_rready[n +: 1]), + + /* + * RAM interface + */ + .dma_ram_wr_cmd_be(port_dma_ram_wr_cmd_be[SEG_COUNT*SEG_BE_WIDTH*n +: SEG_COUNT*SEG_BE_WIDTH]), + .dma_ram_wr_cmd_addr(port_dma_ram_wr_cmd_addr[SEG_COUNT*SEG_ADDR_WIDTH*n +: SEG_COUNT*SEG_ADDR_WIDTH]), + .dma_ram_wr_cmd_data(port_dma_ram_wr_cmd_data[SEG_COUNT*SEG_DATA_WIDTH*n +: SEG_COUNT*SEG_DATA_WIDTH]), + .dma_ram_wr_cmd_valid(port_dma_ram_wr_cmd_valid[SEG_COUNT*n +: SEG_COUNT]), + .dma_ram_wr_cmd_ready(port_dma_ram_wr_cmd_ready[SEG_COUNT*n +: SEG_COUNT]), + .dma_ram_rd_cmd_addr(port_dma_ram_rd_cmd_addr[SEG_COUNT*SEG_ADDR_WIDTH*n +: SEG_COUNT*SEG_ADDR_WIDTH]), + .dma_ram_rd_cmd_valid(port_dma_ram_rd_cmd_valid[SEG_COUNT*n +: SEG_COUNT]), + .dma_ram_rd_cmd_ready(port_dma_ram_rd_cmd_ready[SEG_COUNT*n +: SEG_COUNT]), + .dma_ram_rd_resp_data(port_dma_ram_rd_resp_data[SEG_COUNT*SEG_DATA_WIDTH*n +: SEG_COUNT*SEG_DATA_WIDTH]), + .dma_ram_rd_resp_valid(port_dma_ram_rd_resp_valid[SEG_COUNT*n +: SEG_COUNT]), + .dma_ram_rd_resp_ready(port_dma_ram_rd_resp_ready[SEG_COUNT*n +: SEG_COUNT]), + + /* + * Transmit data output + */ + .tx_axis_tdata(tx_axis_tdata[n*AXIS_DATA_WIDTH +: AXIS_DATA_WIDTH]), + .tx_axis_tkeep(tx_axis_tkeep[n*AXIS_KEEP_WIDTH +: AXIS_KEEP_WIDTH]), + .tx_axis_tvalid(tx_axis_tvalid[n +: 1]), + .tx_axis_tready(tx_axis_tready[n +: 1]), + .tx_axis_tlast(tx_axis_tlast[n +: 1]), + .tx_axis_tuser(tx_axis_tuser[n +: 1]), + + /* + * Transmit timestamp input + */ + .s_axis_tx_ptp_ts_96(s_axis_tx_ptp_ts_96[n*PTP_TS_WIDTH +: PTP_TS_WIDTH]), + .s_axis_tx_ptp_ts_valid(s_axis_tx_ptp_ts_valid[n +: 1]), + .s_axis_tx_ptp_ts_ready(s_axis_tx_ptp_ts_ready[n +: 1]), + + /* + * Receive data input + */ + .rx_axis_tdata(rx_axis_tdata[n*AXIS_DATA_WIDTH +: AXIS_DATA_WIDTH]), + .rx_axis_tkeep(rx_axis_tkeep[n*AXIS_KEEP_WIDTH +: AXIS_KEEP_WIDTH]), + .rx_axis_tvalid(rx_axis_tvalid[n +: 1]), + .rx_axis_tready(rx_axis_tready[n +: 1]), + .rx_axis_tlast(rx_axis_tlast[n +: 1]), + .rx_axis_tuser(rx_axis_tuser[n +: 1]), + + /* + * Receive timestamp input + */ + .s_axis_rx_ptp_ts_96(s_axis_rx_ptp_ts_96[n*PTP_TS_WIDTH +: PTP_TS_WIDTH]), + .s_axis_rx_ptp_ts_valid(s_axis_rx_ptp_ts_valid[n +: 1]), + .s_axis_rx_ptp_ts_ready(s_axis_rx_ptp_ts_ready[n +: 1]), + + /* + * PTP clock + */ + .ptp_ts_96(ptp_ts_96), + .ptp_ts_step(ptp_ts_step) + ); + + end + +endgenerate + +endmodule diff --git a/corundum/rtl/port.v b/corundum/rtl/port.v new file mode 100644 index 0000000000000000000000000000000000000000..933e6f5b865a34c63c080377b01a596249a1ae6a --- /dev/null +++ b/corundum/rtl/port.v @@ -0,0 +1,2034 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * NIC Port + */ +module port # +( + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // DMA length field width + parameter DMA_LEN_WIDTH = 16, + // DMA tag field width + parameter DMA_TAG_WIDTH = 8, + // Request tag field width + parameter REQ_TAG_WIDTH = 8, + // Descriptor request tag field width + parameter DESC_REQ_TAG_WIDTH = 8, + // Queue request tag field width + parameter QUEUE_REQ_TAG_WIDTH = 8, + // Queue operation tag field width + parameter QUEUE_OP_TAG_WIDTH = 8, + // Transmit queue index width + parameter TX_QUEUE_INDEX_WIDTH = 8, + // Receive queue index width + parameter RX_QUEUE_INDEX_WIDTH = 8, + // Max queue index width + parameter QUEUE_INDEX_WIDTH = TX_QUEUE_INDEX_WIDTH > RX_QUEUE_INDEX_WIDTH ? TX_QUEUE_INDEX_WIDTH : RX_QUEUE_INDEX_WIDTH, + // Transmit completion queue index width + parameter TX_CPL_QUEUE_INDEX_WIDTH = 8, + // Receive completion queue index width + parameter RX_CPL_QUEUE_INDEX_WIDTH = 8, + // Max completion queue index width + parameter CPL_QUEUE_INDEX_WIDTH = TX_CPL_QUEUE_INDEX_WIDTH > RX_CPL_QUEUE_INDEX_WIDTH ? TX_CPL_QUEUE_INDEX_WIDTH : RX_CPL_QUEUE_INDEX_WIDTH, + // Transmit descriptor table size (number of in-flight operations) + parameter TX_DESC_TABLE_SIZE = 16, + // Transmit packet table size (number of in-progress packets) + parameter TX_PKT_TABLE_SIZE = 8, + // Receive descriptor table size (number of in-flight operations) + parameter RX_DESC_TABLE_SIZE = 16, + // Receive packet table size (number of in-progress packets) + parameter RX_PKT_TABLE_SIZE = 8, + // Width of descriptor table field for tracking outstanding DMA operations + parameter DESC_TABLE_DMA_OP_COUNT_WIDTH = 4, + // Transmit scheduler type + parameter TX_SCHEDULER = "RR", + // Scheduler operation table size + parameter TX_SCHEDULER_OP_TABLE_SIZE = 32, + // Scheduler pipeline setting + parameter TX_SCHEDULER_PIPELINE = 3, + // Scheduler TDMA index width + parameter TDMA_INDEX_WIDTH = 8, + // Queue element pointer width + parameter QUEUE_PTR_WIDTH = 16, + // Enable PTP timestamping + parameter PTP_TS_ENABLE = 1, + // PTP timestamp width + parameter PTP_TS_WIDTH = 96, + // Enable TX checksum offload + parameter TX_CHECKSUM_ENABLE = 1, + // Enable RX RSS + parameter RX_RSS_ENABLE = 1, + // Enable RX hashing + parameter RX_HASH_ENABLE = 1, + // Enable RX checksum offload + parameter RX_CHECKSUM_ENABLE = 1, + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = 16, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // DMA RAM segment count + parameter SEG_COUNT = 2, + // DMA RAM segment data width + parameter SEG_DATA_WIDTH = 64, + // DMA RAM segment address width + parameter SEG_ADDR_WIDTH = 8, + // DMA RAM segment byte enable width + parameter SEG_BE_WIDTH = SEG_DATA_WIDTH/8, + // DMA RAM address width + parameter RAM_ADDR_WIDTH = SEG_ADDR_WIDTH+$clog2(SEG_COUNT)+$clog2(SEG_BE_WIDTH), + // DMA RAM pipeline stages + parameter RAM_PIPELINE = 2, + // Width of AXI stream interfaces in bits + parameter AXIS_DATA_WIDTH = 256, + // AXI stream tkeep signal width (words per cycle) + parameter AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH/8, + // Max transmit packet size + parameter MAX_TX_SIZE = 2048, + // Max receive packet size + parameter MAX_RX_SIZE = 2048, + // Descriptor size (in bytes) + parameter DESC_SIZE = 16, + // Descriptor size (in bytes) + parameter CPL_SIZE = 32, + // Width of AXI stream descriptor interfaces in bits + parameter AXIS_DESC_DATA_WIDTH = DESC_SIZE*8, + // AXI stream descriptor tkeep signal width (words per cycle) + parameter AXIS_DESC_KEEP_WIDTH = AXIS_DESC_DATA_WIDTH/8, + // DMA TX RAM size + parameter TX_RAM_SIZE = TX_PKT_TABLE_SIZE*MAX_TX_SIZE, + // DMA RX RAM size + parameter RX_RAM_SIZE = RX_PKT_TABLE_SIZE*MAX_RX_SIZE +) +( + input wire clk, + input wire rst, + + /* + * Descriptor request output + */ + output wire [0:0] m_axis_desc_req_sel, + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_desc_req_queue, + output wire [DESC_REQ_TAG_WIDTH-1:0] m_axis_desc_req_tag, + output wire m_axis_desc_req_valid, + input wire m_axis_desc_req_ready, + + /* + * Descriptor request status input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_desc_req_status_queue, + input wire [QUEUE_PTR_WIDTH-1:0] s_axis_desc_req_status_ptr, + input wire [CPL_QUEUE_INDEX_WIDTH-1:0] s_axis_desc_req_status_cpl, + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_desc_req_status_tag, + input wire s_axis_desc_req_status_empty, + input wire s_axis_desc_req_status_error, + input wire s_axis_desc_req_status_valid, + + /* + * Descriptor data input + */ + input wire [AXIS_DESC_DATA_WIDTH-1:0] s_axis_desc_tdata, + input wire [AXIS_DESC_KEEP_WIDTH-1:0] s_axis_desc_tkeep, + input wire s_axis_desc_tvalid, + output wire s_axis_desc_tready, + input wire s_axis_desc_tlast, + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_desc_tid, + input wire s_axis_desc_tuser, + + /* + * Completion request output + */ + output wire [0:0] m_axis_cpl_req_sel, + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_cpl_req_queue, + output wire [DESC_REQ_TAG_WIDTH-1:0] m_axis_cpl_req_tag, + output wire [CPL_SIZE*8-1:0] m_axis_cpl_req_data, + output wire m_axis_cpl_req_valid, + input wire m_axis_cpl_req_ready, + + /* + * Completion request status input + */ + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_cpl_req_status_tag, + input wire s_axis_cpl_req_status_full, + input wire s_axis_cpl_req_status_error, + input wire s_axis_cpl_req_status_valid, + + /* + * TX doorbell input + */ + input wire [TX_QUEUE_INDEX_WIDTH-1:0] s_axis_tx_doorbell_queue, + input wire s_axis_tx_doorbell_valid, + + /* + * DMA read descriptor output + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_dma_read_desc_dma_addr, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_dma_read_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_dma_read_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_dma_read_desc_tag, + output wire m_axis_dma_read_desc_valid, + input wire m_axis_dma_read_desc_ready, + + /* + * DMA read descriptor status input + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_dma_read_desc_status_tag, + input wire s_axis_dma_read_desc_status_valid, + + /* + * DMA write descriptor output + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_dma_write_desc_dma_addr, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_dma_write_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_dma_write_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_dma_write_desc_tag, + output wire m_axis_dma_write_desc_valid, + input wire m_axis_dma_write_desc_ready, + + /* + * DMA write descriptor status input + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_dma_write_desc_status_tag, + input wire s_axis_dma_write_desc_status_valid, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * RAM interface + */ + input wire [SEG_COUNT*SEG_BE_WIDTH-1:0] dma_ram_wr_cmd_be, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_wr_cmd_addr, + input wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_wr_cmd_data, + input wire [SEG_COUNT-1:0] dma_ram_wr_cmd_valid, + output wire [SEG_COUNT-1:0] dma_ram_wr_cmd_ready, + input wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_rd_cmd_addr, + input wire [SEG_COUNT-1:0] dma_ram_rd_cmd_valid, + output wire [SEG_COUNT-1:0] dma_ram_rd_cmd_ready, + output wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_rd_resp_data, + output wire [SEG_COUNT-1:0] dma_ram_rd_resp_valid, + input wire [SEG_COUNT-1:0] dma_ram_rd_resp_ready, + + /* + * Transmit data output + */ + output wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata, + output wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep, + output wire tx_axis_tvalid, + input wire tx_axis_tready, + output wire tx_axis_tlast, + output wire tx_axis_tuser, + + /* + * Transmit PTP timestamp input + */ + input wire [PTP_TS_WIDTH-1:0] s_axis_tx_ptp_ts_96, + input wire s_axis_tx_ptp_ts_valid, + output wire s_axis_tx_ptp_ts_ready, + + /* + * Receive data input + */ + input wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep, + input wire rx_axis_tvalid, + output wire rx_axis_tready, + input wire rx_axis_tlast, + input wire rx_axis_tuser, + + /* + * Receive PTP timestamp input + */ + input wire [PTP_TS_WIDTH-1:0] s_axis_rx_ptp_ts_96, + input wire s_axis_rx_ptp_ts_valid, + output wire s_axis_rx_ptp_ts_ready, + + /* + * PTP clock + */ + input wire [PTP_TS_WIDTH-1:0] ptp_ts_96, + input wire ptp_ts_step +); + +parameter DMA_CLIENT_TAG_WIDTH = $clog2(TX_DESC_TABLE_SIZE > RX_DESC_TABLE_SIZE ? TX_DESC_TABLE_SIZE : RX_DESC_TABLE_SIZE); +parameter DMA_CLIENT_LEN_WIDTH = DMA_LEN_WIDTH; + +parameter DESC_REQ_TAG_WIDTH_INT = DESC_REQ_TAG_WIDTH - $clog2(2); + +parameter SCHED_COUNT = (TX_SCHEDULER == "TDMA_RR") ? 2 : 1; +parameter AXIL_SCHED_ADDR_WIDTH = AXIL_ADDR_WIDTH-$clog2(SCHED_COUNT+1); + +// parameter sizing helpers +function [31:0] w_32(input [31:0] val); + w_32 = val; +endfunction + +// AXI lite connections +wire [AXIL_ADDR_WIDTH-1:0] axil_ctrl_awaddr; +wire [2:0] axil_ctrl_awprot; +wire axil_ctrl_awvalid; +wire axil_ctrl_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_ctrl_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_ctrl_wstrb; +wire axil_ctrl_wvalid; +wire axil_ctrl_wready; +wire [1:0] axil_ctrl_bresp; +wire axil_ctrl_bvalid; +wire axil_ctrl_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_ctrl_araddr; +wire [2:0] axil_ctrl_arprot; +wire axil_ctrl_arvalid; +wire axil_ctrl_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_ctrl_rdata; +wire [1:0] axil_ctrl_rresp; +wire axil_ctrl_rvalid; +wire axil_ctrl_rready; + +wire [SCHED_COUNT*AXIL_ADDR_WIDTH-1:0] axil_sched_awaddr; +wire [SCHED_COUNT*3-1:0] axil_sched_awprot; +wire [SCHED_COUNT-1:0] axil_sched_awvalid; +wire [SCHED_COUNT-1:0] axil_sched_awready; +wire [SCHED_COUNT*AXIL_DATA_WIDTH-1:0] axil_sched_wdata; +wire [SCHED_COUNT*AXIL_STRB_WIDTH-1:0] axil_sched_wstrb; +wire [SCHED_COUNT-1:0] axil_sched_wvalid; +wire [SCHED_COUNT-1:0] axil_sched_wready; +wire [SCHED_COUNT*2-1:0] axil_sched_bresp; +wire [SCHED_COUNT-1:0] axil_sched_bvalid; +wire [SCHED_COUNT-1:0] axil_sched_bready; +wire [SCHED_COUNT*AXIL_ADDR_WIDTH-1:0] axil_sched_araddr; +wire [SCHED_COUNT*3-1:0] axil_sched_arprot; +wire [SCHED_COUNT-1:0] axil_sched_arvalid; +wire [SCHED_COUNT-1:0] axil_sched_arready; +wire [SCHED_COUNT*AXIL_DATA_WIDTH-1:0] axil_sched_rdata; +wire [SCHED_COUNT*2-1:0] axil_sched_rresp; +wire [SCHED_COUNT-1:0] axil_sched_rvalid; +wire [SCHED_COUNT-1:0] axil_sched_rready; + +// Checksumming and RSS +wire [AXIS_DATA_WIDTH-1:0] rx_axis_tdata_int; +wire [AXIS_KEEP_WIDTH-1:0] rx_axis_tkeep_int; +wire rx_axis_tvalid_int; +wire rx_axis_tready_int; +wire rx_axis_tlast_int; +wire rx_axis_tuser_int; + +wire [AXIS_DATA_WIDTH-1:0] tx_axis_tdata_int; +wire [AXIS_KEEP_WIDTH-1:0] tx_axis_tkeep_int; +wire tx_axis_tvalid_int; +wire tx_axis_tready_int; +wire tx_axis_tlast_int; +wire tx_axis_tuser_int; + +// Descriptor and completion +wire [0:0] rx_desc_req_sel = 1'b1; +wire [QUEUE_INDEX_WIDTH-1:0] rx_desc_req_queue; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] rx_desc_req_tag; +wire rx_desc_req_valid; +wire rx_desc_req_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] rx_desc_req_status_queue; +wire [QUEUE_PTR_WIDTH-1:0] rx_desc_req_status_ptr; +wire [CPL_QUEUE_INDEX_WIDTH-1:0] rx_desc_req_status_cpl; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] rx_desc_req_status_tag; +wire rx_desc_req_status_empty; +wire rx_desc_req_status_error; +wire rx_desc_req_status_valid; + +wire [AXIS_DESC_DATA_WIDTH-1:0] rx_desc_tdata; +wire [AXIS_DESC_KEEP_WIDTH-1:0] rx_desc_tkeep; +wire rx_desc_tvalid; +wire rx_desc_tready; +wire rx_desc_tlast; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] rx_desc_tid; +wire rx_desc_tuser; + +wire [AXIS_DESC_DATA_WIDTH-1:0] rx_fifo_desc_tdata; +wire [AXIS_DESC_KEEP_WIDTH-1:0] rx_fifo_desc_tkeep; +wire rx_fifo_desc_tvalid; +wire rx_fifo_desc_tready; +wire rx_fifo_desc_tlast; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] rx_fifo_desc_tid; +wire rx_fifo_desc_tuser; + +wire [0:0] tx_desc_req_sel = 1'b0; +wire [QUEUE_INDEX_WIDTH-1:0] tx_desc_req_queue; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] tx_desc_req_tag; +wire tx_desc_req_valid; +wire tx_desc_req_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] tx_desc_req_status_queue; +wire [QUEUE_PTR_WIDTH-1:0] tx_desc_req_status_ptr; +wire [CPL_QUEUE_INDEX_WIDTH-1:0] tx_desc_req_status_cpl; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] tx_desc_req_status_tag; +wire tx_desc_req_status_empty; +wire tx_desc_req_status_error; +wire tx_desc_req_status_valid; + +wire [AXIS_DESC_DATA_WIDTH-1:0] tx_desc_tdata; +wire [AXIS_DESC_KEEP_WIDTH-1:0] tx_desc_tkeep; +wire tx_desc_tvalid; +wire tx_desc_tready; +wire tx_desc_tlast; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] tx_desc_tid; +wire tx_desc_tuser; + +wire [AXIS_DESC_DATA_WIDTH-1:0] tx_fifo_desc_tdata; +wire [AXIS_DESC_KEEP_WIDTH-1:0] tx_fifo_desc_tkeep; +wire tx_fifo_desc_tvalid; +wire tx_fifo_desc_tready; +wire tx_fifo_desc_tlast; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] tx_fifo_desc_tid; +wire tx_fifo_desc_tuser; + +wire [0:0] rx_cpl_req_sel = 1'b1; +wire [QUEUE_INDEX_WIDTH-1:0] rx_cpl_req_queue; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] rx_cpl_req_tag; +wire [CPL_SIZE*8-1:0] rx_cpl_req_data; +wire rx_cpl_req_valid; +wire rx_cpl_req_ready; + +wire [DESC_REQ_TAG_WIDTH_INT-1:0] rx_cpl_req_status_tag; +wire rx_cpl_req_status_full; +wire rx_cpl_req_status_error; +wire rx_cpl_req_status_valid; + +wire [0:0] tx_cpl_req_sel = 1'b0; +wire [QUEUE_INDEX_WIDTH-1:0] tx_cpl_req_queue; +wire [DESC_REQ_TAG_WIDTH_INT-1:0] tx_cpl_req_tag; +wire [CPL_SIZE*8-1:0] tx_cpl_req_data; +wire tx_cpl_req_valid; +wire tx_cpl_req_ready; + +wire [DESC_REQ_TAG_WIDTH_INT-1:0] tx_cpl_req_status_tag; +wire tx_cpl_req_status_full; +wire tx_cpl_req_status_error; +wire tx_cpl_req_status_valid; + +// Scheduler +wire [TX_QUEUE_INDEX_WIDTH-1:0] tx_sched_ctrl_queue; +wire tx_sched_ctrl_enable; +wire tx_sched_ctrl_valid; +wire tx_sched_ctrl_ready; + +// TX engine +wire [TX_QUEUE_INDEX_WIDTH-1:0] tx_req_queue; +wire [REQ_TAG_WIDTH-1:0] tx_req_tag; +wire tx_req_valid; +wire tx_req_ready; + +wire [DMA_CLIENT_LEN_WIDTH-1:0] tx_req_status_len; +wire [REQ_TAG_WIDTH-1:0] tx_req_status_tag; +wire tx_req_status_valid; + +// RX engine +wire [RX_QUEUE_INDEX_WIDTH-1:0] rx_req_queue; +wire [REQ_TAG_WIDTH-1:0] rx_req_tag; +wire rx_req_valid; +wire rx_req_ready; + +wire [REQ_TAG_WIDTH-1:0] rx_req_status_tag; +wire rx_req_status_valid; + +// Timestamps +wire [95:0] rx_ptp_ts_96; +wire rx_ptp_ts_valid; +wire rx_ptp_ts_ready; + +wire [95:0] tx_ptp_ts_96; +wire tx_ptp_ts_valid; +wire tx_ptp_ts_ready; + +// RX hashing +wire [31:0] rx_hash; +wire [3:0] rx_hash_type; +wire rx_hash_valid; + +wire [31:0] rx_fifo_hash; +wire [3:0] rx_fifo_hash_type; +wire rx_fifo_hash_valid; +wire rx_fifo_hash_ready; + +// Checksums +wire [15:0] rx_csum; +wire rx_csum_valid; + +wire [15:0] rx_fifo_csum; +wire rx_fifo_csum_valid; +wire rx_fifo_csum_ready; + +wire tx_csum_cmd_csum_enable; +wire [7:0] tx_csum_cmd_csum_start; +wire [7:0] tx_csum_cmd_csum_offset; +wire tx_csum_cmd_valid; +wire tx_csum_cmd_ready; + +wire tx_fifo_csum_cmd_csum_enable; +wire [7:0] tx_fifo_csum_cmd_csum_start; +wire [7:0] tx_fifo_csum_cmd_csum_offset; +wire tx_fifo_csum_cmd_valid; +wire tx_fifo_csum_cmd_ready; + +// Interface DMA control +wire [RAM_ADDR_WIDTH-1:0] dma_tx_desc_addr; +wire [DMA_CLIENT_LEN_WIDTH-1:0] dma_tx_desc_len; +wire [DMA_CLIENT_TAG_WIDTH-1:0] dma_tx_desc_tag; +wire dma_tx_desc_user; +wire dma_tx_desc_valid; +wire dma_tx_desc_ready; + +wire [DMA_CLIENT_TAG_WIDTH-1:0] dma_tx_desc_status_tag; +wire dma_tx_desc_status_valid; + +wire [RAM_ADDR_WIDTH-1:0] dma_rx_desc_addr; +wire [DMA_CLIENT_LEN_WIDTH-1:0] dma_rx_desc_len; +wire [DMA_CLIENT_TAG_WIDTH-1:0] dma_rx_desc_tag; +wire dma_rx_desc_valid; +wire dma_rx_desc_ready; + +wire [DMA_CLIENT_LEN_WIDTH-1:0] dma_rx_desc_status_len; +wire [DMA_CLIENT_TAG_WIDTH-1:0] dma_rx_desc_status_tag; +wire dma_rx_desc_status_user; +wire dma_rx_desc_status_valid; + +wire dma_enable = 1; + +// Port control registers +reg axil_ctrl_awready_reg = 1'b0; +reg axil_ctrl_wready_reg = 1'b0; +reg axil_ctrl_bvalid_reg = 1'b0; +reg axil_ctrl_arready_reg = 1'b0; +reg [AXIL_DATA_WIDTH-1:0] axil_ctrl_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}; +reg axil_ctrl_rvalid_reg = 1'b0; + +reg sched_enable_reg = 1'b0; + +reg [RX_QUEUE_INDEX_WIDTH-1:0] rss_mask_reg = 0; + +reg tdma_enable_reg = 1'b0; +wire tdma_locked; +wire tdma_error; + +reg [79:0] set_tdma_schedule_start_reg = 0; +reg set_tdma_schedule_start_valid_reg = 0; +reg [79:0] set_tdma_schedule_period_reg = 0; +reg set_tdma_schedule_period_valid_reg = 0; +reg [79:0] set_tdma_timeslot_period_reg = 0; +reg set_tdma_timeslot_period_valid_reg = 0; +reg [79:0] set_tdma_active_period_reg = 0; +reg set_tdma_active_period_valid_reg = 0; + +wire tdma_schedule_start; +wire [TDMA_INDEX_WIDTH-1:0] tdma_timeslot_index; +wire tdma_timeslot_start; +wire tdma_timeslot_end; +wire tdma_timeslot_active; + +assign axil_ctrl_awready = axil_ctrl_awready_reg; +assign axil_ctrl_wready = axil_ctrl_wready_reg; +assign axil_ctrl_bresp = 2'b00; +assign axil_ctrl_bvalid = axil_ctrl_bvalid_reg; +assign axil_ctrl_arready = axil_ctrl_arready_reg; +assign axil_ctrl_rdata = axil_ctrl_rdata_reg; +assign axil_ctrl_rresp = 2'b00; +assign axil_ctrl_rvalid = axil_ctrl_rvalid_reg; + +always @(posedge clk) begin + axil_ctrl_awready_reg <= 1'b0; + axil_ctrl_wready_reg <= 1'b0; + axil_ctrl_bvalid_reg <= axil_ctrl_bvalid_reg && !axil_ctrl_bready; + axil_ctrl_arready_reg <= 1'b0; + axil_ctrl_rvalid_reg <= axil_ctrl_rvalid_reg && !axil_ctrl_rready; + + set_tdma_schedule_start_valid_reg <= 1'b0; + set_tdma_schedule_period_valid_reg <= 1'b0; + set_tdma_timeslot_period_valid_reg <= 1'b0; + set_tdma_active_period_valid_reg <= 1'b0; + + if (axil_ctrl_awvalid && axil_ctrl_wvalid && !axil_ctrl_bvalid) begin + // write operation + axil_ctrl_awready_reg <= 1'b1; + axil_ctrl_wready_reg <= 1'b1; + axil_ctrl_bvalid_reg <= 1'b1; + + case ({{(16 - AXIL_ADDR_WIDTH){1'b0}}, axil_ctrl_awaddr[AXIL_ADDR_WIDTH - 1:2], 2'b00}) + 16'h0040: begin + // Scheduler enable + if (axil_ctrl_wstrb[0]) begin + sched_enable_reg <= axil_ctrl_wdata[0]; + end + end + 16'h0080: rss_mask_reg <= axil_ctrl_wdata; // RSS mask + 16'h0100: begin + // TDMA control + if (axil_ctrl_wstrb[0]) begin + tdma_enable_reg <= axil_ctrl_wdata[0]; + end + end + 16'h0114: set_tdma_schedule_start_reg[29:0] <= axil_ctrl_wdata; // TDMA schedule start ns + 16'h0118: set_tdma_schedule_start_reg[63:32] <= axil_ctrl_wdata; // TDMA schedule start sec l + 16'h011C: begin + // TDMA schedule start sec h + set_tdma_schedule_start_reg[79:64] <= axil_ctrl_wdata; + set_tdma_schedule_start_valid_reg <= 1'b1; + end + 16'h0124: set_tdma_schedule_period_reg[29:0] <= axil_ctrl_wdata; // TDMA schedule period ns + 16'h0128: set_tdma_schedule_period_reg[63:32] <= axil_ctrl_wdata; // TDMA schedule period sec l + 16'h012C: begin + // TDMA schedule period sec h + set_tdma_schedule_period_reg[79:64] <= axil_ctrl_wdata; + set_tdma_schedule_period_valid_reg <= 1'b1; + end + 16'h0134: set_tdma_timeslot_period_reg[29:0] <= axil_ctrl_wdata; // TDMA timeslot period ns + 16'h0138: set_tdma_timeslot_period_reg[63:32] <= axil_ctrl_wdata; // TDMA timeslot period sec l + 16'h013C: begin + // TDMA timeslot period sec h + set_tdma_timeslot_period_reg[79:64] <= axil_ctrl_wdata; + set_tdma_timeslot_period_valid_reg <= 1'b1; + end + 16'h0144: set_tdma_active_period_reg[29:0] <= axil_ctrl_wdata; // TDMA active period ns + 16'h0148: set_tdma_active_period_reg[63:32] <= axil_ctrl_wdata; // TDMA active period sec l + 16'h014C: begin + // TDMA active period sec h + set_tdma_active_period_reg[79:64] <= axil_ctrl_wdata; + set_tdma_active_period_valid_reg <= 1'b1; + end + endcase + end + + if (axil_ctrl_arvalid && !axil_ctrl_rvalid) begin + // read operation + axil_ctrl_arready_reg <= 1'b1; + axil_ctrl_rvalid_reg <= 1'b1; + axil_ctrl_rdata_reg <= {AXIL_DATA_WIDTH{1'b0}}; + + case ({{(16 - AXIL_ADDR_WIDTH){1'b0}}, axil_ctrl_araddr[AXIL_ADDR_WIDTH - 1:2], 2'b00}) + 16'h0000: axil_ctrl_rdata_reg <= 32'd0; // port_id + 16'h0004: begin + // port_features + axil_ctrl_rdata_reg[0] <= RX_RSS_ENABLE && RX_HASH_ENABLE; + axil_ctrl_rdata_reg[4] <= PTP_TS_ENABLE; + axil_ctrl_rdata_reg[8] <= TX_CHECKSUM_ENABLE; + axil_ctrl_rdata_reg[9] <= RX_CHECKSUM_ENABLE; + axil_ctrl_rdata_reg[10] <= RX_HASH_ENABLE; + end + 16'h0008: axil_ctrl_rdata_reg <= MAX_TX_SIZE; // port_mtu + 16'h0010: axil_ctrl_rdata_reg <= SCHED_COUNT; // scheduler_count + 16'h0014: axil_ctrl_rdata_reg <= 2**AXIL_SCHED_ADDR_WIDTH; // scheduler_offset + 16'h0018: axil_ctrl_rdata_reg <= 2**AXIL_SCHED_ADDR_WIDTH; // scheduler_stride + 16'h001C: axil_ctrl_rdata_reg <= 32'd0; // scheduler_type + 16'h0040: begin + // Scheduler enable + axil_ctrl_rdata_reg[0] <= sched_enable_reg; + end + 16'h0080: axil_ctrl_rdata_reg <= rss_mask_reg; // RSS mask + 16'h0100: begin + // TDMA control + axil_ctrl_rdata_reg[0] <= tdma_enable_reg; + end + 16'h0104: begin + // TDMA status + axil_ctrl_rdata_reg[0] <= tdma_locked; + axil_ctrl_rdata_reg[1] <= tdma_error; + end + 16'h0108: axil_ctrl_rdata_reg <= 2**TDMA_INDEX_WIDTH; // TDMA timeslot count + 16'h0114: axil_ctrl_rdata_reg <= set_tdma_schedule_start_reg[29:0]; // TDMA schedule start ns + 16'h0118: axil_ctrl_rdata_reg <= set_tdma_schedule_start_reg[63:32]; // TDMA schedule start sec l + 16'h011C: axil_ctrl_rdata_reg <= set_tdma_schedule_start_reg[79:64]; // TDMA schedule start sec h + 16'h0124: axil_ctrl_rdata_reg <= set_tdma_schedule_period_reg[29:0]; // TDMA schedule period ns + 16'h0128: axil_ctrl_rdata_reg <= set_tdma_schedule_period_reg[63:32]; // TDMA schedule period sec l + 16'h012C: axil_ctrl_rdata_reg <= set_tdma_schedule_period_reg[79:64]; // TDMA schedule period sec h + 16'h0134: axil_ctrl_rdata_reg <= set_tdma_timeslot_period_reg[29:0]; // TDMA timeslot period ns + 16'h0138: axil_ctrl_rdata_reg <= set_tdma_timeslot_period_reg[63:32]; // TDMA timeslot period sec l + 16'h013C: axil_ctrl_rdata_reg <= set_tdma_timeslot_period_reg[79:64]; // TDMA timeslot period sec h + 16'h0144: axil_ctrl_rdata_reg <= set_tdma_active_period_reg[29:0]; // TDMA active period ns + 16'h0148: axil_ctrl_rdata_reg <= set_tdma_active_period_reg[63:32]; // TDMA active period sec l + 16'h014C: axil_ctrl_rdata_reg <= set_tdma_active_period_reg[79:64]; // TDMA active period sec h + endcase + end + + if (rst) begin + axil_ctrl_awready_reg <= 1'b0; + axil_ctrl_wready_reg <= 1'b0; + axil_ctrl_bvalid_reg <= 1'b0; + axil_ctrl_arready_reg <= 1'b0; + axil_ctrl_rvalid_reg <= 1'b0; + + sched_enable_reg <= 1'b0; + rss_mask_reg <= 0; + tdma_enable_reg <= 1'b0; + end +end + +// AXI lite interconnect +parameter AXIL_S_COUNT = 1; +parameter AXIL_M_COUNT = SCHED_COUNT+1; + +axil_interconnect #( + .DATA_WIDTH(AXIL_DATA_WIDTH), + .ADDR_WIDTH(AXIL_ADDR_WIDTH), + .STRB_WIDTH(AXIL_STRB_WIDTH), + .S_COUNT(AXIL_S_COUNT), + .M_COUNT(AXIL_M_COUNT), + .M_ADDR_WIDTH({AXIL_M_COUNT{w_32(AXIL_SCHED_ADDR_WIDTH)}}), + .M_CONNECT_READ({AXIL_M_COUNT{{AXIL_S_COUNT{1'b1}}}}), + .M_CONNECT_WRITE({AXIL_M_COUNT{{AXIL_S_COUNT{1'b1}}}}) +) +axil_interconnect_inst ( + .clk(clk), + .rst(rst), + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + .m_axil_awaddr( {axil_sched_awaddr, axil_ctrl_awaddr}), + .m_axil_awprot( {axil_sched_awprot, axil_ctrl_awprot}), + .m_axil_awvalid({axil_sched_awvalid, axil_ctrl_awvalid}), + .m_axil_awready({axil_sched_awready, axil_ctrl_awready}), + .m_axil_wdata( {axil_sched_wdata, axil_ctrl_wdata}), + .m_axil_wstrb( {axil_sched_wstrb, axil_ctrl_wstrb}), + .m_axil_wvalid( {axil_sched_wvalid, axil_ctrl_wvalid}), + .m_axil_wready( {axil_sched_wready, axil_ctrl_wready}), + .m_axil_bresp( {axil_sched_bresp, axil_ctrl_bresp}), + .m_axil_bvalid( {axil_sched_bvalid, axil_ctrl_bvalid}), + .m_axil_bready( {axil_sched_bready, axil_ctrl_bready}), + .m_axil_araddr( {axil_sched_araddr, axil_ctrl_araddr}), + .m_axil_arprot( {axil_sched_arprot, axil_ctrl_arprot}), + .m_axil_arvalid({axil_sched_arvalid, axil_ctrl_arvalid}), + .m_axil_arready({axil_sched_arready, axil_ctrl_arready}), + .m_axil_rdata( {axil_sched_rdata, axil_ctrl_rdata}), + .m_axil_rresp( {axil_sched_rresp, axil_ctrl_rresp}), + .m_axil_rvalid( {axil_sched_rvalid, axil_ctrl_rvalid}), + .m_axil_rready( {axil_sched_rready, axil_ctrl_rready}) +); + +desc_op_mux #( + .PORTS(2), + .SELECT_WIDTH(1), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .CPL_QUEUE_INDEX_WIDTH(CPL_QUEUE_INDEX_WIDTH), + .S_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH_INT), + .M_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH), + .AXIS_DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .AXIS_KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .ARB_TYPE("ROUND_ROBIN"), + .LSB_PRIORITY("HIGH") +) +desc_op_mux_inst ( + .clk(clk), + .rst(rst), + + /* + * Descriptor request output + */ + .m_axis_req_sel(m_axis_desc_req_sel), + .m_axis_req_queue(m_axis_desc_req_queue), + .m_axis_req_tag(m_axis_desc_req_tag), + .m_axis_req_valid(m_axis_desc_req_valid), + .m_axis_req_ready(m_axis_desc_req_ready), + + /* + * Descriptor request status input + */ + .s_axis_req_status_queue(s_axis_desc_req_status_queue), + .s_axis_req_status_ptr(s_axis_desc_req_status_ptr), + .s_axis_req_status_cpl(s_axis_desc_req_status_cpl), + .s_axis_req_status_tag(s_axis_desc_req_status_tag), + .s_axis_req_status_empty(s_axis_desc_req_status_empty), + .s_axis_req_status_error(s_axis_desc_req_status_error), + .s_axis_req_status_valid(s_axis_desc_req_status_valid), + + /* + * Descriptor data input + */ + .s_axis_desc_tdata(s_axis_desc_tdata), + .s_axis_desc_tkeep(s_axis_desc_tkeep), + .s_axis_desc_tvalid(s_axis_desc_tvalid), + .s_axis_desc_tready(s_axis_desc_tready), + .s_axis_desc_tlast(s_axis_desc_tlast), + .s_axis_desc_tid(s_axis_desc_tid), + .s_axis_desc_tuser(s_axis_desc_tuser), + + /* + * Descriptor request input + */ + .s_axis_req_sel({rx_desc_req_sel, tx_desc_req_sel}), + .s_axis_req_queue({rx_desc_req_queue, tx_desc_req_queue}), + .s_axis_req_tag({rx_desc_req_tag, tx_desc_req_tag}), + .s_axis_req_valid({rx_desc_req_valid, tx_desc_req_valid}), + .s_axis_req_ready({rx_desc_req_ready, tx_desc_req_ready}), + + /* + * Descriptor response output + */ + .m_axis_req_status_queue({rx_desc_req_status_queue, tx_desc_req_status_queue}), + .m_axis_req_status_ptr({rx_desc_req_status_ptr, tx_desc_req_status_ptr}), + .m_axis_req_status_cpl({rx_desc_req_status_cpl, tx_desc_req_status_cpl}), + .m_axis_req_status_tag({rx_desc_req_status_tag, tx_desc_req_status_tag}), + .m_axis_req_status_empty({rx_desc_req_status_empty, tx_desc_req_status_empty}), + .m_axis_req_status_error({rx_desc_req_status_error, tx_desc_req_status_error}), + .m_axis_req_status_valid({rx_desc_req_status_valid, tx_desc_req_status_valid}), + + /* + * Descriptor data output + */ + .m_axis_desc_tdata({rx_desc_tdata, tx_desc_tdata}), + .m_axis_desc_tkeep({rx_desc_tkeep, tx_desc_tkeep}), + .m_axis_desc_tvalid({rx_desc_tvalid, tx_desc_tvalid}), + .m_axis_desc_tready({rx_desc_tready, tx_desc_tready}), + .m_axis_desc_tlast({rx_desc_tlast, tx_desc_tlast}), + .m_axis_desc_tid({rx_desc_tid, tx_desc_tid}), + .m_axis_desc_tuser({rx_desc_tuser, tx_desc_tuser}) +); + +cpl_op_mux #( + .PORTS(2), + .SELECT_WIDTH(1), + .QUEUE_INDEX_WIDTH(QUEUE_INDEX_WIDTH), + .S_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH_INT), + .M_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH), + .CPL_SIZE(CPL_SIZE), + .ARB_TYPE("ROUND_ROBIN"), + .LSB_PRIORITY("HIGH") +) +cpl_op_mux_inst ( + .clk(clk), + .rst(rst), + + /* + * Completion request output + */ + .m_axis_req_sel(m_axis_cpl_req_sel), + .m_axis_req_queue(m_axis_cpl_req_queue), + .m_axis_req_tag(m_axis_cpl_req_tag), + .m_axis_req_data(m_axis_cpl_req_data), + .m_axis_req_valid(m_axis_cpl_req_valid), + .m_axis_req_ready(m_axis_cpl_req_ready), + + /* + * Completion request status input + */ + .s_axis_req_status_tag(s_axis_cpl_req_status_tag), + .s_axis_req_status_full(s_axis_cpl_req_status_full), + .s_axis_req_status_error(s_axis_cpl_req_status_error), + .s_axis_req_status_valid(s_axis_cpl_req_status_valid), + + /* + * Completion request input + */ + .s_axis_req_sel({rx_cpl_req_sel, tx_cpl_req_sel}), + .s_axis_req_queue({rx_cpl_req_queue, tx_cpl_req_queue}), + .s_axis_req_tag({rx_cpl_req_tag, tx_cpl_req_tag}), + .s_axis_req_data({rx_cpl_req_data, tx_cpl_req_data}), + .s_axis_req_valid({rx_cpl_req_valid, tx_cpl_req_valid}), + .s_axis_req_ready({rx_cpl_req_ready, tx_cpl_req_ready}), + + /* + * Completion response output + */ + .m_axis_req_status_tag({rx_cpl_req_status_tag, tx_cpl_req_status_tag}), + .m_axis_req_status_full({rx_cpl_req_status_full, tx_cpl_req_status_full}), + .m_axis_req_status_error({rx_cpl_req_status_error, tx_cpl_req_status_error}), + .m_axis_req_status_valid({rx_cpl_req_status_valid, tx_cpl_req_status_valid}) +); + +generate + +if (TX_SCHEDULER == "RR" || TX_SCHEDULER == "TDMA_RR") begin + + tx_scheduler_rr #( + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_SCHED_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH), + .DMA_CLIENT_LEN_WIDTH(DMA_CLIENT_LEN_WIDTH), + .REQ_TAG_WIDTH(REQ_TAG_WIDTH), + .OP_TABLE_SIZE(TX_SCHEDULER_OP_TABLE_SIZE), + .QUEUE_INDEX_WIDTH(TX_QUEUE_INDEX_WIDTH), + .PIPELINE(TX_SCHEDULER_PIPELINE), + .SCHED_CTRL_ENABLE(TX_SCHEDULER == "TDMA_RR") + ) + tx_scheduler_inst ( + .clk(clk), + .rst(rst), + + /* + * Transmit request output (queue index) + */ + .m_axis_tx_req_queue(tx_req_queue), + .m_axis_tx_req_tag(tx_req_tag), + .m_axis_tx_req_valid(tx_req_valid), + .m_axis_tx_req_ready(tx_req_ready), + + /* + * Transmit request status input + */ + .s_axis_tx_req_status_len(tx_req_status_len), + .s_axis_tx_req_status_tag(tx_req_status_tag), + .s_axis_tx_req_status_valid(tx_req_status_valid), + + /* + * Doorbell input + */ + .s_axis_doorbell_queue(s_axis_tx_doorbell_queue), + .s_axis_doorbell_valid(s_axis_tx_doorbell_valid), + + /* + * Scheduler control input + */ + .s_axis_sched_ctrl_queue(tx_sched_ctrl_queue), + .s_axis_sched_ctrl_enable(tx_sched_ctrl_enable), + .s_axis_sched_ctrl_valid(tx_sched_ctrl_valid), + .s_axis_sched_ctrl_ready(tx_sched_ctrl_ready), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_sched_awaddr[0*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_awprot(axil_sched_awprot[0*3 +: 3]), + .s_axil_awvalid(axil_sched_awvalid[0*1 +: 1]), + .s_axil_awready(axil_sched_awready[0*1 +: 1]), + .s_axil_wdata(axil_sched_wdata[0*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_wstrb(axil_sched_wstrb[0*AXIL_STRB_WIDTH +: AXIL_STRB_WIDTH]), + .s_axil_wvalid(axil_sched_wvalid[0*1 +: 1]), + .s_axil_wready(axil_sched_wready[0*1 +: 1]), + .s_axil_bresp(axil_sched_bresp[0*2 +: 2]), + .s_axil_bvalid(axil_sched_bvalid[0*1 +: 1]), + .s_axil_bready(axil_sched_bready[0*1 +: 1]), + .s_axil_araddr(axil_sched_araddr[0*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_arprot(axil_sched_arprot[0*3 +: 3]), + .s_axil_arvalid(axil_sched_arvalid[0*1 +: 1]), + .s_axil_arready(axil_sched_arready[0*1 +: 1]), + .s_axil_rdata(axil_sched_rdata[0*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_rresp(axil_sched_rresp[0*2 +: 2]), + .s_axil_rvalid(axil_sched_rvalid[0*1 +: 1]), + .s_axil_rready(axil_sched_rready[0*1 +: 1]), + + /* + * Control + */ + .enable(sched_enable_reg), + .active() + ); + +end + +if (TX_SCHEDULER == "TDMA_RR") begin + + tdma_scheduler #( + .INDEX_WIDTH(TDMA_INDEX_WIDTH), + .SCHEDULE_START_S(48'h0), + .SCHEDULE_START_NS(30'h0), + .SCHEDULE_PERIOD_S(48'd0), + .SCHEDULE_PERIOD_NS(30'd1000000), + .TIMESLOT_PERIOD_S(48'd0), + .TIMESLOT_PERIOD_NS(30'd100000), + .ACTIVE_PERIOD_S(48'd0), + .ACTIVE_PERIOD_NS(30'd100000) + ) + tdma_scheduler_inst ( + .clk(clk), + .rst(rst), + .input_ts_96(ptp_ts_96), + .input_ts_step(ptp_ts_step), + .enable(tdma_enable_reg), + .input_schedule_start(set_tdma_schedule_start_reg), + .input_schedule_start_valid(set_tdma_schedule_start_valid_reg), + .input_schedule_period(set_tdma_schedule_period_reg), + .input_schedule_period_valid(set_tdma_schedule_period_valid_reg), + .input_timeslot_period(set_tdma_timeslot_period_reg), + .input_timeslot_period_valid(set_tdma_timeslot_period_valid_reg), + .input_active_period(set_tdma_active_period_reg), + .input_active_period_valid(set_tdma_active_period_valid_reg), + .locked(tdma_locked), + .error(tdma_error), + .schedule_start(tdma_schedule_start), + .timeslot_index(tdma_timeslot_index), + .timeslot_start(tdma_timeslot_start), + .timeslot_end(tdma_timeslot_end), + .timeslot_active(tdma_timeslot_active) + ); + + tx_scheduler_ctrl_tdma #( + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(AXIL_SCHED_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH), + .TDMA_INDEX_WIDTH(TDMA_INDEX_WIDTH), + .QUEUE_INDEX_WIDTH(TX_QUEUE_INDEX_WIDTH), + .PIPELINE(2) + ) + tx_scheduler_ctrl_tdma_inst ( + .clk(clk), + .rst(rst), + + /* + * Scheduler control output + */ + .m_axis_sched_ctrl_queue(tx_sched_ctrl_queue), + .m_axis_sched_ctrl_enable(tx_sched_ctrl_enable), + .m_axis_sched_ctrl_valid(tx_sched_ctrl_valid), + .m_axis_sched_ctrl_ready(tx_sched_ctrl_ready), + + /* + * AXI-Lite slave interface + */ + .s_axil_awaddr(axil_sched_awaddr[1*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_awprot(axil_sched_awprot[1*3 +: 3]), + .s_axil_awvalid(axil_sched_awvalid[1*1 +: 1]), + .s_axil_awready(axil_sched_awready[1*1 +: 1]), + .s_axil_wdata(axil_sched_wdata[1*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_wstrb(axil_sched_wstrb[1*AXIL_STRB_WIDTH +: AXIL_STRB_WIDTH]), + .s_axil_wvalid(axil_sched_wvalid[1*1 +: 1]), + .s_axil_wready(axil_sched_wready[1*1 +: 1]), + .s_axil_bresp(axil_sched_bresp[1*2 +: 2]), + .s_axil_bvalid(axil_sched_bvalid[1*1 +: 1]), + .s_axil_bready(axil_sched_bready[1*1 +: 1]), + .s_axil_araddr(axil_sched_araddr[1*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_arprot(axil_sched_arprot[1*3 +: 3]), + .s_axil_arvalid(axil_sched_arvalid[1*1 +: 1]), + .s_axil_arready(axil_sched_arready[1*1 +: 1]), + .s_axil_rdata(axil_sched_rdata[1*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_rresp(axil_sched_rresp[1*2 +: 2]), + .s_axil_rvalid(axil_sched_rvalid[1*1 +: 1]), + .s_axil_rready(axil_sched_rready[1*1 +: 1]), + + /* + * TDMA schedule inputs + */ + .tdma_schedule_start(tdma_schedule_start), + .tdma_timeslot_index(tdma_timeslot_index), + .tdma_timeslot_start(tdma_timeslot_start), + .tdma_timeslot_end(tdma_timeslot_end), + .tdma_timeslot_active(tdma_timeslot_active) + ); + +end + +endgenerate + +axis_fifo #( + .DEPTH(32*AXIS_DESC_DATA_WIDTH/8), + .DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(1), + .ID_WIDTH(DESC_REQ_TAG_WIDTH_INT), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +tx_desc_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(tx_desc_tdata), + .s_axis_tkeep(tx_desc_tkeep), + .s_axis_tvalid(tx_desc_tvalid), + .s_axis_tready(tx_desc_tready), + .s_axis_tlast(tx_desc_tlast), + .s_axis_tid(tx_desc_tid), + .s_axis_tdest(0), + .s_axis_tuser(tx_desc_tuser), + + // AXI output + .m_axis_tdata(tx_fifo_desc_tdata), + .m_axis_tkeep(tx_fifo_desc_tkeep), + .m_axis_tvalid(tx_fifo_desc_tvalid), + .m_axis_tready(tx_fifo_desc_tready), + .m_axis_tlast(tx_fifo_desc_tlast), + .m_axis_tid(tx_fifo_desc_tid), + .m_axis_tdest(), + .m_axis_tuser(tx_fifo_desc_tuser), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +tx_engine #( + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .DMA_LEN_WIDTH(DMA_LEN_WIDTH), + .DMA_CLIENT_LEN_WIDTH(DMA_CLIENT_LEN_WIDTH), + .REQ_TAG_WIDTH(REQ_TAG_WIDTH), + .DESC_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH_INT), + .DMA_TAG_WIDTH(DMA_TAG_WIDTH), + .DMA_CLIENT_TAG_WIDTH(DMA_CLIENT_TAG_WIDTH), + .QUEUE_REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .QUEUE_OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(TX_QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .CPL_QUEUE_INDEX_WIDTH(TX_CPL_QUEUE_INDEX_WIDTH), + .DESC_TABLE_SIZE(TX_DESC_TABLE_SIZE), + .DESC_TABLE_DMA_OP_COUNT_WIDTH(DESC_TABLE_DMA_OP_COUNT_WIDTH), + .PKT_TABLE_SIZE(TX_PKT_TABLE_SIZE), + .MAX_TX_SIZE(MAX_TX_SIZE), + .DESC_SIZE(DESC_SIZE), + .CPL_SIZE(CPL_SIZE), + .AXIS_DESC_DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .AXIS_DESC_KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .TX_CHECKSUM_ENABLE(TX_CHECKSUM_ENABLE) +) +tx_engine_inst ( + .clk(clk), + .rst(rst), + + /* + * Transmit request input (queue index) + */ + .s_axis_tx_req_queue(tx_req_queue), + .s_axis_tx_req_tag(tx_req_tag), + .s_axis_tx_req_valid(tx_req_valid), + .s_axis_tx_req_ready(tx_req_ready), + + /* + * Transmit request status output + */ + .m_axis_tx_req_status_len(tx_req_status_len), + .m_axis_tx_req_status_tag(tx_req_status_tag), + .m_axis_tx_req_status_valid(tx_req_status_valid), + + /* + * Descriptor request output + */ + .m_axis_desc_req_queue(tx_desc_req_queue), + .m_axis_desc_req_tag(tx_desc_req_tag), + .m_axis_desc_req_valid(tx_desc_req_valid), + .m_axis_desc_req_ready(tx_desc_req_ready), + + /* + * Descriptor request status input + */ + .s_axis_desc_req_status_queue(tx_desc_req_status_queue), + .s_axis_desc_req_status_ptr(tx_desc_req_status_ptr), + .s_axis_desc_req_status_cpl(tx_desc_req_status_cpl), + .s_axis_desc_req_status_tag(tx_desc_req_status_tag), + .s_axis_desc_req_status_empty(tx_desc_req_status_empty), + .s_axis_desc_req_status_error(tx_desc_req_status_error), + .s_axis_desc_req_status_valid(tx_desc_req_status_valid), + + /* + * Descriptor data input + */ + .s_axis_desc_tdata(tx_fifo_desc_tdata), + .s_axis_desc_tkeep(tx_fifo_desc_tkeep), + .s_axis_desc_tvalid(tx_fifo_desc_tvalid), + .s_axis_desc_tready(tx_fifo_desc_tready), + .s_axis_desc_tlast(tx_fifo_desc_tlast), + .s_axis_desc_tid(tx_fifo_desc_tid), + .s_axis_desc_tuser(tx_fifo_desc_tuser), + + /* + * Completion request output + */ + .m_axis_cpl_req_queue(tx_cpl_req_queue), + .m_axis_cpl_req_tag(tx_cpl_req_tag), + .m_axis_cpl_req_data(tx_cpl_req_data), + .m_axis_cpl_req_valid(tx_cpl_req_valid), + .m_axis_cpl_req_ready(tx_cpl_req_ready), + + /* + * Completion request status input + */ + .s_axis_cpl_req_status_tag(tx_cpl_req_status_tag), + .s_axis_cpl_req_status_full(tx_cpl_req_status_full), + .s_axis_cpl_req_status_error(tx_cpl_req_status_error), + .s_axis_cpl_req_status_valid(tx_cpl_req_status_valid), + + /* + * DMA read descriptor output + */ + .m_axis_dma_read_desc_dma_addr(m_axis_dma_read_desc_dma_addr), + .m_axis_dma_read_desc_ram_addr(m_axis_dma_read_desc_ram_addr), + .m_axis_dma_read_desc_len(m_axis_dma_read_desc_len), + .m_axis_dma_read_desc_tag(m_axis_dma_read_desc_tag), + .m_axis_dma_read_desc_valid(m_axis_dma_read_desc_valid), + .m_axis_dma_read_desc_ready(m_axis_dma_read_desc_ready), + + /* + * DMA read descriptor status input + */ + .s_axis_dma_read_desc_status_tag(s_axis_dma_read_desc_status_tag), + .s_axis_dma_read_desc_status_valid(s_axis_dma_read_desc_status_valid), + + /* + * Transmit descriptor output + */ + .m_axis_tx_desc_addr(dma_tx_desc_addr), + .m_axis_tx_desc_len(dma_tx_desc_len), + .m_axis_tx_desc_tag(dma_tx_desc_tag), + .m_axis_tx_desc_user(dma_tx_desc_user), + .m_axis_tx_desc_valid(dma_tx_desc_valid), + .m_axis_tx_desc_ready(dma_tx_desc_ready), + + /* + * Transmit descriptor status input + */ + .s_axis_tx_desc_status_tag(dma_tx_desc_status_tag), + .s_axis_tx_desc_status_valid(dma_tx_desc_status_valid), + + /* + * Transmit checksum command output + */ + .m_axis_tx_csum_cmd_csum_enable(tx_csum_cmd_csum_enable), + .m_axis_tx_csum_cmd_csum_start(tx_csum_cmd_csum_start), + .m_axis_tx_csum_cmd_csum_offset(tx_csum_cmd_csum_offset), + .m_axis_tx_csum_cmd_valid(tx_csum_cmd_valid), + .m_axis_tx_csum_cmd_ready(tx_csum_cmd_ready), + + /* + * Transmit timestamp input + */ + .s_axis_tx_ptp_ts_96(s_axis_tx_ptp_ts_96), + .s_axis_tx_ptp_ts_valid(s_axis_tx_ptp_ts_valid), + .s_axis_tx_ptp_ts_ready(s_axis_tx_ptp_ts_ready), + + /* + * Configuration + */ + .enable(1'b1) +); + +axis_fifo #( + .DEPTH(32*AXIS_DESC_DATA_WIDTH/8), + .DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(1), + .ID_WIDTH(DESC_REQ_TAG_WIDTH_INT), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +rx_desc_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(rx_desc_tdata), + .s_axis_tkeep(rx_desc_tkeep), + .s_axis_tvalid(rx_desc_tvalid), + .s_axis_tready(rx_desc_tready), + .s_axis_tlast(rx_desc_tlast), + .s_axis_tid(rx_desc_tid), + .s_axis_tdest(0), + .s_axis_tuser(rx_desc_tuser), + + // AXI output + .m_axis_tdata(rx_fifo_desc_tdata), + .m_axis_tkeep(rx_fifo_desc_tkeep), + .m_axis_tvalid(rx_fifo_desc_tvalid), + .m_axis_tready(rx_fifo_desc_tready), + .m_axis_tlast(rx_fifo_desc_tlast), + .m_axis_tid(rx_fifo_desc_tid), + .m_axis_tdest(), + .m_axis_tuser(rx_fifo_desc_tuser), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +rx_engine #( + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .DMA_ADDR_WIDTH(DMA_ADDR_WIDTH), + .DMA_LEN_WIDTH(DMA_LEN_WIDTH), + .DMA_CLIENT_LEN_WIDTH(DMA_CLIENT_LEN_WIDTH), + .REQ_TAG_WIDTH(REQ_TAG_WIDTH), + .DESC_REQ_TAG_WIDTH(DESC_REQ_TAG_WIDTH_INT), + .DMA_TAG_WIDTH(DMA_TAG_WIDTH), + .DMA_CLIENT_TAG_WIDTH(DMA_CLIENT_TAG_WIDTH), + .QUEUE_REQ_TAG_WIDTH(QUEUE_REQ_TAG_WIDTH), + .QUEUE_OP_TAG_WIDTH(QUEUE_OP_TAG_WIDTH), + .QUEUE_INDEX_WIDTH(RX_QUEUE_INDEX_WIDTH), + .QUEUE_PTR_WIDTH(QUEUE_PTR_WIDTH), + .CPL_QUEUE_INDEX_WIDTH(RX_CPL_QUEUE_INDEX_WIDTH), + .DESC_TABLE_SIZE(RX_DESC_TABLE_SIZE), + .DESC_TABLE_DMA_OP_COUNT_WIDTH(DESC_TABLE_DMA_OP_COUNT_WIDTH), + .MAX_RX_SIZE(MAX_RX_SIZE), + .DESC_SIZE(DESC_SIZE), + .CPL_SIZE(CPL_SIZE), + .AXIS_DESC_DATA_WIDTH(AXIS_DESC_DATA_WIDTH), + .AXIS_DESC_KEEP_WIDTH(AXIS_DESC_KEEP_WIDTH), + .PKT_TABLE_SIZE(RX_PKT_TABLE_SIZE), + .PTP_TS_ENABLE(PTP_TS_ENABLE), + .RX_HASH_ENABLE(RX_HASH_ENABLE), + .RX_CHECKSUM_ENABLE(RX_CHECKSUM_ENABLE) +) +rx_engine_inst ( + .clk(clk), + .rst(rst), + + /* + * Receive request input (queue index) + */ + .s_axis_rx_req_queue(rx_req_queue), + .s_axis_rx_req_tag(rx_req_tag), + .s_axis_rx_req_valid(rx_req_valid), + .s_axis_rx_req_ready(rx_req_ready), + + /* + * Receive request status output + */ + .m_axis_rx_req_status_tag(rx_req_status_tag), + .m_axis_rx_req_status_valid(rx_req_status_valid), + + /* + * Descriptor request output + */ + .m_axis_desc_req_queue(rx_desc_req_queue), + .m_axis_desc_req_tag(rx_desc_req_tag), + .m_axis_desc_req_valid(rx_desc_req_valid), + .m_axis_desc_req_ready(rx_desc_req_ready), + + /* + * Descriptor request status input + */ + .s_axis_desc_req_status_queue(rx_desc_req_status_queue), + .s_axis_desc_req_status_ptr(rx_desc_req_status_ptr), + .s_axis_desc_req_status_cpl(rx_desc_req_status_cpl), + .s_axis_desc_req_status_tag(rx_desc_req_status_tag), + .s_axis_desc_req_status_empty(rx_desc_req_status_empty), + .s_axis_desc_req_status_error(rx_desc_req_status_error), + .s_axis_desc_req_status_valid(rx_desc_req_status_valid), + + /* + * Descriptor data input + */ + .s_axis_desc_tdata(rx_fifo_desc_tdata), + .s_axis_desc_tkeep(rx_fifo_desc_tkeep), + .s_axis_desc_tvalid(rx_fifo_desc_tvalid), + .s_axis_desc_tready(rx_fifo_desc_tready), + .s_axis_desc_tlast(rx_fifo_desc_tlast), + .s_axis_desc_tid(rx_fifo_desc_tid), + .s_axis_desc_tuser(rx_fifo_desc_tuser), + + /* + * Completion request output + */ + .m_axis_cpl_req_queue(rx_cpl_req_queue), + .m_axis_cpl_req_tag(rx_cpl_req_tag), + .m_axis_cpl_req_data(rx_cpl_req_data), + .m_axis_cpl_req_valid(rx_cpl_req_valid), + .m_axis_cpl_req_ready(rx_cpl_req_ready), + + /* + * Completion request status input + */ + .s_axis_cpl_req_status_tag(rx_cpl_req_status_tag), + .s_axis_cpl_req_status_full(rx_cpl_req_status_full), + .s_axis_cpl_req_status_error(rx_cpl_req_status_error), + .s_axis_cpl_req_status_valid(rx_cpl_req_status_valid), + + /* + * DMA write descriptor output + */ + .m_axis_dma_write_desc_dma_addr(m_axis_dma_write_desc_dma_addr), + .m_axis_dma_write_desc_ram_addr(m_axis_dma_write_desc_ram_addr), + .m_axis_dma_write_desc_len(m_axis_dma_write_desc_len), + .m_axis_dma_write_desc_tag(m_axis_dma_write_desc_tag), + .m_axis_dma_write_desc_valid(m_axis_dma_write_desc_valid), + .m_axis_dma_write_desc_ready(m_axis_dma_write_desc_ready), + + /* + * DMA write descriptor status input + */ + .s_axis_dma_write_desc_status_tag(s_axis_dma_write_desc_status_tag), + .s_axis_dma_write_desc_status_valid(s_axis_dma_write_desc_status_valid), + + /* + * Receive descriptor output + */ + .m_axis_rx_desc_addr(dma_rx_desc_addr), + .m_axis_rx_desc_len(dma_rx_desc_len), + .m_axis_rx_desc_tag(dma_rx_desc_tag), + .m_axis_rx_desc_valid(dma_rx_desc_valid), + .m_axis_rx_desc_ready(dma_rx_desc_ready), + + /* + * Receive descriptor status input + */ + .s_axis_rx_desc_status_len(dma_rx_desc_status_len), + .s_axis_rx_desc_status_tag(dma_rx_desc_status_tag), + .s_axis_rx_desc_status_user(dma_rx_desc_status_user), + .s_axis_rx_desc_status_valid(dma_rx_desc_status_valid), + + /* + * Receive timestamp input + */ + .s_axis_rx_ptp_ts_96(s_axis_rx_ptp_ts_96), + .s_axis_rx_ptp_ts_valid(s_axis_rx_ptp_ts_valid), + .s_axis_rx_ptp_ts_ready(s_axis_rx_ptp_ts_ready), + + /* + * Receive hash input + */ + .s_axis_rx_hash(rx_fifo_hash), + .s_axis_rx_hash_type(rx_fifo_hash_type), + .s_axis_rx_hash_valid(rx_fifo_hash_valid), + .s_axis_rx_hash_ready(rx_fifo_hash_ready), + + /* + * Receive checksum input + */ + .s_axis_rx_csum(rx_fifo_csum), + .s_axis_rx_csum_valid(rx_fifo_csum_valid), + .s_axis_rx_csum_ready(rx_fifo_csum_ready), + + /* + * Configuration + */ + .enable(1'b1) +); + +generate + +if (RX_HASH_ENABLE) begin + + rx_hash #( + .DATA_WIDTH(AXIS_DATA_WIDTH) + ) + rx_hash_inst ( + .clk(clk), + .rst(rst), + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .s_axis_tvalid(rx_axis_tvalid & rx_axis_tready), + .s_axis_tlast(rx_axis_tlast), + .hash_key(320'h6d5a56da255b0ec24167253d43a38fb0d0ca2bcbae7b30b477cb2da38030f20c6a42b73bbeac01fa), + .m_axis_hash(rx_hash), + .m_axis_hash_type(rx_hash_type), + .m_axis_hash_valid(rx_hash_valid) + ); + + axis_fifo #( + .DEPTH(32), + .DATA_WIDTH(32+4), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + rx_hash_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata({rx_hash_type, rx_hash}), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_hash_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata({rx_fifo_hash_type, rx_fifo_hash}), + .m_axis_tkeep(), + .m_axis_tvalid(rx_fifo_hash_valid), + .m_axis_tready(rx_fifo_hash_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() + ); + +end else begin + + assign rx_fifo_hash = 32'd0; + assign rx_fifo_type = 4'd0; + assign rx_fifo_hash_valid = 1'b0; + +end + +if (RX_RSS_ENABLE && RX_HASH_ENABLE) begin + + axis_fifo #( + .DEPTH(AXIS_KEEP_WIDTH*32), + .DATA_WIDTH(AXIS_DATA_WIDTH), + .KEEP_ENABLE(AXIS_KEEP_WIDTH > 1), + .KEEP_WIDTH(AXIS_KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .FRAME_FIFO(0) + ) + rx_hash_data_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(rx_axis_tdata), + .s_axis_tkeep(rx_axis_tkeep), + .s_axis_tvalid(rx_axis_tvalid), + .s_axis_tready(rx_axis_tready), + .s_axis_tlast(rx_axis_tlast), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(rx_axis_tuser), + + // AXI output + .m_axis_tdata(rx_axis_tdata_int), + .m_axis_tkeep(rx_axis_tkeep_int), + .m_axis_tvalid(rx_axis_tvalid_int), + .m_axis_tready(rx_axis_tready_int), + .m_axis_tlast(rx_axis_tlast_int), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(rx_axis_tuser_int), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() + ); + + // Generate RX requests (RSS) + assign rx_req_tag = 0; + + axis_fifo #( + .DEPTH(32), + .DATA_WIDTH(RX_QUEUE_INDEX_WIDTH), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + rx_req_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(rx_hash & rss_mask_reg), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_hash_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata(rx_req_queue), + .m_axis_tkeep(), + .m_axis_tvalid(rx_req_valid), + .m_axis_tready(rx_req_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() + ); + +end else begin + + assign rx_axis_tdata_int = rx_axis_tdata; + assign rx_axis_tkeep_int = rx_axis_tkeep; + assign rx_axis_tvalid_int = rx_axis_tvalid; + assign rx_axis_tready = rx_axis_tready_int; + assign rx_axis_tlast_int = rx_axis_tlast; + assign rx_axis_tuser_int = rx_axis_tuser; + + // Generate RX requests (no RSS) + reg rx_frame_reg = 1'b0; + reg rx_req_valid_reg = 1'b0; + + assign rx_req_queue = 0; + assign rx_req_tag = 0; + assign rx_req_valid = rx_axis_tvalid_int && !rx_frame_reg; + + always @(posedge clk) begin + if (rx_req_ready) begin + rx_req_valid_reg <= 1'b0; + end + + if (rx_axis_tready_int && rx_axis_tvalid_int) begin + if (!rx_frame_reg) begin + rx_req_valid_reg <= 1'b1; + end + rx_frame_reg <= !rx_axis_tlast_int; + end + + if (rst) begin + rx_frame_reg <= 1'b0; + rx_req_valid_reg <= 1'b0; + end + end + +end + +if (RX_CHECKSUM_ENABLE) begin + + rx_checksum #( + .DATA_WIDTH(AXIS_DATA_WIDTH) + ) + rx_checksum_inst ( + .clk(clk), + .rst(rst), + .s_axis_tdata(rx_axis_tdata_int), + .s_axis_tkeep(rx_axis_tkeep_int), + .s_axis_tvalid(rx_axis_tvalid_int & rx_axis_tready_int), + .s_axis_tlast(rx_axis_tlast_int), + .m_axis_csum(rx_csum), + .m_axis_csum_valid(rx_csum_valid) + ); + + axis_fifo #( + .DEPTH(32), + .DATA_WIDTH(16), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + rx_csum_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(rx_csum), + .s_axis_tkeep(0), + .s_axis_tvalid(rx_csum_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata(rx_fifo_csum), + .m_axis_tkeep(), + .m_axis_tvalid(rx_fifo_csum_valid), + .m_axis_tready(rx_fifo_csum_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() + ); + +end else begin + + assign rx_fifo_csum = 16'd0; + assign rx_fifo_csum_valid = 1'b0; + +end + +if (TX_CHECKSUM_ENABLE) begin + + axis_fifo #( + .DEPTH(32), + .DATA_WIDTH(1+8+8), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) + ) + tx_csum_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata({tx_csum_cmd_csum_enable, tx_csum_cmd_csum_start, tx_csum_cmd_csum_offset}), + .s_axis_tkeep(0), + .s_axis_tvalid(tx_csum_cmd_valid), + .s_axis_tready(tx_csum_cmd_ready), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata({tx_fifo_csum_cmd_csum_enable, tx_fifo_csum_cmd_csum_start, tx_fifo_csum_cmd_csum_offset}), + .m_axis_tkeep(), + .m_axis_tvalid(tx_fifo_csum_cmd_valid), + .m_axis_tready(tx_fifo_csum_cmd_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() + ); + + tx_checksum #( + .DATA_WIDTH(AXIS_DATA_WIDTH), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(1), + .USER_WIDTH(1), + .USE_INIT_VALUE(0), + .DATA_FIFO_DEPTH(MAX_TX_SIZE), + .CHECKSUM_FIFO_DEPTH(64) + ) + tx_checksum_inst ( + .clk(clk), + .rst(rst), + + /* + * AXI input + */ + .s_axis_tdata(tx_axis_tdata_int), + .s_axis_tkeep(tx_axis_tkeep_int), + .s_axis_tvalid(tx_axis_tvalid_int), + .s_axis_tready(tx_axis_tready_int), + .s_axis_tlast(tx_axis_tlast_int), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(tx_axis_tuser_int), + + /* + * AXI output + */ + .m_axis_tdata(tx_axis_tdata), + .m_axis_tkeep(tx_axis_tkeep), + .m_axis_tvalid(tx_axis_tvalid), + .m_axis_tready(tx_axis_tready), + .m_axis_tlast(tx_axis_tlast), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(tx_axis_tuser), + + /* + * Control + */ + .s_axis_cmd_csum_enable(tx_fifo_csum_cmd_csum_enable), + .s_axis_cmd_csum_start(tx_fifo_csum_cmd_csum_start), + .s_axis_cmd_csum_offset(tx_fifo_csum_cmd_csum_offset), + .s_axis_cmd_csum_init(16'd0), + .s_axis_cmd_valid(tx_fifo_csum_cmd_valid), + .s_axis_cmd_ready(tx_fifo_csum_cmd_ready) + ); + +end else begin + + assign tx_axis_tdata = tx_axis_tdata_int; + assign tx_axis_tkeep = tx_axis_tkeep_int; + assign tx_axis_tvalid = tx_axis_tvalid_int; + assign tx_axis_tready_int = tx_axis_tready; + assign tx_axis_tlast = tx_axis_tlast_int; + assign tx_axis_tuser = tx_axis_tuser_int; + + assign tx_csum_cmd_ready = 1'b1; + +end + +endgenerate + +wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_rd_cmd_addr_int; +wire [SEG_COUNT-1:0] dma_ram_rd_cmd_valid_int; +wire [SEG_COUNT-1:0] dma_ram_rd_cmd_ready_int; +wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_rd_resp_data_int; +wire [SEG_COUNT-1:0] dma_ram_rd_resp_valid_int; +wire [SEG_COUNT-1:0] dma_ram_rd_resp_ready_int; + +dma_psdpram #( + .SIZE(TX_RAM_SIZE), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .PIPELINE(RAM_PIPELINE) +) +dma_psdpram_tx_inst ( + /* + * Write port + */ + .clk_wr(clk), + .rst_wr(rst), + .wr_cmd_be(dma_ram_wr_cmd_be), + .wr_cmd_addr(dma_ram_wr_cmd_addr), + .wr_cmd_data(dma_ram_wr_cmd_data), + .wr_cmd_valid(dma_ram_wr_cmd_valid), + .wr_cmd_ready(dma_ram_wr_cmd_ready), + + /* + * Read port + */ + .clk_rd(clk), + .rst_rd(rst), + .rd_cmd_addr(dma_ram_rd_cmd_addr_int), + .rd_cmd_valid(dma_ram_rd_cmd_valid_int), + .rd_cmd_ready(dma_ram_rd_cmd_ready_int), + .rd_resp_data(dma_ram_rd_resp_data_int), + .rd_resp_valid(dma_ram_rd_resp_valid_int), + .rd_resp_ready(dma_ram_rd_resp_ready_int) +); + +dma_client_axis_source #( + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH), + .AXIS_KEEP_ENABLE(AXIS_KEEP_WIDTH > 1), + .AXIS_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .AXIS_LAST_ENABLE(1), + .AXIS_ID_ENABLE(0), + .AXIS_DEST_ENABLE(0), + .AXIS_USER_ENABLE(1), + .AXIS_USER_WIDTH(1), + .LEN_WIDTH(DMA_CLIENT_LEN_WIDTH), + .TAG_WIDTH(DMA_CLIENT_TAG_WIDTH) +) +dma_client_axis_source_inst ( + .clk(clk), + .rst(rst), + + /* + * DMA read descriptor input + */ + .s_axis_read_desc_ram_addr(dma_tx_desc_addr), + .s_axis_read_desc_len(dma_tx_desc_len), + .s_axis_read_desc_tag(dma_tx_desc_tag), + .s_axis_read_desc_id(0), + .s_axis_read_desc_dest(0), + .s_axis_read_desc_user(dma_tx_desc_user), + .s_axis_read_desc_valid(dma_tx_desc_valid), + .s_axis_read_desc_ready(dma_tx_desc_ready), + + /* + * DMA read descriptor status output + */ + .m_axis_read_desc_status_tag(dma_tx_desc_status_tag), + .m_axis_read_desc_status_valid(dma_tx_desc_status_valid), + + /* + * AXI stream read data output + */ + .m_axis_read_data_tdata(tx_axis_tdata_int), + .m_axis_read_data_tkeep(tx_axis_tkeep_int), + .m_axis_read_data_tvalid(tx_axis_tvalid_int), + .m_axis_read_data_tready(tx_axis_tready_int), + .m_axis_read_data_tlast(tx_axis_tlast_int), + .m_axis_read_data_tid(), + .m_axis_read_data_tdest(), + .m_axis_read_data_tuser(tx_axis_tuser_int), + + /* + * RAM interface + */ + .ram_rd_cmd_addr(dma_ram_rd_cmd_addr_int), + .ram_rd_cmd_valid(dma_ram_rd_cmd_valid_int), + .ram_rd_cmd_ready(dma_ram_rd_cmd_ready_int), + .ram_rd_resp_data(dma_ram_rd_resp_data_int), + .ram_rd_resp_valid(dma_ram_rd_resp_valid_int), + .ram_rd_resp_ready(dma_ram_rd_resp_ready_int), + + /* + * Configuration + */ + .enable(dma_enable) +); + +wire [SEG_COUNT*SEG_BE_WIDTH-1:0] dma_ram_wr_cmd_be_int; +wire [SEG_COUNT*SEG_ADDR_WIDTH-1:0] dma_ram_wr_cmd_addr_int; +wire [SEG_COUNT*SEG_DATA_WIDTH-1:0] dma_ram_wr_cmd_data_int; +wire [SEG_COUNT-1:0] dma_ram_wr_cmd_valid_int; +wire [SEG_COUNT-1:0] dma_ram_wr_cmd_ready_int; + +dma_psdpram #( + .SIZE(RX_RAM_SIZE), + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .PIPELINE(RAM_PIPELINE) +) +dma_psdpram_rx_inst ( + /* + * Write port + */ + .clk_wr(clk), + .rst_wr(rst), + .wr_cmd_be(dma_ram_wr_cmd_be_int), + .wr_cmd_addr(dma_ram_wr_cmd_addr_int), + .wr_cmd_data(dma_ram_wr_cmd_data_int), + .wr_cmd_valid(dma_ram_wr_cmd_valid_int), + .wr_cmd_ready(dma_ram_wr_cmd_ready_int), + + /* + * Read port + */ + .clk_rd(clk), + .rst_rd(rst), + .rd_cmd_addr(dma_ram_rd_cmd_addr), + .rd_cmd_valid(dma_ram_rd_cmd_valid), + .rd_cmd_ready(dma_ram_rd_cmd_ready), + .rd_resp_data(dma_ram_rd_resp_data), + .rd_resp_valid(dma_ram_rd_resp_valid), + .rd_resp_ready(dma_ram_rd_resp_ready) +); + +dma_client_axis_sink #( + .SEG_COUNT(SEG_COUNT), + .SEG_DATA_WIDTH(SEG_DATA_WIDTH), + .SEG_ADDR_WIDTH(SEG_ADDR_WIDTH), + .SEG_BE_WIDTH(SEG_BE_WIDTH), + .RAM_ADDR_WIDTH(RAM_ADDR_WIDTH), + .AXIS_DATA_WIDTH(AXIS_DATA_WIDTH), + .AXIS_KEEP_ENABLE(AXIS_KEEP_WIDTH > 1), + .AXIS_KEEP_WIDTH(AXIS_KEEP_WIDTH), + .AXIS_LAST_ENABLE(1), + .AXIS_ID_ENABLE(0), + .AXIS_DEST_ENABLE(0), + .AXIS_USER_ENABLE(1), + .AXIS_USER_WIDTH(1), + .LEN_WIDTH(DMA_CLIENT_LEN_WIDTH), + .TAG_WIDTH(DMA_CLIENT_TAG_WIDTH) +) +dma_client_axis_sink_inst ( + .clk(clk), + .rst(rst), + + /* + * DMA write descriptor input + */ + .s_axis_write_desc_ram_addr(dma_rx_desc_addr), + .s_axis_write_desc_len(dma_rx_desc_len), + .s_axis_write_desc_tag(dma_rx_desc_tag), + .s_axis_write_desc_valid(dma_rx_desc_valid), + .s_axis_write_desc_ready(dma_rx_desc_ready), + + /* + * DMA write descriptor status output + */ + .m_axis_write_desc_status_len(dma_rx_desc_status_len), + .m_axis_write_desc_status_tag(dma_rx_desc_status_tag), + .m_axis_write_desc_status_id(), + .m_axis_write_desc_status_dest(), + .m_axis_write_desc_status_user(dma_rx_desc_status_user), + .m_axis_write_desc_status_valid(dma_rx_desc_status_valid), + + /* + * AXI stream write data input + */ + .s_axis_write_data_tdata(rx_axis_tdata_int), + .s_axis_write_data_tkeep(rx_axis_tkeep_int), + .s_axis_write_data_tvalid(rx_axis_tvalid_int), + .s_axis_write_data_tready(rx_axis_tready_int), + .s_axis_write_data_tlast(rx_axis_tlast_int), + .s_axis_write_data_tid(0), + .s_axis_write_data_tdest(0), + .s_axis_write_data_tuser(rx_axis_tuser_int), + + /* + * RAM interface + */ + .ram_wr_cmd_be(dma_ram_wr_cmd_be_int), + .ram_wr_cmd_addr(dma_ram_wr_cmd_addr_int), + .ram_wr_cmd_data(dma_ram_wr_cmd_data_int), + .ram_wr_cmd_valid(dma_ram_wr_cmd_valid_int), + .ram_wr_cmd_ready(dma_ram_wr_cmd_ready_int), + + /* + * Configuration + */ + .enable(dma_enable), + .abort(1'b0) +); + +endmodule diff --git a/corundum/rtl/queue_manager.v b/corundum/rtl/queue_manager.v new file mode 100644 index 0000000000000000000000000000000000000000..54fc2a56e262ecae257abd37496e8f85296a5ade --- /dev/null +++ b/corundum/rtl/queue_manager.v @@ -0,0 +1,693 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Queue manager + */ +module queue_manager # +( + // Base address width + parameter ADDR_WIDTH = 64, + // Request tag field width + parameter REQ_TAG_WIDTH = 8, + // Number of outstanding operations + parameter OP_TABLE_SIZE = 16, + // Operation tag field width + parameter OP_TAG_WIDTH = 8, + // Queue index width (log2 of number of queues) + parameter QUEUE_INDEX_WIDTH = 8, + // Completion queue index width + parameter CPL_INDEX_WIDTH = 8, + // Queue element pointer width (log2 of number of elements) + parameter QUEUE_PTR_WIDTH = 16, + // Log queue size field width + parameter LOG_QUEUE_SIZE_WIDTH = $clog2(QUEUE_PTR_WIDTH), + // Queue element size + parameter DESC_SIZE = 16, + // Log desc block size field width + parameter LOG_BLOCK_SIZE_WIDTH = 2, + // Pipeline stages + parameter PIPELINE = 2, + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = 16, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * Dequeue request input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_dequeue_req_queue, + input wire [REQ_TAG_WIDTH-1:0] s_axis_dequeue_req_tag, + input wire s_axis_dequeue_req_valid, + output wire s_axis_dequeue_req_ready, + + /* + * Dequeue response output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_dequeue_resp_queue, + output wire [QUEUE_PTR_WIDTH-1:0] m_axis_dequeue_resp_ptr, + output wire [ADDR_WIDTH-1:0] m_axis_dequeue_resp_addr, + output wire [LOG_BLOCK_SIZE_WIDTH-1:0] m_axis_dequeue_resp_block_size, + output wire [CPL_INDEX_WIDTH-1:0] m_axis_dequeue_resp_cpl, + output wire [REQ_TAG_WIDTH-1:0] m_axis_dequeue_resp_tag, + output wire [OP_TAG_WIDTH-1:0] m_axis_dequeue_resp_op_tag, + output wire m_axis_dequeue_resp_empty, + output wire m_axis_dequeue_resp_error, + output wire m_axis_dequeue_resp_valid, + input wire m_axis_dequeue_resp_ready, + + /* + * Dequeue commit input + */ + input wire [OP_TAG_WIDTH-1:0] s_axis_dequeue_commit_op_tag, + input wire s_axis_dequeue_commit_valid, + output wire s_axis_dequeue_commit_ready, + + /* + * Doorbell output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_doorbell_queue, + output wire m_axis_doorbell_valid, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * Configuration + */ + input wire enable +); + +parameter QUEUE_COUNT = 2**QUEUE_INDEX_WIDTH; + +parameter CL_OP_TABLE_SIZE = $clog2(OP_TABLE_SIZE); + +parameter CL_DESC_SIZE = $clog2(DESC_SIZE); + +parameter QUEUE_RAM_BE_WIDTH = 16; +parameter QUEUE_RAM_WIDTH = QUEUE_RAM_BE_WIDTH*8; + +// bus width assertions +initial begin + if (OP_TAG_WIDTH < CL_OP_TABLE_SIZE) begin + $error("Error: OP_TAG_WIDTH insufficient for OP_TABLE_SIZE (instance %m)"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI lite interface width must be 32 (instance %m)"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: AXI lite interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXIL_ADDR_WIDTH < QUEUE_INDEX_WIDTH+5) begin + $error("Error: AXI lite address width too narrow (instance %m)"); + $finish; + end + + if (2**$clog2(DESC_SIZE) != DESC_SIZE) begin + $error("Error: Descriptor size must be even power of two (instance %m)"); + $finish; + end + + if (PIPELINE < 2) begin + $error("Error: PIPELINE must be at least 2 (instance %m)"); + $finish; + end +end + +reg op_axil_write_pipe_hazard; +reg op_axil_read_pipe_hazard; +reg op_req_pipe_hazard; +reg op_commit_pipe_hazard; +reg stage_active; + +reg [PIPELINE-1:0] op_axil_write_pipe_reg = {PIPELINE{1'b0}}, op_axil_write_pipe_next; +reg [PIPELINE-1:0] op_axil_read_pipe_reg = {PIPELINE{1'b0}}, op_axil_read_pipe_next; +reg [PIPELINE-1:0] op_req_pipe_reg = {PIPELINE{1'b0}}, op_req_pipe_next; +reg [PIPELINE-1:0] op_commit_pipe_reg = {PIPELINE{1'b0}}, op_commit_pipe_next; + +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_addr_pipeline_reg[PIPELINE-1:0], queue_ram_addr_pipeline_next[PIPELINE-1:0]; +reg [2:0] axil_reg_pipeline_reg[PIPELINE-1:0], axil_reg_pipeline_next[PIPELINE-1:0]; +reg [AXIL_DATA_WIDTH-1:0] write_data_pipeline_reg[PIPELINE-1:0], write_data_pipeline_next[PIPELINE-1:0]; +reg [AXIL_STRB_WIDTH-1:0] write_strobe_pipeline_reg[PIPELINE-1:0], write_strobe_pipeline_next[PIPELINE-1:0]; +reg [REQ_TAG_WIDTH-1:0] req_tag_pipeline_reg[PIPELINE-1:0], req_tag_pipeline_next[PIPELINE-1:0]; + +reg s_axis_dequeue_req_ready_reg = 1'b0, s_axis_dequeue_req_ready_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_dequeue_resp_queue_reg = 0, m_axis_dequeue_resp_queue_next; +reg [QUEUE_PTR_WIDTH-1:0] m_axis_dequeue_resp_ptr_reg = 0, m_axis_dequeue_resp_ptr_next; +reg [ADDR_WIDTH-1:0] m_axis_dequeue_resp_addr_reg = 0, m_axis_dequeue_resp_addr_next; +reg [LOG_BLOCK_SIZE_WIDTH-1:0] m_axis_dequeue_resp_block_size_reg = 0, m_axis_dequeue_resp_block_size_next; +reg [CPL_INDEX_WIDTH-1:0] m_axis_dequeue_resp_cpl_reg = 0, m_axis_dequeue_resp_cpl_next; +reg [REQ_TAG_WIDTH-1:0] m_axis_dequeue_resp_tag_reg = 0, m_axis_dequeue_resp_tag_next; +reg [OP_TAG_WIDTH-1:0] m_axis_dequeue_resp_op_tag_reg = 0, m_axis_dequeue_resp_op_tag_next; +reg m_axis_dequeue_resp_empty_reg = 1'b0, m_axis_dequeue_resp_empty_next; +reg m_axis_dequeue_resp_error_reg = 1'b0, m_axis_dequeue_resp_error_next; +reg m_axis_dequeue_resp_valid_reg = 1'b0, m_axis_dequeue_resp_valid_next; + +reg s_axis_dequeue_commit_ready_reg = 1'b0, s_axis_dequeue_commit_ready_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_doorbell_queue_reg = 0, m_axis_doorbell_queue_next; +reg m_axis_doorbell_valid_reg = 0, m_axis_doorbell_valid_next; + +reg s_axil_awready_reg = 0, s_axil_awready_next; +reg s_axil_wready_reg = 0, s_axil_wready_next; +reg s_axil_bvalid_reg = 0, s_axil_bvalid_next; +reg s_axil_arready_reg = 0, s_axil_arready_next; +reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = 0, s_axil_rdata_next; +reg s_axil_rvalid_reg = 0, s_axil_rvalid_next; + +reg [QUEUE_RAM_WIDTH-1:0] queue_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_read_ptr; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_write_ptr; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_write_data; +reg queue_ram_wr_en; +reg [QUEUE_RAM_BE_WIDTH-1:0] queue_ram_be; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_read_data_reg = 0; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_read_data_pipeline_reg[PIPELINE-1:1]; + +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_data_head_ptr = queue_ram_read_data_pipeline_reg[PIPELINE-1][15:0]; +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_data_tail_ptr = queue_ram_read_data_pipeline_reg[PIPELINE-1][31:16]; +wire [CPL_INDEX_WIDTH-1:0] queue_ram_read_data_cpl_queue = queue_ram_read_data_pipeline_reg[PIPELINE-1][47:32]; +wire [LOG_QUEUE_SIZE_WIDTH-1:0] queue_ram_read_data_log_queue_size = queue_ram_read_data_pipeline_reg[PIPELINE-1][51:48]; +wire [LOG_BLOCK_SIZE_WIDTH-1:0] queue_ram_read_data_log_block_size = queue_ram_read_data_pipeline_reg[PIPELINE-1][53:52]; +wire queue_ram_read_data_active = queue_ram_read_data_pipeline_reg[PIPELINE-1][55]; +wire [CL_OP_TABLE_SIZE-1:0] queue_ram_read_data_op_index = queue_ram_read_data_pipeline_reg[PIPELINE-1][63:56]; +wire [ADDR_WIDTH-1:0] queue_ram_read_data_base_addr = queue_ram_read_data_pipeline_reg[PIPELINE-1][127:64]; + +reg [OP_TABLE_SIZE-1:0] op_table_active = 0; +reg [OP_TABLE_SIZE-1:0] op_table_commit = 0; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_queue[OP_TABLE_SIZE-1:0]; +reg [QUEUE_PTR_WIDTH-1:0] op_table_queue_ptr[OP_TABLE_SIZE-1:0]; +reg [CL_OP_TABLE_SIZE-1:0] op_table_start_ptr_reg = 0; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_start_queue; +reg [QUEUE_PTR_WIDTH-1:0] op_table_start_queue_ptr; +reg op_table_start_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_commit_ptr; +reg op_table_commit_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_finish_ptr_reg = 0; +reg op_table_finish_en; + +assign s_axis_dequeue_req_ready = s_axis_dequeue_req_ready_reg; + +assign m_axis_dequeue_resp_queue = m_axis_dequeue_resp_queue_reg; +assign m_axis_dequeue_resp_ptr = m_axis_dequeue_resp_ptr_reg; +assign m_axis_dequeue_resp_addr = m_axis_dequeue_resp_addr_reg; +assign m_axis_dequeue_resp_block_size = m_axis_dequeue_resp_block_size_reg; +assign m_axis_dequeue_resp_cpl = m_axis_dequeue_resp_cpl_reg; +assign m_axis_dequeue_resp_tag = m_axis_dequeue_resp_tag_reg; +assign m_axis_dequeue_resp_op_tag = m_axis_dequeue_resp_op_tag_reg; +assign m_axis_dequeue_resp_empty = m_axis_dequeue_resp_empty_reg; +assign m_axis_dequeue_resp_error = m_axis_dequeue_resp_error_reg; +assign m_axis_dequeue_resp_valid = m_axis_dequeue_resp_valid_reg; + +assign s_axis_dequeue_commit_ready = s_axis_dequeue_commit_ready_reg; + +assign m_axis_doorbell_queue = m_axis_doorbell_queue_reg; +assign m_axis_doorbell_valid = m_axis_doorbell_valid_reg; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = 2'b00; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = 2'b00; +assign s_axil_rvalid = s_axil_rvalid_reg; + +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_awaddr_queue = s_axil_awaddr >> 5; +wire [2:0] s_axil_awaddr_reg = s_axil_awaddr >> 2; +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_araddr_queue = s_axil_araddr >> 5; +wire [2:0] s_axil_araddr_reg = s_axil_araddr >> 2; + +wire queue_active = op_table_active[queue_ram_read_data_op_index] && op_table_queue[queue_ram_read_data_op_index] == queue_ram_addr_pipeline_reg[PIPELINE-1]; +wire queue_empty_idle = queue_ram_read_data_head_ptr == queue_ram_read_data_tail_ptr; +wire queue_empty_active = queue_ram_read_data_head_ptr == op_table_queue_ptr[queue_ram_read_data_op_index]; +wire queue_empty = queue_active ? queue_empty_active : queue_empty_idle; +wire [QUEUE_PTR_WIDTH-1:0] queue_ram_read_active_tail_ptr = queue_active ? op_table_queue_ptr[queue_ram_read_data_op_index] : queue_ram_read_data_tail_ptr; + +integer i; + +initial begin + for (i = 0; i < QUEUE_COUNT; i = i + 1) begin + queue_ram[i] = 0; + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] = 0; + axil_reg_pipeline_reg[i] = 0; + write_data_pipeline_reg[i] = 0; + write_strobe_pipeline_reg[i] = 0; + req_tag_pipeline_reg[i] = 0; + end + + for (i = 0; i < OP_TABLE_SIZE; i = i + 1) begin + op_table_queue[i] = 0; + op_table_queue_ptr[i] = 0; + end +end + +integer j; + +always @* begin + op_axil_write_pipe_next = {op_axil_write_pipe_reg, 1'b0}; + op_axil_read_pipe_next = {op_axil_read_pipe_reg, 1'b0}; + op_req_pipe_next = {op_req_pipe_reg, 1'b0}; + op_commit_pipe_next = {op_commit_pipe_reg, 1'b0}; + + queue_ram_addr_pipeline_next[0] = 0; + axil_reg_pipeline_next[0] = 0; + write_data_pipeline_next[0] = 0; + write_strobe_pipeline_next[0] = 0; + req_tag_pipeline_next[0] = 0; + for (j = 1; j < PIPELINE; j = j + 1) begin + queue_ram_addr_pipeline_next[j] = queue_ram_addr_pipeline_reg[j-1]; + axil_reg_pipeline_next[j] = axil_reg_pipeline_reg[j-1]; + write_data_pipeline_next[j] = write_data_pipeline_reg[j-1]; + write_strobe_pipeline_next[j] = write_strobe_pipeline_reg[j-1]; + req_tag_pipeline_next[j] = req_tag_pipeline_reg[j-1]; + end + + s_axis_dequeue_req_ready_next = 1'b0; + + m_axis_dequeue_resp_queue_next = m_axis_dequeue_resp_queue_reg; + m_axis_dequeue_resp_ptr_next = m_axis_dequeue_resp_ptr_reg; + m_axis_dequeue_resp_addr_next = m_axis_dequeue_resp_addr_reg; + m_axis_dequeue_resp_block_size_next = m_axis_dequeue_resp_block_size_reg; + m_axis_dequeue_resp_cpl_next = m_axis_dequeue_resp_cpl_reg; + m_axis_dequeue_resp_tag_next = m_axis_dequeue_resp_tag_reg; + m_axis_dequeue_resp_op_tag_next = m_axis_dequeue_resp_op_tag_reg; + m_axis_dequeue_resp_empty_next = m_axis_dequeue_resp_empty_reg; + m_axis_dequeue_resp_error_next = m_axis_dequeue_resp_error_reg; + m_axis_dequeue_resp_valid_next = m_axis_dequeue_resp_valid_reg && !m_axis_dequeue_resp_ready; + + s_axis_dequeue_commit_ready_next = 1'b0; + + m_axis_doorbell_queue_next = m_axis_doorbell_queue_reg; + m_axis_doorbell_valid_next = 1'b0; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + s_axil_arready_next = 1'b0; + s_axil_rdata_next = s_axil_rdata_reg; + s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready; + + queue_ram_read_ptr = 0; + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data = queue_ram_read_data_pipeline_reg[PIPELINE-1]; + queue_ram_wr_en = 0; + queue_ram_be = 0; + + op_table_start_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + op_table_start_queue_ptr = queue_ram_read_active_tail_ptr + 1; + op_table_start_en = 1'b0; + op_table_commit_ptr = s_axis_dequeue_commit_op_tag; + op_table_commit_en = 1'b0; + op_table_finish_en = 1'b0; + + op_axil_write_pipe_hazard = 1'b0; + op_axil_read_pipe_hazard = 1'b0; + op_req_pipe_hazard = 1'b0; + op_commit_pipe_hazard = 1'b0; + stage_active = 1'b0; + + for (j = 0; j < PIPELINE; j = j + 1) begin + stage_active = op_axil_write_pipe_reg[j] || op_axil_read_pipe_reg[j] || op_req_pipe_reg[j] || op_commit_pipe_reg[j]; + op_axil_write_pipe_hazard = op_axil_write_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_awaddr_queue); + op_axil_read_pipe_hazard = op_axil_read_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_araddr_queue); + op_req_pipe_hazard = op_req_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axis_dequeue_req_queue); + op_commit_pipe_hazard = op_commit_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == op_table_queue[op_table_finish_ptr_reg]); + end + + // pipeline stage 0 - receive request + if (s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && !op_axil_write_pipe_reg[0] && !op_axil_write_pipe_hazard) begin + // AXIL write + op_axil_write_pipe_next[0] = 1'b1; + + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + + write_data_pipeline_next[0] = s_axil_wdata; + write_strobe_pipeline_next[0] = s_axil_wstrb; + + queue_ram_read_ptr = s_axil_awaddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_awaddr_queue; + axil_reg_pipeline_next[0] = s_axil_awaddr_reg; + end else if (s_axil_arvalid && (!s_axil_rvalid || s_axil_rready) && !op_axil_read_pipe_reg[0] && !op_axil_read_pipe_hazard) begin + // AXIL read + op_axil_read_pipe_next[0] = 1'b1; + + s_axil_arready_next = 1'b1; + + queue_ram_read_ptr = s_axil_araddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_araddr_queue; + axil_reg_pipeline_next[0] = s_axil_araddr_reg; + end else if (op_table_active[op_table_finish_ptr_reg] && op_table_commit[op_table_finish_ptr_reg] && !op_commit_pipe_reg[0] && !op_commit_pipe_hazard) begin + // dequeue commit finalize (update pointer) + op_commit_pipe_next[0] = 1'b1; + + op_table_finish_en = 1'b1; + + write_data_pipeline_next[0] = op_table_queue_ptr[op_table_finish_ptr_reg]; + + queue_ram_read_ptr = op_table_queue[op_table_finish_ptr_reg]; + queue_ram_addr_pipeline_next[0] = op_table_queue[op_table_finish_ptr_reg]; + end else if (enable && !op_table_active[op_table_start_ptr_reg] && s_axis_dequeue_req_valid && (!m_axis_dequeue_resp_valid || m_axis_dequeue_resp_ready) && !op_req_pipe_reg && !op_req_pipe_hazard) begin + // dequeue request + op_req_pipe_next[0] = 1'b1; + + s_axis_dequeue_req_ready_next = 1'b1; + + req_tag_pipeline_next[0] = s_axis_dequeue_req_tag; + + queue_ram_read_ptr = s_axis_dequeue_req_queue; + queue_ram_addr_pipeline_next[0] = s_axis_dequeue_req_queue; + end + + // read complete, perform operation + if (op_req_pipe_reg[PIPELINE-1]) begin + // request + m_axis_dequeue_resp_queue_next = queue_ram_addr_pipeline_reg[PIPELINE-1]; + m_axis_dequeue_resp_ptr_next = queue_ram_read_active_tail_ptr; + m_axis_dequeue_resp_addr_next = queue_ram_read_data_base_addr + ((queue_ram_read_active_tail_ptr & ({QUEUE_PTR_WIDTH{1'b1}} >> (QUEUE_PTR_WIDTH - queue_ram_read_data_log_queue_size))) << (CL_DESC_SIZE+queue_ram_read_data_log_block_size)); + m_axis_dequeue_resp_block_size_next = queue_ram_read_data_log_block_size; + m_axis_dequeue_resp_cpl_next = queue_ram_read_data_cpl_queue; + m_axis_dequeue_resp_tag_next = req_tag_pipeline_reg[PIPELINE-1]; + m_axis_dequeue_resp_op_tag_next = op_table_start_ptr_reg; + m_axis_dequeue_resp_empty_next = 1'b0; + m_axis_dequeue_resp_error_next = 1'b0; + + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data[63:56] = op_table_start_ptr_reg; + queue_ram_wr_en = 1'b1; + + op_table_start_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + op_table_start_queue_ptr = queue_ram_read_active_tail_ptr + 1; + + if (!queue_ram_read_data_active) begin + // queue inactive + m_axis_dequeue_resp_error_next = 1'b1; + m_axis_dequeue_resp_valid_next = 1'b1; + end else if (queue_empty) begin + // queue empty + m_axis_dequeue_resp_empty_next = 1'b1; + m_axis_dequeue_resp_valid_next = 1'b1; + end else begin + // start dequeue + m_axis_dequeue_resp_valid_next = 1'b1; + + queue_ram_be[7] = 1'b1; + + op_table_start_en = 1'b1; + end + end else if (op_commit_pipe_reg[PIPELINE-1]) begin + // commit + + // update tail pointer + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data[31:16] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[3:2] = 2'b11; + queue_ram_wr_en = 1'b1; + end else if (op_axil_write_pipe_reg[PIPELINE-1]) begin + // AXIL write + s_axil_bvalid_next = 1'b1; + + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_wr_en = 1'b1; + + // TODO parametrize + case (axil_reg_pipeline_reg[PIPELINE-1]) + 3'd0: begin + // base address lower 32 + // base address is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[95:64] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[11:8] = write_strobe_pipeline_reg[PIPELINE-1]; + end + end + 3'd1: begin + // base address upper 32 + // base address is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[127:96] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[15:12] = write_strobe_pipeline_reg[PIPELINE-1]; + end + end + 3'd2: begin + queue_ram_write_data[55:48] = queue_ram_read_data_pipeline_reg[PIPELINE-1][55:48]; + // log size + // log size is read-only when queue is active + if (!queue_ram_read_data_active) begin + if (write_strobe_pipeline_reg[PIPELINE-1][0]) begin + // log queue size + queue_ram_write_data[51:48] = write_data_pipeline_reg[PIPELINE-1][3:0]; + queue_ram_be[6] = 1'b1; + end + if (write_strobe_pipeline_reg[PIPELINE-1][1]) begin + // log desc block size + queue_ram_write_data[53:52] = write_data_pipeline_reg[PIPELINE-1][9:8]; + queue_ram_be[6] = 1'b1; + end + end + // active + if (write_strobe_pipeline_reg[PIPELINE-1][3]) begin + queue_ram_write_data[55] = write_data_pipeline_reg[PIPELINE-1][31]; + queue_ram_be[6] = 1'b1; + end + end + 3'd3: begin + // completion queue index + // completion queue index is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[47:32] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[5:4] = write_strobe_pipeline_reg[PIPELINE-1]; + end + end + 3'd4: begin + // head pointer + queue_ram_write_data[15:0] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[1:0] = write_strobe_pipeline_reg[PIPELINE-1]; + + // generate doorbell on queue head pointer update + m_axis_doorbell_queue_next = queue_ram_addr_pipeline_reg[PIPELINE-1]; + if (queue_ram_read_data_active) begin + m_axis_doorbell_valid_next = 1'b1; + end + end + 3'd6: begin + // tail pointer + // tail pointer is read-only when queue is active + if (!queue_ram_read_data_active) begin + queue_ram_write_data[31:16] = write_data_pipeline_reg[PIPELINE-1]; + queue_ram_be[3:2] = write_strobe_pipeline_reg[PIPELINE-1]; + end + end + endcase + end else if (op_axil_read_pipe_reg[PIPELINE-1]) begin + // AXIL read + s_axil_rvalid_next = 1'b1; + s_axil_rdata_next = 0; + + // TODO parametrize + case (axil_reg_pipeline_reg[PIPELINE-1]) + 3'd0: begin + // base address lower 32 + s_axil_rdata_next = queue_ram_read_data_base_addr[31:0]; + end + 3'd1: begin + // base address upper 32 + s_axil_rdata_next = queue_ram_read_data_base_addr[63:32]; + end + 3'd2: begin + // log queue size + s_axil_rdata_next[7:0] = queue_ram_read_data_log_queue_size; + // log desc block size + s_axil_rdata_next[15:8] = queue_ram_read_data_log_block_size; + // active + s_axil_rdata_next[31] = queue_ram_read_data_active; + end + 3'd3: begin + // completion queue index + s_axil_rdata_next = queue_ram_read_data_cpl_queue; + end + 3'd4: begin + // head pointer + s_axil_rdata_next = queue_ram_read_data_head_ptr; + end + 3'd6: begin + // tail pointer + s_axil_rdata_next = queue_ram_read_data_tail_ptr; + end + endcase + end + + // dequeue commit (record in table) + s_axis_dequeue_commit_ready_next = enable; + if (s_axis_dequeue_commit_ready && s_axis_dequeue_commit_valid) begin + op_table_commit_ptr = s_axis_dequeue_commit_op_tag; + op_table_commit_en = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + op_axil_write_pipe_reg <= {PIPELINE{1'b0}}; + op_axil_read_pipe_reg <= {PIPELINE{1'b0}}; + op_req_pipe_reg <= {PIPELINE{1'b0}}; + op_commit_pipe_reg <= {PIPELINE{1'b0}}; + + s_axis_dequeue_req_ready_reg <= 1'b0; + m_axis_dequeue_resp_valid_reg <= 1'b0; + s_axis_dequeue_commit_ready_reg <= 1'b0; + m_axis_doorbell_valid_reg <= 1'b0; + + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + + op_table_active <= 0; + + op_table_start_ptr_reg <= 0; + op_table_finish_ptr_reg <= 0; + end else begin + op_axil_write_pipe_reg <= op_axil_write_pipe_next; + op_axil_read_pipe_reg <= op_axil_read_pipe_next; + op_req_pipe_reg <= op_req_pipe_next; + op_commit_pipe_reg <= op_commit_pipe_next; + + s_axis_dequeue_req_ready_reg <= s_axis_dequeue_req_ready_next; + m_axis_dequeue_resp_valid_reg <= m_axis_dequeue_resp_valid_next; + s_axis_dequeue_commit_ready_reg <= s_axis_dequeue_commit_ready_next; + m_axis_doorbell_valid_reg <= m_axis_doorbell_valid_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + if (op_table_start_en) begin + op_table_start_ptr_reg <= op_table_start_ptr_reg + 1; + op_table_active[op_table_start_ptr_reg] <= 1'b1; + end + if (op_table_finish_en) begin + op_table_finish_ptr_reg <= op_table_finish_ptr_reg + 1; + op_table_active[op_table_finish_ptr_reg] <= 1'b0; + end + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] <= queue_ram_addr_pipeline_next[i]; + axil_reg_pipeline_reg[i] <= axil_reg_pipeline_next[i]; + write_data_pipeline_reg[i] <= write_data_pipeline_next[i]; + write_strobe_pipeline_reg[i] <= write_strobe_pipeline_next[i]; + req_tag_pipeline_reg[i] <= req_tag_pipeline_next[i]; + end + + m_axis_dequeue_resp_queue_reg <= m_axis_dequeue_resp_queue_next; + m_axis_dequeue_resp_ptr_reg <= m_axis_dequeue_resp_ptr_next; + m_axis_dequeue_resp_addr_reg <= m_axis_dequeue_resp_addr_next; + m_axis_dequeue_resp_block_size_reg <= m_axis_dequeue_resp_block_size_next; + m_axis_dequeue_resp_cpl_reg <= m_axis_dequeue_resp_cpl_next; + m_axis_dequeue_resp_tag_reg <= m_axis_dequeue_resp_tag_next; + m_axis_dequeue_resp_op_tag_reg <= m_axis_dequeue_resp_op_tag_next; + m_axis_dequeue_resp_empty_reg <= m_axis_dequeue_resp_empty_next; + m_axis_dequeue_resp_error_reg <= m_axis_dequeue_resp_error_next; + + m_axis_doorbell_queue_reg <= m_axis_doorbell_queue_next; + + s_axil_rdata_reg <= s_axil_rdata_next; + + if (queue_ram_wr_en) begin + for (i = 0; i < QUEUE_RAM_BE_WIDTH; i = i + 1) begin + if (queue_ram_be[i]) begin + queue_ram[queue_ram_write_ptr][i*8 +: 8] <= queue_ram_write_data[i*8 +: 8]; + end + end + end + queue_ram_read_data_reg <= queue_ram[queue_ram_read_ptr]; + queue_ram_read_data_pipeline_reg[1] <= queue_ram_read_data_reg; + for (i = 2; i < PIPELINE; i = i + 1) begin + queue_ram_read_data_pipeline_reg[i] <= queue_ram_read_data_pipeline_reg[i-1]; + end + + if (op_table_start_en) begin + op_table_commit[op_table_start_ptr_reg] <= 1'b0; + op_table_queue[op_table_start_ptr_reg] <= op_table_start_queue; + op_table_queue_ptr[op_table_start_ptr_reg] <= op_table_start_queue_ptr; + end + if (op_table_commit_en) begin + op_table_commit[op_table_commit_ptr] <= 1'b1; + end +end + +endmodule diff --git a/corundum/rtl/rx_checksum.v b/corundum/rtl/rx_checksum.v new file mode 100644 index 0000000000000000000000000000000000000000..bbf9164d33aebc56f835b2532ef39d4807d8d181 --- /dev/null +++ b/corundum/rtl/rx_checksum.v @@ -0,0 +1,188 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Receive checksum offload module + */ +module rx_checksum # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 256, + // AXI stream tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Checksum start offset + parameter START_OFFSET = 14 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + input wire s_axis_tlast, + + /* + * Checksum output + */ + output wire [15:0] m_axis_csum, + output wire m_axis_csum_valid +); + +parameter LEVELS = $clog2(DATA_WIDTH/8); +parameter OFFSET_WIDTH = START_OFFSET/KEEP_WIDTH > 1 ? $clog2(START_OFFSET/KEEP_WIDTH) : 1; + +// bus width assertions +initial begin + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +reg [OFFSET_WIDTH-1:0] offset_reg = START_OFFSET/KEEP_WIDTH; +reg [KEEP_WIDTH-1:0] mask_reg = {KEEP_WIDTH{1'b1}} << START_OFFSET; +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg [DATA_WIDTH-1:0] sum_reg[LEVELS-2:0]; +reg [LEVELS-2:0] sum_valid_reg = 0; +reg [LEVELS-2:0] sum_last_reg = 0; + +reg [16+LEVELS-1:0] sum_acc_temp = 0; +reg [15:0] sum_acc_reg = 0; + +reg [15:0] m_axis_csum_reg = 0; +reg m_axis_csum_valid_reg = 1'b0; + +assign m_axis_csum = m_axis_csum_reg; +assign m_axis_csum_valid = m_axis_csum_valid_reg; + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < KEEP_WIDTH; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = (s_axis_tkeep[j] && mask_reg[j]) ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +integer i; + +always @(posedge clk) begin + sum_valid_reg[0] <= 1'b0; + + if (s_axis_tvalid) begin + for (i = 0; i < DATA_WIDTH/8/4; i = i + 1) begin + sum_reg[0][i*17 +: 17] <= {s_axis_tdata_masked[(4*i+0)*8 +: 8], s_axis_tdata_masked[(4*i+1)*8 +: 8]} + {s_axis_tdata_masked[(4*i+2)*8 +: 8], s_axis_tdata_masked[(4*i+3)*8 +: 8]}; + end + sum_valid_reg[0] <= 1'b1; + sum_last_reg[0] <= s_axis_tlast; + + if (s_axis_tlast) begin + offset_reg <= START_OFFSET/KEEP_WIDTH; + mask_reg <= {KEEP_WIDTH{1'b1}} << START_OFFSET; + end else if (START_OFFSET < KEEP_WIDTH || offset_reg == 0) begin + mask_reg <= {KEEP_WIDTH{1'b1}}; + end else begin + offset_reg <= offset_reg - 1; + if (offset_reg == 1) begin + mask_reg <= {KEEP_WIDTH{1'b1}} << (START_OFFSET%KEEP_WIDTH); + end else begin + mask_reg <= {KEEP_WIDTH{1'b0}}; + end + end + end + + if (rst) begin + offset_reg <= START_OFFSET/KEEP_WIDTH; + mask_reg <= {KEEP_WIDTH{1'b1}} << START_OFFSET; + sum_valid_reg[0] <= 1'b0; + end +end + +generate + + genvar l; + + for (l = 1; l < LEVELS-1; l = l + 1) begin + + always @(posedge clk) begin + sum_valid_reg[l] <= 1'b0; + + if (sum_valid_reg[l-1]) begin + for (i = 0; i < DATA_WIDTH/8/4/2**l; i = i + 1) begin + sum_reg[l][i*(17+l) +: (17+l)] <= sum_reg[l-1][(i*2+0)*(17+l-1) +: (17+l-1)] + sum_reg[l-1][(i*2+1)*(17+l-1) +: (17+l-1)]; + end + sum_valid_reg[l] <= 1'b1; + sum_last_reg[l] <= sum_last_reg[l-1]; + end + + if (rst) begin + sum_valid_reg[l] <= 1'b0; + end + end + + end + +endgenerate + +always @(posedge clk) begin + m_axis_csum_valid_reg <= 1'b0; + + if (sum_valid_reg[LEVELS-2]) begin + sum_acc_temp = sum_reg[LEVELS-2][16+LEVELS-1-1:0] + sum_acc_reg; + sum_acc_temp = sum_acc_temp[15:0] + (sum_acc_temp >> 16); + sum_acc_temp = sum_acc_temp[15:0] + sum_acc_temp[16]; + + if (sum_last_reg[LEVELS-2]) begin + m_axis_csum_reg <= sum_acc_temp; + m_axis_csum_valid_reg <= 1'b1; + sum_acc_reg <= 0; + end else begin + sum_acc_reg <= sum_acc_temp; + end + end + + if (rst) begin + m_axis_csum_valid_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/rtl/rx_engine.v b/corundum/rtl/rx_engine.v new file mode 100644 index 0000000000000000000000000000000000000000..78c70be3184716f04ac521d072f56acc0972c652 --- /dev/null +++ b/corundum/rtl/rx_engine.v @@ -0,0 +1,967 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Receive engine + */ +module rx_engine # +( + // DMA RAM address width + parameter RAM_ADDR_WIDTH = 16, + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // DMA length field width + parameter DMA_LEN_WIDTH = 20, + // DMA client length field width + parameter DMA_CLIENT_LEN_WIDTH = 20, + // Receive request tag field width + parameter REQ_TAG_WIDTH = 8, + // Descriptor request tag field width + parameter DESC_REQ_TAG_WIDTH = 8, + // DMA tag field width + parameter DMA_TAG_WIDTH = 8, + // DMA client tag field width + parameter DMA_CLIENT_TAG_WIDTH = 8, + // Queue request tag field width + parameter QUEUE_REQ_TAG_WIDTH = 8, + // Queue operation tag field width + parameter QUEUE_OP_TAG_WIDTH = 8, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 4, + // Queue element pointer width + parameter QUEUE_PTR_WIDTH = 16, + // Completion queue index width + parameter CPL_QUEUE_INDEX_WIDTH = 4, + // Descriptor table size (number of in-flight operations) + parameter DESC_TABLE_SIZE = 8, + // Width of descriptor table field for tracking outstanding DMA operations + parameter DESC_TABLE_DMA_OP_COUNT_WIDTH = 4, + // Packet table size (number of in-progress packets) + parameter PKT_TABLE_SIZE = 8, + // Max receive packet size + parameter MAX_RX_SIZE = 2048, + // Descriptor size (in bytes) + parameter DESC_SIZE = 16, + // Descriptor size (in bytes) + parameter CPL_SIZE = 32, + // Width of AXI stream descriptor interfaces in bits + parameter AXIS_DESC_DATA_WIDTH = DESC_SIZE*8, + // AXI stream descriptor tkeep signal width (words per cycle) + parameter AXIS_DESC_KEEP_WIDTH = AXIS_DESC_DATA_WIDTH/8, + // Enable PTP timestamping + parameter PTP_TS_ENABLE = 1, + // Enable RX hashing + parameter RX_HASH_ENABLE = 1, + // Enable RX checksum offload + parameter RX_CHECKSUM_ENABLE = 1 +) +( + input wire clk, + input wire rst, + + /* + * Receive request input (queue index) + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_rx_req_queue, + input wire [REQ_TAG_WIDTH-1:0] s_axis_rx_req_tag, + input wire s_axis_rx_req_valid, + output wire s_axis_rx_req_ready, + + /* + * Receive request status output + */ + output wire [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_rx_req_status_len, + output wire [REQ_TAG_WIDTH-1:0] m_axis_rx_req_status_tag, + output wire m_axis_rx_req_status_valid, + + /* + * Descriptor request output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_desc_req_queue, + output wire [DESC_REQ_TAG_WIDTH-1:0] m_axis_desc_req_tag, + output wire m_axis_desc_req_valid, + input wire m_axis_desc_req_ready, + + /* + * Descriptor request status input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_desc_req_status_queue, + input wire [QUEUE_PTR_WIDTH-1:0] s_axis_desc_req_status_ptr, + input wire [CPL_QUEUE_INDEX_WIDTH-1:0] s_axis_desc_req_status_cpl, + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_desc_req_status_tag, + input wire s_axis_desc_req_status_empty, + input wire s_axis_desc_req_status_error, + input wire s_axis_desc_req_status_valid, + + /* + * Descriptor data input + */ + input wire [AXIS_DESC_DATA_WIDTH-1:0] s_axis_desc_tdata, + input wire [AXIS_DESC_KEEP_WIDTH-1:0] s_axis_desc_tkeep, + input wire s_axis_desc_tvalid, + output wire s_axis_desc_tready, + input wire s_axis_desc_tlast, + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_desc_tid, + input wire s_axis_desc_tuser, + + /* + * Completion request output + */ + output wire [CPL_QUEUE_INDEX_WIDTH-1:0] m_axis_cpl_req_queue, + output wire [DESC_REQ_TAG_WIDTH-1:0] m_axis_cpl_req_tag, + output wire [CPL_SIZE*8-1:0] m_axis_cpl_req_data, + output wire m_axis_cpl_req_valid, + input wire m_axis_cpl_req_ready, + + /* + * Completion request status input + */ + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_cpl_req_status_tag, + input wire s_axis_cpl_req_status_full, + input wire s_axis_cpl_req_status_error, + input wire s_axis_cpl_req_status_valid, + + /* + * DMA write descriptor output + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_dma_write_desc_dma_addr, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_dma_write_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_dma_write_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_dma_write_desc_tag, + output wire m_axis_dma_write_desc_valid, + input wire m_axis_dma_write_desc_ready, + + /* + * DMA write descriptor status input + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_dma_write_desc_status_tag, + input wire s_axis_dma_write_desc_status_valid, + + /* + * Receive descriptor output + */ + output wire [RAM_ADDR_WIDTH-1:0] m_axis_rx_desc_addr, + output wire [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_rx_desc_len, + output wire [DMA_CLIENT_TAG_WIDTH-1:0] m_axis_rx_desc_tag, + output wire m_axis_rx_desc_valid, + input wire m_axis_rx_desc_ready, + + /* + * Receive descriptor status input + */ + input wire [DMA_CLIENT_LEN_WIDTH-1:0] s_axis_rx_desc_status_len, + input wire [DMA_CLIENT_TAG_WIDTH-1:0] s_axis_rx_desc_status_tag, + input wire s_axis_rx_desc_status_user, + input wire s_axis_rx_desc_status_valid, + + /* + * Receive timestamp input + */ + input wire [95:0] s_axis_rx_ptp_ts_96, + input wire s_axis_rx_ptp_ts_valid, + output wire s_axis_rx_ptp_ts_ready, + + /* + * Receive hash input + */ + input wire [31:0] s_axis_rx_hash, + input wire [3:0] s_axis_rx_hash_type, + input wire s_axis_rx_hash_valid, + output wire s_axis_rx_hash_ready, + + /* + * Receive checksum input + */ + input wire [15:0] s_axis_rx_csum, + input wire s_axis_rx_csum_valid, + output wire s_axis_rx_csum_ready, + + /* + * Configuration + */ + input wire enable +); + +parameter CL_DESC_TABLE_SIZE = $clog2(DESC_TABLE_SIZE); +parameter DESC_PTR_MASK = {CL_DESC_TABLE_SIZE{1'b1}}; +parameter CL_PKT_TABLE_SIZE = $clog2(PKT_TABLE_SIZE); + +parameter CL_MAX_RX_SIZE = $clog2(MAX_RX_SIZE); + +// bus width assertions +initial begin + if (DMA_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: DMA tag width insufficient for descriptor table size (instance %m)"); + $finish; + end + + if (DMA_CLIENT_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: DMA client tag width insufficient for descriptor table size (instance %m)"); + $finish; + end + + if (QUEUE_REQ_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: QUEUE_REQ_TAG_WIDTH must be at least $clog2(DESC_TABLE_SIZE) (instance %m)"); + $finish; + end + + if (DESC_REQ_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: DESC_REQ_TAG_WIDTH must be at least $clog2(DESC_TABLE_SIZE) (instance %m)"); + $finish; + end +end + +reg s_axis_rx_req_ready_reg = 1'b0, s_axis_rx_req_ready_next; + +reg [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_rx_req_status_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, m_axis_rx_req_status_len_next; +reg [REQ_TAG_WIDTH-1:0] m_axis_rx_req_status_tag_reg = {REQ_TAG_WIDTH{1'b0}}, m_axis_rx_req_status_tag_next; +reg m_axis_rx_req_status_valid_reg = 1'b0, m_axis_rx_req_status_valid_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_desc_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}, m_axis_desc_req_queue_next; +reg [DESC_REQ_TAG_WIDTH-1:0] m_axis_desc_req_tag_reg = {DESC_REQ_TAG_WIDTH{1'b0}}, m_axis_desc_req_tag_next; +reg m_axis_desc_req_valid_reg = 1'b0, m_axis_desc_req_valid_next; + +reg s_axis_desc_tready_reg = 1'b0, s_axis_desc_tready_next; + +reg [CPL_QUEUE_INDEX_WIDTH-1:0] m_axis_cpl_req_queue_reg = {CPL_QUEUE_INDEX_WIDTH{1'b0}}, m_axis_cpl_req_queue_next; +reg [DESC_REQ_TAG_WIDTH-1:0] m_axis_cpl_req_tag_reg = {DESC_REQ_TAG_WIDTH{1'b0}}, m_axis_cpl_req_tag_next; +reg [CPL_SIZE*8-1:0] m_axis_cpl_req_data_reg = {CPL_SIZE*8{1'b0}}, m_axis_cpl_req_data_next; +reg m_axis_cpl_req_valid_reg = 1'b0, m_axis_cpl_req_valid_next; + +reg [DMA_ADDR_WIDTH-1:0] m_axis_dma_write_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}, m_axis_dma_write_desc_dma_addr_next; +reg [RAM_ADDR_WIDTH-1:0] m_axis_dma_write_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, m_axis_dma_write_desc_ram_addr_next; +reg [DMA_LEN_WIDTH-1:0] m_axis_dma_write_desc_len_reg = {DMA_LEN_WIDTH{1'b0}}, m_axis_dma_write_desc_len_next; +reg [DMA_TAG_WIDTH-1:0] m_axis_dma_write_desc_tag_reg = {DMA_TAG_WIDTH{1'b0}}, m_axis_dma_write_desc_tag_next; +reg m_axis_dma_write_desc_valid_reg = 1'b0, m_axis_dma_write_desc_valid_next; + +reg [RAM_ADDR_WIDTH-1:0] m_axis_rx_desc_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, m_axis_rx_desc_addr_next; +reg [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_rx_desc_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, m_axis_rx_desc_len_next; +reg [DMA_CLIENT_TAG_WIDTH-1:0] m_axis_rx_desc_tag_reg = {DMA_CLIENT_TAG_WIDTH{1'b0}}, m_axis_rx_desc_tag_next; +reg m_axis_rx_desc_valid_reg = 1'b0, m_axis_rx_desc_valid_next; + +reg s_axis_rx_ptp_ts_ready_reg = 1'b0, s_axis_rx_ptp_ts_ready_next; + +reg s_axis_rx_hash_ready_reg = 1'b0, s_axis_rx_hash_ready_next; + +reg s_axis_rx_csum_ready_reg = 1'b0, s_axis_rx_csum_ready_next; + +reg desc_start_reg = 1'b1, desc_start_next; +reg desc_done_reg = 1'b0, desc_done_next; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, desc_len_next; + +reg [DESC_TABLE_SIZE-1:0] desc_table_active = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_rx_done = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_invalid = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_desc_fetched = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_data_written = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_cpl_write_done = 0; +reg [REQ_TAG_WIDTH-1:0] desc_table_tag[DESC_TABLE_SIZE-1:0]; +reg [QUEUE_INDEX_WIDTH-1:0] desc_table_queue[DESC_TABLE_SIZE-1:0]; +reg [QUEUE_PTR_WIDTH-1:0] desc_table_queue_ptr[DESC_TABLE_SIZE-1:0]; +reg [CPL_QUEUE_INDEX_WIDTH-1:0] desc_table_cpl_queue[DESC_TABLE_SIZE-1:0]; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_table_dma_len[DESC_TABLE_SIZE-1:0]; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_table_desc_len[DESC_TABLE_SIZE-1:0]; +reg [CL_PKT_TABLE_SIZE-1:0] desc_table_pkt[DESC_TABLE_SIZE-1:0]; +reg [95:0] desc_table_ptp_ts[DESC_TABLE_SIZE-1:0]; +reg [31:0] desc_table_hash[DESC_TABLE_SIZE-1:0]; +reg [3:0] desc_table_hash_type[DESC_TABLE_SIZE-1:0]; +reg [15:0] desc_table_csum[DESC_TABLE_SIZE-1:0]; +reg desc_table_read_commit[DESC_TABLE_SIZE-1:0]; +reg [DESC_TABLE_DMA_OP_COUNT_WIDTH-1:0] desc_table_write_count_start[DESC_TABLE_SIZE-1:0]; +reg [DESC_TABLE_DMA_OP_COUNT_WIDTH-1:0] desc_table_write_count_finish[DESC_TABLE_SIZE-1:0]; + +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_start_ptr_reg = 0; +reg [QUEUE_INDEX_WIDTH-1:0] desc_table_start_queue; +reg [REQ_TAG_WIDTH-1:0] desc_table_start_tag; +reg [CL_PKT_TABLE_SIZE-1:0] desc_table_start_pkt; +reg desc_table_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_rx_finish_ptr; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_table_rx_finish_len; +reg desc_table_rx_finish_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_dequeue_start_ptr_reg = 0; +reg desc_table_dequeue_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_dequeue_ptr; +reg [QUEUE_PTR_WIDTH-1:0] desc_table_dequeue_queue_ptr; +reg [CPL_QUEUE_INDEX_WIDTH-1:0] desc_table_dequeue_cpl_queue; +reg desc_table_dequeue_invalid; +reg desc_table_dequeue_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_desc_fetched_ptr; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_table_desc_fetched_len; +reg desc_table_desc_fetched_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_data_written_ptr; +reg desc_table_data_written_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_store_ptp_ts_ptr_reg = 0; +reg [95:0] desc_table_store_ptp_ts; +reg desc_table_store_ptp_ts_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_store_hash_ptr_reg = 0; +reg [31:0] desc_table_store_hash; +reg [3:0] desc_table_store_hash_type; +reg desc_table_store_hash_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_store_csum_ptr_reg = 0; +reg [15:0] desc_table_store_csum; +reg desc_table_store_csum_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_cpl_enqueue_start_ptr_reg = 0; +reg desc_table_cpl_enqueue_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_cpl_write_done_ptr; +reg desc_table_cpl_write_done_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_finish_ptr_reg = 0; +reg desc_table_finish_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_write_start_ptr; +reg desc_table_write_start_commit; +reg desc_table_write_start_init; +reg desc_table_write_start_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_write_finish_ptr; +reg desc_table_write_finish_en; + +reg [PKT_TABLE_SIZE-1:0] pkt_table_active = 0; +reg [CL_PKT_TABLE_SIZE-1:0] pkt_table_start_ptr; +reg pkt_table_start_en; +reg [CL_PKT_TABLE_SIZE-1:0] pkt_table_finish_ptr; +reg pkt_table_finish_en; + +assign s_axis_rx_req_ready = s_axis_rx_req_ready_reg; + +assign m_axis_rx_req_status_len = m_axis_rx_req_status_len_reg; +assign m_axis_rx_req_status_tag = m_axis_rx_req_status_tag_reg; +assign m_axis_rx_req_status_valid = m_axis_rx_req_status_valid_reg; + +assign m_axis_desc_req_queue = m_axis_desc_req_queue_reg; +assign m_axis_desc_req_tag = m_axis_desc_req_tag_reg; +assign m_axis_desc_req_valid = m_axis_desc_req_valid_reg; + +assign s_axis_desc_tready = s_axis_desc_tready_reg; + +assign m_axis_cpl_req_queue = m_axis_cpl_req_queue_reg; +assign m_axis_cpl_req_tag = m_axis_cpl_req_tag_reg; +assign m_axis_cpl_req_data = m_axis_cpl_req_data_reg; +assign m_axis_cpl_req_valid = m_axis_cpl_req_valid_reg; + +assign m_axis_dma_write_desc_dma_addr = m_axis_dma_write_desc_dma_addr_reg; +assign m_axis_dma_write_desc_ram_addr = m_axis_dma_write_desc_ram_addr_reg; +assign m_axis_dma_write_desc_len = m_axis_dma_write_desc_len_reg; +assign m_axis_dma_write_desc_tag = m_axis_dma_write_desc_tag_reg; +assign m_axis_dma_write_desc_valid = m_axis_dma_write_desc_valid_reg; + +assign m_axis_rx_desc_addr = m_axis_rx_desc_addr_reg; +assign m_axis_rx_desc_len = m_axis_rx_desc_len_reg; +assign m_axis_rx_desc_tag = m_axis_rx_desc_tag_reg; +assign m_axis_rx_desc_valid = m_axis_rx_desc_valid_reg; + +assign s_axis_rx_ptp_ts_ready = s_axis_rx_ptp_ts_ready_reg; + +assign s_axis_rx_hash_ready = s_axis_rx_hash_ready_reg; + +assign s_axis_rx_csum_ready = s_axis_rx_csum_ready_reg; + +wire pkt_table_free_ptr_valid; +wire [CL_PKT_TABLE_SIZE-1:0] pkt_table_free_ptr; + +priority_encoder #( + .WIDTH(PKT_TABLE_SIZE), + .LSB_PRIORITY("HIGH") +) +pkt_table_free_enc_inst ( + .input_unencoded(~pkt_table_active), + .output_valid(pkt_table_free_ptr_valid), + .output_encoded(pkt_table_free_ptr), + .output_unencoded() +); + +// reg [15:0] stall_cnt = 0; +// wire stalled = stall_cnt[12]; + +// // assign dbg = stalled; + +// always @(posedge clk) begin +// if (rst) begin +// stall_cnt <= 0; +// end else begin +// if (s_axis_rx_req_ready) begin +// stall_cnt <= 0; +// end else begin +// stall_cnt <= stall_cnt + 1; +// end +// end +// end + +// ila_0 ila_inst ( +// .clk(clk), +// .trig_out(), +// .trig_out_ack(1'b0), +// .trig_in(1'b0), +// .trig_in_ack(), +// .probe0({desc_table_active, desc_table_rx_done, desc_table_invalid, desc_table_desc_fetched, desc_table_data_written, desc_table_cpl_write_done, pkt_table_active, +// m_axis_dma_read_desc_len, m_axis_dma_read_desc_tag, m_axis_dma_read_desc_valid, m_axis_dma_read_desc_ready, +// s_axis_dma_read_desc_status_tag, s_axis_dma_read_desc_status_valid, +// m_axis_dma_write_desc_len, m_axis_dma_write_desc_tag, m_axis_dma_write_desc_valid, m_axis_dma_write_desc_ready, +// s_axis_dma_write_desc_status_tag, s_axis_dma_write_desc_status_valid}), +// .probe1(0), +// .probe2(0), +// .probe3(s_axis_rx_req_ready), +// .probe4({desc_table_start_ptr_reg, desc_table_rx_finish_ptr, desc_table_desc_read_start_ptr_reg, desc_table_data_write_start_ptr_reg, desc_table_cpl_enqueue_start_ptr_reg, desc_table_finish_ptr_reg, stall_cnt}), +// .probe5(0) +// ); + +integer i; + +initial begin + for (i = 0; i < DESC_TABLE_SIZE; i = i + 1) begin + desc_table_tag[i] = 0; + desc_table_queue[i] = 0; + desc_table_queue_ptr[i] = 0; + desc_table_cpl_queue[i] = 0; + desc_table_dma_len[i] = 0; + desc_table_desc_len[i] = 0; + desc_table_pkt[i] = 0; + desc_table_ptp_ts[i] = 0; + desc_table_hash[i] = 0; + desc_table_hash_type[i] = 0; + desc_table_csum[i] = 0; + desc_table_read_commit[i] = 0; + desc_table_write_count_start[i] = 0; + desc_table_write_count_finish[i] = 0; + end +end + +always @* begin + s_axis_rx_req_ready_next = 1'b0; + + m_axis_rx_req_status_len_next = m_axis_rx_req_status_len_reg; + m_axis_rx_req_status_tag_next = m_axis_rx_req_status_tag_reg; + m_axis_rx_req_status_valid_next = 1'b0; + + m_axis_desc_req_queue_next = m_axis_desc_req_queue_reg; + m_axis_desc_req_tag_next = m_axis_desc_req_tag_reg; + m_axis_desc_req_valid_next = m_axis_desc_req_valid_reg && !m_axis_desc_req_ready; + + s_axis_desc_tready_next = 1'b0; + + m_axis_cpl_req_queue_next = m_axis_cpl_req_queue_reg; + m_axis_cpl_req_tag_next = m_axis_cpl_req_tag_reg; + m_axis_cpl_req_data_next = m_axis_cpl_req_data_reg; + m_axis_cpl_req_valid_next = m_axis_cpl_req_valid_reg && !m_axis_cpl_req_ready; + + m_axis_dma_write_desc_dma_addr_next = m_axis_dma_write_desc_dma_addr_reg; + m_axis_dma_write_desc_ram_addr_next = m_axis_dma_write_desc_ram_addr_reg; + m_axis_dma_write_desc_len_next = m_axis_dma_write_desc_len_reg; + m_axis_dma_write_desc_tag_next = m_axis_dma_write_desc_tag_reg; + m_axis_dma_write_desc_valid_next = m_axis_dma_write_desc_valid_reg && !m_axis_dma_write_desc_ready; + + m_axis_rx_desc_addr_next = m_axis_rx_desc_addr_reg; + m_axis_rx_desc_len_next = m_axis_rx_desc_len_reg; + m_axis_rx_desc_tag_next = m_axis_rx_desc_tag_reg; + m_axis_rx_desc_valid_next = m_axis_rx_desc_valid_reg && !m_axis_rx_desc_ready; + + s_axis_rx_ptp_ts_ready_next = 1'b0; + + s_axis_rx_hash_ready_next = 1'b0; + + s_axis_rx_csum_ready_next = 1'b0; + + desc_start_next = desc_start_reg; + desc_done_next = desc_done_reg; + desc_len_next = desc_len_reg; + + desc_table_start_tag = s_axis_rx_req_tag; + desc_table_start_queue = s_axis_rx_req_queue; + desc_table_start_pkt = pkt_table_free_ptr; + desc_table_start_en = 1'b0; + desc_table_rx_finish_ptr = s_axis_rx_desc_status_tag; + desc_table_rx_finish_len = s_axis_rx_desc_status_len; + desc_table_rx_finish_en = 1'b0; + desc_table_dequeue_start_en = 1'b0; + desc_table_dequeue_ptr = s_axis_desc_req_status_tag; + desc_table_dequeue_queue_ptr = s_axis_desc_req_status_ptr; + desc_table_dequeue_cpl_queue = s_axis_desc_req_status_cpl; + desc_table_dequeue_invalid = 1'b0; + desc_table_dequeue_en = 1'b0; + desc_table_desc_fetched_ptr = s_axis_desc_tid & DESC_PTR_MASK; + desc_table_desc_fetched_len = desc_len_reg + s_axis_desc_tdata[63:32]; + desc_table_desc_fetched_en = 1'b0; + desc_table_data_written_ptr = s_axis_dma_write_desc_status_tag & DESC_PTR_MASK; + desc_table_data_written_en = 1'b0; + desc_table_store_ptp_ts = s_axis_rx_ptp_ts_96; + desc_table_store_ptp_ts_en = 1'b0; + desc_table_store_hash = s_axis_rx_hash; + desc_table_store_hash_type = s_axis_rx_hash_type; + desc_table_store_hash_en = 1'b0; + desc_table_store_csum = s_axis_rx_csum; + desc_table_store_csum_en = 1'b0; + desc_table_cpl_enqueue_start_en = 1'b0; + desc_table_cpl_write_done_ptr = s_axis_cpl_req_status_tag & DESC_PTR_MASK; + desc_table_cpl_write_done_en = 1'b0; + desc_table_finish_en = 1'b0; + desc_table_write_start_ptr = s_axis_desc_tid; + desc_table_write_start_commit = 1'b0; + desc_table_write_start_init = 1'b0; + desc_table_write_start_en = 1'b0; + desc_table_write_finish_ptr = s_axis_dma_write_desc_status_tag; + desc_table_write_finish_en = 1'b0; + + pkt_table_start_ptr = pkt_table_free_ptr; + pkt_table_start_en = 1'b0; + pkt_table_finish_ptr = desc_table_pkt[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + pkt_table_finish_en = 1'b0; + + // receive packet + // wait for receive request + s_axis_rx_req_ready_next = enable && pkt_table_free_ptr_valid && !desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] && ($unsigned(desc_table_start_ptr_reg - desc_table_finish_ptr_reg) < DESC_TABLE_SIZE) && (!m_axis_rx_desc_valid_reg || m_axis_rx_desc_ready); + if (s_axis_rx_req_ready && s_axis_rx_req_valid) begin + s_axis_rx_req_ready_next = 1'b0; + + // store in descriptor table + desc_table_start_tag = s_axis_rx_req_tag; + desc_table_start_queue = s_axis_rx_req_queue; + desc_table_start_pkt = pkt_table_free_ptr; + desc_table_start_en = 1'b1; + + // store in packet table + pkt_table_start_ptr = pkt_table_free_ptr; + pkt_table_start_en = 1'b1; + + // initiate receive operation + m_axis_rx_desc_addr_next = pkt_table_free_ptr << CL_MAX_RX_SIZE; + m_axis_rx_desc_len_next = MAX_RX_SIZE; + m_axis_rx_desc_tag_next = desc_table_start_ptr_reg & DESC_PTR_MASK; + m_axis_rx_desc_valid_next = 1'b1; + end + + // receive done + // wait for receive completion + if (s_axis_rx_desc_status_valid) begin + // update entry in descriptor table + desc_table_rx_finish_ptr = s_axis_rx_desc_status_tag; + desc_table_rx_finish_len = s_axis_rx_desc_status_len; + desc_table_rx_finish_en = 1'b1; + end + + // descriptor fetch + if (desc_table_active[desc_table_dequeue_start_ptr_reg & DESC_PTR_MASK] && desc_table_dequeue_start_ptr_reg != desc_table_start_ptr_reg) begin + if (desc_table_rx_done[desc_table_dequeue_start_ptr_reg & DESC_PTR_MASK] && !m_axis_desc_req_valid) begin + // update entry in descriptor table + desc_table_dequeue_start_en = 1'b1; + + // initiate descriptor fetch + m_axis_desc_req_queue_next = desc_table_queue[desc_table_dequeue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_desc_req_tag_next = desc_table_dequeue_start_ptr_reg & DESC_PTR_MASK; + m_axis_desc_req_valid_next = 1'b1; + end + end + + // descriptor fetch + // wait for queue query response + if (s_axis_desc_req_status_valid) begin + + // update entry in descriptor table + desc_table_dequeue_ptr = s_axis_desc_req_status_tag & DESC_PTR_MASK; + desc_table_dequeue_queue_ptr = s_axis_desc_req_status_ptr; + desc_table_dequeue_cpl_queue = s_axis_desc_req_status_cpl; + desc_table_dequeue_invalid = 1'b0; + desc_table_dequeue_en = 1'b1; + + if (s_axis_desc_req_status_error || s_axis_desc_req_status_empty) begin + // queue empty or not active + // TODO retry if empty? + + // invalidate entry + desc_table_dequeue_invalid = 1'b1; + end else begin + // descriptor available to dequeue + + // wait for descriptor + end + end + + // descriptor processing and DMA request generation + // TODO descriptor validation? + s_axis_desc_tready_next = !m_axis_dma_write_desc_valid; + if (s_axis_desc_tready && s_axis_desc_tvalid) begin + if (desc_table_active[s_axis_desc_tid & DESC_PTR_MASK]) begin + desc_start_next = 1'b0; + desc_len_next = desc_len_reg + s_axis_desc_tdata[63:32]; + + desc_table_write_start_init = desc_start_reg; + + // initiate data write + m_axis_dma_write_desc_dma_addr_next = s_axis_desc_tdata[127:64]; + m_axis_dma_write_desc_ram_addr_next = (desc_table_pkt[s_axis_desc_tid & DESC_PTR_MASK] << CL_MAX_RX_SIZE) + desc_len_reg; + if (s_axis_desc_tdata[63:32] < (desc_table_dma_len[s_axis_desc_tid & DESC_PTR_MASK] - desc_len_reg)) begin + // limit write to length provided in descriptor + m_axis_dma_write_desc_len_next = s_axis_desc_tdata[63:32]; + end else begin + // write actual packet length + m_axis_dma_write_desc_len_next = desc_table_dma_len[s_axis_desc_tid & DESC_PTR_MASK] - desc_len_reg; + desc_done_next = 1'b1; + end + m_axis_dma_write_desc_tag_next = s_axis_desc_tid & DESC_PTR_MASK; + + desc_table_write_start_ptr = s_axis_desc_tid; + + if (m_axis_dma_write_desc_len_next != 0 && !desc_done_reg) begin + m_axis_dma_write_desc_valid_next = 1'b1; + + // write start + desc_table_write_start_en = 1'b1; + + s_axis_desc_tready_next = 1'b0; + end + + if (s_axis_desc_tlast) begin + desc_start_next = 1'b1; + desc_done_next = 1'b0; + desc_len_next = 0; + + // update entry in descriptor table + desc_table_desc_fetched_ptr = s_axis_desc_tid & DESC_PTR_MASK; + desc_table_desc_fetched_len = desc_len_reg + s_axis_desc_tdata[63:32]; + desc_table_desc_fetched_en = 1'b1; + + // write commit + desc_table_write_start_commit = 1'b1; + end + end + end + + // data write completion + // wait for data write completion + if (s_axis_dma_write_desc_status_valid) begin + // update entry in descriptor table + desc_table_data_written_ptr = s_axis_dma_write_desc_status_tag & DESC_PTR_MASK; + desc_table_data_written_en = 1'b1; + + // write finish + desc_table_write_finish_ptr = s_axis_dma_write_desc_status_tag; + desc_table_write_finish_en = 1'b1; + end + + // store PTP timestamp + if (desc_table_active[desc_table_store_ptp_ts_ptr_reg & DESC_PTR_MASK] && desc_table_store_ptp_ts_ptr_reg != desc_table_start_ptr_reg && PTP_TS_ENABLE) begin + s_axis_rx_ptp_ts_ready_next = 1'b1; + if (desc_table_invalid[desc_table_store_ptp_ts_ptr_reg & DESC_PTR_MASK]) begin + // invalid entry; skip + desc_table_store_ptp_ts_en = 1'b1; + + s_axis_rx_ptp_ts_ready_next = 1'b0; + end else if (s_axis_rx_ptp_ts_ready && s_axis_rx_ptp_ts_valid) begin + // update entry in descriptor table + desc_table_store_ptp_ts = s_axis_rx_ptp_ts_96; + desc_table_store_ptp_ts_en = 1'b1; + + s_axis_rx_ptp_ts_ready_next = 1'b0; + end + end + + // store RX hash + if (desc_table_active[desc_table_store_hash_ptr_reg & DESC_PTR_MASK] && desc_table_store_hash_ptr_reg != desc_table_start_ptr_reg && RX_HASH_ENABLE) begin + s_axis_rx_hash_ready_next = 1'b1; + if (desc_table_invalid[desc_table_store_hash_ptr_reg & DESC_PTR_MASK]) begin + // invalid entry; skip + desc_table_store_hash_en = 1'b1; + + s_axis_rx_hash_ready_next = 1'b0; + end else if (s_axis_rx_hash_ready && s_axis_rx_hash_valid) begin + // update entry in descriptor table + desc_table_store_hash = s_axis_rx_hash; + desc_table_store_hash_type = s_axis_rx_hash_type; + desc_table_store_hash_en = 1'b1; + + s_axis_rx_hash_ready_next = 1'b0; + end + end + + // store RX checksum + if (desc_table_active[desc_table_store_csum_ptr_reg & DESC_PTR_MASK] && desc_table_store_csum_ptr_reg != desc_table_start_ptr_reg && RX_CHECKSUM_ENABLE) begin + s_axis_rx_csum_ready_next = 1'b1; + if (desc_table_invalid[desc_table_store_csum_ptr_reg & DESC_PTR_MASK]) begin + // invalid entry; skip + desc_table_store_csum_en = 1'b1; + + s_axis_rx_csum_ready_next = 1'b0; + end else if (s_axis_rx_csum_ready && s_axis_rx_csum_valid) begin + // update entry in descriptor table + desc_table_store_csum = s_axis_rx_csum; + desc_table_store_csum_en = 1'b1; + + s_axis_rx_csum_ready_next = 1'b0; + end + end + + // finish write data; start completion enqueue + if (desc_table_active[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] && + desc_table_cpl_enqueue_start_ptr_reg != desc_table_start_ptr_reg && + desc_table_cpl_enqueue_start_ptr_reg != desc_table_dequeue_start_ptr_reg && + (desc_table_cpl_enqueue_start_ptr_reg != desc_table_store_ptp_ts_ptr_reg || !PTP_TS_ENABLE) && + (desc_table_cpl_enqueue_start_ptr_reg != desc_table_store_hash_ptr_reg || !RX_HASH_ENABLE) && + (desc_table_cpl_enqueue_start_ptr_reg != desc_table_store_csum_ptr_reg || !RX_CHECKSUM_ENABLE)) begin + if (desc_table_invalid[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]) begin + // invalid entry; skip + desc_table_cpl_enqueue_start_en = 1'b1; + + // invalidate entry in packet table + pkt_table_finish_ptr = desc_table_pkt[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + pkt_table_finish_en = 1'b1; + + end else if (desc_table_data_written[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] && !m_axis_cpl_req_valid_next) begin + // update entry in descriptor table + desc_table_cpl_enqueue_start_en = 1'b1; + + // invalidate entry in packet table + pkt_table_finish_ptr = desc_table_pkt[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + pkt_table_finish_en = 1'b1; + + // initiate completion write + m_axis_cpl_req_queue_next = desc_table_cpl_queue[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_req_tag_next = desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK; + m_axis_cpl_req_data_next = 0; + m_axis_cpl_req_data_next[15:0] = desc_table_queue[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_req_data_next[31:16] = desc_table_queue_ptr[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_req_data_next[47:32] = desc_table_dma_len[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + if (PTP_TS_ENABLE) begin + //m_axis_cpl_req_data_next[127:64] = desc_table_ptp_ts[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] >> 16; + m_axis_cpl_req_data_next[111:64] = desc_table_ptp_ts[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] >> 16; + end + if (RX_HASH_ENABLE) begin + m_axis_cpl_req_data_next[159:128] = desc_table_hash[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_req_data_next[167:160] = desc_table_hash_type[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + end + if (RX_CHECKSUM_ENABLE) begin + m_axis_cpl_req_data_next[127:112] = desc_table_csum[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + end + m_axis_cpl_req_valid_next = 1'b1; + end + end + + // start completion write + // wait for queue query response + if (s_axis_cpl_req_status_valid) begin + // update entry in descriptor table + desc_table_cpl_write_done_ptr = s_axis_cpl_req_status_tag & DESC_PTR_MASK; + desc_table_cpl_write_done_en = 1'b1; + end + + // operation complete + if (desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] && desc_table_finish_ptr_reg != desc_table_start_ptr_reg && desc_table_finish_ptr_reg != desc_table_cpl_enqueue_start_ptr_reg) begin + if (desc_table_invalid[desc_table_finish_ptr_reg & DESC_PTR_MASK]) begin + // invalidate entry in descriptor table + desc_table_finish_en = 1'b1; + + // return receive request completion + m_axis_rx_req_status_len_next = 0; + m_axis_rx_req_status_tag_next = desc_table_tag[desc_table_finish_ptr_reg & DESC_PTR_MASK]; + m_axis_rx_req_status_valid_next = 1'b1; + end else if (desc_table_cpl_write_done[desc_table_finish_ptr_reg & DESC_PTR_MASK]) begin + // invalidate entry in descriptor table + desc_table_finish_en = 1'b1; + + // return receive request completion + m_axis_rx_req_status_len_next = desc_table_dma_len[desc_table_finish_ptr_reg & DESC_PTR_MASK]; + m_axis_rx_req_status_tag_next = desc_table_tag[desc_table_finish_ptr_reg & DESC_PTR_MASK]; + m_axis_rx_req_status_valid_next = 1'b1; + end + end +end + +always @(posedge clk) begin + s_axis_rx_req_ready_reg <= s_axis_rx_req_ready_next; + + m_axis_rx_req_status_len_reg <= m_axis_rx_req_status_len_next; + m_axis_rx_req_status_tag_reg <= m_axis_rx_req_status_tag_next; + m_axis_rx_req_status_valid_reg <= m_axis_rx_req_status_valid_next; + + m_axis_desc_req_queue_reg <= m_axis_desc_req_queue_next; + m_axis_desc_req_tag_reg <= m_axis_desc_req_tag_next; + m_axis_desc_req_valid_reg <= m_axis_desc_req_valid_next; + + s_axis_desc_tready_reg <= s_axis_desc_tready_next; + + m_axis_cpl_req_queue_reg <= m_axis_cpl_req_queue_next; + m_axis_cpl_req_tag_reg <= m_axis_cpl_req_tag_next; + m_axis_cpl_req_data_reg <= m_axis_cpl_req_data_next; + m_axis_cpl_req_valid_reg <= m_axis_cpl_req_valid_next; + + m_axis_dma_write_desc_dma_addr_reg <= m_axis_dma_write_desc_dma_addr_next; + m_axis_dma_write_desc_ram_addr_reg <= m_axis_dma_write_desc_ram_addr_next; + m_axis_dma_write_desc_len_reg <= m_axis_dma_write_desc_len_next; + m_axis_dma_write_desc_tag_reg <= m_axis_dma_write_desc_tag_next; + m_axis_dma_write_desc_valid_reg <= m_axis_dma_write_desc_valid_next; + + m_axis_rx_desc_addr_reg <= m_axis_rx_desc_addr_next; + m_axis_rx_desc_len_reg <= m_axis_rx_desc_len_next; + m_axis_rx_desc_tag_reg <= m_axis_rx_desc_tag_next; + m_axis_rx_desc_valid_reg <= m_axis_rx_desc_valid_next; + + s_axis_rx_ptp_ts_ready_reg <= s_axis_rx_ptp_ts_ready_next; + + s_axis_rx_hash_ready_reg <= s_axis_rx_hash_ready_next; + + s_axis_rx_csum_ready_reg <= s_axis_rx_csum_ready_next; + + desc_start_reg <= desc_start_next; + desc_done_reg <= desc_done_next; + desc_len_reg <= desc_len_next; + + // descriptor table operations + if (desc_table_start_en) begin + desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b1; + desc_table_rx_done[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_invalid[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_desc_fetched[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_data_written[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_cpl_write_done[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_queue[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_queue; + desc_table_tag[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_tag; + desc_table_pkt[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_pkt; + desc_table_start_ptr_reg <= desc_table_start_ptr_reg + 1; + end + + if (desc_table_rx_finish_en) begin + desc_table_dma_len[desc_table_rx_finish_ptr & DESC_PTR_MASK] <= desc_table_rx_finish_len; + desc_table_rx_done[desc_table_rx_finish_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_dequeue_start_en) begin + desc_table_dequeue_start_ptr_reg <= desc_table_dequeue_start_ptr_reg + 1; + end + + if (desc_table_dequeue_en) begin + desc_table_queue_ptr[desc_table_dequeue_ptr & DESC_PTR_MASK] <= desc_table_dequeue_queue_ptr; + desc_table_cpl_queue[desc_table_dequeue_ptr & DESC_PTR_MASK] <= desc_table_dequeue_cpl_queue; + if (desc_table_dequeue_invalid) begin + desc_table_invalid[desc_table_dequeue_ptr & DESC_PTR_MASK] <= 1'b1; + end + end + + if (desc_table_desc_fetched_en) begin + desc_table_desc_len[desc_table_desc_fetched_ptr & DESC_PTR_MASK] <= desc_table_desc_fetched_len; + desc_table_desc_fetched[desc_table_desc_fetched_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_data_written_en) begin + desc_table_data_written[desc_table_data_written_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_store_ptp_ts_en) begin + desc_table_ptp_ts[desc_table_store_ptp_ts_ptr_reg & DESC_PTR_MASK] <= desc_table_store_ptp_ts; + desc_table_store_ptp_ts_ptr_reg <= desc_table_store_ptp_ts_ptr_reg + 1; + end + + if (desc_table_store_hash_en) begin + desc_table_hash[desc_table_store_hash_ptr_reg & DESC_PTR_MASK] <= desc_table_store_hash; + desc_table_hash_type[desc_table_store_hash_ptr_reg & DESC_PTR_MASK] <= desc_table_store_hash_type; + desc_table_store_hash_ptr_reg <= desc_table_store_hash_ptr_reg + 1; + end + + if (desc_table_store_csum_en) begin + desc_table_csum[desc_table_store_csum_ptr_reg & DESC_PTR_MASK] <= desc_table_store_csum; + desc_table_store_csum_ptr_reg <= desc_table_store_csum_ptr_reg + 1; + end + + if (desc_table_cpl_enqueue_start_en) begin + desc_table_cpl_enqueue_start_ptr_reg <= desc_table_cpl_enqueue_start_ptr_reg + 1; + end + + if (desc_table_cpl_write_done_en) begin + desc_table_cpl_write_done[desc_table_cpl_write_done_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_finish_en) begin + desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_finish_ptr_reg <= desc_table_finish_ptr_reg + 1; + end + + if (desc_table_write_start_en) begin + desc_table_read_commit[desc_table_write_start_ptr] <= desc_table_write_start_commit; + if (desc_table_write_start_init) begin + desc_table_write_count_start[desc_table_write_start_ptr] <= desc_table_write_count_finish[desc_table_write_start_ptr] + 1; + end else begin + desc_table_write_count_start[desc_table_write_start_ptr] <= desc_table_write_count_start[desc_table_write_start_ptr] + 1; + end + end else if (desc_table_write_start_commit || desc_table_write_start_init) begin + desc_table_read_commit[desc_table_write_start_ptr] <= desc_table_write_start_commit; + if (desc_table_write_start_init) begin + desc_table_write_count_start[desc_table_write_start_ptr] <= desc_table_write_count_finish[desc_table_write_start_ptr]; + end + end + + if (desc_table_write_finish_en) begin + desc_table_write_count_finish[desc_table_write_finish_ptr] <= desc_table_write_count_finish[desc_table_write_finish_ptr] + 1; + end + + // packet table operations + if (pkt_table_start_en) begin + pkt_table_active[pkt_table_start_ptr] <= 1'b1; + end + + if (pkt_table_finish_en) begin + pkt_table_active[pkt_table_finish_ptr] <= 1'b0; + end + + if (rst) begin + s_axis_rx_req_ready_reg <= 1'b0; + m_axis_rx_req_status_valid_reg <= 1'b0; + m_axis_desc_req_valid_reg <= 1'b0; + s_axis_desc_tready_reg <= 1'b0; + m_axis_cpl_req_valid_reg <= 1'b0; + m_axis_dma_write_desc_valid_reg <= 1'b0; + m_axis_rx_desc_valid_reg <= 1'b0; + s_axis_rx_ptp_ts_ready_reg <= 1'b0; + s_axis_rx_hash_ready_reg <= 1'b0; + s_axis_rx_csum_ready_reg <= 1'b0; + + desc_start_reg <= 1'b1; + desc_done_reg <= 1'b0; + desc_len_reg <= 0; + + desc_table_active <= 0; + desc_table_invalid <= 0; + desc_table_desc_fetched <= 0; + desc_table_data_written <= 0; + desc_table_rx_done <= 0; + + desc_table_start_ptr_reg <= 0; + desc_table_dequeue_start_ptr_reg <= 0; + desc_table_store_ptp_ts_ptr_reg <= 0; + desc_table_store_hash_ptr_reg <= 0; + desc_table_store_csum_ptr_reg <= 0; + desc_table_cpl_enqueue_start_ptr_reg <= 0; + desc_table_finish_ptr_reg <= 0; + + pkt_table_active <= 0; + end +end + +endmodule diff --git a/corundum/rtl/rx_hash.v b/corundum/rtl/rx_hash.v new file mode 100644 index 0000000000000000000000000000000000000000..104f7f391348809a1550bbdf32f4cf01b36c4285 --- /dev/null +++ b/corundum/rtl/rx_hash.v @@ -0,0 +1,354 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Receive hashing module + */ +module rx_hash # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 256, + // AXI stream tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + input wire s_axis_tlast, + + /* + * Control + */ + input wire [40*8-1:0] hash_key, + + /* + * Hash output + */ + output wire [31:0] m_axis_hash, + output wire [3:0] m_axis_hash_type, + output wire m_axis_hash_valid +); + +parameter CYCLE_COUNT = (38+KEEP_WIDTH-1)/KEEP_WIDTH; + +parameter PTR_WIDTH = $clog2(CYCLE_COUNT); + +// bus width assertions +initial begin + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +/* + +TCP/UDP Frame (IPv4) + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x0800) 2 octets + Version (4) 4 bits + IHL (5-15) 4 bits + DSCP (0) 6 bits + ECN (0) 2 bits + length 2 octets + identification (0?) 2 octets + flags (010) 3 bits + fragment offset (0) 13 bits + time to live (64?) 1 octet + protocol (6 or 17) 1 octet + header checksum 2 octets + source IP 4 octets + destination IP 4 octets + options (IHL-5)*4 octets + + source port 2 octets + desination port 2 octets + other fields + payload + +TCP/UDP Frame (IPv6) + + Field Length + Destination MAC address 6 octets + Source MAC address 6 octets + Ethertype (0x86dd) 2 octets + Version (4) 4 bits + Traffic class 8 bits + Flow label 20 bits + length 2 octets + next header (6 or 17) 1 octet + hop limit 1 octet + source IP 16 octets + destination IP 16 octets + + source port 2 octets + desination port 2 octets + other fields + payload + +*/ + +reg active_reg = 1'b1, active_next; +reg [PTR_WIDTH-1:0] ptr_reg = 0, ptr_next; + +reg [15:0] eth_type_reg = 15'd0, eth_type_next; +reg [3:0] ihl_reg = 4'd0, ihl_next; + +reg [36*8-1:0] hash_data_reg = 0, hash_data_next; +reg hash_data_ipv4_reg = 1'b0, hash_data_ipv4_next; +reg hash_data_tcp_reg = 1'b0, hash_data_tcp_next; +reg hash_data_udp_reg = 1'b0, hash_data_udp_next; +reg [3:0] hash_data_type_reg = 4'b0000, hash_data_type_next; +reg hash_data_valid_reg = 0, hash_data_valid_next; + +reg [31:0] hash_part_ipv4_ip_reg = 32'd0; +reg [31:0] hash_part_ipv4_port_reg = 32'd0; +reg hash_part_ipv4_reg = 0; +reg hash_part_tcp_reg = 0; +reg hash_part_udp_reg = 0; +reg [3:0] hash_part_type_reg = 4'b0000; +reg hash_part_valid_reg = 0; + +reg [31:0] hash_reg = 32'd0; +reg [3:0] hash_type_reg = 4'b0000; +reg hash_valid_reg = 0; + +assign m_axis_hash = hash_reg; +assign m_axis_hash_type = hash_type_reg; +assign m_axis_hash_valid = hash_valid_reg; + +function [31:0] hash_toep(input [36*8-1:0] data, input [5:0] len, input [40*8-1:0] key); + integer i, j; + begin + hash_toep = 0; + for (i = 0; i < len; i = i + 1) begin + for (j = 0; j < 8; j = j + 1) begin + if (data[i*8 + (7-j)]) begin + hash_toep = hash_toep ^ key[40*8 - 32 - i*8 - j +: 32]; + end + end + end + end +endfunction + +// compute toeplitz hashes +wire [31:0] hash_part_ipv4_ip = hash_toep(hash_data_reg, 8, hash_key); +wire [31:0] hash_part_ipv4_port = hash_toep(hash_data_reg >> 8*8, 4, hash_key << 8*8); + +always @* begin + active_next = active_reg; + ptr_next = ptr_reg; + + eth_type_next = eth_type_reg; + ihl_next = ihl_reg; + + hash_data_next = hash_data_reg; + hash_data_ipv4_next = hash_data_ipv4_reg; + hash_data_tcp_next = hash_data_tcp_reg; + hash_data_udp_next = hash_data_udp_reg; + hash_data_type_next = hash_data_type_reg; + hash_data_valid_next = 1'b0; + + if (s_axis_tvalid) begin + if (active_reg) begin + ptr_next = ptr_reg + 1; + + if (ptr_reg == 0) begin + hash_data_ipv4_next = 1'b0; + hash_data_tcp_next = 1'b0; + hash_data_udp_next = 1'b0; + end + if (ptr_reg == 12/KEEP_WIDTH) begin + // eth type MSB + eth_type_next[15:8] = s_axis_tdata[(12%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 13/KEEP_WIDTH) begin + // eth type LSB + eth_type_next[7:0] = s_axis_tdata[(13%KEEP_WIDTH)*8 +: 8]; + + // check eth type + if (eth_type_next == 16'h0800) begin + // ipv4 + hash_data_ipv4_next = 1'b1; + end else if (eth_type_next == 16'h86dd) begin + // ipv6 + // TODO + end else begin + // other + hash_data_type_next = 4'b0000; + hash_data_valid_next = 1'b1; + active_next = 1'b0; + end + end + if (ptr_reg == 14/KEEP_WIDTH) begin + // capture IHL + ihl_next = s_axis_tdata[(14%KEEP_WIDTH)*8 +: 8]; + end + if (hash_data_ipv4_next) begin + if (ptr_reg == 23/KEEP_WIDTH) begin + // capture protocol + if (s_axis_tdata[(23%KEEP_WIDTH)*8 +: 8] == 8'h06 && ihl_next == 5) begin + // TCP + hash_data_tcp_next = 1'b1; + end else if (s_axis_tdata[(23%KEEP_WIDTH)*8 +: 8] == 8'h11 && ihl_next == 5) begin + // UDP + hash_data_udp_next = 1'b1; + end + end + if (ptr_reg == 26/KEEP_WIDTH) begin + // capture source IP + hash_data_next[7:0] = s_axis_tdata[(26%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 27/KEEP_WIDTH) begin + // capture source IP + hash_data_next[15:8] = s_axis_tdata[(27%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 28/KEEP_WIDTH) begin + // capture source IP + hash_data_next[23:16] = s_axis_tdata[(28%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 29/KEEP_WIDTH) begin + // capture source IP + hash_data_next[31:24] = s_axis_tdata[(29%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 30/KEEP_WIDTH) begin + // capture dest IP + hash_data_next[39:32] = s_axis_tdata[(30%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 31/KEEP_WIDTH) begin + // capture dest IP + hash_data_next[47:40] = s_axis_tdata[(31%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 32/KEEP_WIDTH) begin + // capture dest IP + hash_data_next[55:48] = s_axis_tdata[(32%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 33/KEEP_WIDTH) begin + // capture dest IP + hash_data_next[63:56] = s_axis_tdata[(33%KEEP_WIDTH)*8 +: 8]; + if (!(hash_data_tcp_next || hash_data_udp_next)) begin + hash_data_type_next = {1'b0, 1'b0, 1'b0, 1'b1}; + hash_data_valid_next = 1'b1; + active_next = 1'b0; + end + end + if (hash_data_tcp_next || hash_data_udp_next) begin + // TODO IHL (skip options) + if (ptr_reg == 34/KEEP_WIDTH) begin + // capture source port + hash_data_next[71:64] = s_axis_tdata[(34%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 35/KEEP_WIDTH) begin + // capture source port + hash_data_next[79:72] = s_axis_tdata[(35%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 36/KEEP_WIDTH) begin + // capture dest port + hash_data_next[87:80] = s_axis_tdata[(36%KEEP_WIDTH)*8 +: 8]; + end + if (ptr_reg == 37/KEEP_WIDTH) begin + // capture dest port + hash_data_next[95:88] = s_axis_tdata[(37%KEEP_WIDTH)*8 +: 8]; + hash_data_type_next = {hash_data_udp_next, hash_data_tcp_next, 1'b0, 1'b1}; + hash_data_valid_next = 1'b1; + active_next = 1'b0; + end + end + end + end + + if (s_axis_tlast) begin + if (active_next) begin + hash_data_type_next = 4'b0000; + hash_data_valid_next = 1'b1; + end + ptr_next = 0; + active_next = 1'b1; + end + end +end + +always @(posedge clk) begin + active_reg <= active_next; + ptr_reg <= ptr_next; + + eth_type_reg <= eth_type_next; + ihl_reg <= ihl_next; + + hash_data_reg <= hash_data_next; + hash_data_ipv4_reg <= hash_data_ipv4_next; + hash_data_tcp_reg <= hash_data_tcp_next; + hash_data_udp_reg <= hash_data_udp_next; + hash_data_type_reg <= hash_data_type_next; + hash_data_valid_reg <= hash_data_valid_next; + + hash_part_ipv4_ip_reg <= hash_part_ipv4_ip; + hash_part_ipv4_port_reg <= hash_part_ipv4_port; + hash_part_ipv4_reg <= hash_data_ipv4_reg; + hash_part_tcp_reg <= hash_data_tcp_reg; + hash_part_udp_reg <= hash_data_udp_reg; + hash_part_type_reg <= hash_data_type_reg; + hash_part_valid_reg <= hash_data_valid_reg; + + if (hash_part_ipv4_reg) begin + if (hash_part_tcp_reg || hash_part_udp_reg) begin + hash_reg <= hash_part_ipv4_ip_reg ^ hash_part_ipv4_port_reg; + end else begin + hash_reg <= hash_part_ipv4_ip_reg; + end + end else begin + hash_reg <= 0; + end + hash_type_reg <= hash_part_type_reg; + hash_valid_reg <= hash_part_valid_reg; + + if (rst) begin + active_reg <= 1'b1; + ptr_reg <= 0; + hash_data_valid_reg <= 0; + end +end + +endmodule diff --git a/corundum/rtl/tdma_ber.v b/corundum/rtl/tdma_ber.v new file mode 100644 index 0000000000000000000000000000000000000000..5b92d612d55232d1f9a7db6edcfe8239d9e856ad --- /dev/null +++ b/corundum/rtl/tdma_ber.v @@ -0,0 +1,461 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * TDMA BER module + */ +module tdma_ber # +( + // Channel count + parameter COUNT = 1, + // Timeslot index width + parameter INDEX_WIDTH = 6, + // Slice index width + parameter SLICE_WIDTH = 5, + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = INDEX_WIDTH+4+1+$clog2(COUNT), + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // Schedule absolute PTP start time, seconds part + parameter SCHEDULE_START_S = 48'h0, + // Schedule absolute PTP start time, nanoseconds part + parameter SCHEDULE_START_NS = 30'h0, + // Schedule period, seconds part + parameter SCHEDULE_PERIOD_S = 48'd0, + // Schedule period, nanoseconds part + parameter SCHEDULE_PERIOD_NS = 30'd1000000, + // Timeslot period, seconds part + parameter TIMESLOT_PERIOD_S = 48'd0, + // Timeslot period, nanoseconds part + parameter TIMESLOT_PERIOD_NS = 30'd100000, + // Timeslot active period, seconds part + parameter ACTIVE_PERIOD_S = 48'd0, + // Timeslot active period, nanoseconds part + parameter ACTIVE_PERIOD_NS = 30'd100000 +) +( + input wire clk, + input wire rst, + + /* + * PHY connections + */ + input wire [COUNT-1:0] phy_tx_clk, + input wire [COUNT-1:0] phy_rx_clk, + input wire [COUNT*7-1:0] phy_rx_error_count, + output wire [COUNT-1:0] phy_tx_prbs31_enable, + output wire [COUNT-1:0] phy_rx_prbs31_enable, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * PTP clock + */ + input wire [95:0] ptp_ts_96, + input wire ptp_ts_step +); + +// check configuration +initial begin + if (AXIL_ADDR_WIDTH < INDEX_WIDTH+4+1+$clog2(COUNT)) begin + $error("Error: AXI address width too narrow (instance %m)"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI data width must be 32 (instance %m)"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +wire [AXIL_ADDR_WIDTH-1:0] axil_csr_awaddr; +wire [2:0] axil_csr_awprot; +wire axil_csr_awvalid; +wire axil_csr_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_csr_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_csr_wstrb; +wire axil_csr_wvalid; +wire axil_csr_wready; +wire [1:0] axil_csr_bresp; +wire axil_csr_bvalid; +wire axil_csr_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_csr_araddr; +wire [2:0] axil_csr_arprot; +wire axil_csr_arvalid; +wire axil_csr_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_csr_rdata; +wire [1:0] axil_csr_rresp; +wire axil_csr_rvalid; +wire axil_csr_rready; + +// control registers +reg axil_csr_awready_reg = 1'b0; +reg axil_csr_wready_reg = 1'b0; +reg axil_csr_bvalid_reg = 1'b0; +reg axil_csr_arready_reg = 1'b0; +reg [AXIL_DATA_WIDTH-1:0] axil_csr_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}; +reg axil_csr_rvalid_reg = 1'b0; + +reg tdma_enable_reg = 1'b0; +wire tdma_locked; +wire tdma_error; + +reg [79:0] set_tdma_schedule_start_reg = 0; +reg set_tdma_schedule_start_valid_reg = 0; +reg [79:0] set_tdma_schedule_period_reg = 0; +reg set_tdma_schedule_period_valid_reg = 0; +reg [79:0] set_tdma_timeslot_period_reg = 0; +reg set_tdma_timeslot_period_valid_reg = 0; +reg [79:0] set_tdma_active_period_reg = 0; +reg set_tdma_active_period_valid_reg = 0; + +wire tdma_schedule_start; +wire [INDEX_WIDTH-1:0] tdma_timeslot_index; +wire tdma_timeslot_start; +wire tdma_timeslot_end; +wire tdma_timeslot_active; + +assign axil_csr_awready = axil_csr_awready_reg; +assign axil_csr_wready = axil_csr_wready_reg; +assign axil_csr_bresp = 2'b00; +assign axil_csr_bvalid = axil_csr_bvalid_reg; +assign axil_csr_arready = axil_csr_arready_reg; +assign axil_csr_rdata = axil_csr_rdata_reg; +assign axil_csr_rresp = 2'b00; +assign axil_csr_rvalid = axil_csr_rvalid_reg; + +always @(posedge clk) begin + axil_csr_awready_reg <= 1'b0; + axil_csr_wready_reg <= 1'b0; + axil_csr_bvalid_reg <= axil_csr_bvalid_reg && !axil_csr_bready; + axil_csr_arready_reg <= 1'b0; + axil_csr_rvalid_reg <= axil_csr_rvalid_reg && !axil_csr_rready; + + set_tdma_schedule_start_valid_reg <= 1'b0; + set_tdma_schedule_period_valid_reg <= 1'b0; + set_tdma_timeslot_period_valid_reg <= 1'b0; + set_tdma_active_period_valid_reg <= 1'b0; + + if (axil_csr_awvalid && axil_csr_wvalid && !axil_csr_bvalid) begin + // write operation + axil_csr_awready_reg <= 1'b1; + axil_csr_wready_reg <= 1'b1; + axil_csr_bvalid_reg <= 1'b1; + + case (axil_csr_awaddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0100: begin + // TDMA control + tdma_enable_reg <= axil_csr_wdata[0]; + end + 16'h0114: set_tdma_schedule_start_reg[29:0] <= axil_csr_wdata; // TDMA schedule start ns + 16'h0118: set_tdma_schedule_start_reg[63:32] <= axil_csr_wdata; // TDMA schedule start sec l + 16'h011C: begin + // TDMA schedule start sec h + set_tdma_schedule_start_reg[79:64] <= axil_csr_wdata; + set_tdma_schedule_start_valid_reg <= 1'b1; + end + 16'h0124: set_tdma_schedule_period_reg[29:0] <= axil_csr_wdata; // TDMA schedule period ns + 16'h0128: set_tdma_schedule_period_reg[63:32] <= axil_csr_wdata; // TDMA schedule period sec l + 16'h012C: begin + // TDMA schedule period sec h + set_tdma_schedule_period_reg[79:64] <= axil_csr_wdata; + set_tdma_schedule_period_valid_reg <= 1'b1; + end + 16'h0134: set_tdma_timeslot_period_reg[29:0] <= axil_csr_wdata; // TDMA timeslot period ns + 16'h0138: set_tdma_timeslot_period_reg[63:32] <= axil_csr_wdata; // TDMA timeslot period sec l + 16'h013C: begin + // TDMA timeslot period sec h + set_tdma_timeslot_period_reg[79:64] <= axil_csr_wdata; + set_tdma_timeslot_period_valid_reg <= 1'b1; + end + 16'h0144: set_tdma_active_period_reg[29:0] <= axil_csr_wdata; // TDMA active period ns + 16'h0148: set_tdma_active_period_reg[63:32] <= axil_csr_wdata; // TDMA active period sec l + 16'h014C: begin + // TDMA active period sec h + set_tdma_active_period_reg[79:64] <= axil_csr_wdata; + set_tdma_active_period_valid_reg <= 1'b1; + end + endcase + end + + if (axil_csr_arvalid && !axil_csr_rvalid) begin + // read operation + axil_csr_arready_reg <= 1'b1; + axil_csr_rvalid_reg <= 1'b1; + axil_csr_rdata_reg <= {AXIL_DATA_WIDTH{1'b0}}; + + case (axil_csr_araddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0000: axil_csr_rdata_reg <= 0; + 16'h0010: axil_csr_rdata_reg <= COUNT; + 16'h0014: axil_csr_rdata_reg <= INDEX_WIDTH; + 16'h0018: axil_csr_rdata_reg <= SLICE_WIDTH; + 16'h0100: begin + // TDMA control + axil_csr_rdata_reg[0] <= tdma_enable_reg; + end + 16'h0104: begin + // TDMA status + axil_csr_rdata_reg[0] <= tdma_locked; + axil_csr_rdata_reg[1] <= tdma_error; + end + 16'h0114: axil_csr_rdata_reg <= set_tdma_schedule_start_reg[29:0]; // TDMA schedule start ns + 16'h0118: axil_csr_rdata_reg <= set_tdma_schedule_start_reg[63:32]; // TDMA schedule start sec l + 16'h011C: axil_csr_rdata_reg <= set_tdma_schedule_start_reg[79:64]; // TDMA schedule start sec h + 16'h0124: axil_csr_rdata_reg <= set_tdma_schedule_period_reg[29:0]; // TDMA schedule period ns + 16'h0128: axil_csr_rdata_reg <= set_tdma_schedule_period_reg[63:32]; // TDMA schedule period sec l + 16'h012C: axil_csr_rdata_reg <= set_tdma_schedule_period_reg[79:64]; // TDMA schedule period sec h + 16'h0134: axil_csr_rdata_reg <= set_tdma_timeslot_period_reg[29:0]; // TDMA timeslot period ns + 16'h0138: axil_csr_rdata_reg <= set_tdma_timeslot_period_reg[63:32]; // TDMA timeslot period sec l + 16'h013C: axil_csr_rdata_reg <= set_tdma_timeslot_period_reg[79:64]; // TDMA timeslot period sec h + 16'h0144: axil_csr_rdata_reg <= set_tdma_active_period_reg[29:0]; // TDMA active period ns + 16'h0148: axil_csr_rdata_reg <= set_tdma_active_period_reg[63:32]; // TDMA active period sec l + 16'h014C: axil_csr_rdata_reg <= set_tdma_active_period_reg[79:64]; // TDMA active period sec h + endcase + end + + if (rst) begin + axil_csr_awready_reg <= 1'b0; + axil_csr_wready_reg <= 1'b0; + axil_csr_bvalid_reg <= 1'b0; + axil_csr_arready_reg <= 1'b0; + axil_csr_rvalid_reg <= 1'b0; + + tdma_enable_reg <= 1'b0; + end +end + +tdma_scheduler #( + .INDEX_WIDTH(INDEX_WIDTH), + .SCHEDULE_START_S(SCHEDULE_START_S), + .SCHEDULE_START_NS(SCHEDULE_START_NS), + .SCHEDULE_PERIOD_S(SCHEDULE_PERIOD_S), + .SCHEDULE_PERIOD_NS(SCHEDULE_PERIOD_NS), + .TIMESLOT_PERIOD_S(TIMESLOT_PERIOD_S), + .TIMESLOT_PERIOD_NS(TIMESLOT_PERIOD_NS), + .ACTIVE_PERIOD_S(ACTIVE_PERIOD_S), + .ACTIVE_PERIOD_NS(ACTIVE_PERIOD_NS) +) +tdma_scheduler_inst ( + .clk(clk), + .rst(rst), + .input_ts_96(ptp_ts_96), + .input_ts_step(ptp_ts_step), + .enable(tdma_enable_reg), + .input_schedule_start(set_tdma_schedule_start_reg), + .input_schedule_start_valid(set_tdma_schedule_start_valid_reg), + .input_schedule_period(set_tdma_schedule_period_reg), + .input_schedule_period_valid(set_tdma_schedule_period_valid_reg), + .input_timeslot_period(set_tdma_timeslot_period_reg), + .input_timeslot_period_valid(set_tdma_timeslot_period_valid_reg), + .input_active_period(set_tdma_active_period_reg), + .input_active_period_valid(set_tdma_active_period_valid_reg), + .locked(tdma_locked), + .error(tdma_error), + .schedule_start(tdma_schedule_start), + .timeslot_index(tdma_timeslot_index), + .timeslot_start(tdma_timeslot_start), + .timeslot_end(tdma_timeslot_end), + .timeslot_active(tdma_timeslot_active) +); + +wire [COUNT*AXIL_ADDR_WIDTH-1:0] axil_ch_awaddr; +wire [COUNT*3-1:0] axil_ch_awprot; +wire [COUNT-1:0] axil_ch_awvalid; +wire [COUNT-1:0] axil_ch_awready; +wire [COUNT*AXIL_DATA_WIDTH-1:0] axil_ch_wdata; +wire [COUNT*AXIL_STRB_WIDTH-1:0] axil_ch_wstrb; +wire [COUNT-1:0] axil_ch_wvalid; +wire [COUNT-1:0] axil_ch_wready; +wire [COUNT*2-1:0] axil_ch_bresp; +wire [COUNT-1:0] axil_ch_bvalid; +wire [COUNT-1:0] axil_ch_bready; +wire [COUNT*AXIL_ADDR_WIDTH-1:0] axil_ch_araddr; +wire [COUNT*3-1:0] axil_ch_arprot; +wire [COUNT-1:0] axil_ch_arvalid; +wire [COUNT-1:0] axil_ch_arready; +wire [COUNT*AXIL_DATA_WIDTH-1:0] axil_ch_rdata; +wire [COUNT*2-1:0] axil_ch_rresp; +wire [COUNT-1:0] axil_ch_rvalid; +wire [COUNT-1:0] axil_ch_rready; + +parameter CH_ADDR_WIDTH = INDEX_WIDTH+4; +parameter CH_BASE_ADDR_WIDTH = (COUNT+1)*AXIL_ADDR_WIDTH; +parameter CH_BASE_ADDR = calcBaseAddrs(CH_ADDR_WIDTH); + +function [CH_BASE_ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] width); + integer i; + begin + calcBaseAddrs = {CH_BASE_ADDR_WIDTH{1'b0}}; + for (i = 0; i < COUNT+1; i = i + 1) begin + calcBaseAddrs[i * AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH] = i * (2**width); + end + end +endfunction + +function [31:0] w_32(input [31:0] val); + w_32 = val; +endfunction + +axil_interconnect #( + .DATA_WIDTH(AXIL_DATA_WIDTH), + .ADDR_WIDTH(AXIL_ADDR_WIDTH), + .S_COUNT(1), + .M_COUNT(COUNT+1), + .M_BASE_ADDR(CH_BASE_ADDR), + .M_ADDR_WIDTH({COUNT+1{w_32(CH_ADDR_WIDTH)}}), + .M_CONNECT_READ({COUNT+1{1'b1}}), + .M_CONNECT_WRITE({COUNT+1{1'b1}}) +) +axil_csr_interconnect_inst ( + .clk(clk), + .rst(rst), + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + .m_axil_awaddr( {axil_ch_awaddr, axil_csr_awaddr}), + .m_axil_awprot( {axil_ch_awprot, axil_csr_awprot}), + .m_axil_awvalid( {axil_ch_awvalid, axil_csr_awvalid}), + .m_axil_awready( {axil_ch_awready, axil_csr_awready}), + .m_axil_wdata( {axil_ch_wdata, axil_csr_wdata}), + .m_axil_wstrb( {axil_ch_wstrb, axil_csr_wstrb}), + .m_axil_wvalid( {axil_ch_wvalid, axil_csr_wvalid}), + .m_axil_wready( {axil_ch_wready, axil_csr_wready}), + .m_axil_bresp( {axil_ch_bresp, axil_csr_bresp}), + .m_axil_bvalid( {axil_ch_bvalid, axil_csr_bvalid}), + .m_axil_bready( {axil_ch_bready, axil_csr_bready}), + .m_axil_araddr( {axil_ch_araddr, axil_csr_araddr}), + .m_axil_arprot( {axil_ch_arprot, axil_csr_arprot}), + .m_axil_arvalid( {axil_ch_arvalid, axil_csr_arvalid}), + .m_axil_arready( {axil_ch_arready, axil_csr_arready}), + .m_axil_rdata( {axil_ch_rdata, axil_csr_rdata}), + .m_axil_rresp( {axil_ch_rresp, axil_csr_rresp}), + .m_axil_rvalid( {axil_ch_rvalid, axil_csr_rvalid}), + .m_axil_rready( {axil_ch_rready, axil_csr_rready}) +); + +generate + genvar n; + + for (n = 0; n < COUNT; n = n + 1) begin + + tdma_ber_ch #( + .INDEX_WIDTH(INDEX_WIDTH), + .SLICE_WIDTH(SLICE_WIDTH), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(CH_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) + ) + tdma_ber_ch_inst ( + .clk(clk), + .rst(rst), + .phy_tx_clk(phy_tx_clk[n]), + .phy_rx_clk(phy_rx_clk[n]), + .phy_rx_error_count(phy_rx_error_count[n*7 +: 7]), + .phy_tx_prbs31_enable(phy_tx_prbs31_enable[n]), + .phy_rx_prbs31_enable(phy_rx_prbs31_enable[n]), + .s_axil_awaddr(axil_ch_awaddr[n*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_awprot(axil_ch_awprot[n*3 +: 3]), + .s_axil_awvalid(axil_ch_awvalid[n]), + .s_axil_awready(axil_ch_awready[n]), + .s_axil_wdata(axil_ch_wdata[n*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_wstrb(axil_ch_wstrb[n*AXIL_STRB_WIDTH +: AXIL_STRB_WIDTH]), + .s_axil_wvalid(axil_ch_wvalid[n]), + .s_axil_wready(axil_ch_wready[n]), + .s_axil_bresp(axil_ch_bresp[n*2 +: 2]), + .s_axil_bvalid(axil_ch_bvalid[n]), + .s_axil_bready(axil_ch_bready[n]), + .s_axil_araddr(axil_ch_araddr[n*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_arprot(axil_ch_arprot[n*3 +: 3]), + .s_axil_arvalid(axil_ch_arvalid[n]), + .s_axil_arready(axil_ch_arready[n]), + .s_axil_rdata(axil_ch_rdata[n*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_rresp(axil_ch_rresp[n*2 +: 2]), + .s_axil_rvalid(axil_ch_rvalid[n]), + .s_axil_rready(axil_ch_rready[n]), + .tdma_timeslot_index(tdma_timeslot_index), + .tdma_timeslot_start(tdma_timeslot_start), + .tdma_timeslot_active(tdma_timeslot_active) + ); + + end +endgenerate + +endmodule diff --git a/corundum/rtl/tdma_ber_ch.v b/corundum/rtl/tdma_ber_ch.v new file mode 100644 index 0000000000000000000000000000000000000000..1bd88044a79d27d67ea533b6c2a41ee68fb669c8 --- /dev/null +++ b/corundum/rtl/tdma_ber_ch.v @@ -0,0 +1,590 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * TDMA BER module + */ +module tdma_ber_ch # +( + // Timeslot index width + parameter INDEX_WIDTH = 6, + // Slice index width + parameter SLICE_WIDTH = 5, + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = INDEX_WIDTH+4, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * PHY connections + */ + input wire phy_tx_clk, + input wire phy_rx_clk, + input wire [6:0] phy_rx_error_count, + output wire phy_tx_prbs31_enable, + output wire phy_rx_prbs31_enable, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * TDMA schedule + */ + input wire [INDEX_WIDTH-1:0] tdma_timeslot_index, + input wire tdma_timeslot_start, + input wire tdma_timeslot_active +); + +parameter VALID_ADDR_WIDTH = AXIL_ADDR_WIDTH - $clog2(AXIL_STRB_WIDTH); +parameter WORD_WIDTH = AXIL_STRB_WIDTH; +parameter WORD_SIZE = AXIL_DATA_WIDTH/WORD_WIDTH; + +// check configuration +initial begin + if (AXIL_ADDR_WIDTH < INDEX_WIDTH+4) begin + $error("Error: AXI address width too narrow (instance %m)"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI data width must be 32 (instance %m)"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +reg tx_prbs31_enable_reg = 1'b0, tx_prbs31_enable_next; +reg rx_prbs31_enable_reg = 1'b0, rx_prbs31_enable_next; + +// PHY TX BER interface +reg phy_tx_prbs31_enable_reg = 1'b0; + +always @(posedge phy_tx_clk) begin + phy_tx_prbs31_enable_reg <= tx_prbs31_enable_reg; +end + +assign phy_tx_prbs31_enable = phy_tx_prbs31_enable_reg; + +// PHY RX BER interface +reg phy_rx_prbs31_enable_reg = 1'b0; + +// accumulate errors, dump every 16 cycles +reg [10:0] phy_rx_error_count_reg = 0; +reg [10:0] phy_rx_error_count_acc_reg = 0; +reg [3:0] phy_rx_count_reg = 4'd0; +reg phy_rx_flag_reg = 1'b0; + +always @(posedge phy_rx_clk) begin + phy_rx_prbs31_enable_reg <= rx_prbs31_enable_reg; + + phy_rx_count_reg <= phy_rx_count_reg + 1; + + if (phy_rx_count_reg == 0) begin + phy_rx_error_count_reg <= phy_rx_error_count_acc_reg; + phy_rx_error_count_acc_reg <= phy_rx_error_count; + phy_rx_flag_reg <= !phy_rx_flag_reg; + end else begin + phy_rx_error_count_acc_reg <= phy_rx_error_count_acc_reg + phy_rx_error_count; + end +end + +assign phy_rx_prbs31_enable = phy_rx_prbs31_enable_reg; + +// synchronize dumped counts to control clock domain +reg rx_flag_sync_reg_1 = 1'b0; +reg rx_flag_sync_reg_2 = 1'b0; +reg rx_flag_sync_reg_3 = 1'b0; + +always @(posedge clk) begin + rx_flag_sync_reg_1 <= phy_rx_flag_reg; + rx_flag_sync_reg_2 <= rx_flag_sync_reg_1; + rx_flag_sync_reg_3 <= rx_flag_sync_reg_2; +end + +reg [31:0] cycle_count_reg = 32'd0, cycle_count_next; +reg [31:0] update_count_reg = 32'd0, update_count_next; +reg [31:0] rx_error_count_reg = 32'd0, rx_error_count_next; + +reg [31:0] atomic_cycle_count_reg = 32'd0, atomic_cycle_count_next; +reg [31:0] atomic_update_count_reg = 32'd0, atomic_update_count_next; +reg [31:0] atomic_rx_error_count_reg = 32'd0, atomic_rx_error_count_next; + +reg accumulate_enable_reg = 1'b0, accumulate_enable_next; +reg slice_enable_reg = 1'b0, slice_enable_next; + +reg [31:0] slice_time_reg = 0, slice_time_next; +reg [31:0] slice_offset_reg = 0, slice_offset_next; + +reg [SLICE_WIDTH-1:0] slice_select_reg = 0, slice_select_next; + +reg error_count_mem_read_reg = 0; +reg [31:0] error_count_mem_read_data_reg = 0; +reg update_count_mem_read_reg = 0; +reg [31:0] update_count_mem_read_data_reg = 0; + +reg [31:0] error_count_mem[(2**(INDEX_WIDTH+SLICE_WIDTH))-1:0]; +reg [31:0] update_count_mem[(2**(INDEX_WIDTH+SLICE_WIDTH))-1:0]; + +integer i; + +initial begin + for (i = 0; i < 2**(INDEX_WIDTH+SLICE_WIDTH); i = i + 1) begin + error_count_mem[i] = 0; + update_count_mem[i] = 0; + end +end + +reg [10:0] phy_rx_error_count_sync_reg = 0; +reg phy_rx_error_count_sync_valid_reg = 1'b0; + +always @(posedge clk) begin + phy_rx_error_count_sync_valid_reg <= 1'b0; + if (rx_flag_sync_reg_2 ^ rx_flag_sync_reg_3) begin + phy_rx_error_count_sync_reg <= phy_rx_error_count_reg; + phy_rx_error_count_sync_valid_reg <= 1'b1; + end +end + +reg slice_running_reg = 1'b0; +reg slice_active_reg = 1'b0; +reg [31:0] slice_count_reg = 0; +reg [SLICE_WIDTH-1:0] cur_slice_reg = 0; + +reg [1:0] accumulate_state_reg = 0; +reg [31:0] rx_ts_update_count_read_reg = 0; +reg [31:0] rx_ts_error_count_read_reg = 0; +reg [31:0] rx_ts_update_count_reg = 0; +reg [31:0] rx_ts_error_count_reg = 0; +reg [INDEX_WIDTH+SLICE_WIDTH-1:0] index_reg = 0; + +always @(posedge clk) begin + if (tdma_timeslot_start) begin + slice_running_reg <= 1'b1; + if (slice_offset_reg) begin + slice_active_reg <= 1'b0; + slice_count_reg <= slice_offset_reg; + end else begin + slice_active_reg <= 1'b1; + slice_count_reg <= slice_time_reg; + end + cur_slice_reg <= 0; + end else if (slice_count_reg > 0) begin + slice_count_reg <= slice_count_reg - 1; + end else begin + slice_count_reg <= slice_time_reg; + slice_active_reg <= slice_running_reg; + if (slice_active_reg && slice_running_reg) begin + cur_slice_reg <= cur_slice_reg + 1; + slice_running_reg <= cur_slice_reg < {SLICE_WIDTH{1'b1}}; + slice_active_reg <= cur_slice_reg < {SLICE_WIDTH{1'b1}}; + end + end + + case (accumulate_state_reg) + 2'd0: begin + if (accumulate_enable_reg && tdma_timeslot_active && phy_rx_error_count_sync_valid_reg) begin + if (slice_enable_reg) begin + index_reg <= (tdma_timeslot_index << SLICE_WIDTH) + cur_slice_reg; + if (slice_active_reg) begin + accumulate_state_reg <= 2'd1; + end + end else begin + index_reg <= (tdma_timeslot_index << SLICE_WIDTH); + accumulate_state_reg <= 2'd1; + end + end + end + 2'd1: begin + rx_ts_update_count_read_reg <= update_count_mem[index_reg]; + rx_ts_error_count_read_reg <= error_count_mem[index_reg]; + + accumulate_state_reg <= 2'd2; + end + 2'd2: begin + rx_ts_error_count_reg <= rx_ts_error_count_read_reg + phy_rx_error_count_sync_reg; + rx_ts_update_count_reg <= rx_ts_update_count_read_reg + 1; + + // if ((rx_ts_error_count_reg + inc_reg) >> 32) begin + // rx_ts_error_count_reg <= 32'hffffffff; + // end else begin + // rx_ts_error_count_reg <= rx_ts_error_count_reg + inc_reg; + // end + + // if ((rx_ts_update_count_reg + 1) >> 32) begin + // rx_ts_update_count_reg <= 32'hffffffff; + // end else begin + // rx_ts_update_count_reg <= rx_ts_update_count_reg + 1; + // end + + accumulate_state_reg <= 2'd3; + end + 2'd3: begin + update_count_mem[index_reg] <= rx_ts_update_count_reg; + error_count_mem[index_reg] <= rx_ts_error_count_reg; + + accumulate_state_reg <= 2'd0; + end + default: begin + accumulate_state_reg <= 2'd0; + end + endcase +end + +// control registers +reg read_eligible; +reg write_eligible; + +reg mem_wr_en; +reg mem_rd_en; + +reg last_read_reg = 1'b0, last_read_next; + +reg s_axil_awready_reg = 1'b0, s_axil_awready_next; +reg s_axil_wready_reg = 1'b0, s_axil_wready_next; +reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next; +reg s_axil_arready_reg = 1'b0, s_axil_arready_next; +reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}, s_axil_rdata_next; +reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = 2'b00; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = error_count_mem_read_reg ? error_count_mem_read_data_reg : (update_count_mem_read_reg ? update_count_mem_read_data_reg : s_axil_rdata_reg); +assign s_axil_rresp = 2'b00; +assign s_axil_rvalid = s_axil_rvalid_reg; + +wire [INDEX_WIDTH+SLICE_WIDTH-1:0] axil_ram_addr = ((mem_rd_en ? s_axil_araddr[INDEX_WIDTH+1+2-1:1+2] : s_axil_awaddr[INDEX_WIDTH+1+2-1:1+2]) << SLICE_WIDTH) | slice_select_reg; + +always @* begin + mem_wr_en = 1'b0; + mem_rd_en = 1'b0; + + last_read_next = last_read_reg; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + s_axil_arready_next = 1'b0; + s_axil_rdata_next = s_axil_rdata_reg; + s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready; + + cycle_count_next = cycle_count_reg + 1; + + update_count_next = update_count_reg; + rx_error_count_next = rx_error_count_reg; + if (phy_rx_error_count_sync_valid_reg) begin + update_count_next = update_count_reg + 1; + rx_error_count_next = phy_rx_error_count_sync_reg + rx_error_count_reg; + end + + atomic_cycle_count_next = atomic_cycle_count_reg + 1; + if (atomic_cycle_count_reg[31] && ~atomic_cycle_count_next[31]) begin + atomic_cycle_count_next = 32'hffffffff; + end + + atomic_update_count_next = atomic_update_count_reg; + atomic_rx_error_count_next = atomic_rx_error_count_reg; + if (phy_rx_error_count_sync_valid_reg) begin + atomic_update_count_next = atomic_update_count_reg + 1; + atomic_rx_error_count_next = phy_rx_error_count_sync_reg + atomic_rx_error_count_reg; + + // saturate + if (atomic_update_count_reg[31] && ~atomic_update_count_next[31]) begin + atomic_update_count_next = 32'hffffffff; + end + if (atomic_rx_error_count_reg[31] && ~atomic_rx_error_count_next[31]) begin + atomic_rx_error_count_next = 32'hffffffff; + end + end + + accumulate_enable_next = accumulate_enable_reg; + slice_enable_next = slice_enable_reg; + + slice_time_next = slice_time_reg; + slice_offset_next = slice_offset_reg; + + slice_select_next = slice_select_reg; + + tx_prbs31_enable_next = tx_prbs31_enable_reg; + rx_prbs31_enable_next = rx_prbs31_enable_reg; + + write_eligible = s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && (!s_axil_awready && !s_axil_wready); + read_eligible = s_axil_arvalid && (!s_axil_rvalid || s_axil_rready) && (!s_axil_arready); + + if (write_eligible && (!read_eligible || last_read_reg)) begin + last_read_next = 1'b0; + + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + s_axil_bvalid_next = 1'b1; + + if (s_axil_awaddr[INDEX_WIDTH+2+1+1-1]) begin + mem_wr_en = 1'b1; + end else begin + case (s_axil_awaddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0000: begin + // control + tx_prbs31_enable_next = s_axil_wdata[0]; + rx_prbs31_enable_next = s_axil_wdata[1]; + end + 16'h0004: begin + // cycle count + cycle_count_next = 1; + end + 16'h0008: begin + // update count + update_count_next = 0; + end + 16'h000C: begin + // error count + rx_error_count_next = 0; + end + 16'h0014: begin + // cycle count + atomic_cycle_count_next = 1; + end + 16'h0018: begin + // update count + atomic_update_count_next = 0; + end + 16'h001C: begin + // error count + atomic_rx_error_count_next = 0; + end + 16'h0020: begin + // control + accumulate_enable_next = s_axil_wdata[0]; + slice_enable_next = s_axil_wdata[1]; + end + 16'h0024: begin + // slice time + slice_time_next = s_axil_wdata; + end + 16'h0028: begin + // slice offset + slice_offset_next = s_axil_wdata; + end + 16'h0030: begin + // slice select + slice_select_next = s_axil_wdata; + end + endcase + end + end else if (read_eligible) begin + last_read_next = 1'b1; + + s_axil_arready_next = 1'b1; + s_axil_rvalid_next = 1'b1; + s_axil_rdata_next = {AXIL_DATA_WIDTH{1'b0}}; + + if (s_axil_araddr[INDEX_WIDTH+2+1+1-1]) begin + mem_rd_en = 1'b1; + end else begin + case (s_axil_araddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0000: begin + // control + s_axil_rdata_next[0] = tx_prbs31_enable_reg; + s_axil_rdata_next[1] = rx_prbs31_enable_reg; + end + 16'h0004: begin + // cycle count + s_axil_rdata_next = cycle_count_reg; + end + 16'h0008: begin + // update count + s_axil_rdata_next = update_count_reg; + end + 16'h000C: begin + // error count + s_axil_rdata_next = rx_error_count_reg; + end + 16'h0014: begin + // cycle count + s_axil_rdata_next = atomic_cycle_count_reg; + atomic_cycle_count_next = 1; + end + 16'h0018: begin + // update count + s_axil_rdata_next = atomic_update_count_reg; + atomic_update_count_next = 0; + end + 16'h001C: begin + // error count + s_axil_rdata_next = atomic_rx_error_count_reg; + atomic_rx_error_count_next = 0; + end + 16'h0020: begin + // control + s_axil_rdata_next[0] = accumulate_enable_reg; + s_axil_rdata_next[1] = slice_enable_reg; + end + 16'h0024: begin + // slice time + s_axil_rdata_next = slice_time_reg; + end + 16'h0028: begin + // slice offset + s_axil_rdata_next = slice_offset_reg; + end + 16'h0030: begin + // slice select + s_axil_rdata_next = slice_select_reg; + end + endcase + end + end +end + +always @(posedge clk) begin + if (rst) begin + last_read_reg <= 1'b0; + + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + + cycle_count_reg <= 32'd0; + update_count_reg <= 32'd0; + rx_error_count_reg <= 32'd0; + + atomic_cycle_count_reg <= 32'd0; + atomic_update_count_reg <= 32'd0; + atomic_rx_error_count_reg <= 32'd0; + + accumulate_enable_reg <= 1'b0; + slice_enable_reg <= 1'b0; + + slice_time_reg <= 32'd0; + slice_offset_reg <= 32'd0; + + slice_select_reg <= 0; + + tx_prbs31_enable_reg <= 1'b0; + rx_prbs31_enable_reg <= 1'b0; + end else begin + last_read_reg <= last_read_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + cycle_count_reg <= cycle_count_next; + update_count_reg <= update_count_next; + rx_error_count_reg <= rx_error_count_next; + + atomic_cycle_count_reg <= atomic_cycle_count_next; + atomic_update_count_reg <= atomic_update_count_next; + atomic_rx_error_count_reg <= atomic_rx_error_count_next; + + accumulate_enable_reg <= accumulate_enable_next; + slice_enable_reg <= slice_enable_next; + + slice_time_reg <= slice_time_next; + slice_offset_reg <= slice_offset_next; + + slice_select_reg <= slice_select_next; + + tx_prbs31_enable_reg <= tx_prbs31_enable_next; + rx_prbs31_enable_reg <= rx_prbs31_enable_next; + end + + s_axil_rdata_reg <= s_axil_rdata_next; + + error_count_mem_read_reg <= 1'b0; + update_count_mem_read_reg <= 1'b0; + + if (mem_rd_en) begin + if (s_axil_araddr[2]) begin + error_count_mem_read_data_reg <= error_count_mem[axil_ram_addr]; + error_count_mem_read_reg <= 1'b1; + end else begin + update_count_mem_read_data_reg <= update_count_mem[axil_ram_addr]; + update_count_mem_read_reg <= 1'b1; + end + end else begin + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (mem_wr_en && s_axil_wstrb[i]) begin + if (s_axil_awaddr[2]) begin + error_count_mem[axil_ram_addr][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE]; + end else begin + update_count_mem[axil_ram_addr][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE]; + end + end + end + end +end + +endmodule diff --git a/corundum/rtl/tdma_scheduler.v b/corundum/rtl/tdma_scheduler.v new file mode 100644 index 0000000000000000000000000000000000000000..ec6f529cc4f299d2e472f60f71e6c3bf81c6508b --- /dev/null +++ b/corundum/rtl/tdma_scheduler.v @@ -0,0 +1,399 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * TDMA scheduler module + */ +module tdma_scheduler # +( + // Timeslot index width + parameter INDEX_WIDTH = 8, + // Schedule absolute PTP start time, seconds part + parameter SCHEDULE_START_S = 48'h0, + // Schedule absolute PTP start time, nanoseconds part + parameter SCHEDULE_START_NS = 30'h0, + // Schedule period, seconds part + parameter SCHEDULE_PERIOD_S = 48'd0, + // Schedule period, nanoseconds part + parameter SCHEDULE_PERIOD_NS = 30'd1000000, + // Timeslot period, seconds part + parameter TIMESLOT_PERIOD_S = 48'd0, + // Timeslot period, nanoseconds part + parameter TIMESLOT_PERIOD_NS = 30'd100000, + // Timeslot active period, seconds part + parameter ACTIVE_PERIOD_S = 48'd0, + // Timeslot active period, nanoseconds part + parameter ACTIVE_PERIOD_NS = 30'd100000 +) +( + input wire clk, + input wire rst, + + /* + * Timestamp input from PTP clock + */ + input wire [95:0] input_ts_96, + input wire input_ts_step, + + /* + * Control + */ + input wire enable, + input wire [79:0] input_schedule_start, + input wire input_schedule_start_valid, + input wire [79:0] input_schedule_period, + input wire input_schedule_period_valid, + input wire [79:0] input_timeslot_period, + input wire input_timeslot_period_valid, + input wire [79:0] input_active_period, + input wire input_active_period_valid, + + /* + * Status + */ + output wire locked, + output wire error, + + /* + * TDMA schedule outputs + */ + output wire schedule_start, + output wire [INDEX_WIDTH-1:0] timeslot_index, + output wire timeslot_start, + output wire timeslot_end, + output wire timeslot_active +); + +/* + + schedule + start + | + V + |<-------- schedule period -------->| + -----+--------+--------+--------+--------+--------+--- + | SLOT 0 | SLOT 1 | SLOT 2 | SLOT 3 | SLOT 0 | + -----+--------+--------+--------+--------+--------+--- + |<------>| + timeslot + period + + + |<-------- timeslot period -------->| + -----+-----------------------------------+------------ + | SLOT 0 | SLOT 1 + -----+-----------------------------------+------------ + |<---- active period ----->| + +*/ + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_UPDATE_SCHEDULE_1 = 3'd1, + STATE_UPDATE_SCHEDULE_2 = 3'd2, + STATE_UPDATE_SLOT_1 = 3'd3, + STATE_UPDATE_SLOT_2 = 3'd4, + STATE_UPDATE_SLOT_3 = 3'd5, + STATE_WAIT = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg [47:0] time_s_reg = 0; +reg [30:0] time_ns_reg = 0; + +reg [47:0] first_slot_s_reg = 0, first_slot_s_next; +reg [30:0] first_slot_ns_reg = 0, first_slot_ns_next; + +reg [47:0] next_slot_s_reg = 0, next_slot_s_next; +reg [30:0] next_slot_ns_reg = 0, next_slot_ns_next; + +reg [47:0] active_end_s_reg = 0, active_end_s_next; +reg [30:0] active_end_ns_reg = 0, active_end_ns_next; + +reg [47:0] schedule_start_s_reg = SCHEDULE_START_S; +reg [30:0] schedule_start_ns_reg = SCHEDULE_START_NS; + +reg [47:0] schedule_period_s_reg = SCHEDULE_PERIOD_S; +reg [30:0] schedule_period_ns_reg = SCHEDULE_PERIOD_NS; + +reg [47:0] timeslot_period_s_reg = TIMESLOT_PERIOD_S; +reg [30:0] timeslot_period_ns_reg = TIMESLOT_PERIOD_NS; + +reg [47:0] active_period_s_reg = ACTIVE_PERIOD_S; +reg [30:0] active_period_ns_reg = ACTIVE_PERIOD_NS; + +reg [29:0] ts_ns_inc_reg = 0, ts_ns_inc_next; +reg [30:0] ts_ns_ovf_reg = 0, ts_ns_ovf_next; + +reg locked_reg = 1'b0, locked_next; +reg locked_int_reg = 1'b0, locked_int_next; +reg error_reg = 1'b0, error_next; +reg schedule_running_reg = 1'b0, schedule_running_next; + +reg schedule_start_reg = 1'b0, schedule_start_next; +reg [INDEX_WIDTH-1:0] timeslot_index_reg = 0, timeslot_index_next; +reg timeslot_start_reg = 1'b0, timeslot_start_next; +reg timeslot_end_reg = 1'b0, timeslot_end_next; +reg timeslot_active_reg = 1'b0, timeslot_active_next; + +assign locked = locked_reg; +assign error = error_reg; + +assign schedule_start = schedule_start_reg; +assign timeslot_index = timeslot_index_reg; +assign timeslot_start = timeslot_start_reg; +assign timeslot_end = timeslot_end_reg; +assign timeslot_active = timeslot_active_reg; + +always @* begin + state_next = STATE_IDLE; + + first_slot_s_next = first_slot_s_reg; + first_slot_ns_next = first_slot_ns_reg; + + next_slot_s_next = next_slot_s_reg; + next_slot_ns_next = next_slot_ns_reg; + + active_end_s_next = active_end_s_reg; + active_end_ns_next = active_end_ns_reg; + + ts_ns_inc_next = ts_ns_inc_reg; + + ts_ns_ovf_next = ts_ns_ovf_reg; + + locked_next = locked_reg; + locked_int_next = locked_int_reg; + error_next = error_reg; + schedule_running_next = schedule_running_reg; + + schedule_start_next = 1'b0; + timeslot_index_next = timeslot_index_reg; + timeslot_start_next = 1'b0; + timeslot_end_next = 1'b0; + timeslot_active_next = timeslot_active_reg; + + if (input_schedule_start_valid || input_schedule_period_valid || input_ts_step) begin + timeslot_index_next = 0; + timeslot_start_next = 1'b0; + timeslot_end_next = timeslot_active_reg; + timeslot_active_next = 1'b0; + error_next = input_ts_step; + state_next = STATE_IDLE; + end else begin + case (state_reg) + STATE_IDLE: begin + // set next rise to start time + first_slot_s_next = schedule_start_s_reg; + first_slot_ns_next = schedule_start_ns_reg; + next_slot_s_next = schedule_start_s_reg; + next_slot_ns_next = schedule_start_ns_reg; + timeslot_index_next = 0; + timeslot_start_next = 1'b0; + timeslot_end_next = timeslot_active_reg; + timeslot_active_next = 1'b0; + locked_next = 1'b0; + locked_int_next = 1'b0; + schedule_running_next = 1'b0; + state_next = STATE_WAIT; + end + STATE_UPDATE_SCHEDULE_1: begin + // set next schedule start time to next schedule start time plus schedule period + ts_ns_inc_next = first_slot_ns_reg + schedule_period_ns_reg; + ts_ns_ovf_next = first_slot_ns_reg + schedule_period_ns_reg - 31'd1_000_000_000; + state_next = STATE_UPDATE_SCHEDULE_2; + end + STATE_UPDATE_SCHEDULE_2: begin + if (!ts_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + first_slot_s_next = first_slot_s_reg + schedule_period_s_reg + 1; + first_slot_ns_next = ts_ns_ovf_reg; + end else begin + // no increment seconds field + first_slot_s_next = first_slot_s_reg + schedule_period_s_reg; + first_slot_ns_next = ts_ns_inc_reg; + end + next_slot_s_next = first_slot_s_reg; + next_slot_ns_next = first_slot_ns_reg; + state_next = STATE_UPDATE_SLOT_1; + end + STATE_UPDATE_SLOT_1: begin + // set next fall time to next rise time plus width + ts_ns_inc_next = next_slot_ns_reg + active_period_ns_reg; + ts_ns_ovf_next = next_slot_ns_reg + active_period_ns_reg - 31'd1_000_000_000; + state_next = STATE_UPDATE_SLOT_2; + end + STATE_UPDATE_SLOT_2: begin + if (!ts_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + active_end_s_next = next_slot_s_reg + active_period_s_reg + 1; + active_end_ns_next = ts_ns_ovf_reg; + end else begin + // no increment seconds field + active_end_s_next = next_slot_s_reg + active_period_s_reg; + active_end_ns_next = ts_ns_inc_reg; + end + // set next timeslot start time to next timeslot start time plus timeslot period + ts_ns_inc_next = next_slot_ns_reg + timeslot_period_ns_reg; + ts_ns_ovf_next = next_slot_ns_reg + timeslot_period_ns_reg - 31'd1_000_000_000; + state_next = STATE_UPDATE_SLOT_3; + end + STATE_UPDATE_SLOT_3: begin + if (!ts_ns_ovf_reg[30]) begin + // if the overflow lookahead did not borrow, one second has elapsed + next_slot_s_next = next_slot_s_reg + timeslot_period_s_reg + 1; + next_slot_ns_next = ts_ns_ovf_reg; + end else begin + // no increment seconds field + next_slot_s_next = next_slot_s_reg + timeslot_period_s_reg; + next_slot_ns_next = ts_ns_inc_reg; + end + state_next = STATE_WAIT; + end + STATE_WAIT: begin + if ((time_s_reg > first_slot_s_reg) || (time_s_reg == first_slot_s_reg && time_ns_reg > first_slot_ns_reg)) begin + // start of next schedule period + schedule_start_next = enable && locked_int_reg; + timeslot_index_next = 0; + timeslot_start_next = enable && locked_int_reg; + timeslot_end_next = timeslot_active_reg; + timeslot_active_next = enable && locked_int_reg; + schedule_running_next = 1'b1; + locked_next = locked_int_reg; + error_next = error_reg && !locked_int_reg; + state_next = STATE_UPDATE_SCHEDULE_1; + end else if ((time_s_reg > next_slot_s_reg) || (time_s_reg == next_slot_s_reg && time_ns_reg > next_slot_ns_reg)) begin + // start of next timeslot + timeslot_index_next = timeslot_index_reg + 1; + timeslot_start_next = enable && locked_reg; + timeslot_end_next = timeslot_active_reg; + timeslot_active_next = enable && locked_reg; + state_next = STATE_UPDATE_SLOT_1; + end else if (timeslot_active_reg && ((time_s_reg > active_end_s_reg) || (time_s_reg == active_end_s_reg && time_ns_reg > active_end_ns_reg))) begin + // end of timeslot + timeslot_end_next = 1'b1; + timeslot_active_next = 1'b0; + state_next = STATE_WAIT; + end else begin + locked_int_next = schedule_running_reg; + state_next = STATE_WAIT; + end + end + endcase + end +end + +always @(posedge clk) begin + state_reg <= state_next; + + time_s_reg <= input_ts_96[95:48]; + time_ns_reg <= input_ts_96[45:16]; + + if (input_schedule_start_valid) begin + schedule_start_s_reg <= input_schedule_start[79:32]; + schedule_start_ns_reg <= input_schedule_start[31:0]; + end + + if (input_schedule_period_valid) begin + schedule_period_s_reg <= input_schedule_period[79:32]; + schedule_period_ns_reg <= input_schedule_period[31:0]; + end + + if (input_timeslot_period_valid) begin + timeslot_period_s_reg <= input_timeslot_period[79:32]; + timeslot_period_ns_reg <= input_timeslot_period[31:0]; + end + + if (input_active_period_valid) begin + active_period_s_reg <= input_active_period[79:32]; + active_period_ns_reg <= input_active_period[31:0]; + end + + first_slot_s_reg <= first_slot_s_next; + first_slot_ns_reg <= first_slot_ns_next; + + next_slot_s_reg <= next_slot_s_next; + next_slot_ns_reg <= next_slot_ns_next; + + active_end_s_reg <= active_end_s_next; + active_end_ns_reg <= active_end_ns_next; + + ts_ns_inc_reg <= ts_ns_inc_next; + ts_ns_ovf_reg <= ts_ns_ovf_next; + + locked_reg <= locked_next; + locked_int_reg <= locked_int_next; + error_reg <= error_next; + schedule_running_reg <= schedule_running_next; + + schedule_start_reg <= schedule_start_next; + timeslot_index_reg <= timeslot_index_next; + timeslot_start_reg <= timeslot_start_next; + timeslot_end_reg <= timeslot_end_next; + timeslot_active_reg <= timeslot_active_next; + + if (rst) begin + state_reg <= STATE_IDLE; + + time_s_reg <= 0; + time_ns_reg <= 0; + + schedule_start_s_reg <= SCHEDULE_START_S; + schedule_start_ns_reg <= SCHEDULE_START_NS; + + schedule_period_s_reg <= SCHEDULE_PERIOD_S; + schedule_period_ns_reg <= SCHEDULE_PERIOD_NS; + + timeslot_period_s_reg <= TIMESLOT_PERIOD_S; + timeslot_period_ns_reg <= TIMESLOT_PERIOD_NS; + + active_period_s_reg <= ACTIVE_PERIOD_S; + active_period_ns_reg <= ACTIVE_PERIOD_NS; + + locked_reg <= 1'b0; + locked_int_reg <= 1'b0; + error_reg <= 1'b0; + schedule_running_reg <= 1'b0; + + schedule_start_reg <= 1'b0; + timeslot_index_reg <= 0; + timeslot_start_reg <= 1'b0; + timeslot_end_reg <= 1'b0; + timeslot_active_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/rtl/tx_checksum.v b/corundum/rtl/tx_checksum.v new file mode 100644 index 0000000000000000000000000000000000000000..ce3c25bab97e1dfb913c4737e7894e3fd23d5a4f --- /dev/null +++ b/corundum/rtl/tx_checksum.v @@ -0,0 +1,574 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Transmit checksum offload module + */ +module tx_checksum # +( + // Width of AXI stream interfaces in bits + parameter DATA_WIDTH = 256, + // AXI stream tkeep signal width (words per cycle) + parameter KEEP_WIDTH = (DATA_WIDTH/8), + // Propagate tid signal + parameter ID_ENABLE = 0, + // tid signal width + parameter ID_WIDTH = 8, + // Propagate tdest signal + parameter DEST_ENABLE = 0, + // tdest signal width + parameter DEST_WIDTH = 8, + // Propagate tuser signal + parameter USER_ENABLE = 1, + // tuser signal width + parameter USER_WIDTH = 1, + // Use checksum init value + parameter USE_INIT_VALUE = 0, + // Depth of data FIFO in words + parameter DATA_FIFO_DEPTH = 4096, + // Depth of checksum FIFO + parameter CHECKSUM_FIFO_DEPTH = 64 +) +( + input wire clk, + input wire rst, + + /* + * AXI input + */ + input wire [DATA_WIDTH-1:0] s_axis_tdata, + input wire [KEEP_WIDTH-1:0] s_axis_tkeep, + input wire s_axis_tvalid, + output wire s_axis_tready, + input wire s_axis_tlast, + input wire [ID_WIDTH-1:0] s_axis_tid, + input wire [DEST_WIDTH-1:0] s_axis_tdest, + input wire [USER_WIDTH-1:0] s_axis_tuser, + + /* + * AXI output + */ + output wire [DATA_WIDTH-1:0] m_axis_tdata, + output wire [KEEP_WIDTH-1:0] m_axis_tkeep, + output wire m_axis_tvalid, + input wire m_axis_tready, + output wire m_axis_tlast, + output wire [ID_WIDTH-1:0] m_axis_tid, + output wire [DEST_WIDTH-1:0] m_axis_tdest, + output wire [USER_WIDTH-1:0] m_axis_tuser, + + /* + * Control + */ + input wire s_axis_cmd_csum_enable, + input wire [7:0] s_axis_cmd_csum_start, + input wire [7:0] s_axis_cmd_csum_offset, + input wire [15:0] s_axis_cmd_csum_init, + input wire s_axis_cmd_valid, + output wire s_axis_cmd_ready +); + +parameter LEVELS = $clog2(DATA_WIDTH/8); + +// bus width assertions +initial begin + if (KEEP_WIDTH * 8 != DATA_WIDTH) begin + $error("Error: AXI stream interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end +end + +reg transfer_in_reg = 1'b0; + +reg [15:0] csum_in_csum_reg = 0; +reg [7:0] csum_in_offset_reg = 0; +reg csum_in_enable_reg = 1'b0; +reg csum_in_valid_reg = 1'b0; +wire csum_in_ready; + +wire [15:0] csum_out_csum; +wire [7:0] csum_out_offset; +wire csum_out_enable; +wire csum_out_valid; +reg csum_out_ready = 1'b0; + +reg [KEEP_WIDTH-1:0] mask_reg = 0; +reg first_cycle_reg = 1'b0; +reg [7:0] input_offset_reg = 0; +reg [DATA_WIDTH-1:0] s_axis_tdata_masked; + +reg frame_reg = 1'b0, frame_next; + +reg [15:0] csum_reg = 16'd0, csum_next; +reg [7:0] csum_offset_reg = 8'd0, csum_offset_next; +reg csum_enable_reg = 1'b0, csum_enable_next; +reg csum_split_reg = 1'b0, csum_split_next; + +reg [DATA_WIDTH-1:0] sum_reg[LEVELS-2:0]; +reg [LEVELS-2:0] sum_valid_reg = 0; +reg [LEVELS-2:0] sum_odd_reg = 0; +reg [LEVELS-2:0] sum_last_reg = 0; +reg [LEVELS-2:0] sum_enable_reg = 0; +reg [7:0] sum_offset_reg[LEVELS-2:0]; +reg [15:0] sum_init_reg[LEVELS-2:0]; +reg [LEVELS-2:0] sum_init_valid_reg = 0; + +wire [DATA_WIDTH-1:0] sum_reg_0 = sum_reg[0]; +wire [DATA_WIDTH-1:0] sum_reg_1 = sum_reg[1]; +wire [DATA_WIDTH-1:0] sum_reg_2 = sum_reg[2]; + +reg [16+LEVELS-1:0] sum_acc_temp = 0; +reg [15:0] sum_acc_reg = 0; + +// internal datapath +reg [DATA_WIDTH-1:0] m_axis_tdata_int; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_int; +reg m_axis_tvalid_int; +reg m_axis_tready_int_reg = 1'b0; +reg m_axis_tlast_int; +reg [ID_WIDTH-1:0] m_axis_tid_int; +reg [DEST_WIDTH-1:0] m_axis_tdest_int; +reg [USER_WIDTH-1:0] m_axis_tuser_int; +wire m_axis_tready_int_early; + +wire [DATA_WIDTH-1:0] data_in_axis_tdata; +wire [KEEP_WIDTH-1:0] data_in_axis_tkeep; +wire data_in_axis_tvalid; +wire data_in_axis_tready; +wire data_in_axis_tlast; +wire [ID_WIDTH-1:0] data_in_axis_tid; +wire [DEST_WIDTH-1:0] data_in_axis_tdest; +wire [USER_WIDTH-1:0] data_in_axis_tuser; + +wire [DATA_WIDTH-1:0] data_out_axis_tdata; +wire [KEEP_WIDTH-1:0] data_out_axis_tkeep; +wire data_out_axis_tvalid; +reg data_out_axis_tready; +wire data_out_axis_tlast; +wire [ID_WIDTH-1:0] data_out_axis_tid; +wire [DEST_WIDTH-1:0] data_out_axis_tdest; +wire [USER_WIDTH-1:0] data_out_axis_tuser; + +assign s_axis_tready = data_in_axis_tready && csum_in_ready && transfer_in_reg; + +assign s_axis_cmd_ready = !transfer_in_reg; + +// data FIFO +assign data_in_axis_tdata = s_axis_tdata; +assign data_in_axis_tkeep = s_axis_tkeep; +assign data_in_axis_tvalid = s_axis_tvalid && csum_in_ready && transfer_in_reg; +assign data_in_axis_tlast = s_axis_tlast; +assign data_in_axis_tid = s_axis_tid; +assign data_in_axis_tdest = s_axis_tdest; +assign data_in_axis_tuser = s_axis_tuser; + +axis_fifo #( + .DEPTH(DATA_FIFO_DEPTH), + .DATA_WIDTH(DATA_WIDTH), + .KEEP_ENABLE(1), + .KEEP_WIDTH(KEEP_WIDTH), + .LAST_ENABLE(1), + .ID_ENABLE(ID_ENABLE), + .ID_WIDTH(ID_WIDTH), + .DEST_ENABLE(DEST_ENABLE), + .DEST_WIDTH(DEST_WIDTH), + .USER_ENABLE(USER_ENABLE), + .USER_WIDTH(USER_WIDTH), + .FRAME_FIFO(0) +) +data_fifo ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata(data_in_axis_tdata), + .s_axis_tkeep(data_in_axis_tkeep), + .s_axis_tvalid(data_in_axis_tvalid), + .s_axis_tready(data_in_axis_tready), + .s_axis_tlast(data_in_axis_tlast), + .s_axis_tid(data_in_axis_tid), + .s_axis_tdest(data_in_axis_tdest), + .s_axis_tuser(data_in_axis_tuser), + // AXI output + .m_axis_tdata(data_out_axis_tdata), + .m_axis_tkeep(data_out_axis_tkeep), + .m_axis_tvalid(data_out_axis_tvalid), + .m_axis_tready(data_out_axis_tready), + .m_axis_tlast(data_out_axis_tlast), + .m_axis_tid(data_out_axis_tid), + .m_axis_tdest(data_out_axis_tdest), + .m_axis_tuser(data_out_axis_tuser), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +// checksum FIFO +axis_fifo #( + .DEPTH(CHECKSUM_FIFO_DEPTH), + .DATA_WIDTH(16+8+1), + .KEEP_ENABLE(0), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +csum_fifo ( + .clk(clk), + .rst(rst), + // AXI input + .s_axis_tdata({csum_in_csum_reg, csum_in_offset_reg, csum_in_enable_reg}), + .s_axis_tkeep(0), + .s_axis_tvalid(csum_in_valid_reg), + .s_axis_tready(csum_in_ready), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + // AXI output + .m_axis_tdata({csum_out_csum, csum_out_offset, csum_out_enable}), + .m_axis_tkeep(), + .m_axis_tvalid(csum_out_valid), + .m_axis_tready(csum_out_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +// Mask input data +integer j; + +always @* begin + for (j = 0; j < KEEP_WIDTH; j = j + 1) begin + s_axis_tdata_masked[j*8 +: 8] = (s_axis_tkeep[j] && mask_reg[j]) ? s_axis_tdata[j*8 +: 8] : 8'd0; + end +end + +// Compute checksum +integer i; + +always @(posedge clk) begin + sum_valid_reg[0] <= sum_valid_reg[0] && !csum_in_ready; + + if (s_axis_tvalid && s_axis_tready) begin + for (i = 0; i < DATA_WIDTH/8/4; i = i + 1) begin + sum_reg[0][i*17 +: 17] <= {s_axis_tdata_masked[(4*i+0)*8 +: 8], s_axis_tdata_masked[(4*i+1)*8 +: 8]} + {s_axis_tdata_masked[(4*i+2)*8 +: 8], s_axis_tdata_masked[(4*i+3)*8 +: 8]}; + end + sum_valid_reg[0] <= 1'b1; + sum_last_reg[0] <= s_axis_tlast; + sum_init_valid_reg[0] <= first_cycle_reg; + + first_cycle_reg <= 1'b0; + + if (s_axis_tlast) begin + transfer_in_reg <= 1'b0; + end + + if (input_offset_reg > 0) begin + if (input_offset_reg >= KEEP_WIDTH) begin + mask_reg <= 0; + input_offset_reg <= input_offset_reg - KEEP_WIDTH; + end else begin + mask_reg <= {KEEP_WIDTH{1'b1}} << input_offset_reg; + input_offset_reg <= 0; + end + end else begin + mask_reg <= {KEEP_WIDTH{1'b1}}; + end + end + + if (s_axis_cmd_valid && s_axis_cmd_ready) begin + transfer_in_reg <= 1'b1; + sum_odd_reg[0] <= s_axis_cmd_csum_start[0]; + sum_enable_reg[0] <= s_axis_cmd_csum_enable; + sum_offset_reg[0] <= s_axis_cmd_csum_offset; + sum_init_reg[0] <= s_axis_cmd_csum_init; + first_cycle_reg <= 1'b1; + + if (s_axis_cmd_csum_start >= KEEP_WIDTH) begin + mask_reg <= 0; + input_offset_reg <= s_axis_cmd_csum_start - KEEP_WIDTH; + end else begin + mask_reg <= {KEEP_WIDTH{1'b1}} << s_axis_cmd_csum_start; + input_offset_reg <= 0; + end + end + + if (rst) begin + transfer_in_reg <= 1'b0; + sum_valid_reg[0] <= 1'b0; + end +end + +generate + + genvar l; + + for (l = 1; l < LEVELS-1; l = l + 1) begin + + always @(posedge clk) begin + sum_valid_reg[l] <= sum_valid_reg[l] && !csum_in_ready; + + if (sum_valid_reg[l-1] && csum_in_ready) begin + for (i = 0; i < DATA_WIDTH/8/4/2**l; i = i + 1) begin + sum_reg[l][i*(17+l) +: (17+l)] <= sum_reg[l-1][(i*2+0)*(17+l-1) +: (17+l-1)] + sum_reg[l-1][(i*2+1)*(17+l-1) +: (17+l-1)]; + end + sum_valid_reg[l] <= 1'b1; + sum_odd_reg[l] <= sum_odd_reg[l-1]; + sum_last_reg[l] <= sum_last_reg[l-1]; + sum_enable_reg[l] <= sum_enable_reg[l-1]; + sum_offset_reg[l] <= sum_offset_reg[l-1]; + sum_init_reg[l] <= sum_init_reg[l-1]; + sum_init_valid_reg[l] <= sum_init_valid_reg[l-1]; + end + + if (rst) begin + sum_valid_reg[l] <= 1'b0; + end + end + + end + +endgenerate + +always @(posedge clk) begin + csum_in_valid_reg <= 1'b0; + + if (sum_valid_reg[LEVELS-2] && csum_in_ready) begin + sum_acc_temp = sum_reg[LEVELS-2][16+LEVELS-1-1:0] + (sum_init_valid_reg[LEVELS-2] && USE_INIT_VALUE ? sum_init_reg[LEVELS-2] : sum_acc_reg); + sum_acc_temp = sum_acc_temp[15:0] + (sum_acc_temp >> 16); + sum_acc_temp = sum_acc_temp[15:0] + sum_acc_temp[16]; + + if (sum_last_reg[LEVELS-2]) begin + if (sum_odd_reg[LEVELS-2]) begin + csum_in_csum_reg[7:0] <= ~sum_acc_temp[15:8]; + csum_in_csum_reg[15:8] <= ~sum_acc_temp[7:0]; + end else begin + csum_in_csum_reg[7:0] <= ~sum_acc_temp[7:0]; + csum_in_csum_reg[15:8] <= ~sum_acc_temp[15:8]; + end + csum_in_offset_reg <= sum_offset_reg[LEVELS-2]; + csum_in_enable_reg <= sum_enable_reg[LEVELS-2]; + csum_in_valid_reg <= 1'b1; + sum_acc_reg <= 0; + end else begin + sum_acc_reg <= sum_acc_temp; + end + end + + if (rst) begin + csum_in_valid_reg <= 1'b0; + end +end + +// Insert checksum +always @* begin + data_out_axis_tready = m_axis_tready_int_reg && frame_reg; + csum_out_ready = 1'b0; + + frame_next = frame_reg; + + csum_next = csum_reg; + csum_offset_next = csum_offset_reg; + csum_enable_next = csum_enable_reg; + csum_split_next = csum_split_reg; + + m_axis_tdata_int = data_out_axis_tdata; + m_axis_tkeep_int = data_out_axis_tkeep; + m_axis_tvalid_int = data_out_axis_tvalid && data_out_axis_tready; + m_axis_tlast_int = data_out_axis_tlast; + m_axis_tid_int = data_out_axis_tid; + m_axis_tdest_int = data_out_axis_tdest; + m_axis_tuser_int = data_out_axis_tuser; + + if (frame_reg) begin + if (data_out_axis_tvalid && data_out_axis_tready) begin + if (data_out_axis_tlast) begin + frame_next = 1'b0; + end + + if (csum_enable_reg) begin + if (csum_offset_reg >= KEEP_WIDTH) begin + csum_offset_next = csum_offset_reg - KEEP_WIDTH; + end else if (csum_split_reg) begin + // other byte of split checksum + m_axis_tdata_int[0 +: 8] = csum_reg[7:0]; + csum_enable_next = 1'b0; + end else if (csum_offset_reg == KEEP_WIDTH-1) begin + // split across two cycles + m_axis_tdata_int[DATA_WIDTH-8 +: 8] = csum_reg[15:8]; + csum_split_next = 1'b1; + end else begin + m_axis_tdata_int[csum_offset_reg*8 +: 8] = csum_reg[15:8]; + m_axis_tdata_int[(csum_offset_reg+1)*8 +: 8] = csum_reg[7:0]; + + csum_enable_next = 1'b0; + end + end + end + end else begin + csum_out_ready = 1'b1; + csum_next = csum_out_csum; + csum_offset_next = csum_out_offset; + csum_enable_next = csum_out_enable; + csum_split_next = 1'b0; + if (csum_out_valid) begin + frame_next = 1'b1; + end + end +end + +always @(posedge clk) begin + frame_reg <= frame_next; + + csum_reg <= csum_next; + csum_offset_reg <= csum_offset_next; + csum_enable_reg <= csum_enable_next; + csum_split_reg <= csum_split_next; + + if (rst) begin + frame_reg <= 1'b0; + csum_enable_reg <= 1'b0; + end +end + +// output datapath logic +reg [DATA_WIDTH-1:0] m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg m_axis_tvalid_reg = 1'b0, m_axis_tvalid_next; +reg m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +reg [DATA_WIDTH-1:0] temp_m_axis_tdata_reg = {DATA_WIDTH{1'b0}}; +reg [KEEP_WIDTH-1:0] temp_m_axis_tkeep_reg = {KEEP_WIDTH{1'b0}}; +reg temp_m_axis_tvalid_reg = 1'b0, temp_m_axis_tvalid_next; +reg temp_m_axis_tlast_reg = 1'b0; +reg [ID_WIDTH-1:0] temp_m_axis_tid_reg = {ID_WIDTH{1'b0}}; +reg [DEST_WIDTH-1:0] temp_m_axis_tdest_reg = {DEST_WIDTH{1'b0}}; +reg [USER_WIDTH-1:0] temp_m_axis_tuser_reg = {USER_WIDTH{1'b0}}; + +// datapath control +reg store_axis_int_to_output; +reg store_axis_int_to_temp; +reg store_axis_temp_to_output; + +assign m_axis_tdata = m_axis_tdata_reg; +assign m_axis_tkeep = m_axis_tkeep_reg; +assign m_axis_tvalid = m_axis_tvalid_reg; +assign m_axis_tlast = m_axis_tlast_reg; +assign m_axis_tid = ID_ENABLE ? m_axis_tid_reg : {ID_WIDTH{1'b0}}; +assign m_axis_tdest = DEST_ENABLE ? m_axis_tdest_reg : {DEST_WIDTH{1'b0}}; +assign m_axis_tuser = USER_ENABLE ? m_axis_tuser_reg : {USER_WIDTH{1'b0}}; + +// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input) +assign m_axis_tready_int_early = m_axis_tready || (!temp_m_axis_tvalid_reg && (!m_axis_tvalid_reg || !m_axis_tvalid_int)); + +always @* begin + // transfer sink ready state to source + m_axis_tvalid_next = m_axis_tvalid_reg; + temp_m_axis_tvalid_next = temp_m_axis_tvalid_reg; + + store_axis_int_to_output = 1'b0; + store_axis_int_to_temp = 1'b0; + store_axis_temp_to_output = 1'b0; + + if (m_axis_tready_int_reg) begin + // input is ready + if (m_axis_tready || !m_axis_tvalid_reg) begin + // output is ready or currently not valid, transfer data to output + m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_output = 1'b1; + end else begin + // output is not ready, store input in temp + temp_m_axis_tvalid_next = m_axis_tvalid_int; + store_axis_int_to_temp = 1'b1; + end + end else if (m_axis_tready) begin + // input is not ready, but output is ready + m_axis_tvalid_next = temp_m_axis_tvalid_reg; + temp_m_axis_tvalid_next = 1'b0; + store_axis_temp_to_output = 1'b1; + end +end + +always @(posedge clk) begin + if (rst) begin + m_axis_tvalid_reg <= 1'b0; + m_axis_tready_int_reg <= 1'b0; + temp_m_axis_tvalid_reg <= 1'b0; + end else begin + m_axis_tvalid_reg <= m_axis_tvalid_next; + m_axis_tready_int_reg <= m_axis_tready_int_early; + temp_m_axis_tvalid_reg <= temp_m_axis_tvalid_next; + end + + // datapath + if (store_axis_int_to_output) begin + m_axis_tdata_reg <= m_axis_tdata_int; + m_axis_tkeep_reg <= m_axis_tkeep_int; + m_axis_tlast_reg <= m_axis_tlast_int; + m_axis_tid_reg <= m_axis_tid_int; + m_axis_tdest_reg <= m_axis_tdest_int; + m_axis_tuser_reg <= m_axis_tuser_int; + end else if (store_axis_temp_to_output) begin + m_axis_tdata_reg <= temp_m_axis_tdata_reg; + m_axis_tkeep_reg <= temp_m_axis_tkeep_reg; + m_axis_tlast_reg <= temp_m_axis_tlast_reg; + m_axis_tid_reg <= temp_m_axis_tid_reg; + m_axis_tdest_reg <= temp_m_axis_tdest_reg; + m_axis_tuser_reg <= temp_m_axis_tuser_reg; + end + + if (store_axis_int_to_temp) begin + temp_m_axis_tdata_reg <= m_axis_tdata_int; + temp_m_axis_tkeep_reg <= m_axis_tkeep_int; + temp_m_axis_tlast_reg <= m_axis_tlast_int; + temp_m_axis_tid_reg <= m_axis_tid_int; + temp_m_axis_tdest_reg <= m_axis_tdest_int; + temp_m_axis_tuser_reg <= m_axis_tuser_int; + end +end + +endmodule diff --git a/corundum/rtl/tx_engine.v b/corundum/rtl/tx_engine.v new file mode 100644 index 0000000000000000000000000000000000000000..1ce5169af68c5fce993083a0d0c517517c07ed3b --- /dev/null +++ b/corundum/rtl/tx_engine.v @@ -0,0 +1,972 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Transmit engine + */ +module tx_engine # +( + // DMA RAM address width + parameter RAM_ADDR_WIDTH = 16, + // DMA address width + parameter DMA_ADDR_WIDTH = 64, + // DMA length field width + parameter DMA_LEN_WIDTH = 20, + // DMA client length field width + parameter DMA_CLIENT_LEN_WIDTH = 20, + // Transmit request tag field width + parameter REQ_TAG_WIDTH = 8, + // Descriptor request tag field width + parameter DESC_REQ_TAG_WIDTH = 8, + // DMA tag field width + parameter DMA_TAG_WIDTH = 8, + // DMA client tag field width + parameter DMA_CLIENT_TAG_WIDTH = 8, + // Queue request tag field width + parameter QUEUE_REQ_TAG_WIDTH = 8, + // Queue operation tag field width + parameter QUEUE_OP_TAG_WIDTH = 8, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 4, + // Queue element pointer width + parameter QUEUE_PTR_WIDTH = 16, + // Completion queue index width + parameter CPL_QUEUE_INDEX_WIDTH = 4, + // Descriptor table size (number of in-flight operations) + parameter DESC_TABLE_SIZE = 8, + // Width of descriptor table field for tracking outstanding DMA operations + parameter DESC_TABLE_DMA_OP_COUNT_WIDTH = 4, + // Packet table size (number of in-progress packets) + parameter PKT_TABLE_SIZE = 8, + // Max transmit packet size + parameter MAX_TX_SIZE = 2048, + // Descriptor size (in bytes) + parameter DESC_SIZE = 16, + // Descriptor size (in bytes) + parameter CPL_SIZE = 32, + // Width of AXI stream descriptor interfaces in bits + parameter AXIS_DESC_DATA_WIDTH = DESC_SIZE*8, + // AXI stream descriptor tkeep signal width (words per cycle) + parameter AXIS_DESC_KEEP_WIDTH = AXIS_DESC_DATA_WIDTH/8, + // Enable PTP timestamping + parameter PTP_TS_ENABLE = 1, + // Enable TX checksum offload + parameter TX_CHECKSUM_ENABLE = 1 +) +( + input wire clk, + input wire rst, + + /* + * Transmit request input (queue index) + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_tx_req_queue, + input wire [REQ_TAG_WIDTH-1:0] s_axis_tx_req_tag, + input wire s_axis_tx_req_valid, + output wire s_axis_tx_req_ready, + + /* + * Transmit request status output + */ + output wire [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_tx_req_status_len, + output wire [REQ_TAG_WIDTH-1:0] m_axis_tx_req_status_tag, + output wire m_axis_tx_req_status_valid, + + /* + * Descriptor request output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_desc_req_queue, + output wire [DESC_REQ_TAG_WIDTH-1:0] m_axis_desc_req_tag, + output wire m_axis_desc_req_valid, + input wire m_axis_desc_req_ready, + + /* + * Descriptor request status input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_desc_req_status_queue, + input wire [QUEUE_PTR_WIDTH-1:0] s_axis_desc_req_status_ptr, + input wire [CPL_QUEUE_INDEX_WIDTH-1:0] s_axis_desc_req_status_cpl, + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_desc_req_status_tag, + input wire s_axis_desc_req_status_empty, + input wire s_axis_desc_req_status_error, + input wire s_axis_desc_req_status_valid, + + /* + * Descriptor data input + */ + input wire [AXIS_DESC_DATA_WIDTH-1:0] s_axis_desc_tdata, + input wire [AXIS_DESC_KEEP_WIDTH-1:0] s_axis_desc_tkeep, + input wire s_axis_desc_tvalid, + output wire s_axis_desc_tready, + input wire s_axis_desc_tlast, + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_desc_tid, + input wire s_axis_desc_tuser, + + /* + * Completion request output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_cpl_req_queue, + output wire [DESC_REQ_TAG_WIDTH-1:0] m_axis_cpl_req_tag, + output wire [CPL_SIZE*8-1:0] m_axis_cpl_req_data, + output wire m_axis_cpl_req_valid, + input wire m_axis_cpl_req_ready, + + /* + * Completion request status input + */ + input wire [DESC_REQ_TAG_WIDTH-1:0] s_axis_cpl_req_status_tag, + input wire s_axis_cpl_req_status_full, + input wire s_axis_cpl_req_status_error, + input wire s_axis_cpl_req_status_valid, + + /* + * DMA read descriptor output + */ + output wire [DMA_ADDR_WIDTH-1:0] m_axis_dma_read_desc_dma_addr, + output wire [RAM_ADDR_WIDTH-1:0] m_axis_dma_read_desc_ram_addr, + output wire [DMA_LEN_WIDTH-1:0] m_axis_dma_read_desc_len, + output wire [DMA_TAG_WIDTH-1:0] m_axis_dma_read_desc_tag, + output wire m_axis_dma_read_desc_valid, + input wire m_axis_dma_read_desc_ready, + + /* + * DMA read descriptor status input + */ + input wire [DMA_TAG_WIDTH-1:0] s_axis_dma_read_desc_status_tag, + input wire s_axis_dma_read_desc_status_valid, + + /* + * Transmit descriptor output + */ + output wire [RAM_ADDR_WIDTH-1:0] m_axis_tx_desc_addr, + output wire [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_tx_desc_len, + output wire [DMA_CLIENT_TAG_WIDTH-1:0] m_axis_tx_desc_tag, + output wire m_axis_tx_desc_user, + output wire m_axis_tx_desc_valid, + input wire m_axis_tx_desc_ready, + + /* + * Transmit descriptor status input + */ + input wire [DMA_CLIENT_TAG_WIDTH-1:0] s_axis_tx_desc_status_tag, + input wire s_axis_tx_desc_status_valid, + + /* + * Transmit checksum command output + */ + output wire m_axis_tx_csum_cmd_csum_enable, + output wire [7:0] m_axis_tx_csum_cmd_csum_start, + output wire [7:0] m_axis_tx_csum_cmd_csum_offset, + output wire m_axis_tx_csum_cmd_valid, + input wire m_axis_tx_csum_cmd_ready, + + /* + * Transmit timestamp input + */ + input wire [95:0] s_axis_tx_ptp_ts_96, + input wire s_axis_tx_ptp_ts_valid, + output wire s_axis_tx_ptp_ts_ready, + + /* + * Configuration + */ + input wire enable +); + +parameter CL_DESC_TABLE_SIZE = $clog2(DESC_TABLE_SIZE); +parameter DESC_PTR_MASK = {CL_DESC_TABLE_SIZE{1'b1}}; +parameter CL_PKT_TABLE_SIZE = $clog2(PKT_TABLE_SIZE); + +parameter CL_MAX_TX_SIZE = $clog2(MAX_TX_SIZE); + +// bus width assertions +initial begin + if (DMA_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: DMA tag width insufficient for descriptor table size (instance %m)"); + $finish; + end + + if (DMA_CLIENT_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: DMA client tag width insufficient for descriptor table size (instance %m)"); + $finish; + end + + if (QUEUE_REQ_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: QUEUE_REQ_TAG_WIDTH must be at least $clog2(DESC_TABLE_SIZE) (instance %m)"); + $finish; + end + + if (DESC_REQ_TAG_WIDTH < CL_DESC_TABLE_SIZE) begin + $error("Error: DESC_REQ_TAG_WIDTH must be at least $clog2(DESC_TABLE_SIZE) (instance %m)"); + $finish; + end + + if (QUEUE_REQ_TAG_WIDTH < REQ_TAG_WIDTH) begin + $error("Error: QUEUE_REQ_TAG_WIDTH must be at least REQ_TAG_WIDTH (instance %m)"); + $finish; + end +end + +reg s_axis_tx_req_ready_reg = 1'b0, s_axis_tx_req_ready_next; + +reg [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_tx_req_status_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, m_axis_tx_req_status_len_next; +reg [REQ_TAG_WIDTH-1:0] m_axis_tx_req_status_tag_reg = {REQ_TAG_WIDTH{1'b0}}, m_axis_tx_req_status_tag_next; +reg m_axis_tx_req_status_valid_reg = 1'b0, m_axis_tx_req_status_valid_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_desc_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}, m_axis_desc_req_queue_next; +reg [DESC_REQ_TAG_WIDTH-1:0] m_axis_desc_req_tag_reg = {DESC_REQ_TAG_WIDTH{1'b0}}, m_axis_desc_req_tag_next; +reg m_axis_desc_req_valid_reg = 1'b0, m_axis_desc_req_valid_next; + +reg s_axis_desc_tready_reg = 1'b0, s_axis_desc_tready_next; + +reg [CPL_QUEUE_INDEX_WIDTH-1:0] m_axis_cpl_req_queue_reg = {CPL_QUEUE_INDEX_WIDTH{1'b0}}, m_axis_cpl_req_queue_next; +reg [DESC_REQ_TAG_WIDTH-1:0] m_axis_cpl_req_tag_reg = {DESC_REQ_TAG_WIDTH{1'b0}}, m_axis_cpl_req_tag_next; +reg [CPL_SIZE*8-1:0] m_axis_cpl_req_data_reg = {CPL_SIZE*8{1'b0}}, m_axis_cpl_req_data_next; +reg m_axis_cpl_req_valid_reg = 1'b0, m_axis_cpl_req_valid_next; + +reg [DMA_ADDR_WIDTH-1:0] m_axis_dma_read_desc_dma_addr_reg = {DMA_ADDR_WIDTH{1'b0}}, m_axis_dma_read_desc_dma_addr_next; +reg [RAM_ADDR_WIDTH-1:0] m_axis_dma_read_desc_ram_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, m_axis_dma_read_desc_ram_addr_next; +reg [DMA_LEN_WIDTH-1:0] m_axis_dma_read_desc_len_reg = {DMA_LEN_WIDTH{1'b0}}, m_axis_dma_read_desc_len_next; +reg [DMA_TAG_WIDTH-1:0] m_axis_dma_read_desc_tag_reg = {DMA_TAG_WIDTH{1'b0}}, m_axis_dma_read_desc_tag_next; +reg m_axis_dma_read_desc_valid_reg = 1'b0, m_axis_dma_read_desc_valid_next; + +reg [RAM_ADDR_WIDTH-1:0] m_axis_tx_desc_addr_reg = {RAM_ADDR_WIDTH{1'b0}}, m_axis_tx_desc_addr_next; +reg [DMA_CLIENT_LEN_WIDTH-1:0] m_axis_tx_desc_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, m_axis_tx_desc_len_next; +reg [DMA_CLIENT_TAG_WIDTH-1:0] m_axis_tx_desc_tag_reg = {DMA_CLIENT_TAG_WIDTH{1'b0}}, m_axis_tx_desc_tag_next; +reg m_axis_tx_desc_user_reg = 1'b0, m_axis_tx_desc_user_next; +reg m_axis_tx_desc_valid_reg = 1'b0, m_axis_tx_desc_valid_next; + +reg m_axis_tx_csum_cmd_csum_enable_reg = 1'b0, m_axis_tx_csum_cmd_csum_enable_next; +reg [7:0] m_axis_tx_csum_cmd_csum_start_reg = 7'd0, m_axis_tx_csum_cmd_csum_start_next; +reg [7:0] m_axis_tx_csum_cmd_csum_offset_reg = 7'd0, m_axis_tx_csum_cmd_csum_offset_next; +reg m_axis_tx_csum_cmd_valid_reg = 1'b0, m_axis_tx_csum_cmd_valid_next; + +reg s_axis_tx_ptp_ts_ready_reg = 1'b0, s_axis_tx_ptp_ts_ready_next; + +reg desc_start_reg = 1'b1, desc_start_next; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, desc_len_next; + +reg [DMA_CLIENT_LEN_WIDTH-1:0] early_tx_req_status_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, early_tx_req_status_len_next; +reg [REQ_TAG_WIDTH-1:0] early_tx_req_status_tag_reg = {REQ_TAG_WIDTH{1'b0}}, early_tx_req_status_tag_next; +reg early_tx_req_status_valid_reg = 1'b0, early_tx_req_status_valid_next; + +reg [DMA_CLIENT_LEN_WIDTH-1:0] finish_tx_req_status_len_reg = {DMA_CLIENT_LEN_WIDTH{1'b0}}, finish_tx_req_status_len_next; +reg [REQ_TAG_WIDTH-1:0] finish_tx_req_status_tag_reg = {REQ_TAG_WIDTH{1'b0}}, finish_tx_req_status_tag_next; +reg finish_tx_req_status_valid_reg = 1'b0, finish_tx_req_status_valid_next; + +reg [DESC_TABLE_SIZE-1:0] desc_table_active = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_invalid = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_desc_fetched = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_data_fetched = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_tx_done = 0; +reg [DESC_TABLE_SIZE-1:0] desc_table_cpl_write_done = 0; +reg [REQ_TAG_WIDTH-1:0] desc_table_tag[DESC_TABLE_SIZE-1:0]; +reg [QUEUE_INDEX_WIDTH-1:0] desc_table_queue[DESC_TABLE_SIZE-1:0]; +reg [QUEUE_PTR_WIDTH-1:0] desc_table_queue_ptr[DESC_TABLE_SIZE-1:0]; +reg [CPL_QUEUE_INDEX_WIDTH-1:0] desc_table_cpl_queue[DESC_TABLE_SIZE-1:0]; +reg [6:0] desc_table_csum_start[DESC_TABLE_SIZE-1:0]; +reg [7:0] desc_table_csum_offset[DESC_TABLE_SIZE-1:0]; +reg desc_table_csum_enable[DESC_TABLE_SIZE-1:0]; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_table_len[DESC_TABLE_SIZE-1:0]; +reg [CL_PKT_TABLE_SIZE-1:0] desc_table_pkt[DESC_TABLE_SIZE-1:0]; +reg [95:0] desc_table_ptp_ts[DESC_TABLE_SIZE-1:0]; +reg desc_table_read_commit[DESC_TABLE_SIZE-1:0]; +reg [DESC_TABLE_DMA_OP_COUNT_WIDTH-1:0] desc_table_read_count_start[DESC_TABLE_SIZE-1:0]; +reg [DESC_TABLE_DMA_OP_COUNT_WIDTH-1:0] desc_table_read_count_finish[DESC_TABLE_SIZE-1:0]; + +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_start_ptr_reg = 0; +reg [QUEUE_INDEX_WIDTH-1:0] desc_table_start_queue; +reg [REQ_TAG_WIDTH-1:0] desc_table_start_tag; +reg [CL_PKT_TABLE_SIZE-1:0] desc_table_start_pkt; +reg desc_table_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_dequeue_ptr; +reg [QUEUE_PTR_WIDTH-1:0] desc_table_dequeue_queue_ptr; +reg [CPL_QUEUE_INDEX_WIDTH-1:0] desc_table_dequeue_cpl_queue; +reg desc_table_dequeue_invalid; +reg desc_table_dequeue_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_desc_ctrl_ptr; +reg [6:0] desc_table_desc_ctrl_csum_start; +reg [7:0] desc_table_desc_ctrl_csum_offset; +reg desc_table_desc_ctrl_csum_enable; +reg desc_table_desc_ctrl_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_desc_fetched_ptr; +reg [DMA_CLIENT_LEN_WIDTH-1:0] desc_table_desc_fetched_len; +reg desc_table_desc_fetched_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_data_fetched_ptr; +reg desc_table_data_fetched_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_tx_start_ptr_reg = 0; +reg desc_table_tx_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_tx_finish_ptr; +reg desc_table_tx_finish_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_store_ptp_ts_ptr_reg = 0; +reg [95:0] desc_table_store_ptp_ts; +reg desc_table_store_ptp_ts_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_cpl_enqueue_start_ptr_reg = 0; +reg desc_table_cpl_enqueue_start_en; +reg [CL_DESC_TABLE_SIZE-1:0] desc_table_cpl_write_done_ptr; +reg desc_table_cpl_write_done_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_finish_ptr_reg = 0; +reg desc_table_finish_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_read_start_ptr; +reg desc_table_read_start_commit; +reg desc_table_read_start_init; +reg desc_table_read_start_en; +reg [CL_DESC_TABLE_SIZE+1-1:0] desc_table_read_finish_ptr; +reg desc_table_read_finish_en; + +reg [PKT_TABLE_SIZE-1:0] pkt_table_active = 0; +reg [CL_PKT_TABLE_SIZE-1:0] pkt_table_start_ptr; +reg pkt_table_start_en; +reg [CL_PKT_TABLE_SIZE-1:0] pkt_table_finish_1_ptr; +reg pkt_table_finish_1_en; +reg [CL_PKT_TABLE_SIZE-1:0] pkt_table_finish_2_ptr; +reg pkt_table_finish_2_en; + +assign s_axis_tx_req_ready = s_axis_tx_req_ready_reg; + +assign m_axis_tx_req_status_len = m_axis_tx_req_status_len_reg; +assign m_axis_tx_req_status_tag = m_axis_tx_req_status_tag_reg; +assign m_axis_tx_req_status_valid = m_axis_tx_req_status_valid_reg; + +assign m_axis_desc_req_queue = m_axis_desc_req_queue_reg; +assign m_axis_desc_req_tag = m_axis_desc_req_tag_reg; +assign m_axis_desc_req_valid = m_axis_desc_req_valid_reg; + +assign s_axis_desc_tready = s_axis_desc_tready_reg; + +assign m_axis_cpl_req_queue = m_axis_cpl_req_queue_reg; +assign m_axis_cpl_req_tag = m_axis_cpl_req_tag_reg; +assign m_axis_cpl_req_data = m_axis_cpl_req_data_reg; +assign m_axis_cpl_req_valid = m_axis_cpl_req_valid_reg; + +assign m_axis_dma_read_desc_dma_addr = m_axis_dma_read_desc_dma_addr_reg; +assign m_axis_dma_read_desc_ram_addr = m_axis_dma_read_desc_ram_addr_reg; +assign m_axis_dma_read_desc_len = m_axis_dma_read_desc_len_reg; +assign m_axis_dma_read_desc_tag = m_axis_dma_read_desc_tag_reg; +assign m_axis_dma_read_desc_valid = m_axis_dma_read_desc_valid_reg; + +assign m_axis_tx_desc_addr = m_axis_tx_desc_addr_reg; +assign m_axis_tx_desc_len = m_axis_tx_desc_len_reg; +assign m_axis_tx_desc_tag = m_axis_tx_desc_tag_reg; +assign m_axis_tx_desc_user = m_axis_tx_desc_user_reg; +assign m_axis_tx_desc_valid = m_axis_tx_desc_valid_reg; + +assign m_axis_tx_csum_cmd_csum_enable = m_axis_tx_csum_cmd_csum_enable_reg; +assign m_axis_tx_csum_cmd_csum_start = m_axis_tx_csum_cmd_csum_start_reg; +assign m_axis_tx_csum_cmd_csum_offset = m_axis_tx_csum_cmd_csum_offset_reg; +assign m_axis_tx_csum_cmd_valid = m_axis_tx_csum_cmd_valid_reg; + +assign s_axis_tx_ptp_ts_ready = s_axis_tx_ptp_ts_ready_reg; + +wire pkt_table_free_ptr_valid; +wire [CL_PKT_TABLE_SIZE-1:0] pkt_table_free_ptr; + +priority_encoder #( + .WIDTH(PKT_TABLE_SIZE), + .LSB_PRIORITY("HIGH") +) +pkt_table_free_enc_inst ( + .input_unencoded(~pkt_table_active), + .output_valid(pkt_table_free_ptr_valid), + .output_encoded(pkt_table_free_ptr), + .output_unencoded() +); + +// reg [15:0] stall_cnt = 0; +// wire stalled = stall_cnt[12]; + +// // assign dbg = stalled; + +// always @(posedge clk) begin +// if (rst) begin +// stall_cnt <= 0; +// end else begin +// if (s_axis_tx_req_ready) begin +// stall_cnt <= 0; +// end else begin +// stall_cnt <= stall_cnt + 1; +// end +// end +// end + +// ila_0 ila_inst ( +// .clk(clk), +// .trig_out(), +// .trig_out_ack(1'b0), +// .trig_in(1'b0), +// .trig_in_ack(), +// .probe0({desc_table_active, desc_table_invalid, desc_table_desc_fetched, desc_table_data_fetched, desc_table_tx_done, desc_table_cpl_write_done, pkt_table_active, +// m_axis_dma_read_desc_len, m_axis_dma_read_desc_tag, m_axis_dma_read_desc_valid, m_axis_dma_read_desc_ready, +// s_axis_dma_read_desc_status_tag, s_axis_dma_read_desc_status_valid, +// m_axis_dma_write_desc_len, m_axis_dma_write_desc_tag, m_axis_dma_write_desc_valid, m_axis_dma_write_desc_ready, +// s_axis_dma_write_desc_status_tag, s_axis_dma_write_desc_status_valid}), +// .probe1(0), +// .probe2(0), +// .probe3(s_axis_tx_req_ready), +// .probe4({desc_table_start_ptr_reg, desc_table_desc_read_start_ptr_reg, desc_table_data_fetch_start_ptr_reg, desc_table_tx_start_ptr_reg, desc_table_cpl_enqueue_start_ptr_reg, desc_table_finish_ptr_reg, stall_cnt}), +// .probe5(0) +// ); + +integer i; + +initial begin + for (i = 0; i < DESC_TABLE_SIZE; i = i + 1) begin + desc_table_tag[i] = 0; + desc_table_queue[i] = 0; + desc_table_queue_ptr[i] = 0; + desc_table_cpl_queue[i] = 0; + desc_table_csum_start[i] = 0; + desc_table_csum_offset[i] = 0; + desc_table_csum_enable[i] = 0; + desc_table_len[i] = 0; + desc_table_pkt[i] = 0; + desc_table_ptp_ts[i] = 0; + desc_table_read_commit[i] = 0; + desc_table_read_count_start[i] = 0; + desc_table_read_count_finish[i] = 0; + end +end + +always @* begin + s_axis_tx_req_ready_next = 1'b0; + + m_axis_tx_req_status_len_next = m_axis_tx_req_status_len_reg; + m_axis_tx_req_status_tag_next = m_axis_tx_req_status_tag_reg; + m_axis_tx_req_status_valid_next = 1'b0; + + m_axis_desc_req_queue_next = m_axis_desc_req_queue_reg; + m_axis_desc_req_tag_next = m_axis_desc_req_tag_reg; + m_axis_desc_req_valid_next = m_axis_desc_req_valid_reg && !m_axis_desc_req_ready; + + s_axis_desc_tready_next = 1'b0; + + m_axis_cpl_req_queue_next = m_axis_cpl_req_queue_reg; + m_axis_cpl_req_tag_next = m_axis_cpl_req_tag_reg; + m_axis_cpl_req_data_next = m_axis_cpl_req_data_reg; + m_axis_cpl_req_valid_next = m_axis_cpl_req_valid_reg && !m_axis_cpl_req_ready; + + m_axis_dma_read_desc_dma_addr_next = m_axis_dma_read_desc_dma_addr_reg; + m_axis_dma_read_desc_ram_addr_next = m_axis_dma_read_desc_ram_addr_reg; + m_axis_dma_read_desc_len_next = m_axis_dma_read_desc_len_reg; + m_axis_dma_read_desc_tag_next = m_axis_dma_read_desc_tag_reg; + m_axis_dma_read_desc_valid_next = m_axis_dma_read_desc_valid_reg && !m_axis_dma_read_desc_ready; + + m_axis_tx_desc_addr_next = m_axis_tx_desc_addr_reg; + m_axis_tx_desc_len_next = m_axis_tx_desc_len_reg; + m_axis_tx_desc_tag_next = m_axis_tx_desc_tag_reg; + m_axis_tx_desc_user_next = m_axis_tx_desc_user_reg; + m_axis_tx_desc_valid_next = m_axis_tx_desc_valid_reg && !m_axis_tx_desc_ready; + + m_axis_tx_csum_cmd_csum_enable_next = m_axis_tx_csum_cmd_csum_enable_reg; + m_axis_tx_csum_cmd_csum_start_next = m_axis_tx_csum_cmd_csum_start_reg; + m_axis_tx_csum_cmd_csum_offset_next = m_axis_tx_csum_cmd_csum_offset_reg; + m_axis_tx_csum_cmd_valid_next = m_axis_tx_csum_cmd_valid_reg && !m_axis_tx_csum_cmd_ready; + + s_axis_tx_ptp_ts_ready_next = 1'b0; + + desc_start_next = desc_start_reg; + desc_len_next = desc_len_reg; + + early_tx_req_status_len_next = early_tx_req_status_len_reg; + early_tx_req_status_tag_next = early_tx_req_status_tag_reg; + early_tx_req_status_valid_next = early_tx_req_status_valid_reg; + + finish_tx_req_status_len_next = finish_tx_req_status_len_reg; + finish_tx_req_status_tag_next = finish_tx_req_status_tag_reg; + finish_tx_req_status_valid_next = finish_tx_req_status_valid_reg; + + desc_table_start_tag = s_axis_tx_req_tag; + desc_table_start_queue = s_axis_tx_req_queue; + desc_table_start_pkt = pkt_table_free_ptr; + desc_table_start_en = 1'b0; + desc_table_dequeue_ptr = s_axis_desc_req_status_tag; + desc_table_dequeue_queue_ptr = s_axis_desc_req_status_ptr; + desc_table_dequeue_cpl_queue = s_axis_desc_req_status_cpl; + desc_table_dequeue_invalid = 1'b0; + desc_table_dequeue_en = 1'b0; + desc_table_desc_ctrl_ptr = s_axis_desc_tid & DESC_PTR_MASK; + if (TX_CHECKSUM_ENABLE) begin + desc_table_desc_ctrl_csum_start = s_axis_desc_tdata[23:16]; + desc_table_desc_ctrl_csum_offset = s_axis_desc_tdata[30:24]; + desc_table_desc_ctrl_csum_enable = s_axis_desc_tdata[31]; + end else begin + desc_table_desc_ctrl_csum_start = 0; + desc_table_desc_ctrl_csum_offset = 0; + desc_table_desc_ctrl_csum_enable = 0; + end + desc_table_desc_ctrl_en = 1'b0; + desc_table_desc_fetched_ptr = s_axis_desc_tid & DESC_PTR_MASK; + desc_table_desc_fetched_len = desc_len_reg + s_axis_desc_tdata[63:32]; + desc_table_desc_fetched_en = 1'b0; + desc_table_data_fetched_ptr = s_axis_dma_read_desc_status_tag & DESC_PTR_MASK; + desc_table_data_fetched_en = 1'b0; + desc_table_tx_start_en = 1'b0; + desc_table_tx_finish_ptr = s_axis_tx_desc_status_tag; + desc_table_tx_finish_en = 1'b0; + desc_table_store_ptp_ts = s_axis_tx_ptp_ts_96; + desc_table_store_ptp_ts_en = 1'b0; + desc_table_cpl_enqueue_start_en = 1'b0; + desc_table_cpl_write_done_ptr = s_axis_cpl_req_status_tag & DESC_PTR_MASK; + desc_table_cpl_write_done_en = 1'b0; + desc_table_finish_en = 1'b0; + desc_table_read_start_ptr = s_axis_desc_tid; + desc_table_read_start_commit = 1'b0; + desc_table_read_start_init = 1'b0; + desc_table_read_start_en = 1'b0; + desc_table_read_finish_ptr = s_axis_dma_read_desc_status_tag; + desc_table_read_finish_en = 1'b0; + + pkt_table_start_ptr = pkt_table_free_ptr; + pkt_table_start_en = 1'b0; + pkt_table_finish_1_ptr = desc_table_pkt[s_axis_desc_req_status_tag & DESC_PTR_MASK]; + pkt_table_finish_1_en = 1'b0; + pkt_table_finish_2_ptr = desc_table_pkt[s_axis_tx_desc_status_tag & DESC_PTR_MASK]; + pkt_table_finish_2_en = 1'b0; + + // descriptor fetch + // wait for transmit request + s_axis_tx_req_ready_next = enable && pkt_table_free_ptr_valid && !desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] && ($unsigned(desc_table_start_ptr_reg - desc_table_finish_ptr_reg) < DESC_TABLE_SIZE) && (!m_axis_desc_req_valid || m_axis_desc_req_ready); + if (s_axis_tx_req_ready && s_axis_tx_req_valid) begin + s_axis_tx_req_ready_next = 1'b0; + + // store in descriptor table + desc_table_start_tag = s_axis_tx_req_tag; + desc_table_start_queue = s_axis_tx_req_queue; + desc_table_start_pkt = pkt_table_free_ptr; + desc_table_start_en = 1'b1; + + // store in packet table + pkt_table_start_ptr = pkt_table_free_ptr; + pkt_table_start_en = 1'b1; + + // initiate descriptor fetch + m_axis_desc_req_queue_next = s_axis_tx_req_queue; + m_axis_desc_req_tag_next = desc_table_start_ptr_reg & DESC_PTR_MASK; + m_axis_desc_req_valid_next = 1'b1; + end + + // descriptor fetch + // wait for queue query response + if (s_axis_desc_req_status_valid) begin + + // update entry in descriptor table + desc_table_dequeue_ptr = s_axis_desc_req_status_tag & DESC_PTR_MASK; + desc_table_dequeue_queue_ptr = s_axis_desc_req_status_ptr; + desc_table_dequeue_cpl_queue = s_axis_desc_req_status_cpl; + desc_table_dequeue_invalid = 1'b0; + desc_table_dequeue_en = 1'b1; + + if (s_axis_desc_req_status_error || s_axis_desc_req_status_empty) begin + // queue empty or not active + + // invalidate entry + desc_table_dequeue_invalid = 1'b1; + + // invalidate entry in packet table + pkt_table_finish_1_ptr = desc_table_pkt[s_axis_desc_req_status_tag & DESC_PTR_MASK]; + pkt_table_finish_1_en = 1'b1; + + // return transmit request completion + early_tx_req_status_len_next = 0; + early_tx_req_status_tag_next = desc_table_tag[s_axis_desc_req_status_tag & DESC_PTR_MASK]; + early_tx_req_status_valid_next = 1'b1; + end else begin + // descriptor available to dequeue + + // wait for descriptor + end + end + + // descriptor processing and DMA request generation + // TODO descriptor validation? + s_axis_desc_tready_next = !m_axis_dma_read_desc_valid; + if (s_axis_desc_tready && s_axis_desc_tvalid) begin + if (desc_table_active[s_axis_desc_tid & DESC_PTR_MASK]) begin + desc_start_next = 1'b0; + desc_len_next = desc_len_reg + s_axis_desc_tdata[63:32]; + + desc_table_read_start_init = desc_start_reg; + + // update entry in descriptor table + desc_table_desc_ctrl_ptr = s_axis_desc_tid & DESC_PTR_MASK; + if (TX_CHECKSUM_ENABLE) begin + desc_table_desc_ctrl_csum_start = s_axis_desc_tdata[23:16]; + desc_table_desc_ctrl_csum_offset = s_axis_desc_tdata[30:24]; + desc_table_desc_ctrl_csum_enable = s_axis_desc_tdata[31]; + end + desc_table_desc_ctrl_en = desc_start_reg; + + // initiate data fetch to onboard RAM + m_axis_dma_read_desc_dma_addr_next = s_axis_desc_tdata[127:64]; + m_axis_dma_read_desc_ram_addr_next = (desc_table_pkt[s_axis_desc_tid & DESC_PTR_MASK] << CL_MAX_TX_SIZE) + desc_len_reg; + m_axis_dma_read_desc_len_next = s_axis_desc_tdata[63:32]; + m_axis_dma_read_desc_tag_next = s_axis_desc_tid & DESC_PTR_MASK; + + desc_table_read_start_ptr = s_axis_desc_tid; + + if (m_axis_dma_read_desc_len_next != 0) begin + m_axis_dma_read_desc_valid_next = 1'b1; + + // read start + desc_table_read_start_en = 1'b1; + + s_axis_desc_tready_next = 1'b0; + end + + if (s_axis_desc_tlast) begin + desc_start_next = 1'b1; + desc_len_next = 0; + + // update entry in descriptor table + desc_table_desc_fetched_ptr = s_axis_desc_tid & DESC_PTR_MASK; + desc_table_desc_fetched_len = desc_len_reg + s_axis_desc_tdata[63:32]; + desc_table_desc_fetched_en = 1'b1; + + // read commit + desc_table_read_start_commit = 1'b1; + end + end + end + + // data fetch completion + // wait for data fetch completion + if (s_axis_dma_read_desc_status_valid) begin + // update entry in descriptor table + desc_table_data_fetched_ptr = s_axis_dma_read_desc_status_tag & DESC_PTR_MASK; + desc_table_data_fetched_en = 1'b1; + + // read finish + desc_table_read_finish_ptr = s_axis_dma_read_desc_status_tag; + desc_table_read_finish_en = 1'b1; + end + + // transmit + // wait for data fetch completion + if (desc_table_active[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] && desc_table_tx_start_ptr_reg != desc_table_start_ptr_reg) begin + if (desc_table_invalid[desc_table_tx_start_ptr_reg & DESC_PTR_MASK]) begin + // invalid entry; skip + desc_table_tx_start_en = 1'b1; + //end else if (desc_table_data_fetched[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] && !m_axis_tx_desc_valid && (!m_axis_tx_csum_cmd_valid || !TX_CHECKSUM_ENABLE)) begin + end else if (desc_table_desc_fetched[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] && desc_table_read_commit[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] && (desc_table_read_count_start[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] == desc_table_read_count_finish[desc_table_tx_start_ptr_reg & DESC_PTR_MASK]) && !m_axis_tx_desc_valid && (!m_axis_tx_csum_cmd_valid || !TX_CHECKSUM_ENABLE)) begin + // update entry in descriptor table + desc_table_tx_start_en = 1'b1; + + // initiate transmit operation + m_axis_tx_desc_addr_next = desc_table_pkt[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] << CL_MAX_TX_SIZE; + m_axis_tx_desc_len_next = desc_table_len[desc_table_tx_start_ptr_reg & DESC_PTR_MASK]; + m_axis_tx_desc_tag_next = desc_table_tx_start_ptr_reg & DESC_PTR_MASK; + m_axis_tx_desc_user_next = 1'b0; + m_axis_tx_desc_valid_next = 1'b1; + + // send TX checksum command + if (TX_CHECKSUM_ENABLE) begin + m_axis_tx_csum_cmd_csum_enable_next = desc_table_csum_enable[desc_table_tx_start_ptr_reg & DESC_PTR_MASK]; + m_axis_tx_csum_cmd_csum_start_next = desc_table_csum_start[desc_table_tx_start_ptr_reg & DESC_PTR_MASK]; + m_axis_tx_csum_cmd_csum_offset_next = desc_table_csum_start[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] + desc_table_csum_offset[desc_table_tx_start_ptr_reg & DESC_PTR_MASK]; + m_axis_tx_csum_cmd_valid_next = 1'b1; + end + end + end + + // transmit done + // wait for transmit completion + if (s_axis_tx_desc_status_valid) begin + // update entry in descriptor table + desc_table_tx_finish_ptr = s_axis_tx_desc_status_tag; + desc_table_tx_finish_en = 1'b1; + + // invalidate entry in packet table + pkt_table_finish_2_ptr = desc_table_pkt[s_axis_tx_desc_status_tag & DESC_PTR_MASK]; + pkt_table_finish_2_en = 1'b1; + end + + // store PTP timestamp + if (desc_table_active[desc_table_store_ptp_ts_ptr_reg & DESC_PTR_MASK] && desc_table_store_ptp_ts_ptr_reg != desc_table_start_ptr_reg && desc_table_store_ptp_ts_ptr_reg != desc_table_tx_start_ptr_reg && PTP_TS_ENABLE) begin + s_axis_tx_ptp_ts_ready_next = 1'b1; + if (desc_table_invalid[desc_table_store_ptp_ts_ptr_reg & DESC_PTR_MASK]) begin + // invalid entry; skip + desc_table_store_ptp_ts_en = 1'b1; + + s_axis_tx_ptp_ts_ready_next = 1'b0; + end else if (s_axis_tx_ptp_ts_ready && s_axis_tx_ptp_ts_valid) begin + // update entry in descriptor table + desc_table_store_ptp_ts = s_axis_tx_ptp_ts_96; + desc_table_store_ptp_ts_en = 1'b1; + + s_axis_tx_ptp_ts_ready_next = 1'b0; + end + end + + // finish transmit; start completion enqueue + if (desc_table_active[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] && desc_table_cpl_enqueue_start_ptr_reg != desc_table_start_ptr_reg && desc_table_cpl_enqueue_start_ptr_reg != desc_table_tx_start_ptr_reg && (desc_table_cpl_enqueue_start_ptr_reg != desc_table_store_ptp_ts_ptr_reg || !PTP_TS_ENABLE)) begin + if (desc_table_invalid[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]) begin + // invalid entry; skip + desc_table_cpl_enqueue_start_en = 1'b1; + end else if (desc_table_tx_done[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] && !m_axis_cpl_req_valid_next) begin + // update entry in descriptor table + desc_table_cpl_enqueue_start_en = 1'b1; + + // initiate queue query + m_axis_cpl_req_queue_next = desc_table_cpl_queue[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_req_tag_next = desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK; + m_axis_cpl_req_data_next = 0; + m_axis_cpl_req_data_next[15:0] = desc_table_queue[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_req_data_next[31:16] = desc_table_queue_ptr[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + m_axis_cpl_req_data_next[47:32] = desc_table_len[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK]; + if (PTP_TS_ENABLE) begin + //m_axis_cpl_req_data_next[127:64] = desc_table_ptp_ts[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] >> 16; + m_axis_cpl_req_data_next[111:64] = desc_table_ptp_ts[desc_table_cpl_enqueue_start_ptr_reg & DESC_PTR_MASK] >> 16; + end + m_axis_cpl_req_valid_next = 1'b1; + end + end + + // start completion write + // wait for queue query response + if (s_axis_cpl_req_status_valid) begin + // update entry in descriptor table + desc_table_cpl_write_done_ptr = s_axis_cpl_req_status_tag & DESC_PTR_MASK; + desc_table_cpl_write_done_en = 1'b1; + end + + // operation complete + if (desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] && desc_table_finish_ptr_reg != desc_table_start_ptr_reg && desc_table_finish_ptr_reg != desc_table_cpl_enqueue_start_ptr_reg && !finish_tx_req_status_valid_reg) begin + if (desc_table_invalid[desc_table_finish_ptr_reg & DESC_PTR_MASK]) begin + // invalidate entry in descriptor table + desc_table_finish_en = 1'b1; + end else if (desc_table_cpl_write_done[desc_table_finish_ptr_reg & DESC_PTR_MASK]) begin + // invalidate entry in descriptor table + desc_table_finish_en = 1'b1; + + // return transmit request completion + finish_tx_req_status_len_next = desc_table_len[desc_table_finish_ptr_reg & DESC_PTR_MASK]; + finish_tx_req_status_tag_next = desc_table_tag[desc_table_finish_ptr_reg & DESC_PTR_MASK]; + finish_tx_req_status_valid_next = 1'b1; + end + end + + // transmit request completion arbitration + if (finish_tx_req_status_valid_next && !m_axis_tx_req_status_valid_reg) begin + m_axis_tx_req_status_len_next = finish_tx_req_status_len_next; + m_axis_tx_req_status_tag_next = finish_tx_req_status_tag_next; + m_axis_tx_req_status_valid_next = 1'b1; + finish_tx_req_status_valid_next = 1'b0; + end else if (early_tx_req_status_valid_next && !m_axis_tx_req_status_valid_reg) begin + m_axis_tx_req_status_len_next = early_tx_req_status_len_next; + m_axis_tx_req_status_tag_next = early_tx_req_status_tag_next; + m_axis_tx_req_status_valid_next = 1'b1; + early_tx_req_status_valid_next = 1'b0; + end +end + +always @(posedge clk) begin + s_axis_tx_req_ready_reg <= s_axis_tx_req_ready_next; + + m_axis_tx_req_status_len_reg <= m_axis_tx_req_status_len_next; + m_axis_tx_req_status_tag_reg <= m_axis_tx_req_status_tag_next; + m_axis_tx_req_status_valid_reg <= m_axis_tx_req_status_valid_next; + + m_axis_desc_req_queue_reg <= m_axis_desc_req_queue_next; + m_axis_desc_req_tag_reg <= m_axis_desc_req_tag_next; + m_axis_desc_req_valid_reg <= m_axis_desc_req_valid_next; + + s_axis_desc_tready_reg <= s_axis_desc_tready_next; + + m_axis_cpl_req_queue_reg <= m_axis_cpl_req_queue_next; + m_axis_cpl_req_tag_reg <= m_axis_cpl_req_tag_next; + m_axis_cpl_req_data_reg <= m_axis_cpl_req_data_next; + m_axis_cpl_req_valid_reg <= m_axis_cpl_req_valid_next; + + m_axis_dma_read_desc_dma_addr_reg <= m_axis_dma_read_desc_dma_addr_next; + m_axis_dma_read_desc_ram_addr_reg <= m_axis_dma_read_desc_ram_addr_next; + m_axis_dma_read_desc_len_reg <= m_axis_dma_read_desc_len_next; + m_axis_dma_read_desc_tag_reg <= m_axis_dma_read_desc_tag_next; + m_axis_dma_read_desc_valid_reg <= m_axis_dma_read_desc_valid_next; + + m_axis_tx_desc_addr_reg <= m_axis_tx_desc_addr_next; + m_axis_tx_desc_len_reg <= m_axis_tx_desc_len_next; + m_axis_tx_desc_tag_reg <= m_axis_tx_desc_tag_next; + m_axis_tx_desc_user_reg <= m_axis_tx_desc_user_next; + m_axis_tx_desc_valid_reg <= m_axis_tx_desc_valid_next; + + s_axis_tx_ptp_ts_ready_reg <= s_axis_tx_ptp_ts_ready_next; + + m_axis_tx_csum_cmd_csum_enable_reg <= m_axis_tx_csum_cmd_csum_enable_next; + m_axis_tx_csum_cmd_csum_start_reg <= m_axis_tx_csum_cmd_csum_start_next; + m_axis_tx_csum_cmd_csum_offset_reg <= m_axis_tx_csum_cmd_csum_offset_next; + m_axis_tx_csum_cmd_valid_reg <= m_axis_tx_csum_cmd_valid_next; + + desc_start_reg <= desc_start_next; + desc_len_reg <= desc_len_next; + + early_tx_req_status_len_reg <= early_tx_req_status_len_next; + early_tx_req_status_tag_reg <= early_tx_req_status_tag_next; + early_tx_req_status_valid_reg <= early_tx_req_status_valid_next; + + finish_tx_req_status_len_reg <= finish_tx_req_status_len_next; + finish_tx_req_status_tag_reg <= finish_tx_req_status_tag_next; + finish_tx_req_status_valid_reg <= finish_tx_req_status_valid_next; + + // descriptor table operations + if (desc_table_start_en) begin + desc_table_active[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b1; + desc_table_invalid[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_desc_fetched[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_data_fetched[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_tx_done[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_cpl_write_done[desc_table_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_queue[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_queue; + desc_table_tag[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_tag; + desc_table_pkt[desc_table_start_ptr_reg & DESC_PTR_MASK] <= desc_table_start_pkt; + desc_table_start_ptr_reg <= desc_table_start_ptr_reg + 1; + end + + if (desc_table_dequeue_en) begin + desc_table_queue_ptr[desc_table_dequeue_ptr & DESC_PTR_MASK] <= desc_table_dequeue_queue_ptr; + desc_table_cpl_queue[desc_table_dequeue_ptr & DESC_PTR_MASK] <= desc_table_dequeue_cpl_queue; + if (desc_table_dequeue_invalid) begin + desc_table_invalid[desc_table_dequeue_ptr & DESC_PTR_MASK] <= 1'b1; + end + end + + if (desc_table_desc_ctrl_en) begin + desc_table_csum_start[desc_table_desc_ctrl_ptr & DESC_PTR_MASK] <= desc_table_desc_ctrl_csum_start; + desc_table_csum_offset[desc_table_desc_ctrl_ptr & DESC_PTR_MASK] <= desc_table_desc_ctrl_csum_offset; + desc_table_csum_enable[desc_table_desc_ctrl_ptr & DESC_PTR_MASK] <= desc_table_desc_ctrl_csum_enable; + end + + if (desc_table_desc_fetched_en) begin + desc_table_len[desc_table_desc_fetched_ptr & DESC_PTR_MASK] <= desc_table_desc_fetched_len; + desc_table_desc_fetched[desc_table_desc_fetched_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_data_fetched_en) begin + desc_table_data_fetched[desc_table_data_fetched_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_tx_start_en) begin + desc_table_data_fetched[desc_table_tx_start_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_tx_start_ptr_reg <= desc_table_tx_start_ptr_reg + 1; + end + + if (desc_table_tx_finish_en) begin + desc_table_tx_done[desc_table_tx_finish_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_store_ptp_ts_en) begin + desc_table_ptp_ts[desc_table_store_ptp_ts_ptr_reg & DESC_PTR_MASK] <= desc_table_store_ptp_ts; + desc_table_store_ptp_ts_ptr_reg <= desc_table_store_ptp_ts_ptr_reg + 1; + end + + if (desc_table_cpl_enqueue_start_en) begin + desc_table_cpl_enqueue_start_ptr_reg <= desc_table_cpl_enqueue_start_ptr_reg + 1; + end + + if (desc_table_cpl_write_done_en) begin + desc_table_cpl_write_done[desc_table_cpl_write_done_ptr & DESC_PTR_MASK] <= 1'b1; + end + + if (desc_table_finish_en) begin + desc_table_active[desc_table_finish_ptr_reg & DESC_PTR_MASK] <= 1'b0; + desc_table_finish_ptr_reg <= desc_table_finish_ptr_reg + 1; + end + + if (desc_table_read_start_en) begin + desc_table_read_commit[desc_table_read_start_ptr] <= desc_table_read_start_commit; + if (desc_table_read_start_init) begin + desc_table_read_count_start[desc_table_read_start_ptr] <= desc_table_read_count_finish[desc_table_read_start_ptr] + 1; + end else begin + desc_table_read_count_start[desc_table_read_start_ptr] <= desc_table_read_count_start[desc_table_read_start_ptr] + 1; + end + end else if (desc_table_read_start_commit || desc_table_read_start_init) begin + desc_table_read_commit[desc_table_read_start_ptr] <= desc_table_read_start_commit; + if (desc_table_read_start_init) begin + desc_table_read_count_start[desc_table_read_start_ptr] <= desc_table_read_count_finish[desc_table_read_start_ptr]; + end + end + + if (desc_table_read_finish_en) begin + desc_table_read_count_finish[desc_table_read_finish_ptr] <= desc_table_read_count_finish[desc_table_read_finish_ptr] + 1; + end + + // packet table operations + if (pkt_table_start_en) begin + pkt_table_active[pkt_table_start_ptr] <= 1'b1; + end + + if (pkt_table_finish_1_en) begin + pkt_table_active[pkt_table_finish_1_ptr] <= 1'b0; + end + + if (pkt_table_finish_2_en) begin + pkt_table_active[pkt_table_finish_2_ptr] <= 1'b0; + end + + if (rst) begin + s_axis_tx_req_ready_reg <= 1'b0; + m_axis_tx_req_status_valid_reg <= 1'b0; + m_axis_desc_req_valid_reg <= 1'b0; + s_axis_desc_tready_reg <= 1'b0; + m_axis_cpl_req_valid_reg <= 1'b0; + m_axis_dma_read_desc_valid_reg <= 1'b0; + m_axis_tx_desc_valid_reg <= 1'b0; + s_axis_tx_ptp_ts_ready_reg <= 1'b0; + m_axis_tx_csum_cmd_valid_reg <= 1'b0; + + desc_start_reg <= 1'b1; + desc_len_reg <= 0; + + early_tx_req_status_valid_reg <= 1'b0; + finish_tx_req_status_valid_reg <= 1'b0; + + desc_table_active <= 0; + desc_table_invalid <= 0; + desc_table_desc_fetched <= 0; + desc_table_data_fetched <= 0; + desc_table_tx_done <= 0; + + desc_table_start_ptr_reg <= 0; + desc_table_tx_start_ptr_reg <= 0; + desc_table_store_ptp_ts_ptr_reg <= 0; + desc_table_cpl_enqueue_start_ptr_reg <= 0; + desc_table_finish_ptr_reg <= 0; + + pkt_table_active <= 0; + end +end + +endmodule diff --git a/corundum/rtl/tx_scheduler_ctrl_tdma.v b/corundum/rtl/tx_scheduler_ctrl_tdma.v new file mode 100644 index 0000000000000000000000000000000000000000..ac4498f7a60fe1809cab59bf661b925a80be18e7 --- /dev/null +++ b/corundum/rtl/tx_scheduler_ctrl_tdma.v @@ -0,0 +1,577 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * TDMA Transmit scheduler control module + */ +module tx_scheduler_ctrl_tdma # +( + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = 16, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // TDMA timeslot index width + parameter TDMA_INDEX_WIDTH = 6, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 6, + // Pipeline stages + parameter PIPELINE = 2 +) +( + input wire clk, + input wire rst, + + /* + * Scheduler control output + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_sched_ctrl_queue, + output wire m_axis_sched_ctrl_enable, + output wire m_axis_sched_ctrl_valid, + input wire m_axis_sched_ctrl_ready, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * TDMA schedule inputs + */ + input wire tdma_schedule_start, + input wire [TDMA_INDEX_WIDTH-1:0] tdma_timeslot_index, + input wire tdma_timeslot_start, + input wire tdma_timeslot_end, + input wire tdma_timeslot_active +); + +parameter WORD_WIDTH = AXIL_STRB_WIDTH; +parameter WORD_SIZE = AXIL_DATA_WIDTH/WORD_WIDTH; + +parameter QUEUE_COUNT = 2**QUEUE_INDEX_WIDTH; + +parameter QUEUE_RAM_BE_WIDTH = (2**TDMA_INDEX_WIDTH+WORD_SIZE-1)/WORD_SIZE; +parameter QUEUE_RAM_WIDTH = QUEUE_RAM_BE_WIDTH*WORD_SIZE; + +parameter ADDR_SHIFT = QUEUE_RAM_BE_WIDTH > AXIL_STRB_WIDTH ? $clog2(QUEUE_RAM_BE_WIDTH) : $clog2(AXIL_STRB_WIDTH); + +parameter SEGMENT_SHIFT = $clog2(AXIL_STRB_WIDTH); +parameter SEGMENT_AW = QUEUE_RAM_BE_WIDTH > AXIL_STRB_WIDTH ? $clog2(QUEUE_RAM_BE_WIDTH) - $clog2(AXIL_STRB_WIDTH) : 0; +parameter SEGMENT_COUNT = 2**SEGMENT_AW; + +// bus width assertions +initial begin + if (WORD_SIZE != 8) begin + $error("Error: AXI lite interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXIL_ADDR_WIDTH < QUEUE_INDEX_WIDTH+ADDR_SHIFT) begin + $error("Error: AXI lite address width too narrow (instance %m)"); + $finish; + end +end + +localparam [2:0] + STATE_IDLE = 3'd0, + STATE_FILL_START = 3'd1, + STATE_WAIT_START = 3'd2, + STATE_START_0 = 3'd3, + STATE_START_N = 3'd4, + STATE_WAIT_STOP = 3'd5, + STATE_STOP = 3'd6; + +reg [2:0] state_reg = STATE_IDLE, state_next; + +reg read_eligible; +reg write_eligible; + +reg set_inconsistent; +reg clear_inconsistent; +reg inconsistent_reg = 1'b0; + +reg last_read_reg = 1'b0, last_read_next; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_sched_ctrl_queue_reg = 1'b0, m_axis_sched_ctrl_queue_next; +reg m_axis_sched_ctrl_enable_reg = 1'b0, m_axis_sched_ctrl_enable_next; +reg m_axis_sched_ctrl_valid_reg = 1'b0, m_axis_sched_ctrl_valid_next; + +reg s_axil_awready_reg = 1'b0, s_axil_awready_next; +reg s_axil_wready_reg = 1'b0, s_axil_wready_next; +reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next; +reg s_axil_arready_reg = 1'b0, s_axil_arready_next; +reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}; +reg s_axil_rvalid_reg = 1'b0; + +// (* RAM_STYLE="BLOCK" *) +reg [QUEUE_RAM_WIDTH-1:0] queue_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_axil_ptr; +reg queue_ram_axil_wr_en; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_axil_wr_data; +reg [QUEUE_RAM_BE_WIDTH-1:0] queue_ram_axil_wr_be; +reg queue_ram_axil_rd_en; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_read_data_pipeline_reg[PIPELINE-1:0]; +reg [SEGMENT_AW-1:0] queue_ram_read_data_shift_pipeline_reg[PIPELINE-1:0]; +reg [PIPELINE-1:0] queue_ram_read_data_valid_pipeline_reg; + +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_ptr_reg = 0, queue_ram_ptr_next; + +reg [QUEUE_INDEX_WIDTH-1:0] start_queue_ts_0_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH+1-1:0] start_queue_ts_0_wr_ptr_reg = 0, start_queue_ts_0_wr_ptr_next; +reg [QUEUE_INDEX_WIDTH+1-1:0] start_queue_ts_0_rd_ptr_reg = 0, start_queue_ts_0_rd_ptr_next; +reg [QUEUE_INDEX_WIDTH-1:0] start_queue_ts_0_wr_data; +reg start_queue_ts_0_wr_en; + +reg [QUEUE_INDEX_WIDTH-1:0] start_queue_ts_n_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH+1-1:0] start_queue_ts_n_wr_ptr_reg = 0, start_queue_ts_n_wr_ptr_next; +reg [QUEUE_INDEX_WIDTH+1-1:0] start_queue_ts_n_rd_ptr_reg = 0, start_queue_ts_n_rd_ptr_next; +reg [QUEUE_INDEX_WIDTH-1:0] start_queue_ts_n_wr_data; +reg start_queue_ts_n_wr_en; + +reg [QUEUE_INDEX_WIDTH-1:0] stop_queue_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH+1-1:0] stop_queue_wr_ptr_reg = 0, stop_queue_wr_ptr_next; +reg [QUEUE_INDEX_WIDTH+1-1:0] stop_queue_rd_ptr_reg = 0, stop_queue_rd_ptr_next; +reg [QUEUE_INDEX_WIDTH-1:0] stop_queue_wr_data; +reg stop_queue_wr_en; + +reg got_start_reg = 0, got_start_next; +reg [TDMA_INDEX_WIDTH-1:0] start_index_reg = 0, start_index_next; +reg [TDMA_INDEX_WIDTH-1:0] cur_index_reg = 0, cur_index_next; +reg got_stop_reg = 0, got_stop_next; + +assign m_axis_sched_ctrl_queue = m_axis_sched_ctrl_queue_reg; +assign m_axis_sched_ctrl_enable = m_axis_sched_ctrl_enable_reg; +assign m_axis_sched_ctrl_valid = m_axis_sched_ctrl_valid_reg; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = 2'b00; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = 2'b00; +assign s_axil_rvalid = s_axil_rvalid_reg; + +integer i, j; + +initial begin + // two nested loops for smaller number of iterations per loop + // workaround for synthesizer complaints about large loop counts + for (i = 0; i < 2**QUEUE_INDEX_WIDTH; i = i + 2**(QUEUE_INDEX_WIDTH/2)) begin + for (j = i; j < i + 2**(QUEUE_INDEX_WIDTH/2); j = j + 1) begin + queue_ram[j] = 0; + end + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_read_data_pipeline_reg[i] = 0; + queue_ram_read_data_shift_pipeline_reg[i] = 0; + queue_ram_read_data_valid_pipeline_reg[i] = 0; + end +end + +always @* begin + state_next = state_reg; + + clear_inconsistent = 1'b0; + + start_queue_ts_0_wr_data = queue_ram_ptr_reg; + start_queue_ts_n_wr_data = queue_ram_ptr_reg; + start_queue_ts_0_wr_en = 1'b0; + start_queue_ts_n_wr_en = 1'b0; + stop_queue_wr_data = 0; + stop_queue_wr_en = 1'b0; + + got_start_next = got_start_reg; + start_index_next = start_index_reg; + cur_index_next = cur_index_reg; + got_stop_next = got_stop_reg; + + start_queue_ts_0_wr_ptr_next = start_queue_ts_0_wr_ptr_reg; + start_queue_ts_0_rd_ptr_next = start_queue_ts_0_rd_ptr_reg; + start_queue_ts_n_wr_ptr_next = start_queue_ts_n_wr_ptr_reg; + start_queue_ts_n_rd_ptr_next = start_queue_ts_n_rd_ptr_reg; + stop_queue_wr_ptr_next = stop_queue_wr_ptr_reg; + stop_queue_rd_ptr_next = stop_queue_rd_ptr_reg; + + queue_ram_ptr_next = queue_ram_ptr_reg; + + m_axis_sched_ctrl_queue_next = m_axis_sched_ctrl_queue_reg; + m_axis_sched_ctrl_enable_next = m_axis_sched_ctrl_enable_reg; + m_axis_sched_ctrl_valid_next = m_axis_sched_ctrl_valid_reg && !m_axis_sched_ctrl_ready; + + case (state_reg) + STATE_IDLE: begin + start_queue_ts_0_rd_ptr_next = 0; + start_queue_ts_0_wr_ptr_next = 0; + start_queue_ts_n_rd_ptr_next = 0; + start_queue_ts_n_wr_ptr_next = 0; + queue_ram_ptr_next = 0; + clear_inconsistent = 1'b1; + state_next = STATE_FILL_START; + end + STATE_FILL_START: begin + // fill up start queue + start_queue_ts_0_wr_data = queue_ram_ptr_reg; + if (queue_ram[queue_ram_ptr_reg] & 1) begin + start_queue_ts_0_wr_en = 1'b1; + start_queue_ts_0_wr_ptr_next = start_queue_ts_0_wr_ptr_reg+1; + end + + start_queue_ts_n_wr_data = queue_ram_ptr_reg; + if (queue_ram[queue_ram_ptr_reg] & (1 << cur_index_reg)) begin + start_queue_ts_n_wr_en = 1'b1; + start_queue_ts_n_wr_ptr_next = start_queue_ts_n_wr_ptr_reg+1; + end + + queue_ram_ptr_next = queue_ram_ptr_reg+1; + + if (queue_ram_ptr_reg+1 == QUEUE_COUNT) begin + state_next = STATE_WAIT_START; + end else begin + state_next = STATE_FILL_START; + end + end + STATE_WAIT_START: begin + // wait for start event + stop_queue_rd_ptr_next = 0; + stop_queue_wr_ptr_next = 0; + if (got_start_reg) begin + cur_index_next = start_index_reg+1; + if (!inconsistent_reg && start_index_reg == 0) begin + if (start_queue_ts_0_wr_ptr_reg == 0) begin + stop_queue_rd_ptr_next = 0; + got_start_next = 1'b0; + state_next = STATE_WAIT_STOP; + end else begin + state_next = STATE_START_0; + end + end else if (!inconsistent_reg && start_index_reg == cur_index_reg) begin + if (start_queue_ts_n_wr_ptr_reg == 0) begin + stop_queue_rd_ptr_next = 0; + got_start_next = 1'b0; + state_next = STATE_WAIT_STOP; + end else begin + state_next = STATE_START_N; + end + end else begin + start_queue_ts_0_rd_ptr_next = 0; + start_queue_ts_0_wr_ptr_next = 0; + start_queue_ts_n_rd_ptr_next = 0; + start_queue_ts_n_wr_ptr_next = 0; + queue_ram_ptr_next = 0; + clear_inconsistent = 1'b1; + cur_index_next = start_index_reg; + state_next = STATE_FILL_START; + end + end else begin + state_next = STATE_WAIT_START; + end + end + STATE_START_0: begin + // output start queue + if (!m_axis_sched_ctrl_valid_reg || m_axis_sched_ctrl_ready) begin + m_axis_sched_ctrl_queue_next = start_queue_ts_0_ram[start_queue_ts_0_rd_ptr_reg]; + m_axis_sched_ctrl_enable_next = 1'b1; + m_axis_sched_ctrl_valid_next = 1'b1; + + stop_queue_wr_data = start_queue_ts_0_ram[start_queue_ts_0_rd_ptr_reg]; + start_queue_ts_0_rd_ptr_next = start_queue_ts_0_rd_ptr_reg+1; + + stop_queue_wr_en = 1'b1; + stop_queue_wr_ptr_next = stop_queue_wr_ptr_reg+1; + + if (start_queue_ts_0_rd_ptr_reg+1 == start_queue_ts_0_wr_ptr_reg) begin + stop_queue_rd_ptr_next = 0; + got_start_next = 1'b0; + state_next = STATE_WAIT_STOP; + end else begin + state_next = STATE_START_0; + end + end else begin + state_next = STATE_START_0; + end + end + STATE_START_N: begin + // output start queue + if (!m_axis_sched_ctrl_valid_reg || m_axis_sched_ctrl_ready) begin + m_axis_sched_ctrl_queue_next = start_queue_ts_n_ram[start_queue_ts_n_rd_ptr_reg]; + m_axis_sched_ctrl_enable_next = 1'b1; + m_axis_sched_ctrl_valid_next = 1'b1; + + stop_queue_wr_data = start_queue_ts_n_ram[start_queue_ts_n_rd_ptr_reg]; + start_queue_ts_n_rd_ptr_next = start_queue_ts_n_rd_ptr_reg+1; + + stop_queue_wr_en = 1'b1; + stop_queue_wr_ptr_next = stop_queue_wr_ptr_reg+1; + + if (start_queue_ts_n_rd_ptr_reg+1 == start_queue_ts_n_wr_ptr_reg) begin + stop_queue_rd_ptr_next = 0; + got_start_next = 1'b0; + state_next = STATE_WAIT_STOP; + end else begin + state_next = STATE_START_N; + end + end else begin + state_next = STATE_START_N; + end + end + STATE_WAIT_STOP: begin + // wait for stop event + if (got_stop_reg) begin + state_next = STATE_STOP; + if (stop_queue_wr_ptr_reg == 0) begin + start_queue_ts_0_rd_ptr_next = 0; + start_queue_ts_0_wr_ptr_next = 0; + start_queue_ts_n_rd_ptr_next = 0; + start_queue_ts_n_wr_ptr_next = 0; + queue_ram_ptr_next = 0; + clear_inconsistent = 1'b1; + got_stop_next = 1'b0; + state_next = STATE_FILL_START; + end else begin + state_next = STATE_STOP; + end + end else begin + state_next = STATE_WAIT_STOP; + end + end + STATE_STOP: begin + // output stop queue + if (!m_axis_sched_ctrl_valid_reg || m_axis_sched_ctrl_ready) begin + m_axis_sched_ctrl_queue_next = stop_queue_ram[stop_queue_rd_ptr_reg]; + m_axis_sched_ctrl_enable_next = 1'b0; + m_axis_sched_ctrl_valid_next = 1'b1; + + stop_queue_rd_ptr_next = stop_queue_rd_ptr_reg+1; + if (stop_queue_rd_ptr_reg+1 == stop_queue_wr_ptr_reg) begin + start_queue_ts_0_rd_ptr_next = 0; + start_queue_ts_0_wr_ptr_next = 0; + start_queue_ts_n_rd_ptr_next = 0; + start_queue_ts_n_wr_ptr_next = 0; + queue_ram_ptr_next = 0; + clear_inconsistent = 1'b1; + got_stop_next = 1'b0; + state_next = STATE_FILL_START; + end else begin + state_next = STATE_STOP; + end + end else begin + state_next = STATE_STOP; + end + end + endcase + + if (!got_start_reg && tdma_timeslot_start) begin + got_start_next = 1'b1; + start_index_next = tdma_timeslot_index; + end + + if (!got_stop_reg && tdma_timeslot_end) begin + got_stop_next = 1'b1; + end +end + +always @(posedge clk) begin + state_reg <= state_next; + + got_start_reg <= got_start_next; + start_index_reg <= start_index_next; + cur_index_reg <= cur_index_next; + got_stop_reg <= got_stop_next; + + start_queue_ts_0_wr_ptr_reg <= start_queue_ts_0_wr_ptr_next; + start_queue_ts_0_rd_ptr_reg <= start_queue_ts_0_rd_ptr_next; + start_queue_ts_n_wr_ptr_reg <= start_queue_ts_n_wr_ptr_next; + start_queue_ts_n_rd_ptr_reg <= start_queue_ts_n_rd_ptr_next; + stop_queue_wr_ptr_reg <= stop_queue_wr_ptr_next; + stop_queue_rd_ptr_reg <= stop_queue_rd_ptr_next; + + queue_ram_ptr_reg <= queue_ram_ptr_next; + + m_axis_sched_ctrl_queue_reg <= m_axis_sched_ctrl_queue_next; + m_axis_sched_ctrl_enable_reg <= m_axis_sched_ctrl_enable_next; + m_axis_sched_ctrl_valid_reg <= m_axis_sched_ctrl_valid_next; + + if (start_queue_ts_0_wr_en) begin + start_queue_ts_0_ram[start_queue_ts_0_wr_ptr_reg] <= start_queue_ts_0_wr_data; + end + if (start_queue_ts_n_wr_en) begin + start_queue_ts_n_ram[start_queue_ts_n_wr_ptr_reg] <= start_queue_ts_n_wr_data; + end + if (stop_queue_wr_en) begin + stop_queue_ram[stop_queue_wr_ptr_reg] <= stop_queue_wr_data; + end + + inconsistent_reg <= (inconsistent_reg && !clear_inconsistent) || set_inconsistent; + + if (rst) begin + state_reg <= STATE_IDLE; + + inconsistent_reg <= 1'b0; + + m_axis_sched_ctrl_valid_reg <= 1'b0; + + got_start_reg <= 1'b0; + got_stop_reg <= 1'b0; + end +end + +// AXIL interface +always @* begin + queue_ram_axil_wr_en = 1'b0; + queue_ram_axil_rd_en = 1'b0; + + queue_ram_axil_ptr = 0; + queue_ram_axil_wr_data = {SEGMENT_COUNT{s_axil_wdata}}; + if (SEGMENT_COUNT > 1) begin + queue_ram_axil_wr_be = s_axil_wstrb << (((s_axil_awaddr >> SEGMENT_SHIFT) & {SEGMENT_AW{1'b1}}) * AXIL_STRB_WIDTH); + end else begin + queue_ram_axil_wr_be = s_axil_wstrb; + end + + set_inconsistent = 1'b0; + + last_read_next = last_read_reg; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + s_axil_arready_next = 1'b0; + + write_eligible = s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && (!s_axil_awready && !s_axil_wready); + read_eligible = s_axil_arvalid && (!s_axil_rvalid || s_axil_rready || ~queue_ram_read_data_valid_pipeline_reg) && (!s_axil_arready); + + if (write_eligible && (!read_eligible || last_read_reg)) begin + last_read_next = 1'b0; + + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + s_axil_bvalid_next = 1'b1; + + set_inconsistent = 1'b1; + + queue_ram_axil_wr_en = 1'b1; + + queue_ram_axil_ptr = s_axil_awaddr >> ADDR_SHIFT; + queue_ram_axil_wr_data = {SEGMENT_COUNT{s_axil_wdata}}; + if (SEGMENT_COUNT > 1) begin + queue_ram_axil_wr_be = s_axil_wstrb << (((s_axil_awaddr >> SEGMENT_SHIFT) & {SEGMENT_AW{1'b1}}) * AXIL_STRB_WIDTH); + end else begin + queue_ram_axil_wr_be = s_axil_wstrb; + end + end else if (read_eligible) begin + last_read_next = 1'b1; + + s_axil_arready_next = 1'b1; + + queue_ram_axil_rd_en = 1'b1; + queue_ram_axil_ptr = s_axil_araddr >> ADDR_SHIFT; + end +end + +always @(posedge clk) begin + last_read_reg <= last_read_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + + s_axil_arready_reg <= s_axil_arready_next; + + if (!s_axil_rvalid_reg || s_axil_rready) begin + s_axil_rdata_reg <= queue_ram_read_data_pipeline_reg[PIPELINE-1] >> (queue_ram_read_data_shift_pipeline_reg[PIPELINE-1] * AXIL_DATA_WIDTH); + s_axil_rvalid_reg <= queue_ram_read_data_valid_pipeline_reg[PIPELINE-1]; + end + + // TODO loop + if (!queue_ram_read_data_valid_pipeline_reg[PIPELINE-1] || s_axil_rready) begin + queue_ram_read_data_pipeline_reg[PIPELINE-1] <= queue_ram_read_data_pipeline_reg[0]; + queue_ram_read_data_shift_pipeline_reg[PIPELINE-1] <= queue_ram_read_data_shift_pipeline_reg[0]; + queue_ram_read_data_valid_pipeline_reg[PIPELINE-1] <= queue_ram_read_data_valid_pipeline_reg[0]; + queue_ram_read_data_valid_pipeline_reg[0] <= 1'b0; + end + + if (queue_ram_axil_rd_en) begin + queue_ram_read_data_pipeline_reg[0] <= queue_ram[queue_ram_axil_ptr]; + if (SEGMENT_COUNT > 1) begin + queue_ram_read_data_shift_pipeline_reg[0] <= (s_axil_araddr >> SEGMENT_SHIFT) & {SEGMENT_AW{1'b1}}; + end else begin + queue_ram_read_data_shift_pipeline_reg[0] <= 0; + end + queue_ram_read_data_valid_pipeline_reg[0] <= 1'b1; + end else begin + for (i = 0; i < QUEUE_RAM_BE_WIDTH; i = i + 1) begin + if (queue_ram_axil_wr_en && queue_ram_axil_wr_be[i]) begin + queue_ram[queue_ram_axil_ptr][WORD_SIZE*i +: WORD_SIZE] <= queue_ram_axil_wr_data[WORD_SIZE*i +: WORD_SIZE]; + end + end + end + + if (rst) begin + last_read_reg <= 1'b0; + + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + end +end + +endmodule diff --git a/corundum/rtl/tx_scheduler_rr.v b/corundum/rtl/tx_scheduler_rr.v new file mode 100644 index 0000000000000000000000000000000000000000..22f3830ea32e188f68e03b42616dbf2fa5508f80 --- /dev/null +++ b/corundum/rtl/tx_scheduler_rr.v @@ -0,0 +1,889 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * Transmit scheduler (round-robin) + */ +module tx_scheduler_rr # +( + // Width of AXI lite data bus in bits + parameter AXIL_DATA_WIDTH = 32, + // Width of AXI lite address bus in bits + parameter AXIL_ADDR_WIDTH = 16, + // Width of AXI lite wstrb (width of data bus in words) + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + // DMA client length field width + parameter DMA_CLIENT_LEN_WIDTH = 16, + // Transmit request tag field width + parameter REQ_TAG_WIDTH = 8, + // Number of outstanding operations + parameter OP_TABLE_SIZE = 16, + // Queue index width + parameter QUEUE_INDEX_WIDTH = 6, + // Pipeline stages + parameter PIPELINE = 2, + // Scheduler control input enable + parameter SCHED_CTRL_ENABLE = 0 +) +( + input wire clk, + input wire rst, + + /* + * Transmit request output (queue index) + */ + output wire [QUEUE_INDEX_WIDTH-1:0] m_axis_tx_req_queue, + output wire [REQ_TAG_WIDTH-1:0] m_axis_tx_req_tag, + output wire m_axis_tx_req_valid, + input wire m_axis_tx_req_ready, + + /* + * Transmit request status input + */ + input wire [DMA_CLIENT_LEN_WIDTH-1:0] s_axis_tx_req_status_len, + input wire [REQ_TAG_WIDTH-1:0] s_axis_tx_req_status_tag, + input wire s_axis_tx_req_status_valid, + + /* + * Doorbell input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_doorbell_queue, + input wire s_axis_doorbell_valid, + + /* + * Scheduler control input + */ + input wire [QUEUE_INDEX_WIDTH-1:0] s_axis_sched_ctrl_queue, + input wire s_axis_sched_ctrl_enable, + input wire s_axis_sched_ctrl_valid, + output wire s_axis_sched_ctrl_ready, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * Control + */ + input wire enable, + output wire active +); + +parameter QUEUE_COUNT = 2**QUEUE_INDEX_WIDTH; + +parameter CL_OP_TABLE_SIZE = $clog2(OP_TABLE_SIZE); + +parameter QUEUE_RAM_BE_WIDTH = 2; +parameter QUEUE_RAM_WIDTH = QUEUE_RAM_BE_WIDTH*8; + +// bus width assertions +initial begin + if (REQ_TAG_WIDTH < CL_OP_TABLE_SIZE) begin + $error("Error: REQ_TAG_WIDTH insufficient for OP_TABLE_SIZE (instance %m)"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI lite interface width must be 32 (instance %m)"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: AXI lite interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (AXIL_ADDR_WIDTH < QUEUE_INDEX_WIDTH+5) begin + $error("Error: AXI lite address width too narrow (instance %m)"); + $finish; + end + + if (PIPELINE < 2) begin + $error("Error: PIPELINE must be at least 2 (instance %m)"); + $finish; + end +end + +reg op_axil_write_pipe_hazard; +reg op_axil_read_pipe_hazard; +reg op_doorbell_pipe_hazard; +reg op_req_pipe_hazard; +reg op_complete_pipe_hazard; +reg op_ctrl_pipe_hazard; +reg op_internal_pipe_hazard; +reg stage_active; + +reg [PIPELINE-1:0] op_axil_write_pipe_reg = {PIPELINE{1'b0}}, op_axil_write_pipe_next; +reg [PIPELINE-1:0] op_axil_read_pipe_reg = {PIPELINE{1'b0}}, op_axil_read_pipe_next; +reg [PIPELINE-1:0] op_doorbell_pipe_reg = {PIPELINE{1'b0}}, op_doorbell_pipe_next; +reg [PIPELINE-1:0] op_req_pipe_reg = {PIPELINE{1'b0}}, op_req_pipe_next; +reg [PIPELINE-1:0] op_complete_pipe_reg = {PIPELINE{1'b0}}, op_complete_pipe_next; +reg [PIPELINE-1:0] op_ctrl_pipe_reg = {PIPELINE{1'b0}}, op_ctrl_pipe_next; +reg [PIPELINE-1:0] op_internal_pipe_reg = {PIPELINE{1'b0}}, op_internal_pipe_next; + +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_addr_pipeline_reg[PIPELINE-1:0], queue_ram_addr_pipeline_next[PIPELINE-1:0]; +reg [AXIL_DATA_WIDTH-1:0] write_data_pipeline_reg[PIPELINE-1:0], write_data_pipeline_next[PIPELINE-1:0]; +reg [AXIL_STRB_WIDTH-1:0] write_strobe_pipeline_reg[PIPELINE-1:0], write_strobe_pipeline_next[PIPELINE-1:0]; +reg [REQ_TAG_WIDTH-1:0] req_tag_pipeline_reg[PIPELINE-1:0], req_tag_pipeline_next[PIPELINE-1:0]; +reg [CL_OP_TABLE_SIZE-1:0] op_index_pipeline_reg[PIPELINE-1:0], op_index_pipeline_next[PIPELINE-1:0]; + +reg [QUEUE_INDEX_WIDTH-1:0] m_axis_tx_req_queue_reg = {QUEUE_INDEX_WIDTH{1'b0}}, m_axis_tx_req_queue_next; +reg [REQ_TAG_WIDTH-1:0] m_axis_tx_req_tag_reg = {REQ_TAG_WIDTH{1'b0}}, m_axis_tx_req_tag_next; +reg m_axis_tx_req_valid_reg = 1'b0, m_axis_tx_req_valid_next; + +reg s_axis_sched_ctrl_ready_reg = 1'b0, s_axis_sched_ctrl_ready_next; + +reg s_axil_awready_reg = 0, s_axil_awready_next; +reg s_axil_wready_reg = 0, s_axil_wready_next; +reg s_axil_bvalid_reg = 0, s_axil_bvalid_next; +reg s_axil_arready_reg = 0, s_axil_arready_next; +reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = 0, s_axil_rdata_next; +reg s_axil_rvalid_reg = 0, s_axil_rvalid_next; + +reg [QUEUE_RAM_WIDTH-1:0] queue_ram[QUEUE_COUNT-1:0]; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_read_ptr; +reg [QUEUE_INDEX_WIDTH-1:0] queue_ram_write_ptr; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_write_data; +reg queue_ram_wr_en; +reg [QUEUE_RAM_BE_WIDTH-1:0] queue_ram_be; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_read_data_reg = 0; +reg [QUEUE_RAM_WIDTH-1:0] queue_ram_read_data_pipeline_reg[PIPELINE-1:1]; + +wire queue_ram_read_data_enabled = queue_ram_read_data_pipeline_reg[PIPELINE-1][0]; +wire queue_ram_read_data_global_enable = queue_ram_read_data_pipeline_reg[PIPELINE-1][1]; +wire queue_ram_read_data_sched_enable = queue_ram_read_data_pipeline_reg[PIPELINE-1][2]; +wire queue_ram_read_data_active = queue_ram_read_data_pipeline_reg[PIPELINE-1][6]; +wire queue_ram_read_data_scheduled = queue_ram_read_data_pipeline_reg[PIPELINE-1][7]; +wire [CL_OP_TABLE_SIZE-1:0] queue_ram_read_data_op_tail_index = queue_ram_read_data_pipeline_reg[PIPELINE-1][15:8]; + +reg [OP_TABLE_SIZE-1:0] op_table_active = 0; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_queue[OP_TABLE_SIZE-1:0]; +reg op_table_doorbell[OP_TABLE_SIZE-1:0]; +reg op_table_is_head[OP_TABLE_SIZE-1:0]; +reg [CL_OP_TABLE_SIZE-1:0] op_table_next_index[OP_TABLE_SIZE-1:0]; +reg [CL_OP_TABLE_SIZE-1:0] op_table_prev_index[OP_TABLE_SIZE-1:0]; +wire [CL_OP_TABLE_SIZE-1:0] op_table_start_ptr; +wire op_table_start_ptr_valid; +reg [QUEUE_INDEX_WIDTH-1:0] op_table_start_queue; +reg op_table_start_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_doorbell_ptr; +reg op_table_doorbell_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_release_ptr; +reg op_table_release_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_update_next_ptr; +reg [CL_OP_TABLE_SIZE-1:0] op_table_update_next_index; +reg op_table_update_next_en; +reg [CL_OP_TABLE_SIZE-1:0] op_table_update_prev_ptr; +reg [CL_OP_TABLE_SIZE-1:0] op_table_update_prev_index; +reg op_table_update_prev_is_head; +reg op_table_update_prev_en; + +reg [CL_OP_TABLE_SIZE+1-1:0] finish_fifo_wr_ptr_reg = 0, finish_fifo_wr_ptr_next; +reg [CL_OP_TABLE_SIZE+1-1:0] finish_fifo_rd_ptr_reg = 0, finish_fifo_rd_ptr_next; +reg [REQ_TAG_WIDTH-1:0] finish_fifo_tag[(2**CL_OP_TABLE_SIZE)-1:0]; +reg finish_fifo_status[(2**CL_OP_TABLE_SIZE)-1:0]; +reg finish_fifo_we; +reg [REQ_TAG_WIDTH-1:0] finish_fifo_wr_tag; +reg finish_fifo_wr_status; + +reg [CL_OP_TABLE_SIZE-1:0] finish_ptr_reg = {CL_OP_TABLE_SIZE{1'b0}}, finish_ptr_next; +reg finish_status_reg = 1'b0, finish_status_next; +reg finish_valid_reg = 1'b0, finish_valid_next; + +reg init_reg = 1'b0, init_next; +reg [QUEUE_INDEX_WIDTH-1:0] init_index_reg = 0, init_index_next; + +reg [QUEUE_INDEX_WIDTH:0] active_queue_count_reg = 0, active_queue_count_next; + +assign m_axis_tx_req_queue = m_axis_tx_req_queue_reg; +assign m_axis_tx_req_tag = m_axis_tx_req_tag_reg; +assign m_axis_tx_req_valid = m_axis_tx_req_valid_reg; + +assign s_axis_sched_ctrl_ready = s_axis_sched_ctrl_ready_reg; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = 2'b00; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = s_axil_rdata_reg; +assign s_axil_rresp = 2'b00; +assign s_axil_rvalid = s_axil_rvalid_reg; + +assign active = active_queue_count_reg != 0; + +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_awaddr_queue = s_axil_awaddr >> 2; +wire [QUEUE_INDEX_WIDTH-1:0] s_axil_araddr_queue = s_axil_araddr >> 2; + +wire queue_tail_active = op_table_active[queue_ram_read_data_op_tail_index] && op_table_queue[queue_ram_read_data_op_tail_index] == queue_ram_addr_pipeline_reg[PIPELINE-1]; + +wire [QUEUE_INDEX_WIDTH-1:0] axis_doorbell_fifo_queue; +wire axis_doorbell_fifo_valid; +reg axis_doorbell_fifo_ready; + +axis_fifo #( + .DEPTH(256), + .DATA_WIDTH(QUEUE_INDEX_WIDTH), + .KEEP_ENABLE(0), + .KEEP_WIDTH(1), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +doorbell_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(s_axis_doorbell_queue), + .s_axis_tkeep(0), + .s_axis_tvalid(s_axis_doorbell_valid), + .s_axis_tready(), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata(axis_doorbell_fifo_queue), + .m_axis_tkeep(), + .m_axis_tvalid(axis_doorbell_fifo_valid), + .m_axis_tready(axis_doorbell_fifo_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +reg [QUEUE_INDEX_WIDTH-1:0] axis_scheduler_fifo_in_queue; +reg axis_scheduler_fifo_in_valid; +wire axis_scheduler_fifo_in_ready; + +wire [QUEUE_INDEX_WIDTH-1:0] axis_scheduler_fifo_out_queue; +wire axis_scheduler_fifo_out_valid; +reg axis_scheduler_fifo_out_ready; + +axis_fifo #( + .DEPTH(2**QUEUE_INDEX_WIDTH), + .DATA_WIDTH(QUEUE_INDEX_WIDTH), + .KEEP_ENABLE(0), + .KEEP_WIDTH(1), + .LAST_ENABLE(0), + .ID_ENABLE(0), + .DEST_ENABLE(0), + .USER_ENABLE(0), + .FRAME_FIFO(0) +) +rr_fifo ( + .clk(clk), + .rst(rst), + + // AXI input + .s_axis_tdata(axis_scheduler_fifo_in_queue), + .s_axis_tkeep(0), + .s_axis_tvalid(axis_scheduler_fifo_in_valid), + .s_axis_tready(axis_scheduler_fifo_in_ready), + .s_axis_tlast(0), + .s_axis_tid(0), + .s_axis_tdest(0), + .s_axis_tuser(0), + + // AXI output + .m_axis_tdata(axis_scheduler_fifo_out_queue), + .m_axis_tkeep(), + .m_axis_tvalid(axis_scheduler_fifo_out_valid), + .m_axis_tready(axis_scheduler_fifo_out_ready), + .m_axis_tlast(), + .m_axis_tid(), + .m_axis_tdest(), + .m_axis_tuser(), + + // Status + .status_overflow(), + .status_bad_frame(), + .status_good_frame() +); + +priority_encoder #( + .WIDTH(OP_TABLE_SIZE), + .LSB_PRIORITY("HIGH") +) +op_table_start_enc_inst ( + .input_unencoded(~op_table_active), + .output_valid(op_table_start_ptr_valid), + .output_encoded(op_table_start_ptr), + .output_unencoded() +); + +integer i; + +initial begin + for (i = 0; i < QUEUE_COUNT; i = i + 1) begin + queue_ram[i] = 0; + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] = 0; + write_data_pipeline_reg[i] = 0; + write_strobe_pipeline_reg[i] = 0; + req_tag_pipeline_reg[i] = 0; + end + + for (i = 0; i < OP_TABLE_SIZE; i = i + 1) begin + op_table_queue[i] = 0; + op_table_next_index[i] = 0; + op_table_prev_index[i] = 0; + op_table_doorbell[i] = 0; + op_table_is_head[i] = 0; + end +end + +integer j; + +always @* begin + op_axil_write_pipe_next = {op_axil_write_pipe_reg, 1'b0}; + op_axil_read_pipe_next = {op_axil_read_pipe_reg, 1'b0}; + op_doorbell_pipe_next = {op_doorbell_pipe_reg, 1'b0}; + op_req_pipe_next = {op_req_pipe_reg, 1'b0}; + op_complete_pipe_next = {op_complete_pipe_reg, 1'b0}; + op_ctrl_pipe_next = {op_ctrl_pipe_reg, 1'b0}; + op_internal_pipe_next = {op_internal_pipe_reg, 1'b0}; + + queue_ram_addr_pipeline_next[0] = 0; + write_data_pipeline_next[0] = 0; + write_strobe_pipeline_next[0] = 0; + req_tag_pipeline_next[0] = 0; + op_index_pipeline_next[0] = 0; + for (j = 1; j < PIPELINE; j = j + 1) begin + queue_ram_addr_pipeline_next[j] = queue_ram_addr_pipeline_reg[j-1]; + write_data_pipeline_next[j] = write_data_pipeline_reg[j-1]; + write_strobe_pipeline_next[j] = write_strobe_pipeline_reg[j-1]; + req_tag_pipeline_next[j] = req_tag_pipeline_reg[j-1]; + op_index_pipeline_next[j] = op_index_pipeline_reg[j-1]; + end + + m_axis_tx_req_queue_next = m_axis_tx_req_queue_reg; + m_axis_tx_req_tag_next = m_axis_tx_req_tag_reg; + m_axis_tx_req_valid_next = m_axis_tx_req_valid_reg && !m_axis_tx_req_ready; + + s_axis_sched_ctrl_ready_next = 1'b0; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + s_axil_arready_next = 1'b0; + s_axil_rdata_next = s_axil_rdata_reg; + s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready; + + queue_ram_read_ptr = 0; + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data = queue_ram_read_data_pipeline_reg[PIPELINE-1]; + queue_ram_wr_en = 0; + queue_ram_be = 0; + + op_table_start_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + op_table_start_en = 1'b0; + op_table_doorbell_ptr = queue_ram_read_data_op_tail_index; + op_table_doorbell_en = 1'b0; + op_table_release_ptr = op_index_pipeline_reg[PIPELINE-1]; + op_table_release_en = 1'b0; + op_table_update_next_ptr = queue_ram_read_data_op_tail_index; + op_table_update_next_index = op_index_pipeline_reg[PIPELINE-1]; + op_table_update_next_en = 1'b0; + op_table_update_prev_ptr = op_index_pipeline_reg[PIPELINE-1]; + op_table_update_prev_index = queue_ram_read_data_op_tail_index; + op_table_update_prev_is_head = !(queue_tail_active && op_index_pipeline_reg[PIPELINE-1] != queue_ram_read_data_op_tail_index); + op_table_update_prev_en = 1'b0; + + finish_fifo_rd_ptr_next = finish_fifo_rd_ptr_reg; + finish_fifo_wr_ptr_next = finish_fifo_wr_ptr_reg; + finish_fifo_we = 1'b0; + finish_fifo_wr_tag = s_axis_tx_req_status_tag; + finish_fifo_wr_status = s_axis_tx_req_status_len != 0; + + finish_ptr_next = finish_ptr_reg; + finish_status_next = finish_status_reg; + finish_valid_next = finish_valid_reg; + + init_next = init_reg; + init_index_next = init_index_reg; + + active_queue_count_next = active_queue_count_reg; + + axis_doorbell_fifo_ready = 1'b0; + + axis_scheduler_fifo_in_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + axis_scheduler_fifo_in_valid = 1'b0; + + axis_scheduler_fifo_out_ready = 1'b0; + + op_axil_write_pipe_hazard = 1'b0; + op_axil_read_pipe_hazard = 1'b0; + op_doorbell_pipe_hazard = 1'b0; + op_req_pipe_hazard = 1'b0; + op_complete_pipe_hazard = 1'b0; + op_ctrl_pipe_hazard = 1'b0; + op_internal_pipe_hazard = 1'b0; + stage_active = 1'b0; + + for (j = 0; j < PIPELINE; j = j + 1) begin + stage_active = op_axil_write_pipe_reg[j] || op_axil_read_pipe_reg[j] || op_doorbell_pipe_reg[j] || op_req_pipe_reg[j] || op_complete_pipe_reg[j] || op_ctrl_pipe_reg[j] || op_internal_pipe_reg[j]; + op_axil_write_pipe_hazard = op_axil_write_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_awaddr_queue); + op_axil_read_pipe_hazard = op_axil_read_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axil_araddr_queue); + op_doorbell_pipe_hazard = op_doorbell_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == axis_doorbell_fifo_queue); + op_req_pipe_hazard = op_req_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == axis_scheduler_fifo_out_queue); + op_complete_pipe_hazard = op_complete_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == op_table_queue[finish_ptr_reg]); + op_ctrl_pipe_hazard = op_ctrl_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == s_axis_sched_ctrl_queue); + op_internal_pipe_hazard = op_internal_pipe_hazard || (stage_active && queue_ram_addr_pipeline_reg[j] == init_index_reg); + end + + // pipeline stage 0 - receive request + if (!init_reg && !op_internal_pipe_hazard) begin + // init queue states + op_internal_pipe_next[0] = 1'b1; + + init_index_next = init_index_reg + 1; + + queue_ram_read_ptr = init_index_reg; + queue_ram_addr_pipeline_next[0] = init_index_reg; + + if (init_index_reg == {QUEUE_INDEX_WIDTH{1'b1}}) begin + init_next = 1'b1; + end + end else if (s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && !op_axil_write_pipe_reg[0] && !op_axil_write_pipe_hazard) begin + // AXIL write + op_axil_write_pipe_next[0] = 1'b1; + + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + + write_data_pipeline_next[0] = s_axil_wdata; + write_strobe_pipeline_next[0] = s_axil_wstrb; + + queue_ram_read_ptr = s_axil_awaddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_awaddr_queue; + end else if (s_axil_arvalid && (!s_axil_rvalid || s_axil_rready) && !op_axil_read_pipe_reg[0] && !op_axil_read_pipe_hazard) begin + // AXIL read + op_axil_read_pipe_next[0] = 1'b1; + + s_axil_arready_next = 1'b1; + + queue_ram_read_ptr = s_axil_araddr_queue; + queue_ram_addr_pipeline_next[0] = s_axil_araddr_queue; + end else if (axis_doorbell_fifo_valid && !op_doorbell_pipe_hazard) begin + // handle doorbell + op_doorbell_pipe_next[0] = 1'b1; + + axis_doorbell_fifo_ready = 1'b1; + + queue_ram_read_ptr = axis_doorbell_fifo_queue; + queue_ram_addr_pipeline_next[0] = axis_doorbell_fifo_queue; + end else if (finish_valid_reg && !op_complete_pipe_reg[0] && !op_complete_pipe_hazard) begin + // transmit complete + op_complete_pipe_next[0] = 1'b1; + + write_data_pipeline_next[0][0] = finish_status_reg || op_table_doorbell[finish_ptr_reg]; + op_index_pipeline_next[0] = finish_ptr_reg; + + finish_valid_next = 1'b0; + + queue_ram_read_ptr = op_table_queue[finish_ptr_reg]; + queue_ram_addr_pipeline_next[0] = op_table_queue[finish_ptr_reg]; + end else if (SCHED_CTRL_ENABLE && s_axis_sched_ctrl_valid && !op_ctrl_pipe_reg[0] && !op_ctrl_pipe_hazard) begin + // Scheduler control + op_ctrl_pipe_next[0] = 1'b1; + + s_axis_sched_ctrl_ready_next = 1'b1; + + write_data_pipeline_next[0] = s_axis_sched_ctrl_enable; + + queue_ram_read_ptr = s_axis_sched_ctrl_queue; + queue_ram_addr_pipeline_next[0] = s_axis_sched_ctrl_queue; + end else if (enable && op_table_start_ptr_valid && axis_scheduler_fifo_out_valid && (!m_axis_tx_req_valid || m_axis_tx_req_ready) && !op_req_pipe_reg && !op_req_pipe_hazard) begin + // transmit request + op_req_pipe_next[0] = 1'b1; + + op_table_start_en = 1'b1; + op_table_start_queue = axis_scheduler_fifo_out_queue; + + op_index_pipeline_next[0] = op_table_start_ptr; + + axis_scheduler_fifo_out_ready = 1'b1; + + queue_ram_read_ptr = axis_scheduler_fifo_out_queue; + queue_ram_addr_pipeline_next[0] = axis_scheduler_fifo_out_queue; + end + + // read complete, perform operation + if (op_internal_pipe_reg[PIPELINE-1]) begin + // internal operation + + // init queue state + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data[0] = 1'b0; // queue enabled + if (SCHED_CTRL_ENABLE) begin + queue_ram_write_data[1] = 1'b0; // queue global enable + queue_ram_write_data[2] = 1'b0; // queue sched enable + end + queue_ram_write_data[6] = 1'b0; // queue active + queue_ram_write_data[7] = 1'b0; // queue scheduled + queue_ram_be[0] = 1'b1; + queue_ram_wr_en = 1'b1; + end else if (op_doorbell_pipe_reg[PIPELINE-1]) begin + // handle doorbell + + // mark queue active + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data[6] = 1'b1; // queue active + queue_ram_be[0] = 1'b1; + queue_ram_wr_en = 1'b1; + + // schedule queue if necessary + if (queue_ram_read_data_enabled && (!SCHED_CTRL_ENABLE || queue_ram_read_data_global_enable || queue_ram_read_data_sched_enable) && !queue_ram_read_data_scheduled) begin + queue_ram_write_data[7] = 1'b1; // queue scheduled + + axis_scheduler_fifo_in_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + axis_scheduler_fifo_in_valid = 1'b1; + + active_queue_count_next = active_queue_count_reg + 1; + end + + if (queue_tail_active) begin + // record doorbell in table so we don't lose it + op_table_doorbell_ptr = queue_ram_read_data_op_tail_index; + op_table_doorbell_en = 1'b1; + end + end else if (op_req_pipe_reg[PIPELINE-1]) begin + // transmit request + m_axis_tx_req_queue_next = queue_ram_addr_pipeline_reg[PIPELINE-1]; + m_axis_tx_req_tag_next = op_index_pipeline_reg[PIPELINE-1]; + + axis_scheduler_fifo_in_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + + // update state + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_write_data[15:8] = op_index_pipeline_reg[PIPELINE-1]; // tail index + queue_ram_be[0] = 1'b1; + queue_ram_wr_en = 1'b1; + + op_table_update_prev_ptr = op_index_pipeline_reg[PIPELINE-1]; + op_table_update_prev_index = queue_ram_read_data_op_tail_index; + op_table_update_prev_is_head = !(queue_tail_active && op_index_pipeline_reg[PIPELINE-1] != queue_ram_read_data_op_tail_index); + + op_table_update_next_ptr = queue_ram_read_data_op_tail_index; + op_table_update_next_index = op_index_pipeline_reg[PIPELINE-1]; + + if (queue_ram_read_data_enabled && (!SCHED_CTRL_ENABLE || queue_ram_read_data_global_enable || queue_ram_read_data_sched_enable) && queue_ram_read_data_active && queue_ram_read_data_scheduled) begin + // queue enabled, active, and scheduled + + // issue transmit request + m_axis_tx_req_valid_next = 1'b1; + + // reschedule + axis_scheduler_fifo_in_valid = 1'b1; + + // update state + queue_ram_write_data[7] = 1'b1; // queue scheduled + queue_ram_be[1] = 1'b1; // tail index + + op_table_update_prev_en = 1'b1; + op_table_update_next_en = queue_tail_active && op_index_pipeline_reg[PIPELINE-1] != queue_ram_read_data_op_tail_index; + end else begin + // queue not enabled, not active, or not scheduled + // deschedule queue + + op_table_release_ptr = op_index_pipeline_reg[PIPELINE-1]; + op_table_release_en = 1'b1; + + // update state + queue_ram_write_data[7] = 1'b0; // queue scheduled + + if (queue_ram_read_data_scheduled) begin + active_queue_count_next = active_queue_count_reg - 1; + end + end + end else if (op_complete_pipe_reg[PIPELINE-1]) begin + // tx complete + + // update state + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_be[0] = 1'b1; + queue_ram_wr_en = 1'b1; + + op_table_update_prev_ptr = op_table_next_index[op_index_pipeline_reg[PIPELINE-1]]; + op_table_update_prev_index = op_table_prev_index[op_index_pipeline_reg[PIPELINE-1]]; + op_table_update_prev_is_head = op_table_is_head[op_index_pipeline_reg[PIPELINE-1]]; + op_table_update_prev_en = op_index_pipeline_reg[PIPELINE-1] != queue_ram_read_data_op_tail_index; // our next pointer only valid if we're not the tail + + op_table_update_next_ptr = op_table_prev_index[op_index_pipeline_reg[PIPELINE-1]]; + op_table_update_next_index = op_table_next_index[op_index_pipeline_reg[PIPELINE-1]]; + op_table_update_next_en = !op_table_is_head[op_index_pipeline_reg[PIPELINE-1]]; // our prev index only valid if we're not the head element + + op_table_doorbell_ptr = op_table_prev_index[op_index_pipeline_reg[PIPELINE-1]]; + op_table_doorbell_en = !op_table_is_head[op_index_pipeline_reg[PIPELINE-1]] && op_table_doorbell[op_index_pipeline_reg[PIPELINE-1]];; + + op_table_release_ptr = op_index_pipeline_reg[PIPELINE-1]; + op_table_release_en = 1'b1; + + if (write_data_pipeline_reg[PIPELINE-1][0]) begin + queue_ram_write_data[6] = 1'b1; // queue active + + // schedule if disabled + if ((!SCHED_CTRL_ENABLE || write_data_pipeline_reg[PIPELINE-1][1] || queue_ram_read_data_sched_enable) && !queue_ram_read_data_scheduled) begin + queue_ram_write_data[7] = 1'b1; // queue scheduled + + axis_scheduler_fifo_in_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + axis_scheduler_fifo_in_valid = 1'b1; + + active_queue_count_next = active_queue_count_reg + 1; + end + end else begin + queue_ram_write_data[6] = 1'b0; // queue active + end + end else if (SCHED_CTRL_ENABLE && op_ctrl_pipe_reg[PIPELINE-1]) begin + // Scheduler control + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_wr_en = 1'b1; + + queue_ram_write_data[2] = write_data_pipeline_reg[PIPELINE-1][0]; // queue sched enable + queue_ram_be[0] = 1'b1; + + // schedule if disabled + if (queue_ram_read_data_enabled && queue_ram_read_data_active && (queue_ram_read_data_global_enable || write_data_pipeline_reg[PIPELINE-1][0]) && !queue_ram_read_data_scheduled) begin + queue_ram_write_data[7] = 1'b1; // queue scheduled + + axis_scheduler_fifo_in_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + axis_scheduler_fifo_in_valid = 1'b1; + + active_queue_count_next = active_queue_count_reg + 1; + end + end else if (op_axil_write_pipe_reg[PIPELINE-1]) begin + // AXIL write + s_axil_bvalid_next = 1'b1; + + queue_ram_write_ptr = queue_ram_addr_pipeline_reg[PIPELINE-1]; + queue_ram_wr_en = 1'b1; + + queue_ram_write_data[0] = write_data_pipeline_reg[PIPELINE-1][0]; // queue enabled + queue_ram_write_data[1] = write_data_pipeline_reg[PIPELINE-1][1]; // queue global enable + queue_ram_be[0] = write_strobe_pipeline_reg[PIPELINE-1][0]; + + // schedule if disabled + if (write_data_pipeline_reg[PIPELINE-1][0] && queue_ram_read_data_active && (!SCHED_CTRL_ENABLE || write_data_pipeline_reg[PIPELINE-1][1] || queue_ram_read_data_sched_enable) && !queue_ram_read_data_scheduled) begin + queue_ram_write_data[7] = 1'b1; // queue scheduled + + axis_scheduler_fifo_in_queue = queue_ram_addr_pipeline_reg[PIPELINE-1]; + axis_scheduler_fifo_in_valid = 1'b1; + + active_queue_count_next = active_queue_count_reg + 1; + end + end else if (op_axil_read_pipe_reg[PIPELINE-1]) begin + // AXIL read + s_axil_rvalid_next = 1'b1; + s_axil_rdata_next = 0; + + s_axil_rdata_next[0] = queue_ram_read_data_enabled; + if (SCHED_CTRL_ENABLE) begin + s_axil_rdata_next[1] = queue_ram_read_data_global_enable; + s_axil_rdata_next[2] = queue_ram_read_data_sched_enable; + end + s_axil_rdata_next[16] = queue_ram_read_data_active; + s_axil_rdata_next[24] = queue_ram_read_data_scheduled; + end + + // finish transmit operation + if (s_axis_tx_req_status_valid) begin + finish_fifo_we = 1'b1; + finish_fifo_wr_tag = s_axis_tx_req_status_tag; + finish_fifo_wr_status = s_axis_tx_req_status_len != 0; + finish_fifo_wr_ptr_next = finish_fifo_wr_ptr_reg + 1; + end + + if (!finish_valid_reg && finish_fifo_wr_ptr_reg != finish_fifo_rd_ptr_reg) begin + finish_ptr_next = finish_fifo_tag[finish_fifo_rd_ptr_reg[CL_OP_TABLE_SIZE-1:0]]; + finish_status_next = finish_fifo_status[finish_fifo_rd_ptr_reg[CL_OP_TABLE_SIZE-1:0]]; + finish_valid_next = 1'b1; + finish_fifo_rd_ptr_next = finish_fifo_rd_ptr_reg + 1; + end +end + +always @(posedge clk) begin + if (rst) begin + op_axil_write_pipe_reg <= {PIPELINE{1'b0}}; + op_axil_read_pipe_reg <= {PIPELINE{1'b0}}; + op_doorbell_pipe_reg <= {PIPELINE{1'b0}}; + op_req_pipe_reg <= {PIPELINE{1'b0}}; + op_complete_pipe_reg <= {PIPELINE{1'b0}}; + op_ctrl_pipe_reg <= {PIPELINE{1'b0}}; + op_internal_pipe_reg <= {PIPELINE{1'b0}}; + + finish_fifo_rd_ptr_reg <= {CL_OP_TABLE_SIZE+1{1'b0}}; + finish_fifo_wr_ptr_reg <= {CL_OP_TABLE_SIZE+1{1'b0}}; + + finish_valid_reg <= 1'b0; + + m_axis_tx_req_valid_reg <= 1'b0; + + s_axis_sched_ctrl_ready_reg <= 1'b0; + + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + + init_reg <= 1'b0; + init_index_reg <= 0; + + active_queue_count_reg <= 0; + + op_table_active <= 0; + end else begin + op_axil_write_pipe_reg <= op_axil_write_pipe_next; + op_axil_read_pipe_reg <= op_axil_read_pipe_next; + op_doorbell_pipe_reg <= op_doorbell_pipe_next; + op_req_pipe_reg <= op_req_pipe_next; + op_complete_pipe_reg <= op_complete_pipe_next; + op_ctrl_pipe_reg <= op_ctrl_pipe_next; + op_internal_pipe_reg <= op_internal_pipe_next; + + finish_fifo_rd_ptr_reg <= finish_fifo_rd_ptr_next; + finish_fifo_wr_ptr_reg <= finish_fifo_wr_ptr_next; + + finish_valid_reg <= finish_valid_next; + + m_axis_tx_req_valid_reg <= m_axis_tx_req_valid_next; + + s_axis_sched_ctrl_ready_reg <= s_axis_sched_ctrl_ready_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + init_reg <= init_next; + init_index_reg <= init_index_next; + + active_queue_count_reg <= active_queue_count_next; + + if (op_table_start_en) begin + op_table_active[op_table_start_ptr] <= 1'b1; + end + if (op_table_release_en) begin + op_table_active[op_table_release_ptr] <= 1'b0; + end + end + + for (i = 0; i < PIPELINE; i = i + 1) begin + queue_ram_addr_pipeline_reg[i] <= queue_ram_addr_pipeline_next[i]; + write_data_pipeline_reg[i] <= write_data_pipeline_next[i]; + write_strobe_pipeline_reg[i] <= write_strobe_pipeline_next[i]; + req_tag_pipeline_reg[i] <= req_tag_pipeline_next[i]; + op_index_pipeline_reg[i] <= op_index_pipeline_next[i]; + end + + finish_ptr_reg <= finish_ptr_next; + finish_status_reg <= finish_status_next; + + m_axis_tx_req_queue_reg <= m_axis_tx_req_queue_next; + m_axis_tx_req_tag_reg <= m_axis_tx_req_tag_next; + + s_axil_rdata_reg <= s_axil_rdata_next; + + if (queue_ram_wr_en) begin + for (i = 0; i < QUEUE_RAM_BE_WIDTH; i = i + 1) begin + if (queue_ram_be[i]) begin + queue_ram[queue_ram_write_ptr][i*8 +: 8] <= queue_ram_write_data[i*8 +: 8]; + end + end + end + queue_ram_read_data_reg <= queue_ram[queue_ram_read_ptr]; + queue_ram_read_data_pipeline_reg[1] <= queue_ram_read_data_reg; + for (i = 2; i < PIPELINE; i = i + 1) begin + queue_ram_read_data_pipeline_reg[i] <= queue_ram_read_data_pipeline_reg[i-1]; + end + + if (op_table_start_en) begin + op_table_queue[op_table_start_ptr] <= op_table_start_queue; + op_table_doorbell[op_table_start_ptr] <= 1'b0; + end + if (op_table_doorbell_en) begin + op_table_doorbell[op_table_doorbell_ptr] <= 1'b1; + end + if (op_table_update_next_en) begin + op_table_next_index[op_table_update_next_ptr] <= op_table_update_next_index; + end + if (op_table_update_prev_en) begin + op_table_prev_index[op_table_update_prev_ptr] <= op_table_update_prev_index; + op_table_is_head[op_table_update_prev_ptr] <= op_table_update_prev_is_head; + end + + if (finish_fifo_we) begin + finish_fifo_tag[finish_fifo_wr_ptr_reg[CL_OP_TABLE_SIZE-1:0]] <= finish_fifo_wr_tag; + finish_fifo_status[finish_fifo_wr_ptr_reg[CL_OP_TABLE_SIZE-1:0]] <= finish_fifo_wr_status; + end +end + +endmodule