# 作業一:RISC-V 組合語言與指令管線
contributed by < [`eastwillow`](https://github.com/eastWillow) >
You can find the source code [here](https://github.com/eastWillow/ca2025-quizzes). Feel free to fork and modify it.
## 解題步驟
0. 理解作業需求,作業說明有說可以利用現實應用來聚焦作業內容
1. 理解 RISCV Unprivileged 的規格,這裡會放一些撰寫作業中用到的部份
2. 理解 RISC-V 5-Stage Pipe Line 的行為
3. 理解 RISC-V Emulator: RIPES 的環境
4. 把大題目拆解成小題目,先使用熟悉的C 語言寫出草稿
5. 利用AI 與數學公式產生測試資料,盡量達到Line Coverage 100%
6. 已經固定下來的程式行為,逐步修改程式(重構),
將C 語言程式碼逐步轉換成RV32I ASM,先把題目寫完
7. C 語言與手寫組合語言的效能差異,使用現實題目當中可能最常使用的程式碼當作舉例
## 利用現實應用來聚焦作業內容
Develop a simplified but informative use case to frame your assignment. You may select examples from LeetCode or practical open-source projects related to the quiz problems. For instance: [^assignment1Request]
### 作業B [^arch2025-quiz1-sol#Problem-B] 應用在超音波距離Sensor 誤差估算
> Assume uf8 implements a logarithmic 8-bit codec that maps 20-bit unsigned integers ([0,1,015,792]) to 8-bit symbols via logarithmic quantization, delivering 2.5:1 compression and ≤6.25% relative error.
>The uf8 encoding scheme is ideal for sensor data applications such as temperature and distance measurements where range is more important than precision. In graphics applications, it efficiently represents level-of-detail (LOD) distances and fog density values. The scheme is also well-suited for timer implementations that use exponential backoff strategies. However, the uf8 encoding should not be used for financial calculations where exact precision is required and rounding errors are unacceptable. It is inappropriate for cryptographic applications that require uniform distribution of values for security purposes.
Error Analysis
- Absolute Error: $\Delta_{\max} = 2^e - 1$
- Relative Error: $\varepsilon_{\max} = 1/16 = 6.25\%$
- Expected Error: $\mathbb{E}[\varepsilon] \approx 3\%$
假設現實應用要使用超音波感測器機器人,並紀錄量測距離。
距離單位:毫米(mm)
感測器真實輸出範圍:20 mm ~ 4000 mm (4m)
直接以它們的「數值語意」來看「能表示的距離區間」做比較表格,先直觀的認識這些數值格式
| format | space | distance range(mm) |
| --------------------- | -------:| ---------------------------------- |
| **uint8_t** | 1 byte | $0 \sim 255\ \text{mm}$ |
| **float (IEEE754)** | 4 bytes | $\pm 3.4 \times 10^{38} \text{mm}$ |
| **uf8 (logarithmic)** | 1 byte | $0 \sim 1,015,792\ \text{mm}$ |
然後為了不要浪費這些最大上限,可以使用Scaling 的方式例如把 0 ~ 4000mm Scaling 到對應的範為當中,就可以讓uint8_t 增加最大距離,uf8 增加精準度。
| format | each step |
| ------ | ------------------------------------------------ |
| uin8_t | $\frac{4000}{255} \approx 15.69 \text{ mm}$ |
| float | error is variable; at 15 mm roughly 1.8e-6 mm |
| float | error is variable; at 4000 mm roughly 0.238 mm |
| uf8 | error is variable; at 15 mm roughly 0.00394 mm |
| uf8 | error is variable; at 4000 mm roughly 12.90 cm |
$$
\text{scale factor} = \frac{4000}{1{,}015{,}792} \approx 0.003937814041
$$
| Exponent | Range | Scaling 4000 mm | Step Size |
| -------- | ---------------------------- | ---------------- | ------------------------------- |
| 0 | $[0, 15]$ | $[0, 0.059]$ | 0.00394 mm |
| 1 | $[16, 46]$ | $[0.063, 0.181]$ | 0.07875 mm |
| 2 | $[48, 108]$ | $[0.189, 0.425]$ | 0.01575 mm |
| 3 | $[112, 232]$ | $[0.441, 0.914]$ | 0.03150 mm |
| ... | ... | ... | $2^e \cdot \text{scale factor}$ |
| 15 | $[524{,}272, 1{,}015{,}792]$ | $[2065, 4000]$ | 12.9034 cm |
### 作業C [^arch2025-quiz1-sol#Problem-C]
#### float32 <-> bfloat16 bit 對應關係與介紹
$$
v = (-1)^S \times 2^{E-127} \times \left(1 + \frac{M}{128}\right)
$$
$S \in \{0, 1\}$ is the sign bit
$E \in [1, 254]$ is the biased exponent
$M \in [0, 127]$ is the mantissa value
```
Float32 (32 bits)
┌──────────┬──────────────┬───────────────────────────────┐
│ Sign (1) │ Exponent (8) │ Mantissa (23 bits) │
└──────────┴──────────────┴───────────────────────────────┘
31 30 23 22 16 0
│ │ │ │ │
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌──────────┬──────────────┬──────────────┐
│ Sign (1) │ Exponent (8) │ Mantissa (7) │ ← 保留高 16 bits
└──────────┴──────────────┴──────────────┘
15 14 7 6 0
```
Here’s how you can extend your bf16_t structure with a union so it can be accessed both as a raw 16-bit value and as individual bit fields (sign, exp, mantissa):
```cpp
#include <stdint.h>
typedef union {
uint16_t bits;
struct {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint16_t mantissa : 7; // M: Fraction bits
uint16_t exponent : 8; // E: Exponent bits (bias = 127)
uint16_t sign : 1; // S: Sign bit (0 = positive, 1 = negative)
#else
uint16_t sign : 1;
uint16_t exponent : 8;
uint16_t mantissa : 7;
#endif
} f;
} bf16_t;
```
You can verify what your compiler defines: [^gccEngian]
```bash
gcc -dM -E - < /dev/null | grep ENDIAN
```
Oh My Computer `gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0`
```cpp
#define __ORDER_LITTLE_ENDIAN__ 1234
#define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__
#define __ORDER_PDP_ENDIAN__ 3412
#define __ORDER_BIG_ENDIAN__ 4321
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
```
#### 順便探討一下不同的C 語言寫法取得 sing bit 的性能差異
| Expression | RV32I cost | Notes |
| --------------------------- | ---------- | ----------------------------------------------------------------- |
| `a.f.sign` | ~3 inst | may involve bitfield layout logic; not always inlined predictably |
| `(b.bits >> 15) & 1` | ~3 inst | always explicit and portable |
| `bool sign = b.bits >> 15;` | ~2 inst | mask implicit; works since shift already leaves 0/1 |

#### float32 -> bfloat16 的轉換步驟
1. Sign & Exponent Bits are remain.
2. float32 is following IEEE-754 Spec *round to nearest, ties to even*
取出要捨棄的低 16 bits
```c
0x0000FFFF + 0x7FFF
```
(0x7FFF = 0111 1111 1111 1111)
這相當於「加上 0.5」來實現「四捨」效果(因為 0x7FFF ≈ 一半)
```c
+ ((f32bits >> 16) & 1)
```
這一步是「ties-to-even」的關鍵。
當剛好在「捨去部分 == 0x8000」時(也就是剛好一半),
如果「保留部分的最後一位是奇數」,就進位;
如果是偶數,就不進位。
這讓結果符合 IEEE 的「成雙」規則(避免統計偏移)。
最後右移 16 bits
完成 round-to-nearest-even。
Section 4.3 — Rounding-direction attributes [^IEEE-754_2019]
#### BF16 preserves exponent to express large dynamic range, but dropped precision can be useful in certain domains
減少一半的 32Bits -> 16 Bits 的儲存空間,原本是Google 用來LLM 保留Exponent 用來表示變化大,但是每一個Step 的精度相對可以捨棄的一種資料格式,這種資料格式捨棄的精度,其實可以套用到實際的物理溫度感測器應用,因為Sensor 本身的精度也沒有可以完全使用到float32 的精度,例如這些溫度感測器
實際應用在溫度變化大的地方,但是精度不用太在意的應用,例如溫度計,音量計之類的。
可以節省紀錄的儲存空間,因為這些感測器,如DHT11 之類的
#### 溫濕度感測器的**實際精度**
| 感測器 | 溫度解析度 | 溫度誤差 | 濕度解析度 | 濕度誤差 |
| ------------------ |:---------- |:-------- | ---------- | -------- |
| **DHT11**[^DHT11] | 1 °C | ±2 °C | 1 %RH | ±5 %RH |
| **DHT22/AM2302[^AM2302]**| 0.1 °C | ±0.5 °C | 0.1 %RH | ±2 %RH |
| **SHT31[^SHT31]** | 0.015 °C | ±0.3 °C | 0.01 %RH | ±2 %RH |
#### Bfloat16 的每一階數值是否能夠表達溫濕度感測的精度
bfloat16 在不同數值範圍的精度是:
$$
\text{step} = \text{value} \times \frac{1}{128} \approx 0.78 \%
$$
##### 溫度精度
| 實際數值 | 1 ULP 大小(約) | 意義 |
| -------- | ---------------- | -------------------- |
| 20 °C | 0.156 °C | 可以表達到 0.16°C 級 |
| 50 °C | 0.39 °C | 約 ±0.4°C |
| 100 °C | 0.78 °C | 約 ±0.8°C |
##### 濕度精度
| 濕度 | 1 ULP 大小(約) |
| ------- | ------------------------------ |
| 50 %RH | $(50 \cdot 0.78\%) = 0.39$ %RH |
| 100 %RH | 0.78 %RH |
#### 結論表格
| 感測器 | 感測誤差 | bfloat16 誤差 | 是否造成精度損失 |
| ----- | ---------------- | ----------------- | ------------- |
| DHT11 | ±2 °C / ±5 %RH | ±0.16 °C / ±0.4 %RH | ❌ 不會 |
| DHT22 | ±0.5 °C / ±2 %RH | ±0.16 °C / ±0.4 %RH | ❌ 不會 |
| SHT31 | ±0.3 °C / ±2 %RH | ±0.16 °C / ±0.4 %RH | ⚠️ 可能略損失,但可接受 |
## 理解 RISCV Unprivileged 的規格 撰寫作業中注意到需要特別的部份
### pseudoinstructions
Assembly will replace the pseudoinstructions[^listing-of-standard-risc-v-pseudoinstructions] to risc-v instruction.
有人整理出來的常見的 pseudoinstruction 轉換
[riscv-card](https://github.com/jameslzhu/riscv-card)
舉例
1. mv rd, rs 實際上是 addi rd, rs, 0 (rd = rs + 0) [^RISCVUnprivileged]
2. nop 根據 spec 是 addi, x0, x0, 0 [^RISCVUnprivileged]
還有些沒有在Spec [^RISCVUnprivileged]當中定義的
li rd, imm
觀察Ripes 的結果會根據imm 大小產生不同的 RV32I instruction
例如 imm < I type addi 當中的 imm[11:0] ,會轉換成 addi rd, x0, imm
### Stack Pointer
use `sp(stack pointer)` to save the stack from the memory 0xFFFFFFFF
we can use stack pointer to save variable from the function caller
要注意 In the standard RISC-V calling convention, the stack grows downward and the stack pointer is always kept 16-byte aligned [^RISCV32_CALLING]
### Logical Shift V.S. Arithmetic Shift
Careful the (Logical shift) (Arithmetic shift) [^c-bitwise]
GCC's implementation on RISC-V aligns with this specification by performing arithmetic right shifts on signed integers. [^gccIntegers-implementation]
`lbu & lb is different`
lb will sign extend (0xF0 -> 0xFFFF FFF0)
lbu will keep the sign (0xF0 -> 0x0000 00F0)
在題目 `C` 當中有使用到 int32_t 的部份 `new_exp >>=1` 這裡就會造成差異
srai s8, s8, 1 # new_exp >>= 1
new_exp is int32_t so need use arithmetic shift will match the c behavior
[bf16_sqrt.c#L65](https://github.com/eastWillow/ca2025-quizzes/blob/db3ee12b4ad4cd8a03ee1f2742354d2cf9b8da11/q1-bfloat16/bf16_sqrt.c#L65)
```c
int32_t new_exp;
/* Get full mantissa with implicit 1 */
mant = 0x80 | mant; /* Range [128, 256) representing [1.0, 2.0) */
/* Adjust for odd exponents: sqrt(2^odd * m) = 2^((odd-1)/2) * sqrt(2*m) */
int32_t mask = (e & 1);
mant <<= mask; /* Double mantissa for odd exponent */
new_exp = ((e - mask) >> 1) + BF16_EXP_BIAS;
```
## RISC-V Single Cycle 的行為

可以上這個線上課程就可以自己做一個基本的 RISC-V Single Core [^LFD111x]
[當時上課每一個章節的HDL Source Code](https://drive.google.com/drive/folders/1LaKL7PigLCAuZSS0Fhs8ON_9FQ7pY5W9?usp=sharing)
[考試通過之後的證明](https://courses.edx.org/certificates/149d377201e74e3fbd4d9b192c9d73bc)
## RISC-V 5-Stage Pipe Line 的行為
### 介紹 5-stage pipelined processor 與 Single Cycle Processor 差異
主要是把 Single Cycle Processor 當中的 Fetch , Decode op-code, Execute or Memory Load save , Write Back 分成5 個 Stage.
| Stage | Abbreviation | Major Hardware Used |
| ------------------ | -------------- | ------------------------------------------------ |
| Instruction Fetch | **IF** | Program Counter (PC), Instruction Memory |
| Instruction Decode | **ID** | Register File, Control Unit, Immediate Generator |
| Execute | **EX (or IE)** | ALU, Branch Unit |
| Memory Access | **MEM** | Data Memory |
| Write Back | **WB** | Register File |
舉例來說從Fetch Instruction 的時候是可以同時 Decode op-code 的因為其實這兩件事情先後順序不會有影響其他指令的結果。那在一個Clock 裡面最佳狀態下是 pipeline 充滿了5 條期望執行的指令。

Ripes 右下角還有提供性能指標
| 參數 | 全名 | 意思 | 解釋 |
| ------------------- | ---------------------- | ------------ | --------------------------------------------------- |
| **Cycles** | Clock Cycles | 總執行週期數 | CPU 執行此程式共花了多少時鐘週期。數字越小 → 執行越快。 |
| **Instrs. retired** | Instructions Retired | 實際完成的指令數 | 完成並提交(retire)的指令總數。 |
| **CPI** | Cycles Per Instruction | 平均每條指令花的週期數 | `CPI = Cycles / Instructions`。數字越小 → pipeline 效率越高。 |
| **IPC** | Instructions Per Cycle | 每個週期平均完成的指令數 | `IPC = 1 / CPI`。數字越大 → CPU 利用率越高。 |
| **Clock rate** | Frequency | 時脈(Hz) | 模擬環境通常顯示為 0 Hz(代表未指定實體時脈)。若知道實際時脈,可算出時間。 |
CPI 為5 現在1 個指令需要5 個Clock 因為,現在pipeline 還沒fill 滿。
預期 CPI 會盡量逼近1 會是最佳的狀態,然後Ripes 不知道是不是沒有模擬一些指令週邊需要更多Clock 來執行,所以現在還不知道 Single Clock RV32I Clock 怎麼比較性能差異。
但現實情況是Memory 應該是需要更多Clock 來完成Store 與 Load,這個時候等待Store 與 Load 的時候就可以先預先把指令讀取進來,展現Pipelined Processor 的優點
### Demonstrate each stage : IF, ID, IE, MEM, WB

Pipline Hazard:
1. Structural Hazard
2. Data Hazard / Memory hazard
3. Control Hazard / Branch Hazard
Branch 會產生 Hazard ,一種pipeline 氣泡,需要盡量採用branch less 來避免這些事情可以改善執行的效率。會造成 CPI 增加,未來可以透過比較 CPI 來證明改善前後的差異。
之後可以放在心中,在撰寫RISC-V ASM 的時候才會想到。
> TODO: 目前先完成作業80% ,後續學習細節後再補充
## RISC-V Emulator : Ripes 的環境介紹
Because RISCV proive the execution environment[^RISCVUnprivileged], we can use the Ripes's `ecalls`[^RipesEcalls] to print the string.
> The implementation of a RISC-V execution environment can be pure hardware, pure software, or a combination of hardware and software. Forexample, opcode traps and software emulation can be used to implement functionality not provided inhardware
目前寫作業使用到的有以下幾個ecall
#### system exit return 0
```c
li a7, 10
ecall
```
#### print string
```c
.data
correct_str: .string "correct\n"
.text
la a0, correct_str
li a7, 4
ecall
```
#### print hex
```c
.data
printed_hex: .word 0x1234
.text
la s0, printed_hex # input pointer
lw a0, 0(s0) # load input element
li a7, 34
ecall
```
## 把大題目拆解成小題目,先使用熟悉的C 語言寫出草稿
Coding Format Follow rv32emu contributing-ov-file[^rv32emu]
利用數學公式、規格、AI產生測試資料,盡量達到Line Coverage 100%
利用 gcov + lcov 產生 Coverage Report 來確認每一行程式碼都有被執行
### Test cases for uf8_decode in Problem B
[uf8_decode.c#L18](https://github.com/eastWillow/ca2025-quizzes/blob/db3ee12b4ad4cd8a03ee1f2742354d2cf9b8da11/q1-uf8/uf8_decode.c#L18)
```
uf8 test_values[] = {0x00, 0x01, 0x0F, 0x10, 0x1F, 0x20,
0x2F, 0x30, 0x3F, 0xF0, 0xFF};
uint32_t expect_values[] = {0, 1, 15, 16, 46, 48,
108, 112, 232, 524272, 1015792};
```
### 問題 `C` 當中的 f32_to_bf16 測試資料
正負無限大(inf, -inf)
正負零(0.0, -0.0)
NaN
可精確表示值(例如 1.0, 0.5)
需要四捨五入的值(例如 1.00097656f, 1.00048828f)
設計 f32_to_bf16 test case 需要注意以下條件
1. 檢查被砍掉的 bit[15:0];
2. 若它大於 0x8000 → 進位;
3. 若它等於 0x8000 且保留位的 bit[16] 是 1 → 進位;
4. 否則不進位。
```cpp
f32bits += ((f32bits >> 16) & 1) + 0x7FFF; // [16] + [15:0] > 0x8000
return (bf16_t){.bits = f32bits >> 16};
```
### 問題 `C` 當中的 bf16_add 與 bf16_sub 測試資料
發現一種test case 的行為需要引用 IEEE-754
>When the sum of two operands with opposite signs (or the difference of two operands with like signs) is exactly zero, the sign of that sum (or difference) shall be +0 under all rounding-direction attributes except **roundTowardNegative**; under that attribute, the sign of an exact zero sum (or difference) shall be −0. However, under all rounding-direction attributes, when x is zero, x+x and x−(−x) have the sign of x. [^IEEE-754_2019]
| 運算式 | 結果符號 | 說明 |
| ------------- | -------- |:--------------------|
| `(+0) + (+0)` | `+0` | 同號相加,保持符號 |
| `(-0) + (-0)` | `-0` | 同號相加,保持符號 |
| `(+0) + (-0)` | `+0` | shall be +0 |
| `(-0) + (+0)` | `+0` | shall be +0 |
| `(+0) - (+0)` | `+0` | 等同 `(+0) + (-0)` |
| `(-0) - (-0)` | `+0` | 等同 `(-0) + (+0)` |
| `(+0) - (-0)` | `+0` | 等同 `(+0) + (+0)` |
| `(-0) - (+0)` | `-0` | 等同 `(-0) + (-0)` |
需要先確認gcc rv32 編譯器的實做
實驗之後 float32 預設是 roundTiesToEven
```
gcc bf16_add.c -o bf16_add.o -lm && ./bf16_add.o
```
結果發現bf16 的實做需要先修正 0 的減法這裡可能要發pull request
確認bf16 行為對齊f32 再翻譯成asm
[commit/9aec45](https://github.com/eastWillow/ca2025-quizzes/commit/9aec4505bc15d05b40baa72b6be607b5aac182c1)
```bash
Now Test: 0 bf16_add: 0x0 bf16_sub: 0x8000 f32_add: +0.000000 f32_sub: +0.000000
```
### 問題 `C` 當中的 bf16_sqrt 好像發現有一部分的C 語言不用轉換成ASM 也保持行為一致
bf16_sqrt 設計test case 的時候發現目前 result 不會出現大於 256 的情況。
例如 0x2222 → 0x4444,這代表 mantissa 的結果越來越大、越靠近上限 256,但不會遇到。
## 利用已經固定下來的程式行為,逐步修改程式(重構)成為RV32I ASM
### 問題 `C` 當中 bfloat16_mul 舉例
#### 乘法操作需要轉換成只有使用RV32I ASM 提供的指令來完成
```c
uint32_t result_mant = (uint32_t) mant_a * mant_b;
```
#### Step1 convert to loop multiplication
```c
static inline uint32_t mul(uint32_t a, uint32_t b)
{
uint32_t result_mant = 0;
for (int i = 0; i < 32; i++) {
if (b & (1 << i))
result_mant += a << i;
}
return result_mant;
}
```
```diff
- uint32_t result_mant = (uint32_t) mant_a * mant_b;
+ uint32_t result_mant = mul(mant_a, mant_b);
```
#### Step2 replace the inline function
```diff
- uint32_t result_mant = mul(mant_a, mant_b);
+ uint32_t result_mant = 0;
+ for (int i = 0; i < 32; i++) {
+ if (mant_b & (1 << i))
+ result_mant += mant_a << i;
+ }
```
#### Step 3: Replace the `if` statement with a bitwise expression
```diff
- if (mant_b & (1 << i))
- result_mant += mant_a << i;
+ uint32_t mask = -((mant_b >> i) & 1);
+ // bit=1 , mask=0xFFFFFFFF
+ // bit=0 , mask=0x00000000
+ result_mant += (mant_a << i) & mask;
```
### 問題 `C` 當中的 bfloat16_sqrt 也需要取代 原本的 multiplication
```diff
- uint32_t sq = (mid * mid) / 128; /* Square and scale */
+ uint32_t sq = 0;
+ for (int i = 0; i < 32; i++) {
+ uint32_t mask = -((mid >> i) & 1);
+ // bit=1 , mask=0xFFFFFFFF
+ // bit=0 , mask=0x00000000
+ sq += (mid << i) & mask;
+ }
+ sq = sq >> 7;
```
### 問題 `C` 當中 bfloat16_div Replace the `if` statement with a bitwise expression
```diff
@@ -60,11 +60,15 @@ static inline bf16_t bf16_div(bf16_t a, bf16_t b)
uint32_t divisor = mant_b;
uint32_t quotient = 0;
+ uint32_t mask = 0;
+ uint32_t shifted_divisor = 0;
for (int i = 0; i < 16; i++) {
quotient <<= 1;
- if (dividend >= (divisor << (15 - i))) {
- dividend -= (divisor << (15 - i));
- quotient |= 1;
- }
+ shifted_divisor = (divisor << (15 - i));
+ mask = (dividend < shifted_divisor);
+ mask = mask ^ 1;
+ mask = -mask;
+ dividend -= (shifted_divisor) &mask;
+ quotient |= 1 & mask;
}
```
### 問題 `C` bfloat16_comp(eq/lt/gt): Replace greater-than comparisons with equivalent less-than forms
Becasue the RV32I only have set less than instruction
`slt rd, rs1, rs2`
`rd = (rs1 < rs2)?1:0`
```diff
@@ -66,7 +66,7 @@ static inline bool bf16_lt(bf16_t a, bf16_t b) // a < b
uint32_t sign_b = (b.bits >> 15) & 1; // s7
if (sign_a != sign_b)
- return sign_a > sign_b;
+ return sign_b < sign_a;
- return sign_a ? a.bits > b.bits : a.bits < b.bits;
+ return sign_a ? b.bits < a.bits : a.bits < b.bits;
}
```
### 分享撰寫過程中的心法

分享寫ASM 的方法有一個 Variable Register Map 做人工Life Cycle管理
這樣子的方式debug 確認暫存器結果都正確

## C 語言與手寫組合語言的效能差異,使用現實題目當中可能最常使用的程式碼當作舉例
題目B 當中預期最常使用到的是 CLZ,單獨拿CLZ 出來比較差異結果
有盡量設計full coverage的方式確保不同的計算方式,計算出來最後的結果是相同的
https://github.com/eastWillow/ca2025-quizzes/blob/main/q1-uf8/clz.c
```bash
Overall coverage rate:
lines......: 100.0% (44 of 44 lines)
functions......: 100.0% (3 of 3 functions)
```
### Code Size 差異,編譯出來的大小差異
直接比較Ripes Instruction Memory 當作來比較Code Size
Ripes RISC-V Compiler Setting:

RISC-V C Compiler Install Manual:
[Lab2: RISC-V `RV32I[MA]` emulator with ELF support](https://hackmd.io/3tO0gfoqT7upOzVpFZlFBw)
##### 題目`B` 是 binary-search loop style clz asm code 最小
[**ASM** Bitwize style clz](https://github.com/eastWillow/ca2025-quizzes/blob/main/q1-uf8/clz.s)
Code Size : 0x100

[**ASM** binary-search loop style clz](https://github.com/eastWillow/ca2025-quizzes/blob/main/q1-uf8/loop_clz.s)
Code Size : 0x9C

[**C Code** binary-search loop style clz](https://github.com/eastWillow/ca2025-quizzes/blob/main/q1-uf8/clz_loop.c)
Code size : 0x11BA4


### Performace 差異,利用RIPES 當中有的指標來比較
比較 CPI 誰比較接近1 代表Performance 相對更好,在RIPES 的限制底下需要是同一顆Processor 互相比較,以下是使用5 state pipelined process 進行比較
#### 題目`B` 是 bitwize style clz 效能最好
[Bitwize style clz](https://github.com/eastWillow/ca2025-quizzes/blob/main/q1-uf8/clz.s)

[binary-search loop style clz](https://github.com/eastWillow/ca2025-quizzes/blob/main/q1-uf8/loop_clz.s)

### Memoray Usage 差異,現實都會需要有感測器紀錄確保發生過程
1. uf8 消耗 1 Byte
2. bf16 消耗 2 Bytes
3. float(IEEE-754) 消耗 4 Bytes
## Reference
[^arch2025-quiz1-sol#Problem-B]: [Quiz1 of Computer Architecture (2025 Fall)](https://hackmd.io/@sysprog/arch2025-quiz1-sol#Problem-B)
[^arch2025-quiz1-sol#Problem-C]: [Quiz1 of Computer Architecture (2025 Fall)](https://hackmd.io/@sysprog/arch2025-quiz1-sol#Problem-C)
[^listing-of-standard-risc-v-pseudoinstructions]: [a-listing-of-standard-risc-v-pseudoinstructions](https://github.com/riscv-non-isa/riscv-asm-manual/blob/e1779658b82451b34e36207042f06bce8f25a034/src/asm-manual.adoc#a-listing-of-standard-risc-v-pseudoinstructions)
[^RipesEcalls]: [Ripes ecalls.md](https://github.com/mortbopet/Ripes/blob/master/docs/ecalls.md)
[^RISCVUnprivileged]: [RISC-V+Technical+Specifications](https://riscv.atlassian.net/wiki/spaces/HOME/pages/16154769/RISC-V+Technical+Specifications)
[^rv32emu]: [rv32emu contributing-ov-file](https://github.com/sysprog21/rv32emu?tab=contributing-ov-file)
[^c-bitwise]: [你所不知道的 C 語言:bitwise 操作](https://hackmd.io/@sysprog/c-bitwise)
[^gccIntegers-implementation]: [gcc-Integers-implementation](https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation)
[^gccEngian]: [gcc-Common-Predefined-Macros](https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html)
[^IEEE-754_2019]: [IEEE-754_2019 on IEEE](https://doi.org/10.1109/IEEESTD.2019.8766229) or [IEEE-754 2019 pdf on web](https://www-users.cse.umn.edu/~vinals/tspot_files/phys4041/2020/IEEE%20Standard%20754-2019.pdf)
[^RISCV32_CALLING]: [riscv-calling.pdf](https://riscv.org/wp-content/uploads/2024/12/riscv-calling.pdf)
[^DHT11]: [DHT11 Datasheet pdf](https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf)
[^AM2302]: [AM2302 Datasheet pdf](https://cdn.sparkfun.com/assets/f/7/d/9/c/DHT22.pdf)
[^SHT31]: [SHT31 Datasheet pdf](https://www.mouser.com/datasheet/2/682/Sensirion_Humidity_SHT3x_Datasheet_analog-767292.pdf)
[^LFD111x]: [building-a-riscv-cpu-core-lfd111x](https://training.linuxfoundation.org/training/building-a-riscv-cpu-core-lfd111x/)