Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 121 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ GVSOC_DIR ?= ./gvsoc
GVSOC_ABS_PATH ?= $(CURR_DIR)/gvsoc
BIN_ABS_PATH ?= $(CMAKE_BUILDDIR)/bin
BIN ?= $(BUILD_DIR)/build/verif
# Prebuilt Verilator model. Built in the MAGIA repo with `make verilate mesh_dv=1`;
# the SDK never builds it. Override to use a model outside $(MAGIA_RTL_DIR).
MAGIA_VERILATOR_BIN ?= $(MAGIA_DIR_ABS)/verilator/build/obj_dir/Vmagia_tb
# Waveform a verilator run dumps under gui=1, relative to the test build dir the
# model runs in. Dumping is off otherwise and costs nothing.
VERILATOR_FST ?= $(test).fst
# Build parallelism, and threads compiled into the model. Passed to the MAGIA
# repo's verilator flow by `make MAGIA platform=verilator`.
verilator_jobs ?= 16
verilator_threads ?= 4
# Which simulator `MAGIA` and `rtl-clean` act on. `run` reads `platform` directly
# and still demands it explicitly; these two default to rtl so that invocations
# predating platform= keep working unchanged.
hw_platform := $(if $(platform),$(platform),rtl)
# Simulator inputs, relative to the test build dir. Same contract for questasim
# and verilator: both drive the magia_tb testbench in the MAGIA repo, so these
# mirror its own defaults.
inst_hex_name ?= build/stim_instr.txt
data_hex_name ?= build/stim_data.txt
itb_file ?= build/verif.itb
inst_entry ?= 0xCC000000
data_entry ?= 0xCC010000
boot_addr ?= 0xCC000080
build_mode ?= update
fsync_mode ?= stall
mesh_dv ?= 1
Expand Down Expand Up @@ -121,9 +144,16 @@ format:
clean:
rm -rf build/

# Note: hw-clean-all also removes .bender, which invalidates any verilator model
# built from the same checkout.
rtl-clean:
ifeq ($(hw_platform), verilator)
cd $(MAGIA_RTL_DIR) && \
make clean-verilate
else
cd $(MAGIA_RTL_DIR) && \
make hw-clean-all
endif
rm -rf $(MAGIA_RTL_DIR)/sw/tests/test_*

build:
Expand All @@ -148,42 +178,91 @@ endif
$(GVSOC_WORK_DIR):
mkdir -p $(GVSOC_WORK_DIR)

# SREC -> $readmemh stimuli, replacing the parse_s19.pl | s19tomem.py pipeline.
# Single file, so rustc directly rather than a cargo project.
RUSTC ?= rustc
S19TOMEM_SRC ?= scripts/s19tomem.rs
S19TOMEM_BIN ?= $(CMAKE_BUILDDIR)/tools/s19tomem

$(S19TOMEM_BIN): $(S19TOMEM_SRC)
mkdir -p $(dir $@)
$(RUSTC) -O -o $@ $<

# Turn the CMake-built ELF into everything an RTL simulator needs, under
# $(MAGIA_RTL_DIR)/sw/tests/$(test)/build/: the ELF itself as `verif`, the
# $readmemh instruction/data images, and the disassembly the core tracer reads.
# Shared by platform=rtl and platform=verilator -- both drive the same magia_tb.
ifeq ($(compiler), GCC_MULTILIB)
OBJDUMP ?= riscv64-unknown-elf-objdump
else
OBJDUMP ?= riscv32-unknown-elf-objdump
endif

.PHONY: rtl_stimuli
rtl_stimuli: $(S19TOMEM_BIN)
ifndef test
$(error Proper formatting is: make rtl_stimuli test=<test_name>)
endif
mkdir -p $(BUILD_DIR_ABS)/build
cp $(BIN_ABS_PATH)/$(test) $(BUILD_DIR_ABS)/build/verif
objcopy --srec-len 1 --output-target=srec $(BIN) $(BIN).s19
$(S19TOMEM_BIN) $(BIN).s19 $(BUILD_DIR_ABS)/build/stim_instr.txt $(BUILD_DIR_ABS)/build/stim_data.txt
$(OBJDUMP) -d -S -Mmarch=$(ISA) $(BIN) > $(BIN).dump
$(OBJDUMP) -d -l -s -Mmarch=$(ISA) $(BIN) > $(BIN).objdump
python3 scripts/objdump2itb.py $(BIN).objdump > $(BIN).itb

run: set_mesh $(GVSOC_WORK_DIR)
@echo 'Magia is available at https://github.com/pulp-platform/MAGIA.git'
@echo 'please run "source setup_env.sh" in the magia folder before running this script'
@echo 'and make sure the risc-v objdump binary is visible on path using "which riscv32-unknown-elf-objdump".'
ifndef test
$(error Proper formatting is: make run test=<test_name> platform=rtl|gvsoc)
$(error Proper formatting is: make run test=<test_name> platform=rtl|verilator|gvsoc)
endif
ifeq (,$(wildcard $(CMAKE_BUILDDIR)/bin/$(test)))
$(error No test found with name: $(test))
endif
ifndef platform
$(error Proper formatting is: make run test=<test_name> platform=rtl|gvsoc)
$(error Proper formatting is: make run test=<test_name> platform=rtl|verilator|gvsoc)
endif
ifeq ($(platform), gvsoc)
$(GVRUN) --target $(target_platform) --param binary=$(BIN_ABS_PATH)/$(test) $(GVRUN_ARGS)
else ifeq ($(platform), rtl)
mkdir -p $(BUILD_DIR_ABS) && cd $(BUILD_DIR_ABS) && mkdir -p build
cp ./build/bin/$(test) $(BUILD_DIR_ABS)/build/verif
objcopy --srec-len 1 --output-target=srec $(BIN) $(BIN).s19
scripts/parse_s19.pl $(BIN).s19 > $(BIN).txt
python3 scripts/s19tomem.py $(BIN).txt $(BUILD_DIR_ABS)/build/stim_instr.txt $(BUILD_DIR_ABS)/build/stim_data.txt
$(MAKE) rtl_stimuli test=$(test)
cd $(BUILD_DIR_ABS) && \
cp -sf "$(MAGIA_DIR_ABS)/sim/modelsim.ini" modelsim.ini && \
ln -sfn "$(MAGIA_DIR_ABS)/sim/work" work
ifeq ($(compiler), GCC_MULTILIB)
riscv64-unknown-elf-objdump -d -S -Mmarch=$(ISA) $(BIN) > $(BIN).dump
riscv64-unknown-elf-objdump -d -l -s -Mmarch=$(ISA) $(BIN) > $(BIN).objdump
else
riscv32-unknown-elf-objdump -d -S -Mmarch=$(ISA) $(BIN) > $(BIN).dump
riscv32-unknown-elf-objdump -d -l -s -Mmarch=$(ISA) $(BIN) > $(BIN).objdump
endif
python3 scripts/objdump2itb.py $(BIN).objdump > $(BIN).itb
cd $(MAGIA_RTL_DIR) && \
make run test=$(test) gui=$(gui) mesh_dv=$(mesh_dv) fast_sim=$(fast_sim)
else ifeq ($(platform), verilator)
@test -x "$(MAGIA_VERILATOR_BIN)" || { \
echo "error: no Verilator model at $(MAGIA_VERILATOR_BIN)" >&2; \
echo " build it in the MAGIA repo first:" >&2; \
echo " make verilate core=CV32E40P mesh_dv=1" >&2; \
exit 1; }
$(MAKE) rtl_stimuli test=$(test)
# No modelsim.ini/work symlinks: the verilated model is self-contained. Run it
# straight rather than via the MAGIA repo's `make verilate-run`, whose `all`
# prerequisite would try to recompile the test from sources that only exist for
# tests living in that repo.
set -o pipefail && \
cd $(BUILD_DIR_ABS) && \
"$(MAGIA_VERILATOR_BIN)" \
+INST_HEX=$(inst_hex_name) \
+DATA_HEX=$(data_hex_name) \
+INST_ENTRY=$(inst_entry) \
+DATA_ENTRY=$(data_entry) \
+BOOT_ADDR=$(boot_addr) \
+itb_file=$(itb_file) \
$(if $(filter 1,$(gui)),+FST=$(VERILATOR_FST),) \
2>&1 | tee transcript_verilator
ifeq ($(gui), 1)
@echo ''
@echo 'Waveform: $(BUILD_DIR_ABS)/$(VERILATOR_FST)'
@echo ' gtkwave $(BUILD_DIR_ABS)/$(VERILATOR_FST)'
@echo ' surfer $(BUILD_DIR_ABS)/$(VERILATOR_FST)'
endif
else
$(error Only rtl and gvsoc are supported as platforms.)
$(error Only rtl, verilator and gvsoc are supported as platforms.)
endif

run_profiling: set_mesh $(GVSOC_WORK_DIR) $(GVSOC2PERFETTO_BIN)
Expand Down Expand Up @@ -227,7 +306,32 @@ endif
--include '$(GVSOC2PERFETTO_INCLUDE)'
rm -f -- $(GVSOC2PERFETTO_VCD)

# Hardware build command, the only part of `make MAGIA` that differs per simulator.
# Recursively expanded on purpose: set_mesh rewrites mesh_dv while the target runs.
ifeq ($(hw_platform), verilator)
HW_BUILD_CMD = make verilate > verilate.log mesh_dv=$(mesh_dv) VERILATOR_JOBS=$(verilator_jobs) VERILATOR_THREADS=$(verilator_threads)
else
HW_BUILD_CMD = make build-hw > build-hw.log mesh_dv=$(mesh_dv) fast_sim=$(fast_sim)
endif

MAGIA: set_mesh
ifeq (,$(filter $(hw_platform), rtl verilator))
$(error Only rtl and verilator can be built with `make MAGIA` (got platform=$(hw_platform)).)
endif
# The MAGIA verilator flow is mesh-only and CV32E40P-only (verilator/verilator.mk).
# tiles=1 is rejected because set_mesh turns it into mesh_dv=0, and magia_v1
# because it seds the core to CV32E40X further down.
ifeq ($(hw_platform), verilator)
ifeq ($(tiles), 1)
$(error platform=verilator requires mesh_dv=1, but tiles=1 forces mesh_dv=0: there is no single-tile verilator target.)
endif
ifneq ($(mesh_dv), 1)
$(error platform=verilator requires mesh_dv=1: there is no single-tile verilator target.)
endif
ifeq ($(target_platform), magia_v1)
$(error platform=verilator does not support magia_v1: it selects CV32E40X, whose hierarchical core traces need per-tile filenames resolved at run time.)
endif
endif
ifeq ($(shell expr $(tiles_2) \> 256), 1)
$(eval tiles_2=256)
endif
Expand Down Expand Up @@ -263,7 +367,7 @@ ifneq (,$(filter $(build_mode), update synth profile))
python -m pip install --upgrade "setuptools<81" && \
make vsim-scripts > vsim-scripts.log mesh_dv=$(mesh_dv) && \
make floonoc-patch || true && \
make build-hw > build-hw.log mesh_dv=$(mesh_dv) fast_sim=$(fast_sim)
$(HW_BUILD_CMD)
else
$(error unrecognized mode (acceptable build modes: update|profile|synth).)
endif
Expand Down
86 changes: 84 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ The following *optional* parameters can be specified when running the make comma

`ISA`: **rv32imcxgap9**|**rv32imafc** (**Default**: rv32imcxgap9) ISA target for the GCC toolcahin.

`platform`: **rtl**|**gvsoc**. Selects the simulation platform. GVSoC is currently WIP, some tests may fail.
`platform`: **rtl**|**verilator**|**gvsoc**. Selects the simulation platform. `rtl` simulates the RTL with QuestaSim, `verilator` simulates the same RTL with Verilator (see [Simulating with Verilator](#simulating-with-verilator)). GVSoC is currently WIP, some tests may fail. `make MAGIA` and `make rtl-clean` accept `rtl` and `verilator` and default to `rtl`; `make run` requires the flag explicitly.

`tiles`: **2**|**4**|**8**|**16** (**Default**: 2). Selects number of rows and columns for the mesh architecture.

Expand Down Expand Up @@ -127,6 +127,11 @@ Once the [Prerequisites](#prerequisites) are in place:

`make MAGIA <target_platform> <tiles> <build_mode> <fsync_mode>`

This builds the QuestaSim libraries. To build a Verilator model of the same RTL instead, add
`platform=verilator` (see [Simulating with Verilator](#simulating-with-verilator)):

`make MAGIA platform=verilator <target_platform> <tiles> <fsync_mode>`

and/or the GVSoC module **NOTE: GCC AND G++ 11.2.0 AND ABOVE IS MANDATORY**:

`make gvsoc <tiles>`
Expand Down Expand Up @@ -159,6 +164,83 @@ To ensure a clean re-build of the RTL, you can run:

before building the RTL back using the `make MAGIA` command.

## Simulating with Verilator

`platform=verilator` runs the same RTL as `platform=rtl`, but on a
[Verilator](https://verilator.org) model instead of QuestaSim — no simulator license needed. The
two flows share the entire software side: the SDK produces exactly the same `verif` ELF, `$readmemh`
stimuli and disassembly, and both drive the same `magia_tb` testbench with the same plusargs.

The flow has the same build-then-run shape as `platform=rtl`, and takes the same `tiles`,
`target_platform` and `fsync_mode`:

```sh
make MAGIA platform=verilator tiles=4 # build the model (minutes)
make build test=test_helloworld tiles=4 # build the test
make run test=test_helloworld platform=verilator tiles=4
```

`make MAGIA platform=verilator` applies exactly the same parameter edits to the RTL as the QuestaSim
build does, then Verilates instead of running `build-hw`. The mesh geometry is compiled into the
model, so **`tiles` must be the same for `MAGIA`, `build` and `run`**, and the model has to be
rebuilt whenever `tiles`, `target_platform`, `fsync_mode` or the RTL itself changes — `make run`
never rebuilds it, and errors out if there is no model at all.

To remove a Verilator build: `make rtl-clean platform=verilator` (without `platform=verilator` this
still does the QuestaSim `hw-clean-all`, which deletes `.bender` and thereby invalidates any
Verilator model built from the same checkout).

Requirements:

- A MAGIA checkout containing the Verilator flow (`verilator/verilator.mk`). This is **not** in the
commit pinned by `scripts/deps.env` yet, so point `MAGIA_RTL_DIR` at a checkout that has it.
- Verilator >= 5.046 — earlier versions miss the hierarchical-block fixes the flow depends on.
- `mesh_dv=1`: the flow is mesh-only, so `tiles=1` is rejected (it forces `mesh_dv=0`).
- Not `target_platform=magia_v1`: it selects CV32E40X, whose hierarchical core traces would need
per-tile filenames resolved at run time. Also rejected.

Both constraints are checked by `make MAGIA`, which stops with an explanatory error rather than
letting the build fail later.

Options:

`verilator_jobs`: **N** (**Default**: 16). Parallelism used to *build* the model. Unrelated to simulation speed.

`verilator_threads`: **N** (**Default**: 4). Threads the *simulation* runs on. Compiled into the model, so it only has an effect on `make MAGIA platform=verilator`.

`MAGIA_VERILATOR_BIN`: Path to the model. **Default**: `$(MAGIA_RTL_DIR)/verilator/build/obj_dir/Vmagia_tb`. Override to run a model kept outside the MAGIA checkout.

`gui`: **0**|**1** (**Default**: 0). Dumps an FST waveform of the run to `<test_name>.fst` in the test directory, and prints ready-to-paste `gtkwave` and `surfer` commands when the simulation ends. This is what `gui` means for Verilator — there is no interactive simulator window as with QuestaSim. Dumping is off otherwise and costs nothing. Override the filename with `VERILATOR_FST`.

`fast_sim` is QuestaSim-only and is ignored.

Outputs, all in `$(MAGIA_RTL_DIR)/sw/tests/<test_name>/`:

- **stdout**, also teed to `transcript_verilator`. Printing from the tiles appears as
`[mhartid N] ...`.
- `trace_core_<hartid>.log`, one per core.
- `<test_name>.fst`, the waveform, if `gui=1` was given.
- `build/` with the ELF and the stimuli, exactly as in the `rtl` flow. No `modelsim.ini`/`work`
symlinks are created — the verilated model is self-contained.

Pass/fail is decided by the testbench's end-of-simulation line rather than by the exit code alone,
because the model also exits 0 when it runs out of events without reaching `$finish`:

```sh
./scripts/sim_ret_errors_verilator.sh $MAGIA_RTL_DIR/sw/tests/test_helloworld/transcript_verilator
```

It exits 0 on success, 1 on a failing simulation, 2 if the log is missing, and 3 if the simulation
never reported an end-of-simulation line (hung or killed).

Two things to keep in mind:

- **Nothing bounds a hung run.** If a tile never signals end-of-computation the simulation waits
forever; interrupt it with Ctrl-C.
- **Let a waveform run reach `$finish`.** A run killed earlier leaves the FST unclosed, and its
signal hierarchy is still sitting in the `<dump>.fst.hier` companion file — such a dump only reads
in place and loses every signal name the moment it is moved.

## Adding your own test

This SDK uses a nested CMakeList mechanism to build and check for dependencies.
Expand Down Expand Up @@ -415,4 +497,4 @@ add_cv32_executable_with_spatz(
```

### Run Tests
To run test a special Makefile rule can be used: `make run test=<test_name> platform=<rtl|gvsoc>`.
To run test a special Makefile rule can be used: `make run test=<test_name> platform=<rtl|verilator|gvsoc>`.
Loading
Loading