## Week-6 Questions ### Topic 1 : Caravel_hk_gpio_spi_mmio #### Question : Explain the Caravel RISC-V firmware code update, and boot-up sequence This is typically the **front-door onboard SPI** flash flow, which the **SPI interface** connected to the **padframe** through **MPRJ GPIO** pins. **1. Using passthru-mode to update spiflash** ![image](https://hackmd.io/_uploads/SyeIxatxZlg.png) In **passthru mode**, external SPI signals are passed through **mprj io pins** to the **housekeeping**. This connects the SPI Flash to an external programmer, allowing firmware updates without CPU involvement. **2. CPU code fetch from spi-flash** On reset, the CPU fetches instructions directly from **SPI Flash** via **iBus**. This enables autonomous boot-up using the stored firmware. --- #### Question: GPIO(MPRJ) can be used for management core or user-project, explain how to program mprj? ![image](https://hackmd.io/_uploads/r1I4wjpmxg.png) **1. What is the MMIO address to configure each MPRJ pin?** `caravel-soc_fpga-main\firmware\caravel.h` ```C = // User Project Control (0x2300_0000) #define reg_mprj_xfer (*(volatile uint32_t*)0x26000000) #define reg_mprj_pwr (*(volatile uint32_t*)0x26000004) #define reg_mprj_irq (*(volatile uint32_t*)0x26100014) #define reg_mprj_datal (*(volatile uint32_t*)0x2600000c) #define reg_mprj_datah (*(volatile uint32_t*)0x26000010) #define reg_mprj_io_0 (*(volatile uint32_t*)0x26000024) #define reg_mprj_io_1 (*(volatile uint32_t*)0x26000028) // ... #define reg_mprj_io_36 (*(volatile uint32_t*)0x260000b4) #define reg_mprj_io_37 (*(volatile uint32_t*)0x260000b8) ``` **2. Which bit is used to set the MPRJ used by the management core or the user** This is controlled via **bit 0**, named `MGMT_ENABLE`: ```C= #define MGMT_ENABLE 0x0001 ``` * If **bit 0 = 1**, the GPIO is controlled by the **management core** * If **bit 0 = 0**, the GPIO is controlled by the **user project** **3. illustrate by code (reference the firmware code)** In the firmware code `caravel-soc_fpga-main\testbench\counter_la\counter_la.c`, we configure each MPRJ GPIO pin to be either controlled by the management SoC or the user project using predefined macros: ```C= // Management SoC reg_mprj_io_31 = GPIO_MODE_MGMT_STD_OUTPUT; // ... reg_mprj_io_16 = GPIO_MODE_MGMT_STD_OUTPUT; // User project reg_mprj_io_15 = GPIO_MODE_USER_STD_OUTPUT; // ... reg_mprj_io_0 = GPIO_MODE_USER_STD_OUTPUT; // Apply the configuration reg_mprj_xfer = 1; while (reg_mprj_xfer == 1); ``` The macros like `GPIO_MODE_USER_STD_OUTPUT` are defined in `caravel-soc_fpga-main\rtl\header\user_defines.v` as: ```verilog= `define GPIO_MODE_USER_STD_OUTPUT 13'h1808 `define GPIO_MODE_MGMT_STD_OUTPUT 13'h1809 ``` These values determine whether the pin is driven by the **user project** or the **management core**, and its behavior (input/output...). --- ### Topic 2 : Caravel_intr_sram_wb_usrprj_firmware #### Question : Explain the procedure/code to move code from spiflash to dff ![image](https://hackmd.io/_uploads/By6uJB1-lg.png) To move program data from **SPI Flash** to **DFF (RAM)**, the boot process follows the logic defined in both the linker script and the startup assembly code. **1. Entry Point & Startup Code** In the linker file `sections.lds` , the entry point is defined as: ```ld= ENTRY(_start) ``` So the boot process begins at the `_start` label in `crt0_vex.s`: ```.s= _start: j crt_init # Jump to the initialization routine ``` **2. Initialization** In `crt0_vex.S`, copy `.data` section from Flash to RAM (DFF) ```.s= data_init: la a0, _fdata # RAM start of .data la a1, _edata # RAM end of .data la a2, _fdata_rom # Flash address where .data is stored data_loop: beq a0,a1,data_done lw a3,0(a2) # Load data from Flash sw a3,0(a0) # Store data to RAM add a0,a0,4 add a2,a2,4 j data_loop data_done: ``` This part transfers each 32-bit word from Flash (_fdata_rom) to RAM (_fdata~_edata). **3. Memory mapping** In `sections.lds`, the linker script do the memory mapping: ```.ld= .data : { . = ALIGN(8); _fdata = .; *(.data .data.* .gnu.linkonce.d.*) *(.data1) _gp = ALIGN(16); *(.sdata .sdata.* .gnu.linkonce.s.*) . = ALIGN(8); _edata = .; } > dff AT > flash PROVIDE(_fdata_rom = LOADADDR(.data)); ``` * `.data` section is loaded into DFF at runtime, but its content is originally stored in Flash. * `AT > flash` means the initial values are stored in flash. * `PROVIDE(_fdata_rom = LOADADDR(.data));` gives the linker a symbol to access the flash address of `.data`. --- #### Question : List the user project interface signals (ref: the user_project wrapper.v). What memory address is used to access the user project? ![image](https://hackmd.io/_uploads/H175kBJble.png) **1. User project interface signals** From the `user_project_wrapper.v`, the following signals are user project interface signals: (1) Wishbone Bus Interface ```verilog= input wb_clk_i, // Clock input wb_rst_i, // Reset input wbs_stb_i, // Strobe input wbs_cyc_i, // Cycle input wbs_we_i, // Write enable input [3:0] wbs_sel_i, // Byte enables input [31:0] wbs_dat_i, // Write data input [31:0] wbs_adr_i, // Address output wbs_ack_o, // Acknowledge output [31:0] wbs_dat_o // Read data ``` This is how the management SoC reads/writes registers inside the user project. (2) Logic Analyzer Interface ```verilog= input [127:0] la_data_in, output [127:0] la_data_out, input [127:0] la_oenb ``` Used for debugging or controlling internal signals from the testbench or firmware. (3) IO Pads ```verilog= input [`MPRJ_IO_PADS-1:0] io_in, output [`MPRJ_IO_PADS-1:0] io_out, output [`MPRJ_IO_PADS-1:0] io_oeb ``` Used for external communication (GPIO, UART, SPI...). (4) Clock ```verilog= input user_clock2 ``` An user-defined clock. (5) User Interrupts ```verilog= output [2:0] user_irq ``` Interrupt signals sent from the user project to the management SoC. **2. Address used to access the user project?** From `section.lds`: ```verilog= mprj : ORIGIN = 0x30000000, LENGTH = 0x00100000 ``` The user project is memory-mapped to the address range: **0x3000_0000 ~ 0x300F_FFFF** --- #### Question : Explain the counter_WB example, Verilog testbench, and firmware C code ![image](https://hackmd.io/_uploads/HkFskry-gx.png) **1. Memory Mapping and Address Decoding** In `section.lds`, the mprj region is defined: ```.ld= mprj : ORIGIN = 0x30000000, LENGTH = 0x00100000 ``` This means that writing or reading from address `0x30000000` in firmware will access the `user_proj_example` via the Wishbone bus. In firmware: ```c= #define reg_mprj_slave (*(volatile uint32_t*)0x30000000) ``` `reg_mprj_slave` maps to the address `0x30000000`. **2. Firmware C Code : counter_wb.c** In `counter_wb.c` ```C= reg_mprj_slave = 0x00002710; // Write to counter if (reg_mprj_slave == 0x2B3D) // Read counter value ``` * Write operation(`wbs_we_i = 1`) : The value is sent via `wbs_dat_i` * Read operation (`wbs_we_i = 0`): The result is received via `wbs_dat_o` The Wishbone interface selects between input (`wbs_dat_i`) and output (`wbs_dat_o`) depending on whether it's a write (`sw`) or read (`lw`) operation. | Operation | `wbs_we_i` | Data Path | | --------- | ---------- | --------------------------- | | Write | `1` | CPU → `wbs_dat_i` → counter | | Read | `0` | counter → `wbs_dat_o` → CPU | **3. RTL code : user_proj_example.v** ```verilog= assign wbs_dat_o = rdata; assign wdata = wbs_dat_i; ``` * Data from CPU (`wbs_dat_i`) is forwarded to the internal counter. * Output from the counter (`rdata`) is sent via `wbs_dat_o`. --- ### Topic 3 : Caravel soc - lab4-0 #### Observe Caravel SoC simulation, show waveforms with related signals ![image](https://hackmd.io/_uploads/S1Mi3wtmel.png) Reference to [Lab4-0 Caravel Simulation](https://hackmd.io/LwFmZAwjTfy8uPvenTmelQ)