Practical 6 Single Cycle Processor 2
Objectives
This section 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 practical.
Following completion of this practical you should be able to:
- Add instructions to a single-cycle datapath drawing.
- Modify a medium-sized verilog project to have new functionality.
- Add jump and link functionality to a single cycle implementation of RISC-V
- Use waveform diagrams and verilog test benches to debug a processor implementation
- Reason about the implication of updating different registers on different clock edges.
Guidelines
- Because you will be iteratively adding functionality to one processor module, we strongly recommend that you periodically add and commit your progress to git as a backup.
Time Estimate This practical will take approximately 5-8 hours per student, varying depending on your familiarity with Verilog and the single-cycle architecture covered in class.
Alternate instructions (more words) are available here.
Preliminary Tasks
Follow this sequence of instructions to complete the practical.
This practical will all be done in your RISC-V-single-cycle-processor
repository where you completed Practical 5.
Expand your RISC-V Single-Cycle Processor
You will now add lui
, jal
, and jalr
support to your processor. Similar to before, do the following for each:
- (Q) Write out the RTL for the instruction on the worksheet. Include any RTL that is common with the other instructions, like
newPC <= PC + 4
- (Q) Edit and trace the datapath diagram to show this type's execution on the practical worksheet datapath where indicated. Label any newly added or newly used wires, and highlight the control signals that are used.
- Expand your
Control.v
andControl_tb.v
to test control for the type. - Expand
Processor.v
to implement the instruction type. - Add a task to
tb_Processor.v
to substantially test the new instruction type. For each type, create a memory file in thetests/
folder that you can use for testing, and add contents to those files - (Q) Take a screenshot of your waveform and annotate it, labeling which portions of the waveform are running which tests. Be sure to submit this annotated waveform on your Practical Worksheet.
- Commit your progress to git.
1 Add lui
support
You will begin by adding the lui
(load upper immediate) instruction to your processor. For this you should not need to add any components, but you will need to update Control and a couple existing components.
-
Control Unit
- Edit
Control.v
to have an additional case forlui
(U_OPCODE
).- HINT: if you modified a mux, you may need to create a new control signal. Instead of making a single-bit control signal into a two-bit signal, we recommend creating a second signal and evaluating both signals when choosing an output for the mux. See the datapath section below for example usage of both signals on one mux.
- Be sure to add a test case in
tb_Control.v
to testlui
. Verify your test succeeds before you continue.
- Edit
-
Datapath
- Update
ImmGen.v
to do the right thing forlui
. - Edit your
MemToReg
mux to support more inputs.
Tip: Expanding the MemToReg mux
Most likely you will simply modify a mux to use your new control signal. Assumming you've got two signals and your new one is called
new_ctl_sig1
, your code might start like this:always @(ctl_sig, X, Y) begin if (ctl_sig == 1) mux_output
- Update
-
Test Bench for
lui
. This will be much like the iterations from the previous practical:- create a
memory-U.txt
file with a few assembled lui instructions in it. Consider also using some R-type instructions to see if lui properly overwrites the lower bits of a register. - Create a
tb_Processor_U.v
file to test yourmemory-U.txt
instructions.- We recommend you copy one of your other tests from the last practical and edit it.
- Run your new tests. Maybe make a waveform to help debug.
- Also make sure all the old tests still work with your new Processor
- create a
-
Once you've got
lui
working, save your progress in git!- Be sure to add, commit, and push only the verilog files, changed memory file, and any waveform
.do
file you edited or created.
- Be sure to add, commit, and push only the verilog files, changed memory file, and any waveform
2 Add jal
Use the same procedure as you did for lui
to implement jal
:
- Control for
jal
- You'll probably need to add a new signal for this, like
jump
for the UJ types that will override the branch signal (sincejal
is not conditional) - And add a test to
tb_Control.v
forjal
.
- You'll probably need to add a new signal for this, like
- Datapath
- Use the new control signal to override the branch.
- Update
ImmGen
to support UJ-types. - Support "Jump" (update the PC) and "Link" (save the PC+4).
- NOTE: This is trickier than it seems at first because of the timing of how the link address gets written to the register file. Consider looking back at your worksheet submission for Practical5 and review the timing of when registers are written and when the PC updates.
- Remember: shortly after PC is written, the new pc value (PC+4) changes too! You might end up with the wrong link address if you're not careful.
- If you need to store data across a clock edge, consider using a register.
- Create a test bench
tb_Processor_UJ.v
and memory filememory-UJ.txt
to test your jal instruction.- Be sure to test both the jump part (that the
PC
gets the right new value at the right time) and also the link part (thatrd
gets the old PC+4 value). You will need at least twoASSERT
s for this.Tip: check a register's value
Remember, you can check a register's value inside a test bench with something like
UUT.Reg.REGES[i]
- Be sure to test both the jump part (that the
- Once it's working, do a git add, commit, push to save your changes.
3 Implement jalr
Use the same procedure as you did for jal
to implement jalr
. While jalr
is an I-type, its opcode is unique so you should treat it like a separate
format.
Changes here are similar to jal
, but instead of setting PC to be PC+offset
,
you will use a register value. Plan this out carefully. Drawing on the
datapath before you write code is critical to get this done quickly.
-
Control for
jalr
- Update the control unit and test bench as necessary for this instruction.
-
Datapath
- This one will be a little easier than
jal
, but might require editing the mux between the branch target adder and the PC register. - You will probably need to use the same register "cache" trick for linking as you did for
jal
.
- This one will be a little easier than
-
Test Bench.
- Even though this is technically an I-type instruction, you should make a file
memory-JALR.txt
to test your jalr instruction. - Add a test to the I-type test bench for jalr. (Be sure your test bench loads the right memory file when it gets to
jalr
testing).
- Even though this is technically an I-type instruction, you should make a file
4 Testing strategy
(Q) On the practical worksheet, answer the Correctness question about how you chose to design the tests for your testbenches. This is on page 8.
5 Design and Implement a new instruction
Before you begin this part, be sure to add, commit, and push any work you've done to git.
With your team, design a new instruction that's not currently supported by RISC-V. It can have a different format and do pretty much whatever you want.
For ideas, consult your instructor.
-
Design the instruction
- (Q) On the practical questions sheet, identify the command name, syntax, and purpose/usage of your new instruction.
- (Q) Draw the instruction format on the practical questions sheet, showing opcode and any other fields. Clearly label each field and it's size (like we did in class).
- (Q) Write RTL for your new instruction on the practical questions sheet.
- (Q) Edit and highlight the datapath on the practical questions sheet necessary to implement your new instruction.
- Plan out your test bench to ensure substantial testing.
-
Once you've fully designed the instruction, implement it.
- Control: Update your
Control.v
unit to support your new instruction. Be sure to write a test like you did for the other instructions. - Datapath: Update
Processor.v
to support your new instruction. - Test Bench.
- Create a memory file with an assembled version of your new instruction to test.
- Add your tests to
tb_Processor.v
- Your memory file can also contain other instructions (you may want this to "set up" a test for your instruction)
- We recommend updating one of your team's assemblers to assemble your new instruction to both (a) check that the instruction format and any addressing modes work, and (b) easily create an assembled machine code version of your instruction.
- Control: Update your
-
Document it
- (Q) Take a screenshot in ModelSim running your test for the new instruction. The waveform should be easy to read, and include the console transcript showing the instructions running for the task. Put this into the worksheet.
- (Q) Answer the Correctness question about how you chose to design the test for this new instruction.
Submission and Grading
Functional Requirements
At the end of the practical you should have done these things:
- Update
Processor.v
andControl.v
to support:lui
/jal
/jalr
/ Your new instruction
- Create individual
memory-X.txt
files that contain the instructions for each testbench task intb_Processor
:memory-U.txt
/memory-UJ.txt
/memory-jalr.txt
/memory-newInstrName.txt
- Implement testbench tasks in
tb_Processor
using thememory-X.txt
files:test_U_types
/test_UJ_types
/test_jalr
/test_newInstrName
- Completed and submitted 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 4 commits in your repo for this practical:
- Git commit 1: upon completion and tested
lui
- Git commit 2: upon completion and tested
jal
- Git commit 3: upon completion and tested
jalr
- Git commit 4: upon completion and tested your team's new instruction
Since this is a team-based practical, there should be numerous iterative commits from each team member. The commits listed above are a minimum set.
Remember, Do not add and commit every single file ModelSim creates. Only add, commit, and push .v, .do, and .mpf files.
Commit and push via git either using VSCode’s built-in source control or with the git bash terminal. Be sure to copy your final commit ID number for the final question on the worksheet. This ID number can be found on the commit history tab on your Github repository.
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.
(Q) Complete the practical worksheet. Specifically, complete the questions on pages 12-14, including the one that asks for your commit ID.
Final Checklist
- Verify that your code compiles and your tests pass (or at least run).
- Verify your verilog code is committed and the commits are pushed to github.
- Submit your completed worksheet to gradescope.
Grading Breakdown
Practical 6 Rubric items | Possible Points | Weight |
---|---|---|
Worksheet | 96 | 50% |
Code | 90 | 50% |
Total out of | 100% |