Seyed Masoud Hosseini · Overview · Study log · Weekly summaries · Ideas · Search · Transcript · RSS feed

FPGA & Verilog Design · Lecture 8 of 12 · 27:32

Part 8: Memory and Block RAM

Introduction to FPGA Part 8 - Memory and Block RAM | Digi-Key Electronics on YouTube

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 clocked always block 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: $readmemh inside a synthesizable initial block loads memory contents from a text file (hex or other radix) at configuration time, controlled by an init_file parameter 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

  1. Why does declaring even a 16-element memory consume an entire 4Kb block RAM resource?
  2. What causes the address bug the lecture finds in its own test bench, and how is it fixed?
  3. What is the difference between how this design's memory behaves versus true read-only memory?
  4. Why is $readmemh placed inside an initial block that is still considered synthesizable in this case?
  5. What does the device utilization chart from apio build -v tell you about a design?

Vocabulary

block RAM (noun)
Dedicated, built-in memory blocks inside an FPGA chip.
The iCE40 has sixteen block RAM units available.
distributed RAM (noun)
Small amounts of memory built from an FPGA's regular logic cells instead of dedicated memory blocks.
Distributed RAM is used when only a tiny amount of storage is needed.
dual-port memory (noun)
A memory that supports two independent accesses, such as one read and one write, at the same time.
The design uses dual-port memory with separate read and write addresses.
single-port memory (noun)
A memory that supports only one access at a time.
Single-port memory is simpler but slower for simultaneous operations.
infer (verb)
For a synthesis tool to automatically recognize and build a specific hardware structure from ordinary code.
The synthesis tool can infer block RAM from a plain array declaration.
write enable (noun)
A control signal that allows data to be written into memory when active.
Data is only written to memory when write enable is high.
device utilization (noun)
A report showing how much of an FPGA's resources a design actually uses.
The device utilization report shows one RAM block is used.
truncate (verb)
To cut off extra bits so a value fits into a smaller declared width.
Verilog silently truncates the constant to fit the four-bit address.
memory initialization (noun)
Loading starting values into memory before it is used.
Memory initialization loads values from a text file at startup.
read-only memory (ROM) (noun)
Memory whose contents can be read but not changed.
Removing the write logic turns the memory into read-only memory.
radix (noun)
The base of a number system, such as binary, decimal, or hexadecimal.
The memory file can be written using a hexadecimal radix.
FPGA (noun)
A chip whose internal circuits can be reprogrammed after manufacturing.
The FPGA has sixteen block RAM blocks available.
lookup table (LUT) (noun)
A small piece of FPGA logic that can be configured to produce any simple function.
Distributed RAM is built from lookup tables instead of dedicated blocks.
instantiate (verb)
To create a specific working copy of a hardware component in a design.
This design avoids instantiating memory with vendor-specific system functions.
vendor-specific (adjective)
Made to work only with one manufacturer's tools or products.
Plain Verilog is used here instead of vendor-specific memory functions.
contrast (verb)
To compare two things by showing how they are different.
The lecture contrasts flip-flop storage with dedicated block RAM.
at [someone's] discretion (idiom)
Decided freely by a person or system, based on their own judgment.
Whether to use distributed RAM is left to the synthesis tool's discretion.
illustrate (verb)
To make an idea clear by giving a clear example.
The address bug illustrates how Verilog truncates oversized constants.
confirm (verb)
To show that something is definitely true.
The test bench confirms every memory element loads correctly.
opt-in (adjective)
Not active unless a person deliberately chooses to turn it on.
The init_file parameter defaults to null, making initialization opt-in.
silently (adverb)
Without giving any warning or error message.
The oversized address silently wraps around to zero.
kilobit (noun)
A unit of data equal to one thousand and twenty-four bits.
Each block RAM unit holds 4 kilobits of data.
wrap around (phrasal verb)
To go past the highest value and start again from the lowest.
An address of 4'h10 wraps around to 0 on a 4-bit bus.
sequencer (noun)
A circuit that produces a fixed pattern of outputs, one step at a time.
The challenge is to build an 8-step LED sequencer using memory.
debouncer (noun)
A circuit that removes false, rapid signal changes caused by a mechanical switch.
The sequencer challenge combines memory with a button debouncer.
roughly (adverb)
Approximately, not exactly.
The iCE40HX1K has roughly 64 kilobits of total block RAM.
toggle (verb)
To switch a signal between two states, like on and off.
A write enable signal toggles high only when new data should be stored.
trade-off (noun)
A balance between two things where gaining one means giving up some of the other.
There is a trade-off between memory size and how many blocks a design uses.

Chapters

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 →