Forum Discussion

HTolentino's avatar
HTolentino
Icon for New Contributor rankNew Contributor
3 years ago

SDRAM without HPS

Hello, I am using a DE1-SOC cyclone v board.

I am trying to read and write into/from my 64Mb SDRAM without using the NIOS hps.

I understand that this topic has been mentioned at

https://community.intel.com/t5/Intel-Quartus-Prime-Software/Sdram/m-p/174628#M43315

However, after going to terasic and downloading DE1_SoC_SDRAM_RTL_Test on the DE1_SOC system CD I found it difficult to understand.

First, I mainly code in VHDL so I have a hard time translating the verilog example.

Next, there are a lot of attached components which I assume is part of the debugging process.

Is there a simple way to confirm reading and writing in SDRAM with a simple wrapper code?

I just assume that controller from the platform designer deal with all the nitty gritty and the "conduit" wires deal with the controller interacting with the SDRAM.

1) Is there going to be complex timing I have to consider in order to read and write from SDRAM?

2) How can we verify that it is working, I tried using signal tap but there are so many weird signals; and signals I want such as counters are not in the list. Also ISSP seems kind of too simplistic.

The code I have is simple, I have the platform designer component wrapped as "ramsys" and I just have a top level design called SDRAM.

In the top level, I simply toggled the Read and write enable by a counter. Likewise, the address follows a counter.

Note: I just have a random FF because I was messing with it to see if the signal can appear in signaltap.

Is this process correct? Or am I too naive?

Thank you

---==================================================================

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
--use work.pcg.all;
entity sdram is
port(
CLOCK_50: IN STD_LOGIC;
SW: IN STD_LOGIC_VECTOR(9 downto 0);
------------------SDRAM---------------------------
DRAM_ADDR: OUT STD_LOGIC_VECTOR(12 downto 0);
DRAM_BA: OUT STD_LOGIC_VECTOR(1 downto 0);
DRAM_CAS_N: OUT STD_LOGIC;
DRAM_CKE: OUT STD_LOGIC;
DRAM_CLK: OUT STD_LOGIC;
DRAM_CS_N: OUT STD_LOGIC;
DRAM_DQ: INOUT STD_LOGIC_VECTOR(15 downto 0);
DRAM_RAS_N: OUT STD_LOGIC;
DRAM_WE_N: OUT STD_LOGIC;
DRAM_LDQM,DRAM_UDQM: OUT STD_LOGIC
);
end sdram;
architecture main of sdram is
TYPE STAGES IS (ST0,ST1);
SIGNAL BUFF_CTRL: STAGES:=ST0;
---------------------------test signals----------------------------
--signal counter : integer range 0 to 1000;
--signal test: std_logic:='0';
--signal testdata: std_logic_vector(7 downto 0):="00000000";
--signal Xpos,Ypos: integer range 0 to 799:=0;
---------------------------sync-----------------------------
signal BUFF_WAIT: std_logic:='0';
signal VGAFLAG: std_logic_vector(2 downto 0);
------------------vga----------------------------------
SIGNAL CLK100: STD_LOGIC;
------------------sdram--------------------------------
SIGNAL SDRAM_ADDR: STD_LOGIC_VECTOR(24 downto 0):= (others => '0');
SIGNAL SDRAM_ADDR_CTR: STD_LOGIC_VECTOR(24 downto 0) := (others => '0');
SIGNAL SDRAM_BE_N: STD_LOGIC_VECTOR(1 downto 0);
SIGNAL SDRAM_CS: STD_LOGIC;
SIGNAL SDRAM_RDVAL,SDRAM_WAIT:STD_LOGIC;
SIGNAL SDRAM_RE_N,SDRAM_WE_N: STD_LOGIC;
attribute syn_keep: boolean;
SIGNAL SDRAM_READDATA: STD_LOGIC_VECTOR(15 downto 0);
attribute syn_keep of SDRAM_READDATA : signal is true;
SIGNAL SDRAM_WRITEDATA: STD_LOGIC_VECTOR(15 downto 0);
SIGNAL DRAM_DQM : STD_LOGIC_VECTOR(1 downto 0);
component ramsys is
port (
clk_clk : in std_logic := 'X'; -- clk
reset_reset_n : in std_logic := 'X'; -- reset_n
wire_addr : out std_logic_vector(12 downto 0); -- addr
wire_ba : out std_logic_vector(1 downto 0); -- ba
wire_cas_n : out std_logic; -- cas_n
wire_cke : out std_logic; -- cke
wire_cs_n : out std_logic; -- cs_n
wire_dq : inout std_logic_vector(15 downto 0) := (others => 'X'); -- dq
wire_dqm : out std_logic_vector(1 downto 0); -- dqm
wire_ras_n : out std_logic; -- ras_n
wire_we_n : out std_logic; -- we_n
sdram_address : in std_logic_vector(24 downto 0) := (others => 'X'); -- address
sdram_byteenable_n : in std_logic_vector(1 downto 0) := (others => 'X'); -- byteenable_n
sdram_chipselect : in std_logic := 'X'; -- chipselect
sdram_writedata : in std_logic_vector(15 downto 0) := (others => 'X'); -- writedata
sdram_read_n : in std_logic := 'X'; -- read_n
sdram_write_n : in std_logic := 'X'; -- write_n
sdram_readdata : out std_logic_vector(15 downto 0); -- readdata
sdram_readdatavalid : out std_logic; -- readdatavalid
sdram_waitrequest : out std_logic -- waitrequest
);
end component ramsys;
component FF_test is
port( Clk :in std_logic;
Q : out std_logic_vector(15 downto 0);
D :in std_logic_vector(15 downto 0)
);
end component;
signal counter : std_logic_vector(7 downto 0) := (others => '0');
signal counter0 : std_logic_vector(7 downto 0) := (others => '0');
signal s_Q : std_logic_vector(15 downto 0) := (others => '0');
--=========================================
begin
u0 : ramsys
port map (
clk_clk => CLOCK_50, -- clk.clk
reset_reset_n =>'1', -- reset.reset_n
wire_addr => DRAM_ADDR, -- wire.addr
wire_ba => DRAM_BA, -- .ba
wire_cas_n => DRAM_CAS_N, -- .cas_n
wire_cke => DRAM_CKE, -- .cke
wire_cs_n => DRAM_CS_N, -- .cs_n
wire_dq => DRAM_DQ, -- .dq
wire_dqm => DRAM_DQM, -- .dqm
wire_ras_n => DRAM_RAS_N, -- .ras_n
wire_we_n => DRAM_WE_N, -- .we_n
sdram_address => SDRAM_ADDR, -- sdram.address
sdram_byteenable_n => SDRAM_BE_N, -- .byteenable_n
sdram_chipselect => SDRAM_CS, -- .chipselect
sdram_writedata => SDRAM_WRITEDATA, -- .writedata
sdram_read_n => SDRAM_RE_N, -- .read_n
sdram_write_n => SDRAM_WE_N, -- .write_n
sdram_readdata => SDRAM_READDATA, -- .readdata
sdram_readdatavalid => SDRAM_RDVAL, -- .readdatavalid
sdram_waitrequest => SDRAM_WAIT -- .waitrequest
);
u1 : FF_test
port map ( clk => clk100,
Q => s_Q,
D => SDRAM_READDATA
);
DRAM_LDQM<=DRAM_DQM(0);
DRAM_UDQM<=DRAM_DQM(1);
DRAM_CLK<=CLK100;
SDRAM_CS<='1';
SDRAM_BE_N<="00";
process(clk100)
begin
if rising_edge(clk100) then
if(counter0 <= "00000111") then
SDRAM_WE_N<='0';
SDRAM_RE_N<='1';
counter0 <= counter0 + '1';
elsif(counter0 > "00000111") then
SDRAM_WE_N<='1';
SDRAM_RE_N<='0';
counter0 <= counter0 + '1';
elsif (counter0 > "00001111") then
counter0 <= (others => '0');
end if;
end if;
end process;
process(clk100)
begin
if rising_edge (clk100) then
case BUFF_CTRL is
when st0=>------------WRITE
if (SDRAM_WAIT='0')then
if (SDRAM_RE_N ='1' and SDRAM_WE_N = '0') then
SDRAM_WRITEDATA(7 downto 0)<= counter;
counter <= counter + '1';
SDRAM_ADDR<= SDRAM_ADDR_CTR;
SDRAM_ADDR_CTR <= SDRAM_ADDR_CTR + '1';
if SDRAM_ADDR_CTR = "000000000000000000000111" then
counter <= (others => '0');
SDRAM_ADDR_CTR <= (others => '0');
end if;
end if;
end if;
when st1=>-----------READ
if (SDRAM_RE_N ='0' and SDRAM_WE_N = '1') then
IF(SDRAM_WAIT='0')THEN
SDRAM_ADDR<= counter;
END IF;
end if;
end case;
end if;
end process;
end main;

11 Replies

  • sstrell's avatar
    sstrell
    Icon for Super Contributor rankSuper Contributor

    Few things I notice.

    You don't cover in your code if waitrequest is high. You have to continue holding the address and control signals when this happens. It might be easier to make use of some off the shelf component in Platform Designer instead of exporting the interface and coding your own Avalon host to communicate with the SDRAM. Maybe a FIFO or something from the IP Catalog.

    For Signal Tap, use the pre-synthesis Signal Tap filter when tapping nodes. That way you'll recognize the signals from the code instead of the changed names you'll see if you use the post-fit filter.

    Also, I presume you have a .sdc file with timing constraints and that you made pin assignments correctly in the Pin Planner.

    • HTolentino's avatar
      HTolentino
      Icon for New Contributor rankNew Contributor

      Hello. I apologize for the late reply. At the moment @sstrell was right about the sdc and and my lack of pin assignment. I was finally able to see the signals i wanted in signal tap after putting in pre synthesis.

      Currently I am able to see some sort of correct semblance on the waveform but for some reason it is not quite right.

      I am certain it is my finite state machine code or something but I am still trying to find the problem functionally.

      I wanted to see if I can get the waveform right before posting it.

      I think my next problem is regarding SDRAM wait request.

      I coded it the address/data to latch when it is high and only change the data/address when it is low as suggested,.

      On the other hand I cant predict when SDRAM will be high or not.

      Is this something completely random or is there some kind of timing it follows so we can predict when SDRAM wait request will be low.

      At the moment, I see this SDRAM wait request as some annoying enable signal. I wonder if this is completely natural in the memory interface.

      Is it alright if I can work on it a bit longer before following up?

      I apologize again for the delay.

      But Thank you for the replies.

      -htolentino

    • HTolentino's avatar
      HTolentino
      Icon for New Contributor rankNew Contributor

      Hello thank you for the link.

      Ive had the chance to look at this before but was confused about it.

      I guess now is the best chance to ask about it.

      Just for reference for the pictures - https://www.intel.com/content/www/us/en/docs/programmable/683091/22-3/typical-read-and-write-transfers.html

      First, I wanted to ask what the vertical squigly lines.

      Second, In the context above Who is the host. Who is agent?

      In this picture it seems my top level custom logic is the M/Master/Host while the SDRAM controller is the S/Slave/Agent

      However, I think based on the reading and writing example I think the host is the controller while the agent is the actual external SDRAM.

      Next are some assumpltions. Please correct any assumptions I have:

      -steps 1 - 4 are READING

      -5 - 6 are for WRITING.

      -The order of reading or writing first doesnot matter. '

      -The memory have something initially or was written data before which is why it is able to READ first.

      For READING.

      Clock cycle 1 : the host sends data/address/read..........the agent asserts wait request

      Clock cycle 2 : the host receives wait request = 1, thereby latching the data/address/read.

      Clock cycle 3: the host still latch data/address/read.......the agents deasserts wait request

      Clock cycle 4: the host receives wait request = 0, thereby finally sampling data/address/read.

      Overall, wait request = 1 for only 2 clock cycles.

      On the other hand, wait request for writing is much longer even though it seems the step is the same as the reading.

      It says on step 6 the wait request deasserts after the rising edge of the clk but the which rising edge it choose seems random.

      If we look at the reading part, the wait request becomes 0 immediately after the host spends a clock cycle to register it as 1.

      But in the writing part it takes way longer

      I apologize if I am making too many assumptions and complicating something that is simple.

      Please let me know any concerns or comments.

      I appreciate everyone as usual.

      -htolentino

  • sstrell's avatar
    sstrell
    Icon for Super Contributor rankSuper Contributor

    Squiggly lines: as long as waitrequest is held high, the other signals must be held. This is showing that it could be multiple cycles before waitrequest is released.

    Host is whatever system component is issuing read or write commands (previously referred to as a master). Agent (previously a slave) responds to commands by either storing data on a write or replying with data on a read.

    For external memory, you have an IP that responds to read/write commands on the Avalon interface that is an intermediary to the external memory. That is the agent, not the external memory.

    The waveforms are generic read and write transfers to understand the signaling on an Avalon interface. It doesn't matter if read or write occurs first.

    cycle 1: The interconnect asserts waitrequest (often due to arbitration due to multiple hosts accessing a single agent at the same time), unless the agent includes a waitrequest output signal for manual control of waitrequest. So the note below the waveform indicates that the agent, in this case, is manually asserting waitrequest.

    cycle 2: it's not a latching. When the host sees waitrequest, logic in the host would know to continue asserting the control signals.

    waitrequest can be any length needed. It doesn't matter whether it's a read or a write. It's up to whatever is controlling it, the interconnect or the agent. Again, the waveform is illustrative to explain how the signaling works, not how every design must work.

    • HTolentino's avatar
      HTolentino
      Icon for New Contributor rankNew Contributor

      Hello thank you for the reply,

      just to clarify, the agent you mentioned is the SDRAM controller IP right?

      In Platform designer I instantiated the SDRAM controller with the PLL.

      This block design is called RAMSYS.

      At the moment, RAMSYS has SDRAM wait request as the output port.

      In my top level design (host), I connected this output to an interconnect which I used to determine if I can proceed with reading or writing.

      I undestand wait request behaves a certain way based on the inputs of the controller .

      It also seems it is dependent on some timing based on the figure before.

      My confusion is that the clock cycle difference from read and write are different.

      Even though write cycle provided the necessary inputs (data/address/read enable), same as the read cycle,

      it still deasserts wait request longer than write.

      If it is indeed based on some sort of timing, can I assume it would be some static timing based if the inputs arrive at the same clock cycle?

      I realized that my host (state machine code) depends on the wait request while the agent (SDRAM controller) depends on the inputs from the host.

      I confused myself even further as it looks this looks like a feedback loop.

      At the end of the day, I really dont know whos really reponsible for wait request.

      I understand you made a note on this ...

      "

      When the host sees waitrequest, logic in the host would know to continue asserting the control signals.

      waitrequest can be any length needed. It doesn't matter whether it's a read or a write. It's up to whatever is controlling it, the interconnect or the agent. Again, the waveform is illustrative to explain how the signaling works, not how every design must work.

      "

      In bold you note that the wait request is controlled by the agent and the one controlling it...isnt the one controlling it the host?.

      OVerall it seems like I am going backwards as I dont even know which really controls who.

      I will continue reading on it and I understand if this question is off tangent the original problem.

      Thank you.

      -htolentino

      • sstrell's avatar
        sstrell
        Icon for Super Contributor rankSuper Contributor

        @HTolentino wrote:

        Hello thank you for the reply,

        just to clarify, the agent you mentioned is the SDRAM controller IP right?

        Yes.

        In Platform designer I instantiated the SDRAM controller with the PLL.

        This block design is called RAMSYS.

        At the moment, RAMSYS has SDRAM wait request as the output port.

        In my top level design (host), I connected this output to an interconnect which I used to determine if I can proceed with reading or writing.

        I undestand wait request behaves a certain way based on the inputs of the controller .

        It also seems it is dependent on some timing based on the figure before.

        The interconnect I referred to is the interconnect automatically generated by Platform Designer. I'm not sure what interconnect you are referring to here. And again, waitrequest is not timing based. It's based on logic. When the agent cannot currently accept commands (arbitration is a common cause), depending on the design, either the interconnect or the agent (if it has manual waitrequest control with an output signal) holds waitrequest high as long as necessary.

        My confusion is that the clock cycle difference from read and write are different.

        Even though write cycle provided the necessary inputs (data/address/read enable), same as the read cycle,

        it still deasserts wait request longer than write.

        Again, that's an illustrative waveform. It's not showing the exact number of cycles for things to happen.

        If it is indeed based on some sort of timing, can I assume it would be some static timing based if the inputs arrive at the same clock cycle?

        Again, no. The assertion of waitrequest is not timing based.

        I realized that my host (state machine code) depends on the wait request while the agent (SDRAM controller) depends on the inputs from the host.

        I confused myself even further as it looks this looks like a feedback loop.

        At the end of the day, I really dont know whos really reponsible for wait request.

        The interconnect or agent is responsible for the generation of waitrequest. The host (your design in this case) is responsible for interpreting waitrequest as an input signal and holding your output control signals steady as long as waitrequest is held high. (i.e. "if waitrequest=1 on this clock cycle, hold outputs at current value until after the first clock cycle where waitrequest is released; that's the cycle the transfer takes place)

        I understand you made a note on this ...

        "

        When the host sees waitrequest, logic in the host would know to continue asserting the control signals.

        waitrequest can be any length needed. It doesn't matter whether it's a read or a write. It's up to whatever is controlling it, the interconnect or the agent. Again, the waveform is illustrative to explain how the signaling works, not how every design must work.

        "

        In bold you note that the wait request is controlled by the agent and the one controlling it...isnt the one controlling it the host?.

        Again, either the agent can control waitrequest manually or if it does not have a waitrequest output, the interconnect can generate and sent waitrequest to the host as needed.

        OVerall it seems like I am going backwards as I dont even know which really controls who.

        I will continue reading on it and I understand if this question is off tangent the original problem.

        Thank you.

        -htolentino


  • aikeu's avatar
    aikeu
    Icon for Regular Contributor rankRegular Contributor

    Hi sstrell,


    Thanks for your help and informative feedback on the issue.


    Thanks.

    Regards,

    Aik Eu


  • aikeu's avatar
    aikeu
    Icon for Regular Contributor rankRegular Contributor

    Hi HTolentino,


    I am closing the thread for now. This thread will be transitioned to community support. If you have a new question, feel free to open a new thread to get the support from Intel experts. Otherwise, the community users will continue to help you on this thread. Thank you.


    Thanks.

    Regards,

    Aik Eu