Design Guidelines - Draft
Overview
This core is a DDR3 controller with two pipelined Wishbone slave
ports.
It is based on the Spartan6 hardware core and a memory controller block
(MCB) core generated by Xilinx
CoreGen.
The Xilinx Memory Controller Block hard-IP is fully documented in Xilinx's user guide ug388.pdf, but as a general overview we may think on it as composed by the following main elements:
- 32 bits width, 64 positions FIFO for data write (1 per each of the two active DDR3 access ports)
- 32 bits width, 64 positions FIFO for data read (1 per each of the two active DDR3 access ports)
- 4 positions FIFO for commands (1 per each of the two active DDR3 access ports)
- 1 arbiter to grant access to the DDR3 to all of the ports (configured to only serve the two active DDR3 access ports)
The inner working mechanism for the MCB is composed by three basic processes:
- Write: you put a data block in the data write FIFO, and then you put into the command FIFO a request for writing the whole block at a specific DDR3 offset.
- Read: you put in the command FIFO a request for a block of specific size starting in a specific FIFO, so once this command is served the memory controller fill the data read FIFO.
- Arbitration: working in a round-robin scheme, the arbiter decides which one of the DDR ports is going to be served and fetches a command from the command FIFO of the selected port.
In this way, both of the Wishbone pipelined slave controllers are just acting as a direct bridge between the Wishbone bus and the MCB, i.e. when a read/write operation is issued from a Wishbone master in one of the Wishbone slaves, the slave controller just translate the signals from the Wishbone bus into MCB data read/write commands. Understanding this point is very important when bringing up a new design as we have not got any extra FIFO capabilities in the logic inside the Spartan6 DDR3 core, but only those FIFOs (write, read & command) included in the MCB hard-IP.
As the pipelined Wishbone bus is intended for transferring unbounded streams of data, there is not a direct match between the potentially variable length of the data bursts being transferred and the size of the inner MCB's FIFOs. In this way, a mechanism to slice the Wishbone data bursts and accommodate the data as read/write commands into the MCB is required for proper operation. In the Wishbone slaves included in the DDR3 controller for Spartan6, this operation can be driven by the associated Wishbone masters or internally managed by the own DDR3 core:
- By using the Wishbone CYC signal, the associated Wishbone master indicates to the DDR that we are starting/ending a data transfer block, so this is the one that indicates to the DDR controller that we want to issue a new DDR command.
- If we try to read/write an "infinite length" data block, the DDR core controller will automatically take care of slicing the data and issuing the read/write commands to the DDR accordingly with a hard-coded internal burst length limit (length limit equal to 32).
NOTE:* it's very important to note that if we push the DDR3 controller core close to the inner MCB's performance limit and assert/de-assert the CYC signal too often in at least one of the Wishbone slave interfaces, we may reach a condition in which the arbiter is not able to serve all of the incoming commands from both of the Wishbone slaves leading to data corruption. This is due to the fact that the 4-positions command FIFO data becomes full very fast, even if there is plenty of room in the read/write data 64-positions FIFOs. As the command FIFO doesn't include a FULL flag, the core always believes it may accommodate a new command as far as there is space enough in the data FIFO... but the new command is simply ignored if the command FIFO is full.
Conditional selection of the desired DDR controller configuration
The DDR3 controller core for Spartan6 supports different DDR3 hardware configurations, so that it's necessary to specify which one we want to use in our top design. In older designs, this was made by pointing to different repository branches, each of them containing the same general code plus the specific variations of the code related to a specific low level DDR3 hardware configuration. By following this approach, efficient code maintenance is hard to accomplish, as the bug fixes or improvements need to be propagated to all of the different branches. As you can check in the repository tab, these hardware-specific branches have been kept for backwards compatibility.
Nowadays, the recommended way of choosing the appropriated hardware specific DDR3 version is to point to the "master" branch and to include an extra "ctrls" parameter to choose the specific DDR3 configuration files in the top Manifest.py. In order to allow this, the "master" branch already contains all of the different DDR3 configuration files plus a "Manifest.py" implementing the required selection logic.
ctrls parameter | DDR3 Configuration |
---|---|
bank3_32b_32b | ddr3_ctrl_spec_bank3_32b_32b |
bank3_64b_32b | ddr3_ctrl_spec_bank3_64b_32b |
bank4_32b_32b | ddr3_ctrl_svec_bank4_32b_32b |
bank4_64b_32b | ddr3_ctrl_svec_bank4_64b_32b |
bank5_32b_32b | ddr3_ctrl_svec_bank5_32b_32b |
bank5_64b_32b | ddr3_ctrl_svec_bank5_64b_32b |
bank1_32b_32b | ddr3_ctrl_vfc_bank1_32b_32b |
bank1_64b_32b | ddr3_ctrl_vfc_bank1_64b_32b |
As a quick example, let's consider we have a design that is based on the SPEC and uses the bank3 32b/32b configuration. In order to point to the appropriated DDR3 config, we should include the following statement in our top Manifest.py:
ctrls = [ "bank3_32b_32b" ]
To make use of this extra parameter, you'll need to issue the following hdlmake commands for fetching the dependencies and generating the ISE project and the Makefile (Hdlmake v2.1):
hdlmake --allow-unknown fetch
hdlmake --allow-unknown auto
Note:* in the Hdlmake develop branch, the --allow-unknown functionality is enabled by default to allow greater flexibility, so that the commands required are just:
hdlmake fetch
hdlmake