Skip to content
Open
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ sw/kernel_pulp/bin/
sw/kernel_pulp/headers_bin/

*.wlf
*.dbg
*.dbg
verilator/build
16 changes: 16 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,22 @@ sources:
- hw/tile/magia_tile_icache_wrap.sv
- hw/tile/magia_redmule_wrap.sv
- hw/tile/magia_tile.sv

- target: all(magia_dv, not(standalone_tile), verilator)
files:
- target/verilator/src/magia_tile_hier.sv
# MAGIA
- hw/mesh/magia.sv
# MAGIA DV
- target/sim/src/tile/magia_tile_tb_pkg.sv
- target/sim/src/mesh/magia_tb_pkg.sv
- target/sim/src/mesh/magia_l2_mem_wrapper.sv
- target/verilator/src/magia_vip.sv
- target/verilator/src/magia_fixture.sv
- target/sim/src/mesh/magia_tb.sv

- target: all(magia_dv, not(standalone_tile), not(verilator))
files:
# MAGIA
- hw/mesh/magia.sv
# MAGIA DV
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -583,4 +583,5 @@ magia-nonfree-init:
cd $(MAGIA_NONFREE_DIR) && git checkout $(MAGIA_NONFREE_COMMIT)
if [ "$(MAGIA_NONFREE_DEPS)" -eq "1" ]; then $(MAKE) nonfree-init-dep ; fi

include verilator/verilator.mk
-include $(MAGIA_NONFREE_DIR)/nonfree.mk
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,96 @@ make all test=fsync_test
make run test=fsync_test
```

### Simulation with Verilator

MAGIA can also be simulated with [Verilator](https://verilator.org) instead of
QuestaSim. The flow is independent of the `vsim` one above: it has its own
targets, its own build directory (`verilator/build`), and needs no
`build-hw`/`vsim-scripts` step.

Verilator builds the mesh **hierarchically**: the tile is compiled once into a
separate library (`magia_tile_hier`) and instantiated 16 times, instead of
being flattened 16 times over. This is what keeps the build tractable, and it
is why the flow requires `mesh_dv=1` — there is no single-tile Verilator
target at this time.

The following *optional* parameters can be specified:

`core`: **CV32E40P** (**Default**: CV32E40P). CV32E40X is not supported by this
flow.

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

`VERILATOR_THREADS`: **N** (**Default**: 1). Threads the *simulation itself* runs on. See
the note below before changing it.

`VERILATOR_FST`: **<file>** (**Default**: empty). Dump a waveform to this file.

**Instructions to build and run a Verilator simulation**:

**1)** *Build* the model (`MAGIA` folder):
```bash
make verilate core=CV32E40P mesh_dv=1 VERILATOR_JOBS=16
```
**2)** *Compile and run* a test (`MAGIA` folder):
```bash
make verilate-run core=CV32E40P mesh_dv=1 test=inter_l1_test
```
Step **1** is *required at least once*: `verilate-run` compiles the test but
never the model — it runs the `Vmagia_tb` that is already in
`verilator/build/obj_dir`, and errors out if there is none.
**After changing the RTL, re-run `make verilate` yourself**, or you will keep simulating the old model.

**Full example**:
```bash
source setup_env.sh
make verilate core=CV32E40P mesh_dv=1 VERILATOR_JOBS=16
make verilate-run core=CV32E40P mesh_dv=1 test=inter_l1_test
```

Other targets: `verilate-gen` (code generation only), `verilate-build` (native
compile only), `verilate-check-hierarchy` (asserts the model really links the
tile library instead of inlining it), and `clean-verilate`.

#### Waveforms (FST)

The model is **always** built with tracing compiled in, so no rebuild is needed
to capture a waveform. Dumping is off until a run asks for it, and a run that
does not ask pays nothing beyond a larger binary:

```bash
make verilate-run core=CV32E40P mesh_dv=1 test=inter_l1_test VERILATOR_FST=dump.fst
```

The file is written in the test's build directory. Waveforms include the
internals of every tile.

**Let the simulation reach `$finish`.** A run killed before it gets there
leaves the FST unclosed, and an unclosed FST is not a file you can keep: the hierarchy is still sitting in a `<dump>.fst.hier`
companion, so the dump reads only in place and loses every signal name the
moment it is moved.

#### Multithreaded simulation (experimental)

`VERILATOR_THREADS=8` runs the model on 8 threads:

```bash
make verilate core=CV32E40P mesh_dv=1 VERILATOR_JOBS=16 VERILATOR_THREADS=8
make verilate-run core=CV32E40P mesh_dv=1 test=inter_l1_test
```

`VERILATOR_THREADS` is compiled *into* the model, so it belongs on `verilate`
only — passing it to `verilate-run` does nothing, and does not rebuild anything.
To change the thread count, re-run `make verilate` with the new value.

**This is experimental and only partially tested.** What is actually known:

- `VERILATOR_THREADS=8` halved `inter_l1_test` (132 s → 66 s, same result, same
`$finish` time). Only that one test was checked, once.
- `VERILATOR_THREADS=4` **segfaults at time zero**, deterministically, in an
`eval_initial` coroutine — from a clean build, same flags but the count.

## ⚙️ Architecture

![](doc/MAGIA.png)
Expand Down
34 changes: 32 additions & 2 deletions hw/mesh/magia.sv
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
* MAGIA
*/

`ifndef VERILATOR
`include "fractal_sync/assign.svh"
`endif

module magia
import magia_pkg::*;
Expand Down Expand Up @@ -74,6 +76,9 @@ module magia

input floo_wide_t [N_TILES_Y-1:0] l2_noc_wide_i,
output floo_wide_t [N_TILES_Y-1:0] l2_noc_wide_o
`ifdef VERILATOR
, output magia_tile_observe_t tile_observe_o[N_TILES]
`endif
);

/*******************************************************/
Expand Down Expand Up @@ -112,6 +117,7 @@ module magia

/*******************************************************/
/** Internal Signal Definitions End **/
`ifndef VERILATOR
/*******************************************************/
/** Interface Definitions Beginning **/
/*******************************************************/
Expand Down Expand Up @@ -140,6 +146,7 @@ module magia

/*******************************************************/
/** Interface Assignments End **/
`endif
/*******************************************************/
/** Hardwired Signals Beginning **/
/*******************************************************/
Expand All @@ -165,16 +172,23 @@ module magia

for (genvar i = 0; i < N_TILES_Y; i++) begin: gen_y_tile
for (genvar j = 0; j < N_TILES_X; j++) begin: gen_x_tile
`ifdef VERILATOR
magia_tile_hier #(
.N_MEM_BANKS ( N_MEM_BANKS ),
.N_WORDS_BANK ( N_WORDS_BANK )
) i_magia_tile (
`else
magia_tile #(
.N_MEM_BANKS ( N_MEM_BANKS ),
.N_WORDS_BANK ( N_WORDS_BANK ),

.CORE_ISA ( ),
.CORE_A ( ),
.CORE_B ( ),
.CORE_M ( ),
.ERROR_CAP ( )
) i_magia_tile (
`endif
.clk_i ,
.rst_ni ,
.test_mode_i ,
Expand Down Expand Up @@ -209,10 +223,21 @@ module magia
.x_id_i ( j ),
.y_id_i ( i ),

`ifdef VERILATOR
.ht_fsync_req_o ( ht_tile_fsync_req[i*N_TILES_X+j][0] ),
.ht_fsync_rsp_i ( ht_tile_fsync_rsp[i*N_TILES_X+j][0] ),
.hn_fsync_req_o ( hn_tile_fsync_req[i*N_TILES_X+j] ),
.hn_fsync_rsp_i ( hn_tile_fsync_rsp[i*N_TILES_X+j] ),
.vt_fsync_req_o ( vt_tile_fsync_req[i*N_TILES_X+j][0] ),
.vt_fsync_rsp_i ( vt_tile_fsync_rsp[i*N_TILES_X+j][0] ),
.vn_fsync_req_o ( vn_tile_fsync_req[i*N_TILES_X+j] ),
.vn_fsync_rsp_i ( vn_tile_fsync_rsp[i*N_TILES_X+j] ),
`else
.ht_fsync_if_o ( ht_fsync_if[i*N_TILES_X+j] ),
.hn_fsync_if_o ( hn_fsync_if[i*N_TILES_X+j] ),
.vt_fsync_if_o ( vt_fsync_if[i*N_TILES_X+j] ),
.vn_fsync_if_o ( vn_fsync_if[i*N_TILES_X+j] ),
`endif

.scan_cg_en_i ,

Expand Down Expand Up @@ -243,11 +268,16 @@ module magia
.fetch_enable_i ,
.core_sleep_o ( core_sleep_o[i*N_TILES_X+j] ),
.wu_wfe_i
`ifdef VERILATOR
, .observe_o ( tile_observe_o[i*N_TILES_X+j] )
`endif
);
`ifdef CORE_TRACES
`ifdef CV32E40X
`ifndef VERILATOR
localparam string core_trace_file_name = $sformatf("%s%0d", "log_file_", i*N_TILES_X+j);
defparam i_magia_tile.i_cv32e40x_ctrl_core.rvfi_i.tracer_i.LOGFILE_PATH_PLUSARG = core_trace_file_name;
`endif
`endif
// Note: cv32e40p tracer generates its own filename: trace_core_{cluster_id}_{core_id}.log
`endif
Expand Down Expand Up @@ -483,4 +513,4 @@ module magia
/** FractalSync Network End **/
/*******************************************************/

endmodule: magia
endmodule: magia
2 changes: 1 addition & 1 deletion hw/mesh/noc/floo_axi_nw_mesh_16x16_noc.sv
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ typedef struct packed {
} sam_rule_t;

localparam sam_rule_t[SamNumRules-1:0] Sam = '{
'{idx: '{x: 0, y: 15, port_id: 0}, start_addr: 32'hfc000000, end_addr: 32'h100000000},// L2_15_sam_idx
'{idx: '{x: 0, y: 15, port_id: 0}, start_addr: 32'hfc000000, end_addr: 32'hffffffff},// L2_15_sam_idx
'{idx: '{x: 0, y: 14, port_id: 0}, start_addr: 32'hf8000000, end_addr: 32'hfc000000},// L2_14_sam_idx
'{idx: '{x: 0, y: 13, port_id: 0}, start_addr: 32'hf4000000, end_addr: 32'hf8000000},// L2_13_sam_idx
'{idx: '{x: 0, y: 12, port_id: 0}, start_addr: 32'hf0000000, end_addr: 32'hf4000000},// L2_12_sam_idx
Expand Down
2 changes: 1 addition & 1 deletion hw/mesh/noc/floo_axi_nw_mesh_2x2_noc.sv
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ typedef struct packed {
} sam_rule_t;

localparam sam_rule_t[SamNumRules-1:0] Sam = '{
'{idx: '{x: 0, y: 1, port_id: 0}, start_addr: 32'he0000000, end_addr: 32'h100000000},// L2_1_sam_idx
'{idx: '{x: 0, y: 1, port_id: 0}, start_addr: 32'he0000000, end_addr: 32'hffffffff},// L2_1_sam_idx
'{idx: '{x: 0, y: 0, port_id: 0}, start_addr: 32'hc0000000, end_addr: 32'he0000000},// L2_0_sam_idx
'{idx: '{x: 2, y: 1, port_id: 0}, start_addr: 32'h00300000, end_addr: 32'h00400000},// magia_tile_x1_y1_sam_idx
'{idx: '{x: 1, y: 1, port_id: 0}, start_addr: 32'h00200000, end_addr: 32'h00300000},// magia_tile_x0_y1_sam_idx
Expand Down
2 changes: 1 addition & 1 deletion hw/mesh/noc/floo_axi_nw_mesh_32x32_noc.sv
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ typedef struct packed {
} sam_rule_t;

localparam sam_rule_t[SamNumRules-1:0] Sam = '{
'{idx: '{x: 0, y: 31, port_id: 0}, start_addr: 32'hfe000000, end_addr: 32'h100000000},// L2_31_sam_idx
'{idx: '{x: 0, y: 31, port_id: 0}, start_addr: 32'hfe000000, end_addr: 32'hffffffff},// L2_31_sam_idx
'{idx: '{x: 0, y: 30, port_id: 0}, start_addr: 32'hfc000000, end_addr: 32'hfe000000},// L2_30_sam_idx
'{idx: '{x: 0, y: 29, port_id: 0}, start_addr: 32'hfa000000, end_addr: 32'hfc000000},// L2_29_sam_idx
'{idx: '{x: 0, y: 28, port_id: 0}, start_addr: 32'hf8000000, end_addr: 32'hfa000000},// L2_28_sam_idx
Expand Down
2 changes: 1 addition & 1 deletion hw/mesh/noc/floo_axi_nw_mesh_4x4_noc.sv
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ typedef struct packed {
} sam_rule_t;

localparam sam_rule_t[SamNumRules-1:0] Sam = '{
'{idx: '{x: 0, y: 3, port_id: 0}, start_addr: 32'hf0000000, end_addr: 32'h100000000},// L2_3_sam_idx
'{idx: '{x: 0, y: 3, port_id: 0}, start_addr: 32'hf0000000, end_addr: 32'hffffffff},// L2_3_sam_idx
'{idx: '{x: 0, y: 2, port_id: 0}, start_addr: 32'he0000000, end_addr: 32'hf0000000},// L2_2_sam_idx
'{idx: '{x: 0, y: 1, port_id: 0}, start_addr: 32'hd0000000, end_addr: 32'he0000000},// L2_1_sam_idx
'{idx: '{x: 0, y: 0, port_id: 0}, start_addr: 32'hc0000000, end_addr: 32'hd0000000},// L2_0_sam_idx
Expand Down
2 changes: 1 addition & 1 deletion hw/mesh/noc/floo_axi_nw_mesh_8x8_noc.sv
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ typedef struct packed {
} sam_rule_t;

localparam sam_rule_t[SamNumRules-1:0] Sam = '{
'{idx: '{x: 0, y: 7, port_id: 0}, start_addr: 32'hf8000000, end_addr: 32'h100000000},// L2_7_sam_idx
'{idx: '{x: 0, y: 7, port_id: 0}, start_addr: 32'hf8000000, end_addr: 32'hffffffff},// L2_7_sam_idx
'{idx: '{x: 0, y: 6, port_id: 0}, start_addr: 32'hf0000000, end_addr: 32'hf8000000},// L2_6_sam_idx
'{idx: '{x: 0, y: 5, port_id: 0}, start_addr: 32'he8000000, end_addr: 32'hf0000000},// L2_5_sam_idx
'{idx: '{x: 0, y: 4, port_id: 0}, start_addr: 32'he0000000, end_addr: 32'he8000000},// L2_4_sam_idx
Expand Down
4 changes: 3 additions & 1 deletion hw/tile/local_interconnect.sv
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ module local_interconnect
parameter int unsigned N_CORE = magia_tile_pkg::N_CORE,
parameter int unsigned N_MEM = magia_pkg::N_MEM_BANKS,
parameter int unsigned EXPFIFO = magia_tile_pkg::EXPFIFO,
parameter int unsigned FILTER_WRITE_R_VALID[0:N_HWPE-1] = '{default: 0},
parameter int unsigned FILTER_WRITE_R_VALID[0:((N_HWPE > N_DMA ? N_HWPE : N_DMA) > N_CORE ?
(N_HWPE > N_DMA ? N_HWPE : N_DMA) : N_CORE)-1] =
'{default: 0},
parameter int unsigned MEM_DATA_W = 0,
parameter int unsigned MEM_ADDR_W = 0,
parameter int unsigned MEM_BYTE_W = 0,
Expand Down
44 changes: 41 additions & 3 deletions hw/tile/magia_tile.sv
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,50 @@ module magia_tile
input logic fetch_enable_i,
output logic core_sleep_o,
input logic wu_wfe_i
`ifdef VERILATOR
, output magia_tile_observe_t observe_o
`endif
);

/*******************************************************/
/** Internal Signal Definitions Beginning **/
/*******************************************************/

`ifdef VERILATOR
// A hierarchical block cannot be observed through parent dotted paths.
always_comb begin
observe_o = '0;
// The print peripheral (0xFFFF_0000/0xFFFF_0004) matches no address rule
// and therefore leaves through the default master port ('0), i.e. the ext
// port. This is the port the pre-hierarchy VIP snooped as
// i_axi_xbar.mst_ports_req_o[0]; observing the OBI port sees no prints.
observe_o.axi_aw_addr = axi_xbar_mst_req[magia_tile_pkg::AXI_MST_EXT_IDX].aw.addr;
observe_o.axi_aw_id = axi_xbar_mst_req[magia_tile_pkg::AXI_MST_EXT_IDX].aw.id;
observe_o.axi_aw_valid = axi_xbar_mst_req[magia_tile_pkg::AXI_MST_EXT_IDX].aw_valid;
observe_o.axi_w_data = axi_xbar_mst_req[magia_tile_pkg::AXI_MST_EXT_IDX].w.data;
observe_o.axi_w_valid = axi_xbar_mst_req[magia_tile_pkg::AXI_MST_EXT_IDX].w_valid;
`ifdef CV32E40X
observe_o.instr_ex = i_cv32e40x_ctrl_core.core_i.id_stage_i.id_ex_pipe_o.instr.bus_resp.rdata;
observe_o.instr_id = i_cv32e40x_ctrl_core.core_i.id_stage_i.if_id_pipe_i.instr.bus_resp.rdata;
observe_o.instr_wb = i_cv32e40x_ctrl_core.core_i.wb_stage_i.ex_wb_pipe_i.instr_valid ?
i_cv32e40x_ctrl_core.core_i.wb_stage_i.ex_wb_pipe_i.instr.bus_resp.rdata : '0;
observe_o.wb_data = observe_o.instr_wb;
`else
// CORE_TRACES is unconditionally defined for the magia_dv target (see
// Bender.yml), so i_cv32e40p_ctrl_core is always a cv32e40p_wrapper
// instance, which instantiates cv32e40p_top as cv32e40p_top_i, which in
// turn instantiates cv32e40p_core as core_i.
observe_o.instr_ex = i_cv32e40p_ctrl_core.cv32e40p_top_i.core_i.ex_valid ?
i_cv32e40p_ctrl_core.cv32e40p_top_i.core_i.id_stage_i.instr_rdata_i : '0;
observe_o.instr_id = i_cv32e40p_ctrl_core.cv32e40p_top_i.core_i.id_stage_i.instr_rdata_i;
observe_o.instr_wb = i_cv32e40p_ctrl_core.cv32e40p_top_i.core_i.wb_valid ?
i_cv32e40p_ctrl_core.cv32e40p_top_i.core_i.instr_rdata_id : '0;
observe_o.wb_data = i_cv32e40p_ctrl_core.cv32e40p_top_i.core_i.wb_valid ?
i_cv32e40p_ctrl_core.cv32e40p_top_i.core_i.regfile_wdata : '0;
`endif
end
`endif

logic[magia_pkg::ADDR_W-1:0] tile_l1_start_addr;
logic[magia_pkg::ADDR_W-1:0] tile_l1_end_addr;
logic[magia_pkg::ADDR_W-1:0] tile_reserved_start_addr;
Expand Down Expand Up @@ -237,8 +275,8 @@ module magia_tile
magia_tile_pkg::axi_xbar_slv_req_t[magia_tile_pkg::AxiXbarNoSlvPorts-1:0] axi_xbar_slv_req; // Index 2 -> ext, Index 1 -> Core Data, Index 0 -> Core Instruction
magia_tile_pkg::axi_xbar_slv_rsp_t[magia_tile_pkg::AxiXbarNoSlvPorts-1:0] axi_xbar_slv_rsp; // Index 2 -> ext, Index 1 -> Core Data, Index 0 -> Core Instruction

magia_pkg::axi_xbar_mst_req_t[magia_tile_pkg::AxiXbarNoMstPorts-1:0] axi_xbar_mst_req; // Index 1 -> ext, Index 0 -> OBI XBAR
magia_pkg::axi_xbar_mst_rsp_t[magia_tile_pkg::AxiXbarNoMstPorts-1:0] axi_xbar_mst_rsp; // Index 1 -> ext, Index 0 -> OBI XBAR
magia_pkg::axi_xbar_mst_req_t[magia_tile_pkg::AxiXbarNoMstPorts-1:0] axi_xbar_mst_req; // Index 0 -> ext, Index 1 -> OBI XBAR, Index 2 -> Spatz bootrom
magia_pkg::axi_xbar_mst_rsp_t[magia_tile_pkg::AxiXbarNoMstPorts-1:0] axi_xbar_mst_rsp; // Index 0 -> ext, Index 1 -> OBI XBAR, Index 2 -> Spatz bootrom

logic[magia_tile_pkg::axi_xbar_cfg.NoSlvPorts-1:0] en_default_mst_port;

Expand Down Expand Up @@ -2671,4 +2709,4 @@ endgenerate



endmodule: magia_tile
endmodule: magia_tile
21 changes: 20 additions & 1 deletion hw/tile/magia_tile_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,25 @@ package magia_tile_pkg;
`FSYNC_TYPEDEF_ALL(hn_tile_fsync, logic[FSYNC_NBR_AGGR_W-1:0], logic[FSYNC_NBR_LVL_W-1:0], logic[FSYNC_NBR_ID_W-1:0])
`FSYNC_TYPEDEF_ALL(vn_tile_fsync, logic[FSYNC_NBR_AGGR_W-1:0], logic[FSYNC_NBR_LVL_W-1:0], logic[FSYNC_NBR_ID_W-1:0])

`ifdef VERILATOR
// Packed observation boundary required by hierarchical Verilation.
typedef struct packed {
logic [magia_pkg::ADDR_W-1:0] axi_aw_addr;
// axi_aw_id is driven from axi_xbar_mst_req_t.aw.id (magia_pkg's NoC AXI
// alias), whose ID width is magia_pkg::AXI_NOC_ID_W. This tile package's
// own AXI_ID_W (3 bits) sizes a *different* xbar (the tile-internal one
// with 5 slave ports) and must not be reused here.
logic [magia_pkg::AXI_NOC_ID_W-1:0] axi_aw_id;
logic axi_aw_valid;
logic [magia_pkg::DATA_W-1:0] axi_w_data;
logic axi_w_valid;
logic [31:0] instr_ex;
logic [31:0] instr_id;
logic [31:0] instr_wb;
logic [31:0] wb_data;
} magia_tile_observe_t;
`endif

/*******************************************************************/
/* Spatz Core Complex Wrapper Types */
/*******************************************************************/
Expand Down Expand Up @@ -821,4 +840,4 @@ package magia_tile_pkg;



endpackage: magia_tile_pkg
endpackage: magia_tile_pkg
Loading