From 86b611c1f7d86b0609fa1c3707175ca7abe40d07 Mon Sep 17 00:00:00 2001 From: Istvan Csomortani Date: Mon, 15 Mar 2021 08:50:39 +0000 Subject: [PATCH] data_offload: Initial commit --- library/Makefile | 2 + library/data_offload/Makefile | 21 + library/data_offload/README.md | 650 ++++++++++++++++++ library/data_offload/data_offload.v | 413 +++++++++++ library/data_offload/data_offload_constr.ttcl | 177 +++++ library/data_offload/data_offload_control.v | 44 ++ library/data_offload/data_offload_fsm.v | 565 +++++++++++++++ library/data_offload/data_offload_ip.tcl | 235 +++++++ library/data_offload/data_offload_regmap.v | 375 ++++++++++ library/data_offload/data_offload_sv.ttcl | 55 ++ 10 files changed, 2537 insertions(+) create mode 100644 library/data_offload/Makefile create mode 100644 library/data_offload/README.md create mode 100644 library/data_offload/data_offload.v create mode 100644 library/data_offload/data_offload_constr.ttcl create mode 100644 library/data_offload/data_offload_control.v create mode 100644 library/data_offload/data_offload_fsm.v create mode 100644 library/data_offload/data_offload_ip.tcl create mode 100644 library/data_offload/data_offload_regmap.v create mode 100644 library/data_offload/data_offload_sv.ttcl diff --git a/library/Makefile b/library/Makefile index 4b02d13c1..ebecfeea5 100644 --- a/library/Makefile +++ b/library/Makefile @@ -61,6 +61,7 @@ clean: $(MAKE) -C cn0363/cn0363_dma_sequencer clean $(MAKE) -C cn0363/cn0363_phase_data_sync clean $(MAKE) -C cordic_demod clean + $(MAKE) -C data_offload clean $(MAKE) -C intel/adi_jesd204 clean $(MAKE) -C intel/avl_adxcfg clean $(MAKE) -C intel/avl_adxcvr clean @@ -177,6 +178,7 @@ lib: $(MAKE) -C cn0363/cn0363_dma_sequencer $(MAKE) -C cn0363/cn0363_phase_data_sync $(MAKE) -C cordic_demod + $(MAKE) -C data_offload $(MAKE) -C intel/adi_jesd204 $(MAKE) -C intel/avl_adxcfg $(MAKE) -C intel/avl_adxcvr diff --git a/library/data_offload/Makefile b/library/data_offload/Makefile new file mode 100644 index 000000000..ff75608f1 --- /dev/null +++ b/library/data_offload/Makefile @@ -0,0 +1,21 @@ +#################################################################################### +## Copyright 2018(c) Analog Devices, Inc. +## Auto-generated, do not modify! +#################################################################################### + +LIBRARY_NAME := data_offload + +GENERIC_DEPS += ../common/up_axi.v +GENERIC_DEPS += ../common/ad_mem_asym.v +GENERIC_DEPS += ../common/ad_axis_inf_rx.v +GENERIC_DEPS += data_offload_fsm.v +GENERIC_DEPS += data_offload_regmap.v +GENERIC_DEPS += data_offload.v + +XILINX_DEPS += data_offload_constr.ttcl +XILINX_DEPS += data_offload_ip.tcl + +XILINX_LIB_DEPS += util_axis_fifo_asym +XILINX_LIB_DEPS += util_cdc + +include ../scripts/library.mk diff --git a/library/data_offload/README.md b/library/data_offload/README.md new file mode 100644 index 000000000..6297fc3ae --- /dev/null +++ b/library/data_offload/README.md @@ -0,0 +1,650 @@ + +# Data offload IP core + +## Description, general use cases + +Data offload module for high-speed converters: + +**NOTE**: This IP will always have a storage unit (internal or external to the FPGA) and is +designed to handle high data rates. If your data paths will run in a lower data +rate, and your intention is just to transfer the data to another clock domain or +to adjust the bus width of the data path, you must to use another IP. + + * in case of DAC, the DMA initialize the storage unit, after that the controller +will push the data to the DAC interface in one-shot or cyclic way, until the next initialization + + * in case of ADC, the DMA request a transfer, the controller will save the data into +the storage unit, after that will push it to the DMA + + * BYPASS mode: simple streaming FIFO in case of clock rate or data width differences +between source and sink interfaces (data rate MUST match in order to work); the BYPASS +mode is used when an initially high rate path is downgraded to lower rates. + +## Table of content + + * [Block diagrams](README.md#block-diagram) + * [Parameters](README.md#parameters) + * [Interfaces](README.md#interfaces) + * [Register map](README.md#register-map) + * [Clock tree](README.md#clock-tree) + * [Data path](README.md#data-path) + * [Control path](README.md#control-path-offload-fsm) + +## Generic arhitecture + +The main role of our data paths, is to stream data from point A to point B +in a particular system. There are always a SOURCE and a DESTINATION +point, which can be a device (ADC or DAC), a DMA (for system memory) or any other +data processing IP. + +In the context of Data Offload IP, we don't need to know who is the source and +who is the destination. Both interface is a AXI4 Stream interface, which can be +supported in both Xilinx's an Intel's architecture, and can be connected to any device +core or DMA. + +The storage unit is connected to the Data Offload controller via two FIFO interface. +This way the same controller can be used for various storage solutions. (BRAM, +URAM, external memory etc.) + +## Block diagram + +![Generic Block Diagram](./docs/do_arch.svg) + +## Parameters + +| NAME | TYPE | DEFAULT | DESCRIPTION | +|----------------------|:-----------:|:----------:|:---------------------------:| +|ID | integer | 0 | Instance ID number | +|MEM_TYPE | [ 0:0] | 0 | Define the used storage type: FPGA RAM - 0; external DDR - 1 | +|MEM_SIZE | [31:0] | 1024 | Define the size of the storage element | +|RX_ENABLE | [ 0:0] | 1 | Enable/disable the ADC path | +|RX_FRONTEND_IF | [ 0:0] | 0 | M_AXIS - 0; FIFO_RD - 1 (FRONTEND is the DMA side) | +|RX_BACKEND_IF | [ 0:0] | 0 | S_AXIS - 0; FIFO_WR - 1 (BACKEND is the device side) | +|RX_FRONTEND_DATA_WIDTH| integer | 64 | The data width of the RX frontend interface, it depends of the dma configuration | +|RX_BACKEND_DATA_WIDTH | integer | 64 | The data width of the RX backend interface, it depends of the device core configuration | +|RX_RAW_DATA_EN | [ 0:0] | 1 | Enables a gearbox module in the RX path, so only the raw samples will be stored in the memory. | +|TX_ENABLE | [ 0:0] | 1 | Enable/disable the DAC path | +|TX_FRONTEND_IF | [ 0:0] | 0 | S_AXIS - 0; FIFO_WR - 1 (FRONTEND is the DMA side) | +|TX_BACKEND_IF | [ 0:0] | 0 | M_AXIS - 0; FIFO_RD - 1 (BACKEND is the device side) | +|TX_FRONTEND_DATA_WIDTH| integer | 64 | The data width of the TX frontend interface, it depends of the dma configuration | +|TX_BACKEND_DATA_WIDTH | integer | 64 | The data width of the TX backend interface, it depends of the device core configuration | +|TX_RAW_DATA_EN | [ 0:0] | 1 | Enables a gearbox module in the TX path, so only the raw samples will be stored in the memory. | +|MEMC_UIF_TYPE | [ 0:0] | 0 | AXI_MM - 0; AVL_MM - 1 | +|MEMC_UIF_DATA_WIDTH | [ 0:0] | 512 | The valid data depends on the DDRx memory controller IP. | +|MEMC_UIF_ADDRESS_WIDTH| integer | 25 | The valid data depends on the DDRx memory controller IP. | +|MEMC_RX_BADDRESS | [31:0] |32'h000000 | DDR base address for the ADC data. | +|MEMC_TX_BADDRESS | [31:0] |32'h100000 | DDR base address for the DAC data. | + +## Interfaces + +![Interfaces](../../docs/block_diagrams/data_offload/interface.svg) + +### AXI4 Lite Memory Mapped Slave (S_AXI4_LITE) + +This interface is used to access the register map. + +```verilog +// interface clock -- system clock -- 100 MHz +input s_axi_aclk +// interface resetn -- synchronous reset active low +input s_axi_aresetn + +/* write address channel */ + +// validates the address on the bus +input s_axi_awvalid +// write address +input [15:0] s_axi_awaddr +// protection type -- not used in the core +input [ 2:0] s_axi_awprot +// write ready, indicates that the slave can accept the address +output s_axi_awready + +/* write data channel */ + +// validate the data on the bus +input s_axi_wvalid +// write data +input [31:0] s_axi_wdata +// write strobe, indicates which byte lanes to update +input [ 3:0] s_axi_wstrb +// write ready, indicates that the slave can accept the data +output s_axi_wready + +/* write response channel */ + +// validates the write response of the slave +output s_axi_bvalid +// write response, indicate the status of the transfer +output [ 1:0] s_axi_bresp +// response ready, indicates that the master can accept the data +input s_axi_bready + +/* read address channel */ + +// validates the address on the bus +input s_axi_arvalid +// read address +input [15:0] s_axi_araddr +// protection type -- not used in the core +input [ 2:0] s_axi_arprot +// read ready, indicates that the slave can accept the address +output s_axi_arready + +/* read data channel */ + +// validate the data on the bus +output s_axi_rvalid +// read response, indicate the status of the transfer +output [ 1:0] s_axi_rresp +// read data drivers by the slave +output [31:0] s_axi_rdata +// read ready, indicates that the master can accept the data +input s_axi_rready +``` + +### Supported data interfaces + +**NOTE**: All the data interfaces for the streams should be supported by both +frontend (DMA) and backend (device) side. Although in general the FIFO_RD and +FIFO_WR interfaces can be found in the device side, and the AXIS interfaces on +the DMA side. + +#### AXI4 Stream interface (S_AXIS | M_AXIS) + +* The AXI Stream Slave (S_AXIS) interface is used to receive AXI stream from +the transmit DMA or ADC device. + +* The AXI Stream Master (M_AXIS) interface is used to transmit AXI stream +to receive DMA or DAC device + +**NOTE**: In all cases the data stream is controlled by the device. Although the +generic AXI Stream interface standard supports back-pressure, in our cases none +the DAC, nore the ADC can wait for data. The DMA always have to be ready, samples +will be lost otherwise! + +```verilog +// NOTE: this reference is a master interface + +// interface clock -- can be device/core clock or DMA clock +input m_axis_aclk +// interface resetn -- synchronous reset with the system clock +input m_axis_resetn +// indicates that the slave can accept a transfer in the current cycle (in case of an ADC core, this will control the stream) +input m_axis_ready +// indicates that the master is driving a valid transfer +output m_axis_valid +// primary payload +output [DATA_WIDTH-1:0] m_axis_data +// indicates the boundary of a packet +output m_axis_last +// byte qualifier, we need this so we can have different DMA and device data widths +output [(DATA_WIDTH/8)-1:0] m_axis_tkeep +``` + +**NOTE**: A packet will always be a full buffer. All the data beats going to be +full beats (all the bytes of the bus are valid), except the last one. **axis_last** +and **axis_tkeep** will be used to indicate a partial last beat. This information +should be transferred from the source domain to the sink domain, so we can read +back the data from memory correctly. + +#### ADI FIFO interface + +This is non-blocking (no back-pressure) interface for the device cores. + +To understand the motivation behind the name, let's look at a simple FIFO and its +interfaces: + +![Simple FIFO](../../docs/block_diagrams/data_offload/simple_fifo.svg) + +A FIFO in general has a **write** and a **read** interface. In each case the +interface is controlled by an external logic. Meaning that the FIFO will always +act as slave. The only difference between the two interfaces is that in case of +the **write** interface the data is driven by the master (we are writing into +the FIFO), and in case of the **read** interface the data is driven by the slave +(we are reading from the FIFO). + +To adapt this concept in our case, the device, which can be an ADC or a DAC, +will always be the master. This means, that an ADC core will have a **fifo write** +interface, and a DAC core will have a **fifo read** interface. + +In the same time, this means, that a processing core, which wants to interface +a device core, need to have a **salve fifo write** or a **slave fifo read** +interface, in other words needs to act as a FIFO. + +**Note:** The processing core (or DMA) can have an AXI stream interface too. To +connect an AXIS stream interface to a FIFO interface the following mapping should +be respected: + + * **fifo write to AXIS slave**: +```verilog + // the processing unit should always be READY, otherwise will lose data + assign s_axis_valid = fifo_wr_valid; + assign s_axis_data = fifo_wr_data; +``` + + * **fifo read to AXIS master**: +```verilog + // the processing unit should drive the data bus with the next valid data, + // as the READY gets asserted + assign m_axis_ready = fifo_rd_valid; + assign fifo_rd_data = m_axis_data; +``` + +User should be aware that in this case the AXI stream interface will loose the +back pressure capability. The processing unit should be designed to compensate this +scarcity. + +**NOTE**: the data stream should arrive in packed format to the core. The core +does not care about number of channels or samples per beat. Result of this +constraint is that the FIFO interface of the **Data Offload** module does not +have any **enable** signals. + +```verilog +// This is a Slave FIFO Read interface +// device digital interface clock, or core clock +input fifo_rd_clk +// enables the channel -- in our case this is redundant -- maybe we do neet to use it at all +input fifo_rd_enable +// validates the data on the bus, it's driven by the device indicates when the core latches the data +input fifo_rd_valid +// primary payload, its data width is equal with the channel's data width +output [DATA_WIDTH-1:0] fifo_rd_data +// indicates an underflow, the source (offload FIFO in this case) can not produce the data fast enough +output fifo_rd_unf +``` + +```verilog +// This is a Slave FIFO Write interface +// device digital interface clock, or core clock +input fifo_wr_clk +// enables the channel -- in our case this is redundant -- maybe we do neet to use it at all +input fifo_wr_enable +// validates the data on the bus, it's driven by the device, indicates when the core drives the bus with new data +input fifo_wr_valid +// primary payload, its data width is equal with the channel's data width +input [DATA_WIDTH-1:0] fifo_wr_data +// indicates an overflow, the sink (offload FIFO in this case) can not consume the data fast enough +output fifo_wr_ovf +``` + +#### AXI4 Memory Mapped master (M_AXI_MM) + +An AXI4 Memory Mapped interface, which transfer data into/from the external DDRx +memory. This interface will be used explicitly with Xilinx FPGAs, to interface +the MC (Memory Controller). + +```verilog +/* clocks and resets */ + +// clock signal of the interface, this is an independent clock from the sys_cpu, in general 200 MHz +input axi_clk +// synchronous active low reset +input axi_resetn + +/* write address channel */ + +// validates the address on the bus +output axi_awvalid +// write address ID, this signal is the identification tag for the write address group of signals +output [ 3:0] axi_awid +// burst type, this must use INCR (incrementing address burst) -- 2'b01 +output [ 1:0] axi_awburst +// lock type, atomic characteristics of the transfer -- must be set to 1'b0 +output axi_awlock +// indicates the bufferable, cacheable, write-through, write-back, and allocate attributes -- 4'b0011 recommended by Xilinx, IP as slaves in general ignores +output [ 3:0] axi_awcache +// protection type -- not used in the core, recommended value 3'b000 +output [ 2:0] axi_awprot +// not implemented in Xilinx Endpoint IP +output [ 3:0] axi_awqos +// not implemented in Xilinx Endpoint IP +output [ 3:0] axi_awuser +// up to 256 beats for incrementing (INCR) +output [ 7:0] axi_awlen +// transfer width 8 to 1024 supported, in general the MIG core has 512 bits interface +output [ 2:0] axi_awsize +// write address +output [ 31:0] axi_awaddr +// write ready, indicates that the slave can accept the address +input axi_awready + +/* write data channel */ + +// validate the data on the bus +output axi_wvalid +// write data +output [AXI_DATA_WIDTH-1:0] axi_wdata +/8)-1:0] axi_wstrb // write strobe, indicates which byte lanes to update +output [(AXI_DATA_WIDTH +// fully supported, this signal indicates the last transfer in a write burst +output axi_wlast +// not implemented in Xilinx Endpoint IP +output [ 3:0] axi_wuser +// write ready, indicates that the slave can accept the data +input axi_wready + +/* write response channel */ + +// validates the write response of the slave +input axi_bvalid +// the identification tag of the write response, the BID value must match the AWID +input [ 3:0] axi_bid +// write response, indicate the status of the transfer +input [ 1:0] axi_bresp +// not implemented in Xilinx Endpoint IP +input [ 3:0] axi_buser +// response ready, indicates that the master can accept the data +output axi_bready + +/* read address channel */ + +// validates the address on the bus +output axi_arvalid +// read address ID, this signal is the identification tag for the read address group of signals +output [ 3:0] axi_arid +// burst type, this must use INCR (incrementing address burst) -- 2'b01 +output [ 1:0] axi_arburst +// lock type, atomic characteristics of the transfer -- must be set to 1'b0 +output axi_arlock +// indicates the bufferable, cacheable, write-through, write-back, and allocate attributes -- 4'b0011 recommended by Xilinx, IP as slaves in general ignores +output [ 3:0] axi_arcache +// protection type -- not used in the core +output [ 2:0] axi_arprot +// not implemented in Xilinx Endpoint IP +output [ 3:0] axi_arqos +// not implemented in Xilinx Endpoint IP +output [ 3:0] axi_aruser +// up to 256 beats for incrementing (INCR) +output [ 7:0] axi_arlen +// transfer width 8 to 1024 supported, in general the MIG core has 512 bits interface +output [ 2:0] axi_arsize +// read address +output [ 31:0] axi_araddr +// read ready, indicates that the slave can accept the address +input axi_arready + +/* read data channel */ + +// validate the data on the bus +input axi_rvalid +// the RID is generated by the slave and must match by the ARID value +input [ 3:0] axi_rid +// not implemented in Xilinx Endpoint IP +input [ 3:0] axi_ruser +// read response, indicate the status of the transfer +input [ 1:0] axi_rresp +// indicates the last transfer in a read burst +input axi_rlast +// read data drivers by the slave +input [AXI_DATA_WIDTH-1:0] axi_rdata +// read ready, indicates that the master can accept the data +output axi_rready +``` + +### Avalon Memory Mapped master (AVL_MM) + +An Avalon Memory Mapped interface which transfer data into/from an external DDR4 +memory. This interface will be used explicitly with Intel FPGAs. + +```verilog +// interface clock and reset +input avl_clk +input avl_reset +// address for read or write +output reg [(AVL_ADDRESS_WIDTH-1):0] avl_address +// indicate the number of transfers in each burst +output reg [ 6:0] avl_burstcount +// enables specific byte lanes during transfers on interfaces fo width greater than 8 bits [3] +output reg [ 63:0] avl_byteenable +// asserted to indicate a read transfer (request) +output avl_read +// read data, driven from the slave to the master +input [(AVL_DATA_WIDTH-1):0] avl_readdata +// used for variable-latency, pipelined read transfers, to validate the data on the bus +input avl_readdata_valid +// or waitrequest_n in specs, indicates the availability of the slave +input avl_ready +// asserted to indicate a write transfer +output avl_write +// write data, driven from the master to the slave +output [(AVL_DATA_WIDTH-1):0] avl_writedata +``` + +### Initialization request interface + +Define a simple request/acknowledge interface to initialize the memory: + + * The request will comes from the system and will put the data offload FSM +into a standby/ready state. + * Both RX and TX path should have a separate initialization request interface. + * Acknowledge will be asserted by the data offload IP as the FSM is ready to +receive data. (from TX_DMA or ADC) + + * In case of ADC: after the acknowledge samples will be stored into the memory +using one of the SYNC modes. + + * In case of the DAC: after acknowledge data from the DMA will be stored into +the memory. Acknowledge will stay asserted until one of the SYNC mode is used, +after that the source interface of the IP will stay in busy state. (all the DMA +transfers will be blocked) + +#### Synchronization modes + + * **AUTOMATIC** + * ADC: As the acknowledge of the initialization interface is asserted, the +IP will start to fill up the buffer with samples. + * DAC: As the DMA will send a valid last, the FSM will start to send the +stored data to the device. + + * **HARDWARE** + * ADC and DAC: An external signal will trigger the write or read into or from +the memory. + * **NOTE**: In case of DAC, if the DMA does not sent all the data into the +buffer, before a hardware sync event, the unsent data will be ignored. It's the +user/software responsibility to sync up these events accordingly. + + * **SOFTWARE** + * The software write a RW1C register which will trigger the reads or writes +into or from the memory. + +## Register Map + +| WORD | BYTE | BITS | NAME | CLK_DOMAIN | TYPE | DESCRIPTION | +|-------:|:--------:|:--------:|:-------------------:|:----------:|:-----:|:-----------------------:| +| 0x0000 | 0x0000 | | VERSION | SYS | RO | Version number | +| | | [31:16] | MAJOR | | | | +| | | [15: 8] | MINOR | | | | +| | | [ 7: 0] | PATCH | | | | +| 0x0001 | 0x0004 | | PERIPHERAL_ID | SYS | RO | Value of the IP configuration parameter | +| 0x0002 | 0x0008 | | SCRATCH | SYS | RW | Scratch register | +| 0x0003 | 0x000C | | IDENTIFICATION | SYS | RO | Peripheral identification. Default value: 0x44414F46 - ('D','A','O','F') | +| 0x0004 | 0x0010 | | CONFIGURATION | SYS | RO | Core configuration registers | +| | | [ 2: 2] | MEMORY_TYPE | | | The used storage type (embedded or external) | +| | | [ 1: 1] | TX_PATH | | | TX path synthesized/implemented | +| | | [ 0: 0] | RX_PATH | SYS | | RX path synthesized/implemented | +| 0x0005 | 0x0014 | | CONFIG_RX_SIZE_LSB | SYS | RO | 32bits LSB of the receive memory size register | +| 0x0006 | 0x0018 | | CONFIG_RX_SIZE_MSB | SYS | RO | 2bits MSB of the receive memory size register | +| | | [ 1: 0] | RX_SIZE_MSB | SYS | | | +| 0x0007 | 0x001C | | CONFIG_TX_SIZE_LSB | SYS | RO | 32bits LSB of the transmit memory size register | +| 0x0008 | 0x0020 | | CONFIG_TX_SIZE_MSB | SYS | RO | 2bits MSB of the transmit memory size register | +| | | [ 1: 0] | TX_SIZE_MSB | SYS | | | +| 0x0020 | 0x0080 | | MEM_PHY_STATE | DDR | RO | Status bits of the memory controller IP | +| | | [ 0: 0] | CALIB_COMPLETE | | | Indicates that the memory initialization and calibration have completed successfully | +| 0x0021 | 0x0084 | | RESET_OFFLOAD | ALL | RW | Reset all the internal address registers and state machines | +| | | [ 1: 1] | RESET_TX | | | | +| | | [ 0: 0] | RESET_RX | | | | +| 0x0022 | 0x0088 | | RX_CONTROL_REG | RX/RX_DMA | RW | A global control register | +| | | [ 0: 0] | OFFLOAD_BYPASS | | | Bypass the offload storage, the data path consist just of a CDC FIFO | +| 0x0023 | 0x008C | | TX_CONTROL_REG | TX/TX_DMA | RW | A global control register | +| | | [ 1: 1] | ONESHOT_EN | | | By default the TX path runs on CYCLIC mode, set this bit to switch it to ONE-SHOT mode | +| | | [ 0: 0] | OFFLOAD_BYPASS | | | Bypass the offload storage, the data path consist just of a CDC FIFO | +| 0x0040 | 0x0100 | | SYNC_OFFLOAD | | RW1P | Synchronization setup for RX and TX path | +| | | [ 1: 1] | TX_SYNC | TX | | Synchronize the TX data transfer | +| | | [ 0: 0] | RX_SYNC | RX | | Synchronize the RX data capture | +| 0x0041 | 0x0104 | | SYNC_RX_CONFIG | | RW | Synchronization setup for RX path | +| | | [ 1: 0] | SYNC_CONFIG | RX | | Auto - '0'; hardware - '1'; software - '2' | +| 0x0042 | 0x0108 | | SYNC_TX_CONFIG | | RW | Synchronization setup for TX path +| | | [ 1: 0] | SYNC_CONFIG | TX | | Auto - '0'; hardware - '1'; software - '2' | +| 0x0080 | 0x0200 | | RX_FSM_DBG | RX_DMA | RW | Debug register for the RX offload FSM | +| | | [15: 8] | CONTROL_FSM | | | Force the offload state machine into a required state | +| | | [ 7: 0] | FSM_STATE | | | The current state of the offload state machine | +| 0x0081 | 0x0204 | | TX_FSM_DBG | TX_DMA | RW | Debug register for the TX offload FSM | +| | | [16:16] | NO_TLAST | | | This bits gets asserted, if the memory is empty and the DMA trying to read out data | +| | | [15: 8] | CONTROL_FSM | | | Force the offload state machine into a required state | +| | | [ 7: 0] | FSM_STATE | | | The current state of the offload state machine | +| 0x0082 | 0x0204 | | RX_SAMPLE_COUNT_LSB | RX_DMA | RO | Stored sample count for the RX path (32 LSB) | +| 0x0083 | 0x0208 | | RX_SAMPLE_COUNT_MSB | RX_DMA | RO | Stored sample count for the RX path (32 MSB) | +| 0x0084 | 0x020C | | TX_SAMPLE_COUNT_LSB | TX_DMA | RO | Stored sample count for the TX path (32 LSB) | +| 0x0085 | 0x0210 | | TX_SAMPLE_COUNT_MSB | TX_DMA | RO | Stored sample count for the TX path (32 MSB) | + +## Clock tree + +In general there are at least two different clock in the data offload module: + + * DMA or system clock : on this clock will run all the front end interfaces + * Memory Controller user clock : user interface clock of the DDRx controller (**optional**) + * Device clock : the digital interface clock of the converter + +![Clocks](../../docs/block_diagrams/data_offload/clocks.svg) + +A general frequency relationship of the above clocks are: + +``` + CLKdma <= CLKddr <= CLKconverter +``` + +The clock domain crossing should be handled by the [util_axis_fifo](https://github.com/analogdevicesinc/hdl/tree/master/library/util_axis_fifo) module. + * **TODO** : Make sure that we support both AXIS and FIFO + * **TODO** : Add support for asymmetric aspect ratio. + +All the back end paths (device side) are time critical. The module must read or +write from or into the storage at the speed of the device. + +``` + DDR data rate >= Device data rate + DDR data rate >= ADC data rate + DAC data rate +``` + +## Data path + +![Data path](../../docs/block_diagrams/data_offload/datapath.svg) + + * The data path should be designed to support any kind of difference between +the source, memory and sink data width. + + * The data width adjustments will be made by the CDC_FIFO. + + * In both path (ADC and DAC) the data stream at the front-end side is packatized, +meaning there is a valid TLAS/TKEEP in the stream. While in the back-end side +the stream is continuous. (no TLAST/TKEEP) + * The DAC path have to have a depacketizer to get rid of the last partial beat +from the stream. + * Because the ADC path already arrive in a packed form, and we always will +fill up the whole storage, we don't need to treat special use-cases. + +### Used storage elements + +| | ZC706 | ZCU102 | A10SOC | +|:----------------------|:------------------:|:-----------------:|:----------------:| +| FPGA | XC7Z045 FFG900 – 2 | XCZU9EG-2FFVB1156 | 10AS066N3F40E2SG | +| External Memory Type | DDR3 SODIMM | DDR4 | DDR4 HILO | +| External Memory Size | 1 GB | 512 MB | 2 GB | +| Embedded Memory Type | BRAM | BRAM | M20K | +| Embedded Memory Size | 19.1 Mb | 32.1 Mb | 41 Mb | + +### Data width manipulation + +* data width differences should be treated by the CDC FIFO + +* the smallest granularity should be 8 bits. This constraints mainly will generate +additional logic just in the TX path, taking the fact that the data from the ADC +will come packed. + +* the gearbox main role is to improve the DDR's bandwidth, strips the padding bits +of each samples, so the raw data could be stored into the memory. + +### Xilinx's MIG vs. Intel's EMIF + +* Incrementing burst support for 1 to 256 beats, the length of the burst should +be defined by the internal controller + +* Concurrent read/write access, the external memory to be shared between an ADC +and DAC +* Dynamic burst length tuning: an FSM reads and writes dummy data until both +ADC's overflow and DAC's underflow lines are de-asserted. Pre-requisites : both +device's interface should be up and running. + +* **TODO**: prefetch the next transfer if it's possible, by driving the address channels ahead (e.g. Overlapping read burst in case of AXI4) + +* Optional gearbox to congest the samples in order to increase the maximum data rate. +* In general we packing all samples into 16 bits. This can add a significant +overhand to the maximum real data rate on the memory interface. The gearbox main +role is to pack and unpack the device's samples into the required data width. (in general 512 or 1024 bit) + +Boards with FPGA side DDR3/4 SODIMMs/HILO: ZC706, ZCU102, A10SOC + +| | ZC706 | ZCU102 | A10SOC | +|------------------------------|:---------:|:----------:|:----------:| +| Max data throughputs (MT/s) | 1600 | 2666 | 2133 | +| DDRx reference clocks | 200 MHz | 300 MHz | 133 MHz | +| DDRx Data bus width | 64 | 16 | 64 | +| Memory to FPGA clock ratio | 4:1 | 4:1 | 4:1 | +| UI type & burst length | AXI4-256 | AXI4-256 | Avalon Memory Map | +| UI data width | 512 | 512 | 512 | + +### Internal cyclic buffer support for the TX path + +![Data path with external storage](../../docs/block_diagrams/data_offload/architecture_DDR.svg) + +* On the front end side if the TX path, a special buffer will handle the data +width up/down conversions and run in cyclic mode if the length of the data set +is smaller than 4/8 AXI/Avalon burst. This way we can avoid to overload the memory +interface with small bursts. + +* On the back end side, because the smallest granularity can be 8 bytes, we need +a dynamic 'depackatizer' or re-aligner, which will filter out the invalid data +bytes from the data stream. (this module will use the tlast and tkeep signal of +the AXI stream interface) + +## Control path - Offload FSM + +### RX control FSM for internal RAM mode + +![RX_control FMS for internal RAM mode](../../docs/block_diagrams/data_offload/rx_bram_fsm.svg) + +### TX control FSM for internal RAM mode + +![TX_control FMS for internal RAM mode](../../docs/block_diagrams/data_offload/tx_bram_fsm.svg) + +**TODO** FSMs for the external DDR mode + +## References + +### AMBA AXI + * [AMBA specification](http://infocenter.arm.com/help/topic/com.arm.doc.set.amba/index.html#specs) + * [UG761 AXI Reference Guide](https://www.xilinx.com/support/documentation/ip_documentation/ug761_axi_reference_guide.pdf) + +### Avalon + * [Avalon Interface Specification](https://www.altera.com/en_US/pdfs/literature/manual/mnl_avalon_spec.pdf) + +### Xilinx + * [Device Memory Interface Solutions](https://www.xilinx.com/support/documentation/ip_documentation/mig_7series/v4_2/ds176_7Series_MIS.pdf) + * [Device Memory Interface Solutions User Guide](https://www.xilinx.com/support/documentation/ip_documentation/mig_7series/v4_2/ug586_7Series_MIS.pdf) + * [Ultrascale Architecutre-Based FPGAs Memory IP (v1.4)](https://www.xilinx.com/support/documentation/ip_documentation/ultrascale_memory_ip/v1_4/pg150-ultrascale-memory-ip.pdf) + * [Xilinx FIFO Generator](https://www.xilinx.com/products/intellectual-property/fifo_generator.html#documentation) + * [7 Series FPGAs Memory Resources](https://www.xilinx.com/support/documentation/user_guides/ug473_7Series_Memory_Resources.pdf) + * [Ultrascale Memory Resources](https://www.xilinx.com/support/documentation/user_guides/ug573-ultrascale-memory-resources.pdf) + +### Intel + * [Intel Arria 10 Core Fabric and General Purpose I/Os Handbook](https://www.altera.com/en_US/pdfs/literature/hb/arria-10/a10_handbook.pdf) + * [Intel Arria 10 External Memory Interface IP User Guide](https://www.altera.com/en_US/pdfs/literature/ug/ug-20115.pdf) + * [Intel Arria 10 External Memory Interface IP Design Example](https://www.altera.com/en_US/pdfs/literature/ug/ug-20118.pdf) + * [Intel SCFIFO and DCFIFO](https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/ug/ug_fifo.pdf) + * [Intel Startix 10 High-Performance Design Handbook](https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/stratix-10/s10_hp_hb.pdf) + * [Intel Stratix 10 Embedded Memory User Guide](https://www.altera.com/en_US/pdfs/literature/hb/stratix-10/ug-s10-memory.pdf) + +### Supported FPGA boards + * [ZC706](https://www.xilinx.com/products/boards-and-kits/ek-z7-zc706-g.html) + * [ZCU102](https://www.xilinx.com/products/boards-and-kits/ek-u1-zcu102-g.html) + * [A10SOC](https://www.altera.com/products/boards_and_kits/dev-kits/altera/arria-10-soc-development-kit.html) diff --git a/library/data_offload/data_offload.v b/library/data_offload/data_offload.v new file mode 100644 index 000000000..1ab7ad7c8 --- /dev/null +++ b/library/data_offload/data_offload.v @@ -0,0 +1,413 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2018 (c) Analog Devices, Inc. All rights reserved. +// +// In this HDL repository, there are many different and unique modules, consisting +// of various HDL (Verilog or VHDL) components. The individual modules are +// developed independently, and may be accompanied by separate and unique license +// terms. +// +// The user should read each of these license terms, and understand the +// freedoms and responsibilities that he or she has by using this source/core. +// +// This core is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. +// +// Redistribution and use of source or resulting binaries, with or without modification +// of this file, are permitted under one of the following two license terms: +// +// 1. The GNU General Public License version 2 as published by the +// Free Software Foundation, which can be found in the top level directory +// of this repository (LICENSE_GPL2), and also online at: +// +// +// OR +// +// 2. An ADI specific BSD license, which can be found in the top level directory +// of this repository (LICENSE_ADIBSD), and also on-line at: +// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD +// This will allow to generate bit files and not release the source code, +// as long as it attaches to an ADI device. +// +// *************************************************************************** +// *************************************************************************** +`timescale 1ns / 1ps + +module data_offload #( + + parameter ID = 0, + parameter [ 0:0] MEM_TYPE = 1'b0, // 1'b0 -FPGA RAM; 1'b1 - external memory + parameter [31:0] MEM_SIZE = 1023, // memory size in bytes -1 - max 16 GB + parameter MEMC_UIF_DATA_WIDTH = 512, + parameter MEMC_UIF_ADDRESS_WIDTH = 31, + parameter [31:0] MEMC_BADDRESS = 32'h00000000, + + parameter TX_OR_RXN_PATH = 0, // if set IP is used in TX path, other wise in RX path + + parameter SRC_DATA_WIDTH = 64, + parameter SRC_RAW_DATA_EN = 1'b0, + + parameter SRC_ADDR_WIDTH = 8, + parameter DST_ADDR_WIDTH = 7, + + parameter DST_DATA_WIDTH = 128, + parameter DST_RAW_DATA_EN = 1'b0, // TBD + parameter DST_CYCLIC_EN = 1'b0, // 1'b1 - CYCLIC mode enabled; 1'b0 - CYCLIC mode disabled + + parameter AUTO_BRINGUP = 1) ( + + // AXI4 Slave for configuration + + input s_axi_aclk, + input s_axi_aresetn, + input s_axi_awvalid, + input [15:0] s_axi_awaddr, + input [ 2:0] s_axi_awprot, + output s_axi_awready, + input s_axi_wvalid, + input [31:0] s_axi_wdata, + input [ 3:0] s_axi_wstrb, + output s_axi_wready, + output s_axi_bvalid, + output [ 1:0] s_axi_bresp, + input s_axi_bready, + input s_axi_arvalid, + input [15:0] s_axi_araddr, + input [ 2:0] s_axi_arprot, + output s_axi_arready, + output s_axi_rvalid, + input s_axi_rready, + output [ 1:0] s_axi_rresp, + output [31:0] s_axi_rdata, + + // AXI4 stream slave for source stream (TX_DMA or ADC) -- Source interface + + input s_axis_aclk, + input s_axis_aresetn, + output s_axis_ready, + input s_axis_valid, + input [SRC_DATA_WIDTH-1:0] s_axis_data, + input s_axis_last, + input [SRC_DATA_WIDTH/8-1:0] s_axis_tkeep, + + + // AXI4 stream master for destination stream (RX_DMA or DAC) -- Destination + // interface + + input m_axis_aclk, + input m_axis_aresetn, + input m_axis_ready, + output m_axis_valid, + output [DST_DATA_WIDTH-1:0] m_axis_data, + output m_axis_last, + output [DST_DATA_WIDTH/8-1:0] m_axis_tkeep, + + // initialization request interface + + input init_req, + output init_ack, + + input sync_ext, + + // FIFO interface - Memory UI + + output fifo_src_wen, + output fifo_src_resetn, + output [SRC_ADDR_WIDTH-1:0] fifo_src_waddr, + output [SRC_DATA_WIDTH-1:0] fifo_src_wdata, + output fifo_src_wlast, + + output fifo_dst_ren, + output fifo_dst_resetn, + output [DST_ADDR_WIDTH-1:0] fifo_dst_raddr, + input [DST_DATA_WIDTH-1:0] fifo_dst_rdata, + output fifo_dst_rlast, + input fifo_dst_ready, + + // Status and monitor + + input ddr_calib_done +); + + // local parameters -- to make the code more readable + + localparam SRC_ADDR_WIDTH_BYPASS = (SRC_DATA_WIDTH > DST_DATA_WIDTH) ? 3 : 3 + $clog2(SRC_DATA_WIDTH/DST_DATA_WIDTH); + localparam DST_ADDR_WIDTH_BYPASS = (SRC_DATA_WIDTH <= DST_DATA_WIDTH) ? 3 + $clog2(DST_DATA_WIDTH/SRC_DATA_WIDTH) : 3; + + localparam SRC_BEAT_BYTE = $clog2(SRC_DATA_WIDTH/8); + + // NOTE: Clock domain prefixes + // src_* - AXI4 Stream Slave interface's clock domain + // dst_* - AXI4 Stream Master interface's clock domain + + // internal signals + wire up_clk; + wire up_rstn; + wire up_wreq_s; + wire [13:0] up_waddr_s; + wire [31:0] up_wdata_s; + wire up_rreq_s; + wire [13:0] up_raddr_s; + wire up_wack_s; + wire up_rack_s; + wire [31:0] up_rdata_s; + + wire src_clk; + wire src_rstn; + wire src_valid_out_s; + wire [SRC_ADDR_WIDTH-1:0] src_wr_addr_s; + wire src_wr_ready_s; + wire src_wr_last_s; + wire [SRC_DATA_WIDTH/8-1:0] src_wr_tkeep_s; + + wire dst_clk; + wire dst_rstn; + wire [DST_ADDR_WIDTH-1:0] dst_raddr_s; + wire [DST_DATA_WIDTH-1:0] dst_mem_data_s; + + wire src_bypass_s; + wire dst_bypass_s; + wire oneshot_s; + wire [63:0] sample_count_s; + wire [ 1:0] sync_config_s; + wire sync_int_s; + wire valid_bypass_s; + wire [DST_DATA_WIDTH-1:0] data_bypass_s; + wire ready_bypass_s; + wire [ 1:0] src_fsm_status_s; + wire [ 1:0] dst_fsm_status_s; + wire m_axis_valid_s; + wire m_axis_last_s; + wire [DST_DATA_WIDTH-1:0] m_axis_data_s; + wire dst_mem_last_s; + wire dst_mem_valid_s; + wire dst_mem_valid_int_s; + wire m_axis_reset_int_s; + + wire [31:0] src_transfer_length_s; + wire src_wr_last_int_s; + wire [31:0] src_wr_last_beat_s; + + assign src_clk = s_axis_aclk; + assign dst_clk = m_axis_aclk; + + // internal registers + + reg [31:0] src_data_counter = 0; + reg dst_mem_valid_d = 1'b0; + + generate + if (TX_OR_RXN_PATH) begin + assign src_wr_last_s = s_axis_last; + assign src_wr_tkeep_s = s_axis_tkeep; + assign m_axis_reset_int_s = ~dst_rstn; + end else begin + assign src_wr_last_s = src_wr_last_int_s; + assign src_wr_tkeep_s = {(SRC_DATA_WIDTH/8){1'b1}}; + assign m_axis_reset_int_s = ~dst_rstn | ~init_req; + end + endgenerate + assign fifo_src_wlast = src_wr_last_s; + assign fifo_dst_rlast = dst_mem_last_s; + + // Offload FSM and control + data_offload_fsm #( + .TX_OR_RXN_PATH (TX_OR_RXN_PATH), + .WR_ADDRESS_WIDTH (SRC_ADDR_WIDTH), + .WR_DATA_WIDTH (SRC_DATA_WIDTH), + .RD_ADDRESS_WIDTH (DST_ADDR_WIDTH), + .RD_DATA_WIDTH (DST_DATA_WIDTH)) + i_data_offload_fsm ( + .wr_clk (src_clk), + .wr_resetn_in (src_rstn), + .wr_resetn_out (fifo_src_resetn), + .wr_valid_in (s_axis_valid), + .wr_valid_out (fifo_src_wen), + .wr_ready (src_wr_ready_s), + .wr_addr (fifo_src_waddr), + .wr_last (src_wr_last_s), + .wr_tkeep (src_wr_tkeep_s), + .rd_clk (dst_clk), + .rd_resetn_in (dst_rstn), + .rd_resetn_out (fifo_dst_resetn), + .rd_ready (fifo_dst_ready_int_s), + .rd_valid (dst_mem_valid_s), + .rd_addr (fifo_dst_raddr), + .rd_last (dst_mem_last_s), + .rd_tkeep (m_axis_tkeep), + .rd_oneshot (oneshot_s), + .init_req (init_req), + .init_ack (init_ack), + .sync_config (sync_config_s), + .sync_external (sync_ext), + .sync_internal (sync_int_s), + .wr_fsm_state (src_fsm_status_s), + .rd_fsm_state (dst_fsm_status_s), + .sample_count (sample_count_s) + ); + + // In case of external memory, read back can not start right after the write + // was finished (because of the CDC FIFOs and the latency of the EMIF + // interface) + generate + if (MEM_TYPE == 1'b1) begin + assign fifo_dst_ready_int_s = fifo_dst_ready; + assign dst_mem_valid_int_s = dst_mem_valid_s & m_axis_ready; + end else begin + assign fifo_dst_ready_int_s = 1'b1; + // Compensate the 1 cycle READ latency of the BRAM + always @(posedge m_axis_aclk) begin + dst_mem_valid_d <= dst_mem_valid_s; + end + assign dst_mem_valid_int_s = dst_mem_valid_d; + end + endgenerate + + assign fifo_src_wdata = s_axis_data; + assign fifo_dst_ren = dst_mem_valid_s; + + ad_axis_inf_rx #( + .DATA_WIDTH (DST_DATA_WIDTH)) + i_rx_axis_inf ( + .clk (m_axis_aclk), + .rst (m_axis_reset_int_s), + .valid (dst_mem_valid_int_s), + .data (fifo_dst_rdata), + .last (dst_mem_last_s), + .inf_valid (m_axis_valid_s), + .inf_last (m_axis_last_s), + .inf_data (m_axis_data_s), + .inf_ready (m_axis_ready)); + + assign m_axis_valid = (dst_bypass_s) ? valid_bypass_s : m_axis_valid_s; + assign m_axis_data = (dst_bypass_s) ? data_bypass_s : m_axis_data_s; + assign m_axis_last = (dst_bypass_s) ? 1'b0 : m_axis_last_s; + assign s_axis_ready = (src_bypass_s) ? ready_bypass_s : src_wr_ready_s; + + // Bypass module instance -- the same FIFO, just a smaller depth + // NOTE: Generating an overflow is making sense just in BYPASS mode, and + // it's supported just with the FIFO interface + util_axis_fifo_asym #( + .S_DATA_WIDTH (SRC_DATA_WIDTH), + .S_ADDRESS_WIDTH (SRC_ADDR_WIDTH_BYPASS), + .M_DATA_WIDTH (DST_DATA_WIDTH), + .ASYNC_CLK (1)) + i_bypass_fifo ( + .m_axis_aclk (m_axis_aclk), + .m_axis_aresetn (dst_rstn), + .m_axis_ready (m_axis_ready), + .m_axis_valid (valid_bypass_s), + .m_axis_data (data_bypass_s), + .m_axis_tlast (), + .m_axis_empty (), + .m_axis_almost_empty (), + .s_axis_aclk (s_axis_aclk), + .s_axis_aresetn (src_rstn), + .s_axis_ready (ready_bypass_s), + .s_axis_valid (s_axis_valid), + .s_axis_data (s_axis_data), + .s_axis_tlast (), + .s_axis_full (), + .s_axis_almost_full () + ); + + // register map + + data_offload_regmap #( + .ID (ID), + .MEM_TYPE (MEM_TYPE), + .MEM_SIZE (MEM_SIZE), + .TX_OR_RXN_PATH (TX_OR_RXN_PATH), + .AUTO_BRINGUP (AUTO_BRINGUP)) + i_regmap ( + .up_clk (up_clk), + .up_rstn (up_rstn), + .up_rreq (up_rreq_s), + .up_rack (up_rack_s), + .up_raddr (up_raddr_s), + .up_rdata (up_rdata_s), + .up_wreq (up_wreq_s), + .up_wack (up_wack_s), + .up_waddr (up_waddr_s), + .up_wdata (up_wdata_s), + .src_clk (s_axis_aclk), + .dst_clk (m_axis_aclk), + .src_sw_resetn (src_rstn), + .dst_sw_resetn (dst_rstn), + .ddr_calib_done (ddr_calib_done), + .src_bypass (src_bypass_s), + .dst_bypass (dst_bypass_s), + .oneshot (oneshot_s), + .sync (sync_int_s), + .sync_config (sync_config_s), + .src_transfer_length (src_transfer_length_s), + .src_fsm_status (src_fsm_status_s), + .dst_fsm_status (dst_fsm_status_s), + .sample_count_msb (sample_count_s[63:32]), + .sample_count_lsb (sample_count_s[31: 0]) + ); + + // axi interface wrapper + + assign up_clk = s_axi_aclk; + assign up_rstn = s_axi_aresetn; + + up_axi #( + .AXI_ADDRESS_WIDTH (16)) + i_up_axi ( + .up_rstn (up_rstn), + .up_clk (up_clk), + .up_axi_awvalid (s_axi_awvalid), + .up_axi_awaddr (s_axi_awaddr), + .up_axi_awready (s_axi_awready), + .up_axi_wvalid (s_axi_wvalid), + .up_axi_wdata (s_axi_wdata), + .up_axi_wstrb (s_axi_wstrb), + .up_axi_wready (s_axi_wready), + .up_axi_bvalid (s_axi_bvalid), + .up_axi_bresp (s_axi_bresp), + .up_axi_bready (s_axi_bready), + .up_axi_arvalid (s_axi_arvalid), + .up_axi_araddr (s_axi_araddr), + .up_axi_arready (s_axi_arready), + .up_axi_rvalid (s_axi_rvalid), + .up_axi_rresp (s_axi_rresp), + .up_axi_rdata (s_axi_rdata), + .up_axi_rready (s_axi_rready), + .up_wreq (up_wreq_s), + .up_waddr (up_waddr_s), + .up_wdata (up_wdata_s), + .up_wack (up_wack_s), + .up_rreq (up_rreq_s), + .up_raddr (up_raddr_s), + .up_rdata (up_rdata_s), + .up_rack (up_rack_s)); + +/* Beat counter on the source interface +* + * The storage unit can have size of a couple of Gbyte, which in case of an RX + * path would mean to fill up all that memory space before pushing over the + * stream to the RX DMA. (ADC can not generate a tlast) To make things more + * practical, user can set an arbitrary transfer length using the + * transfer_length register, which will be used to generate an internal tlast + * signal for the source FSM. If the register is set to zero, all the memory + * will be filled up, before passing control to the destination FSM. + * + */ + +always @(posedge s_axis_aclk) begin + if (fifo_src_resetn == 1'b0) begin // counter should reset when source FMS resets + src_data_counter <= 0; + end else begin + if (fifo_src_wen & src_wr_ready_s) begin + src_data_counter <= src_data_counter + 1'b1; + end + end +end +// transfer length is in bytes, but counter monitors the source data beats +assign src_wr_last_beat_s = (src_transfer_length_s == 32'h0) ? MEM_SIZE[31:SRC_BEAT_BYTE] : src_transfer_length_s[31:SRC_BEAT_BYTE]; +assign src_wr_last_int_s = (src_data_counter == src_wr_last_beat_s) ? 1'b1 : 1'b0; + +endmodule + diff --git a/library/data_offload/data_offload_constr.ttcl b/library/data_offload/data_offload_constr.ttcl new file mode 100644 index 000000000..1c2f28e33 --- /dev/null +++ b/library/data_offload/data_offload_constr.ttcl @@ -0,0 +1,177 @@ +<: set ComponentName [getComponentNameString] :> +<: setOutputDirectory "./" :> +<: setFileName [ttcl_add $ComponentName "_constr"] :> +<: setFileExtension ".xdc" :> +<: setFileProcessingOrder late :> +<: set mem_type [getBooleanValue "MEM_TYPE"] :> +<: set tx_enable [getBooleanValue "TX_OR_RXN_PATH"] :> + +## for all synchronization registers from util_cdc modules +set_property ASYNC_REG TRUE \ + [get_cells -hier {*cdc_sync_stage1_reg*}] \ + [get_cells -hier {*cdc_sync_stage2_reg*}] + +## For RX in case of BRAMs +<: if { $tx_enable == 0 } { :> + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_sync_wr_sync/cdc_sync_stage1_reg[*]/D}] + +<: if { !$mem_type } { :> + + set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*adc_init_sync.i_rd_init_ack_sync/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*adc_init_sync.i_rd_init_ack_sync/i_sync_out/cdc_sync_stage1_reg[*]/D}] + + set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*adc_init_sync.i_rd_init_ack_sync/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*adc_init_sync.i_rd_init_ack_sync/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +<: } :> + +<: } :> + +## For TX in case of BRAMs +<: if { $tx_enable == 1 } { :> + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_sync_rd_sync/cdc_sync_stage1_reg[*]/D}] + +<: if { !$mem_type } { :> + + set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*dac_init_sync.i_wr_init_ack_sync/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*dac_init_sync.i_wr_init_ack_sync/i_sync_out/cdc_sync_stage1_reg[*]/D}] + + set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*dac_init_sync.i_wr_init_ack_sync/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*dac_init_sync.i_wr_init_ack_sync/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +<: } :> + +<: } :> + +## For external DDRx memory + +<: if { $mem_type == 1 } { :> + + set_false_path \ + -from [get_cells -hierarchical * -filter {NAME=~*/i_sync_src_transfer_length/cdc_hold_reg[*]}] \ + -to [get_cells -hierarchical * -filter {NAME=~*/i_sync_src_transfer_length/out_data_reg[*]}] + + set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_sync_src_transfer_length/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_sync_src_transfer_length/i_sync_out/cdc_sync_stage1_reg[*]/D}] + + set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_sync_src_transfer_length/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_sync_src_transfer_length/i_sync_in/cdc_sync_stage1_reg[*]/D}] + + set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_ddr_calib_done_sync/cdc_sync_stage1_reg[0]/D}] + +<: } :> + +## Common constraints + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_xfer_status/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_xfer_status/i_sync_out/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_xfer_status/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_xfer_status/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_cells -hierarchical * -filter {NAME=~*/i_xfer_status/cdc_hold_reg[*]}] \ + -to [get_cells -hierarchical * -filter {NAME=~*/i_xfer_status/out_data_reg[*]}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_dst_fsm_status/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_dst_fsm_status/i_sync_out/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_dst_fsm_status/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_dst_fsm_status/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_cells -hierarchical * -filter {NAME=~*/i_dst_fsm_status/cdc_hold_reg[*]}] \ + -to [get_cells -hierarchical * -filter {NAME=~*/i_dst_fsm_status/out_data_reg[*]}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_src_fsm_status/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_src_fsm_status/i_sync_out/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_src_fsm_status/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_src_fsm_status/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_cells -hierarchical * -filter {NAME=~*/i_src_fsm_status/cdc_hold_reg[*]}] \ + -to [get_cells -hierarchical * -filter {NAME=~*/i_src_fsm_status/out_data_reg[*]}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_wr_empty_sync/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_wr_empty_sync/i_sync_out/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_wr_empty_sync/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_wr_empty_sync/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_rd_full_sync/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_rd_full_sync/i_sync_out/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_rd_full_sync/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_rd_full_sync/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_rd_wr_last_sync/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_rd_wr_last_sync/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*/i_rd_wr_last_sync/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_rd_wr_last_sync/i_sync_out/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*i_sync_xfer_control/in_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*i_sync_xfer_control/i_sync_out/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_pins -hierarchical * -filter {NAME=~*i_sync_xfer_control/out_toggle_d1_reg/C}] \ + -to [get_pins -hierarchical * -filter {NAME=~*i_sync_xfer_control/i_sync_in/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -from [get_cells -hierarchical * -filter {NAME=~*i_sync_xfer_control/cdc_hold_reg[*]}] \ + -to [get_cells -hierarchical * -filter {NAME=~*i_sync_xfer_control/out_data_reg[*]}] + + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_rd_init_req_sync/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_wr_init_req_sync/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_rd_last_address/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*/i_rd_last_keep/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_src_xfer_control/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_dst_xfer_control/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_dst_oneshot_sync/cdc_sync_stage1_reg[0]/D}] + +## Constraints for the bypass module + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_waddr_sync_gray/cdc_sync_stage1_reg[*]/D}] + +set_false_path \ + -to [get_pins -hierarchical * -filter {NAME=~*i_raddr_sync_gray/cdc_sync_stage1_reg[*]/D}] diff --git a/library/data_offload/data_offload_control.v b/library/data_offload/data_offload_control.v new file mode 100644 index 000000000..480658f88 --- /dev/null +++ b/library/data_offload/data_offload_control.v @@ -0,0 +1,44 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2019 (c) Analog Devices, Inc. All rights reserved. +// +// In this HDL repository, there are many different and unique modules, consisting +// of various HDL (Verilog or VHDL) components. The individual modules are +// developed independently, and may be accompanied by separate and unique license +// terms. +// +// The user should read each of these license terms, and understand the +// freedoms and responsibilities that he or she has by using this source/core. +// +// This core is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. +// +// Redistribution and use of source or resulting binaries, with or without modification +// of this file, are permitted under one of the following two license terms: +// +// 1. The GNU General Public License version 2 as published by the +// Free Software Foundation, which can be found in the top level directory +// of this repository (LICENSE_GPL2), and also online at: +// +// +// OR +// +// 2. An ADI specific BSD license, which can be found in the top level directory +// of this repository (LICENSE_ADIBSD), and also on-line at: +// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD +// This will allow to generate bit files and not release the source code, +// as long as it attaches to an ADI device. +// +// *************************************************************************** +// *************************************************************************** +`timescale 1ns / 1ps + +module data_offload_control #( + +) ( + +); + +endmodule + diff --git a/library/data_offload/data_offload_fsm.v b/library/data_offload/data_offload_fsm.v new file mode 100644 index 000000000..1ff1d28d7 --- /dev/null +++ b/library/data_offload/data_offload_fsm.v @@ -0,0 +1,565 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2018 (c) Analog Devices, Inc. All rights reserved. +// +// In this HDL repository, there are many different and unique modules, consisting +// of various HDL (Verilog or VHDL) components. The individual modules are +// developed independently, and may be accompanied by separate and unique license +// terms. +// +// The user should read each of these license terms, and understand the +// freedoms and responsibilities that he or she has by using this source/core. +// +// This core is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. +// +// Redistribution and use of source or resulting binaries, with or without modification +// of this file, are permitted under one of the following two license terms: +// +// 1. The GNU General Public License version 2 as published by the +// Free Software Foundation, which can be found in the top level directory +// of this repository (LICENSE_GPL2), and also online at: +// +// +// OR +// +// 2. An ADI specific BSD license, which can be found in the top level directory +// of this repository (LICENSE_ADIBSD), and also on-line at: +// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD +// This will allow to generate bit files and not release the source code, +// as long as it attaches to an ADI device. +// +// *************************************************************************** +// *************************************************************************** +`timescale 1ns/100ps + +/* This module controls the read and write access to the storage unit. It is +* used for bot transmit and receive use cases +*/ + +module data_offload_fsm #( + + parameter TX_OR_RXN_PATH = 0, + parameter WR_ADDRESS_WIDTH = 4, + parameter WR_DATA_WIDTH = 128, + parameter RD_ADDRESS_WIDTH = 4, + parameter RD_DATA_WIDTH = 128)( + + // write control interface + input wr_clk, + input wr_resetn_in, + output reg wr_resetn_out, + input wr_valid_in, + output wr_valid_out, + output wr_ready, + output reg [WR_ADDRESS_WIDTH-1:0] wr_addr, + input wr_last, + input [WR_DATA_WIDTH/8-1:0] wr_tkeep, + + // read control interface + input rd_clk, + input rd_resetn_in, + output reg rd_resetn_out, + input rd_ready, + output reg rd_valid = 1'b0, + output reg [RD_ADDRESS_WIDTH-1:0] rd_addr, + output reg rd_last, + output reg [RD_DATA_WIDTH/8-1:0] rd_tkeep, + input rd_oneshot, // 0 - CYCLIC; 1 - ONE_SHOT; + + // Synchronization interface - synchronous to the device clock (ADC or DAC) + input init_req, + output init_ack, + input [ 1:0] sync_config, + + input sync_external, + input sync_internal, + + // FSM debug + output [ 1:0] wr_fsm_state, + output [ 1:0] rd_fsm_state, + output reg [63:0] sample_count + + ); + + // FSM states + + localparam WR_IDLE = 2'b00; + localparam WR_SYNC = 2'b01; + localparam WR_WRITE_TO_MEM = 2'b11; + localparam WR_WAIT_TO_END = 2'b10; + + localparam RD_IDLE = 2'b00; + localparam RD_SYNC = 2'b01; + localparam RD_READ_FROM_MEM = 2'b11; + + // Synchronization options + + localparam AUTOMATIC = 2'b00; + localparam HARDWARE = 2'b01; + localparam SOFTWARE = 2'b10; + + // helper parameters for last address, tkeep conversion + localparam LSB = (WR_ADDRESS_WIDTH > RD_ADDRESS_WIDTH) ? WR_ADDRESS_WIDTH - RD_ADDRESS_WIDTH : + RD_ADDRESS_WIDTH - WR_ADDRESS_WIDTH; + localparam POW2_LSB = 1 << LSB; + + // internal registers + + reg [WR_ADDRESS_WIDTH-1:0] wr_last_addr; + reg [WR_DATA_WIDTH/8-1:0] wr_last_keep; + + reg [RD_DATA_WIDTH/8-1:0] rd_tkeep_last; + reg [RD_ADDRESS_WIDTH-1:0] rd_last_addr; + reg rd_isempty; + reg rd_init_req_d; + reg wr_init_req_d; + + // internal signals + + wire wr_full; + wire wr_init_req_s; + wire wr_init_req_pos_s; + wire wr_init_ack_s; + wire rd_isfull_s; + wire wr_isempty_s; + wire rd_empty_s; + wire rd_wr_last_s; + wire rd_init_req_s; + wire rd_init_req_neg_s; + wire rd_init_ack_s; + wire [WR_ADDRESS_WIDTH-1:0] rd_wr_last_addr_s; + wire [WR_DATA_WIDTH/8-1:0] rd_wr_last_tkeep_s; + wire wr_sync_internal_s; + wire rd_sync_internal_s; + wire wr_sync_external_s; + wire rd_sync_external_s; + + (* DONT_TOUCH = "TRUE" *) reg [1:0] wr_fsm_state = 2'b00; + (* DONT_TOUCH = "TRUE" *) reg [1:0] rd_fsm_state = 2'b00; + + // Mealy state machine for write control + always @(posedge wr_clk) begin + if (wr_resetn_in == 1'b0) begin + wr_fsm_state <= WR_IDLE; + end else begin + case (wr_fsm_state) + + WR_IDLE: begin + if (wr_init_req_pos_s) begin + wr_fsm_state <= (TX_OR_RXN_PATH) ? WR_WRITE_TO_MEM : WR_SYNC; + end else begin + wr_fsm_state <= WR_IDLE; + end + end + + WR_SYNC: begin + // do not lock the FSM if something goes wrong + if (TX_OR_RXN_PATH) begin + wr_fsm_state <= WR_WRITE_TO_MEM; + end else begin // SOURCE_IS_BACK_END + case (sync_config) + AUTOMATIC: begin + wr_fsm_state <= WR_WRITE_TO_MEM; + end + HARDWARE: begin + if (wr_sync_external_s) begin + wr_fsm_state <= WR_WRITE_TO_MEM; + end + end + SOFTWARE: begin + if (wr_sync_internal_s) begin + wr_fsm_state <= WR_WRITE_TO_MEM; + end + end + default: begin + wr_fsm_state <= WR_WRITE_TO_MEM; + end + endcase + end + end + + WR_WRITE_TO_MEM: begin + if ((wr_full || wr_last) && wr_valid_out) begin + wr_fsm_state <= WR_WAIT_TO_END; + end else begin + wr_fsm_state <= WR_WRITE_TO_MEM; + end + end + + WR_WAIT_TO_END: begin + if ((wr_isempty_s) || (wr_init_req_pos_s)) begin + wr_fsm_state <= WR_IDLE; + end else begin + wr_fsm_state <= WR_WAIT_TO_END; + end + end + + default: wr_fsm_state <= WR_IDLE; + endcase + + end + end + + // the initialization interface (init_req) is edge sensitive + always @(posedge wr_clk) begin + wr_init_req_d <= wr_init_req_s; + end + assign wr_init_req_pos_s = ~wr_init_req_d & wr_init_req_s; + + // status bits + assign wr_full = (wr_addr == {WR_ADDRESS_WIDTH{1'b1}}) ? 1'b1 : 1'b0; + + // generate INIT acknowledge signal in WRITE domain (in case of ADCs) + assign wr_init_ack_s = (wr_fsm_state == WR_SYNC) ? 1'b1 : 1'b0; + + // write address generation + always @(posedge wr_clk) begin + if ((wr_resetn_in == 1'b0) || (wr_fsm_state == WR_IDLE)) begin + wr_addr <= 'b0; + end else begin + if (wr_valid_out) begin + wr_addr <= wr_addr + 1'b1; + end + end + end + + // reset the storage unit's FMS before each transfer + always @(posedge wr_clk) begin + if ((wr_resetn_in == 1'b0) || (wr_fsm_state == WR_IDLE)) begin + wr_resetn_out <= 1'b0; + end else begin + wr_resetn_out <= 1'b1; + end + end + + always @(posedge wr_clk) begin + if (wr_resetn_in == 1'b0) begin + wr_last_addr <= {WR_ADDRESS_WIDTH{1'b1}}; + end else begin + wr_last_addr <= (wr_last && wr_valid_out) ? wr_addr : wr_last_addr; + end + end + + always @(posedge wr_clk) begin + if (wr_resetn_in == 1'b0) begin + wr_last_keep <= {WR_DATA_WIDTH/8{1'b1}}; + end else begin + if (wr_last) begin + // if the SOURCE is at back-end, the interface is FIFO, set the tkeep + // to its default + wr_last_keep <= (TX_OR_RXN_PATH) ? wr_tkeep : {WR_DATA_WIDTH/8{1'b1}}; + end + end + end + + // write control + assign wr_ready = (wr_fsm_state != WR_WRITE_TO_MEM) ? 1'b0 : 1'b1; + assign wr_valid_out = wr_ready & wr_valid_in; + + // sample counter for debug purposes, the value of the counter resets at + // every new incoming request + + always @(posedge wr_clk) begin + if (wr_init_req_pos_s == 1'b1) begin + sample_count <= 64'b0; + end else begin + if (wr_ready && wr_valid_in) begin + sample_count <= sample_count + 1'b1; + end + end + end + + // Mealy state machine for read control + always @(posedge rd_clk) begin + if (rd_resetn_in == 1'b0) begin + rd_fsm_state <= RD_IDLE; + end else begin + case (rd_fsm_state) + + RD_IDLE: begin + if ((rd_isfull_s) || (rd_wr_last_s)) begin + if (TX_OR_RXN_PATH) begin + rd_fsm_state <= RD_SYNC; + end else begin + rd_fsm_state <= RD_READ_FROM_MEM; + end + end else begin + rd_fsm_state <= RD_IDLE; + end + end + + RD_SYNC : begin + // do not lock the FSM if something goes wrong + if (!TX_OR_RXN_PATH) begin + rd_fsm_state <= RD_READ_FROM_MEM; + end else begin // TX_OR_RXN_PATH + case (sync_config) + AUTOMATIC: begin + rd_fsm_state <= RD_READ_FROM_MEM; + end + HARDWARE: begin + if (rd_sync_external_s) begin + rd_fsm_state <= RD_READ_FROM_MEM; + end + end + SOFTWARE: begin + if (rd_sync_internal_s) begin + rd_fsm_state <= RD_READ_FROM_MEM; + end + end + default: begin + rd_fsm_state <= RD_READ_FROM_MEM; + end + endcase + end + end + + // read until empty or next init_req + RD_READ_FROM_MEM : begin + if ((rd_empty_s && rd_oneshot && rd_ready && rd_last) || (rd_init_req_neg_s)) begin + rd_fsm_state <= RD_IDLE; + end else begin + rd_fsm_state <= RD_READ_FROM_MEM; + end + end + + default : rd_fsm_state <= RD_IDLE; + endcase + end + end + + // the initialization interface (init_req) is edge sensitive + // TODO: This should be redefined! Will work only of init_req is active + // during the whole DMA transfer (use xfer_req for driving init_req) + always @(posedge rd_clk) begin + rd_init_req_d <= rd_init_req_s; + end + assign rd_init_req_neg_s = rd_init_req_d & ~rd_init_req_s; + + // generate INIT acknowledge signal in WRITE domain (in case of ADCs) + assign rd_init_ack_s = (rd_fsm_state == RD_SYNC) ? 1'b1 : 1'b0; + + // Reset the storage unit's FSM before each transfer + always @(posedge rd_clk) begin + if ((rd_resetn_in == 1'b0) || (rd_fsm_state == RD_IDLE)) begin + rd_resetn_out <= 1'b0; + end else begin + rd_resetn_out <= 1'b1; + end + end + + // read address generation + assign rd_reading_s = (rd_fsm_state == RD_READ_FROM_MEM) ? 1'b1 : 1'b0; + always @(posedge rd_clk) begin + if (rd_fsm_state != RD_READ_FROM_MEM) begin + rd_addr <= 'b0; + end else begin + if (rd_ready && rd_valid) begin + if (rd_oneshot) + rd_addr <= (rd_last_addr == rd_addr) ? rd_addr : rd_addr + 1'b1; + else + rd_addr <= (rd_last_addr == rd_addr) ? {RD_ADDRESS_WIDTH{1'b0}} : rd_addr + 1'b1; + end + end + end + + assign rd_empty_s = (rd_addr == rd_last_addr) ? 1'b1 : 1'b0; + always @(posedge rd_clk) begin + if (rd_resetn_in == 1'b0) begin + rd_last <= 1'b0; + rd_isempty <= 1'b0; + end else begin + rd_isempty <= rd_empty_s; + if (rd_empty_s & ~rd_isempty) begin + // in CYCLIC mode rd_last stays low + rd_last <= rd_oneshot; + end else if (rd_last & rd_ready & rd_valid)begin + rd_last <= 1'b0; + end + end + end + + always @(posedge rd_clk) begin + if (rd_resetn_in == 1'b0) begin + rd_valid <= 1'b0; + end else begin + if ((rd_ready) && (rd_fsm_state == RD_READ_FROM_MEM)) begin + rd_valid <= 1'b1; + end else begin + rd_valid <= 1'b0; + end + end + end + + // CDC circuits + + sync_event #( + .NUM_OF_EVENTS (1), + .ASYNC_CLK (1)) + i_wr_empty_sync ( + .in_clk (rd_clk), + .in_event (rd_isempty), + .out_clk (wr_clk), + .out_event (wr_isempty_s) + ); + + sync_event #( + .NUM_OF_EVENTS (1), + .ASYNC_CLK(1)) + i_rd_full_sync ( + .in_clk (wr_clk), + .in_event (wr_full), + .out_clk (rd_clk), + .out_event (rd_isfull_s) + ); + + sync_event #( + .NUM_OF_EVENTS (1), + .ASYNC_CLK (1)) + i_rd_wr_last_sync ( + .in_clk (wr_clk), + .in_event ((wr_last & wr_valid_out)), + .out_clk (rd_clk), + .out_event (rd_wr_last_s) + ); + + sync_bits #( + .NUM_OF_BITS (1), + .ASYNC_CLK (1)) + i_rd_init_req_sync ( + .in_bits (init_req), + .out_clk (rd_clk), + .out_resetn (1'b1), + .out_bits (rd_init_req_s) + ); + + sync_bits #( + .NUM_OF_BITS (1), + .ASYNC_CLK (1)) + i_wr_init_req_sync ( + .in_bits (init_req), + .out_clk (wr_clk), + .out_resetn (1'b1), + .out_bits (wr_init_req_s) + ); + + generate if (TX_OR_RXN_PATH == 0) begin : adc_init_sync + + sync_event #( + .NUM_OF_EVENTS (1), + .ASYNC_CLK (1)) + i_rd_init_ack_sync ( + .in_clk (wr_clk), + .in_event (wr_init_ack_s), + .out_clk (rd_clk), + .out_event (init_ack) + ); + + end else begin : dac_init_sync + + sync_event #( + .NUM_OF_EVENTS (1), + .ASYNC_CLK (1)) + i_wr_init_ack_sync ( + .in_clk (rd_clk), + .in_event (rd_init_ack_s), + .out_clk (wr_clk), + .out_event (init_ack) + ); + + end + endgenerate + + // convert write address and last/keep to read address and last/keep + + sync_bits #( + .NUM_OF_BITS (WR_ADDRESS_WIDTH), + .ASYNC_CLK (1)) + i_rd_last_address ( + .in_bits (wr_last_addr), + .out_clk (rd_clk), + .out_resetn (1'b1), + .out_bits (rd_wr_last_addr_s) + ); + + sync_bits #( + .NUM_OF_BITS (WR_DATA_WIDTH/8), + .ASYNC_CLK (1)) + i_rd_last_keep ( + .in_bits (wr_last_keep), + .out_clk (rd_clk), + .out_resetn (1'b1), + .out_bits (rd_wr_last_tkeep_s) + ); + + // upsizing - WR_DATA_WIDTH < RD_DATA_WIDTH + generate if (WR_ADDRESS_WIDTH > RD_ADDRESS_WIDTH) begin + + always @(posedge rd_clk) begin + rd_last_addr <= rd_wr_last_addr_s[WR_ADDRESS_WIDTH-1 : LSB]; + end + + // the read tkeep will be wider than the write tkeep, and its value + // depends on when the write tlast was asserted + always @(posedge rd_clk) begin :tkeep_gen + integer i; + for (i = 0; i < POW2_LSB; i = i + 1) begin : a_tkeep + if (rd_last_addr[LSB-1:0] < i) + rd_tkeep_last[(i+1)*WR_DATA_WIDTH/8-1 -: WR_DATA_WIDTH/8] <= {WR_DATA_WIDTH/8{1'b0}}; + else + rd_tkeep_last[(i+1)*WR_DATA_WIDTH/8-1 -: WR_DATA_WIDTH/8] <= (i == 0) ? rd_wr_last_tkeep_s : {WR_DATA_WIDTH/8{1'b1}}; + end + end + + end else if (WR_ADDRESS_WIDTH < RD_ADDRESS_WIDTH) begin // downsizing - WR_DATA_WIDTH > RD_DATA_WIDTH or equal + + always @(posedge rd_clk) begin + rd_tkeep_last <= rd_wr_last_tkeep_s[RD_DATA_WIDTH/8-1 : 0]; + rd_last_addr <= {rd_wr_last_addr_s, {LSB{1'b1}}}; + end + + end else begin + + always @(posedge rd_clk) begin + rd_tkeep_last <= rd_wr_last_tkeep_s; + rd_last_addr <= rd_wr_last_addr_s; + end + + end + endgenerate + + always @(posedge rd_clk) begin + if (rd_fsm_state == RD_IDLE) begin + rd_tkeep <= {(RD_DATA_WIDTH/8){1'b1}}; + end else begin + if (rd_empty_s && rd_ready) + rd_tkeep <= rd_tkeep_last; + else if (rd_ready) + rd_tkeep <= {(RD_DATA_WIDTH/8){1'b1}}; + end + end + + sync_bits #( + .NUM_OF_BITS (2), + .ASYNC_CLK (1)) + i_sync_wr_sync ( + .in_bits ({ sync_internal, sync_external }), + .out_clk (wr_clk), + .out_resetn (1'b1), + .out_bits ({ wr_sync_internal_s, wr_sync_external_s }) + ); + + sync_bits #( + .NUM_OF_BITS (2), + .ASYNC_CLK (1)) + i_sync_rd_sync ( + .in_bits ({ sync_internal, sync_external }), + .out_clk (rd_clk), + .out_resetn (1'b1), + .out_bits ({ rd_sync_internal_s, rd_sync_external_s }) + ); + +endmodule + diff --git a/library/data_offload/data_offload_ip.tcl b/library/data_offload/data_offload_ip.tcl new file mode 100644 index 000000000..ffa0048f0 --- /dev/null +++ b/library/data_offload/data_offload_ip.tcl @@ -0,0 +1,235 @@ +source ../scripts/adi_env.tcl +source $ad_hdl_dir/library/scripts/adi_ip_xilinx.tcl + +adi_ip_create data_offload +adi_ip_files data_offload [list \ + "data_offload_sv.ttcl" \ + "$ad_hdl_dir/library/common/up_axi.v" \ + "$ad_hdl_dir/library/common/ad_mem_asym.v" \ + "$ad_hdl_dir/library/common/ad_axis_inf_rx.v" \ + "data_offload_regmap.v" \ + "data_offload_fsm.v" \ + "data_offload.v" ] + +## NOTE: To solve the issue AR# 70646 we need to call the following command +##set_property source_mgmt_mode DisplayOnly [current_project] + +adi_ip_properties data_offload +adi_ip_ttcl data_offload "data_offload_constr.ttcl" +adi_ip_sim_ttcl data_offload "data_offload_sv.ttcl" + +adi_ip_add_core_dependencies { \ + analog.com:user:util_cdc:1.0 \ + analog.com:user:util_axis_fifo_asym:1.0 \ +} + +set_property display_name "ADI Data Offload Controller" [ipx::current_core] +set_property description "ADI Data Offload Controller" [ipx::current_core] + +## Interface definitions + +## destination interfaces (e.g. RX_DMA or DAC core) + +adi_add_bus "m_axis" "master" \ + "xilinx.com:interface:axis_rtl:1.0" \ + "xilinx.com:interface:axis:1.0" \ + [ list \ + {"m_axis_ready" "TREADY"} \ + {"m_axis_valid" "TVALID"} \ + {"m_axis_data" "TDATA"} \ + {"m_axis_last" "TLAST"} \ + {"m_axis_tkeep" "TKEEP"} ] +adi_add_bus_clock "m_axis_aclk" "m_axis" "m_axis_aresetn" + +## source interface (e.g. TX_DMA or ADC core) + +adi_add_bus "s_axis" "slave" \ + "xilinx.com:interface:axis_rtl:1.0" \ + "xilinx.com:interface:axis:1.0" \ + [ list \ + {"s_axis_ready" "TREADY"} \ + {"s_axis_valid" "TVALID"} \ + {"s_axis_data" "TDATA"} \ + {"s_axis_last" "TLAST"} \ + {"s_axis_tkeep" "TKEEP"} ] +adi_add_bus_clock "s_axis_aclk" "s_axis" "s_axis_aresetn" + +set cc [ipx::current_core] + +## Parameter validations + +## MEM_TPYE +set_property -dict [list \ + "value_validation_type" "pairs" \ + "value_validation_pairs" { \ + "Internal memory" "0" \ + "External memory" "1" \ + } \ + ] \ + [ipx::get_user_parameters MEM_TYPE -of_objects $cc] + +set_property -dict [list \ + "value_validation_type" "pairs" \ + "value_validation_pairs" { \ + "RX path" "0" \ + "TX path" "1" \ + } \ + ] \ + [ipx::get_user_parameters TX_OR_RXN_PATH -of_objects $cc] + +## MEMC_UIF_DATA_WIDTH +set_property -dict [list \ + "value_validation_type" "list" \ + "value_validation_list" "64 128 256 512 1024" \ + ] \ + [ipx::get_user_parameters MEMC_UIF_DATA_WIDTH -of_objects $cc] + +## MEMC_UIF_ADDRESS_WIDTH +set_property -dict [list \ + "value_validation_type" "range_long" \ + "value_validation_range_minimum" "8" \ + "value_validation_range_maximum" "31" \ + ] \ + [ipx::get_user_parameters MEMC_UIF_ADDRESS_WIDTH -of_objects $cc] + +## MEM_SIZE - 8GB?? +set_property -dict [list \ + "value_validation_type" "range_long" \ + "value_validation_range_minimum" "2" \ + "value_validation_range_maximum" "8589934592" \ + ] \ + [ipx::get_user_parameters MEM_SIZE -of_objects $cc] + +## Boolean parameters +foreach {k v} { \ + "SRC_RAW_DATA_EN" "false" \ + "DST_RAW_DATA_EN" "false" \ + "DST_CYCLIC_EN" "true" \ + } { \ + set_property -dict [list \ + "value_format" "bool" \ + "value" $v \ + ] \ + [ipx::get_user_parameters $k -of_objects $cc] + set_property -dict [list \ + "value_format" "bool" \ + "value" $v \ + ] \ + [ipx::get_hdl_parameters $k -of_objects $cc] +} + +### Customize IP Layout + +## Remove the automatically generated GUI page +ipgui::remove_page -component $cc [ipgui::get_pagespec -name "Page 0" -component $cc] +ipx::save_core [ipx::current_core] + +## Create a new GUI page +ipgui::add_page -name {Data Offload} -component [ipx::current_core] -display_name {Data Offload} +set page0 [ipgui::get_pagespec -name "Data Offload" -component $cc] + +## General Configurations +set general_group [ipgui::add_group -name "General Configuration" -component $cc \ + -parent $page0 -display_name "General Configuration" ] + +ipgui::add_param -name "ID" -component $cc -parent $general_group +set_property -dict [list \ + "display_name" "Core ID" \ +] [ipgui::get_guiparamspec -name "ID" -component $cc] + +ipgui::add_param -name "TX_OR_RXN_PATH" -component $cc -parent $general_group +set_property -dict [list \ + "widget" "comboBox" \ + "display_name" "Data path type" \ +] [ipgui::get_guiparamspec -name "TX_OR_RXN_PATH" -component $cc] + +ipgui::add_param -name "MEM_TYPE" -component $cc -parent $general_group +set_property -dict [list \ + "widget" "comboBox" \ + "display_name" "Storage Type" \ +] [ipgui::get_guiparamspec -name "MEM_TYPE" -component $cc] + +ipgui::add_param -name "MEM_SIZE" -component $cc -parent $general_group +set_property -dict [list \ + "display_name" "Storage Size" \ +] [ipgui::get_guiparamspec -name "MEM_SIZE" -component $cc] + +## DDR controller's user interface related configurations +set m_controller_group [ipgui::add_group -name "DDR Controller Interface Configuration" -component $cc \ + -parent $page0 -display_name "DDR Controller Interface Configuration" ] + +ipgui::add_param -name "MEMC_UIF_DATA_WIDTH" -component $cc -parent $m_controller_group +set_property -dict [list \ + "widget" "comboBox" \ + "display_name" "Interface data width" \ +] [ipgui::get_guiparamspec -name "MEMC_UIF_DATA_WIDTH" -component $cc] +set_property enablement_tcl_expr {$MEM_TYPE == 1} [ipx::get_user_parameters MEMC_UIF_DATA_WIDTH -of_objects $cc] + +ipgui::add_param -name "MEMC_UIF_ADDRESS_WIDTH" -component $cc -parent $m_controller_group +set_property -dict [list \ + "widget" "comboBox" \ + "display_name" "Interface address width" \ +] [ipgui::get_guiparamspec -name "MEMC_UIF_ADDRESS_WIDTH" -component $cc] +set_property enablement_tcl_expr {$MEM_TYPE == 1} [ipx::get_user_parameters MEMC_UIF_ADDRESS_WIDTH -of_objects $cc] + +ipgui::add_param -name "MEMC_BADDRESS" -component $cc -parent $m_controller_group +set_property -dict [list \ + "display_name" "PL DDR base address" \ +] [ipgui::get_guiparamspec -name "MEMC_BADDRESS" -component $cc] +set_property enablement_tcl_expr {$MEM_TYPE == 1} [ipx::get_user_parameters MEMC_BADDRESS -of_objects $cc] + +## Transmit and receive endpoints +set source_group [ipgui::add_group -name "Source Endpoint Configuration" -component $cc \ + -parent $page0 -display_name "Source Endpoint Configuration" \ + -layout "horizontal"] +set destination_group [ipgui::add_group -name "Destination Endpoint Configuration" -component $cc \ + -parent $page0 -display_name "Destination Endpoint Configuration" \ + -layout "horizontal"] + +ipgui::add_param -name "SRC_DATA_WIDTH" -component $cc -parent $source_group +set_property -dict [list \ + "display_name" "Source Interface data width" \ +] [ipgui::get_guiparamspec -name "SRC_DATA_WIDTH" -component $cc] + +ipgui::add_param -name "SRC_ADDR_WIDTH" -component $cc -parent $source_group +set_property -dict [list \ + "display_name" "Source Interface address width" \ +] [ipgui::get_guiparamspec -name "SRC_ADDR_WIDTH" -component $cc] + +ipgui::add_param -name "DST_DATA_WIDTH" -component $cc -parent $destination_group +set_property -dict [list \ + "display_name" "Destination Interface data width" \ +] [ipgui::get_guiparamspec -name "DST_DATA_WIDTH" -component $cc] + +ipgui::add_param -name "DST_ADDR_WIDTH" -component $cc -parent $destination_group +set_property -dict [list \ + "display_name" "Destination Interface address width" \ +] [ipgui::get_guiparamspec -name "DST_ADDR_WIDTH" -component $cc] + +## Other features +set features_group [ipgui::add_group -name "Features" -component $cc \ + -parent $page0 -display_name "Features" ] + + +ipgui::add_param -name "SRC_RAW_DATA_EN" -component $cc -parent $features_group +set_property -dict [list \ + "display_name" "Source Raw Data Enable" \ +] [ipgui::get_guiparamspec -name "SRC_RAW_DATA_EN" -component $cc] +set_property enablement_tcl_expr {$TX_OR_RXN_PATH == 0} [ipx::get_user_parameters SRC_RAW_DATA_EN -of_objects $cc] + +ipgui::add_param -name "DST_RAW_DATA_EN" -component $cc -parent $features_group +set_property -dict [list \ + "display_name" "Destionation Raw Data Enable" \ +] [ipgui::get_guiparamspec -name "DST_RAW_DATA_EN" -component $cc] +set_property enablement_tcl_expr {$TX_OR_RXN_PATH == 1} [ipx::get_user_parameters DST_RAW_DATA_EN -of_objects $cc] + +ipgui::add_param -name "DST_CYCLIC_EN" -component $cc -parent $features_group +set_property -dict [list \ + "display_name" "Destination Cyclic Mode Enabled" \ +] [ipgui::get_guiparamspec -name "DST_CYCLIC_EN" -component $cc] +set_property enablement_tcl_expr {$TX_OR_RXN_PATH == 1} [ipx::get_user_parameters DST_CYCLIC_EN -of_objects $cc] + +## Create and save the XGUI file +ipx::create_xgui_files $cc + +ipx::save_core [ipx::current_core] diff --git a/library/data_offload/data_offload_regmap.v b/library/data_offload/data_offload_regmap.v new file mode 100644 index 000000000..98a014e65 --- /dev/null +++ b/library/data_offload/data_offload_regmap.v @@ -0,0 +1,375 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2018 (c) Analog Devices, Inc. All rights reserved. +// +// In this HDL repository, there are many different and unique modules, consisting +// of various HDL (Verilog or VHDL) components. The individual modules are +// developed independently, and may be accompanied by separate and unique license +// terms. +// +// The user should read each of these license terms, and understand the +// freedoms and responsibilities that he or she has by using this source/core. +// +// This core is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. +// +// Redistribution and use of source or resulting binaries, with or without modification +// of this file, are permitted under one of the following two license terms: +// +// 1. The GNU General Public License version 2 as published by the +// Free Software Foundation, which can be found in the top level directory +// of this repository (LICENSE_GPL2), and also online at: +// +// +// OR +// +// 2. An ADI specific BSD license, which can be found in the top level directory +// of this repository (LICENSE_ADIBSD), and also on-line at: +// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD +// This will allow to generate bit files and not release the source code, +// as long as it attaches to an ADI device. +// +// *************************************************************************** +// *************************************************************************** +`timescale 1ns/100ps + +module data_offload_regmap #( + + parameter ID = 0, + parameter [ 0:0] MEM_TYPE = 1'b0, + parameter [33:0] MEM_SIZE = 1024, + parameter TX_OR_RXN_PATH = 0, + parameter AUTO_BRINGUP = 0) ( + + // microprocessor interface + input up_clk, + input up_rstn, + + input up_rreq, + output reg up_rack, + input [13:0] up_raddr, + output reg [31:0] up_rdata, + input up_wreq, + output reg up_wack, + input [13:0] up_waddr, + input [31:0] up_wdata, + + // source clock domain + input src_clk, + + // destination clock domain + input dst_clk, + + // resets for all clock domains + output reg src_sw_resetn, + output reg dst_sw_resetn, + + // status bit from the memory controller + input ddr_calib_done, + + // bypass control + output src_bypass, + output dst_bypass, + output oneshot, + + // synchronization + output sync, + output [ 1:0] sync_config, + + output [31:0] src_transfer_length, + + // FSM control and status + input [ 1:0] src_fsm_status, + input [ 1:0] dst_fsm_status, + + input [31:0] sample_count_msb, + input [31:0] sample_count_lsb + +); + + // local parameters + + localparam [31:0] CORE_VERSION = 32'h00000100; // 0.01.0 + localparam [31:0] CORE_MAGIC = 32'h44414F46; // DAOF + + // internal registers + + reg [31:0] up_scratch = 'd0; + reg up_sw_resetn = 'd0; + reg up_bypass = 'd0; + reg up_sync = 'd0; + reg [ 1:0] up_sync_config = 'd0; + reg up_oneshot = 1'b0; + reg [31:0] up_transfer_length = 'd0; + + //internal signals + + wire up_ddr_calib_done_s; + wire [ 1:0] up_wr_fsm_status_s; + wire [ 1:0] up_rd_fsm_status_s; + wire [31:0] up_sample_count_msb_s; + wire [31:0] up_sample_count_lsb_s; + wire src_sw_resetn_s; + wire dst_sw_resetn_s; + + // write interface + always @(posedge up_clk) begin + if (up_rstn == 1'b0) begin + up_wack <= up_wreq; + up_scratch <= 'd0; + up_sw_resetn <= AUTO_BRINGUP; + up_oneshot <= ~TX_OR_RXN_PATH; + up_bypass <= 'd0; + up_sync <= 'd0; + up_sync_config <= 'd0; + up_transfer_length <= 32'h0; + end else begin + up_wack <= up_wreq; + /* Scratch Register */ + if ((up_wreq == 1'b1) && (up_waddr[11:0] == 14'h02)) begin + up_scratch <= up_wdata; + end + /* Transfer Length Register */ + if ((up_wreq == 1'b1) && (up_waddr[11:0] == 14'h07)) begin + up_transfer_length <= up_wdata; + end + /* Reset Offload Register */ + if ((up_wreq == 1'b1) && (up_waddr[11:0] == 14'h21)) begin + up_sw_resetn <= up_wdata[0]; + end + /* Control Register */ + if ((up_wreq == 1'b1) && (up_waddr[11:0] == 14'h22)) begin + up_oneshot <= up_wdata[1]; + up_bypass <= up_wdata[0]; + end + /* SYNC Offload Register - self cleared, one pulse signal */ + if ((up_wreq == 1'b1) && (up_waddr[11:0] == 14'h40)) begin + up_sync <= up_wdata[0]; + end else begin + up_sync <= 1'b0; + end + /* SYNC RX Configuration Register */ + if ((up_wreq == 1'b1) && (up_waddr[11:0] == 14'h41)) begin + up_sync_config <= up_wdata[1:0]; + end + end + end + + //read interface for common registers + always @(posedge up_clk) begin + if (up_rstn == 1'b0) begin + up_rack <= 1'b0; + up_rdata <= 14'b0; + end else begin + up_rack <= up_rreq; + case(up_raddr) + + /* Version Register */ + 14'h000: up_rdata <= { + CORE_VERSION[31:16], /* MAJOR */ + CORE_VERSION[15: 8], /* MINOR */ + CORE_VERSION[ 7: 0] /* PATCH */ + }; + /* Peripheral ID Register */ + 14'h001: up_rdata <= ID; + + /* Peripheral ID Register */ + 14'h002: up_rdata <= up_scratch; + + /* Identification Register */ + 14'h003: up_rdata <= CORE_MAGIC; + + /* Configuration Register */ + 14'h004: up_rdata <= { + 31'b0, + /* 0 */ MEM_TYPE + }; + /* Configuration Storage Unit Size LSB Register */ + 14'h005: up_rdata <= MEM_SIZE[31:0]; + + /* Configuration Storage Unit Size MSB Register */ + 14'h006: up_rdata <= { + 29'b0, + /* 00-01 */ MEM_SIZE[33:32] + }; + + /* Configuration data transfer length */ + 14'h007: up_rdata <= up_transfer_length; + + /* 0x08-0x1f reserved for future use */ + + /* Memory Physical Interface Status */ + 14'h020: up_rdata <= { + 31'b0, + /* 0 */ up_ddr_calib_done_s + }; + /* Reset Offload Register */ + 14'h021: up_rdata <= { + 31'b0, + /* 0 */ up_sw_resetn + }; + /* Control Register */ + 14'h022: up_rdata <= { + 30'b0, + /* 1 */ up_oneshot, + /* 0 */ up_bypass + }; + /* 0x24-0x3f reserved for future use */ + + /* SYNC Offload Register */ + 14'h040: up_rdata <= { + 31'b0, + /* 0 */ up_sync + }; + /* SYNC RX Configuration Register */ + 14'h041: up_rdata <= { + 30'b0, + /* 00-01 */ up_sync_config + }; + /* 0x42-0x7f reserved for future use */ + + /* FMS Debug Register */ + 14'h080: up_rdata <= { + 16'b0, + /* 07-06 */ 2'b0, + /* 05-04 */ up_rd_fsm_status_s, + /* 03-02 */ 2'b0, + /* 01-00 */ up_wr_fsm_status_s + }; + /* Sample Count LSB Register */ + 14'h081: up_rdata <= up_sample_count_lsb_s; + + /* Sample Count MSB Register */ + 14'h082: up_rdata <= up_sample_count_msb_s; + + default: up_rdata <= 32'h00000000; + endcase + end + end /* read interface */ + + // Clock Domain Crossing Logic for reset, control and status signals + + sync_data #( + .NUM_OF_BITS (2), + .ASYNC_CLK (1)) + i_dst_fsm_status ( + .in_clk (dst_clk), + .in_data (dst_fsm_status), + .out_clk (up_clk), + .out_data (up_rd_fsm_status_s) + ); + + sync_data #( + .NUM_OF_BITS (2), + .ASYNC_CLK (1)) + i_src_fsm_status ( + .in_clk (src_clk), + .in_data (src_fsm_status), + .out_clk (up_clk), + .out_data (up_wr_fsm_status_s) + ); + + sync_data #( + .NUM_OF_BITS (64), + .ASYNC_CLK (1)) + i_xfer_status ( + .in_clk (src_clk), + .in_data ({sample_count_msb, + sample_count_lsb}), + .out_clk (up_clk), + .out_data ({up_sample_count_msb_s, + up_sample_count_lsb_s}) + ); + + generate + if (TX_OR_RXN_PATH) begin : sync_tx_path + + sync_data #( + .NUM_OF_BITS (3), + .ASYNC_CLK (1)) + i_sync_xfer_control ( + .in_clk (up_clk), + .in_data ({up_sync_config, + up_sync}), + .out_clk (dst_clk), + .out_data ({sync_config, + sync}) + ); + + end else begin : sync_rx_path + + sync_data #( + .NUM_OF_BITS (3), + .ASYNC_CLK (1)) + i_sync_xfer_control ( + .in_clk (up_clk), + .in_data ({up_sync_config, + up_sync}), + .out_clk (src_clk), + .out_data ({sync_config, + sync}) + ); + + end + endgenerate + + sync_bits #( + .NUM_OF_BITS (2), + .ASYNC_CLK (1)) + i_src_xfer_control ( + .in_bits ({ up_sw_resetn, up_bypass }), + .out_clk (src_clk), + .out_resetn (1'b1), + .out_bits ({ src_sw_resetn_s, src_bypass }) + ); + + sync_bits #( + .NUM_OF_BITS (2), + .ASYNC_CLK (1)) + i_dst_xfer_control ( + .in_bits ({ up_sw_resetn, up_bypass }), + .out_clk (dst_clk), + .out_resetn (1'b1), + .out_bits ({ dst_sw_resetn_s, dst_bypass }) + ); + + sync_bits #( + .NUM_OF_BITS (1), + .ASYNC_CLK (1)) + i_ddr_calib_done_sync ( + .in_bits (ddr_calib_done), + .out_clk (up_clk), + .out_resetn (1'b1), + .out_bits (up_ddr_calib_done_s) + ); + + sync_bits #( + .NUM_OF_BITS (1), + .ASYNC_CLK (1)) + i_dst_oneshot_sync ( + .in_bits (up_oneshot), + .out_clk (dst_clk), + .out_resetn (1'b1), + .out_bits (oneshot) + ); + + sync_data #( + .NUM_OF_BITS (32), + .ASYNC_CLK (1)) + i_sync_src_transfer_length ( + .in_clk (up_clk), + .in_data (up_transfer_length), + .out_clk (src_clk), + .out_data (src_transfer_length) + ); + + always @(posedge src_clk) begin + src_sw_resetn <= src_sw_resetn_s; + end + + always @(posedge dst_clk) begin + dst_sw_resetn <= dst_sw_resetn_s; + end + +endmodule diff --git a/library/data_offload/data_offload_sv.ttcl b/library/data_offload/data_offload_sv.ttcl new file mode 100644 index 000000000..402d9243a --- /dev/null +++ b/library/data_offload/data_offload_sv.ttcl @@ -0,0 +1,55 @@ +<: :> +<: set ComponentName [getComponentNameString] :> +<: setOutputDirectory "./sim/" :> +<: setFileName ${ComponentName}_pkg :> +<: setFileExtension ".sv" :> +<: set id [get_property MODELPARAM_VALUE.ID] :> +<: set mem_type [get_property MODELPARAM_VALUE.MEM_TYPE] :> +<: set mem_size [get_property MODELPARAM_VALUE.MEM_SIZE] :> +<: set memc_uif_data_width [get_property MODELPARAM_VALUE.MEMC_UIF_DATA_WIDTH] :> +<: set memc_uif_address_width [get_property MODELPARAM_VALUE.MEMC_UIF_ADDRESS_WIDTH] :> +<: set memc_baddress [get_property MODELPARAM_VALUE.MEMC_BADDRESS] :> +<: set tx_or_rxn_path [get_property MODELPARAM_VALUE.TX_OR_RXN_PATH] :> +<: set src_data_width [get_property MODELPARAM_VALUE.src_data_width] :> +<: set src_raw_data_en [get_property MODELPARAM_VALUE.src_raw_data_en] :> +<: set src_addr_width [get_property MODELPARAM_VALUE.src_addr_width] :> +<: set dst_data_width [get_property MODELPARAM_VALUE.dst_data_width] :> +<: set dst_raw_data_en [get_property MODELPARAM_VALUE.dst_raw_data_en] :> +<: set dst_addr_width [get_property MODELPARAM_VALUE.dst_addr_width] :> +<: set dst_cyclic_en [get_property MODELPARAM_VALUE.dst_cyclic_en] :> + +// boolean to intiger +<: proc b2i {b} { if {$b==true} {return 1} else {return 0}} :> + +// C hex to verilog hex +<: proc h2vh {a} { return [string replace $a 0 1 "'h"]} :> + +/////////////////////////////////////////////////////////////////////////// +//NOTE: This file has been automatically generated by Vivado. +/////////////////////////////////////////////////////////////////////////// + +package <=: ComponentName :>_pkg; + +/////////////////////////////////////////////////////////////////////////// +// These parameters are named after the component for use in your verification +// environment. +/////////////////////////////////////////////////////////////////////////// + + parameter <=: ComponentName :>_ID = <=: $id :>; + parameter <=: ComponentName :>_MEM_TYPE = <=: $mem_type :>; + parameter <=: ComponentName :>_MEM_SIZE = <=: $mem_size :>; + parameter <=: ComponentName :>_MEMC_UIF_DATA_WIDTH = <=: $memc_uif_data_width :>; + parameter <=: ComponentName :>_MEMC_UIF_ADDRESS_WIDTH = <=: $memc_uif_address_width :>; + parameter <=: ComponentName :>_MEMC_BADDRESS = <=: h2vh $memc_baddress :>; + parameter <=: ComponentName :>_TX_OR_RXN_PATH = <=: b2i $tx_or_rxn_path :>; + parameter <=: ComponentName :>_SRC_DATA_WIDTH = <=: b2i $src_data_width :>; + parameter <=: ComponentName :>_SRC_RAW_DATA_WIDTH = <=: b2i $src_raw_data_en :>; + parameter <=: ComponentName :>_SRC_ADDR_WIDTH = <=: b2i $src_addr_width :>; + parameter <=: ComponentName :>_DST_DATA_WIDTH = <=: b2i $dst_data_width :>; + parameter <=: ComponentName :>_DST_RAW_DATA_WIDTH = <=: b2i $dst_raw_data_en :>; + parameter <=: ComponentName :>_DST_ADDR_WIDTH = <=: b2i $dst_addr_width :>; + parameter <=: ComponentName :>_DST_CYCLIC_EN = <=: b2i $dst_cyclic_en :>; + +////////////////////////////////////////////////////////////////////////// + +endpackage : <=: ComponentName :>_pkg