4. Simulating the Core

The Chromite repository also contains a simple test-soc for the purpose of simulating applications and verifying the core.

To create a verilated executable follow the steps in Section 3 and then run the following command:

$ make link_verilator_elf

This will generate a bin folder containing the verilated chromite_core executable. This can be used for simulation as described in Section 4.

Please note that the bram-based memory in the test-bench can only hold upto 32MB of code. Thus the elf2hex arguments will need to applied accordingly.

Note

The elf2hex program is available from the modified spike application.

Note

The size of the BRAM Memory can be changed by changing the configuration bsc_compile_options.test_memory_size in the configuration YAML.

4.1. BootRom Content

By default, on system-reset the core will always jump to 0x1000 which is mapped to the bootrom. The bootrom is initialized using the file boot.mem. The bootrom after a few instructions causes a re-direction jump to address 0x80000000 where the application program is expected to be. It is thus required that all programs are linked with text-section begining at 0x80000000. The rest of the boot-rom holds a dummy device-tree-string information.

To boot.mem file is generated in the bin folder using the following command:

$ make generate_boot_files

Tip

You can skip executing the bootrom by changing the reset_pc field in the configuration YAML. However, the verilated executable will still require a dummy boot.mem file to initiate simulation

4.1.1. Verilated Executable

We use verilator to simulate the core and the test-soc described above. In order to generate the verilated executable do the following (you can skip this is you have already followed the steps so far)

$ cd chromite
$ python -m configure.main -ispec sample_config/default.yaml
$ make -j<jobs> generate_verilog
$ make link_verilator_elf generate_boot_files

The above should result in following files in the bin folder:

  • chromite_core

  • boot.mem

4.1.2. Executing User Programs

Let’s assume the software program that you would like to simulate on the core is called prog.elf (compiled using standard riscv-gcc).

In order to run this elf on the chromite verilated executable you can do the following:

$ ./chromite_core +elf=<path to prog.elf>

Note

Since the boot code in the bootrom implicitly jumps to 0x80000000 the programs should also be compiled at 0x80000000.

4.2. Hello World

To run hello-world first ensure the verilated executable is available in the bin folder (use steps mentionedin in Section 4.1.1. After which run the following:

$ make hello
Hello World

4.3. Dhrystone

To run dhrystone first ensure the verilated executable is available in the bin folder (use steps mentionedin in Section 4.1.1. After which run the following:

$ make dhrystone ITERATIONS=10000

Microseconds for one run through Dhrystone:     10.0
Dhrystones per Second:                       98448.0

Note

The above numbers are obtained by using the samples/rv64imacsu/* config files which have been configured for high performance. The performance numbers will change based on the config used to generate the core instance.

4.4. CoreMarks

To run coremarks first ensure the verilated executable is available in the bin folder (use steps mentionedin in Section 4.1.1. After which run the following:

$ make coremarks ITERATIONS=35

2K performance run parameters for coremark.
CoreMark Size    : 666
Total ticks      : 12323206
Total time (secs): 12
Iterations/Sec   : 2
Iterations       : 35
Compiler version : riscv64-unknown-elf-9.2.0
Compiler flags   : -mcmodel=medany -DCUSTOM -DPERFORMANCE_RUN=1 -DMAIN_HAS_NOARGC=1 \
                   -DHAS_STDIO -DHAS_PRINTF -DHAS_TIME_H -DUSE_CLOCK -DHAS_FLOAT=0 \
                   -DITERATIONS=35 -O3 -fno-common -funroll-loops -finline-functions \
                   -fselective-scheduling -falign-functions=16 -falign-jumps=4 \
                   -falign-loops=4 -finline-limit=1000 -nostartfiles -nostdlib \
                   -ffast-math -fno-builtin-printf -march=rv64imafdc -mexplicit-relocs
Memory location  : STACK
seedcrc          : 0xe9f5
[0]crclist       : 0xe714
[0]crcmatrix     : 0x1fd7
[0]crcstate      : 0x8e3a
[0]crcfinal      : 0xcf56
Correct operation validated. See README.md for run and reporting rules.

Note

The above numbers are obtained by using the samples/default.yaml config file which has been configured for high performance. The performance numbers will change based on the config used to generate the core instance.

4.4.1. Notes on Simulation

4.5. Support for PutChar

The test-soc for simulation contains a simple uart. The putchar function for the same is available HERE. This has to be used in the printf functions. The output of the putchar is captured in a separate file app_log during simulation.

4.6. Simulation Arguments (plusargs)

  1. ./chromite_core +elf=<elf-path> +rtldump: if the core has been configured with trace_dump: true , then a rtl.dump file is created which shows the log of instruction execution. Each line in the file has the following format:

    <privilege-mode> <program-counter> <instruction> <register-updated> <register value>
    
  2. To enable printing of debug statements from the bluespec code, one can pass custom logger arguments to the simulation binary as follows

    • ./chromite_core +elf=<elf-path> +fullverbose: prints all the logger statements across all modules and all levels of verbosity

    • ./chromite_core +elf=<elf-path> +mstage1 +l0: prints all the logger statements within module stage1 which are at verbosity level 0.

    • ./chromite_core +elf=<elf-path> +mstage2 +mstage4 +l0 +l3: prints all the logger statements within modules stage2 and stage4 which are at verbosity levels 0 and 3 only.

  3. To print the cycle difference between 2 consecutive instruction commits you can use

+instr_latency along with rtldump to appen this in the rtl.dump file itself.

  1. To exit the simulation when a self-loop is encountered use +halt_selfloop with the compiled binary

  2. An app_log file is also created which captures the output of the uart, typically used in the putchar function in C/C++ codes as mentioned above.

4.7. Connect to GDB in Simulation

A debugger implementation following the riscv-debug-draft-014 has been integrated with the core. This can be instantiated in the design by configuring with: debugger_support: true

Perform the following steps to connect to the core executable with a gdb terminal. This assumes you have installed openocd and is available as part of you $PATH variable.

Modify the sample_config/default.yaml to enable: debugger_support and open_ocd. Generate a new executable with this config to support jtag remote-bitbang in the test-bench

$ python -m configure.main -ispec sample_config/default.yaml
$ make gdb # generate executable with open-ocd vpi enabled in the test-bench
  1. Simulate the RTL In a new terminal do the following:

    $ cd chromite/bin/
    $ ./chromite_core +elf=<elf-path> > /dev/null
    
  2. Connect to OpenOCD Open a new terminal and type the following:

    $ cd chromite/test_soc/gdb_setup/
    $ openocd -f shakti_ocd.cfg
    
  3. Connect to GDB Open yet another terminal and type the following:

    $ cd chromite/test_soc/gdb_setup
    $ riscv64-unknown-elf-gdb -x gdb.script
    

In this window you can now perform gdb commands like : set $pc, i r, etc

To reset the SoC via the debugger you can execute the following within the gdb shell:

$ monitor reset halt
$ monitor gdb_sync
$ stepi
$ i r

Note

The above will not reset memories like the BRAM Memory.