You are assumed to have the following correctly built:
riscv64-unknown-linux-gnu-
toolchain- bbl binary at
$OUT/bbl
- linux kernel at
$OUT/vmlinux
- linux kernel at
- disk image at
$OUT/riscv_disk
- In gem5: using
configs/example/riscv/fs_linux.py
or replace it withsetup/fs_linux.py
for a bit more flexibility (will be merged by next release)
Please customize the following commands according to your likings (binary type, debug flags, cpu type etc.):
DTB automatically generated
cd $G5
build/RISCV/gem5.opt --debug-flags=Clint -d $RISCV/logs -re configs/example/riscv/fs_linux.py --kernel=$OUT/bbl --caches --mem-size=256MB --mem-type=DDR4_2400_8x8 --cpu-type=AtomicSimpleCPU --disk-image=$OUT/riscv_disk -n 1
DTB compiled into BBL
Precompiled BBL and devicetree for single / double core are given under the prebuilt/single_cpu
and prebuilt/dual_cpu
folders. Add the --bare-metal
flag when DTB generation is not needed.
cd $G5
build/RISCV/gem5.opt --debug-flags=Clint -d $RISCV/logs -re configs/example/riscv/fs_linux.py --kernel=$OUT/bbl_single --caches --mem-size=256MB --mem-type=DDR4_2400_8x8 --cpu-type=AtomicSimpleCPU --disk-image=$OUT/riscv_disk --bare-metal
See Section 3 for more alternatives.
To debug gem5 code, append gdb --args
to the above command.
To debug the RISC-V code, append --param 'system.cpu[0].wait_for_remote_gdb = True'
. Use the following command to access the remote GDB:
riscv64-unknown-linux-gnu-gdb $OUT/bbl
# In GDB shell
add-symbol-file ~/riscv/out/vmlinux
target remote :7000 # CPU 1 will be at 7001 etc.
This section goes through the Python configurations which are relevant to RISC-V full system.
The HiFive platform is constructed based on SiFive's HiFive board series. By default, it contains the interrupt controllers CLINT and PLIC at addresses 0x2000000
and 0xc000000
respectively. It also has a default UART terminal attached at 0x10000000
.
system.platform = HiFive()
CLINT is responsible for timer interrupts (through setting MMIO mtimecmp
) and software interrupts. The mtime
register in CLINT is incremented by an external signal (interrupt pin). By default, it is connected to a dummy RiscvRTC
object which generates a fixed-frequency clock signal but provides no RTC MMIO interface for setting kernel wall clock time.
system.platform.rtc = RiscvRTC(frequency=Frequency("10MHz"))
system.platform.clint.int_pin = system.platform.rtc.int_pin
See Section 4 for known issues regard CLINT's devicetree. It has high impace on the simulation speed.
If you use setup/fs_linux.py
, a disk image can be optionally passed in. The disk image is wrapped as a VirtIOMMIO device. Multiple disks can be added if desired, following the format below:
image = CowDiskImage(child=RawDiskImage(read_only=True), read_only=False)
image.child.image_file = mdesc.disks()[0]
system.platform.disk = MmioVirtIO(
vio=VirtIOBlock(image=image),
interrupt_id=0x8,
pio_size=4096,
pio_addr=0x10008000
)
Connect the off-chip devices to the system's iobus
, connect on-chip devices to membus
:
system.bridge.ranges = system.platform._off_chip_ranges()
system.platform.attachOnChipIO(system.membus)
system.platform.attachOffChipIO(system.iobus)
Route off-chip PlicIntDevice
(see gem5/src/dev/riscv/PlicDevice.py
) have their interrupts through PLIC (use Platform::postPciInt(int line)
to raise interrupt to PLIC). attachPlic()
register their interrupt_id
to PLIC and configure the number of interrupt sources in the devicetree:
system.platform.attachPlic()
The PMAChecker adds flags to translated virtual addresses (e.g. uncacheability). Configure the uncacheable memory ranges (or any custom-flag ranges by extending PMAChecker
class) after CPUs have been added to the system:
uncacheable_range = [
*system.platform._on_chip_ranges(),
*system.platform._off_chip_ranges()
]
for cpu in system.cpu:
cpu.mmu.pma_checker = PMAChecker(uncacheable=uncacheable_range)
Call generateDtb(system)
to generate dtb and embed it into the workload binary (this might be merged into HiFive
class in the future).
Supply --dtb-filename=<path_to_dtb_file>
to avoid automatic DTB generation. Configuration in dtb should match the system setup in Python. It might be easier to automatically generate a DTB from Python then make the necessary changes (e.g. new devices / change settings) to get a custom dtb.
If devicetree is already embedded in bootloader binary (bootloader should handling setting the register a1
to point to devicetree), use --bare-metal
. For example, this can be done by building bbl with the --with-dts
option.
If you use setup/fs_linux.py
, disk image is optional.
Currently gem5 full system does not support H-mode. But machine-mode hypervisors like Diosix can be booted. However, some bugfixes might be involved.
Checkpointing and restoration is supported for RISC-V full system (although it doesn't take long to boot from O3CPU).
You can take checkpoint use the --take-checkpoint=...,...
option.
Or you can write C scripts that uses m5 functions (e.g. m5_checkpoint
) to checkpoint in the linux terminal.
- Build m5 library:
scons riscv.CROSS_COMPILE=riscv64-unknown-linux-gnu- build/riscv/out/m5
in the directorygem5/util/m5
- Compile and link using flags:
-static -L${G5}/util/m5/build/riscv/out -lm5
- Copy binary into riscv disk (see Benchmark Guide.md)
Use the flag -r <N>
where <N>
starting from 1 is the index of the checkpoint folder inside your logs
folder.
Checkpoint restore
Currently, the DTB generation functionality produces a constant 10MHz
timebase for the CPUs. If the RTC signal frequency is different from 10MHz
, it is necessary to manually edit gem5/src/dev/riscv/HiFive.py
(see below) and rebuild gem5.
In most cases, however, it is desirable to leave the timebase at 10MHz
with the RTC signal set at 100MHz
to improve simulation speeds.
# Under class HiFive
def generateDeviceTree(self, state):
cpus_node = FdtNode("cpus")
cpus_node.append(FdtPropertyWords("timebase-frequency", [10000000]))
yield cpus_node
Currently gem5 RISC-V only supports SV39
address translation, but the generated devicetree specifies SV48
. Edit gem5/src/dev/riscv/HiFive.py
(see below) and rebuild gem5 to correct that.
# Under class HiFive
def annotateCpuDeviceNode(self, cpu, state):
cpu.append(FdtPropertyStrings('mmu-type', 'riscv,sv48'))