[4.1] Debugging the core using behavioral simulation

Ask questions or leave feedback about release 4.x here.
Post Reply
jvanstraten
Posts: 6
Joined: Mon Feb 20, 2017 3:44 pm

[4.1] Debugging the core using behavioral simulation

Post by jvanstraten » Fri Feb 24, 2017 1:01 pm

When debugging the core, the most important (simulation-only) signal is the "rv2sim" output. This signal contains disassembly of the fetched instructions, the data committed to the register files and data memory, the reason for having selected the current program counter (normal flow, branch, trap, halting in preparation for reconfiguration, etc.), and implicitly the current configuration. This should almost always be enough, without cluttering up the wave view with countless internal signals. There are however some other signals that could prove useful.

Monitoring the register files

To monitor the branch registers, use "cxreg_inst/cr_ccr_branch_r". This is a two-dimensional array; the first index is the context ID, the second index is the register. To monitor the link register, use "cxreg_inst/cr_lr_lr_r". This is also a two-dimensional array, where the first index is the context ID again, and the second index is the bit within the 32-bit value. Keep in mind that these values are only written in S_SWB; the forwarding logic is not included here.

Monitoring the state of the general purpose register file is a little more complicated. The BRAM-based synthesis version of the register file is particularly difficult to monitor, because you would have to constantly cross-reference the live-value table contents with the BRAM data. To make this easier, we also have a simulation-only mockup. This mockup can be enabled in core_intIface_pkg.vhd using the SIM_FULL_GPREG_FILE constant. If this boolean is set to false, the mockup will be used for simulation. It does not affect synthesis. The general purpose register file state can now be monitored using "gpreg_inst/sim_mem_gen/sim_mem/ram". The first index is the register index plus context ID times 64, the second index is the bit within the 32-bit register. Note that $r0.0 behaves as a normal register for as far as the register file is concerned; the value is overridden with 0 when it is read in pipelane.vhd. As with the other registers, keep in mind that forwarding is not taken into consideration. The general purpose register file is read in the S_WB stage.

Monitoring the control registers

Most of the context and global control registers can be monitored directly, others have to be monitored indirectly. The signals can be found in the "cxreg_inst" and "gbreg_inst" instances. The naming convention is "cr_<register mnemonic>_<field mnemonic>_r". Refer to the user manual in the doc folder for figuring out what register does what.

Some control registers do not have a "_r" signal, or have one that does not 100% match the layout of the control register as perceived by the debug bus. They might do some post-processing combinatorially when read, or may have no need to store values written to them. For these registers, you may need to look in the config/creg folder to see how they work. The syntax of the latex-like configuration files is documented in the user manual.

Monitoring the pipeline registers

If the state signals above are still not enough to solve the issue, chances are that you want to debug the pipeline in core_pipelane.vhd, as this entity contains most of the datapath. It is also a very large file that can be daunting at first, but hopefully you'll find that it isn't as complicated as it seems. Before continuing, you should read the "read me please" comment section at the top of core_pipelane.vhd, so you know how the pipeline is modeled. Now that that's out of the way, go ahead and add the "pls_inst/pl_gen(<lane>)/pl_inst/si" signal to the wave viewer for the lane(s) that you're interested in. This signal represents the output of the registers and the input of the combinatorial logic. You can also use the "so" signal if you prefer, which is the output of the combinatorial logic. As explained in the readme section of core_pipelane.vhd, the first array index is the pipeline stage index. You then end up in the "syllableState_type" record type, declared in the architecture header of core_pipelane.vhd, containing (almost) the full pipeline state information for its lane.

Signals which have logically not yet been assigned a value in the monitored stage of the pipeline will usually be initialized with the RVEX_UNDEF constant, defined in core_intIface_pkg.vhd. It should be set to 'U' upon release to make the undefinedness explicit. However, some synthesis tools have issues with 'U', in which case you may want to set it to '0'. We may also forget to set it to 'U' when we release because we hardly ever have to debug the core in this way nowadays though, so if you're debugging like this it may be worth to check it.

Register consistency check (find differences between two core versions)

When changing the core, you can sometimes run into problems which only appear in programs that are too long to look through manually. In some cases, the register consistency check system can point out where things start going wrong. Your case has to meet the following conditions:
  • You have a version of the core where a certain program runs correctly and a version where it does not. The binaries run on both versions of the core must be identical.
  • There should not be ANY differences between the runs on either platform. Waiting on a UART peripheral or servicing interrupts in such a way that the control flow differs will trigger a false-positive check error. Spurious reconfigurations should be okay, though.
  • You don't do multithreading, or it is sufficient to only check one context.
The system works by first serializing all register file write accesses to a file using an older version of the core that runs the code correctly, and then doing the same operations on the modified core but instead comparing the serialized data with the existing file. The compare process will report an error for the first mismatch, along with what the actual difference is, or when the consistency check file ends. Note that the consistency check stops after the first mismatch, as the control flow may be dependent on the incorrect data. This would cause a long stream of useless error messages, polluting the simulation log.

The consistency check is activated using the following generics passed to the ρ-VEX core: RCC_RECORD, RCC_CHECK, and RCC_CTXT. First, when running the simulation with the version of the core that runs the program correctly, set RCC_RECORD to a string specifying the filename in which the register access data is to be recorded. Then, for the to-be-debugged version of the core, set RCC_CHECK to the same filename. RCC_CTXT specifies which context is recorded or checked.

It is not currently possible to check hardware trace data from the processor against an RCC file. However, all necessary data can be recorded in a hardware trace, so making something that does this should not be too complicated.

Supported simulators

We only verify that the simulation works with Modelsim 10.2a. Of course you're free to try other simulators, but there are no guarantees. In fact, we know that the following simulators do NOT work:
  • Xilinx ISIM: reports an index-out-of-range error somewhere in pipelane.vhd for some unknown reason, known to segfault sporadically before the index-out-of-range started happening.
  • Xilinx Vivado: segfaults.
  • Older Modelsim versions: known to segfault during elaboration sporadically.
We're not sure exactly why they don't work, but as the core simulates properly in 10.2a and also synthesizes properly, we've been attributing it to bugs in the simulators. If you have a workaround or a fix, please tell us so we can incorporate it in the next release.


If you have questions, feel free to ask them in this thread or to create a new thread.

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest