---
# System prepended metadata

title: Lab4-0--Caravel Simulation

---

## Lab4-0 Caravel Simulation

### 1. spiflash access & code execution (observe CPU trace )
#### Intro:
In the Caravel SoC, CPU doesn’t have much internal memory. It needs to load its firmware (the program code) from an external storage device like SPI Flash to start running.
#### Data flow: 
* `spiflash.v` role : simulates the behavior of a real **SPI Flash** chip
* Flow:
1️⃣ CPU wants to fetch instructions
2️⃣ CPU sends SPI commands to spiflash
3️⃣ spiflash reads the corresponding program code from the BRAM
4️⃣ Return to the CPU
#### CPU access spiflash: 
`caravel-soc_fpga-main\rtl\soc-efabless\VexRiscv_MinDebugCache.v`

CPU access spiflash by two bus:
| Purpose | Bus | 
| -------- | -------- | 
| Instruction fetch     | iBus     |
Data access    | dBus|

**iBus signal**: 
| Signal name                | Description                                            |
| -------------------------- | ------------------------------------------------------ |
| `iBus_cmd_valid`           |  High when the CPU requests a new instruction fetch  |
| `iBus_cmd_ready`           |  High when SPI Flash is ready to accept the request  |
| `iBus_cmd_payload_address` |  The address of the instruction to fetch           |
| `iBus_rsp_valid`           |  High when the data is ready and being returned      |
| `iBus_rsp_payload_data`    |  The data returned (typically a 32-bit instruction)|

![image](https://hackmd.io/_uploads/S1UPvlpgxx.png)

**dBus signal**: 
| Signal name                | Description                                            |
| -------------------------- | ------------------------------------------------------ |
| `dBus_cmd_valid`           | High when the CPU issues a data access request      |
| `dBus_cmd_ready`           | High when the target is ready to accept the request |
| `dBus_cmd_payload_address` | The memory address to access                      |
| `dBus_cmd_payload_wr`      | 1 = write operation, 0 = read operation            |
| `dBus_cmd_payload_data`    | Data to write (for write operations)               |
| `dBus_cmd_payload_mask`    | Which bytes are valid (used for partial writes)     |
| `dBus_rsp_valid`           | High when data is returned (for reads)              |
| `dBus_rsp_payload_data`    | The returned data (for reads)                      |

![image](https://hackmd.io/_uploads/SJAN5gTlxe.png)

### 2. CPU WB cycles interaction with user project area.
#### Intro: 
CPU and user project transfer data by wishbone bus
#### Signals
| Signal Name             | Description                                                |
| ----------------------- | ---------------------------------------------------------- |
| `wb_clk_i`              | Wishbone bus clock                                         |
| `wb_rst_i` | Reset signals                                              |
| `wb_adr_i[31:0]`        | Address bus, the target address specified by the CPU       |
| `wb_dat_i[31:0]`        | Data written from the CPU                                  |
| `wb_dat_o[31:0]`        | Data read from the user project                            |
| `wb_cyc_i`              | Indicates the start of a valid Wishbone transfer cycle     |
| `wb_stb_i`              | Data transfer request, asserted to initiate a transfer     |
| `wb_we_i`               | Write enable, high for write, low for read                 |
| `wb_ack_o`              | Acknowledgment from user project indicating transfer is done |
| `wb_sel_i[3:0]`         | Byte enable, selects valid byte                       |

#### Data Flow
**CPU wirte to user project:**
* CPU setup
    * `wb_adr_i`: write in address
    * `wb_dat_i`: write in data
    * `wb_we_i` = 1: write operation
    * `wb_cyc_i` = 1 和 wb_stb_i = 1: transfer activation
* user project respond
    * write in data of the corresponding address
    * `wb_ack_o = 1`: transfer done
    
**CPU read from user project:**
* CPU setup
    * `wb_adr_i`: read address
    * `wb_we_i = 0`: read operation
    * `wb_cyc_i = 1` 和 `wb_stb_i = 1`: transfer activation
* user project respond
    * fetch data from the address, and put the data on `wb_dat_o`
    * `wb_ack_o = 1`: data prepared
    
![image](https://hackmd.io/_uploads/SJlf410eel.png)

### 3. CPU interface with user project with la
#### Intro:
LA(Logic analyzer) is like a programmable io lines(bit-level), it can process data between CPU and user project.
#### Signals:

| Signal               | direction                   | Function                                                      |
| ------------- | ------- | -------------------- 
| `la_data_in[127:0]`  | CPU -> user project | data send form CPU to user project                             |
| `la_data_out[127:0]` | user project -> CPU          | data send form user project to CPU             |
| `la_oenb[127:0]`     |  CPU -> user project  | 1: CPU read from user project; 0: CPU write to user project |
#### Data flow:
**CPU part:** 
```C=
// Now, apply the configuration
reg_mprj_xfer = 1;
while (reg_mprj_xfer == 1);

// Configure LA probes [31:0], [127:64] as inputs to the cpu 
// Configure LA probes [63:32] as outputs from the cpu
reg_la0_oenb = reg_la0_iena = 0x00000000;    // [31:0]
reg_la1_oenb = reg_la1_iena = 0xFFFFFFFF;    // [63:32]    
reg_la2_oenb = reg_la2_iena = 0x00000000;    // [95:64]
reg_la3_oenb = reg_la3_iena = 0x00000000;    // [127:96]

// Flag start of the test 
reg_mprj_datal = 0xAB400000;

// Set Counter value to zero through LA probes [63:32]
reg_la1_data = 0x00000000;

// Configure LA probes from [63:32] as inputs to disable counter write
reg_la1_oenb = reg_la1_iena = 0x00000000;    

while (1) {
    if (reg_la0_data_in > 0x1F4) {
        reg_mprj_datal = 0xAB410000;
        break;
    }
}
//print("\n");
//print("Monitor: Test 1 Passed\n\n");	// Makes simulation very long!
reg_mprj_datal = 0xAB510000;
```
* Initialization: After `reg_mprj_xfer` is cleared to 0, user project is considered loaded and ready to run.
* LA configuration: LA probes [63:32] are outputs from CPU to user project; others are inputs to CPU.
* Flags: `reg_mprj_datal` serves as start/end flag .  Here, start at `0xAB400000` and end at `0xAB410000`
* Termination: The CPU polls LA[31:0] (user project counter output), and ends the test when it exceeds `0x1F4 `(500), writing `0xAB410000` to `reg_mprj_datal`

**user project part:**
```verilog=
// LA
assign la_data_out = {{(127-BITS){1'b0}}, count};
// Assuming LA probes [63:32] are for controlling the count register  
assign la_write = ~la_oenb[63:32] & ~{BITS{valid}};
// Assuming LA probes [65:64] are for controlling the count clk & reset  
assign clk = (~la_oenb[64]) ? la_data_in[64]: wb_clk_i;
assign rst = (~la_oenb[65]) ? la_data_in[65]: wb_rst_i;
```
```verilog=
if (~|la_write) begin
    count <= count + 1;
end
if (valid && !ready) begin
    ready <= 1'b1;
    rdata <= count;
    if (wstrb[0]) count[7:0]   <= wdata[7:0];
    if (wstrb[1]) count[15:8]  <= wdata[15:8];
    if (wstrb[2]) count[23:16] <= wdata[23:16];
    if (wstrb[3]) count[31:24] <= wdata[31:24];
end else if (|la_write) begin
    count <= la_write & la_input;
end
```
* Initialization: `reg_la1_data = 0xFFFFFFFF` + `la1_oenb = 0x00000000` → `la_write != 0` → `|la_write` holds → enter `count <= la_write & la_input`
* Start counting: `reg_la1_data = 0x00000000` + `reg_la1_oenb = 0xFFFFFFFF` → `la_write = 0` → `~|la_write` holds → enter `count <= count + 1`

**Summarize**
| Operation          | CPU                 | user project |
| ----------- | --------------------------- | ------------------------- |
| CPU write in user project      | `reg_la1_data = 0`          | `count <= la_input`       |
| activate `count + 1` | `reg_la1_oenb = 0x00000000` | `count <= count + 1`      |
| CPU read `count` in user project  | `reg_la0_data_in`           | `la_data_out = count`     |

![image](https://hackmd.io/_uploads/ryFVhGRgex.png)

### 4. User project/RISC-V uses mprj pin, and interacts with Testbench
#### Intro:
User project communicate with testbench via `mprj_io[37:0]` .
* In real situation: 
 pin <--> padframe <--> mprj_io <--> user project
* In simulation:
  testbench --(assign)-- mprj_io <--> user project
#### Data flow and Signals
```
user_project_wrapper.v
   │
   └─► user_io_out
          │
          └─► gpio_control_block.v (user_gpio_out)
                  │
                  └─► pad_gpio_out
                         │
                         └─► mprj_io_out (chip_io.v)
                                 │
                                 └─► io_out (mprj_io.v)
                                         │
                                         └─► io
                                              │
                                              └─► mprj_io (chip_io.v and caravel.v)
```
* `user_project_wrapper.v` :  Defines the user logic and drives `io_out`, `io_oeb`, and reads `io_in` to control GPIO pins.
* `gpio_control_block.v` : Translates user signals into pad-level control signals (`pad_gpio_out`, `pad_gpio_outenb`, `pad_gpio_in`)
* `chip_io.v`: Connects all GPIO control blocks to the physical padframe, organizing routing between Caravel and I/Os.
* `mprj_io.v`: Implements bidirectional I/O buffers that connect internal control signals to the actual I/O pads.
* `caravel.v`: Top-level module that integrates everything, including the user project, management SoC, and chip I/O.
#### What to observe in testbench
`checkbits  = mprj_io[31:16]`
![image](https://hackmd.io/_uploads/S17BpVAglg.png)
The checkbits correspond to the `reg_mprj_datal` 