Hoseon Yu - personal website

← ../Computer_Architecture

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...