Session 5 - RTL Design & Verification¶
Summary¶
RTL = Registers + Combinational Logic + Clock Use wire + assign for combinational Use reg + always @(posedge clk) for sequential FSMs organize sequential behavior Testbenches verify your design before synthesis
Homework¶
-
Write Verilog for your project’s core module (aim for 10-30 lines to start) Integrate with any provided library modules (e.g., debounce, UART, PWM) — create a top-level wrapper
-
Simulate with a testbench and examine waveforms in GTKWave Run linter (verilator –lint-only) and fix any warnings
Class memo¶
What is a RTL level?¶
We have touched to transistor level and gate level so far. In this class we learned about RTL level design.
Verilog basic¶
In Verilog, number is shown as bellow. - 8’b10101010 // 8-bit binary: 170 - 8’hAA // 8-bit hex: 170 - 8’d170 // 8-bit decimal: 170 - 8’o252 // 8-bit octal: 170
- 4’b1010 // 4-bit value: 10
- 4’bxxxx // 4-bit unknown
- 4’bzzzz // 4-bit high-impedance
Difference of Assign and Always¶
- “assign” seems connection
assign out = in1 & in2;
- “always” seems proceedure. It work following to the CLK.
always @(posedge clk) begin
out <= in;
end
Assignment 1¶
Write Verilog for your project’s core module (aim for 10-30 lines to start)¶
I tried to design a code that generate 261.63Hz square wave
- Following to web search, I set 10MHz for system CLK.
- Then 10,000,000Hz / 261.63 Hz → 38,222 count

- I asked Gemini to …
- “Design Verilog program that generate 261.63Hz square wave from 10MHz clock wave.”
- Then I got the code bellow.
note_c_generator.v¶
module note_c_generator (
input wire clk, // System clock input 10MHz
input wire rst_n, // Active-low asynchronous reset
input wire enable, // Enable signal (High to output sound)
output reg sq_wave // Square wave output (Note C)
);
localparam CLK_COUNT_MAX = 19111;
// 15-bit counter to reach 19111
reg [14:0] counter;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// Asynchronous reset to initial state
counter <= 15'd0;
sq_wave <= 1'b0;
end else if (enable) begin
if (counter >= CLK_COUNT_MAX - 1) begin
// Reset counter and toggle the output state
counter <= 15'd0;
sq_wave <= ~sq_wave;
end else begin
// Increment counter each clock cycle
counter <= counter + 1'b1;
end
end else begin
// Reset state when enable is low (Silence)
counter <= 15'd0;
sq_wave <= 1'b0;
end
end
endmodule
Assignment 2¶
Integrate with any provided library modules (e.g., debounce, UART, PWM) — create a top-level wrapper¶
I updated my system diagram.
- I asked Gemini to …
- “Design Verilog program that integrate button input and speaker output.”
Then I got 2 following code.
1. Top-level Wrapper (top.v)¶
module top (
input wire clk, // 10MHz System Clock
input wire rst_n, // Reset
input wire btn_in, // Physical Button input
output wire spk_out // To Speaker/Buzzer
);
wire clean_enable;
// 1. Instantiate Debounce module
// To filter out mechanical noise from the button
debounce u_debounce (
.clk(clk),
.rst_n(rst_n),
.noisy_in(btn_in),
.clean_out(clean_enable)
);
// 2. Instantiate your Note C Generator
// Generates ~261.63Hz when clean_enable is High
note_c_generator u_note_c (
.clk(clk),
.rst_n(rst_n),
.enable(clean_enable),
.sq_wave(spk_out)
);
endmodule
2. Simple Debounce Module (debounce.v)¶
module debounce (
input wire clk,
input wire rst_n,
input wire noisy_in,
output reg clean_out
);
// 10MHz clock, 1ms delay = 10,000 cycles
reg [13:0] count;
reg sync_0, sync_1;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sync_0 <= 1'b0;
sync_1 <= 1'b0;
count <= 14'd0;
clean_out <= 1'b0;
end else begin
sync_0 <= noisy_in; // Synchronize to clk domain
sync_1 <= sync_0;
if (sync_1 == clean_out) begin
count <= 14'd0;
end else if (count >= 14'd10000) begin
clean_out <= sync_1;
count <= 14'd0;
end else begin
count <= count + 1'b1;
end
end
end
endmodule
Assignment 3¶
Simulate with a testbench and examine waveforms in GTKWave¶
- I asked Gemini to …
- “Make test bench file”
Then I got following code.
Testbench code (top_tb.v)¶
timescale 1ns / 1ps
module top_tb();
// Inputs
reg clk;
reg rst_n;
reg btn_in;
// Output
wire spk_out;
// Instantiate the Top-level module
top uut (
.clk(clk),
.rst_n(rst_n),
.btn_in(btn_in),
.spk_out(spk_out)
);
// Clock generation: 10MHz (Period = 100ns)
always #50 clk = ~clk;
initial begin
// --- Step 1: Initialize signals ---
clk = 0;
rst_n = 0;
btn_in = 0;
// --- Step 2: Release Reset ---
#200 rst_n = 1;
$display("Reset released at %t", $time);
// --- Step 3: Simulate Button Chattering ---
// Debounce logic waits for 1ms stability.
#1000;
btn_in = 1; #50000; // High for 50us
btn_in = 0; #50000; // Low for 50us
btn_in = 1; #50000; // High for 50us
$display("Button chattering finished at %t", $time);
// --- Step 4: Hold Button (Stable) ---
// Now keep it High for more than 1ms to trigger the sound
btn_in = 1;
$display("Button held stable from %t", $time);
// Wait for sound to start and observe a few cycles
// 1ms (debounce) + 10ms (approx 2.5 cycles of Note C)
#11000000;
// --- Step 5: Release Button ---
btn_in = 0;
$display("Button released at %t", $time);
#2000000; // Observe silence
$display("Simulation finished.");
$finish;
end
// Create a waveform file for GTKWave
initial begin
$dumpfile("top_tb.vcd");
$dumpvars(0, top_tb);
end
endmodule
I prepared following files.

Compile¶
Then I compiled them with following command.
iverilog -o sim.out top.v debounce.v note_c_generator.v top_tb.v

Run simulation¶
vvp sim.out
vvp is a runtime engine for Icarus Verilog.

Show result¶
gtkwave top_tb.vcd

Assignment 4¶
Run linter (verilator –lint-only) and fix any warnings¶
verilator --lint-only -Wall top.v debounce.v note_c_generator.v
After hitting above command, I got the result
- V e r i l a t i o n R e p o r t: Verilator 5.044 2026-01-01 rev v5.044
- Verilator: Built from 0.061 MB sources in 4 modules, into 0.010 MB in 3 C++ files needing 0.000 MB
- Verilator: Walltime 0.007 s (elab=0.000, cvt=0.002, bld=0.000); cpu 0.006 s on 1 threads; alloced 30.348 MB
This message means these 4 modules I created ran in 0.007s(Walltime) without any erro/wornings.
At last, I tried to make mistake.
I changed the value of signal widths line 14 to 10.
collect:
reg [14:0] counter;
↓
wrong:
reg [10:0] counter;

The result of simulation is already wrong…

Then I got following wornings by linting.
%Warning-WIDTHTRUNC: note_c_generator.v:17:21: Operator ASSIGNDLY expects 11 bits on the Assign RHS, but Assign RHS's CONST '15'h0' generates 15 bits.
: ... note: In instance 'top.u_note_c'
17 | counter <= 15'd0;
| ^~
... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=5.044
... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message.
%Warning-WIDTHTRUNC: note_c_generator.v:22:25: Operator ASSIGNDLY expects 11 bits on the Assign RHS, but Assign RHS's CONST '15'h0' generates 15 bits.
: ... note: In instance 'top.u_note_c'
22 | counter <= 15'd0;
| ^~
%Warning-WIDTHEXPAND: note_c_generator.v:20:25: Operator GTE expects 32 or 15 bits on the LHS, but LHS's VARREF 'counter' generates 11 bits.
: ... note: In instance 'top.u_note_c'
20 | if (counter >= CLK_COUNT_MAX - 1) begin
| ^~
... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=5.044
... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message.
%Warning-WIDTHTRUNC: note_c_generator.v:30:21: Operator ASSIGNDLY expects 11 bits on the Assign RHS, but Assign RHS's CONST '15'h0' generates 15 bits.
: ... note: In instance 'top.u_note_c'
30 | counter <= 15'd0;
| ^~
%Error: Exiting due to 4 warning(s)
I could see the warning by chaning the error inside of the Verilog code.