user7586019
user7586019

Reputation: 1

My SDRAM can keep data valid forever without refreshing, how can this be possible?

I designed a simple SDRAM driving module with Verilog HDL. The module defines a simple finite state machine. It performs initialization of the SDRAM (Winbond's W9825G6KH 4M * 4Banks * 16bits) in the first few states, then enters a normal mode state to wait for read/write requests sent by microprocessors. It responds to each request according to the timing diagram defined in the SDRAM datasheet and returns to normal mode state afterwards. I tested the module on Intel Altera FPGA and it seems to work as expected. Data can be written and read correctly. However, I noticed that the SDRAM seems to be able to keep stored data forever without refreshing! Currently, the module does not have any refreshing explicitly implemented (at least what I thought so). Attached is the FSM. Can anyone help diagnose my code a little bit and provide some guidance? Does the code contain some hidden unseen logic that automatically refreshes the SDRAM without my noticing? Much appreciated.

/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/* normal operation loop */
5'b01000:  //normal operation starts here
begin
    if((wbRPtr==wbWPtr)&&(rbRPtr==rbWPtr)) //if no CPU request for data (write/read)
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01000; //stay on the same state
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
    else if(wbRPtr!=wbWPtr) //CPU has sent a data item to write to the DRAM
    begin
        cs_ <= 0; 
        ras_ <= 0; 
        cas_ <= 1; 
        we_ <= 0; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01001; //go to precharge waiting
        counterEnable <= 1;
        rst2 <= 1;
        addr <= 0;
        bs <= waddrBuffer[wbRPtr][23:22]; //highest two bits of the buffered address are bank selection
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
    else //CPU has sent a read request
    begin
        cs_ <= 0; 
        ras_ <= 0; 
        cas_ <= 1; 
        we_ <= 0; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01110; //go to precharge waiting
        counterEnable <= 1;
        rst2 <= 1;
        addr <= 0;
        bs <= raddrBuffer[rbWPtr][23:22]; //highest two bits of the buffered address are bank selection
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
end

/////////////////////////////////
/////////////////////////////////
/////////////////////////////////
/*write request handling states*/
5'b01001:
begin
    if(counter<2)
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01001; //stay on precharge waiting
        counterEnable <= 1;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
    else
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01010; //go to activate
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
end
5'b01010:
begin
    cs_ <= 0; 
    ras_ <= 0; 
    cas_ <= 1; 
    we_ <= 1; 
    ldqm <= 0; 
    udqm <= 0; 
    cke <= 1;
    dqInEn <= 0;
    next_state <= 5'b01011; //go to activate waiting
    counterEnable <= 1;
    rst2 <= 1;
    addr <= waddrBuffer[wbRPtr][21:9]; //row address extracted from write address buffer
    bs <= waddrBuffer[wbRPtr][23:22]; //bank address extracted from write address buffer
    dq_out <= 0;
    inc <= 0;
    wbRPtrUpdate <= 0;
    rbWPtrUpdate <= 0;
    rbWEn <= 0;
    rtEnable <= 1;
end
5'b01011:
begin
    if(counter<2)
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01011; //stay on activate waiting
        counterEnable <= 1;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
    else
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01100; //go to write
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
end
5'b01100:
begin
    cs_ <= 0; 
    ras_ <= 1; 
    cas_ <= 0; 
    we_ <= 0; 
    ldqm <= 0; 
    udqm <= 0; 
    cke <= 1;
    dqInEn <= 0;
    next_state <= 5'b01101; //go to write waiting
    counterEnable <= 1;
    rst2 <= 1;
    addr <= {4'b0000, waddrBuffer[wbRPtr][8:0]}; //column address extracted from write address buffer
    bs <= waddrBuffer[wbRPtr][23:22]; //bank address extracted from write address buffer
    dq_out <= writeBuffer[wbRPtr];
    inc <= 0;
    wbRPtrUpdate <= 1;
    rbWPtrUpdate <= 0;
    rbWEn <= 0;
    rtEnable <= 1;
end
5'b01101:
begin
    if(counter<2)
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01101; //stay on write waiting
        counterEnable <= 1;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
    else
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01000; //go back to normal
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
end

//////////////////////////////////
//////////////////////////////////
//////////////////////////////////
/* read request handling states */
5'b01110:
begin
    if(counter<2)
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01110; //stay on precharge waiting
        counterEnable <= 1;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
    else
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01111; //go to activate
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
      rbWEn <= 0;
        rtEnable <= 1;      
    end
end
5'b01111:
begin
    cs_ <= 0; 
    ras_ <= 0; 
    cas_ <= 1; 
    we_ <= 1; 
    ldqm <= 0; 
    udqm <= 0; 
    cke <= 1;
    dqInEn <= 0;
    next_state <= 5'b10000; //go to activate waiting
    counterEnable <= 1;
    rst2 <= 1;
    addr <= raddrBuffer[rbWPtr][21:9]; //row address extracted from read address buffer
    bs <= raddrBuffer[rbWPtr][23:22]; //bank address extracted from read address buffer
    dq_out <= 0;
    inc <= 0;
    wbRPtrUpdate <= 0;
    rbWPtrUpdate <= 0;
    rbWEn <= 0;
    rtEnable <= 1;
end
5'b10000:
begin
    if(counter<2)
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b10000; //stay on activate waiting
        counterEnable <= 1;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;
    end
    else
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b10001; //go to read
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
      rbWEn <= 0;
        rtEnable <= 1;
    end
end
5'b10001:
begin
    cs_ <= 0; 
    ras_ <= 1; 
    cas_ <= 0; 
    we_ <= 1; 
    ldqm <= 0; 
    udqm <= 0; 
    cke <= 1;
    dqInEn <= 1; //must turn on dq input now
    next_state <= 5'b10010; //go to read waiting
    counterEnable <= 1;
    rst2 <= 1;
    addr <= {4'b0000, raddrBuffer[rbWPtr][8:0]}; //column address extracted from read address buffer
    bs <= raddrBuffer[rbWPtr][23:22]; //bank address extracted from write address buffer
    dq_out <= 0; //read command cycle don't care dq bus value
    inc <= 0;
    wbRPtrUpdate <= 0;
    rbWPtrUpdate <= 0;
  rbWEn <= 0;
    rtEnable <= 1;
end
5'b10010:
begin
    if(counter<2)
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 1;
        next_state <= 5'b10010; //stay on read waiting
        counterEnable <= 1;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
      rbWEn <= 0;
        rtEnable <= 1;
    end
    else
    begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 1;
        next_state <= 5'b10011; //go to finishing the read
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 1;
        rtEnable <= 1;
    end
end
5'b10011:
begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 1;
        next_state <= 5'b01000; //go back to normal
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 1;
        rbWEn <= 0;
        rtEnable <= 1;
end
default:
begin
        cs_ <= 1; 
        ras_ <= 1; 
        cas_ <= 1; 
        we_ <= 1; 
        ldqm <= 0; 
        udqm <= 0; 
        cke <= 1;
        dqInEn <= 0;
        next_state <= 5'b01000; //stay on the same state
        counterEnable <= 0;
        rst2 <= 0;
        addr <= 0;
        bs <= 0;
        dq_out <= 0;
        inc <= 0;
        wbRPtrUpdate <= 0;
        rbWPtrUpdate <= 0;
        rbWEn <= 0;
        rtEnable <= 1;  
end
endcase

end

I tried to disable the 8 auto-refreshing cycles required in the initialization part, but the SDRAM were still refreshing by itself somehow. I also tried to switch PCB boards (I have multiple), all of them showed the same problem. Here is the link to the SDRAM pdf

https://www.mouser.com/datasheet/2/949/w9825g6kh_a04-1489735.pdf?srsltid=AfmBOoqw5VIPM6f-QGWjLmPLCcZ3av1AbZLhvKU7OVS-tlvBYwZGy2x1

Upvotes: 0

Views: 61

Answers (1)

Vlad
Vlad

Reputation: 5857

You didn't explain the test you're doing, particularly the access pattern. Keep in mind that the mere fact of accessing the relevant data will keep those rows refreshed, you don't require specifically the refresh command to keep needed data alive.

To see the data degraded you must not access the rows for quite some time. I.e. write something unique to the row, precharge it, and don't access it for more than 64mS, preferably longer. Write something unique as to avoid misinterpreting aliasing addresses (in case of bugs) as different, as accessing same line frequently will keep it alive.

Another possibility which I didn't initially consider - if you only test operations on a single row, without ever precharging it - you essentially operate on a static ram of activated row's buffer, so you also will see the data living infinitely without doing refresh.

Upvotes: 1

Related Questions