Reputation: 1
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
Upvotes: 0
Views: 61
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