diff --git a/Makefile b/Makefile index 5efa814b..71c2ebb9 100644 --- a/Makefile +++ b/Makefile @@ -110,9 +110,9 @@ gui ?= 0 ipstools ?= 0 inst_hex_name ?= build/stim_instr.txt data_hex_name ?= build/stim_data.txt -inst_entry ?= 0xCC000000 -data_entry ?= 0xCC010000 -boot_addr ?= 0xCC000080 +inst_entry ?= 0xC0000000 +data_entry ?= 0xC0008800 +boot_addr ?= 0xC0000080 test ?= hello_world mesh_dv ?= 1 fast_sim ?= 0 @@ -158,7 +158,7 @@ INC += -Isw/kernel_pulp/headers_bin # sw/kernel_pulp/headers_bin/_pulp_task_bin.h # which the CV32 main.c `#include`s. The linker (sw/kernel/link.ld) KEEPs # that array inside the `.pulp_binary` section, right after the optional -# Spatz binary in instrram (0xCC000000...). This mirrors what is done for +# Spatz binary in instrram (0xC0000000...). This mirrors what is done for # Spatz tasks via spatz/sw/Makefile and the `.spatz_binary` section. # ---------------------------------------------------------------------------- TEST_DIR_PATH := $(if $(_FOUND_TEST_DIR),$(_FOUND_TEST_DIR),sw/tests/$(test)) diff --git a/README.md b/README.md index 2bae4a2e..1cb0d150 100644 --- a/README.md +++ b/README.md @@ -99,14 +99,18 @@ The following *optional* parameters can be specified: `core`: **CV32E40P**|**CV32E40X** (**Default**: CV32E40P). Each core and each `mesh_dv` has its own build directory, so the models can be built side by side. -`VERILATOR_JOBS`: **N** (**Default**: 4). Parallelism used to *build* the model. +`VERILATOR_JOBS`: **N** (**Default**: 16). Parallelism used to *build* the model. Unrelated to simulation speed. -`VERILATOR_THREADS`: **N** (**Default**: 1). Threads the *simulation itself* runs on. See +`VERILATOR_THREADS`: **N** (**Default**: 4 for the mesh, 1 for the tile). Threads the *simulation itself* runs on. See the note below before changing it. `VERILATOR_FST`: **<file>** (**Default**: empty). Dump a waveform to this file. +`OBJCACHE`: **ccache** (**Default**: empty). Compiler cache for the model. Off by +default: ccache 3.x can serve objects of an older model with Verilator's +precompiled headers, and the model then segfaults at time zero. + **Instructions to build and run a Verilator simulation**: **1)** *Build* the model (`MAGIA` folder): @@ -176,8 +180,7 @@ To change the thread count, re-run `make verilate` with the new value. - `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. +- `VERILATOR_THREADS=4` is the mesh default, used by the regression. ## ⚙️ Architecture @@ -190,15 +193,149 @@ Each tile is controlled by a [CV32E40P](https://github.com/pulp-platform/cv32e40 `magia_tile` wraps `magia_isle` (cores, accelerators, L1, crossbars and Event Unit) with the FlooNoC network interface and router. What a tile contains is set by its `TileCfg` (`magia_tile_cfg_t` in `hw/mesh/magia_pkg.sv`). -#### PULP Cluster -Each tile embeds a cluster of `TileCfg.Cluster.NumCores` (8 by default) [CV32E40P](https://github.com/pulp-platform/cv32e40p) cores. Cluster cores share a Snitch instruction cache with an AXI refill path to L2; each core reaches the tile's L1, stack and reserved region through its own HCI port and everything else (accelerator registers, `PULP_CTRL`, remote memory) through its own OBI master port into the tile crossbar. The cluster has its own [Event Unit](https://github.com/pulp-platform/event_unit_flex), separate from the main core's, with a dispatch FIFO, hardware barriers and a hardware mutex. The main core dispatches a task to cluster core 0 through the `PULP_CTRL` register block (`0x1740`); core 0 can then spread it over the other cores through the cluster Event Unit. When the task ends, `PULP_CTRL` raises EU bit 12 on the main core's Event Unit. +Memories of a tile with the default `TileCfg`: + +| Memory | Size | Organization | +|---|---|---| +| L1 (TCDM) | 1 MiB: 128 KiB private + 896 KiB global | 32 banks x 8192 words x 32 bit | +| PULP cluster I$ | 4 KiB, shared by the 8 cores | 4 ways x 32 lines x 256 bit | +| Control-core I$ | 2 KiB | 4 ways x 32 lines x 128 bit | +| Spatz CC I$ | 2 KiB | 2 ways x 32 lines x 256 bit | ### Mesh Replicating the MAGIA tile, we scale up to a two-dimensional (2D) mesh of compute tiles. Each tile takes its `TileCfg` from the `TILE_CFGS` parameter of `magia` (`HOMO_TILE_CFGS` by default). The NoC allows access to the global west-side L2 through row-side interfaces, while tiles exchange traffic through FlooNoC router. The mesh uses XY routing and carries both AXI narrow channels (32-bit) and AXI wide channels (256-bit), with protocol conversion handled by per-tile Network Interfaces (NIs). Rendez-vous among tiles are managed through the FractalSync (FS) mechanism and the dedicated network. -#### Collective Operations +The routers also implement collective operations (multicast, broadcast, reduction, barrier) in +hardware: see [Collective Operations](#collective-operations). + + +### Memory map +The hardware sets where each memory and peripheral answers (`hw/tile/magia_tile_pkg.sv`); the +software sets where code, data and stacks go ([Software layout](#software-layout)). + +![MAGIA memory map](doc/memory_map.svg) + +Address space seen by tile k, with N tiles and tile j from 0 to N-1, ends included. The map holds +up to 16x16 tiles: on a 32x32 mesh the tile windows cover the Spatz CC BootROM and the collective +window overlaps the L2. + +| Start | End | Region | +|---------------------------------|-------------------------------------|--------| +| `j * 0x0010_0000` | `j * 0x0010_0000 + 0x0001_FFFF` | Unmapped | +| `j * 0x0010_0000 + 0x0002_0000` | `j * 0x0010_0000 + 0x000F_FFFF` | L1 of tile j, global part (896 KiB) | +| `N * 0x0010_0000` | `0x0FFF_FFFF` | Unmapped | +| `0x1000_0000` | `0x1000_00FF` | Spatz CC BootROM | +| `0x1000_0100` | `0x9FFF_FFFF` | Unmapped | +| `0xA000_0000` | `0xA000_4FFF` | Tile peripherals (below) | +| `0xA000_5000` | `0xA00F_FFFF` | Unmapped | +| `0xA010_0000` | `0xA011_FFFF` | L1 of tile k, private part (128 KiB) | +| `0xA012_0000` | `0xAFFF_FFFF` | Unmapped | +| `0xB000_0000` | `0xB000_0000 + N * 0x0010_0000 - 1` | Collective window | +| `0xB000_0000 + N * 0x0010_0000` | `0xBFFF_FFFF` | Unmapped | +| `0xC000_0000` | `0xFFFF_FFFF` | L2 (1 GiB) | + +A smaller L1 keeps the same map: with `N_WORDS_BANK` = 2048 (256 KiB) the global part ends at +`j * 0x0010_0000 + 0x0003_FFFF` and the rest of the window is unmapped. Set `NUM_L1_BANKS` and +`WORDS_BANK` in `sw/utils/magia_tile_utils.h` to the instantiated L1, and `L1_PRIV_SIZE` to +`TileCfg.L1.PrivBytes` if you change it; `L1_SIZE` and `SYNC_BASE` follow. + +**Tile peripherals.** Units every tile has come first, the optional ones after. An absent unit or +an unmapped address answers with an OBI error, which stops the test in simulation. + +| Start | End | Unit | +|---------------|---------------|------| +| `0xA000_0000` | `0xA000_0FFF` | Ctrl-core Event Unit | +| `0xA000_1000` | `0xA000_13FF` | iDMA CTRL | +| `0xA000_1400` | `0xA000_143F` | HCI CTRL | +| `0xA000_1440` | `0xA000_1FFF` | Free | +| `0xA000_2000` | `0xA000_20FF` | RedMulE CTRL (optional) | +| `0xA000_2100` | `0xA000_21FF` | FractalSync CTRL (optional) | +| `0xA000_2200` | `0xA000_223F` | Spatz CTRL (optional) | +| `0xA000_2300` | `0xA000_237F` | PULP CTRL (optional) | +| `0xA000_2400` | `0xA000_24FF` | Collective CTRL (optional) | +| `0xA000_2500` | `0xA000_2FFF` | Free | +| `0xA000_3000` | `0xA000_3FFF` | Cluster Event Unit, direct (cluster cores only) | +| `0xA000_4000` | `0xA000_4FFF` | Cluster Event Unit, SoC-side | + +**Simulation only**, decoded by the testbench VIP: + +| Address | Use | +|---------------|-----| +| `0xFFFF_0000` | Memory-mapped stderr sink | +| `0xFFFF_0004` | Memory-mapped stdio sink | + +### Software layout +Set by the software (`sw/utils/magia_tile_utils.h`, the linker scripts and the crt0 files). Every +tile runs the same ELF; the L2 part is contiguous from `0xC000_0000`. + +![MAGIA software layout](doc/sw_layout.svg) + +| Start | End | Memory | Region | +|---------------------------------|---------------------------------|------------|--------| +| `0xC000_0000` | `0xC000_7FFF` | L2 | Code: control core, then the embedded Spatz and PULP binaries | +| `0xC000_8000` | `0xC000_87FF` | L2 | Exit codes, 2 B per tile, read by the testbench | +| `0xC000_8800` | `0xC0FF_FFFF` | L2 | `.data`, `.rodata`, `.bss`, shared by every tile | +| `0xC100_0000` | `0xFFFE_FFFF` | L2 | Free | +| `0xFFFF_0000` | `0xFFFF_0007` | L2 | stderr and stdio sinks, decoded by the testbench | +| `0xFFFF_0008` | `0xFFFF_FFFF` | L2 | Free | +| `k * 0x0010_0000 + 0x0002_0000` | `k * 0x0010_0000 + 0x000F_EFFF` | L1 global | Data buffers (`L1_BASE + mhartid * L1_TILE_OFFSET`) | +| `k * 0x0010_0000 + 0x000F_F000` | `k * 0x0010_0000 + 0x000F_FFFF` | L1 global | Sync words (`SYNC_BASE`) | +| `0xA010_0000` | `0xA011_77FF` | L1 private | Scratch (`SCRATCH_BASE`) | +| `0xA011_7800` | `0xA011_B7FF` | L1 private | PULP stacks, 8 x 2 KiB | +| `0xA011_B800` | `0xA011_BFFF` | L1 private | Control-core stack, 2 KiB | +| `0xA011_C000` | `0xA011_FFFF` | L1 private | Spatz stack, 16 KiB | + +Stacks have the same address in every tile; data shared with other tiles, DMA or collectives goes +in the global part. + +## 🖥️ Programming model +The flow is memory-mapped (MM): software configures and starts accelerators by writing control registers in each tile address space. + +- Execution model: SPMD over tiles, with `mhartid` selecting `tile_base = mhartid * L1_TILE_OFFSET`. +- Control path: the control core (CV32E40P or CV32E40X) accesses RedMulE, iDMA, FractalSync, Event Unit, and Spatz control registers via MMIO. +- Data path: iDMA moves data between L1 and external memory, while compute engines consume/produce data in L1. +- Synchronization: Event Unit and FractalSync provide interrupt/event and barrier mechanisms for inter-tile coordination. + +Software APIs for MM control are under `sw/utils/` (for example `redmule_mm_utils.h`, `idma_mm_utils.h`, `fsync_mm_api.h`, `magia_spatz_utils.h` and `event_unit_utils.h`). + +### Accelerators +The control core drives each accelerator through its registers and waits for it by polling or on +its Event Unit (`event_unit_utils.h`). + +#### iDMA +Moves data between the tile's L1 and the L2 or another tile's L1 (`idma_mm_utils.h`): +`idma_L2ToL1(src, dst, len)` and `idma_L1ToL2(src, dst, len)` start a transfer, `dma_wait(id)` +waits for it. It also issues the wide collectives. + +#### RedMulE +GeMM engine on operands in L1 (`redmule_mm_utils.h`): `hwpe_acquire_job()` reserves it, +`redmule_cfg(..)` sets addresses, sizes and format, `hwpe_trigger_job()` starts it and +`hwpe_wait_for_completion()` waits for the end. + +#### Spatz CC +Vector core complex running tasks from the Spatz binary embedded in the CV32 ELF +(`magia_spatz_utils.h`): `spatz_init(..)` boots it, `spatz_run_task(..)` starts a task and +`eu_wait_spatz_polling(..)` waits for it. See [spatz/README.md](spatz/README.md). + +#### PULP Cluster +Each tile embeds a cluster of `TileCfg.Cluster.NumCores` (8 by default) [CV32E40P](https://github.com/pulp-platform/cv32e40p) cores with a shared instruction cache and their own [Event Unit](https://github.com/pulp-platform/event_unit_flex) (dispatch FIFO, hardware barriers and mutex). + +The PULP cluster uses a bare-metal, two-level dispatch model. The cluster binary is compiled as a position-independent ELF (origin `0x0`), converted to a flat binary, and embedded in the CV32 ELF as a byte array in the `.pulp_binary` section (see `sw/kernel_pulp/`). + +**Main core → cluster core 0** (`sw/utils/cluster_utils.h`, `sw/utils/magia_pulp_utils.h`): + +1. `cluster_boot(binary_start)` — writes `PULP_BINARY`, sets `PULP_FETCH_EN`, polls `PULP_READY` until all cluster cores have booted. +2. `cluster_arm_done_event()` — clears the CV32 Event Unit buffer and enables only EU bit 12 (cluster-done). +3. `cluster_dispatch_task(task_addr)` — writes `PULP_TASKBIN`/`PULP_DATA` and rings `PULP_START`; returns once core 0 has taken the task. +4. `cluster_wait_done_eu()` — the control core sleeps on a blocking Event Unit load (`cv.elw` on CV32E40P, `lw` on CV32E40X) until EU bit 12 fires. + +**Core 0 → the other cores** (`sw/utils/cluster_utils.h`): `pi_cl_team_fork(n, entry, arg)` runs `entry(arg)` on `n` cores through the cluster Event Unit and ends on a hardware barrier, as in the pulp-sdk API. + +Cluster task sources live under `sw/tests//pulp_task/`. A test directory containing a `pulp_task/` subdirectory automatically triggers the dual-binary build flow in the Makefile. + +### Collective Operations Beyond point-to-point traffic, the routers also implement **collective** operations in hardware: a single transaction can be replicated towards a whole row, column, or the entire mesh (multicast/broadcast), and many transactions can be aggregated on their way to a single destination (reduction, barrier). Two extra fields on the AXI `user` channel tag a transaction as collective: @@ -258,63 +395,6 @@ column, both a broadcast. Reductions use the same mask: the router derives the set of contributors it must wait for and merges their flits as they converge on the destination. - -### Memory map -This map reflects the RTL memory-mapped layout defined in `hw/tile/magia_tile_pkg.sv`. - -- `tile_base = mhartid * 0x0010_0000` - -Private map: the same range in every tile, each tile maps it to its own copy: - -| Region | Range | -|-------------------|---------------------------| -| *RedMulE CTRL* | `0x0000_0100-0x0000_01FF` | -| *iDMA CTRL* | `0x0000_0200-0x0000_05FF` | -| *FractalSync CTRL*| `0x0000_0600-0x0000_06FF` | -| *Ctrl-core Event Unit* | `0x0000_0700-0x0000_16FF` | -| *Spatz CTRL* | `0x0000_1700-0x0000_173F` | -| *PULP CTRL* | `0x0000_1740-0x0000_17BF` | -| *HCI CTRL* | `0x0000_17C0-0x0000_17FF` | -| *Collective CTRL* | `0x0000_1800-0x0000_18FF` | -| *Cluster Event Unit (direct)* | `0x0000_1900-0x0000_28FF` | -| *Cluster Event Unit (SoC-side)* | `0x0000_2900-0x0000_38FF` | -| *Reserved* | `0x0000_3900-0x0000_FFFF` | -| *Stack* | `0x0001_0000-0x0001_FFFF` | -| *Spatz BootROM* | `0x1000_0000-0x1000_00FF` | - -Shared/global map: - -| Region | Range | Notes | -|-------------------|---------------------------|-------| -| *L1 SPM* | `tile_base + 0x0002_0000 ... 0x000F_FFFF` | Reachable by every tile | -| *L2* | `0xC000_0000-0xFFFF_FFFF` | Global L2 window | -| *Instructions* | `0xCC00_0000-0xCC00_7FFF` | Instruction sub-region inside L2 | - -An access to an unmapped address of the peripheral window gets an OBI error, and in simulation it stops the test. - -Software/test utility addresses (used by SW runtime and testbench VIP): - -| Region | Address | Notes | -|-------------------|--------------------------------------------|-------| -| *Test End* | `0xCCFF_0000` | Exit code location used by SW runtime/tests | -| *String (utoa)* | `0x0000_3900` | String scratch location (`RESERVED_START + STR_OFFSET`) | -| *Print (stderr)* | `0xFFFF_0000` | Memory-mapped stderr sink in simulation VIP | -| *Print (stdio)* | `0xFFFF_0004` | Memory-mapped stdio sink in simulation VIP | -| *Synch.* | `tile_base + 0x000F_F000` | Top 4 KiB of L1 (`L1_BASE + SYNC_OFFSET`) | - -## 🖥️ Programming model -The flow is memory-mapped (MM): software configures and starts accelerators by writing control registers in each tile address space. - -- Execution model: SPMD over tiles, with `mhartid` selecting `tile_base = mhartid * 0x0010_0000`. -- Control path: the control core (CV32E40P or CV32E40X) accesses RedMulE, iDMA, FractalSync, Event Unit, and Spatz control registers via MMIO. -- Data path: iDMA moves data between L1 and external memory, while compute engines consume/produce data in L1. -- Synchronization: Event Unit and FractalSync provide interrupt/event and barrier mechanisms for inter-tile coordination. - -Software APIs for MM control are under `sw/utils/` (for example `redmule_mm_utils.h`, `idma_mm_utils.h`, `fsync_mm_api.h`, `magia_spatz_utils.h` and `event_unit_utils.h`). -For Spatz Core Complex programming flow (runtime handshake, task loading, and execution model), see [spatz/README.md](spatz/README.md). - - -### Collective Operations MAGIA supports collectives on both the narrow and the wide FlooNoC channel, through two independent programming paths. @@ -333,31 +413,18 @@ dma_wait(transfer_id_1); // poll for completion #### Narrow channel The narrow channel can be used to issue barrier, reduce and multicast transactions. Before issuing a collective over the narrow channel two tile CRSs must be properly configured: -| Register | Address | Meaning | -|---|---|---| -| `COLLECTIVE_MASK` | `tile_base + 0x0000_1800` | *Which* tiles take part | -| `COLLECTIVE_OP` | `tile_base + 0x0000_1804` | *What* the routers do with the flits | +| Register | Meaning | +|---|---| +| `COLLECTIVE_MASK` | *Which* tiles take part | +| `COLLECTIVE_OP` | *What* the routers do with the flits | -A store is then tagged collective if it is written at `COLLECTIVE_ADDR_OFFSET` (`0xB000_0000`). The APIs required to configure and use collective transactions over the narrow channel can be found in [magia_coll_utils.h](sw/utils/magia_coll_utils.h). +A store is then tagged collective if it is written at `COLLECTIVE_ADDR_OFFSET`, the collective window. The APIs required to configure and use collective transactions over the narrow channel can be found in [magia_coll_utils.h](sw/utils/magia_coll_utils.h). **Programming flow** 1. `set_collective_mask()` to set the `COLLECTIVE_MASK` register 2. `set_collective_op()` to set the `COLLECTIVE_OP` register -3. A store at address `0xB000_0000` is then interpreted as a collective transaction - -### PULP Cluster programming flow -The PULP cluster uses a bare-metal, two-level dispatch model. The cluster binary is compiled as a position-independent ELF (origin `0x0`), converted to a flat binary, and embedded in the CV32 ELF as a byte array in the `.pulp_binary` section (see `sw/kernel_pulp/`). - -**Main core → cluster core 0** (`sw/utils/cluster_utils.h`, `sw/utils/magia_pulp_utils.h`): +3. A store in the collective window is then interpreted as a collective transaction -1. `cluster_boot(binary_start)` — writes `PULP_BINARY`, sets `PULP_FETCH_EN`, polls `PULP_READY` until all cluster cores have booted. -2. `cluster_arm_done_event()` — clears the CV32 Event Unit buffer and enables only EU bit 12 (cluster-done). -3. `cluster_dispatch_task(task_addr)` — writes `PULP_TASKBIN`/`PULP_DATA` and rings `PULP_START`; returns once core 0 has taken the task. -4. `cluster_wait_done_eu()` — the control core sleeps on a blocking Event Unit load (`cv.elw` on CV32E40P, `lw` on CV32E40X) until EU bit 12 fires. - -**Core 0 → the other cores** (`sw/utils/cluster_utils.h`): `pi_cl_team_fork(n, entry, arg)` runs `entry(arg)` on `n` cores through the cluster Event Unit and ends on a hardware barrier, as in the pulp-sdk API. - -Cluster task sources live under `sw/tests//pulp_task/`. A test directory containing a `pulp_task/` subdirectory automatically triggers the dual-binary build flow in the Makefile. ## 🧰 Changing number of tiles **Supported Mesh Configurations**: `2x2`, `4x4`, `8x8`, `16x16`, `32x32` diff --git a/doc/memory_map.svg b/doc/memory_map.svg new file mode 100644 index 00000000..3829688a --- /dev/null +++ b/doc/memory_map.svg @@ -0,0 +1,161 @@ + + + + + + + + + Tiles 0 … k-1 + + Tiles k+1 … N-1 + + Tiles in mask + + L2 + + + NoC + + + + + + + + + + L1 of tiles 0 … k-1 · global parts + + + unmapped + + + L1 of tile k, global part · 896 KiB + + + L1 of tiles k+1 … N-1 · global parts + + + unmapped + + + BootROM Spatz CC · 256 B + + + unmapped + + + tile peripherals · 20 KiB + + + unmapped + + + L1 of tile k, private part · 128 KiB + + + unmapped + + + collective window + + + unmapped + + + L2 · 1 GiB + + + + + + + + + + + BootROM Spatz CC + + CSRs + Event Unit + + + TCDM · 1 MiB + + Global part + 896 KiB + + + Private part + 128 KiB + + + + + + + MAGIA Tile k + + + Tile peripherals + + + + fixed + + + optional + + + Event Unit + iDMA + HCI + free + RedMulE + FSync + Spatz + PULP + Coll. + free + Cluster EUdirect + Cluster EUSoC-side + + EU link + CTRL Demux + Cluster EU link + Cluster EU port + + 4 KiB + 1 KiB + 64 B + 256 B + 256 B + 64 B + 128 B + 256 B + 4 KiB + 4 KiB + diff --git a/doc/sw_layout.svg b/doc/sw_layout.svg new file mode 100644 index 00000000..c68d7968 --- /dev/null +++ b/doc/sw_layout.svg @@ -0,0 +1,59 @@ + + + + + L2 + one copy for every tile + + control-core code + + Spatz code + + PULP code + + free · rest of the 32 KiB of code + + exit codes · 2 B per tile + + .data .rodata .bss + + free · rest of the data region + + free · rest of the L2 + + + L1 private part · 128 KiB + same address in every tile, each sees its own + + scratch + 94 KiB + + PULP stacks + 8 cores × 2 KiB + + control-core stack + 2 KiB + + Spatz stack + 16 KiB + + + L1 global part · 896 KiB + in the tile window, reachable by every tile + + data buffers + 892 KiB + + sync words + 4 KiB + diff --git a/hw/mesh/magia_pkg.sv b/hw/mesh/magia_pkg.sv index 28540035..59a99848 100644 --- a/hw/mesh/magia_pkg.sv +++ b/hw/mesh/magia_pkg.sv @@ -58,6 +58,7 @@ package magia_pkg; typedef struct packed { int unsigned NumBanks; // Number of TCDM banks int unsigned NumWordsBank; // Number of words per TCDM bank + int unsigned PrivBytes; // Bytes at the bottom of L1, reachable only by the own tile } l1_cfg_t; typedef struct packed { @@ -117,7 +118,8 @@ package magia_pkg; localparam l1_cfg_t MagiaL1DefaultCfg = '{ NumBanks: N_MEM_BANKS, - NumWordsBank: N_WORDS_BANK + NumWordsBank: N_WORDS_BANK, + PrivBytes: 32'h0002_0000 // 128 KiB }; localparam ctrl_core_cfg_t MagiaCtrlCoreDefaultCfg = '{ diff --git a/hw/tile/magia_isle.sv b/hw/tile/magia_isle.sv index 68d09fc1..427c6b20 100644 --- a/hw/tile/magia_isle.sv +++ b/hw/tile/magia_isle.sv @@ -134,6 +134,8 @@ module magia_isle localparam int unsigned NumMemBanks = TileCfg.L1.NumBanks; localparam int unsigned NumWordsBank = TileCfg.L1.NumWordsBank; localparam int unsigned L1BankAddrW = $clog2(NumWordsBank * magia_tile_pkg::DW_LIC / magia_tile_pkg::BW_LIC); // Byte address within a bank + localparam logic [magia_pkg::ADDR_W-1:0] L1Bytes = magia_pkg::ADDR_W'(NumMemBanks) << L1BankAddrW; + localparam logic [magia_pkg::ADDR_W-1:0] L1PrivBytes = magia_pkg::ADDR_W'(TileCfg.L1.PrivBytes); localparam int unsigned NumHwpe = TileCfg.EnRedMule ? 1 : 0; // 0 drops the RedMulE leaf of the HCI interconnect localparam int unsigned NumDma = magia_tile_pkg::N_DMA; @@ -212,23 +214,37 @@ module magia_isle /** Tile Address Map Beginning **/ /*******************************************************/ - // L1 is global: each tile owns the slot at mhartid*L1_TILE_OFFSET +`ifndef SYNTHESIS + // The L1 offset is the address modulo L1_TILE_OFFSET, so the L1 must fit in a tile window + if (L1Bytes > magia_tile_pkg::L1_TILE_OFFSET) begin : gen_err_l1_size + $fatal(1, "magia_isle: the L1 does not fit in a tile window (L1_TILE_OFFSET)"); + end + if (L1PrivBytes == '0 || L1PrivBytes >= L1Bytes) begin : gen_err_l1_priv_size + $fatal(1, "magia_isle: TileCfg.L1.PrivBytes must leave both a private and a global part of L1"); + end + if (magia_tile_pkg::TILE_PERIPH_BASE % magia_tile_pkg::L1_TILE_OFFSET != 0) begin : gen_err_periph_base + $fatal(1, "magia_isle: TILE_PERIPH_BASE must be aligned to L1_TILE_OFFSET"); + end +`endif + + // One address per L1 byte: below L1PrivBytes the private view, above it the tile window + logic[magia_pkg::ADDR_W-1:0] tile_window_addr; 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_event_unit_start_addr; logic[magia_pkg::ADDR_W-1:0] tile_event_unit_end_addr; - assign tile_l1_start_addr = magia_tile_pkg::L1_ADDR_START + mhartid_i*magia_tile_pkg::L1_TILE_OFFSET; - assign tile_l1_end_addr = magia_tile_pkg::L1_ADDR_END + mhartid_i*magia_tile_pkg::L1_TILE_OFFSET; + assign tile_window_addr = magia_tile_pkg::L1_ADDR_START + mhartid_i*magia_tile_pkg::L1_TILE_OFFSET; + assign tile_l1_start_addr = tile_window_addr + L1PrivBytes; + assign tile_l1_end_addr = tile_window_addr + L1Bytes; assign tile_event_unit_start_addr = magia_tile_pkg::CTRL_EU_ADDR_START; assign tile_event_unit_end_addr = magia_tile_pkg::CTRL_EU_ADDR_END; // TCDM ranges of core data, idx 0 of every core data demux magia_tile_pkg::obi_xbar_rule_t [magia_tile_pkg::NUM_TCDM_RULES-1:0] tcdm_rules; - // Reserved and stack are a private alias, the same in every tile, the L1 is shifted per tile - assign tcdm_rules[0] = '{idx: 0, start_addr: magia_tile_pkg::RESERVED_ADDR_START, end_addr: magia_tile_pkg::STACK_ADDR_END}; - assign tcdm_rules[1] = '{idx: 0, start_addr: tile_l1_start_addr, end_addr: tile_l1_end_addr }; + assign tcdm_rules[0] = '{idx: 0, start_addr: magia_tile_pkg::L1_PRIV_ADDR_START, end_addr: magia_tile_pkg::L1_PRIV_ADDR_START + L1PrivBytes}; + assign tcdm_rules[1] = '{idx: 0, start_addr: tile_l1_start_addr, end_addr: tile_l1_end_addr }; /*******************************************************/ /** Tile Address Map End **/ @@ -774,40 +790,30 @@ module magia_isle assign obi_xbar_slv_req[ObiMgr.ext] = ext_obi_data_req; assign ext_obi_data_rsp = obi_xbar_slv_rsp[ObiMgr.ext]; - localparam int unsigned RuleL2 = 0; - localparam int unsigned RuleL1 = 1; - localparam int unsigned RuleRes = 2; - localparam int unsigned RuleStack = 3; - localparam int unsigned RuleCtrl = 4; - localparam int unsigned RuleEu = 5; - localparam int unsigned RuleCtrlHi = 6; - localparam int unsigned RuleClusterEu = 7; - - localparam logic [magia_pkg::ADDR_W-1:0] CtrlHiEnd = TileCfg.EnCluster ? - magia_tile_pkg::CLUSTER_EU_ADDR_START : magia_tile_pkg::TILE_PERIPH_ADDR_END; + // The last matching rule wins in addr_decode, so every rule after RuleCtrl overrides it + localparam int unsigned RuleCtrl = 0; + localparam int unsigned RuleL2 = 1; + localparam int unsigned RuleL1 = 2; + localparam int unsigned RuleL1Priv = 3; + localparam int unsigned RuleEu = 4; + localparam int unsigned RuleClusterEu = 5; magia_tile_pkg::obi_xbar_rule_t[ObiSbr.num_rules-1:0] obi_xbar_rule; logic[ObiMgr.num_mgr-1:0] obi_xbar_en_default_idx; logic[ObiMgr.num_mgr-1:0][magia_tile_pkg::gen_idx_width(ObiSbr.num_sbr)-1:0] obi_xbar_default_idx; - assign obi_xbar_rule[RuleL2] = '{idx: ObiSbr.l2, start_addr: magia_tile_pkg::L2_ADDR_START, end_addr: magia_tile_pkg::L2_ADDR_END }; - assign obi_xbar_rule[RuleL1] = '{idx: ObiSbr.l1, start_addr: tile_l1_start_addr, end_addr: tile_l1_end_addr }; - assign obi_xbar_rule[RuleRes] = '{idx: ObiSbr.l1, start_addr: magia_tile_pkg::RESERVED_ADDR_START, end_addr: magia_tile_pkg::RESERVED_ADDR_END }; - assign obi_xbar_rule[RuleStack] = '{idx: ObiSbr.l1, start_addr: magia_tile_pkg::STACK_ADDR_START, end_addr: magia_tile_pkg::STACK_ADDR_END }; - // The control window is split around the Event Units: every address matches one rule - assign obi_xbar_rule[RuleCtrl] = '{idx: ObiSbr.ctrl, start_addr: magia_tile_pkg::TILE_PERIPH_BASE, end_addr: tile_event_unit_start_addr }; - assign obi_xbar_rule[RuleEu] = '{idx: ObiSbr.eu, start_addr: tile_event_unit_start_addr, end_addr: tile_event_unit_end_addr }; - assign obi_xbar_rule[RuleCtrlHi] = '{idx: ObiSbr.ctrl, start_addr: tile_event_unit_end_addr, end_addr: CtrlHiEnd }; + // Control spans the peripheral and the private L1 slots: their holes get the ctrl_demux error + assign obi_xbar_rule[RuleCtrl] = '{idx: ObiSbr.ctrl, start_addr: magia_tile_pkg::TILE_PERIPH_BASE, end_addr: magia_tile_pkg::L1_PRIV_ADDR_START + magia_tile_pkg::L1_TILE_OFFSET}; + assign obi_xbar_rule[RuleL2] = '{idx: ObiSbr.l2, start_addr: magia_tile_pkg::L2_ADDR_START, end_addr: magia_tile_pkg::L2_ADDR_END }; + assign obi_xbar_rule[RuleL1] = '{idx: ObiSbr.l1, start_addr: tcdm_rules[1].start_addr, end_addr: tcdm_rules[1].end_addr }; + assign obi_xbar_rule[RuleL1Priv] = '{idx: ObiSbr.l1, start_addr: tcdm_rules[0].start_addr, end_addr: tcdm_rules[0].end_addr }; + assign obi_xbar_rule[RuleEu] = '{idx: ObiSbr.eu, start_addr: tile_event_unit_start_addr, end_addr: tile_event_unit_end_addr }; if (TileCfg.EnCluster) begin: gen_cluster_eu_rule assign obi_xbar_rule[RuleClusterEu] = '{idx: ObiSbr.cluster_eu, start_addr: magia_tile_pkg::CLUSTER_EU_ADDR_START, end_addr: magia_tile_pkg::CLUSTER_EU_ADDR_END}; - if (magia_tile_pkg::CLUSTER_EU_ADDR_END != magia_tile_pkg::TILE_PERIPH_ADDR_END) - begin: gen_err_cluster_eu_not_last - $fatal(1, "magia_isle: peripherals after the cluster EU need a control rule above it"); - end end - // Anything outside L1 and the peripheral window goes to the AXI crossbar + // Any other address goes to the AXI crossbar assign obi_xbar_en_default_idx = '1; assign obi_xbar_default_idx = '0; @@ -1134,7 +1140,7 @@ module magia_isle assign axi_xbar_mst_rsp[AxiMst.ext] = axi_narrow_mst_rsp_i; - // Remote accesses to this tile's L1 and reserved region reach the OBI crossbar + // Remote accesses to this tile's window reach the OBI crossbar logic[magia_tile_pkg::AID_WIDTH-1:0] axi2obi_req_write_aid; logic[magia_tile_pkg::AUSER_WIDTH-1:0] axi2obi_req_write_auser; logic[magia_tile_pkg::WUSER_WIDTH-1:0] axi2obi_req_write_wuser; @@ -1366,6 +1372,23 @@ module magia_isle $fatal(1, "magia_isle: TileCfg.L1 (%0d banks x %0d words) exceeds SPATZ_TCDM_ADDR_WIDTH=%0d", NumMemBanks, NumWordsBank, magia_tile_pkg::SPATZ_TCDM_ADDR_WIDTH); end + + // The bank decode drops the bits above L1Bytes and HCI has no error response, so a hole would alias + for (genvar i = 0; i < NumHciCore; i++) begin : gen_l1_range_core_assert + assert property (@(posedge sys_clk) disable iff (!rst_ni) + hci_core_if[i].req |-> (hci_core_if[i].add % magia_tile_pkg::L1_TILE_OFFSET) < L1Bytes) + else $fatal(1, "magia_isle: HCI core port %0d accesses beyond TileCfg.L1", i); + end + for (genvar i = 0; i < NumDma; i++) begin : gen_l1_range_dma_assert + assert property (@(posedge sys_clk) disable iff (!rst_ni) + hci_dma_if[i].req |-> (hci_dma_if[i].add % magia_tile_pkg::L1_TILE_OFFSET) < L1Bytes) + else $fatal(1, "magia_isle: HCI DMA port %0d accesses beyond TileCfg.L1", i); + end + for (genvar i = 0; i < NumHwpe; i++) begin : gen_l1_range_hwpe_assert + assert property (@(posedge sys_clk) disable iff (!rst_ni) + hci_redmule_if[i].req |-> (hci_redmule_if[i].add % magia_tile_pkg::L1_TILE_OFFSET) < L1Bytes) + else $fatal(1, "magia_isle: RedMulE accesses beyond TileCfg.L1"); + end `endif /*******************************************************/ @@ -1831,7 +1854,7 @@ module magia_isle .test_mode_i ( test_mode_i ), .hart_id_i ( 32'(HartIdBase) + mhartid_i ), - .tcdm_addr_base_i ( tile_l1_start_addr ), + .tcdm_addr_base_i ( tile_window_addr ), // Snitch needs an aligned TCDM base .irq_i ( spatz_irq ), diff --git a/hw/tile/magia_tile_pkg.sv b/hw/tile/magia_tile_pkg.sv index 4c3da9b2..560ab4b3 100644 --- a/hw/tile/magia_tile_pkg.sv +++ b/hw/tile/magia_tile_pkg.sv @@ -46,66 +46,57 @@ package magia_tile_pkg; // Address map - // Bases of the tile peripherals and of the tile window, default '0 + // Base of the tile peripherals and of the private L1 view, the same in every tile, 1 MiB aligned `ifdef MAGIA_TILE_PERIPH_BASE localparam logic [magia_pkg::ADDR_W-1:0] TILE_PERIPH_BASE = `MAGIA_TILE_PERIPH_BASE; `else - localparam logic [magia_pkg::ADDR_W-1:0] TILE_PERIPH_BASE = 32'h0000_0000; + localparam logic [magia_pkg::ADDR_W-1:0] TILE_PERIPH_BASE = 32'hA000_0000; `endif + // Base of the tile windows, one per tile, each holding the whole L1 of its tile `ifdef MAGIA_TILE_WINDOW_BASE localparam logic [magia_pkg::ADDR_W-1:0] TILE_WINDOW_BASE = `MAGIA_TILE_WINDOW_BASE; `else localparam logic [magia_pkg::ADDR_W-1:0] TILE_WINDOW_BASE = 32'h0000_0000; `endif - // Peripherals (private): chained control registers - localparam logic [magia_pkg::ADDR_W-1:0] REDMULE_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_0100; - localparam logic [magia_pkg::ADDR_W-1:0] REDMULE_CTRL_SIZE = 32'h0000_0100; - localparam logic [magia_pkg::ADDR_W-1:0] REDMULE_CTRL_ADDR_END = REDMULE_CTRL_ADDR_START + REDMULE_CTRL_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] IDMA_CTRL_ADDR_START = REDMULE_CTRL_ADDR_END; + // Peripherals (private), at fixed offsets: units of every tile first, then the optional ones + localparam logic [magia_pkg::ADDR_W-1:0] CTRL_EU_ADDR_START = TILE_PERIPH_BASE + 32'h0000_0000; + localparam logic [magia_pkg::ADDR_W-1:0] CTRL_EU_SIZE = 32'h0000_1000; + localparam logic [magia_pkg::ADDR_W-1:0] CTRL_EU_ADDR_END = CTRL_EU_ADDR_START + CTRL_EU_SIZE; + localparam logic [magia_pkg::ADDR_W-1:0] IDMA_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_1000; localparam logic [magia_pkg::ADDR_W-1:0] IDMA_CTRL_SIZE = 32'h0000_0400; localparam logic [magia_pkg::ADDR_W-1:0] IDMA_CTRL_ADDR_END = IDMA_CTRL_ADDR_START + IDMA_CTRL_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] FSYNC_CTRL_ADDR_START = IDMA_CTRL_ADDR_END; + localparam logic [magia_pkg::ADDR_W-1:0] HCI_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_1400; + localparam logic [magia_pkg::ADDR_W-1:0] HCI_CTRL_SIZE = 32'h0000_0040; + localparam logic [magia_pkg::ADDR_W-1:0] HCI_CTRL_ADDR_END = HCI_CTRL_ADDR_START + HCI_CTRL_SIZE; + // Optional units: the range of an absent one answers with an error + localparam logic [magia_pkg::ADDR_W-1:0] REDMULE_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_2000; + localparam logic [magia_pkg::ADDR_W-1:0] REDMULE_CTRL_SIZE = 32'h0000_0100; + localparam logic [magia_pkg::ADDR_W-1:0] REDMULE_CTRL_ADDR_END = REDMULE_CTRL_ADDR_START + REDMULE_CTRL_SIZE; + localparam logic [magia_pkg::ADDR_W-1:0] FSYNC_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_2100; localparam logic [magia_pkg::ADDR_W-1:0] FSYNC_CTRL_SIZE = 32'h0000_0100; localparam logic [magia_pkg::ADDR_W-1:0] FSYNC_CTRL_ADDR_END = FSYNC_CTRL_ADDR_START + FSYNC_CTRL_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] CTRL_EU_ADDR_START = FSYNC_CTRL_ADDR_END; - localparam logic [magia_pkg::ADDR_W-1:0] CTRL_EU_SIZE = 32'h0000_1000; - localparam logic [magia_pkg::ADDR_W-1:0] CTRL_EU_ADDR_END = CTRL_EU_ADDR_START + CTRL_EU_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] SPATZ_CTRL_ADDR_START = CTRL_EU_ADDR_END; + localparam logic [magia_pkg::ADDR_W-1:0] SPATZ_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_2200; localparam logic [magia_pkg::ADDR_W-1:0] SPATZ_CTRL_SIZE = 32'h0000_0040; localparam logic [magia_pkg::ADDR_W-1:0] SPATZ_CTRL_ADDR_END = SPATZ_CTRL_ADDR_START + SPATZ_CTRL_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_CTRL_ADDR_START = SPATZ_CTRL_ADDR_END; + localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_2300; localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_CTRL_SIZE = 32'h0000_0080; localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_CTRL_ADDR_END = CLUSTER_CTRL_ADDR_START + CLUSTER_CTRL_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] HCI_CTRL_ADDR_START = CLUSTER_CTRL_ADDR_END; - localparam logic [magia_pkg::ADDR_W-1:0] HCI_CTRL_SIZE = 32'h0000_0040; - localparam logic [magia_pkg::ADDR_W-1:0] HCI_CTRL_ADDR_END = HCI_CTRL_ADDR_START + HCI_CTRL_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] COLL_CTRL_ADDR_START = HCI_CTRL_ADDR_END; + localparam logic [magia_pkg::ADDR_W-1:0] COLL_CTRL_ADDR_START = TILE_PERIPH_BASE + 32'h0000_2400; localparam logic [magia_pkg::ADDR_W-1:0] COLL_CTRL_SIZE = 32'h0000_0100; localparam logic [magia_pkg::ADDR_W-1:0] COLL_CTRL_ADDR_END = COLL_CTRL_ADDR_START + COLL_CTRL_SIZE; // Cluster-private Event Unit: direct link of the cluster cores, and memory-mapped view on the OBI crossbar - localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_DIRECT_START = COLL_CTRL_ADDR_END; + localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_DIRECT_START = TILE_PERIPH_BASE + 32'h0000_3000; localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_DIRECT_SIZE = 32'h0000_1000; localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_DIRECT_END = CLUSTER_EU_DIRECT_START + CLUSTER_EU_DIRECT_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_ADDR_START = CLUSTER_EU_DIRECT_END; + localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_ADDR_START = TILE_PERIPH_BASE + 32'h0000_4000; localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_SIZE = 32'h0000_1000; localparam logic [magia_pkg::ADDR_W-1:0] CLUSTER_EU_ADDR_END = CLUSTER_EU_ADDR_START + CLUSTER_EU_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] TILE_PERIPH_ADDR_END = CLUSTER_EU_ADDR_END; - - // Reserved (private): software scratch, such as the print buffer - localparam logic [magia_pkg::ADDR_W-1:0] RESERVED_ADDR_START = TILE_WINDOW_BASE + (TILE_PERIPH_ADDR_END - TILE_PERIPH_BASE); - localparam logic [magia_pkg::ADDR_W-1:0] RESERVED_ADDR_END = TILE_WINDOW_BASE + 32'h0001_0000; - - // Stack (private) - localparam logic [magia_pkg::ADDR_W-1:0] STACK_ADDR_START = RESERVED_ADDR_END; - localparam logic [magia_pkg::ADDR_W-1:0] STACK_SIZE = 32'h0001_0000; - localparam logic [magia_pkg::ADDR_W-1:0] STACK_ADDR_END = STACK_ADDR_START + STACK_SIZE; - - // L1 (shared) - localparam logic [magia_pkg::ADDR_W-1:0] L1_ADDR_START = STACK_ADDR_END; - localparam logic [magia_pkg::ADDR_W-1:0] L1_SIZE = 32'h000E_0000; - localparam logic [magia_pkg::ADDR_W-1:0] L1_ADDR_END = L1_ADDR_START + L1_SIZE; - localparam logic [magia_pkg::ADDR_W-1:0] L1_TILE_OFFSET = 32'h0010_0000; + + // L1 of each tile: the first TileCfg.L1.PrivBytes are private, the rest global + localparam logic [magia_pkg::ADDR_W-1:0] L1_TILE_OFFSET = 32'h0010_0000; // Tile window size, tile k's at L1_ADDR_START + k*L1_TILE_OFFSET + localparam logic [magia_pkg::ADDR_W-1:0] L1_ADDR_START = TILE_WINDOW_BASE; // Window of tile 0: the global part of each L1 is in its window + localparam logic [magia_pkg::ADDR_W-1:0] L1_PRIV_ADDR_START = TILE_PERIPH_BASE + L1_TILE_OFFSET; // Private part of each L1, the same address in every tile localparam logic [magia_pkg::ADDR_W-1:0] TILES_ADDR_END = TILE_WINDOW_BASE + magia_pkg::N_TILES*L1_TILE_OFFSET; // End of all tile windows // Collectives and L2 (global) @@ -116,7 +107,7 @@ package magia_tile_pkg; localparam logic [magia_pkg::ADDR_W-1:0] L2_ADDR_END = L2_ADDR_START + L2_SIZE; // Instruction region for Spatz code (cacheable region) - localparam logic [magia_pkg::ADDR_W-1:0] INSTRRAM_ADDR_START = 32'hCC00_0000; + localparam logic [magia_pkg::ADDR_W-1:0] INSTRRAM_ADDR_START = L2_ADDR_START; localparam logic [magia_pkg::ADDR_W-1:0] INSTRRAM_PMA_MASK = 32'hFFFF_8000; @@ -367,12 +358,12 @@ package magia_tile_pkg; typedef struct packed { int unsigned num_units; // Number of units behind the control port (one address rule each) - int unsigned redmule; // RedMulE control (valid iff EnRedMule) int unsigned idma; // iDMA control + int unsigned hci; // HCI arbiter control + int unsigned redmule; // RedMulE control (valid iff EnRedMule) int unsigned fsync; // FractalSync control (valid iff MAGIA_FSYNC) int unsigned spatz; // Spatz CC control (valid iff EnSpatzCC) int unsigned cluster; // PULP cluster control (valid iff EnCluster) - int unsigned hci; // HCI arbiter control int unsigned coll; // Collective control (valid iff EnCollective) } ctrl_map_t; @@ -381,12 +372,14 @@ package magia_tile_pkg; int unsigned idx; ret = '0; idx = 0; + ret.idma = idx; + idx = idx + 1; + ret.hci = idx; + idx = idx + 1; if (cfg.EnRedMule) begin ret.redmule = idx; idx = idx + 1; end - ret.idma = idx; - idx = idx + 1; if (magia_pkg::MagiaEnFractalSync) begin ret.fsync = idx; idx = idx + 1; @@ -399,8 +392,6 @@ package magia_tile_pkg; ret.cluster = idx; idx = idx + 1; end - ret.hci = idx; - idx = idx + 1; if (cfg.EnCollective) begin ret.coll = idx; idx = idx + 1; @@ -430,8 +421,8 @@ package magia_tile_pkg; ret.cluster_eu = 4; end ret.num_sbr = 4 + 32'(cfg.EnCluster); - // L2, L1, reserved, stack, EU and the control window around it, plus the cluster EU - ret.num_rules = 7 + 32'(cfg.EnCluster); + // L2, the global and the private part of L1, EU, control, plus the cluster EU + ret.num_rules = 5 + 32'(cfg.EnCluster); return ret; endfunction @@ -522,7 +513,7 @@ package magia_tile_pkg; parameter int unsigned L0_LINE_COUNT = 32; // i$ L0 Cache Line Count parameter int unsigned LINE_WIDTH = 128; // i$ Cache Line Width; >= 64 parameter int unsigned LINE_COUNT = 32; // i$ The number of cache lines per set. Power of two; >= 2. - parameter int unsigned WAY_COUNT = 32; // i$ The set associativity of the cache. Power of two; >= 1. + parameter int unsigned WAY_COUNT = 4; // i$ The set associativity of the cache. Power of two; >= 1. parameter int unsigned L0_PARITY_W = 0; // i$ Parity of the L0 cache parameter int unsigned FETCH_AW = magia_pkg::ADDR_W; // i$ Fetch interface address width. Same as FETCH_AW; >= 1. parameter int unsigned FETCH_DW = magia_pkg::DATA_W; // i$ Fetch interface data width. Power of two; >= 8. @@ -566,7 +557,7 @@ package magia_tile_pkg; logic[magia_pkg::ADDR_W-1:0] end_addr; } obi_xbar_rule_t; - // TCDM ranges taken by the dedicated HCI ports: the private reserved and stack regions, the shared L1 + // TCDM ranges taken by the dedicated HCI ports: the private and the global part of L1 localparam int unsigned NUM_TCDM_RULES = 2; typedef obi_xbar_rule_t [CTRL_MAX_UNITS-1:0] ctrl_rules_t; @@ -577,10 +568,11 @@ package magia_tile_pkg; ctrl_map_t map; ret = '0; map = gen_ctrl_map(cfg); + ret[map.idma] = '{idx: map.idma, start_addr: IDMA_CTRL_ADDR_START, end_addr: IDMA_CTRL_ADDR_END}; + ret[map.hci] = '{idx: map.hci, start_addr: HCI_CTRL_ADDR_START, end_addr: HCI_CTRL_ADDR_END}; if (cfg.EnRedMule) begin ret[map.redmule] = '{idx: map.redmule, start_addr: REDMULE_CTRL_ADDR_START, end_addr: REDMULE_CTRL_ADDR_END}; end - ret[map.idma] = '{idx: map.idma, start_addr: IDMA_CTRL_ADDR_START, end_addr: IDMA_CTRL_ADDR_END}; if (magia_pkg::MagiaEnFractalSync) begin ret[map.fsync] = '{idx: map.fsync, start_addr: FSYNC_CTRL_ADDR_START, end_addr: FSYNC_CTRL_ADDR_END}; end @@ -590,7 +582,6 @@ package magia_tile_pkg; if (cfg.EnCluster) begin ret[map.cluster] = '{idx: map.cluster, start_addr: CLUSTER_CTRL_ADDR_START, end_addr: CLUSTER_CTRL_ADDR_END}; end - ret[map.hci] = '{idx: map.hci, start_addr: HCI_CTRL_ADDR_START, end_addr: HCI_CTRL_ADDR_END}; if (cfg.EnCollective) begin ret[map.coll] = '{idx: map.coll, start_addr: COLL_CTRL_ADDR_START, end_addr: COLL_CTRL_ADDR_END}; end @@ -690,14 +681,6 @@ package magia_tile_pkg; logic[NR_FETCH_PORTS-1:0] rerror; } core_cache_instr_rsp_t; - typedef enum logic[2:0]{ - AXI_XBAR_STACK_IDX = 4, - AXI_XBAR_RESERVED_IDX = 3, - AXI_XBAR_L1SPM_IDX = 2, - AXI_XBAR_BOOTROM_IDX = 1, - AXI_XBAR_L2_IDX = 0 - } axi_mem_array_idx_e; - typedef enum logic[2:0]{ AXI_SLV_CLUSTER_INSTR_IDX = 4, AXI_SLV_SPATZ_INSTR_IDX = 3, diff --git a/hw/tile/obi_slave_ctrl_cluster.sv b/hw/tile/obi_slave_ctrl_cluster.sv index 030feb57..3688cae8 100644 --- a/hw/tile/obi_slave_ctrl_cluster.sv +++ b/hw/tile/obi_slave_ctrl_cluster.sv @@ -40,7 +40,7 @@ module obi_slave_ctrl_cluster ); //----------------------------------------------------------------------------- -// Register map (offsets from BaseAddr; instantiated inside gen_pulp_cluster at CLUSTER_CTRL_ADDR_START = 0x1740) +// Register map (offsets from BaseAddr; instantiated inside gen_pulp_cluster at CLUSTER_CTRL_ADDR_START) // 0x00 FETCH_EN : RW broadcast fetch_enable, resets READY; sticky in cv32e40p, so 0 does not stop the cores // 0x04 BINARY : RW PULP binary entry point (boot address) // 0x08 DONE : W the dispatcher core writes 1 after the task returns -> done_o pulses @@ -163,7 +163,7 @@ end always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin fetch_en_q <= 1'b0; - entry_point_q <= 32'hCC000080; + entry_point_q <= INSTRRAM_ADDR_START + 32'h80; // _start of the ELF, after .vectors done_q <= 1'b0; taskbin_q <= 32'h0; data_q <= 32'h0; diff --git a/hw/tile/spatz_bootrom.sv b/hw/tile/spatz_bootrom.sv index 6572b3d0..6e16344e 100644 --- a/hw/tile/spatz_bootrom.sv +++ b/hw/tile/spatz_bootrom.sv @@ -21,8 +21,8 @@ module spatz_bootrom #( const logic [RomSize-1:0][DataWidth-1:0] mem = { 32'h00030067, 32'h0002a303, - 32'h70c28293, - 32'h000012b7 + 32'h20c28293, + 32'ha00022b7 }; // Sequential read with registered address diff --git a/scripts/generate_spatz_bootrom.py b/scripts/generate_spatz_bootrom.py index b85eb8dc..47cd4c29 100644 --- a/scripts/generate_spatz_bootrom.py +++ b/scripts/generate_spatz_bootrom.py @@ -73,7 +73,7 @@ def generate_bootrom_sv(bin_file, output_file): end // Return data based on registered address - assign rdata_o = (addr_q < RomSize) ? mem[addr_q] : '0; + assign rdata_o = (32'(addr_q) < RomSize) ? mem[addr_q] : '0; endmodule ''' diff --git a/scripts/s19tomem.py b/scripts/s19tomem.py index e0165477..3c6c6344 100755 --- a/scripts/s19tomem.py +++ b/scripts/s19tomem.py @@ -18,11 +18,10 @@ import numpy as np import sys -# Default memory layout: main-core ELF at 0xCC000000. -# Override with optional argv[4] (instr base) and argv[5] (data base) -# for the PULP cluster-core ELF which lives at 0xC0000000 / 0xC0100000. -DEFAULT_MEM_START = 0xcc000000 -DEFAULT_DATA_OFFSET = 0x10000 # data = instr_base + 0x10000 (main) or explicit +# Default memory layout: main-core ELF at 0xC0000000, as in sw/kernel/link.ld. +# Override with optional argv[4] (instr base) and argv[5] (data base). +DEFAULT_MEM_START = 0xc0000000 +DEFAULT_DATA_OFFSET = 0x8800 # dataram of sw/kernel/link.ld, after the exit codes if len(sys.argv) >= 5: MEM_START = int(sys.argv[4], 16) @@ -36,14 +35,11 @@ INSTR_SIZE = 0x8000 INSTR_END = MEM_START + INSTR_SIZE -DATA_SIZE = 0x30000 +DATA_SIZE = 0x40000 DATA_END = DATA_BASE + DATA_SIZE -STACK_BASE = 0x10000 -STACK_SIZE = 0x10000 -STACK_END = STACK_BASE + STACK_SIZE INSTR_MEM_SIZE = INSTR_SIZE -DATA_MEM_SIZE = DATA_SIZE + STACK_SIZE +DATA_MEM_SIZE = DATA_SIZE with open(sys.argv[1], "r") as f: s = f.read() diff --git a/spatz/README.md b/spatz/README.md index ebf960a3..8983e038 100644 --- a/spatz/README.md +++ b/spatz/README.md @@ -24,17 +24,17 @@ Spatz CC is controlled through an **OBI slave interface** (`obi_slave_ctrl_spatz ### Control Registers -The OBI slave exposes the following registers (base address: `SPATZ_CTRL_BASE = 0x00001700`): +The OBI slave exposes the following registers (base address: `SPATZ_CTRL_BASE = 0xA0002200`): | Register Name | Offset | Address | Description | |------------------|--------|------------|----------------------------------------------------------------------------------------------------------| -| `SPATZ_CLK_EN` | 0x00 | 0x00001700 | Clock enable (write 1 to enable, 0 to disable Spatz CC clock) | -| `SPATZ_READY` | 0x04 | 0x00001704 | Ready flag: Spatz writes 1 when ready for interrupts, 0 after first task; CV32 polls before triggering | -| `SPATZ_START` | 0x08 | 0x00001708 | Trigger register: write 1 to send interrupt to Spatz CC, Snitch clears it to 0 | -| `SPATZ_TASKBIN` | 0x0C | 0x0000170C | Task binary address: CV32 writes task entry point address, Snitch reads it | -| `SPATZ_DATA` | 0x10 | 0x00001710 | Data/parameter pointer: CV32 writes pointer to task parameters, Snitch reads it | -| `SPATZ_RETURN` | 0x14 | 0x00001714 | Return value: Snitch writes task exit code (0=success, non-zero=error/exception), CV32 reads it | -| `SPATZ_DONE` | 0x18 | 0x00001718 | Done flag: Snitch writes 1 when task completes, generates 1-cycle pulse to Event Unit, auto-clears | +| `SPATZ_CLK_EN` | 0x00 | 0xA0002200 | Clock enable (write 1 to enable, 0 to disable Spatz CC clock) | +| `SPATZ_READY` | 0x04 | 0xA0002204 | Ready flag: Spatz writes 1 when ready for interrupts, 0 after first task; CV32 polls before triggering | +| `SPATZ_START` | 0x08 | 0xA0002208 | Trigger register: write 1 to send interrupt to Spatz CC, Snitch clears it to 0 | +| `SPATZ_TASKBIN` | 0x0C | 0xA000220C | Task binary address: CV32 writes task entry point address, Snitch reads it | +| `SPATZ_DATA` | 0x10 | 0xA0002210 | Data/parameter pointer: CV32 writes pointer to task parameters, Snitch reads it | +| `SPATZ_RETURN` | 0x14 | 0xA0002214 | Return value: Snitch writes task exit code (0=success, non-zero=error/exception), CV32 reads it | +| `SPATZ_DONE` | 0x18 | 0xA0002218 | Done flag: Snitch writes 1 when task completes, generates 1-cycle pulse to Event Unit, auto-clears | **Register Behavior:** - **CLK_EN, START, READY, TASKBIN, DATA, RETURN**: Preserved registers - values persist until overwritten @@ -178,7 +178,7 @@ Defined in `hw/tile/magia_tile_pkg.sv`. See [Spatz documentation](https://github 3. **OBI Slave Interface (Control Registers):** - Spatz CC exposes an **OBI slave interface** (`obi_slave_ctrl_spatz`) for CV32 control - - Base address: `0x00001700` + - Base address: `0xA0002200` - Contains 7 control registers: `SPATZ_CLK_EN`, `SPATZ_READY`, `SPATZ_START`, `SPATZ_TASKBIN`, `SPATZ_DATA`, `SPATZ_RETURN`, `SPATZ_DONE` - CV32 accesses these registers via OBI to command Spatz CC operation --- @@ -205,7 +205,7 @@ uint32_t spatz_get_exit_code(void); // Read exit code from The bootROM is **extremely minimal** (only 3 instructions) to minimize hardware impact and ROM area. It performs only: ```assembly // Read dispatcher entry point from TASKBIN register (set by CV32) -li t0, TASKBIN_ADDR // 0x0000170C +li t0, TASKBIN_ADDR // 0xA000220C lw t1, 0(t0) jr t1 // Jump to spatz_crt0.S _start ``` @@ -217,7 +217,7 @@ The CV32 must write the Spatz CC binary's `_start` address to `SPATZ_TASKBIN` ** The **spatz_crt0.S** runtime performs complete initialization and enters the task dispatcher loop. It is designed to be **completely transparent** to the programmer. **Initialization Steps:** -1. **Stack Setup:** `sp = 0x0001FFF8` (128KB - 8 bytes, stack at top of Snitch local memory) +1. **Stack Setup:** `sp = 0xA011FFF8` (top of the Spatz slot of the stacks, in the private L1 view) 2. **Register Clearing:** All integer registers `x1-x31` cleared (except `sp`) 3. **Vector Extension Enable:** Set `mstatus.VS = 0x200` (Initial state to enable Spatz vector coprocessor) 4. **BSS Clearing:** Zero-initialize `.bss` section diff --git a/spatz/bootrom/spatz_init.S b/spatz/bootrom/spatz_init.S index 7de019a4..0b4e7a63 100644 --- a/spatz/bootrom/spatz_init.S +++ b/spatz/bootrom/spatz_init.S @@ -21,7 +21,7 @@ * Minimal Boot ROM - only read TASKBIN_ADDR and jump */ -#define TASKBIN_ADDR 0x0000170C // OBI slave ctrl exchange register +#define TASKBIN_ADDR 0xA000220C // OBI slave ctrl exchange register (SPATZ_CTRL_BASE + 0x0C) .section .text.bootrom .global _start diff --git a/spatz/sw/kernel/spatz_crt0.S b/spatz/sw/kernel/spatz_crt0.S index 752175da..309b0b22 100644 --- a/spatz/sw/kernel/spatz_crt0.S +++ b/spatz/sw/kernel/spatz_crt0.S @@ -21,12 +21,12 @@ * */ -#define READY_ADDR 0x00001704 -#define START_ADDR 0x00001708 -#define TASKBIN_ADDR 0x0000170C -#define DATA_ADDR 0x00001710 -#define RETURN_ADDR 0x00001714 -#define DONE_ADDR 0x00001718 +#define READY_ADDR 0xA0002204 // SPATZ_CTRL_BASE + 0x04 +#define START_ADDR 0xA0002208 +#define TASKBIN_ADDR 0xA000220C +#define DATA_ADDR 0xA0002210 +#define RETURN_ADDR 0xA0002214 +#define DONE_ADDR 0xA0002218 #define MSTATUS_VS 0x200 // Vector Status [9:8] = 01 #define MSTATUS_MIE 0x8 // Machine Interrupt Enable #define MIE_MEIE 0x800 // External interrupt enable @@ -36,7 +36,7 @@ _start: /* Init stack pointer */ - li sp, 0x0001FFF8 + li sp, 0xA011FFF8 // _stack_start of spatz_program.ld /* Clear ALL integer registers */ mv x1, x0 diff --git a/spatz/sw/kernel/spatz_program.ld b/spatz/sw/kernel/spatz_program.ld index 998ecbf0..621e086f 100644 --- a/spatz/sw/kernel/spatz_program.ld +++ b/spatz/sw/kernel/spatz_program.ld @@ -17,12 +17,12 @@ * Authors: Luca Balboni * * Memory Layout (when embedded in CV32 instrram): - * 0xCC000000: CV32 .text - * 0xCC000000 + CV32_size: .spatz_binary (Spatz code inline) + * 0xC0000000: CV32 .text + * 0xC0000000 + CV32_size: .spatz_binary (Spatz code inline) * - * Stack: Spatz uses the 16KB above the CV32/PULP stack region (0x0001C000-0x00020000) - * - CV32 and PULP: below 0x0001C000 (2KB slots, see sw/kernel/link.ld) - * - Spatz uses: 0x0001C000-0x00020000 (16KB) + * Stack: Spatz uses the 16KB above the CV32/PULP stacks (0xA011C000-0xA0120000, private L1) + * - CV32 and PULP: below 0xA011C000 (2KB slots, see sw/kernel/link.ld) + * - Spatz uses: 0xA011C000-0xA0120000 (16KB) */ OUTPUT_ARCH(riscv) @@ -33,8 +33,8 @@ MEMORY prog (rwxa) : ORIGIN = 0x00000000, LENGTH = 0x10000 /* 64KB max - relative addressing */ } -/* Spatz stack: 0x0001C000-0x00020000 (16KB), above the CV32/PULP slots of sw/kernel/link.ld */ -_stack_start = 0x0001FFF8; /* Top of Spatz stack (grows downward) */ +/* Spatz stack: 0xA011C000-0xA0120000 (16KB), above the CV32/PULP slots of sw/kernel/link.ld */ +_stack_start = 0xA011FFF8; /* Top of Spatz stack (grows downward) */ SECTIONS { diff --git a/sw/kernel/crt0.S b/sw/kernel/crt0.S index b6658f60..68ff5bfc 100644 --- a/sw/kernel/crt0.S +++ b/sw/kernel/crt0.S @@ -83,7 +83,7 @@ _start: skip_bss_clear: - /* Stack: top of slot 0 at _stack_start (0x1C000), the PULP harts take the 2 KB slots below */ + /* Stack: top of slot 0 at _stack_start (0xA011C000), the PULP harts take the 2 KB slots below */ la x2, _stack_start .section .text @@ -112,14 +112,14 @@ _fini: exit: csrr a0, mhartid andi s0, s0, 0x7ff - li a1, 0xCCff0000 + li a1, 0xC0008000 /* TEST_END_ADDR */ li a2, 0x800 xor s0, s0, a2 add a1, a1, a0 add a1, a1, a0 sh s0, 0(a1) #ifdef CV32E40P - li a1, 0x00000738 + li a1, 0xA0000038 /* EU_CORE_EVENT_WAIT */ cv.elw zero, 0(a1) #else wfi diff --git a/sw/kernel/link.ld b/sw/kernel/link.ld index c528c3dd..59cb9be6 100644 --- a/sw/kernel/link.ld +++ b/sw/kernel/link.ld @@ -20,14 +20,14 @@ __DYNAMIC = 0; MEMORY { - instrram : ORIGIN = 0xcc000000, LENGTH = 0x8000 - dataram : ORIGIN = 0xcc010000, LENGTH = 0xF00000 /* MAGIA: data at 0xCC010000 */ - stack : ORIGIN = 0x00010000, LENGTH = 0x0C000 /* CV32/PULP stacks below Spatz [0x1C000, 0x20000) */ + instrram : ORIGIN = 0xc0000000, LENGTH = 0x8000 + dataram : ORIGIN = 0xc0008800, LENGTH = 0xFF7800 /* After the exit codes at TEST_END_ADDR, up to 0xC0FFFFFF */ + stack : ORIGIN = 0xA0117800, LENGTH = 0x04800 /* CV32/PULP stacks, private L1, below Spatz [0xA011C000, 0xA0120000) */ } /* Stack information variables. - * Spatz owns [0x1C000, 0x20000) in the tile SPM (_stack_start = 0x1FFF8 in - * spatz_program.ld); this region stops at 0x1C000 to avoid overlap. + * Spatz owns [0xA011C000, 0xA0120000) of the private L1 view (_stack_start = 0xA011FFF8 in + * spatz_program.ld); this region stops at 0xA011C000 to avoid overlap. * Each hart gets _stack_slice_size bytes; slot n starts at * sp = stack - n * _stack_slice_size * Slot 0 = CV32 main core (crt0.S), slots 1..8 = PULP harts 0..7 (pulp_crt0.S). */ @@ -160,7 +160,7 @@ SECTIONS _bss_end = .; } > dataram :data - /* Stack placed at the TOP of the CV32/PULP region: stack = _stack_start = 0x1C000 */ + /* Stack placed at the TOP of the CV32/PULP region: stack = _stack_start = 0xA011C000 */ .stack ORIGIN(stack) + LENGTH(stack) - _min_stack (NOLOAD): { . = ALIGN(4); _stack_bottom = .; diff --git a/sw/kernel_pulp/Makefile b/sw/kernel_pulp/Makefile index a9341d0a..13b2ec77 100644 --- a/sw/kernel_pulp/Makefile +++ b/sw/kernel_pulp/Makefile @@ -59,7 +59,7 @@ ARCH_FLAGS = -march=$(ARCH)$(XLEN)$(XTEN) -mabi=$(ABI)$(XLEN)$(XABI) # -fno-jump-tables: switch statements otherwise compile to a jump table # whose base address is referenced ABSOLUTELY (e.g. `li a4, 0x618`). # Since this binary is linked at ORIGIN=0 but copied/relocated to -# 0xCC00_xxxx at runtime without a dynamic loader, that absolute base +# 0xC000_xxxx at runtime without a dynamic loader, that absolute base # is never fixed up: the indirect jump lands in unmapped space and the # cluster icache stalls forever on a fill that loops in the NoC. # Forcing compare-chains keeps every branch PC-relative (PIC-safe). diff --git a/sw/kernel_pulp/pulp_crt0.S b/sw/kernel_pulp/pulp_crt0.S index beb5973c..69a92e6d 100644 --- a/sw/kernel_pulp/pulp_crt0.S +++ b/sw/kernel_pulp/pulp_crt0.S @@ -21,14 +21,14 @@ #define PULP_CORE_COUNT 8 #endif -#define PULP_DONE_ADDR 0x00001748 /* PULP_CTRL_BASE + 0x08 */ -#define PULP_TASKBIN_ADDR 0x0000174C /* PULP_CTRL_BASE + 0x0C */ -#define PULP_DATA_ADDR 0x00001750 /* PULP_CTRL_BASE + 0x10 */ -#define PULP_START_ADDR 0x00001754 /* PULP_CTRL_BASE + 0x14 */ -#define PULP_READY_ADDR 0x00001758 /* PULP_CTRL_BASE + 0x18 */ -#define PULP_RETURN_ADDR 0x0000175C /* PULP_CTRL_BASE + 0x1C */ - -#define EU_DEMUX_BASE 0x00001900 /* CLUSTER_EU_DIRECT_BASE */ +#define PULP_DONE_ADDR 0xA0002308 /* PULP_CTRL_BASE + 0x08 */ +#define PULP_TASKBIN_ADDR 0xA000230C /* PULP_CTRL_BASE + 0x0C */ +#define PULP_DATA_ADDR 0xA0002310 /* PULP_CTRL_BASE + 0x10 */ +#define PULP_START_ADDR 0xA0002314 /* PULP_CTRL_BASE + 0x14 */ +#define PULP_READY_ADDR 0xA0002318 /* PULP_CTRL_BASE + 0x18 */ +#define PULP_RETURN_ADDR 0xA000231C /* PULP_CTRL_BASE + 0x1C */ + +#define EU_DEMUX_BASE 0xA0003000 /* CLUSTER_EU_DIRECT_BASE */ #define EU_CORE_MASK_OR (EU_DEMUX_BASE + 0x08) #define EU_EVENT_WAIT_CLR (EU_DEMUX_BASE + 0x3C) #define EU_DISPATCH_FIFO (EU_DEMUX_BASE + 0x80) @@ -37,7 +37,7 @@ #define EU_MASK_BAR_MUTEX_DISPATCH ((1 << 16) | (1 << 17) | (1 << 18)) /* PIC binary: MMIO and stack addresses are absolute, so they are loaded with li, not la */ -#define STACK_TOP 0x0001B800 /* 2KB below tile top (0x1C000): reserves CV32's slot */ +#define STACK_TOP 0xA011B800 /* 2KB below the stack top (0xA011C000): reserves CV32's slot */ #define LOG2_SLICE 11 /* log2(2 KB) — each hart gets 2 KB */ .section .text.start, "ax" @@ -53,7 +53,7 @@ _start: slli t0, a1, LOG2_SLICE sub x2, x2, t0 - /* Clear BSS (benign all-harts race); norelax keeps la PC-relative, relaxed to li it would zero tile MMIO */ + /* Clear BSS (benign all-harts race); norelax keeps la PC-relative in this PIC binary */ .option push .option norelax la t0, __bss_start diff --git a/sw/kernel_pulp/pulp_program.ld b/sw/kernel_pulp/pulp_program.ld index f66c5b68..bbab4abc 100644 --- a/sw/kernel_pulp/pulp_program.ld +++ b/sw/kernel_pulp/pulp_program.ld @@ -19,17 +19,17 @@ * Aligned with magia-sdk targets/magia_v3/pulp/src/pulp_program.ld. * * Memory layout when embedded: - * 0xCC000000: CV32 .text - * 0xCC000000 + CV32_size: .spatz_binary (Spatz code, if present) - * 0xCC000000 + offset: _pulp_binary_start (.pulp_binary, this binary) + * 0xC0000000: CV32 .text + * 0xC0000000 + CV32_size: .spatz_binary (Spatz code, if present) + * 0xC0000000 + offset: _pulp_binary_start (.pulp_binary, this binary) * * Binary is position-independent (compiled with -fPIC -mcmodel=medany). * ORIGIN=0x0 means all section addresses are relative to load address. * - * Local stack allocation (tile address space, absolute, set by each crt0): - * PULP stack: 0x00017800 – 0x0001B800 (16 KB, 8 harts × 2 KB each, pulp_crt0.S) - * CV32 stack: 0x0001B800 – 0x0001C000 (2 KB, slot 0 of sw/kernel/link.ld) - * Spatz stack: 0x0001C000 – 0x00020000 (16 KB, spatz_crt0.S) + * Local stack allocation (private L1 view, absolute, set by each crt0): + * PULP stack: 0xA0117800 – 0xA011B800 (16 KB, 8 harts × 2 KB each, pulp_crt0.S) + * CV32 stack: 0xA011B800 – 0xA011C000 (2 KB, slot 0 of sw/kernel/link.ld) + * Spatz stack: 0xA011C000 – 0xA0120000 (16 KB, spatz_crt0.S) */ OUTPUT_ARCH(riscv) diff --git a/sw/tests/cluster_tests/hci_arb_redmule_bench/pulp_task/hci_arb_redmule_bench_task.c b/sw/tests/cluster_tests/hci_arb_redmule_bench/pulp_task/hci_arb_redmule_bench_task.c index 60921ecd..1394bbc5 100644 --- a/sw/tests/cluster_tests/hci_arb_redmule_bench/pulp_task/hci_arb_redmule_bench_task.c +++ b/sw/tests/cluster_tests/hci_arb_redmule_bench/pulp_task/hci_arb_redmule_bench_task.c @@ -23,7 +23,7 @@ #include "cluster_utils.h" #define HCI_STRESS_ITERS (4096) -#define HCI_STRESS_BASE (L1_BASE + 0x00060000) +#define HCI_STRESS_BASE (L1_BASE + 0x00020000) #define HCI_STRESS_STRIDE (0x00000400) #define HCI_STRESS_WORDS (HCI_STRESS_STRIDE / 4) diff --git a/sw/tests/cluster_tests/hello_pulp/main.c b/sw/tests/cluster_tests/hello_pulp/main.c index 6cac7de6..95a227e5 100644 --- a/sw/tests/cluster_tests/hello_pulp/main.c +++ b/sw/tests/cluster_tests/hello_pulp/main.c @@ -20,7 +20,7 @@ /* * hello_pulp — main core (CV32) binary. * - * This ELF is linked at 0xCC000000 and executed by the CV32 main core + * This ELF is linked at 0xC0000000 and executed by the CV32 main core * of each tile (mhartid 0..NUM_CLUSTERS-1). * * Flow: diff --git a/sw/tests/eu_tests/event_unit_test.c b/sw/tests/eu_tests/event_unit_test.c index 6faf7c3c..b57b30e2 100644 --- a/sw/tests/eu_tests/event_unit_test.c +++ b/sw/tests/eu_tests/event_unit_test.c @@ -44,8 +44,8 @@ #define Z_BASE_2 (L2_BASE + 0x00046000) #define Z_BASE_4 (L2_BASE + 0x0004A000) -#define DMA_BUFFER_1 (L1_BASE + 0x00036048) -#define DMA_BUFFER_2 (L1_BASE + 0x0003A048) +#define DMA_BUFFER_1 (L1_BASE + 0x00002048) +#define DMA_BUFFER_2 (L1_BASE + 0x00006048) #define M_SIZE (96) #define N_SIZE (64) diff --git a/sw/tests/eu_tests/idma_test_event_unit.c b/sw/tests/eu_tests/idma_test_event_unit.c index dbe3f94d..ea42bf1c 100644 --- a/sw/tests/eu_tests/idma_test_event_unit.c +++ b/sw/tests/eu_tests/idma_test_event_unit.c @@ -30,7 +30,7 @@ #define X_BASE (L1_BASE + 0x00012048) #define Y_BASE (L1_BASE + 0x00016048) -#define Z_BASE (L2_BASE + 0x00042000) /* offset safe: clear of instrram (0xCC000000..0xCC007FFF) and dataram header */ +#define Z_BASE (L2_BASE + 0x00042000) /* offset safe: clear of instrram (0xC0000000..0xC0007FFF) and dataram header */ #define W_BASE (L2_BASE + 0x00046000) #define M_SIZE (96) diff --git a/sw/tests/mm_tests/idma_test_mm.c b/sw/tests/mm_tests/idma_test_mm.c index c097a125..f5ad1890 100644 --- a/sw/tests/mm_tests/idma_test_mm.c +++ b/sw/tests/mm_tests/idma_test_mm.c @@ -27,7 +27,7 @@ #define X_BASE (L1_BASE + 0x00012048) #define Y_BASE (L1_BASE + 0x00016048) -#define Z_BASE (L2_BASE + 0x00042000) /* offset safe: clear of instrram (0xCC000000..0xCC007FFF) and dataram header */ +#define Z_BASE (L2_BASE + 0x00042000) /* offset safe: clear of instrram (0xC0000000..0xC0007FFF) and dataram header */ #define W_BASE (L2_BASE + 0x00046000) #define M_SIZE (96) diff --git a/sw/tests/spatz_tests/gemm_spatz_test.c b/sw/tests/spatz_tests/gemm_spatz_test.c index 78721354..482aa0f8 100644 --- a/sw/tests/spatz_tests/gemm_spatz_test.c +++ b/sw/tests/spatz_tests/gemm_spatz_test.c @@ -31,9 +31,9 @@ #define K_SIZE 64 #define A_BASE (L1_BASE + 0x00010000) -#define B_BASE (L1_BASE + 0x00020000) -#define C_BASE (L1_BASE + 0x00030000) -#define PARAM_BASE (L1_BASE + 0x00040000) +#define B_BASE (L1_BASE + 0x00014000) +#define C_BASE (L1_BASE + 0x00018000) +#define PARAM_BASE (L1_BASE + 0x0001C000) typedef uint16_t fp16_t; static const fp16_t fp16_one = 0x3C00; // 1.0 diff --git a/sw/utils/magia_tile_utils.h b/sw/utils/magia_tile_utils.h index 7329c4c2..74fe10d6 100644 --- a/sw/utils/magia_tile_utils.h +++ b/sw/utils/magia_tile_utils.h @@ -31,18 +31,29 @@ #define BITS_WORD (32) #define BITS_BYTE (8) -#define REDMULE_BASE (0x00000100) -#define REDMULE_END (0x000001FF) -#define IDMA_BASE (0x00000200) -#define IDMA_END (0x000005FF) -#define FSYNC_BASE (0x00000600) -#define FSYNC_END (0x000006FF) -#define EVENT_UNIT_BASE (0x00000700) -#define EVENT_UNIT_END (0x000016FF) -#define SPATZ_CTRL_BASE (0x00001700) -#define SPATZ_CTRL_END (0x0000173F) -/* PULP Cluster Control registers (tile_csr + 0x40), bare-metal dispatch model */ -#define PULP_CTRL_BASE (0x00001740) +/* Tile peripherals: private, the same in every tile, laid out as in magia_tile_pkg */ +#define TILE_PERIPH_BASE (0xA0000000) +#define EVENT_UNIT_BASE (TILE_PERIPH_BASE + 0x0000) +#define EVENT_UNIT_END (TILE_PERIPH_BASE + 0x0FFF) +#define IDMA_BASE (TILE_PERIPH_BASE + 0x1000) +#define IDMA_END (TILE_PERIPH_BASE + 0x13FF) +// HCI arbiter control register +#define HCI_ARB_CTRL_BASE (TILE_PERIPH_BASE + 0x1400) +#define HCI_ARB_CTRL (HCI_ARB_CTRL_BASE + 0x00) +#define HCI_ARB_CTRL_END (TILE_PERIPH_BASE + 0x143F) +#define HCI_ARB_CTRL_DEFAULT (0x00000000u) /* Denominator 0: default ratio of magia_hci_interconnect */ +#define HCI_ARB_INVERT_PRIO (1u << 0) +#define HCI_ARB_PRIO_NUM(n) (((n) & 0xFFu) << 8) +#define HCI_ARB_PRIO_DEN(n) (((n) & 0xFFu) << 16) +/* Optional units: an absent one answers with an error */ +#define REDMULE_BASE (TILE_PERIPH_BASE + 0x2000) +#define REDMULE_END (TILE_PERIPH_BASE + 0x20FF) +#define FSYNC_BASE (TILE_PERIPH_BASE + 0x2100) +#define FSYNC_END (TILE_PERIPH_BASE + 0x21FF) +#define SPATZ_CTRL_BASE (TILE_PERIPH_BASE + 0x2200) +#define SPATZ_CTRL_END (TILE_PERIPH_BASE + 0x223F) +/* PULP Cluster Control registers, bare-metal dispatch model */ +#define PULP_CTRL_BASE (TILE_PERIPH_BASE + 0x2300) #define PULP_FETCH_EN (PULP_CTRL_BASE + 0x00) /* R/W boot enable, sticky in the cores; every write resets READY */ #define PULP_BINARY (PULP_CTRL_BASE + 0x04) /* R/W boot address of all cluster cores */ #define PULP_DONE (PULP_CTRL_BASE + 0x08) /* W core 0 signals completion, pulsing EU bit 12 on the CV32 */ @@ -52,32 +63,27 @@ #define PULP_READY (PULP_CTRL_BASE + 0x18) /* R 1 once all cores booted, W each core posts 1 once armed */ #define PULP_RETURN (PULP_CTRL_BASE + 0x1C) /* R/W task exit code, written by core 0 before PULP_DONE */ #define PULP_RETURN_CRASHED_BIT (0x80000000u) /* Set when core 0 trapped instead of returning */ -#define PULP_CTRL_END (0x000017BF) +#define PULP_CTRL_END (TILE_PERIPH_BASE + 0x237F) #define PULP_CORE_COUNT (8) #define PULP_HARTID_BASE (32) -// HCI arbiter control register (tile_csr + 0xC0) -#define HCI_ARB_CTRL_BASE (0x000017C0) -#define HCI_ARB_CTRL (HCI_ARB_CTRL_BASE + 0x00) -#define HCI_ARB_CTRL_END (0x000017FF) -#define HCI_ARB_CTRL_DEFAULT (0x00000000u) /* Denominator 0: default ratio of magia_hci_interconnect */ -#define HCI_ARB_INVERT_PRIO (1u << 0) -#define HCI_ARB_PRIO_NUM(n) (((n) & 0xFFu) << 8) -#define HCI_ARB_PRIO_DEN(n) (((n) & 0xFFu) << 16) -#define COLL_CTRL_BASE (0x00001800) -#define COLL_CTRL_END (0x000018FF) -#define CLUSTER_EU_DIRECT_BASE (0x00001900) -#define CLUSTER_EU_DIRECT_END (0x000028FF) -#define CLUSTER_EU_BASE (0x00002900) -#define CLUSTER_EU_END (0x000038FF) -#define RESERVED_START (0x00003900) -#define RESERVED_END (0x0000FFFF) -#define STACK_START (0x00010000) -#define STACK_END (0x0001FFFF) -#define L1_BASE (0x00020000) -#define L1_SIZE (0x000E0000) +#define COLL_CTRL_BASE (TILE_PERIPH_BASE + 0x2400) +#define COLL_CTRL_END (TILE_PERIPH_BASE + 0x24FF) +#define CLUSTER_EU_DIRECT_BASE (TILE_PERIPH_BASE + 0x3000) +#define CLUSTER_EU_DIRECT_END (TILE_PERIPH_BASE + 0x3FFF) +#define CLUSTER_EU_BASE (TILE_PERIPH_BASE + 0x4000) +#define CLUSTER_EU_END (TILE_PERIPH_BASE + 0x4FFF) +/* L1 of tile k: private part at L1_PRIV_BASE, global part at k * L1_TILE_OFFSET + L1_PRIV_SIZE */ #define L1_TILE_OFFSET (0x00100000) -#define L2_BASE (0xCC000000) -#define TEST_END_ADDR (0xCCFF0000) +#define L1_PRIV_BASE (0xA0100000) +#define L1_PRIV_SIZE (0x00020000) /* TileCfg.L1.PrivBytes */ +/* SW layout of the private part: scratch, then the stacks of the linker scripts */ +#define SCRATCH_BASE (L1_PRIV_BASE + 0x00000) +#define SCRATCH_END (L1_PRIV_BASE + 0x177FF) /* Up to the stacks of sw/kernel/link.ld */ +#define L1_BASE (L1_PRIV_SIZE) /* Global part of tile 0 */ +/* Global bytes per tile */ +#define L1_SIZE (NUM_L1_BANKS * WORDS_BANK * (BITS_WORD / BITS_BYTE) - L1_PRIV_SIZE) +#define L2_BASE (0xC0000000) +#define TEST_END_ADDR (0xC0008000) /* Must match sw/kernel/link.ld and the testbench VIPs */ #define DEFAULT_EXIT_CODE (0xDEFC) #define PASS_EXIT_CODE (0xAAAA) diff --git a/sw/utils/magia_utils.h b/sw/utils/magia_utils.h index 9e0b2057..eca00904 100644 --- a/sw/utils/magia_utils.h +++ b/sw/utils/magia_utils.h @@ -24,13 +24,15 @@ #include "magia_tile_utils.h" -// Print buffer in the tile-private reserved region +// Print buffer in the private scratch of each tile #define STR_OFFSET (0x00000000) -#define STR_BASE (RESERVED_START + STR_OFFSET) +#define STR_BASE (SCRATCH_BASE + STR_OFFSET) // Sync words in the top 4 KiB of L1, which every tile can reach #define SYNC_OFFSET (L1_SIZE - 0x00001000) #define SYNC_BASE (L1_BASE + SYNC_OFFSET) #define SYNC_EN (SYNC_BASE + 0x4) +// The testbench VIPs decode only this address, in every tile +#define STDIO_ADDR (0xFFFF0004) #define MESH_Y_TILES (4) #define MESH_X_TILES (4) @@ -97,38 +99,38 @@ char* hs(uint32_t x) { } void h_psprint(uint32_t hartid, const char* string){ - mmio8(0xFFFF0004 + (hartid*4)) = '['; - mmio8(0xFFFF0004 + (hartid*4)) = 'm'; - mmio8(0xFFFF0004 + (hartid*4)) = 'h'; - mmio8(0xFFFF0004 + (hartid*4)) = 'a'; - mmio8(0xFFFF0004 + (hartid*4)) = 'r'; - mmio8(0xFFFF0004 + (hartid*4)) = 't'; - mmio8(0xFFFF0004 + (hartid*4)) = 'i'; - mmio8(0xFFFF0004 + (hartid*4)) = 'd'; - mmio8(0xFFFF0004 + (hartid*4)) = ' '; + mmio8(STDIO_ADDR) = '['; + mmio8(STDIO_ADDR) = 'm'; + mmio8(STDIO_ADDR) = 'h'; + mmio8(STDIO_ADDR) = 'a'; + mmio8(STDIO_ADDR) = 'r'; + mmio8(STDIO_ADDR) = 't'; + mmio8(STDIO_ADDR) = 'i'; + mmio8(STDIO_ADDR) = 'd'; + mmio8(STDIO_ADDR) = ' '; char* mhardid_str = ds(hartid); uint8_t mhartid_idx = 0; while (mhardid_str[mhartid_idx] != '\0') - mmio8(0xFFFF0004 + (hartid*4)) = mhardid_str[mhartid_idx++]; - mmio8(0xFFFF0004 + (hartid*4)) = ']'; - mmio8(0xFFFF0004 + (hartid*4)) = ' '; + mmio8(STDIO_ADDR) = mhardid_str[mhartid_idx++]; + mmio8(STDIO_ADDR) = ']'; + mmio8(STDIO_ADDR) = ' '; uint8_t index = 0; while (string[index] != '\0') - mmio8(0xFFFF0004 + (hartid*4)) = string[index++]; + mmio8(STDIO_ADDR) = string[index++]; } void n_psprint(uint32_t hartid, const char* string){ uint8_t index = 0; while (string[index] != '\0') - mmio8(0xFFFF0004 + (hartid*4)) = string[index++]; - mmio8(0xFFFF0004 + (hartid*4)) = '\n'; + mmio8(STDIO_ADDR) = string[index++]; + mmio8(STDIO_ADDR) = '\n'; } void psprint(uint32_t hartid, const char* string){ uint8_t index = 0; while (string[index] != '\0') - mmio8(0xFFFF0004 + (hartid*4)) = string[index++]; + mmio8(STDIO_ADDR) = string[index++]; } #endif /*MAGIA_UTILS_H*/ diff --git a/target/sim/src/mesh/magia_tb.sv b/target/sim/src/mesh/magia_tb.sv index adaf42b2..d939dc3d 100644 --- a/target/sim/src/mesh/magia_tb.sv +++ b/target/sim/src/mesh/magia_tb.sv @@ -46,7 +46,7 @@ module magia_tb; if (!$value$plusargs("DATA_HEX=%s" , data_hex)) data_hex = ""; if (!$value$plusargs("BOOT_ADDR=%h", boot_addr)) boot_addr = 0; - // Single-binary flow: the CV32 ELF (@ 0xCC000000) embeds the optional + // Single-binary flow: the CV32 ELF (@ 0xC0000000) embeds the optional // Spatz/PULP task binaries in dedicated linker sections // (.spatz_binary / .pulp_binary), so only one stimulus pair is preloaded. fixture.vip.inst_preload(inst_hex); diff --git a/target/sim/src/mesh/magia_vip.sv b/target/sim/src/mesh/magia_vip.sv index 7ad5b4db..457cf2c3 100644 --- a/target/sim/src/mesh/magia_vip.sv +++ b/target/sim/src/mesh/magia_vip.sv @@ -169,8 +169,8 @@ module magia_vip fetch_enable = 1'b0; boot_addr = entry_addr; for (int i = 0; i < magia_tb_pkg::N_TILES; i++) begin - i_l2_mem.i_l2_mem.mem[32'hCCFF_0000 + 2*i] = 8'h00; - i_l2_mem.i_l2_mem.mem[32'hCCFF_0000 + 2*i+1] = 8'h00; + i_l2_mem.i_l2_mem.mem[32'hC000_8000 + 2*i] = 8'h00; + i_l2_mem.i_l2_mem.mem[32'hC000_8000 + 2*i+1] = 8'h00; end #1000; endtask: init @@ -189,15 +189,15 @@ module magia_vip tile_cnt = 0; //eoc = 1'b1; for (int i = 0; i < magia_tb_pkg::N_TILES; i++) - if (i_l2_mem.i_l2_mem.mem[32'hCCFF_0000 + 2*i+1][3] == 1'b1) + if (i_l2_mem.i_l2_mem.mem[32'hC000_8000 + 2*i+1][3] == 1'b1) tile_cnt++; #10000; end while(tile_cnt/dev/null || echo $(CXX))"; \ echo "cxx_version=$$($(CXX) --version 2>/dev/null | head -1)"; \ - echo "objcache=$$(if [ -n "$$OBJCACHE" ]; then printf '%s' "$$OBJCACHE"; else command -v ccache 2>/dev/null || echo none; fi)"; \ + echo "objcache=$(or $(OBJCACHE),none)"; \ echo "dpi=$(VERILATOR_DPI)"; \ echo "verilator_args=$(VERILATOR_ARGS)$(if $(filter 1,$(VERILATOR_HIER)), --hierarchical)"; \ echo "control_sha256=$$(sha256sum $(VERILATOR_CONTROL) | cut -d' ' -f1)"; \