CSCI 320 — More SystemVerilog

In this lab, you are going to be given an SystemVerilog “include” file and the outlines of two SystemVerilog modules, one that will test the other. Each will contain comments that you should complete in the lab.

This lab is also a graded homework assignment. If you have your modules working by the end of the lab, just upload them to Moodle as Homework 8. Otherwise, you will need to work on them as you would any other homework assignment.

The big picture

You are going to program a countdown module. The countdown module has three commands that can be performed in a clock cycle.

The countdown has a single output, atZero, which is 1 only when the register has the value zero.

Creating a project

Start by creating a SystemVerilog project called countdown in a new directory with three initially empty SystemVerilog programs with the names given below.

Oddly enough, you should be able to successfully compile these files even though they contain nothing.

The “include”

SystemVerilog has an include mechanism similar to C”s and, consequently, far less useful than the import mechanism of Java. The entire contents of count_defines.sv will be the following definition of the bit fields for the three commands. This use of the enum is similar to that of Java and C.

typedef enum logic [1:0] {
  LOAD   = 2'b00,
  DOWN   = 2'b01,
  HOLD   = 2'b10
} commandType ;

Copy this typedef into count_defines.sv.

Next, start both countdown.sv and countdownTB.sv with the following compiler directive.

`include "count_defines.sv"

This directive effectively inserts count_defines.sv into your program. It is similar to the #include directive of C and C++.

Be sure you can still compile this project that does nothing.

Working on the countdown module

Parameters, inputs and outputs

Insert the following as the body of countdown.sv.

module countdown
  // Define a parameter DATA_WIDTH here.
  // Give it the default size of 8.
  // This is the size of the register of the countdown module
  #()

  // Define inputs and outputs for your module.
  // Be sure to use the suggested names
  (
  // Here are the four inputs
  // 1:  A clock input:                            clk

  // 2:  A reset input:                            reset

  // 3:  A commandType, from the include, input:   action

  // 4:  A input with DATA_WIDTH bits:             value
  
  // And here is the single input
  //     An output for the result:                 atZero

  ) ;

endmodule

You should still be able to compile your project.

Edit countdown.sv to insert the four input and one output variable definitions mentioned in the comments. This will be similar to, but simpler than, the module header used in Homework 6. Be sure to use the suggested names. It will make it easier to follow the next part of the assignment.

Be very careful that you spell the variable names correctly. If you do not declare a variable in SystemVerilog, it will be treated as an implicitly declared one-bit logic variable. This means that misspelled variables are often ignored by the compiler. This in one way SystemVerilog like FORTRAN IV.

Also, your program might just look a little better if you delete those fill-in-the-blank comments.

Make sure it still compiles.

Completing the module

Now you must make the module do its job. Start by inserting the following comments into the countdown module.

You should be able to figure out where the comments go.

  // Add a single declaration here for the count down register.
  // This register must hold DATA_WIDTH bits.
  // Call your variable countValue
  
  
  // Now add an always_ff block that is triggered on the
  //   positive edge of clk or any change of reset
  // The block performs the following actions
  // If reset is 1
  //   set countValue to 0

  // If reset is 0 and action is LOAD
  //   set countValue to value (load it!)

  // If reset is 0 and action is DOWN and countValue is more than 0
  //   decrement countValue
  
  // Finally, add a continuous assignment statement to set atZero to 1
  //   when countValue is zero.

Use the non-blocking assignment (<=) inside the always_ff block. The continuous assignment statement is the one which starts with the keyword assign.

Whenever you encounter a situation where you would have to use braces ({ … }) in Java, JavaScript, C, or C++, you must use the much more wordy begin … end in SystemVerilog.

Yep. Compile it.

Testing in the simulator

Start up the simulator and test your module. When you see the Start Simulation window, you select the countdown module, which should also be known as the work.countdown design unit.

Take a very careful look at the variables listed in the Objects window. It should look something like the following picture:
Objects in countdown
Your Objects window doesn’t have match this one exactly. In particular, you may have chosen a different default for DATA_WIDTH. But be on the outlook for pairs of similarly named variables, such as atZero and atzero. These will cause you much trouble.

You’ll have to use force to set the input values and run to execute your program for a few milliseconds. I’ve noticed a few students using a one-line command consisting of four vsim commands to clock the circuit. Combined with the up-arrow key to recall previous commands, this really speeds up manual simulation. You can also use the names defined in count_defines.sv in your commands. Here’s an example of few useful simulator commands.

force value 'd42
force action LOAD
force clk 1 ; run ; force clk 0 ; run
force action DOWN
force clk 1 ; run ; force clk 0 ; run

Load the countdown module with 13 and count down to zero.

Remember to use the up-arrow key to recall previous in the simulator.

Finally a test bench

We now turn our attention to countdownTB.sv.

The module header

The module header is easy. Just use the following.

module countdownTB
      () ;

endmodule

Adding the dut — device-under-test

Instantiation seems to be a problem in courses CSCI N where 181 ≤ N < 400, so let’s get some more practice!

First add the following comments into your module.

  // The countdown module has five connections: 4 input and 1 output.
  // In this program, the countdown module must be instantiated using the
  //   following five variables to use code provide with the lab.
  // Declare these variables below.
  
  // clk, a 1-bit logic value
  
  // reset, a 1-bit logic value
  
  // command, a value of type commandType
  
  // limit, an 8-bit logic value
  
  // outData, a 1-bit logic value
  
  // Now instantiate a countdown module using the variables declared above.
  // The width of the instantiated module should be 8.
  // The name of the instantiated module should be "dut".
  // This is a single statement in SystemVerilog!
  // It is also like an internal method call in Java.

Again, this is similar to but simpler than what was needed for Homework 6.

If you wish you can add this compiler directive at the beginning of your program. It will do some checking for misspelled words.

`default_nettype none

Once again compile your module; but, to make sure the parameters and input and output variables match, you should also start up the simulator even though you will do nothing with it.

Adding a task

A task is similar to a function, but it can use delays. Add the following humongous task into your testbench.

  task countToN(int unsigned N) ;
    int i ;
    limit = N ;
    $display("Counting from %d", limit) ;
    command = LOAD ;
    #100 clk = 0 ;
    #100 clk = 1 ;
    command = DOWN ;
    for (i=0; i<limit; ++i)
      begin
        $display("Output at clock cycle %d after load: %b", i, outData) ;
        #100 clk = 0 ;
        #100 clk = 1 ;
      end
    for (i=limit; i<limit+10; ++i)
      begin
        $display("Output at clock cycle %d after load: %b", i, outData) ;
        #100 clk = 0 ;
        #100 clk = 1 ;
      end
  endtask

Better use a compile to make sure you pasted the cut correctly.

Using the task

The only thing you need now is an initial block to call the codeToN task. Insert the following comments and add code to do what they say.

  // Add an initial block to perform the following activities
  // First,     Set reset and clk to initialize the countdown module
  // Second,    Call countToN to test the module
  // Third,     $stop ;

Compile and simulate your testbench.

Start up the simulator and test your module. In the Start Simulation window, select the countdownTD module, which is also known as the work.countdownTD design unit.

If you run it for a million units, it should display 0 for thirty clock cycles and 1 for ten clock cycles.

By the way, your Objects window should look something like this before you start the simulation.
Objects in countdownTB

If feeling ambitious

If you want some more to do, you can try modify countToN so that it uses an external clock. This requires adding something like the following always block to the testbench.

      always
      begin
        #100 clk = 0 ;
        #100 clk = 1 ;
      end

Then you need to remove the delays from the countToN task and replace them with a block on the clock. This really can reduce the size of the testbench code by removing a lot of those delay statements.

Here is an example loop with a block on a clock from a testbench used to test Homework 6.

        while (i<1000 && !ready)
        begin
          @(negedge clk)
          ++i ;
        end