---
# System prepended metadata

title: Lab4_2

---

# Lab4_2
[1. Reference Workbook](https://drive.google.com/file/d/16MzSuKLZ-L679w-FC9tt5ThfWpS33fkX/view)
[2. github code package](https://github.com/Raywang908/lab4/tree/master/lab4_2/lab-caravel_fir)
[3. no_buffer fir design](https://github.com/Eltdot/SoC_Lab3_Fir)

## Spec
* `0x300000000` = ap_idle, ap_start, ap_done
* `0x300000010` = data_length
* `0x300000014` = tap number
* `0x300000040` = x[n] input
* `0x300000044` = y[n] output
* `0x300000080 ~ 0x3000000FF` = memory to store coefficient (tap) 

## Overview
### run_sim
```
rm -f counter_la_fir.hex

riscv32-unknown-elf-gcc -O3 -g -Wl,--no-warn-rwx-segments -g \
	--save-temps \
	-Xlinker -Map=output.map \
	-I../../firmware \
	-march=rv32i -mabi=ilp32 -D__vexriscv__ \
	-Wl,-Bstatic,-T,../../firmware/sections.lds,--strip-discarded \
	-ffreestanding -nostartfiles -o counter_la_fir.elf ../../firmware/crt0_vex.S ../../firmware/isr.c fir.c counter_la_fir.c
# -nostartfiles	

cp fir_opt.hex counter_la_fir.hex
#riscv32-unknown-elf-objcopy -O verilog counter_la_fir.elf counter_la_fir.hex
riscv32-unknown-elf-objdump -D counter_la_fir.elf > counter_la_fir.out

# to fix flash base address
sed -ie 's/@10/@00/g' counter_la_fir.hex

iverilog -Ttyp -DFUNCTIONAL -DSIM -DUNIT_DELAY=#1 \
	-f./include.rtl.list -o counter_la_fir.vvp counter_la_fir_tb.v

vvp counter_la_fir.vvp
rm -f counter_la_fir.vvp counter_la_fir.elf counter_la_fir.hexe
```
* `cp fir_opt.hex counter_la_fir.hex` this infers that we use `fir_opt.hex` as the instruction set. The reason will be discuss below. Mainly because we want to reduce pipeline stall.
* `riscv32-unknown-elf-gcc -O3 -g -Wl,--no-warn-rwx-segments -g \` we use -O3 to optimize the `.hex` code generation because we want to unroll the loop and reduce the instructions when sending x and receiving y.

| Option                        | Description                                                                 |
|------------------------------|-----------------------------------------------------------------------------|
| `-O0`                        | No optimization (default), used for debugging.                             |
| `-O1`                        | Basic optimization, fast compilation with minor code changes.              |
| `-O2`                        | performs most safe optimizations without changing semantics. |
| `-O3`                        | Enables further optimizations like loop unrolling and inlining, for performance needs but may increase code size. |
| `-Os`                        | Optimizes for smaller code size (suitable for embedded systems).           |
| `-Ofast`                     | All `-O3` optimizations plus aggressive ones that may break IEEE compliance; higher risk. |
| `-g`                         | Includes debugging information (can be combined with `-O2`).              |
| `-Wl,--no-warn-rwx-segments` | Passes to linker (`ld`); suppresses RWX segment warnings.                 |

### fir.h
```cpp
#ifndef __FIR_H__
#define __FIR_H__

#include <stdint.h>

#define N 11
#define data_length 64

// MMIO register addresses（保留變數名稱）
#define reg_fir_control  0x30000000
#define reg_data_length  0x30000010
#define reg_tap_number   0x30000014
#define fir_x            0x30000040
#define fir_y            0x30000044
#define reg_fir_coeff    0x30000080  // base address for tap coefficients

// MMIO access macros
#define write_reg(addr, data) (*(volatile uint32_t*)(addr) = (data))
#define read_reg(addr)        (*(volatile uint32_t*)(addr))

// Data buffers
int taps[N] = {0, -10, -9, 23, 56, 63, 56, 23, -9, -10, 0};
int inputbuffer[N];
int outputsignal[N];
//int reg_fir_x;
int reg_fir_y;

#endif
```
* we use `write_reg` to write data into the register.
* we use `read_reg` to read data from the register.

### fir.c
```cpp
#include "fir.h"
#include <defs.h>

void __attribute__ ( ( section ( ".mprjram" ) ) ) initfir() {
	//initial your fir
	//reg_fir_x = 0;
	reg_fir_y = 0;
	write_reg(reg_data_length, data_length);
	write_reg(reg_tap_number, N);
	for (int i = 0; i < N; i = i + 1){
		write_reg((reg_fir_coeff + (4 * i)), taps[i]);
	}
 	for (int i = 0; i < N; i = i + 1){
		reg_mprj_datal = (read_reg(reg_fir_coeff + (4 * i)) << 16);
	}
}
```
* we first write in `data_length` and `tap_length` to `fir.v`.
* The first for loop is to write tap into BRAM (in user_project).
* The second for loop is to read tap from BRAM.

```cpp
int* __attribute__ ( ( section ( ".mprjram" ) ) ) fir(){
	//write down your fir
    reg_mprj_datal = 0x00A50000;
    initfir();
    polling
    while(1) {
	if((read_reg(reg_fir_control) & (1 << 2)) == 0x00000004){
            write_reg(reg_fir_control, 1);
            break;
	    } 
    }

    for(int i = 0; i < data_length; i = i + 1){
        //reg_fir_x = i;
        write_reg(fir_x, i);
        reg_fir_y = read_reg(fir_y);
        //reg_mprj_datal = reg_fir_y << 16;
    }

    reg_mprj_datal = (reg_fir_y << 24) | (0x005A0000);
    reg_mprj_datal = 0x00A50000;
    while(1) {
        if((read_reg(reg_fir_control) & (1 << 2)) == 0x00000004){
            write_reg(reg_fir_control, 1);
            break;
        } 
    }

    for(int i = 0; i < data_length; i = i + 1){
        //reg_fir_x = i;
        write_reg(fir_x, i);
        reg_fir_y = read_reg(fir_y);
        reg_mprj_datal = reg_fir_y << 16;
    }
    reg_mprj_datal = (reg_fir_y << 24) | (0x005A0000);
    reg_mprj_datal = 0x00A50000;
    while(1) {
        if((read_reg(reg_fir_control) & (1 << 2)) == 0x00000004){
            write_reg(reg_fir_control, 1);
            break;
        } 
    }

    for(int i = 0; i < data_length; i = i + 1){
        //reg_fir_x = i;
        write_reg(fir_x, i);
        reg_fir_y = read_reg(fir_y);
        reg_mprj_datal = reg_fir_y << 16;
    }
    reg_mprj_datal = (reg_fir_y << 24) | (0x005A0000);

    return outputsignal;
}
```
* The while loop is to read ap_crtl and check if the engine is `ap_idle == 1`. If it is ap_idle, it assert `ap_start == 1`.
* The first for loop only write x and receive y while other for loop send y via `mprj_io` to testbench.

### .hex
```
38000224:	04f72023          	sw	a5,64(a4) # 30000040 <_esram_rom+0x1ffffd08>
38000228:	04472683          	lw	a3,68(a4)
3800022c:	00178793          	addi	a5,a5,1
38000230:	fec79ae3          	bne	a5,a2,38000224 <fir+0x124>
38000234:	01869793          	slli	a5,a3,0x18
38000238:	005a06b7          	lui	a3,0x5a0
```
![image](https://hackmd.io/_uploads/BkMVTeAggg.png)
* This is the result if we only use -O3 to optimize the .hex file. The latency between x and latency between y is **17 cycles**.
* We observe that CPU keep repeating the instruction above, and even after `SM` shakehands `38000230` still remains in cache. So, we assume that Risc-V structure does not have forwarding, the only way to solve harzard problem is to stall pipeline. Additionly, cache will fetch `38000234` and `38000238`, so it seems that Risc-V uses `always not taken` branch prediction.
* In order to make less cycle requirement. I choose to change `bne a5,a2,38000224` to `beq a5,a2,38000238` and `jal x0, 38000224` to make branch prediction correct. Simultaneously, I choose to switch `lw	a3,68(a4)` and `addi	a5,a5,1`. In order to solve data hazard(RAW) between `addi	a5,a5,1` and `bne a5,a2,38000224`.

![image](https://hackmd.io/_uploads/B1Wxzb0exe.png)
* This is the result after modifying the .hex file, and we the latency between x and latency between y is **13 cycles**. 
* The latency can be reduce to **12 cycle** since `valid` is designed to pullup at the next cycle when `wbs_cyc == 1`.

![image](https://hackmd.io/_uploads/ry2NQZRlxe.png)
![image](https://hackmd.io/_uploads/BymLX-Aegl.png)
* Final version

![image](https://hackmd.io/_uploads/BJgjQ9dZxx.png)
* Using fir design with no buffer, and the latency is 51 cycle.

## Waveform View
![image](https://hackmd.io/_uploads/H1PnNWRleg.png)
* The left activity in iBus is doing the system initailization, and when doing the instruction `100000d8 <sram_loop>:`, CPU is told to move instruction of fir.c into mprj_ram(`0x38000000`). Resulting in the activity in dBus.

![image](https://hackmd.io/_uploads/rk1KIW0ggg.png)
* Then, we write tap and read tap which is mention in the `fir.c`.

![image](https://hackmd.io/_uploads/Sk1GDZ0lxe.png)
* The last part that is doing ss and sm transfer which is writing x and receiving y.


