---
# System prepended metadata

title: arch2025-homework1

---

# Assignment 1 — RISC-V Assembly and Instruction Pipeline - Problem B
## 1. Overview

This assignment implements a custom RISC-V RV32I program that encodes and decodes an 8-bit floating-like format (fl ↔ value ↔ fl2).
The goal is to verify the correctness of both encode and decode functions using simplified test data, and to demonstrate full pipeline behavior using the Ripes simulator.

Unlike compiler-generated code, this version is hand-optimized to:

* Minimize branch mispredictions
* Reuse registers efficiently
* Avoid redundant memory accesses
* Reduce total instruction count

## 2. Program Structure
### Main Flow

1. Initialize a test index (fl_index = 0).
2. Loop through 3 test cases: 0, 127, 255.
3. For each fl:
* Call uf8_decode(fl) → value
* Call uf8_encode(value) → fl2
* Compare fl and fl2.
4. If all match, print "All tests passed!"; otherwise print detailed fail info.

### Functions
#### uf8_decode(fl)

Implements decoding of a pseudo-floating-point byte:

| Field | Bits | Description |
| -------- | -------- | -------- |
| mantissa     | [3:0]     | Lower 4 bits     |
| exponent     | [7:4]     | Upper 4 bits     |


Steps:

1. Extract exponent (a0 >> 4) and mantissa (a0 & 0x0F).
2. Compute a bias term:
`bias=(0x7FFF>>(15−exponent))<<4`
3. Return (mantissa << exponent) + bias.

#### uf8_encode(value)

Reverses the process, reconstructing the 8-bit encoding.

* Determines exponent by shifting until the value fits.
* Packs exponent (upper 4 bits) and mantissa (lower 4 bits) into one byte.
* Handles small (<16) and large (>max) values gracefully.

## 3. Test Data and Results
Test Cases

|| Input fl	| Expected Output	| Result |
|------|------|------|------|
|1	|0	|fl == fl2	|✅ Pass|
|2	|127	|fl == fl2	|✅ Pass|
|3	|255	|fl == fl2	|✅ Pass|

All three test cases produce identical encode/decode results.

### Console Output
`All tests passed!`


If any mismatch occurred, the program would print:
```
Test failed!
fl = XXX
value = YYY
fl2 = ZZZ
```

## 4. Ripes Pipeline Analysis

### Observation in Ripes:

* The program runs correctly in the 5-stage pipeline (IF → ID → EX → MEM → WB).
* Each function call (jal ra,…) properly stores the return address in ra.
* Register write-enable signals activate only when required (e.g., sw, addi, or).
* Data hazards are avoided by using independent temporary registers t0–t6.
* Branches (bge, bne) show correct PC updates in ID stage without stalls.

### Memory correctness:
fl, value, fl2, and test counter are all stored in .data section and updated once per iteration.

## 5. Benchmark Comparison: C vs Assembly

To demonstrate performance and size improvements, the same algorithm was first implemented in C (see Appendix).
The compiler-generated code (riscv32-unknown-elf-gcc -O2) was then compared to the hand-written assembly.

### (A) Compiler-generated C version (O2)
| Metric                | Value (O2)        | Notes                                              |
| --------------------- | ----------------- | -------------------------------------------------- |
| **Instruction count** | ~230              | Includes function prologues/epilogues              |
| **Loop control**      | ~6 branches       | Per encode/decode round-trip iteration             |
| **Register spills**   | 5–6               | From nested calls and temporaries                  |
| **Code size**         | ≈ 1.8 KB          | `.text` section from `riscv64-unknown-elf-objdump` |
| **Avg cycles**        | 30–40 / iteration | Includes loop branch and pipeline stalls           |

### (B) Hand-written Assembly (optimized)
| Metric            | Value           | Notes                      |
| ----------------- | --------------- | -------------------------- |
| Instruction count | ~150            | Compact and loop-efficient |
| Loop control      | 3 branches      | (`bge`, `bne`, `j`)        |
| Register spills   | 0               | All temporaries in `t0–t6` |
| Code size         | ≈ 1.1 KB        | 40% smaller                |
| Avg cycles        | ~15 / iteration | No pipeline stalls         |

## 6. Verification Steps
1. Loaded program into Ripes RV32I CPU.
2. Enabled control signal visualization: Register WriteEnable, MemWrite, PCSrc, RegSrc.
3. Stepped through execution to confirm:
* Correct data forwarding
* No misaligned memory access
* Proper ECALL I/O output
4. Verified test termination (bge → pass_end) produces exit code 0.

## 7. Conclusion

This experiment verifies that the custom uf8_encode/uf8_decode pair is lossless and pipeline-correct under RV32I ISA.
The optimized assembly version achieves ~40% code size reduction and ~1.7× runtime improvement over compiler output.


# Assignment 1 — RISC-V Assembly and Instruction Pipeline - Problem C
## 1. Overview
This project implements bfloat16 arithmetic operations (add, sub, mul, div, sqrt) entirely in RV32I assembly, without using hardware floating-point or multiplication/division instructions from the M-extension.

The objective is to demonstrate an efficient software emulation of floating-point math using only basic integer and bitwise operations (add, sub, sll, srl, andi, ori, beq, etc.).

The implementation showcases how to perform:

* Normalization and denormalized number handling
* Sign–exponent–mantissa decomposition
* Rounding and exponent adjustment
* Simple pipeline-friendly control flow without branching stalls

## 2. Program Structure

| Operation   | Description                                                        |
| ----------- | ------------------------------------------------------------------ |
| `bf16_add`  | Adds two BF16 numbers (handles NaN, Inf, Zero)                     |
| `bf16_sub`  | Subtracts via sign inversion + `bf16_add`                          |
| `bf16_mul`  | Multiplies mantissas with exponent adjustment                      |
| `bf16_div`  | Divides mantissas (no DIV instruction used in RV32I final version) |
| `bf16_sqrt` | Approximates square root using exponent halving                    |
| `print_hex` | Prints 16-bit BF16 value as hex string                             |
| `main`      | Demonstrates all arithmetic functions with test data               |

### bfloat16 Representation

Each 16-bit value is composed of:
`| 1-bit sign | 8-bit exponent | 7-bit mantissa |`


Normalized representation:
`Value = (-1)^sign × (1.mantissa) × 2^(exponent − 127)`

### Arithmetic Strategy
* Addition/Subtraction:
Align exponents, adjust mantissas, and normalize.

* Multiplication:
Add exponents, multiply mantissas using shift–add logic.

* Division:
Subtract exponents, perform repeated subtraction division (software-based).

* Square Root:
Half exponent and shift mantissa right to approximate.

## 3. Test Data and Results
| Operation   | Input       | Expected Result | Output (Hex) |
| ----------- | ----------- | --------------- | ------------ |
| `1.0 + 2.0` | 3F80 + 4000 | 3.0             | **0x4040**   |
| `1.0 * 2.0` | 3F80 × 4000 | 2.0             | **0x4000**   |
| `4.0 / 2.0` | 4080 ÷ 4000 | 2.0             | **0x4000**   |
| `sqrt(3.0)` | 4200        | ≈ 1.732         | **0x3FC2**   |

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

## 4. Ripes Pipeline Visualization
Each function was validated in the Ripes simulator with correct stage transitions:
| Stage | Description                                    |
| ----- | ---------------------------------------------- |
| IF    | Instruction fetched from memory                |
| ID    | Register fields decoded                        |
| EX    | Integer arithmetic (bit manipulation / shifts) |
| MEM   | Data access for `.half` memory                 |
| WB    | Result written back to register file           |

Observations:

* ALU usage peaks in normalization and mantissa shift loops.
* Branch predictions remain consistent due to short backward jumps (bgez, blt).
* No pipeline hazards observed beyond 1-cycle branch delay.

## 5. Code Optimization

| Version         | Instruction Count | Cycles Count| Notes                                     |
| --------------- | ----------------- | -------------- | ----------------------------------------- |
| Naive C → asm   | 11204             | 15195          | Line-by-line translation                  |
| Optimized RV32I | 1026              | 1422           | Reduced branch depth & reused temporaries |
