Session 5: RTL Design & Verification¶
What I learned this week¶
This is a flip-flop example from EDA playground. In this example, both clock and reset signal trig at positive edge. This is different from the example in the course. My intent here is to focus on the overall process, not on that specific code

Run Verilator on the code to check it

This is the test harness for the flip-flop example

Using iVerilog, we can build the flip-flop example along with its test harness

Then, run the tests

And show the waves on a graph

“
Useful links¶
Project homework:¶
Write Verilog for your project’s core module (aim for 10-30 lines to start)¶
The Arbiter
- input: the signal coming from the two counters
- output: one bit to indicate the race is over and another to indicate who won
- job to to: decide which counter wins (i.e trigs first)
- design:
module arbiter (
input wire clk, // Clock
input wire rst_n, // Reset (active low)
input wire data_in1, // 1-bit input, player 1
input wire data_in2, // 1-bit input, player 2
output reg data_out, // 1-bit output, HIGH when first player wins, LOW otherwise
output reg finish // 1 bit output, HIGH whwn at least one players cross the finish line
);
// State encoding
parameter IDLE = 2'b00;
parameter L1_WIN = 2'b01;
parameter L2_WIN = 2'b10;
reg [1:0] state, next_state;
// State Register
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
data_out <= 0;
finish <= 0;
end
else begin
state <= next_state;
end
end
// Next State Logic & Outputs
always @(*) begin
next_state = state;
case (state)
IDLE: begin
if (data_in1 == 1 && data_in2 == 1)
next_state = L1_WIN; // Tie-breaker: Lane 1 wins
else if (data_in1 == 1)
next_state = L1_WIN;
else if (data_in2 == 1)
next_state = L2_WIN;
else begin
data_out = 0;
finish = 0;
next_state = IDLE;
end
end
L1_WIN: begin
data_out = 1;
finish = 1;
// Stay in this state until reset
end
L2_WIN: begin
data_out = 0;
finish = 1;
// Stay in this state until reset
end
default: next_state = IDLE;
endcase
end
endmodule
Run linter (verilator –lint-only) and fix any warnings¶

Simulate with a testbench¶
Test harness
`timescale 1ns/1ps
module test_arbiter;
// Inputs are reg (we drive them)
reg clk, rst_n, data_in1, data_in2;
// Outputs are wire (DUT drives them)
wire data_out, finish;
// Instantiate Device Under Test
arbiter arbiter (
.clk(clk),
.rst_n(rst_n),
.data_in1(data_in1),
.data_in2(data_in2),
.data_out(data_out),
.finish(finish)
);
// Clock generation
always #10 clk = ~clk; // 50 MHz (20ns period)
// Test stimulus
initial begin
$dumpfile("waves.vcd");
$dumpvars(0, test_arbiter);
// Initialize
$display("Initialize");
#10
clk = 0;
display;
// Reset
$display("----------- Reset --------------");
rst_n = 0;
data_in1 = 0;
data_in2 = 0;
#20
rst_n = 1;
#20
$display("----------------- --------------");
// Test case 1
$display("==> Test case 1: first player wins");
data_in1 = 1;
#20
$display("Second player follows");
data_in2 = 1;
#20
display;
// Reset
$display(" --------- Reset --------------");
rst_n = 0;
data_in1 = 0;
data_in2 = 0;
#20
rst_n = 1;
#20
$display("----------------- --------------");
// Test case 2
$display("==> Test case 2: second player wins");
data_in2 = 1;
#20
$display("First player follows");
data_in1 = 1;
#20
display;
// Reset
$display(" --------- Reset --------------");
rst_n = 0;
data_in1 = 0;
data_in2 = 0;
#20
rst_n = 1;
#20
$display("----------------- --------------");
// Test case 2
$display("==> Test case 3: both wins... player 1 is picked");
data_in2 = 1;
data_in1 = 1;
#20
display;
// Reset
$display(" --------- Reset --------------");
rst_n = 0;
data_in1 = 0;
data_in2 = 0;
#20
rst_n = 1;
#20
$display("----------------- --------------");
#100
$finish;
end
task display;
#1 $display("data_in1:%0h, data_in2:%0h, data_out:%0h, finish:%0h, reset:%0h",
data_in1, data_in2, data_out, finish, rst_n);
endtask
endmodule
Examine waveforms in GTKWave¶

Integrate with any provided library modules (e.g., debounce, UART, PWM) — create a top-level wrapper¶
`timescale 1ns/1ps
module puf_bit (
input wire[7:0] challenge, // the PUF challenge
input wire clk, // the clock
input wire rst_n, // reset
input wire enable, // enable
output wire response, // the response to the challenge
output wire done // end of the race
);
localparam n_ro = 32; // we have 2 series of 16 ROs, with a 7 inverters chain in each
localparam n_half = n_ro / 2;
wire[n_ro-1:0] ro_out; // the outputs of the 32 ROs
wire mux_out_1; // the output of the first MUX
wire mux_out_2; // the output of the second MUX
/* verilator lint_off UNUSEDSIGNAL */
wire[15:0] cnt_out_1; // the output of the first counter
wire[15:0] cnt_out_2; // the output of the second counter
/* verilator lint_on UNUSEDSIGNAL */
wire finish_1; // a flag indicating the first counter has reached its threshold
wire finish_2; // a flag indicating the second counter has reached its threshold
genvar i;
// This block generates 32 ROs
generate
for (i = 0; i < n_ro; i = i + 1) begin : ro_gen
ro ro_inst (.enable(enable), .endOfChain(ro_out[i]));
end
endgenerate
mux mux_1(.ro_inputs(ro_out[n_half-1:0]), .challenge(challenge[3:0]), .out(mux_out_1)); // setup the first MUX, it gets data from the first 16 ROs
mux mux_2(.ro_inputs(ro_out[n_ro-1:n_half]), .challenge(challenge[7:4]), .out(mux_out_2)); // setup the second MUX, it gets data from the second 16 ROs
`ifndef SYNTHESIS
counter #(.THRESHOLD(16'b0000000011111111)) cnt_1(.clk(clk), .rst_n(rst_n), .data_in(mux_out_1),
.data_cnt(cnt_out_1), .finish(finish_1)); // setup the first counter, it gets data from first MUX
counter #(.THRESHOLD(16'b0000000011111111)) cnt_2(.clk(clk), .rst_n(rst_n), .data_in(mux_out_2),
.data_cnt(cnt_out_2), .finish(finish_2)); // setup the second counter, it gets data from the second MUX
`else
counter #(.THRESHOLD(16'b1111111111111111)) cnt_1(.clk(clk), .rst_n(rst_n), .data_in(mux_out_1),
.data_cnt(cnt_out_1), .finish(finish_1)); // setup the first counter, it gets data from first MUX
counter #(.THRESHOLD(16'b1111111111111111)) cnt_2(.clk(clk), .rst_n(rst_n), .data_in(mux_out_2),
.data_cnt(cnt_out_2), .finish(finish_2)); // setup the second counter, it gets data from the second MUX
`endif
arbiter race_arb(.clk(clk), .rst_n(rst_n), .data_in1(finish_1), .data_in2(finish_2),
.data_out(response), .finish(done)); // setup the arbiter, it gets data from both counters
endmodule