06 · The 64 MB SDRAM

The DE10-Lite has 64 MB of SDRAM next to the FPGA: about 40 times all the FPGA's block memory together. This example drives it with a small controller written here, and tests every one of its 33,554,432 words.

Verified on the board 2026-10-24: all 32M words checked Quartus Prime Lite 17.1 No shield needed Skill: use-sdram
Download sdram.qar (full project)

The repo keeps the full project (.qar, plus the sources here) but no prebuilt bitstream: one build (a few minutes) makes the .sof on your computer.

Yes, it is DRAM

SDRAM = synchronous dynamic RAM: every bit is a tiny capacitor that leaks, so each row must be refreshed every 64 ms (the controller does it every 7.8 µs, one row at a time), and a word is reached in steps: open the row (ACTIVATE), read or write the column, close the row (PRECHARGE). The chip on the board is an IS42S16320: 4 banks × 8,192 rows × 1,024 columns × 16 bits.

Measured on the board

PASS 00 delay=2 64K words errors=00010000 clocks=00163944     ← read delay 2: every word wrong
PASS 01 delay=3 64K words errors=00000000 clocks=00173C60     ← 3 and up: all correct
PASS 02 delay=4 64K words errors=00000000 clocks=00183ED1
PASS 03 delay=5 64K words errors=00000000 clocks=001941A0
PASS 04 delay=6 64K words errors=00000000 clocks=001A4335
PASS 05 delay=7 64K words errors=00000000 clocks=001B46C4
PASS 06 delay=4 32M words errors=00000001 clocks=307DA7D4     ← the whole chip; the 1 error is the one SW0 injected
PASS 07 delay=4 32M words errors=00000001 clocks=307DA7D6

What you will see

WhereWhat
HEX5..HEX0Errors since start-up (with SW0 down it must stay 0)
LEDR9 / LEDR1 / LEDR0SDRAM ready / reading / writing; LEDR2 shows SW0
SW0 upInject one wrong word per pass (proves the checker); KEY0: start over
Your PCThe lines above, plus progress every 4M words

Block diagram

pll_de1050 MHz + 50 MHz 180° memory testwrite all, read all,compare, report sdram_ctrlinit, refresh 7.8 µs,ACT → READ/WRITEwith auto-prechargeread delay 4 SDRAM IS42S16320 (64 MB)DRAM_CLK ← 180° clockCS RAS CAS WE, BA[1:0]ADDR[12:0], DQ[15:0]4 banks × 8192 rows× 1024 cols × 16 bit

The controller (lib/rtl/sdram_ctrl.sv)

What it took (three real bugs, each found on the board)

  1. Requests swallowed by refresh. The test saw ready, raised req for one clock, and in that very clock a refresh became due: the controller refreshed and the request vanished; the test hung. Signal Tap showed the test waiting and the controller idle. Fix: the controller now latches every request and serves it after the refresh.
  2. Every write one address late. The test raised req and moved its address counter on in the same clock, so the controller (which takes the request a clock later) saw the next address. Reading address 1 twice proved it. Fix: a separate request-address register set together with req.
  3. A name used before its declaration became a silent 1-bit wire. Now the file starts with `default_nettype none, which turns that into a compile error.

The simulation (lib/sim/tb_sdram_ctrl.sv with the SDRAM model lib/sim/sdram_model.sv, which drives read data only in the datasheet's valid window) passed all along: it fails with read delay 3 or 5, so it checks the timing, but it could not see bugs that lived in how the test talked to the controller. The board and Signal Tap could.

Run it

python scripts/de10.py build examples/06_sdram
python scripts/de10.py program examples/06_sdram
python scripts/de10.py log 40

Build it yourself

python scripts/new_project.py examples/06_sdram sdram_top --lib pll sdram print bcd seg7
python scripts/de10.py build examples/06_sdram

Test without the board

python scripts/sim.py lib/sim/tb_sdram_ctrl.sv      → 300 words, 0 read errors, 0 protocol errors   PASS

Files

FileWhat
src/sdram_top.svThe memory test
src/extra.qsfI/O-cell registers for the SDRAM pins
lib/rtl/sdram_ctrl.svThe controller
lib/sim/sdram_model.sv, lib/sim/tb_sdram_ctrl.svSDRAM model and unit test