• 0

Nexys4 DDR DDR2 Memory Interface


Hi all. I would like to ask you a question regarding the RAM/DDR controller of the Nexys4DDR.

I would like to access (IP parameters in the MIG as in https://reference.digilentinc.com/learn/programmable-logic/tutorials/nexys-4-ddr-user-demo/start ) the ddr memory whose component is shown below using a 16b width data.
For this, if I am correct, the address is handled RANK_BANK_ROW_COLUMN.
So I do not understand why in the provided code from Mihaita Nagy they create the user_interface address like these
            mem_addr <= ram_a_int(26 downto 4) & "0000";
Here, mem_addr has 27b width.
Similarly, I wonder why in the ram control the mask and the read data uses the following LSB and not the MSB of the address:
case(ram_a_int(3 downto 1)) is
      when "000" =>
          if ram_ub_int = '0' and ram_lb_int = '1' then -- UB
             ram_dq_o <= mem_rd_data(15 downto 8) &
                         mem_rd_data(15 downto 8);
Would not it be more sense to store the 16b words contiguously, starting at the address 0?
and if so, how would the vhdl code look like?
Thank you very much for your time, and regards.
The component used looks like
component ddr_xadc
port (
   -- Inouts
   ddr2_dq              : inout std_logic_vector(15 downto 0);
   ddr2_dqs_p           : inout std_logic_vector(1 downto 0);
   ddr2_dqs_n           : inout std_logic_vector(1 downto 0);
   -- Outputs
   ddr2_addr            : out   std_logic_vector(12 downto 0);
   ddr2_ba              : out   std_logic_vector(2 downto 0);
   ddr2_ras_n           : out   std_logic;
   ddr2_cas_n           : out   std_logic;
   ddr2_we_n            : out   std_logic;
   ddr2_ck_p            : out   std_logic_vector(0 downto 0);
   ddr2_ck_n            : out   std_logic_vector(0 downto 0);
   ddr2_cke             : out   std_logic_vector(0 downto 0);
   ddr2_cs_n            : out   std_logic_vector(0 downto 0);
   ddr2_dm              : out   std_logic_vector(1 downto 0);
   ddr2_odt             : out   std_logic_vector(0 downto 0);
   -- Inputs
   sys_clk_i            : in    std_logic;
   sys_rst              : in    std_logic;
   -- user interface signals
   app_addr             : in    std_logic_vector(26 downto 0);
   app_cmd              : in    std_logic_vector(2 downto 0);
   app_en               : in    std_logic;
   app_wdf_data         : in    std_logic_vector(127 downto 0);
   app_wdf_end          : in    std_logic;
   app_wdf_mask         : in    std_logic_vector(15 downto 0);
   app_wdf_wren         : in    std_logic;
   app_rd_data          : out   std_logic_vector(127 downto 0);
   app_rd_data_end      : out   std_logic;
   app_rd_data_valid    : out   std_logic;
   app_rdy              : out   std_logic;
   app_wdf_rdy          : out   std_logic;
   app_sr_req           : in    std_logic;
   app_sr_active        : out   std_logic;
   app_ref_req          : in    std_logic;
   app_ref_ack          : out   std_logic;
   app_zq_req           : in    std_logic;
   app_zq_ack           : out   std_logic;
   ui_clk               : out   std_logic;
   ui_clk_sync_rst      : out   std_logic;
  --  device_temp_i        : in    std_logic_vector(11 downto 0); -- not used, inside the core
   init_calib_complete  : out   std_logic);
end component;

Share this post

Link to post
Share on other sites

2 answers to this question

Recommended Posts

  • 0


Welcome to the forum!  That's a good question.  You see there are only 16 data lines, so ... why not access the memory 16-bits at a time?

The bottom line answer is: that's now how the DDR2 memory interface standard works.

I'll reply further based upon DDR3, the spec I understand best, but understand that it is similar to DDR2.

DDR3 requires 8-transactions to take place across 4-clocks.  If the data width is 16-bits wide, this means that the minimum transaction width is 128 bits.  Always.  There's some synchronization reasons in that choice, for example a strobe needs to be created and synchronized to, five strobes are needed to write across four clocks, four strobes for subsequent, etc.

If you did further into the DDR spec, you'll find that the DDR3 spec allows for half transfers, such as 64-bits instead of 128-bits.  These half transfers, though, still require all four clocks.  Hence, there's no real advantage to using them.

One way to deal with this very wide memory width, when you want to do smaller operations, is to  accumulate your operation into 128-bit operations.

If you want to stay at 16 bits, you'll need to do a couple of things.  First, for writes, you'll want to create a data mask.  This data mask will tell the controller which of the 128-bits will be written to.  The mask itself has 8-bit resolution, so expect 128/8=16 bits in the mask going to the controller.  When reading, you'll need to select the 16-bits you want from the 128-bits returned.

If you are using an AXI interface, you may configure the AXI interface to handle a smaller width.  I know I tend to access it using a 32-bit width, but I think you can control that bit width when you set up the MIG controller.  This doesn't change the options for dealing with the 128-bit width that I outlined above, it just means that the logic to do so has been pushed into the controller.


Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now