Seyed Masoud Hosseini · Overview · Study log · Ideas · Transcript · RSS feed
FPGA & Verilog Design · Lecture 8 of 12 · 27:32
Part 8: Memory and Block RAM
Study guide
What this lecture covers
This lecture answers how to store more than a few bits of data on an FPGA. It contrasts using individual flip-flops or lookup tables (distributed RAM) with the iCE40's dedicated embedded block RAM (EBR), then writes plain Verilog that lets the synthesis tool infer a small dual-port memory rather than instantiating it with vendor-specific system functions.
By the end you can declare a memory array in Verilog, write and read it through an always block, check how many block RAM resources a design uses via the device utilization report, test the memory with a test bench and GTKWave, and initialize memory contents from a text file at configuration time - including building simple read-only memory. The episode sets up a challenge to build an 8-step LED sequencer using memory, a clock divider, and a button debouncer together.
Key ideas
- Distributed RAM: small amounts of storage can be built from lookup tables when block RAM isn't needed, at the synthesis tool's discretion.
- Embedded block RAM (EBR): dedicated on-chip memory blocks (4Kb each on the iCE40HX1K, 16 blocks total) configurable as single-port, dual-port, or FIFO memory with different width/depth combinations.
- Minimum block allocation: declaring even a tiny memory (like 16 x 8 bits) still consumes one entire 4Kb block; you can't share the rest of that block for something else.
- Inferred memory: writing a registered array (
reg [7:0] mem [0:15]) and reading/writing it inside a clockedalwaysblock lets the synthesis tool recognize and map it onto block RAM automatically. - Read-before-write: on the iCE40, reading and writing the same address in the same clock cycle returns the old value before the new one is stored; other FPGAs may behave differently.
- Memory initialization file:
$readmemhinside a synthesizableinitialblock loads memory contents from a text file (hex or other radix) at configuration time, controlled by aninit_fileparameter that defaults to null. - Read-only memory: removing the write enable, write address, and write data logic from the same design turns it into ROM, useful for things like fixed instruction memory for a processor.
Walkthrough
Block RAM basics (0:32)
The lecture explains why flip-flops don't scale for storage, introduces embedded block RAM as a separate FPGA resource with configurable width and depth, and notes that the iCE40HX1K on the iCEstick has 16 blocks totaling 64 kilobits.
Designing the memory module (5:52)
A small 16-element, 8-bit-wide dual-port memory is defined with reg [7:0] mem [0:15]. A single always block, clocked on the positive edge, writes w_data to mem[w_addr] when w_en is high and copies mem[r_addr] to r_data when r_en is high, using one shared clock for both operations.
Checking device utilization (9:40)
Using a dummy .pcf file (so synthesis and place-and-route can run without uploading to hardware), apio build -v prints a device utilization chart showing logic cells, RAM blocks, I/O pins, and other resources used - confirming that even this small memory design consumes one full RAM block out of sixteen available.
Writing a test bench for the memory (12:07)
The test bench instantiates the memory module as the unit under test, generates a simulated ~12 MHz clock, and runs a sequence of reads and writes. A bug surfaces where an address of 4'h10 silently wraps to 0 because the address bus is only 4 bits wide - illustrating that Verilog truncates a constant to fit a signal's declared width.
Initializing memory from a file (19:29)
Rather than looping to set values in hardware, the lecture creates a plain text file of hex values and adds a synthesizable initial block using $readmemh, controlled by a parameter that defaults to null so initialization is opt-in. The test bench confirms every memory element loads the expected value at startup.
Read-only memory (24:27)
By removing the write enable, write address, and write data signals from the same module, the lecture turns it into read-only memory whose contents come entirely from the initialization file - framed as a way to store fixed instructions for a soft processor.
Before you watch
- Complete Part 7 ("Verilog Testbenches and Simulation"), since this lecture builds its memory test bench the same way.
- Having modules for a clock divider and button debouncer ready (from Parts 4-6) will help with this episode's LED-sequencer challenge.
Check your understanding
- Why does declaring even a 16-element memory consume an entire 4Kb block RAM resource?
- What causes the address bug the lecture finds in its own test bench, and how is it fixed?
- What is the difference between how this design's memory behaves versus true read-only memory?
- Why is
$readmemhplaced inside aninitialblock that is still considered synthesizable in this case? - What does the device utilization chart from
apio build -vtell you about a design?
Chapters
- 0:00 <Untitled Chapter 1>
- 0:32 Block Ram in Verilog
- 1:22 Embedded Block Rams
- 3:11 Embedded Block Ram
- 3:45 Example Code for Creating Single and Dual Port Memory Configurations
- 4:33 Diagram of the Block Memory
- 5:52 Creating Verilog
- 6:50 Declare the Memory
- 8:37 Dummy Physical Constraint
- 9:40 Device Utilization Chart
- 12:07 Storage Elements
- 19:29 Initial Values
- 20:51 Initial Block
- 24:27 Read Only Memory
- 27:06 Phase Locked Loop
From the YouTube description
A field-programmable gate array (FPGA) is an integrated circuit (IC) that lets you implement custom digital circuits. You can use an FPGA to create optimized digital logic for things like digital signal processing (DSP), machine learning, and cryptocurrency mining. Because of the FPGA’s flexibility, you can often implement entire processors using its digital logic. You can find FPGAs in consumer electronics, satellites, and in servers used to perform specialized calculations.
In this series, we will see how an FPGA works and demonstrate how to create custom digital logic using the Verilog hardware description language (HDL).
Previously, we demonstrated how to create a Verilog testbench and simulate a design using Icarus Verilog (https://youtu.be/ykBi2H2NGyA). In this episode, we look at using block RAM to store data.
The solution to the challenge at the end of the episode can be found here: https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-8-memory-and-block-ram/df7bcadef0de430ab89d0d9c21e3a14c
All code examples and solutions for this series can be found here: https://github.com/ShawnHymel/introduction-to-fpga
Often you will need to store data in your digital design. This could be samples from a sensor, instructions for a CPU, or output from complex mathematical calculations. One obvious place to store data is in the D flip-flops in the logic cells. However, as each flip-flop can store only 1 bit, you would quickly waste most of your cells trying to store more than a few bytes.
One solution is to store data in the look-up tables (LUTs). This is known as “distributed RAM.” If you don’t need to store much data, your synthesis tool might allocate LUTs as distributed RAM. However, if you need to store more than a few bytes, you might be better off using block RAM.
A block RAM is a contiguous piece of memory that exists alongside the reprogrammable fabric. In most cases, block RAM is reconfigurable to a number of widths and depths. We can use system functions (denoted with a ‘$’) to allocate block RAM, but these commands are often unique to a particular synthesis tool.
Instead, we will write pure Verilog code that describes the behavior of the RAM. The synthesis tool will infer that we want to use block RAM and allocate it for us.
Additionally, we can write a text file with initial RAM values if we wish to have the synthesis tool load those values into RAM during the FPGA configuration process.
Your challenge is to create a simple 2-bit sequencer that records and plays up to 8 steps. You should use two buttons to enter a pattern and another button to record that pattern to memory. Meanwhile, the FPGA should be looping through the memory elements (e.g. 8 memory elements) and displaying the stored patterns on 2 of the LEDs.
Product Links:
https://www.digikey.com/en/products/detail/lattice-semiconductor-corporation/ICE40HX1K-STICK-EVN/4289604
Related Videos:
https://www.youtube.com/watch?v=z8Oldd-nrfs
https://www.youtube.com/watch?v=5kNXX67mchE
https://www.youtube.com/watch?v=iwcxLQ6AB88
Related Project Links:
https://www.digikey.com/en/maker/projects/introduction-to-fpga-part-8-memory-and-block-ram/df7bcadef0de430ab89d0d9c21e3a14c
Related Articles:
https://www.digikey.com/en/pdf/r/renesas-electronics-america/powering-fpga-applications
https://www.digikey.com/en/videos/d/dsp/edge-machine-deep-learning-on-fpga
Learn more:
Maker.io - https://www.digikey.com/en/maker
Digi-Key’s Blog – TheCircuit https://www.digikey.com/en/blog
Connect with Digi-Key on Facebook https://www.facebook.com/digikey.electronics/
And follow us on Twitter https://twitter.com/digikey
← Part 7: Verilog Testbenches and Simulation · Part 9: Phase-Locked Loop (PLL) and Glitches →
