Home ] Contents ] Search ]

DRAM Controller Design Tutorial



Build Your Own IP

If you want to design a moderately complex piece of IP, there are not too many good sources for hardware design methodology, so for my next design I have decided to document the process.

List of tasks:

  • Select IP
  • Requirements
  • Interfaces
  • Architecture
  • Coding

Select IP

I have decided to design an SDRAM controller. This seems to be a very popular design. There are a lot of engineers who have designed their own SDRAM controller to cater for their own peculiar needs, but at the same time there are not too many options on the Opencores web site. So let's see if we can create a flexible design capable of satisfying multiple requirements.


First off let's aim for a design that will operate at a high clock speed. Let's aim for 167MHz in an Altera Cyclone II. Possible? Maybe, maybe not, but we'll make that the target in order to emphasize high speed design concepts. Similarly the controller should have the capability to run at close to 100% of theoretical bandwidth, when data accesses are sequential.

List of requirements:

  • Wishbone bus interface.
  • Fifo interface.
  • 167MHz core, memory and wishbone clock.
  • Single Date Rate (SDR).
  • 32-bit data bus.
  • Peak data throughput of 600 Mbyte/s (90% of 167MHz*4bytes).
  • More than one access port. Example one port for processor access, one for streaming hardware access.


First off we need to be familiar with the application. We need to know how SDRAM works and the operation of the Wishbone interface. Once we fully understand the interfaces of the IP core, then we can start thinking about how we can best service these interfaces whilst fulfilling our list of requirements. The best places to start are the Wishbone specification, and the Micron SDRAM datasheet. Also see the Wikipedia SDRAM introduction.

SDRAM interface

Pin name Dir Description
clk output SDRAM clock input. SDRAM samples all signals at the positive edge of clk
cke output Clock enable. 1 = clock enabled.
cs_n output Chip select. 0 = chip selected.
we_n output Write enable. Command input. See command table
cas_n output Column address strobe. Command input. See command table
ras_n output Row address strobe. Command input. See command table
dqm[3:0] output Data mask. Input mask signal during writes, and output enable during reads. Each dqm bit is associated with 8 dq bits.
ba[1:0] output Bank address. Specify the memory bank to be accessed. See command table
a[11:0] output Address. Specifies row address during active command, column address during read/write command (A10 is precharge control), and command during load command. See command table
dq[31:0] inout Data.

SDRAM Commands

Command cs_n ras_n cas_n we_n ba a10 a
Command inhibit (NOP) H x x x x x x
No operation (NOP) L H H H x x x
Active. Select a bank and activate row within the bank. Previously activated rows must be closed before issuing this command. L L H H bank row
Read. Select column within bank's currently active row, and start read burst L H L H bank L col
Read with auto precharge. Select column within bank's currently active row, and start read burst. Precharge (close row) when done L H L H bank H col
Write. Select column within bank's currently active row, and start write burst. L H L L bank L col
Write with auto precharge. Select column within bank's currently active row, and start write burst. Precharge (close row) when done. L H L L bank H col
Precharge. Close bank's currently active row. L L H L bank L x
Precharge all. Close currently active rows in all banks. L L H L x H x
Auto refresh. Increment internal counter, and refresh one row in each bank. All banks must be idle (ie no open rows). L L L H x x x
Load mode register. All banks must be idle. L L L L 00 mode


Wishbone interface

Pin name Dir Description
clk_i input Clock.
dat_i[31:0] input Data in.
dat_o[31:0] output Data out.
adr_i[31:0] input Address
sel_i[3:0] input Data select. Data mask during writes, and data enable during reads.
we_i input Write enable
cyc_i input Cycle active. Asserted during block accesses.
stb_i input Strobe select. Slave only responds when this signal is asserted
ack_o output Acknowledge. Asserted at the end of a normal bus access.

Fifo interface

Pin name Dir Description
clk_i input Clock.
dat_i[31:0] input Data in.
dat_o[31:0] output Data out.
we_i input Write enable
re_i input Read enable
writeFifoAlmostFull output Write Fifo is almost full
readFifoEmpty input Read Fifo empty



Initially we will concentrate on the fifo interface, and the interface to the DRAM, and making the logic within the DRAM clock domain as simple as possible. Once we have the fifo interface designed the Wishbone interface should be somewhat similar. At this stage I have to admit that the DRAM controller part of the design is much more specialized than I originally thought would be the case. And this is probably why so many people design their own DRAM controllers to suit their own specialized application. For example, if you need a DRAM controller that is capable of close to 100% of theoretical maximum throughput for a streaming video application, then a generic DRAM controller probably won't meet your requirements.

The page burst mode seems like a good candidate for high speed streaming access. It should be possible to construct the control/data sequence in fifo/memory and then stream directly from fifo/memory to/from the DRAM. For a write sequence, load the data into a fifo, and load the control sequence into dual port memory, then stream everything out to the DRAM. For a read sequence only the control sequence needs to be loaded and then streamed to the DRAM, the read data can be read back to a fifo. The aim is to reduce the amount of logic in the DRAM clock domain portion of the controller, thus increasing the maximum clock speed.  Fig (1) shows a block diagram of the DRAM controller.

We have two fifos for data, plus one dual port memory buffer for address/control. The address buffers are mostly full of NOPs which are written once at start up, so only a limited number of write operations are required prior to a read/write burst sequence:

  1. Write bank row Active.
  2. Write bank column Read/Write
  3. Write NOP to old precharge location.
  4. Write new precharge.

The control section is split into two portions. The more complex within the input clock domain, and the simpler portion within the DRAM clock domain.

Fig (2) shows the DRAM arbiter. This handles access requests from multiple DRAM controllers.

The description and block diagrams shown above are a good high level view, and sufficient to determine the high level modules within the design, but we need more details on the interface signals between the blocks. I don't want to spend the time producing detailed block diagrams using Open Office Draw, and so will complete this stage of the design on paper.




Send mail to with questions or comments about this web site.
Last modified: 07/12/11