In Lab 2, your group will build a single-cycle processor, like the one described in Chapter 5 of COD. You will create the design in Verilog. The processor will run in simulation (ModelSim) and on real hardware (Xilinx). You will create a test plan for your processor. You will use the test plan for bug diagnosis, and to verify that your processor executes a subset of the MIPS instruction set.
Lab 2 has several "checkoffs" and a final deadline:
On checkoff days, be prepared to demo by the time your section begins! The sooner you begin, the more time you will have to recover from problems. Also, be sure to follow the specifications in this report (and in the MIPS ISA manual in the Resources section). A working processor that doesn't correctly implement all of the instructions will fail the checkoff.
Lab Report Submission Policies: To submit your lab report, run m:\bin\submit-fall2006.exe, or at command prompt type "submit-fall2006.exe" then follow the instructions. The required format for lab reports is shown on the resources page, as is the required format for your design notebook.
Lab 2 Document History:
Do these tasks before your group begins the design.
During the first week of the course, we will be forming project groups. After your first checkoff meeting on Friday 9/1, you should be a member of a group with 4 or 5 members. Once group membership is set, your group should send email to cs152-st aff@cory register the group.
Your group will elect a spokesperson for Lab 2. The responsibility of the spokesperson is to communicate questions to the TA and reply to questions from the TA. Spokesperson duties rotate with each Lab assignment; for each lab, your group will elect a different spokesperson.
Your group will prepare a design document. The design document will be 2-4 pages in length, and will contain:
See the start of this document for the deadlines associated with the design document (preliminary submission, TA review, and final submission).
As part of this lab, your group will keep an on-line notebook. An example notebook appears in the resource section of the class website, and in the slides for the 9/7 lecture (available on the calendar website page). Each member should keep a separate notebook, stored as files (or subdirectories of files) under a global notebook directory.
Be sure to follow these steps when keeping your notebook:
The most important thing to put in your design notebook is documentation of how to do things, where files are, etc. Without documentation of this kind, your group members may need to find you (wake you up, force you to come into the lab, etc) in order to continue working on the project -- perhaps in the middle of the night!
For this lab (and all future labs) you should use cvs (or another a source code control system). We have included cvs on the instructional machines in M:\bin\cvs.exe (version 1.11.12). (Watch out, there is another version of cvs (1.11.5) that has been compiled for cygwin that is in c:\cygwin\bin. This version will not work if you are not running in the cygwin environment; it will complain about windows-style paths. So make sure that you are running the version in M:\bin.)
If you have no idea how to use cvs then feel free to look at a couple of tutorials on how to use cvs: one for windows and one for unix (plus general usage).
We require you to use CVS keyword substitution in your Verilog source files (don't try to use keywords in things like project files or schematics, as the keywords are not preserved by the CAD tools). You should place the keywords at the top of the file. You should include the author as well as the version number and the check-in time. We also recommend using either $Id$ or $Header$.
Source control systems are not perfect -- in some situations, they may damage or lose files due to corruption issues.
To prevent lost work, be paranoid! Source code control should not be your only tool to protect the integrity of your project. Back up your design files at regular intervals, and store them off-site -- for example, in Spring 05 one group sent all of their Verilog to a gmail account every night, and used the gmail search facility to recover files.
In this lab, you will be synthesizing ChipScope into your Xilinx design, so that you can monitor the operation of your processor on the board. To learn about ChipScope, see the "Tutorial For the Xilinx Tool Flow" paper on the Resources section of the website. You may wish to do a simple test design using ChipScope at this time, so that you don't have to learn about it as you are debugging your processor.
The tool flow tutorial also contains other information about the tools that may be helpful in Lab 2. In particular, be sure to read the final section ("5. Tips and Hints").
The following components are provided for this assignment and future ones. The Verilog descriptions of these components are located in the Lab 2 directory (M:\lab2).
A description of these components, LAB2_Help, also appears in the directory.
You may use the MIPSASM program to transfer MIPS machine code into the memory component, for both simulation and Xilinx execution. Like any software tool, MIPSASM may not always generate perfect code. If you are struggling with a bug, you might check the binary output from MIPSASM to make sure it is correctly translating assembly language code to binary.
To use the components, begin by copying scram.v and C_REG_FD_V6_0.v to your working directory. Make two new top-level ramblocks, called "instmem()" and "datamem()" as shown in LAB2_Help. For your instruction memory, use an initialization file called "instmem.smallcontents". For your data memory, use a file called "datamem.smallcontents".
A .smallcontents file consists of up to 128 lines of 8 hexadecimal characters. Each line of the file will be loaded into the memory in the order that it appears in the file. (eg. line 1 will be placed in mem, line 2 will be placed in mem, etc.) An example file (example.smallcontents) has been placed in M:\lab2 for your perusal. It initializes the RAM with ascending numbers beginning at 0. Note that .smallcontents files are used for simulation only.
For instance, you should do the following:
Design unit test benches (described in the testing lecture) for each of component listed in Problem 1b. Depending on the complexity of the component, your test benches may use a mix of random, directed, and directed random tests. Your directed (or directed random) tests should test a wide range of corner cases. You may wish to use Verilog file I/O primitives to log errors caught by your test benches (look at section 17.2 of the IEEE spec on the resources page).
A test bench for a component should not be written by the person who wrote that component. Hopefully, the unit test bench author will think of corner conditions that the designer did not. Note that a unit test bench is only used during the unit testing phase for a component, and is not wired into your processor.
Turn in the output from your test benches that illustrate that your components are working.
Using the components listed above, build a single cycle datapath in Verilog that implements the following subset of MIPS instructions: addu, addiu, subu, ori, lw, lui, sll, srl, slt, sw, jr, beq (these are a subset of the instructions you used in Lab 1--you will implement the remainder during lab 3). Note that the CPU design in this lab does not use delay slots (i.e. branch delay slots, jump delay slots, or load delay slots). Make sure that your design is clean -- you will be breaking it apart to build a pipelined processor in future assignments. At this time, do not implement the PC and the instruction memory. Note that unlike commercial implementations, your processor does not implement exception handling. So, if an instruction other than the ones listed above appears in the instruction stream, what your processor does is undefined by this spec (a practical option is to treat undefined instructions as no-ops).
To test your datapath, build a multi-unit test bench (described in the Testing lecture).
This file is a top-level Verilog file that instantiates your datapath, along with a test bench. Conceptually, the test bench is a single initial block with an inline list of test cases. In practice, it may be cleaner to use file I/O for test data from separate files.
The test bench drives the datapath control signals, the instruction value, and in some cases, the result signals (the destination register, the equal? signal, etc). Each of these test cases takes the form:
// Test case #1: Make sure that register read works
A=something; B=somethingelse; CLK = 0;
#100 // Wait for datapath to settle
if (Foo!=something|| )
$display("Failure for Test case #1\n");
// Test case #2,
Turn in a copy of your multi-unit test bench, and a log of your simulation showing that the instructions worked. The objective of this problem is to make sure that you have all the parts of the datapath in place to execute all the instructions, and that you have a clear idea of what the control signals have to be for each instruction.
In this section, you will build the controller for your datapath. You will use Verilog to create the control module. Label the pins of the control module appropriately, and connect it to the datapath you built in problem 1. This controller receives the bits from the 32-bit instruction as input, and uses a combinational circuit to generate the control signals.
Also, add the instruction memory, PC, and the next instruction logic to your design, by modifying your schematic.
Next, build a special Verilog entity that takes as input the clock, the instruction address, the output of the instruction memory, the output of the registers, and the destination register. This will be a monitor module which will monitor the execution your processor. It should output information in textual format, using the "$display" statement or using file I/O to output to a diagnostic trace file. In the interests of making grading and checkoff easier, we are asking everybody to have the exact same monitor module format. Please follow the format below:
0x00000000: addu $3, $4, $5
$3=0x7978C754, $4=0x34235432, $5=0x45557322
0x00000004: addiu $6, $7, 100 $6=0x45557386, $7=0x45557322
Your monitor should be able to disassemble the complete set of instructions above and print out the course of execution. This monitor will show you in graphical form that your processor is "doing the right thing". It should print a new line at the falling edge of the clock (this is the only thing that should run off the negedge). Note that $display statements are much like C printf() statements, and include support string formatting.
Design your monitor module in a flexible and structured way; later in the term, you will modify the module to add pipelining support.
Note that this module is used in simulation only. You should instantiate the monitor module in your complete processor test bench, not in your processor Verilog file. Use Verilog's hierarchal referencing feature to access registers/wires of the processor from the test bench. We prefer this method over "hardwiring" the monitor module into the processor, because the more you edit your processor Verilog post-simulation (to "un-hardwire" the monitor), the greater the odds of introducing new bugs on the way to the Calinx board!
Wire together your datapath, control path, and memory modules. Make sure to keep track of the instance names for your memory modules. These will form a complete path that you can use in the simulator to examine memory during simulation.
For instance, let's say that you called the scram in your instmem() module "tempBlock" and then called the block "MyInstMem" at the data path level. Then, you will be able to refer to the memory block as "MyInstMem/tempBlock/inst/ram_data" in ModelSim. Note that the middle "inst" and "ram_data" pieces are not under your control (they are inside the "scram" and " C_DIST_MEM_V6_0" blocks.
Also assemble a "processor with self-checks" module at this time. The module may be simple, and check for conditions that should never happen in the datapath and control (easy to do), or may be more sophisticated and check aspects of correctness. Don't over-do it: it is easy to add more checks to a well-written "processor with self checks". However, it is harder to create one from scratch after debugging begins, which is why we recommend you do it now.
Make a top-level testing module to drive your processor. Make it easy to switch between you "normal" processor and your self-checking processor. Remember to instantiate your monitor module into the testing module.
The top-level testing module should have a clearly-defined clock generator defined as an always loop with a delay statement (like #100). For example:
CLK = ~CLK;
Follow the directions in LAB2_Help for loading your memory blocks into simulation. (The loader may have problems if your initial clock phase is a "1" -- if so, try initializing your clock to "0"). Recall that in 1a we made the memory contents be "instmem.smallcontents" and "datamem.smallcontents". You should be able to go into the command mode of ModelSim to set breakpoints at points of your clock cycle and thereby step cycle by cycle.
Write a suite of test programs to run on your "complete processor testing" and "processor testing with self-checks" test bench. You may reuse code from the programs you wrote in Lab 1. However, remember that a single cycle processor does not have delay slots, and you have not implemented the complete subset of instructions from Lab 1. In later labs, you will be adding more instructions to your processor and will be able to use all of the code from Lab 1.
Note that unlike spim, your ModelSim simulation won't be able to show the contents of the registers in a constantly updating GUI. However, you may use ModelSim to monitor waveforms.
If you need more diagnostics to fully test your processor, add them to your testing software in a disciplined way: add more self-checks to your self-checking processor, or create (or modify) unit test benches and multi-unit test benches as need be. Don't randomly add test code to the processor Verilog files themselves.
An observation from previous years: most groups fail the checkoff the first time, because the semantics of one of the instructions is tricky to get perfect. So, read over the MIPS ISA manual page for each of the 12 instructions very carefully, and make sure each instruction will work in all situations (positive and negative register values, postive and negative overflow, etc).
Turn in a copy of your Verilog code, a schematic drawing of your datapath (to assist the TAs in understanding your design), test bench Verilog, your mini-MIPS test program suite, a simulation log that shows the correct execution of the program, and your Design Notebooks.
After you have finished debugging your processor it is time to push to board. You will have to generate a special ram file for use on board, see LAB2_Help.pdf for more information on how to do this.
You may be wondering how to determine if your processor actually works on the board. You should use ChipScope to inspect the register file as the program runs to figure out what is happening. In particular your ChipScope module should trigger on the PC and capture the status of registers 1 to 6 in your register file as well as the output from the instruction register. You may use other ChipScope modules to debug your processor, but you should have at least one setup in this manner so that you can be checked off.
Note that you will need to directly add ChipScope to your processor Verilog, and this will impact your ability to go back and do more ModelSim simulation. If possible, add ChipScope to your processor in a way that minimizes the amount of hand-editing needed to enable and disable ChipScope.
You should not have to worry about your clock rate, because the clock that we are providing you with has a frequency of several hundred KHz.
In previous years, some students had trouble getting their CPU running on the board, because Synplify would decide that the entire processor was "dead Verilog code" and would remove it from the design! You can fix this problem by connecting at least one output from your processor to logic that ends up driving an LED on the board -- for example, wire a CPU register to the 7-segment LED display pins.
You should use the MIPSASM tool to generate synchronous write / asynchronous read RAM data for use on the board (you can find these options under "Advanced"). Define a flow to incorporate the output of this program into your processor that is resistant to manual errors (copy/paste into a Verilog file may be inevitable, but a file should be dedicated to this purpose, so that editing bugs do not wreak havoc with your design files!).