Quartus/Modelsim Tips and Update for Lab2b

Written by Prof. Michael Taylor 5/1/2010

From previous lab. Includes suggestions for many problems faced by students.

 

Behavioral Simulation:

A bunch of folks have noticed that they could get timing simulation working but not behavioral. Although timing simulation is ultimately the "final test" that your design works, behavioral is a lot more effective for debugging. Here's the procedure for behavioral; also there are a bunch of useful tips interspersed:

From Quartus, run ModelSim by doing Tools->Run EDA Simluation Tool->EDA RTL Simluation. Create a new ModelSim project, and add your *.v source files to the ModelSim project (note: do not add any .sdo files; those are for timing simulation only and will result in "Failed to Find Instance" errors.) same ones that were in your Quartus project. Do NOT add _bb.v or _syn.v files. Also add the .tfw testbench file you want to use.

Here's the big mystery: You need to add two files to support the macros generated by MegaWizard. They are:
C:/altera/91sp2/quartus/eda/sim_lib/altera_mf.v
C:/altera/91sp2/quartus/eda/sim_lib/220model.v
Using the _bb.v files were causing the high-impedance output issue.

If you see instantiation errors for altsyncram, it's probably because you ommitted these files.

To simulate, go to Processes Window; Select Compile->All; then Simulate->Start Simulation...; Select Work/Test (or whatever your test is called); OK. ModelSim seems to be all about chasing after windows. A useful tip is, to switch from your current window to another one, select Window->(the window you want.) The most useful ones after you have started your simulation are the "sim" window and the "wave" window. You drag items from the "sim" window over to the left side of the wave window. Most useful to drag are UUT, ram, and fetch_fifo.; This will show most of your signals, correlated over time. This is the main way that you debug hard bugs. One of the first things you want to do is make sure you have a reasonable time scale. You may be zoomed in or out too much. The best way to calibrate is to look for your clock signal; but to do that, you first need to simulate for a little. There's a text box that usually starts out as "0 ps"; you can change that to "200 ns" or so, and that will cause your machine to simulate 10 or so cycles when you push the first button to the right of the text box (mouse-over and you will see a tooltip that says "Run"). After you've clicked on that once or twice, then right click on the wave window to get a menu that will let you zoom in and out. You want to adjust the zoom so the time scale is in 10's of ns or so, and you can see your clock waveform.

The test_new.tfw and branch_new.tfw testbenches, which seek to initialize the memory internally instead of using the .hex file, don't work for behavioral simulation because of two subtle bugs. The first bug is that the first word of the ram is attempted to be written before the clock starts. As a result, word 0 of the RAM is not initialized correctly, which creates issues. The other issue is that the input vectors are being toggled slower than the clock, which results in a different test pattern. However, you can get the original testbench working if you use a valid .hex file (see next items.)

For your testbench, watch out when your testbench changes inputs to your module via #(delay), where those delays fall on the positive edge of the clock. This can cause race conditions. It's safer to assert inputs on the negative edge of the clock (I believe you can even use the construct @ (negedge clk) inside your initial statements; or you can calculate the delay relative to the parameter you used to define the clock period.) I believe the original testbenches were probably okay for this.

Generating hex files. The lab2a.hex file provided in the latest release should be fine to use. You can also generate a .hex file of your own (in this case the .hex files was just 64 17-bit values, starting at 0 and increasing by 1) inside Quartus via New->Hexadecimal (Intel-Format) File, and then filling in the values you want and saving. This is what I did.

Using hex files. If ModelSim can't find your hex file, it will complain after you first try to step some time in your simulation (see above for how to do that.) You can see the error by choosing Window->Transcript (or sometimes Window->More..->Transcript.) If there's no error, it probably found the file. By default, the generated ram files have the last person's path in there for the location of the hex file. In quartus, you can change this path by double clicking on the MegaWizard ram file (e.g.Window->Project Navigator; underneath "Fetch", double click on "ram_17_1024b".) You want to leave the defaults; so just click Next->Next and you should see a path listed for the "Initial Content of Memory". You have two choices .. change it to an absolute path, or, put it in the same directory as your Quartus project, using just the file name (i.e. relative path), and then copy the file into the same directory as your model sim project (typically simulation/modelsim/). Quartus does a funny thing if you try to specify the full path of your default directory -- it won't let you keep the fully qualified path; so I ended up using the copy trick.

FIFO:

The Altera MegaWizard FIFOs by default do not have a "deque" signal, but actually have a read request signal instead. The semantics are that the word does not appear until _after_ the read request is asserted. If you edit the file via MegaWizard (like I showed for the ram file), there's an option to make the semantics be the deque semantics we wanted. However, if you look at the manual for this FIFO, it turns out that it has quite a long latency (2-3 cycles). This means our test bench probably would not be correct for it.
As a result, I hacked up a new FIFO, which is in the final lab release. To use it, remove both the old fifo.v AND fifo_d.v. This new FIFO is made out of LUTs and does not take up blockrams (M4Ks). Note: the size is configurable, but currently set to 7 elements (1 element is always wasted.) This may affect the behavior of the testbenches versus the old design, if they are testing "fullness".


For the new FIFO, the inputs are registered but the outputs are not. The head element of the FIFO (including data and valid signal) stays asserted on the output lines until it is dequed.
So the first value that arrives at the input flop to the FIFO will appear on the addr/data/valid lines immediately after the first positive clock edge. If in that cycle, you assert deque, then the next cycle, the second value will appear on the addr/data lines, and so forth. For the previous design, the value would not appear on the addr/data lines until after you asserted the "rdreq" (mislabled deque) signal. So there would be an additional cycle of latency, not to mention that if the backend logic was looking for a instruction_valid signal before asserting deque, it would never see it.

Debugging:

Typically, you scan for the "restart" signal, and then check each pipeline stage to see if the correct data is appearing. Things to check for first are "high impedance" signals (typically not connected) and the "spread of red". The spread of red is basically undefined or unitialized signals that propagate through your circuit. You want to look back and find the earliest signal that is red that should not be.Then you jump to the verilog and trace the derivation of that signal back further.
You can add $display statements in your testbench to display state, or even to print out error messages (see my fifo.v example, which checks for under/over flow of the fifo). The synopsys translate off tells your synthesizer to ignore those statements when generating a circuit; but the statements work fine in behavioral simulation. Note that the output shows up in the "Transcript" Window. You can also use $monitor statements to print out values whenever they change. This is nice if you're more into printf-style debugging than using the waveform viewer (typically for the really hairy timing related bugs, you need the waveform.)
You probably didn't know this, but in behavioral testbenches, you don't need to route signals to the top level, you can just traverse the hierarchy with ".". E.g.

$monitor("rptr: %x",UUT.fetch_fifo.rptr_r);

which reads a signal (UUT.fetch_fifo.rptr_r) three modules deep.

Gate-Level (or Timing) Simulation:

After you have debugged your design with behavioral simulation (this is easier because you can see all of the signals inside your design), then it's time to move to gate-level simulation. This verifies that your design works at the speed that your testbench drives it at.

0. Make sure you've debugged everything in behavioral simulation.

1. First, in Quartus, make sure Quartus is outputing .vo files (Verilog) and not .vho (VHDL) files. You can do this by going to "EDA Netlist Writer" (it's in the "Task" pane, beneath Assembler and Classic Timing Analysis.) Double click on "Edit Settings." For Simulation, it should say "ModelSim-Altera and Verilog HDL". If it does not, double click on that line, and fix it.

2. Do a complete compile of your design. Check transcript window for errors. If you used x.y.z to peek inside your module during behavioral simulation, you will have to guard it with an

`ifdef BEHAVIORAL

`endif

and then do a

`define BEHAVIORAL

when you want to do behavioral simulation and enable that stuff.

3. Click Tools->Run EDA Simulation Tool->EDA Gate Level Simulation... YOU MUST invoke ModelSim from Quartus otherwise you will have issues related to unfound files. If you gives you some error about a file, it's maybe because you already have a project open in Modelsim. ModelSim will startup.

4. In ModelSim project pane, File->New->Project.. Enter in a name, then Click "Add Existing File" and find your .vo file (this is your entire post-synthesis design compiled into one verilog file), and your .tfw (test bench verilog file).

5. Do a Compile->Compile All. Check transcript window to ensure no errors.

6. Simulate->Start Simulation... Under libraries, select "browse.." and add C:\altera91\modelsim_ase\altera\verilog\cycloneii (path may be slightly different for you.) Under SDF, click "Add.." and add your _v.sdo file (I found it in the simulation/modelsim subdirectory of my project), and enter in the box "Apply to Region" /UUT (that's the instance name of your module in the .tfw file, if you see errors about "Failed to find INSTANCE", this is what's missing.) Under design tab, select your test bench file (usually under the "work" flippy-plus tab).

7. You should see a flurry of motion from ModelSim. Check the Transcript window to see if there were any errors.

8. Now you are just about ready to simulation -- drag "test" (the module name of your test bench) to the "wave" window. Since this is gate-level simulation, it will be harder to see signals inside your actual module, so you are mostly restricted to signals outside the test. Typically, you will only be able to decipher registers, and even then it will be messy, but not impossible.

9. At this point, it's basically just like behavioral simulation.