Hardware Description Languages
Hardware Description Languages (HDLs) describe digital circuits in text form, enabling simulation, synthesis, and verification of complex designs.
HDL Concepts
Hardware vs Software Mindset
| Software | Hardware (HDL) | |---|---| | Sequential execution | Concurrent execution (everything runs simultaneously) | | Variables hold values | Wires carry signals continuously | | Functions compute results | Modules describe circuits | | Memory is implicit | Every register must be explicit | | Timing is abstracted | Timing is fundamental |
Key Difference: Concurrency
In hardware, all signals update simultaneously. An HDL assignment A = B & C doesn't "execute" — it describes a permanent wire connection from an AND gate to signal A.
Levels of Abstraction
| Level | Description | Example | |---|---|---| | Behavioral | Algorithm, control flow | "Sort these numbers" | | RTL (Register Transfer Level) | Registers + combinational logic | "On clock edge, store A + B" | | Gate level | Logic gates and connections | "NAND2(a, b) → wire1" | | Transistor level | Individual transistors | SPICE netlists |
RTL is the sweet spot for design — high enough for productivity, low enough for synthesis tools to produce efficient circuits.
RTL Design
Combinational Logic in RTL
Described with continuous assignments or always blocks sensitive to all inputs:
// Continuous assignment (Verilog-like pseudocode)
assign y = a & b | c;
// Always block (combinational)
always @(*) begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
2'b11: y = d;
endcase
end
Rule: Combinational blocks must assign outputs for every possible input combination (no latches inferred).
Sequential Logic in RTL
Described with clocked always blocks:
// D flip-flop with synchronous reset
always @(posedge clk) begin
if (reset)
q <= 0;
else
q <= d;
end
The <= (non-blocking assignment) is used for sequential logic. = (blocking) for combinational.
Common RTL Patterns
Counter:
always @(posedge clk) begin
if (reset)
count <= 0;
else if (enable)
count <= count + 1;
end
Shift register:
always @(posedge clk) begin
shift_reg <= {shift_reg[N-2:0], serial_in};
end
FSM:
// State register
always @(posedge clk) begin
if (reset) state <= IDLE;
else state <= next_state;
end
// Next-state logic (combinational)
always @(*) begin
case (state)
IDLE: next_state = start ? RUNNING : IDLE;
RUNNING: next_state = done ? COMPLETE : RUNNING;
COMPLETE: next_state = IDLE;
default: next_state = IDLE;
endcase
end
// Output logic (combinational)
assign busy = (state == RUNNING);
Simulation
Event-Driven Simulation
The simulator processes events (signal changes) and propagates their effects:
- Initialize all signals.
- Process events in time order.
- When a signal changes, schedule dependent events (after gate delays).
- Advance simulation time to next event.
- Repeat.
Zero-delay simulation: All combinational logic evaluates in zero time. Useful for functional verification.
Timing simulation: Includes gate delays. Checks for timing violations.
Testbenches
A testbench is a non-synthesizable HDL module that:
- Instantiates the design under test (DUT)
- Generates stimulus (clock, reset, input patterns)
- Checks outputs against expected values
- Reports pass/fail
// Testbench pseudocode
module tb;
reg clk = 0;
always #5 clk = ~clk; // 10-unit period clock
reg [7:0] a, b;
wire [8:0] sum;
adder DUT(.a(a), .b(b), .sum(sum));
initial begin
a = 8'd10; b = 8'd20;
#10;
assert(sum == 9'd30) else $error("Test failed!");
$finish;
end
endmodule
Waveform Viewing
Simulation results are visualized as waveforms — signal values over time. Tools: GTKWave, ModelSim, Vivado simulator.
Synthesis
Synthesis converts RTL to a gate-level netlist:
RTL code → Synthesis tool → Gate netlist → Place & Route → Physical layout
What is Synthesizable
| Synthesizable | Not Synthesizable | |---|---| | Combinational logic (assign, always @(*)) | Delays (#10) | | Sequential logic (always @(posedge clk)) | File I/O (display, $finish) | | Parameters, generate | Infinite loops |
Synthesis Constraints
- Clock period: Target frequency
- I/O timing: Input/output delays relative to clock
- Area: Maximum gate count or utilization
- Power: Maximum power budget
The synthesis tool optimizes the design to meet these constraints.
Timing Constraints
create_clock -period 10 [get_ports clk] // 100 MHz clock
set_input_delay -clock clk 2 [get_ports data_in]
set_output_delay -clock clk 1 [get_ports data_out]
The tool reports timing slack = (required time) - (actual time). Positive slack: timing met. Negative slack: timing violation.
Modern HDL Landscape
Verilog / SystemVerilog
- Most widely used in industry (especially US, Asia)
- SystemVerilog adds verification features (classes, assertions, coverage)
- UVM (Universal Verification Methodology) for testbench infrastructure
VHDL
- Strongly typed, verbose
- Popular in Europe, defense, aerospace
- VHDL-2008 added modern features
Chisel (Scala-based)
- Generates Verilog from Scala
- Used by RISC-V projects (SiFive, BOOM)
- Higher-level abstractions (parameterized generators)
SpinalHDL (Scala-based)
- Similar to Chisel but different philosophy
- Comprehensive type system, strong compile-time checks
Amaranth (Python-based)
- Python library for digital design
- Generates Verilog or directly targets FPGAs
Clash (Haskell-based)
- Functional HDL
- Strong type system catches many design errors
Rust-Based Hardware Simulation
While Rust is not an HDL, it excels at hardware simulation and verification:
// Simple D flip-flop simulation
STRUCTURE DFlipFlop
q : boolean
FUNCTION NEW_DFLIPFLOP()
ff ← new DFlipFlop
ff.q ← false
RETURN ff
PROCEDURE CLOCK_EDGE(ff, d)
ff.q ← d
FUNCTION OUTPUT(ff)
RETURN ff.q
// 4-bit counter simulation
STRUCTURE Counter
value : integer
max : integer
FUNCTION NEW_COUNTER(max)
ctr ← new Counter
ctr.value ← 0
ctr.max ← max
RETURN ctr
PROCEDURE TICK(ctr, enable, reset)
IF reset THEN
ctr.value ← 0
ELSE IF enable THEN
IF ctr.value ≥ ctr.max THEN
ctr.value ← 0
ELSE
ctr.value ← ctr.value + 1
Rust is used for:
- Cycle-accurate simulators: Verilator generates C++/Rust from Verilog
- Formal verification tools: SAT/SMT solvers in Rust
- Hardware test frameworks: cocotb (Python) and Rust alternatives
- FPGA toolchains: Parts of Yosys, nextpnr ecosystem
Verification
Simulation-Based
- Directed tests: Manual test cases for specific scenarios
- Random/constrained random: Generate random but legal inputs
- Coverage-driven: Track which states/transitions have been tested
Formal Verification
- Model checking: Exhaustively verify properties for all possible inputs
- Equivalence checking: Prove two designs implement the same function
- Property checking: Verify assertions hold for all reachable states
Assertion-Based Verification
// SystemVerilog assertion: FIFO should never be read when empty
assert property (@(posedge clk) (read_en |-> !empty));
// Cover: Make sure we actually test the full condition
cover property (@(posedge clk) (count == MAX_COUNT));
Applications in CS
- Processor design: x86, ARM, RISC-V processors designed in HDLs. Millions of lines of RTL.
- ASIC design: Custom chips for AI (TPU), networking, automotive. HDL → synthesis → fabrication.
- FPGA development: HDL is the primary design entry for FPGAs. IP cores reuse HDL modules.
- Hardware-software co-design: Simulate hardware + firmware together. Virtual platforms.
- Education: Understanding HDLs deepens understanding of computer architecture.
- Open-source hardware: RISC-V cores (Rocket, BOOM), OpenTitan, LibreCores — all in HDLs.