Practical 4: Verilog Modules
Objectives
This is not a list of tasks for you to do. It is a list of skills you will have or things you will know after you complete the lab.
Following completion of this lab you should be able to:
- Implement hardware modules in Verilog.
- Test hardware modules using verilog test benches (functional testing) in ModelSim.
- Use a basic unit test framework when writing tests.
- Read and interpret simulated waveforms.
- Debug and modify hardware modules using waveforms and other debugging tools.
Guidelines
-
During this lab, ModelSim will create lots of temporary files. Do not commit every file created to git. Only commit source and project files.
-
At a few points during this lab, we will directly instruct you to add, commit, and push files to git. You are encouraged to do that more often than we tell you to show "iteration" on your worksheet.
-
As you write code, especially test bench code, use comments to explain the details of any complicated operations or reasons for the code you wrote. Documenting your code is important so others can understand it.
-
If you do not have Quartus and ModelSim installed from ECE233, go install it now! You can find instructions for installing and testing it here.
Time Estimate This practical will take approximately 6–8 hours per student, varying depending on your familiarity with Verilog. Even if you do not have a full grasp of programming in Verilog, this practical is designed to bring you up to comprehensive understanding upon completion.
Preliminary Tasks
Install ModelSim and VSCode Extensions
This practical, and all remaining practicals, will be implemented using Verilog
and tested in ModelSim. If you have ModelSim already installed (via Quartus),
check that you have the correct version of ModelSim INTEL FPGA STARTER EDITION 10.5b
(comes with Quartus Prime 18.1). You can check this by launching ModelSim, then go to Help → About ModelSim.
If you do not have ModelSim version 10.5b...
If you do not have ModelSim installed, go to Intel’s Website to download ModelSim. Do not download Quartus that is defaulted on the website. You will only need ModelSim, not the entire Quartus Software Suite. We will be looking for the ModelSim standalone download in the Individual Files tab:
Download ModelSim-Intel® FPGA Edition (includes Starter Edition) under the Intel® Quartus® Software section. Launch and run the installer, and verify that you have installed INTEL FPGA STARTER EDITION 10.5b.
Modify modelsim.ini
The unit testing tools you will be using for this practical and all remaining practicals will require you to turn on ModelSim messages. This is required.
Navigate to the ModelSim directory on your machine. It is usually in one of these locations:
C:\intelFPGA_lite\18.1\modelsim_ase\
C:\intelFPGA\18.1\modelsim_ase\
C:\Quartus\modelsim_ase\
In the modelsim_ase
folder you will find a file called modelsim.ini
. You may need to show file extensions to see the full name of this file. Open this file in a text editor and append the following two lines to the end of the file. Make sure they are both uncommented.
msgmode = both
displaymsgmode = both
Save and close the file. When you create a new ModelSim project, it will duplicate this file as the base settings and enable better message display to see failed/passed unit tests.
If you cannot find a modelsim.ini
file in that modelsim_ase
directory, you can download and save this copy of modelsim.ini by right-clicking that link, and clicking "save link as".
Install Verilog VSCode Extensions
In VSCode, install the following extensions:
- Verilog HDL, by leafvmaple,
- Verilog-HDL/SystemVerilog/Bluespec SystemVerilog, by Masahiro Hiramori.
More Extension Install details (optional)
- Click the "Extensions" button in the Activity bar (far left). It looks like this:
Search for "Verilog HDL" and install one of the one by
leafvmaple
.- The other (Verilog-HDL/SystemVerilog/Bluespec) looks like a microchip.
If you already installed these in Practical 1, then double check that they are installed
Clone your verilog-modules
Git repository
You can access your practical repository verilog-modules
via Github Classroom.
As in past practicals, clone the repository onto your local machine via SSH.
Make sure this repository is not cloned into Microsoft OneDrive. Desktop,
documents, or any other local file path is acceptable.
Verilog Modules
We will start with a guided walkthrough of how to:
- Set up a ModelSim project
- Run a testbench in ModelSim
- Modify a testbench
You will do this for all remaining practicals. Be sure to famliarize yourself with this workflow, including the occasional quirks of ModelSim. Yes, it is a lot of steps, but if you follow this procedure you will minimize the bugs you encounter and work efficiently.
1 The Register
and tb_Register
modules
- Launch VS Code, and when it's open, choose "Open Folder" either from the main window or from the File menu.
- Navigate to your git repo and open that folder.
- Open a few of the Verlog files that appear in the file navigator:
Register.v
tb_Register.v
- Right now editing verilog is annoying in VS Code. Install the "Verilog HDL" extension to make it nicer:
- Navigate back to the verilog files you opened and make sure syntax coloring works.
Set up a ModelSim project
For every practical, you will need to create your own .mpf
ModelSim project file. Even if you are working in a group for later practicals, each one of you need to create your own .mpf
project file.
ModelSim uses absolute pathing in its project settings to reference files
(e.g., that path will include your machine’s username), therefore you cannot launch someone else’s .mpf
file without editing it.
-
Start ModelSim.
-
Create a new project for this lab
- Choose
New > New Project
from theFile
menu. - Set your Project Name to include your username (e.g.,
practical4_rosie
) - Browse... and set the directory to be your
verilog-modules-<username>
repository folder. - Verify that Copy Settings From is set to
modelsim_ase/modelsim.ini
and Copy Library Mappings is selected. - Click OK
- Choose
-
When prompted, choose
Add Existing Files to Project
-
Before we continue, navigate to your repository and open the
.mpf
project file in a text editor. This file was created when you created a new project in ModelSim. Check that you have the linesmsgmode = both
anddisplaymsgmode = both
in the file. If you followed themodelsim.ini
setup carefully, those lines should appear in the file; if not, you can add them manually here. -
Returning to ModelSim, where your project is open, make sure you can compile them. Near each file in the list, the status column should contain a blue question mark to indicate they need compiling. Choose
Compile All
from theCompile
menu or click the "Compile All" button in the toolbar().
-
There will be two compile errors. Double-click the red error message and modelsim will open an error message window with a verbose description. These descriptions (hidden behind double-clicking) will be extremely helpful.
-
Read the messages carefully, then find and fix the errors. While you can edit files in ModelSim, we strongly recommend you use VS Code instead because it is more powerful. (This should be a quick fix).
-
If you edit the files in VSCode (recommended), you don't have to close ModelSim. Just switch back and forth between the two programs to edit or compile.
-
Examining the vunit
framework and the tb_Register.v
test bench.
Your instructors have created a very basic unit test framework for you to use in this course called vunit
. In order to use it in a verilog test bench, you make an instance of the vunit
module, then execute various tasks within that instance.
Lets look at how to use vunit
in a test bench.
-
Open
tb_Register.v
in VS Code. Inside thetb_Register
module near the top, there are a lot of definitions:-
The module definition (see the
module
keyword) -
The
vunit
framework instantiation (VU()
) -
The clock register (
reg CLK
) -
The input drivers (
write_enable, reset, data_in, default_value
). These are declaredreg
because they will hold inputs the test bench sets. -
The
wire
instancedata_out
, which is a probe we will connect to the output of our Register unit under test so we can read its output. -
The declaration of
UUT
, which is an instance of theRegister
module. -
The perpetual clock code in an
initial
block.These are the main components "on" our test bench. The rest of the code in
tb_Register
is the testing procedure.
-
-
Scroll past the three tasks declared until you get to the
initial
block at the bottom. It has a comment at the top that says "Run the tests". This is the main driver for our test bench, and is where you found the compiling error.- This has to be down at the bottom so the tasks it refers to can be found. It's annoying, sorry.
- There are three tests that run: one to test that reset works, one to test that write-enable works, and then finally a third test that re-checks reset again.
-
If you scroll up to the tasks you passed before, you can see each one (when it runs) will execute a specific test. These are kind of like subroutines (but not quite). Creating tasks for each test is a nice way to break up your tests so you can quickly turn them on or off by commenting them out where they get called.
Running tb_Register
in ModelSim
Let's return to ModelSim and try running tb_Register
. You can do this from inside your ModelSim project:
-
First, make sure everything is compiled.
-
Then expand the
work
library and double-clicktb_Register
to start the simulation.- NOTE: if
tb_Register
isn't in the work library, click back to the "Project" tab, then try closing the project (in the File menu) and then open your Practical4 project file again
Tip 1: missing test bench in library
In the future, if you do not find the testbench you are looking for, it is likely that the
module
defined in the file is different from the name of the file. ModelSim uses the name of themodule
, not the name of the file. This might happen if you copy/paste a testbench file but forget to update the module name.Tip 2: fix errors before simulating.
When you load a testbench, ModelSim may throw another set errors in the Transcript. Some of these are fatal errors — you must resolve them, recompile, and load the testbench again. Others are warnings — it will allow the testbench to run, but your behavior will often be incorrect due to these warnings resulting in undesired behaviors. Resolve all warnings before you proceed.
- NOTE: if
-
When the simulation starts, it should open a new window with two panels: "Objects" and "Wave". If you do not see them or accidentally close them, you can load them again via View -> Objects/Wave.
-
Populate the waveform with the signals we are interested in. In this case, it is everything except
HALF_PERIOD
. Make suretb_Register
is selected in thesim
view, then select all of the signals in theObjects
panel and drag them over to the waveform window to add them, or type ctrl+w: -
From here, we can run the simulation by typing
run -all
in the Transcript, choosingrun -all
from the Simulate -> Run menu, or using the button on the toolbarWhen the simulation completes, you should see a bunch of output in the transcript (bottom of the screen), and waves in the waveform.
Tip 3: Hiding long paths in wave names
Notice that all the objects in the Wave panel all start with
/tb Register/
. This clutters the panel and makes it hard to read. Click on the Wave panel, and the Wave menu option should show up at the top of the window. Go to Wave → Wave Preferences... and set the value of # elements to 1. -
You'll quickly notice green and red triangles on the weaveform. If not, make sure the
msgmode
anddisplaymsgmode
settings are in your.mpf
file.
A green triangle indicates that a test (ASSERT
) passed in the test bench, and a red one means that something failed. -
At the top of the waves, you'll see some inverted triangles. If you don't see these, make sure the
msgmode
anddisplaymsgmode
settings are in your.mpf
file.The triangles indicate the times when messages were posted to the Transcript. The green arrow are for "info" or a successful test (the one on the left in the image below) and the red ones (on the right in the image below) corresponds to an error or failed test.
You can hover the mouse over a triangle to learn more about the test.
You can also Double-click a red triangle. Try it to see what happens!
-
In the case of our
tb_Register.v
, theRegister
module is implemented correctly but the test bench has an error.Use the signals on the waveform and read through the transcript output and try to identify which asserts in
tb_Register.v
failed. HINT: only one assert fails, but it gets executed twice by the test bench.Tip 5: Approaching test creation with tasks
Spend time reading both tasks in
tb_Register.v
. These tasks document the approach to testing modules with ModelSim. Make sure you understand how the test progresses time using (posedge CLK
) / (negedge CLK
). You will be writing your own tests, so familiarize yourself with this structure before proceeding -
Fix the broken assert so that it tests the right thing.
-
After you resolve the error (make your edits in VSCode), you'll need to recompile the files in ModelSim. Do this the same way you compiled the first time. You may need to change from the
sim
tab to theproject
tab to get the compile buttons. -
After recompiling successfully, you need to restart the simulation (
Restart
from theSimulate
menu or the restart button)and then execute
run -all
again. -
Verify that the red triangles and "ERROR" messages in the transcript are gone before moving on.
-
Save your waveform config so you can quickly reuse it later if you want:
- Click (left-click, not right-click) in the middle of the waves.
- Choose "Save Format" or type Control-S
- Click the "Browse..." button.
- In the dialog that pops up, change the name of the file from
wave.do
totb_Register.do
- Click "Save", then "OK".
You can later reload this by going to File > Load > Macro File... and selecting your
.do
file while a simulation is running.
Once you've fixed the errors in tb_Register.v
, you should save your changes.
Add, commit, and push the following files to your git repository.
Be sure to include a message that indicates your current progress.
tb_Register.v
(The edited test)tb_Register.do
(Your waveform config)Practical4.mpf
(Your ModelSim project file)
DO NOT add all the temporary files made by ModelSim.
It'll clutter your git repo and may cause problems down the road. git commit -a
will do this, so don't use -a
.
In VS Code, you must choose which files to stage or it will add them all by default.
Do the extra step to pick which files to commit: it will save you time in the future.
3 Create and Test ImmGen
In class we talked about the Immediate Generator that looks at the bits of an instruction to figure out how to construct an immediate.
We've provided you with an ImmGen.v
file that has the start of the immediate generator, but you must finish implementing it. We're also providing you with a test bench tb_ImmGen.v
that has a few tests, but you need to write more tests.
As you implement each case in ImmGen
, you should be testing their correctness
by writing corresponding tests in tb_ImmGen.v
. You should not be implementing
ImmGen
in its entirety without doing any tests for individual instruction
formats.
- If your simulation of
tb_Register
is still running, end that (Simulate > End Simulation
menu item). - Implement one type of instruction at a time in the
ImmGen
. - For each instruction type, be sure there are at least two tests in
tb_ImmGen.v
to test that type of instruction. We've given you a couple for the first type of instruction (I-Types). - (Q) Come up with a strategy for constructing tests. How will you know when you've tested enough? Explain how these tests substantially check enough variations for you to be confident that your ImmGen module will work for all input variation.
Follow these guidelines:
- Create a "task" in
tb_ImmGen
for each type of instruction you're testing and put your instructions in there, much like the other test benches in this lab. Then, call the task from theinitial
block at the bottom of the test module. - Use your assembler from Practical 2 to efficiently produce test instruction machine code (binary) for testing.
- We've created a list of
parameter
definitions that allow you to use variable names instead of binary opcode values in your case statement. Use those.
Tip: aliases for subsections of an instruction
You may create wire busses and assign them to various bits of the instruction as shorthand for subfields of an instruction. You must do this outside of any initial
, task
, or always
blocks. For example:
wire [6:0] opcode;
assign opcode = instruction[6:0];
// now you can use "opcode" instead of "instruction[6:0]" to refer to the same bits.
You might consider doing this for the various instruction fields rs1
, rs2
, rd
, etc.
Tip: concatenating busses in verilog
To concatenate values in verilog, put them in braces separated by commas. For example:
{12'hF00, 12'h123} // evaluates to the 24-bit value, 0xF00123
Tip: replicating (duplciating) values in verilog
You can make copies of bits in verilog (to sign extend, for example) using the replication operator:
{24{opcode[1]}} // evaluates to 24 copies of bit 1 of the opcode bus
Tip: always
recomputing outputs when the input changes
Notice that this module's output is a reg
. Since the immediate generator is not
clocked and constructs the immediate "instantaneously", we would assume the
output should be a wire and defined via assign. This is correct, however
defining the entirety of imm
as a single assign statement is non-trivial since
we cannot use case or if/elseif statements.
Instead, the output imm
is defined as a reg
, but instead of defining it in a clocked always @(posedge CLK)
block, we define it in an always @(instruction)
block :
always @ ( instruction ) begin
...
end
This allows us to use the coding niceties of using a case statement and
still emulate the behavior of an instantaneous assign
statement. This tells
Verilog to re-calculate the output value imm
whenever the input instruction
changes, which is basically an assign
statement created as an always
block.
You will use this strategy to define other modules in the future (e.g., control unit, ALU, branch detection, etc.).
-
(Q) Once you've implemented and tested
ImmGen
, you need to create a screenshot and annotation that shows yourtb_ImmGen
passes tests. Take a screenshot of the waveform and the transcript window (as shown below). Add at least three annotations to your screenshot to indicate what exactly you are testing at which sections on the waveform. Include this annotated screenshot in your worksheet submission. -
Add, commit, and push the following files to your git repository. Be sure to include a message that indicates your current progress.
tb_ImmGen.v
(The edited test bench)tb_ImmGen.do
(if you created a waveform, add and commit it)ImmGen.v
(The edited ImmGen module)Practical4.mpf
(Your updated ModelSim project file)
4 Single and Dual Port Memory
Next, you will see how RAM is constructed and pre-populated with values (such as your machine code) in ModelSim.
Looking at Single Port Memory
In VS code, open up SP_Memory.v
.
This is a basic RAM module that was autogenerated by Quartus. It is word-addressed (not byte-addressed), and each address is 10 bits long (not 32). This physical module is much smaller than a 32-bit processor supports, so we will have to engineer around that in the future.
There are two very important details about this memory module:
parameter DATA_WIDTH=32
indicates that this module is word-addressed (32 bits of data per address). This is in contrast to RISC-V’s memory being byte- addressed (8 bits of data per address).parameter ADDR_WIDTH=10
indicates that this module is addressable with only 10-bits (232 rows of memory). This is in contract to RISC-V’s memory being 32-bit addressable.
The reason of this setup is because ModelSim cannot handle a ram declaration consisting of 232 rows of memory; ModelSim simply refuses to compile something so expansive. Instead, we create a ram module with 210 rows that is more comfortable for ModelSim. While this is substantially less than RISC-V’s full 32-bit addressable memory, it is enough for us to simulate small benchmark programs.
However, we are able to bulk up our memory by mapping each of the 210 addresses of ram to 32-bits (1 RISC-V word) instead of the standard 8-bits that follows RISC-V’s memory setup. This quadruples our memory without making ModelSim explode. There is very little actual code in this file, it is mostly comments. The comments explain how to create an intialization file. You can read them now or later.
- The
ram
variable is a big array of words. - Near the bottom of the file is an
always @(posedge clk)
block: this is where memory is written. - Below that always block is an
assign
; this is a continuous connection that sets the output port value to whatever is at the currently provided address. This means that the output should change very quickly, and asynchronously (not with the clock).
Tip 6: Sketch out the address mapping for RISC-V
Before continuing, we highly encourage you to sketch out both RISC-V’s 32-bit addressable memory with 1 byte rows as well as Verilog’s 10-bit addressable memory with 4 byte rows. Understanding the differences in data and address width is imperative for the next and future practicals as you wire together a full RISC-V single-cycle processor. Feel free use the diagram below to get started:
Looking at Dual Port Memory
Open DP_Memory.v
in VS Code next.
This is a version of memory that has two sets of inputs, two sets of outputs, and two clock inputs. Effectively it is two memories that share contents. Notice that it looks very similar to SP_Memory except:
- This version has two
always @
blocks that look at different clocks. - This version also employs synchronous reads (only on the clock edge). This means the output values will only change on the clock edge, regardless of the other inputs.
Testing memory with tb_memory.v
Open up tb_memory.v
. This is a test bench for both memories.
The structure of this test bench is similar to the one for the register, but there are two different modules instantiated. One is called sp_ram
and one is called dp_ram
.
-
Take time to read through the two tasks,
test_singleport_memory()
andtest_dualport_memory()
to get familiar with what this test bench is testing. These are completed tests and you don't have to modify anything, but you should get famliar with how to use verilog to directly inspect the contents of modules. This technique will be useful for future practicals. -
Your next job is to run the test in ModelSim. Compile and start the simulation like you did for
tb_Register
, but this time starttb_memory
.- When you
run -all
, you should see in the transcript that tests completed and none failed.
- When you
-
Next you should configure a waveform to help you visualize both memory instances. YOU MUST configure it in this exact order because it will correspond to a worksheet question.
-
Start by adding the signal
CLK
to your waveform. -
Next, select all the signals in the
tb_memory
instance that start withsp_
(shift-click or control-click to select many). Then drag them to the wave view or type Control-W to add them. -
When they appear in the wave panel, they will all be selected. With all of them selected, right click one of them and choose
Group...
. Name the group "Single Port RAM". -
Repeat for the dual-port connections (they start with
dp_
). -
Restart and run your simulation again. Now you should have pretty waves!
- At this point, you may want to change some of the values on the waveform to use hexadecimal radix so they're a little easier to read. Right click them and select from the "Radix" menu to change how they are interpreted.
-
Make sure your resulting waveform resembles the image below (click it for a bigger one):
-
Save your waveform as
tb_memory.do
to avoid repeating these steps. -
(Q) In the lab worksheet, there is a waveform that is incomplete. Draw what you expect to come out given the inputs provided (you are adding the missing parts of the
dp_q_a
anddp_q_b
signals). Remember that dual port memory has two clocks, and port B is activated on the falling edge of the other clock. Draw the missing signals to show what you expect the outputs to change (and write in their new values).
-
Completing the test_dualport_bothports()
task
Currently the test_dualport_bothports
task is commented out in the intial block at the bottom of the test bench. This means it doesn't run!
- Uncomment the two lines so that
test_dualport_bothports
task runs after the$info
task. - Restart and run the
tb_memory
tests and note that there will be some failures. This is because the tests are incomplete! - Edit the
test_dualport_bothports
task to complete part 2, verifying that your waveform is correct. You can use the waveform you completed in the lab worksheet as a guide. - Keep in mind that the
SP_memory
tests are running before the ones forDP_memory
, make sure you're looking at the right parts of the waveform as you debug! - Ensure that all 19
tb_Memory
tests pass (review the transcript output in ModelSim)
Add, commit, and push the following files to your git repository. Be sure to include a message that indicates your current progress.
tb_memory.v
(The edited test)tb_memory.do
(Your waveform config)Practical4.mpf
(Your updated ModelSim project file)
5 Create RegFile
and tests
Okay, you're mostly on your own for this module. You must create a register file and test it. We'll give you the specifications, and you can choose how you want to implement and test it, but you MUST test all features of the register file. You will be using this module during future practicals, so you want it to work properly.
-
First, create a
RegFile.v
file and declare aRegFile
module in that file.- Your register file should contain 32 general purpose registers, with one exception: the zero register should always be equal to zero.
- It will have the following inputs:
regnum_a
- a 5-bit register specifier to read, usually this will be "rs1"regnum_b
- a 5-bit register specifier to read, usually this will be "rs2"write_regnum
- a 5-bit register specifier of which register to write ("rd", for example)data_in
- a 32-bit value to write intowrite_regnum
write_enable
- a one-bit value that indicates whether or not to write data towrite_regnum
reset
- a one-bit value that indicates whether or not to reset the register file's contentsCLK
- a one-bit clock signal
- It will also have two outputs
regdata_a
- a 32-bit value that was read fromregnum_a
regdata_b
- a 32-bit value that was read fromregnum_a
- On the rising edge (posedge) of the clock, the register file will write from
data_in
into the register specified bywrite_regnum
, but only ifwrite_enable
is set to 1.- except if
write_regnum
is 0; register 0 should never change.
- except if
- If
reset
is set to 1 at the rising edge, all registers should be set to zero except for register 2, which should be set to0xfffffff0
. regdata_a
andregdata_b
continuously update and read the registers specified byregnum_a
andregnum_b
respectively. (IMPORTANT: This is an async read).
Here's a picture of what you're building:
-
(Q) Explain what purpose the
RegFile
will serve when you implement a processor. Where is the source of values connected toRegFile
'sregnum_a
,regnum_b
, andwrite_regnum
inputs? Where will the outputs go?
Tip: Making an array of Registers
You can create the registers inside your regfile either as native reg
data types, or using the Register
module from this lab.
- If you choose to use the
Register
module, consider using a generate loop or an "array of instances" approach. - if you choose to use the reg primitive type, see the Resources section of this web site for help.
Also, consider looking at the ram
array in memory for an example.
-
Write tests to sufficiently test all functionality of your register file. Write tests for each part you implement as you implement the register file.
Consider the following testing milestones to be written as individual tasks in your testbench:
- Testing reset behavior,
- Testing asynchronous read from
regadata_a
andregdata_b
, - Testing writing behavior when
write_enable=1
, - Testing writing behavior when
write_enable=0
, - Testing writing behavior when
write_regnum=0
.
In addition to these standard unit tests that checks for basic
RegFile
behavior, write a larger test that substantially testsRegFile
— test reading/writing to all 32 registers and combinations withwrite_enable
andreset
.Tip: Initialize register values from test bench
You can directly set the values of the RegFile contents in the testbench:
task test_example(); begin UUT.REGES[1] = 32'h00000001; UUT.REGES[2] = 32'h00000002; UUT.REGES[3] = 32'h00000003; UUT.REGES[4] = 32'h00000004; end endtask
This allows you to test reading behavior without needing to modify them via
REGES[write_regnum] = write_data
syntax.Knowing how to directly change reg content values in the testbench will allow you to test lw and sw instructions very easily in later practicals. Commit this to memory!
Tip: Use loops to automate similar tests
You can use integer and repeat to set up loops to automate checks:
integer i; task test_example(); begin i = 0; repeat (32) begin rs1 = i; // set value of regnum_a rs2 = i; // set value of regnum_b #1; // wait for regdata_a and _b to update VU.ASSERT(A === i); // if REGES[i] = i is set up VU.ASSERT(B === i); // if REGES[i] = i is set up end end endtask
You can combine this with the previous tip to very efficiently test the entirety of your RegFile.
Tip: Inspecting register file contents on the waveform
Being able to set the contents of REGES or any other internal storage in module is useful; it will also be able to view the contents of
REGES
and similar storage on ModelSim. On ModelSim you are only able to view the values of the input drivers and output wires,REGES
is nowhere to be found:REGES
is not found becauseREGES
is not defined intb_RegFile
; it is defined inRegFile
. Go to the sim tab on the very left, and selectUUT
or what you named the instance of yourRegFile
. Once you selectUUT
, the Objects panel should update to show every wire and reg defined inRegFile
, includingREGES
:From here, you can add
REGES
to the waveform, expandREGES
, change its radix, and directly inspect the values of each of the 32 registers. You may need to restart and run the simulation again after addingREGES
to the waveform. -
To run your testbench in ModelSim you'll need to add the new files to your ModelSim project.
- In the
Project
tab (where the files are listed and the green checkmarks for compiling are visible) right click and select "Add to Project > Existing File..." then select yourRegFile.v
andtb_RegFile.v
to the project. - After adding them you can compile and run the test bench the same way you have the others.
- In the
-
(Q) On the lab worksheet describe your strategy for constructing tests for tb_RegFile. Explain how these tests substantially check enough variations for you to be confident that your RegFile module will work for all input variation.
-
(Q) After you've completed your tests, take a screenshot of a waveform to show at least 3 of the key tests for your register file passing.
- Your wave form should contain all 7 of the input signals to the register file, and the values of at least 2 registers relevant to your tests. Your register values should be displayed in decimal, you should pick appropriate radix settings for the other signals.
- Your waveform should include annotations like in the immgen sample above that show where the tests occur, as well as the console output window.
Add, commit, and push the following files to your git repository. Be sure to include a message that indicates your current progress.
tb_RegFile.v
(Your new test bench)tb_RegFile.do
(if you created a waveform, add and commit it)RegFile.v
(Your new RegFile module)Practical4.mpf
(Your updated ModelSim project file)
Working Ahead
You cannot work ahead towards later practical as that requires teams to be formed. However, you can read ahead to Practical 5 and start planning how you would construct the RISC-V single cycle processor in Verilog.
Submission and Grading
Functional Requirements
At the end of the practical you should have done these things:
- Modify
tb_Register
to pass all tests - Implement
ImmGen
to correctly construct immediates for each instruction format:- R-type / I-type / S-type / SB-type / U-type / UJ-type
- Expand
tb_ImmGen
to run at least two tests for each instruction format- R-type / I-type / S-type / SB-type / U-type / UJ-type
- Modify
tb_Memory
to pass all tests - Create the
RegFile
module according to the specs - Create a
tb_RegFile
module to substantially test and verifyRegFile
's behavior - Complete and submit the Practical Worksheet
Git Requirements
In addition to the list below, you should regularly commit and push whenever you fix a bug, work to a stopping point, or make any incremental updates. At minimum, you must have at least 5 commits in your repo for this practical (one for each function):
- Git commit 1: upon completion of tb_Register.
- Git commit 2–7: upon completion of each instruction format in ImmGen with corresponding tests in tb_ImmGen,
- Git commit 8: upon completion of tb_Memory.
- Git commit 9: upon completion of RegFile.
- Git commit 10: upon completion of tb_RegFile.
Remember, Do not add and commit every single file ModelSim creates. Only add, commit, and push .v, .do, and .mpf files.
Worksheet Requirement
All the practicals for CSSE232 have these general requirements:
General Requirements for all Practicals
- The solution fits the need
- Aspects of performance are discussed
- The solution is tested for correctness
- The submission shows iteration and documentation
Some practicals will hit some of these requirements more than others. But you should always be thinking about them.
Final Checklist
- Verify your verilog code is committed and the commits are pushed to github.
- Submit your completed worksheet to gradescope.
Grading Breakdown
Practical 4 Rubric items | Possible Points | Weight |
---|---|---|
Worksheet | 76 | 50% |
Code | 70 | 50% |
Total out of | 100% |