Lab 3 — Combinational Logic: Decoder on the Tang Nano 9K
Goal
Implement a 2-to-4 decoder in SystemVerilog, verify it exhaustively with a Verilator testbench, and deploy it to the Tang Nano 9K so that two buttons control which of four LEDs lights up. By the end you will have a concrete feel for the write–simulate–synthesize–deploy loop on real combinational hardware.
What you need
- Lab 2 completed (OSS CAD Suite installed, board detected, blink working)
- The Tang Nano 9K connected via USB
What is a decoder?
A decoder takes N binary inputs and activates exactly one of 2^N outputs. The 2-to-4 case maps a 2-bit select signal to a 4-bit one-hot output:
sel[1:0] |
y[3:0] |
|---|---|
2'b00 |
4'b0001 |
2'b01 |
4'b0010 |
2'b10 |
4'b0100 |
2'b11 |
4'b1000 |
Decoders are everywhere in digital design: address decoders in memory maps, instruction decoders in CPUs, and one-hot FSM output encoders. You will use exactly this pattern in Project 2 when your bus needs to route an address to the right peripheral.
Step 1 — Write the module
Activity 1
Create decoder2to4.sv with the following content:
Before simulating, answer: what are the values of y when sel = 2'b10?
Variable part-select
The single-line body y[sel] = 1'b1 is a variable part-select — indexing a vector with another signal rather than a constant. Yosys handles this correctly for small vectors like this one. The always_comb block ensures the tool can verify there are no unintended latches.
Step 2 — Simulate with Verilator
Before touching the board, prove the design is correct in simulation. Simulation is free — it catches bugs in seconds rather than after a slow synthesis run.
Activity 2
Write a C++ testbench tb_decoder.cpp that:
- Instantiates
Vdecoder2to4 - Loops
selthrough all 4 values (0–3) - After each, asserts that
y == (1 << sel)— exactly one bit set, and the correct one - Prints
PASSorFAILfor each case
Compile and run:
Expected output:
Hint — assert in C++
Step 3 — Add a top-level and deploy to the board
The decoder module is generic. To deploy it you need a top-level that connects it to the physical pins of the Tang Nano 9K.
Activity 3
Create top.sv that connects the board's 2 buttons to sel and 4 LEDs to y. Note: LEDs are active-low on the Tang Nano 9K — a 0 turns the LED on and a 1 turns it off — so invert the decoder output before driving the LED pins.
Synthesize, place-and-route, and program the board:
Verify on the board: pressing each button combination should light exactly one LED. Try all four combinations and confirm the one-hot behavior.
Check your .cst file
The pin constraint file (tangnano9k.cst) must assign btn[0], btn[1], and led[0]–led[5] to the correct physical pins for your board revision. If you reuse the constraint file from Lab 2's blink, verify that the button pins are included.
Step 4 — Extend it (challenge)
Activity 4
Change the design to a 3-to-8 decoder: sel[2:0] selecting among y[7:0]. If you only have 2 physical buttons, drive the third select bit from a slow clock divider so it toggles automatically, letting you observe all 8 outputs over time.
Update the testbench to cover all 8 cases (loop from 0 to 7).
On the board, you only have 6 LEDs — tie the top 2 outputs to unused signals or leave them unconnected. Observe that exactly one of the 6 visible LEDs lights at a time for the lower 6 states.
Hint
The module change is minimal: widen the ports to logic [2:0] sel and logic [7:0] y — the body y[sel] = 1'b1 stays identical. The testbench loop upper bound changes from 3 to 7. For the board, you only observe led[5:0] driven by ~y[5:0]; connect y[7:6] to open signals or leave them unwired in the top-level.
Step 5 — Think about it
Activity 5
A decoder is often the first stage of address decoding for memory-mapped peripherals. In Project 2, your bus will use a decoder to route a memory address to the right peripheral register.
Sketch (on paper or in a text file) a 2-to-4 decoder used as an address decoder: inputs are addr[1:0], outputs are chip-select signals cs_uart, cs_timer, cs_gpio, cs_cmac. Which address maps to which peripheral?
There is no single correct answer — this is your first sketch of the Project 2 memory map. Think about which peripheral is accessed most often and whether address ordering matters for the software layer.
Checklist
-
decoder2to4.svimplemented and simulates cleanly with Verilator (all 4 cases pass). - Design deployed to the Tang Nano 9K — button presses light exactly one LED.
- 3-to-8 decoder variant working in simulation.
- Address-decoder sketch done.
Summary
You implemented a decoder in SystemVerilog, verified it with an exhaustive testbench, and deployed it to real hardware. The y[sel] = 1'b1 idiom (variable part-select) is a compact way to encode one-hot outputs — you will see the same pattern in the FSM output logic in Lab 4, and in the bus address decoder in Project 2.