data_offload: Initial commit
parent
6e97803437
commit
86b611c1f7
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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:
|
||||
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
|
||||
//
|
||||
// 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
|
||||
|
|
@ -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}]
|
|
@ -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:
|
||||
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
|
||||
//
|
||||
// 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
|
||||
|
|
@ -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:
|
||||
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
|
||||
//
|
||||
// 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
|
||||
|
|
@ -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]
|
|
@ -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:
|
||||
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
|
||||
//
|
||||
// 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
|
|
@ -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
|
Loading…
Reference in New Issue