# VDK Platform Guide: Linux-Booting Keraunos PCIe Tile **Version:** 1.0 **Date:** March 26, 2026 **Author:** System Architecture Team --- ## 1. Introduction This guide documents the final validated Synopsys Virtualizer (VDK) platform that boots RISC-V Linux on the host chiplet, enumerates a PCIe Endpoint connected to the Keraunos PCIe Tile, and enables data transfer from the host to memory attached to the tile's `noc_n_initiator` port. The reference implementation is at `/localdev/pdroy/ruchika_ws/Keraunos_PCIE_Tile`. ### 1.1 What This Platform Demonstrates - Full Linux boot on a RISC-V virtual CPU (TT_Rocket_LT) via OpenSBI - PCIe link-up between a DesignWare Root Complex (host) and Endpoint (device) - Linux kernel PCIe enumeration using the `snps,dw-pcie` driver - BAR0 mapping and MMIO access from Linux userspace - End-to-end data transfer: Host → RC → EP → PCIE_TILE → `noc_n_initiator` → Target_Memory --- ## 2. Platform Architecture ### 2.1 Two-Chiplet Topology The VDK instantiates two chiplet groups with a direct PCIe link: ```{mermaid} graph TB subgraph host["Host_Chiplet"] direction TB H_CPU["TT_Rocket_LT
RISC-V rv64imac
Runs Linux"] H_SMC["SMC (CPU + Peripherals)"] H_DRAM["DRAM
256 MB @ 0x80000000"] H_UART["UART @ 0xC000A000"] H_PLIC["PLIC @ 0xC4000000"] H_RC["PCIE_RC
DesignWare PCIe 2.0
Root Complex"] H_MAP["SharedMemoryMap"] H_CLK["CLK_GEN"] H_RST["RST_GEN"] H_CPU --> H_SMC H_SMC --> H_MAP H_MAP --> H_DRAM H_MAP --> H_UART H_MAP --> H_PLIC H_MAP -->|DBI 0x44000000
4 MB| H_RC H_MAP -->|AXI 0x70000000
256 MB| H_RC end subgraph device["Keraunos_PCIE_Chiplet"] direction TB D_CPU["TT_Rocket_LT
RISC-V
pcie_bringup firmware"] D_SMC["SMC_Configure"] D_EP["PCIe_EP
DesignWare PCIe 2.0
Endpoint"] D_TILE["PCIE_TILE
Keraunos PCIe Tile"] D_MEM["Target_Memory
16 MB @ 0x0"] D_MAP["SharedMemoryMap"] D_CLK["CLK_GEN"] D_RST["RST_GEN"] D_CPU --> D_SMC D_SMC --> D_MAP D_EP -->|BusMaster| D_TILE D_TILE -->|pcie_controller_initiator| D_EP D_TILE -->|noc_n_initiator @ 0x0| D_MAP D_TILE -->|smn_n_initiator @ 0x0| D_MAP D_MAP --> D_MEM end H_RC <-->|"PCIMem ↔ PCIMem_Slave
Direct PCIe Link"| D_EP H_RC -.->|"msi_ctrl_int → irqS[11]"| H_SMC style H_CPU fill:#e3f2fd style H_RC fill:#ffcccc,stroke:#c00 style D_EP fill:#fff3e0 style D_TILE fill:#c8e6c9,stroke:#2e7d32 style D_MEM fill:#ffe6cc style H_DRAM fill:#e1ffe1 ``` ### 2.2 PCIe Link Wiring The PCIe link uses direct peer-to-peer binding: | Source | Destination | Purpose | |--------|-------------|---------| | `PCIE_RC.PCIMem` (master) | `PCIe_EP.PCIMem_Slave` (slave) | Downstream TLPs (host → device) | | `PCIe_EP.PCIMem` (master) | `PCIE_RC.PCIMem_Slave` (slave) | Upstream TLPs (device → host) | | `PCIE_RC.BusMaster` | `Host_Chiplet.SharedMemoryMap` | RC DMA to host memory | | `PCIe_EP.BusMaster` | `PCIE_TILE.pcie_controller_target` | EP delivers inbound TLPs to tile | | `PCIE_TILE.pcie_controller_initiator` | `PCIe_EP.AXI_Slave` | Tile sends outbound TLPs to EP | ### 2.3 PCIE_TILE Interface Connections The Keraunos PCIe Tile connects to the chiplet's SharedMemoryMap and EP: | Tile Port | Direction | Connected To | Address / Offset | |-----------|-----------|-------------|------------------| | `pcie_controller_target` | Target (in) | EP BusMaster | Inbound TLPs from host | | `pcie_controller_initiator` | Initiator (out) | EP AXI_Slave | Outbound TLPs to host | | `noc_n_initiator` | Initiator (out) | SharedMemoryMap (left) | Start 0x0 | | `noc_n_target` | Target (in) | SharedMemoryMap (right) | 0x44400000, 16 MB | | `smn_n_initiator` | Initiator (out) | SharedMemoryMap (left) | Start 0x0 | | `smn_n_target` | Target (in) | SharedMemoryMap (right) | 0x18000000, 8 MB | --- ## 3. Memory Maps ### 3.1 Host_Chiplet CPU Address Space | Address | Size | Target | Description | |---------|------|--------|-------------| | `0x02000000` | 64 KB | CLINT | Machine timer and software interrupts | | `0x44000000` | 4 MB | PCIE_RC DBI | RC configuration registers (DesignWare DBI) | | `0x44300000` | 128 KB | PCIE_RC ATU | iATU outbound/inbound (via DBI CS2 range) | | `0x70000000` | 256 MB | PCIE_RC AXI_Slave | PCIe outbound window | | `0x70000000–0x70FFFFFF` | 16 MB | — Config | Type 0/1 Configuration TLPs (iATU-translated) | | `0x71000000–0x7FFFFFFF` | 240 MB | — MEM | Memory Read/Write TLPs to EP BARs | | `0x80000000` | 256 MB | DRAM | Host main memory (OpenSBI + Linux + initramfs) | | `0xC000A000` | 256 B | UART | DW APB UART, 115.2 MHz clock, 7.2 Mbaud | | `0xC4000000` | 2 MB | PLIC | RISC-V PLIC, 64 interrupt sources | ### 3.2 Keraunos_PCIE_Chiplet SharedMemoryMap Decode Configured via VPCFG `range_mappings`: `0x44000000:0x00400000:s;0x18000000:0x00800000;0x44400000:0x01000000;0x0:0x1000000` | Address | Size | Target | Purpose | |---------|------|--------|---------| | `0x00000000` | 16 MB | Target_Memory (MEM) | Data memory — host-accessible via EP BAR0 | | `0x18000000` | 8 MB | PCIE_TILE smn_n_target | SMN access into tile registers | | `0x44000000` | 4 MB | PCIe_EP AXI_DBI | EP DBI configuration (`:s` = secure) | | `0x44400000` | 16 MB | PCIE_TILE noc_n_target | NoC access into tile subsystem | --- ## 4. Linux Boot ### 4.1 Boot Flow 1. **Virtualizer loads images**: `fw_payload.elf` on Host_Chiplet CPU, `pcie_bringup.elf` on Device CPU 2. **OpenSBI** initializes M-mode on Host CPU at entry `0x80000000` 3. OpenSBI transitions to S-mode and jumps to the embedded Linux kernel 4. **Linux** parses the compiled device tree (`keraunos_host.dtb`) 5. Kernel initializes CLINT (timer), PLIC (interrupts), UART (console) 6. The `snps,dw-pcie` driver probes the PCIe RC at DBI `0x44000000` 7. Driver programs iATU outbound windows and scans bus 1 8. **EP is enumerated**: vendor/device ID read, BARs assigned 9. Linux mounts initramfs and launches `/init` (BusyBox shell) 10. The `pcie_xfer` application is available for PCIe data transfer ### 4.2 Device Tree Highlights ``` / { compatible = "tt,smc-pcie-tile"; cpus { cpu@0 { riscv,isa = "rv64imac"; mmu-type = "riscv,sv39"; }; }; memory@80000000 { reg = <0x0 0x80000000 0x0 0x10000000>; }; /* 256 MB */ soc { clint@2000000 { reg = <0x0 0x02000000 0x0 0x10000>; }; plic@c4000000 { riscv,ndev = <64>; }; uart@c000a000 { clock-frequency = <0x6DDD000>; }; /* 115.2 MHz */ pcie@44000000 { compatible = "snps,dw-pcie"; reg = <0x0 0x44000000 0x0 0x400000>, /* DBI */ <0x0 0x70000000 0x0 0x01000000>; /* config */ bus-range = <0x0 0x1>; ranges = <0x02000000 0x0 0x71000000 0x0 0x71000000 0x0 0x0F000000>; /* 240 MB MEM */ interrupts = <32>, <33>; /* MSI, INTx */ num-lanes = <4>; }; }; chosen { bootargs = "console=hvc0 earlycon=sbi rdinit=/init pci=realloc pci=assign-busses pci=noaer pcie_aspm=off"; }; }; ``` ### 4.3 Key Boot Parameters | Parameter | Value | Purpose | |-----------|-------|---------| | `console=hvc0` | HVC console | Uses SBI console calls (fastest in VP) | | `earlycon=sbi` | SBI earlycon | Early console via OpenSBI ecall | | `rdinit=/init` | Initramfs init | Uses embedded initramfs (no disk) | | `pci=realloc` | PCI realloc | Reallocate PCI resources (no BIOS pre-assignment) | | `pci=assign-busses` | Assign buses | Force bus number assignment | | `pci=noaer` | Disable AER | Advanced Error Reporting not needed in VP | | `pcie_aspm=off` | Disable ASPM | Active State Power Management not modeled | ### 4.4 Toolchain Requirements | Component | Toolchain | ISA | Notes | |-----------|-----------|-----|-------| | Linux kernel | ASC GCC 14 (`riscv64-unknown-linux-gnu-`) | rv64imac | `CONFIG_FPU=n` | | Initramfs (BusyBox) | Musl (`riscv64-unknown-linux-musl-`) | rv64imac / lp64 | Soft-float, static | | OpenSBI | ASC GCC 14 | rv64imac | Builds `fw_payload.elf` | | pcie_bringup (device) | Bare-metal GCC | rv64imac | No OS | --- ## 5. PCIe Enumeration and BAR Assignment ### 5.1 Enumeration Topology ```{mermaid} graph LR subgraph bus0["PCI Bus 0"] RC["Root Complex
Type 1 Header"] end subgraph bus1["PCI Bus 1"] EP["Endpoint
Type 0 Header
BAR0: 16 MB"] end RC -->|"Downstream Port"| EP style RC fill:#e3f2fd style EP fill:#fff3e0 ``` | Property | Value | |----------|-------| | RC Bus/Dev/Fn | 00:00.0 | | EP Bus/Dev/Fn | 01:00.0 | | EP BAR0 size | 16 MB (`BAR0_MASK = 0xFFFFFF`) | | EP BAR0 type | 64-bit Memory, non-prefetchable | | Config access | iATU-translated (no ECAM hardware) | | Host MEM window | `0x71000000–0x7FFFFFFF` | ### 5.2 iATU Configuration The `dw-pcie` Linux driver programs iATU outbound windows: - **Window 0 (Config)**: Maps `0x70000000` (16 MB) → Type 0/1 config TLPs to bus 1 - **Window 1 (MEM)**: Maps `0x71000000` (240 MB) → Memory TLPs targeting EP BARs Linux assigns BAR0 an address within the `0x71000000–0x7FFFFFFF` MEM window. The physical address is visible in sysfs at `/sys/bus/pci/devices/0000:01:00.0/resource`. --- ## 6. Data Transfer Application (pcie_xfer) ### 6.1 Purpose `pcie_xfer` is a Linux userspace C application that maps EP BAR0 into process address space (via sysfs `resource0` or `/dev/mem`) and performs MMIO read/write operations. Each operation traverses the full data path to `Target_Memory` on the device chiplet. ### 6.2 Data Path ``` Host CPU (MMIO store) → RC AXI_Slave (0x70000000 window) → iATU translates to Memory Write TLP → RC.PCIMem → PCIe link → EP.PCIMem_Slave → EP.BusMaster → PCIE_TILE.pcie_controller_target → Internal tile fabric → noc_n_initiator → SharedMemoryMap decode → Target_Memory @ 0x0 (16 MB) ``` ### 6.3 Supported Commands | Command | Syntax | Description | |---------|--------|-------------| | Write | `write ` | Single 32-bit write at BAR0 + offset | | Read | `read ` | Single 32-bit read at BAR0 + offset | | Fill | `fill ` | Fill `count` dwords with a constant value | | Dump | `dump ` | Hex dump `count` dwords from BAR0 | | Pattern | `pattern ` | Write `0xA0000000 | i` pattern and verify | | Burst | `burst ` | Timed burst write, reports throughput | | Verify | `verify ` | Verify memory matches expected value | | Send | `send ` | Write binary file contents to BAR0 | ### 6.4 EP Auto-Detection The application walks `/sys/bus/pci/devices/` to find the first non-bridge device (class != `0x0604xx` or `0x0600xx`), reads BAR0 physical address and size from the sysfs `resource` file, and maps it via `resource0` (falling back to `/dev/mem` if needed). ### 6.5 Building pcie_xfer Cross-compile for the host initramfs with the musl toolchain: ```bash riscv64-unknown-linux-musl-gcc -O2 -static -o pcie_xfer pcie_xfer.c ``` The static binary is included in the initramfs CPIO archive. --- ## 7. Bare-Metal Test (pcie_e2e_test) An alternative bare-metal test (`pcie_e2e_test.c`) runs on the Host_Chiplet without Linux. It provides: - Trap-safe MMIO operations (catch and report access faults) - RC DBI register dump (Vendor/Device ID, Command/Status, BARs, Capabilities) - Port Logic register inspection - iATU programming and verification - End-to-end data transfer test through the PCIe link - Detailed topology verification (Bus 0 RC, Bus 1 EP) This test is useful for pre-Linux validation of the PCIe link and tile connectivity. --- ## 8. VP Configuration Reference ### 8.1 Available Configurations | Name | Active Config Path | Description | |------|-------------------|-------------| | **default** | `vpconfigs/default/default.vpcfg` | Full Linux with standard kernel, quantum 6000 ps | | **mini_riscv64_linux** | `vpconfigs/mini_riscv64_linux/mini_riscv64_linux.vpcfg` | Mini Linux for fast boot, quantum 1000 ps | Set the active config in `snps.vpproject` → `activeConfig` attribute. ### 8.2 Critical VPCFG Parameters **Host_Chiplet:** | Parameter | Value | Purpose | |-----------|-------|---------| | `RST_GEN.active_at_start` | `true` | Start host CPU immediately | | `PCIE_RC.SHARED_DBI_ENABLED` | `false` | Separate DBI window | | `PCIE_RC.cc_pipe_clk_Override` | 250 | PIPE clock 250 MHz | | `PCIE_RC.cc_*_Override` | 100 | AXI/DBI/aux clocks 100 MHz | | `initial_image` | `fw_payload.elf` (empty load addr) | OpenSBI + Linux | | `quantum_value` | 6000 (default) / 1000 (mini) | Temporal decoupling quantum | | `UART_CLK` | 115200000 | 115.2 MHz UART clock | **Keraunos_PCIE_Chiplet:** | Parameter | Value | Purpose | |-----------|-------|---------| | `RST_GEN.active_at_start` | `true` | Start device CPU immediately | | `PCIe_EP.SHARED_DBI_ENABLED` | `false` | Separate DBI window | | `PCIe_EP.cc_pipe_clk_Override` | 250 | PIPE clock 250 MHz | | `initial_image` | `pcie_bringup.elf` | Bare-metal EP init firmware | | `SharedMemoryMap_intf.range_mappings` | See Section 3.2 | Chiplet address decode | ### 8.3 Image Loading **Critical**: The host `initial_image` uses the format `{path/fw_payload.elf,,,image+symbols,}` with **empty load address fields**. The Virtualizer applies ELF program header addresses (entry at `0x80000000`). **Never** set load address to `0x0` — this breaks OpenSBI execution. --- ## 9. Sideband Signal Mapping ### 9.1 EP ↔ PCIE_TILE Sideband | VDK Connection Source | VDK Connection Sink | Description | |-----------------------|---------------------|-------------| | `PCIe_EP.edma_int` / `edma_int_*` | `PCIE_TILE.pcie_misc_int` | DMA interrupts | | `PCIe_EP.pcie_parc_int` | `PCIE_TILE.pcie_ras_error` | Parity/RAS error | | `PCIe_EP.lbc_cii_hv` | `PCIE_TILE.pcie_cii_hv` | CII header valid | | `PCIe_EP.lbc_cii_hdr_type` | `PCIE_TILE.pcie_cii_hdr_type` | CII header type | | `PCIe_EP.lbc_cii_hdr_addr` | `PCIE_TILE.pcie_cii_hdr_addr` | CII header address | | `PCIe_EP.cfg_flr_pf_active_x[0]` | `PCIE_TILE.pcie_flr_request` | Function Level Reset | ### 9.2 Clocks and Resets | Source | Destination | Notes | |--------|-------------|-------| | `Chiplet.Infra.CLK_GEN` | `PCIE_TILE`, `PCIe_EP` | System clock | | `Chiplet.Infra.RST_GEN` | `PCIE_TILE`, `PCIe_EP` | System reset | | `Host.Infra.CLK_GEN` | `PCIE_RC` | Host clock | | `Host.Infra.RST_GEN` | `PCIE_RC` | Host reset | ### 9.3 MSI Interrupt Path ``` PCIE_RC.msi_ctrl_int → Host_Chiplet.SMC.irqS[11] → PLIC IRQ → Linux ISR ``` --- ## 10. Troubleshooting ### 10.1 Host Crashes or Nonsense PC 1. Verify `fw_payload.elf` uses ELF-header load addresses (not forced `0x0`) 2. Check `readelf -l fw_payload.elf` — VirtAddr should show `0x8000xxxx` 3. Ensure kernel is built with `CONFIG_FPU=n` for rv64imac CPU 4. Verify initramfs binaries are musl-compiled for rv64imac (no FPU instructions) ### 10.2 PCIe EP Not Detected 1. Verify `pcie_bringup.elf` is loaded on device CPU and runs to completion 2. Check that device `RST_GEN.active_at_start = true` 3. Confirm direct PCIe link wiring (RC.PCIMem ↔ EP.PCIMem_Slave) 4. Verify DT `bus-range = <0x0 0x1>` and bootargs include `pci=assign-busses` ### 10.3 BAR0 Access Returns All-Ones 1. EP may not have BAR0 properly sized — check `pcie_bringup` programs `BAR0_MASK` 2. iATU outbound window may not cover the assigned BAR address 3. Verify chiplet SharedMemoryMap decode includes `0x0:0x1000000` for Target_Memory ### 10.4 Data Mismatch on Read-Back 1. Check that `noc_n_initiator` is connected to SharedMemoryMap at start 0x0 2. Verify Target_Memory size (16 MB) covers the access offset 3. Confirm byte-enable and data width compatibility across the path --- ## 11. Related Documents | Document | Description | |----------|-------------| | [Keraunos System Architecture](Keraunos_System_Architecture.md) | Full system architecture including chiplet ecosystem | | [Keraunos PCIe Tile HLD](Keraunos_PCIe_Tile_HLD.md) | High-level design of the PCIe Tile | | [SystemC Design Document](Keraunos_PCIE_Tile_SystemC_Design_Document.md) | Detailed SystemC implementation | | [Test Plan](Keraunos_PCIE_Tile_Testplan.md) | Verification test plan | --- **Document Control:** - **Classification:** Internal Use Only - **Distribution:** Keraunos Project Team, VDK Integration Team --- **End of Document**