Seyed Masoud Hosseini · Overview · Study log · Ideas · Transcript · RSS feed

FPGA & Verilog Design · Lecture 7 of 12 · 27:03

Part 7: Verilog Testbenches and Simulation

Introduction to FPGA Part 7 - Verilog Testbenches and Simulation | Digi-Key Electronics on YouTube

Study guide

What this lecture covers

This lecture answers how to verify a Verilog design without uploading it to real hardware every time. It uses the clock divider module from Part 6 as a running example, building a test bench that drives its inputs, simulating it with Icarus Verilog, and inspecting the results as waveforms in GTKWave.

By the end you can write a test bench with an internal clock generator and a reset pulse, dump signal changes to a .vcd file, and read a waveform viewer closely enough to catch a genuine off-by-one bug in the clock divider's max-count parameter - which the lecture deliberately left unfixed in Part 5 to demonstrate here. It ends with a challenge to simulate the button-debounce design from Part 5.

Key ideas

  • Unit under test (UUT): the module being verified, instantiated inside the test bench just like it would be in a top-level design, but with no physical pins involved.
  • Test bench code doesn't need to be synthesizable: constructs like delays (#), initial blocks, and system tasks ($dumpfile, $finish) are valid for simulation but have no hardware equivalent.
  • ` timescale ``: a compiler directive setting the time unit and precision used for delays in the test bench, analogous to setting the time-axis resolution on an oscilloscope.
  • Simulated clock generation: since there's no physical oscillator in simulation, the test bench toggles a clock register in a loop using delays to approximate the target frequency.
  • Value change dump (.vcd): $dumpfile and $dumpvars record how signals change over the simulation run, viewable afterward in GTKWave.
  • apio's _tb convention: apio sim looks for a file ending in _tb.v to identify the test bench and runs Icarus Verilog and GTKWave automatically.
  • Finding the bug: measuring the divided clock's period in GTKWave showed it was longer than intended, revealing that the max-count parameter needed a - 1 because the counter starts at zero.

Walkthrough

Why simulate before using real hardware (1:12)

The lecture explains that synthesizing and uploading a design takes time, and that observing real hardware at megahertz speeds requires equipment like an oscilloscope or logic analyzer. Simulating a module with a test bench and viewing the output in a waveform viewer is presented as a faster, cheaper way to catch bugs.

Structuring the test bench module (6:55)

The test bench for the clock divider module has no ports, since everything is internal. It declares registers for clock and reset (so they can be driven directly) and a wire for out, and sets initial values for the clock and reset registers - something only valid in simulation.

Generating a clock and pulsing reset (8:33)

An always block with no sensitivity list toggles the clock register every ~41.67 ns to approximate a 12 MHz signal, using the # delay syntax. A separate initial block pulses the reset line high briefly near the start of the simulation, running concurrently with the clock-generation block.

Instantiating the unit under test with scaled-down parameters (11:12)

The clock divider is instantiated as uut, overriding its count_width and max_count parameters to much smaller values than real hardware would use, so the simulation only needs to run a manageable number of clock cycles instead of waiting for a full-scale divide ratio.

Dumping signals to a VCD file (14:15)

$dumpfile names the output .vcd file, and $dumpvars(0, ...) records every signal in the test bench and all instantiated modules beneath it. A fixed simulation duration and $finish ensure the simulation stops instead of running indefinitely.

Debugging with GTKWave (19:59)

After running apio sim, the lecture loads the clock, counter, reset, and divided-output signals into GTKWave, uses markers to measure the divided clock's period, and finds it's longer than the intended 500 ns. Because the counter starts at zero, the max-count parameter needs to be one less than expected; adding - 1 and re-simulating confirms the fix.

Before you watch

  • Complete Part 6 ("Verilog Modules and Parameters"), since this lecture tests the same clock divider module built there.
  • Recall the finite state machine from Part 5; its clock-divider timing bug is the one diagnosed in this lecture.

Check your understanding

  1. Why can test benches use constructs like delays and initial blocks that wouldn't be valid in synthesizable code?
  2. How does the test bench generate an approximate 12 MHz clock without real hardware?
  3. What does $dumpvars(0, ...) do, and how would using 1 instead of 0 change what gets recorded?
  4. Why does apio require a test bench file to be named with a _tb suffix?
  5. What was the actual bug found in the clock divider, and why did the counter starting at zero cause it?

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 showed how to create modules in Verilog and use parameters to change the functionality of instantiated modules (https://youtu.be/0BKyiY8R5NU). We’ll build on those concepts in this video, where we demonstrate how to create a testbench in Verilog, simulate the design with Icarus Verilog (iverilog), and view the output waveform with GTKWave.

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-7-verilog-testbenches-and-simulation/1b741d1b8b864afeacbe28075b1427cd

All code examples and solutions for this series can be found here: https://github.com/ShawnHymel/introduction-to-fpga

Uploading your design to a real FPGA can sometimes take a while (especially for larger designs and denser FPGAs). Additionally, to check the timing and operation for fast-changing signals (think RAM bus or USB signals), you would need other specialized test equipment, such as a logic analyzer. Connecting all of the FPGA pins to a logic analyzer can also be a time-consuming processor.

To save us time, we can write Verilog code that tests our design (known as a “testbench”). We use a special simulation program (Icarus Verilog, in our case) to run the testbench code. The testbench code should instantiate the module(s) under test (often called a “unit under test” or “uut”) and toggle the necessary input lines.

The simulation will run our testbench, and it will log all how and when the various signals/wires change in the design. It will store this log in a “value change dumpfile” (.vcd). We can use a waveform viewer, such as GTKWave, to visualize these changes. The waveforms should look similar to what you might find on a logic analyzer.

Your challenge is to create a Verilog testbench for your button debouncing code from episode 5. You are welcome to use my solution to that challenge as a starting point (https://github.com/ShawnHymel/introduction-to-fpga/tree/main/05-finite-state-machines/solution-button-debouncing). Note that you might need to make some changes to the original code to allow for passing parameters to the design.

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-7-verilog-testbenches-and-simulation/1b741d1b8b864afeacbe28075b1427cd

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 6: Verilog Modules and Parameters · Part 8: Memory and Block RAM →