# Week 6 Assignment
## Caravel_hk_gpio_spi_mmio
### Question 1
Explain the Caravel RISC-V firmware code update, and boot-up sequence, it involves
1. Using passthru-mode to update spiflash
2. CPU code fetch from spi-flash
::: info
1. Using Passthru-Mode to Update SPI Flash
The Caravel RISC-V firmware update leverages passthru-mode to bypass the CPU and directly program the SPI flash via the Housekeeping SPI interface. This is initiated by sending specific commands (e.g., 0x11000100 for management flash or 0x11000110 for user flash) to reroute SPI pins (SDI, SDO, SCK, CSB) to the flash chip, resetting the CPU to prevent interference. An external host then writes firmware binaries (e.g., counter_la.hex) to predefined flash addresses using SPI mode 0 (data sampled on SCK rising edge). Raising CSB terminates passthru-mode, exits CPU reset, and restores normal SPI operation, ensuring the updated firmware is ready for boot.
2. CPU Code Fetch from SPI Flash During Boot-Up
On power-up, the Power-On Reset (POR) initializes the SPI controller, configures GPIO pins for communication, and sets the CPU’s reset vector to the SPI flash base address (0x10000000). The CPU fetches the bootloader from this address, which copies user firmware (e.g., from 0x10001000) to SRAM (dff/dff2 regions) via the Wishbone bus and initializes peripherals (clocks, GPIO). Execution transitions to the user code in SRAM or runs directly from flash in Execute-In-Place (XIP) mode if supported, with critical registers like SPI_ENABLE and SPI_CLK_DIV managing clock speed and controller status. Flash partitioning prevents bootloader overwrites, while XIP trades latency for simplicity, favoring SRAM for performance-critical tasks.
:::
### Question 2
GPIO(MPRJ) can be used for management core or user-project, explain how to program mprj
1. What is the MMIO address to configure each MPRJ pin
2. Which bit is used to set the MPRJ used by the management core or the user
3. illustrate by code (reference the firmware code)
::: info
1. MMIO Address to Configure Each MPRJ Pin
Each MPRJ GPIO pin (0–37) has a dedicated memory-mapped I/O (MMIO) register for configuration. The addresses follow a sequential pattern starting from 0x26000024 for MPRJ_IO_0 to 0x260000B8 for MPRJ_IO_37, with a 4-byte offset per pin (e.g., MPRJ_IO_1 = 0x26000028, MPRJ_IO_2 = 0x2600002C, etc.). These addresses are defined in firmware/caravel.h.
2. Bit to Assign MPRJ Control (Management vs. User)
Bit 0 (LSB) of each MPRJ pin’s configuration register determines control:
Bit 0 = 1: Management core controls the pin.
Bit 0 = 0: User project controls the pin.
This bit is part of the 13-bit configuration value.
3.
``` c=
// Set MPRJ_IO_31 as management-controlled output (Bit 0 = 1)
reg_mprj_io_31 = GPIO_MODE_MGMT_STD_OUTPUT; // Defined in user_defines.v
// Set MPRJ_IO_15 as user-controlled output (Bit 0 = 0)
reg_mprj_io_15 = GPIO_MODE_USER_STD_OUTPUT;
// Apply configurations (transfer settings to padframe)
reg_mprj_xfer = 1;
while (reg_mprj_xfer == 1); // Wait until transfer completes
// Example: Drive MPRJ_IO_31 (management) and MPRJ_IO_15 (user)
reg_mprj_datal = (1 << 31); // Set pin 31 high (management side)
reg_mprj_datah = (1 << (15 - 32)); // Set pin 15 high (user side)
```
Key Definitions from user_defines.v (Page 34):
GPIO_MODE_MGMT_STD_OUTPUT: 13'h1809 (Bit 0 = 1).
GPIO_MODE_USER_STD_OUTPUT: 13'h1808 (Bit 0 = 0).
This code configures pin 31 for the management core and pin 15 for the user project, then sets their output values. The mprj_xfer register triggers the hardware to apply the configurations.
:::
## caravel_intr_sram_wb_usrprj_firmware
### Question 1
Explain the procedure/code to move code from spiflash to dff
::: info
Procedure to Move Code from SPI Flash to DFF in Caravel SoC
The process involves copying firmware/data from the SPI flash (address 0x10000000) to the dff region (SRAM, starting at 0x00000000) during boot-up. Below is the step-by-step explanation with code:
1. Initialize SPI Controller for Flash Access
Configure the SPI controller to read from the external flash:
``` c=
// Enable SPI controller and set clock divider (Page 39/40)
reg_spi_enable = 1; // Enable SPI (address 0xF0004000)
reg_spi_clk_div = 0x10; // Set SPI clock speed (address 0xF0004818)
```
2. Define Source (Flash) and Destination (DFF) Addresses
``` c=
#define FLASH_BASE_ADDR 0x10000000 // SPI flash base address (Page 39)
#define DFF_BASE_ADDR 0x00000000 // dff SRAM base address (Page 39)
uint32_t *src = (uint32_t*)FLASH_BASE_ADDR;
uint32_t *dest = (uint32_t*)DFF_BASE_ADDR;
```
3. Copy Data from Flash to DFF via Wishbone Bus
Loop through flash data and write to SRAM:
``` c=
// Example code from bootloader (Page 56)
void copy_flash_to_dff(uint32_t size_bytes) {
uint32_t words = size_bytes / 4; // Transfer 32-bit words
for (uint32_t i = 0; i < words; i++) {
dest[i] = src[i]; // Read from flash, write to dff
}
}
```
4. Trigger Code Execution from DFF
After copying, jump to the code in dff:
``` c=
// Assembly snippet (crt0_vex.s, Page 56)
_start:
la sp, _fstack // Initialize stack pointer
la a0, trap_entry // Set exception handler
csrw mtvec, a0 // Write to machine trap vector
jal main // Jump to C code in dff
```
5. Full Example Code
Reference counter_la.c (Page 37):
``` c=
// Copy firmware from flash to dff (simplified)
void main() {
// 1. Configure SPI and GPIO
reg_spi_enable = 1;
reg_spi_clk_div = 0x10;
// 2. Copy 1KB of code from flash to dff
copy_flash_to_dff(1024);
// 3. Execute code from dff
asm volatile("jalr %0" :: "r"(DFF_BASE_ADDR));
}
```
:::
### Question 2
List the user project interface signals (ref: the user_project wrapper.v)
What memory address is used to access the user project?
::: info
1. User Project Interface Signals
The wrapper provides these key signals for communication between the Management Core and User Project:
| Signal Group | Width | Direction | Description |
|---------------------|-------|-----------|----------------------------------------------------------------|
| **Wishbone Slave Port** | | | |
| wb_clk_i | 1 | Input | Wishbone clock |
| wb_rst_i | 1 | Input | Wishbone reset |
| wbs_cyc_i | 1 | Input | Wishbone cycle signal |
| wbs_stb_i | 1 | Input | Wishbone strobe signal |
| wbs_we_i | 1 | Input | Wishbone write enable |
| wbs_adr_i | 32 | Input | Wishbone address bus (for accessing user project registers) |
| wbs_dat_i | 32 | Input | Wishbone write data |
| wbs_dat_o | 32 | Output | Wishbone read data |
| wbs_ack_o | 1 | Output | Wishbone acknowledgment |
| **Logic Analyzer (LA)** | | | |
| la_data_in | 128 | Input | Logic analyzer probe inputs (from Management Core to monitor user project) |
| la_data_out | 128 | Output | Logic analyzer probe outputs (from User Project to Management Core) |
| la_oenb | 128 | Input | Logic analyzer output enable (active-low) |
| **MPRJ_IO** | | | |
| io_in | 38 | Input | User project GPIO input signals |
| io_out | 38 | Output | User project GPIO output signals |
| io_oeb | 38 | Output | User project GPIO output enable (active-low) |
| **User Clock** | | | |
| user_clock2 | 1 | Input | Optional secondary clock (for custom timing) |
| **IRQ** | | | |
| user_irq | 3 | Output | User project interrupt requests to Management Core (user_irq[2:0]) |
2. Memory Address for User Project Access
Address Range: 0x30000000 to 0x3FFFFFFF (1GB space).
Purpose:
The Management Core accesses the User Project’s registers or memory via the Wishbone bus within this range.
Example: Writing to 0x30000000 targets the User Project’s first Wishbone register.
:::
### Question 3
Explain the counter_WB example, Verilog testbench, and firmware C code
::: info
1. Counter Design
The counter design is usually a simple hardware module that counts up or down based on control signals. It interfaces with the Wishbone bus, allowing external devices to read the current count value or modify it.
Key Features of the Counter:
Increment/Decrement Control: The counter can be configured to increment or decrement based on a control signal.
Reset Functionality: It typically has a reset input to set the counter back to zero.
Output: The current count value is available on the output bus.
2. Verilog Testbench
The testbench is used to simulate the behavior of the counter module. It generates clock signals, applies reset, and sends read/write commands to the counter.
Key Features of the Testbench:
Clock Generation: A clock signal is generated to drive the counter.
Reset Logic: The testbench applies a reset signal to initialize the counter.
Stimulus Application: It applies different inputs to test both read and write operations.
3. Firmware C Code
The firmware in C is typically designed to run on a microcontroller or processor that communicates with the counter via the Wishbone interface. The firmware will include functions to read and write to the counter.
Key Features of the Firmware:
Initialization: Set up the hardware and prepare for communication.
Read/Write Functions: Functions to read the counter value and write to it.
:::
## caravel soc - lab4-0
Observe Caravel SoC simulation, show waveforms with related signals
::: info

1. SPI Flash Access & Code Execution (Observe CPU Trace)
Observation:
When the CPU accesses the SPI flash, you will see a series of signals indicating the start of a read operation, followed by the clock and data signals.
The CPU will assert the necessary control signals (like spi_csb, spi_clk, etc.) to initiate the read process.
Waveform Signals:
SPI Clock (spi_clk): A square wave indicating the clock signal for SPI communication.
SPI Chip Select (spi_csb): This signal will go low to select the SPI flash for communication.
Data Lines (spi_mosi, spi_miso): These will show the data being sent and received during the operation.
CPU Instruction Fetch (pcout, irout): After the data is fetched, you will see the instruction register (irout) being loaded with the new instruction from the SPI flash, indicated by a change in the irout signal.
2. CPU WB Cycles Interaction with User Project Area
Observation:
The CPU will perform Wishbone bus cycles to read from and write to the user project area.
You will observe the wb_cyc, wb_stb, and wb_ack signals during these interactions.
Waveform Signals:
Wishbone Clock (wb_clk_i): The clock signal for the Wishbone bus.
Wishbone Cycle (wb_cyc_i): This signal will be asserted when the CPU initiates a transaction.
Wishbone Strobe (wb_stb_i): Indicates that the CPU is ready to send or receive data.
Wishbone Acknowledge (wb_ack_o): This signal will go high to acknowledge that the transaction has completed.
Address (wbs_adr_i): Shows the address being accessed in the user project area.
3. CPU Interface with User Project with Logic Analyzer (LA)
Observation:
The Logic Analyzer (LA) probes will capture the signals from the CPU interacting with the user project.
You will see the data being sent and received as well as control signals.
Waveform Signals:
LA Data Inputs (la_data_in): Displays the data being monitored from the user project.
LA Data Outputs (la_data_out): Shows the data being sent back to the CPU or other parts of the system.
Control Signals: Signals like la_oenb (output enable) will indicate when the logic analyzer is active.
4. User Project/RISC-V Uses MPRJ Pin and Interacts with Testbench
Observation:
The user project utilizes the MPRJ pins to interact with external components or the testbench.
You will observe how the user project communicates with the testbench through these pins.
Waveform Signals:
MPRJ I/O Pins (mprj_io): These signals will show the interaction between the user project and the external environment.
Testbench Signals: Signals from the testbench that indicate commands or data being sent to the user project.
Interrupt Requests (user_irq): If applicable, you may see interrupt signals being asserted if the user project generates any interrupts.
:::