Learning SystemVerilog 01
Last modified: Feb 14, 2026
Overview
Well… I had to learn SystemVerilog. This is something that I made while trying to learn it.
The original code was from Prof. Marano’s website.
I added a lot of comments, and I thought some people might find them helpful.
Module Example
module my_module (
input logic clk, // single bit signal
input logic rst,
input logic [3:0] data_in, // 4 bit signal MSB:3 , LSB: 0
output logic [3:0] data_out
);
// here we define internal signals, parameters, and types before using them
logic [3:0] internal_signal;
localparam WIDTH = 4;
// this just declares a local variable called WIDTH and sets it to 4.
// it is not used here anyway.
// Dataflow: continuous assignment
assign internal_signal = ~data_in; // ~ is bitwise not
// this is making sequential logic here
always_ff @(posedge clk) begin
if (rst)
data_out <= 4'b0; // with always_ff, you use <=; with always_comb, you use =
else
data_out <= internal_signal;
end
endmodule
// to test this logic, we need a testbench.
// --> let's go to tb_my_module.sv
Testbench Example
Think of this code as your “device.” To test your device, you need something else to test it with, and we call this a testbench. The following is an example testbench for the code presented above.
`timescale 1ns/1ps // this defines timeunit / precision
// time unit defines what #1 means, in this case 1ns
// time precision is the smallest unit that simulator can
// represent
module tb_my_module; // components that you want to test should be declared here
// DUT signals, *dut meaning design under test (DUT: Device Under Test)
// here you are declaring variables to connect to DUT.
// don't do input logic clk or output logic ... because that is not the module
logic clk;
logic rst;
logic [3:0] data_in;
logic [3:0] data_out;
// Instantiate DUT
// This is like wiring things together
my_module dut(
.clk(clk), // testbench clk --> DUT clk, * inside_the_module(testbench_variable)
.rst(rst),
.data_in(data_in),
.data_out(data_out)
);
initial clk = 0; // initialize clk to be 0
always #5 clk = ~clk; // you made a clock nice
// dump waves (for GTKwave)
initial begin
$dumpfile("wave.vcd");
$dumpvars(0, tb_my_module);
end
// ------------------------------------
// Nice job, now you have initialized your testbench
// ------------------------------------
initial begin
$display("time | rst | clk | data_in | data_out |");
$monitor("t=%0t| rst=%b | clk=%b | data_in=%h | data_out=%h", $time, clk, rst, data_in, data_out);
// think of display and monitor as printf in C.
// display only prints out the text, and monitor is how you print variables with printf.
// t for time
// b for binary
// h for hex
rst = 1;
data_in = 4'h0; //4 bits, hexadecimal base, A is the number
repeat (2) @(posedge clk);
rst = 0;
// it holds rst = 0 for 2 positive clock edges
// let's test some values
data_in = 4'hA; @(posedge clk);
data_in = 4'h0; @(posedge clk);
data_in = 4'hB; @(posedge clk);
$finish; //you need this to finish it
end
endmodule
Input Timing Pattern
Another useful thing that I wanted to mention is that when you have several input variables, you can make something that is very similar to a truth table:
initial a = 0;
always #5 a = ~a;
initial b = 0;
always #10 b = ~b;
initial c = 0;
always #20 c = ~b;
//and so on...