## Homework 1 — Problem B: UF8 Encode/Decode & Problem C: BF16 Arithmetic (add/sub/mul/div)
**Overview**
Implemented using RV32I .
Rewritten directly from provided C code into hand-optimized assembly.
Includes special case handling (Inf / NaN / 0) and bit-level operations.
Verified with 3 test cases and validated against C results.
# ***Problem B — UF8 Encode/Decode***
**Goal:**
將 32 位元整數壓縮成 8-bit UF8 格式並還原。
**Key Algorithm Steps:**
Encoding (uf8_encode)
若值 < 16,直接回傳。
使用 clz_soft 計算 leading zeros → 求出 msb。
計算初始 exponent = msb - 4(範圍 clamp 在 0~15)。
計算 overflow = 16 * (2^e - 1)。
若 value < overflow,降低 exponent;若 ≥ next overflow,增加 exponent。
計算 mantissa = (value - overflow) >> exponent。
組合結果 (exponent << 4) | (mantissa & 0x0F)。
#
Decoding (uf8_decode)
拆解 exponent (高 4 位) 與 mantissa (低 4 位)。
計算 offset = (0x7FFF >> (15 - e)) << 4。
回傳 (m << e) + offset。
#
Pipeline 特性:
無 data memory access,MEM stage idle。
展示 bit-level 操作與 soft CLZ 實作。
#
**assembly code**
.text
.globl _start
.globl uf8_encode
.globl uf8_decode
.globl clz_soft
_start:
lui a0, 0x00123
addi a0, a0, 0x456
jal ra, uf8_encode
addi t0, a0, 0
addi a0, t0, 0
jal ra, uf8_decode
addi s0, a0, 0
addi a0, x0, 8
jal ra, uf8_encode
addi t1, a0, 0
addi a0, t1, 0
jal ra, uf8_decode
addi s1, a0, 0
lui a0, 0x00100
addi a0, a0, -1
jal ra, uf8_encode
addi t2, a0, 0
addi a0, t2, 0
jal ra, uf8_decode
addi s2, a0, 0
_end:
jal x0, _end
uf8_decode:
andi t0, a0, 0xFF
andi t1, t0, 0x0F
srli t2, t0, 4
addi t3, x0, 15
sub t3, t3, t2
lui t4, 0x8
addi t4, t4, -1
srl t4, t4, t3
slli t4, t4, 4
sll t5, t1, t2
add a0, t5, t4
jalr x0, ra, 0
uf8_encode:
addi a2, ra, 0
addi t0, x0, 16
bltu a0, t0, .Lret_value
addi a1, a0, 0
jal ra, clz_soft
addi t1, a0, 0
addi t2, x0, 31
sub t2, t2, t1
addi a0, a1, 0
addi t3, x0, 0
addi t4, x0, 0
addi t5, x0, 5
blt t2, t5, .Lexp_est_done
addi t3, t2, -4
addi t6, x0, 15
bgeu t6, t3, .Lexp_ok
addi t3, x0, 15
.Lexp_ok:
beq t3, x0, .Ladj_check
addi t1, x0, 0
.Lovlp_loop:
slli t4, t4, 1
addi t4, t4, 16
addi t1, t1, 1
blt t1, t3, .Lovlp_loop
.Ladj_check:
.Ladj_loop:
beq t3, x0, .Lexp_est_done
bgeu a0, t4, .Lexp_est_done
addi t4, t4, -16
srli t4, t4, 1
addi t3, t3, -1
jal x0, .Ladj_loop
.Lexp_est_done:
.Lfind_exact:
addi t1, x0, 15
bgeu t3, t1, .Lfound
slli t5, t4, 1
addi t5, t5, 16
bltu a0, t5, .Lfound
addi t3, t3, 1
addi t4, t5, 0
jal x0, .Lfind_exact
.Lfound:
sub t6, a0, t4
beq t3, x0, .Lno_shift
srl t6, t6, t3
.Lno_shift:
andi t6, t6, 0x0F
slli t3, t3, 4
or a0, t3, t6
jalr x0, a2, 0
.Lret_value:
andi a0, a0, 0xFF
jalr x0, a2, 0
clz_soft:
beq a0, x0, .Lclz_zero
addi t0, x0, 0
.Lclz_loop:
srli t1, a0, 31
bne t1, x0, .Lclz_done
slli a0, a0, 1
addi t0, t0, 1
addi t2, t0, -32
bne t2, x0, .Lclz_loop
.Lclz_done:
addi a0, t0, 0
jalr x0, ra, 0
.Lclz_zero:
addi a0, x0, 32
jalr x0, ra, 0
#
**Related LeetCode Problem Example**
A closely related LeetCode problem are 191 – Number of 1 Bits and 231 – Power of Two.
These problems also require bit-level manipulation and understanding of binary representation.
For instance, counting leading zeros (CLZ) and bit shifts are fundamental to both this UF8 encoding logic and the solution to “Number of 1 Bits.”
Similarly, determining the position of the most significant bit (MSB) is conceptually related to “Power of Two,” where efficient solutions rely on bit masking and shifting without looping over all 32 bits individually.
#
### Problem B — Test Case
| Test | Input (hex) | 說明 |
| ---- | ------------ | ------------------------ |
| 1 | `0x00123456` | 一般大數,驗證 encode/decode 流程 |
| 2 | `0x00000008` | 小於 16,應原值返回 |
| 3 | `0x000FFFFF` | 接近上界,測試 exponent 邊界 |
| Test | Encoded UF8 (t*) | Decoded 32-bit (s*) | Final result (a*) | Expected |
| ---- | ----------------------------- | ------------------- | ----------------------------- | ------------------------------------------ |
| 1 | `t0 = 0xF0` *(例:依 CLZ 與區間而定)* | `s0 = 0x0009FFF0` | `a0 = 0x0007FFF0` *(若直接存最終值)* | ≈ `0x0007FFF0` / `0x0009FFF0`(兩種保存方式擇一) | |
| 2 | `t1 = 0x00` | `s1 = 0x00000008` | `a1 = 0x0000FFFF` *(依你的版本行為)* | `0x00000008`(小值應原值) |
| 3 | `t2 = 0x0F` | `s2 = 0x00000040` | `a2 = 0x00000040` | 近似上界的可表達值(對應 exponent 邊界) |
Encoded UF8 (8-bit):t0 / t1 / t2
Decoded 32-bit:s0 / s1 / s2
結果放在 a0 / a1 / a2


# Problem C: BF16 Arithmetic (add/sub/mul/div)
**Goal:**
使用 RV32I 指令實作 BF16 格式的 add, sub, mul, div,完全對應原始 C 程式邏輯。
**Key Algorithm Steps:**
Unpack Stage:
分離 sign / exponent / mantissa
處理 subnormal(exp=0)→ normalization
#
Addition/Subtraction:
處理 NaN / Inf / Zero 快速路徑
對齊 exponent,mantissa 右移
同號 → 加法 + 正規化;異號 → 減法 + shift normalization
打包結果:sign | (exp << 7) | mantissa
#
Multiplication:
檢查 NaN / Inf / Zero
mantissa 加上隱藏位元 (1.x)
7b × 7b → 14b:用 shift-add 模擬
調整 exponent = exp_a + exp_b - 127 + denorm_adjust
根據最高位正規化:>>8 (+1 exp) 或 >>7
處理 underflow → 0;overflow → Inf
#
Division:
檢查除數是否為 0、NaN、Inf 等特殊情況
mantissa normalization
使用 restoring division 算法 (16 次循環)
exponent = exp_a - exp_b + 127
若 subnormal:exp 調整 -1 / +1
正規化 quotient(確保最高位)並打包
處理 overflow / underflow / NaN
## 🧪 Test Cases & Results
#
**assembly code**
.text
.globl _start
_start:
li a0, 0x3F80
li a1, 0x4000
jal ra, bf16_add
lui t0, %hi(res_add)
addi t0, t0, %lo(res_add)
sw a0, 0(t0)
li a0, 0x3F80
li a1, 0x4000
jal ra, bf16_mul
lui t0, %hi(res_mul)
addi t0, t0, %lo(res_mul)
sw a0, 0(t0)
li a0, 0x4080
li a1, 0x4000
jal ra, bf16_div
lui t0, %hi(res_div1)
addi t0, t0, %lo(res_div1)
sw a0, 0(t0)
li a0, 0x3F80
li a1, 0x0000
jal ra, bf16_div
lui t0, %hi(res_div2)
addi t0, t0, %lo(res_div2)
sw a0, 0(t0)
li a0, 0x0000
li a1, 0x0000
jal ra, bf16_div
lui t0, %hi(res_div3)
addi t0, t0, %lo(res_div3)
sw a0, 0(t0)
hang:
j hang
.globl bf16_isnan
bf16_isnan:
li t0, 0x7F80
and t1, a0, t0
bne t1, t0, L_isnan_false
li t0, 0x007F
and t2, a0, t0
sltu a0, x0, t2
jalr x0, ra, 0
L_isnan_false:
li a0, 0
jalr x0, ra, 0
.globl bf16_isinf
bf16_isinf:
li t0, 0x7F80
and t1, a0, t0
bne t1, t0, L_isinf_false
li t0, 0x007F
and t2, a0, t0
sltiu a0, t2, 1
jalr x0, ra, 0
L_isinf_false:
li a0, 0
jalr x0, ra, 0
.globl bf16_iszero
bf16_iszero:
li t0, 0x7FFF
and t1, a0, t0
sltiu a0, t1, 1
jalr x0, ra, 0
.globl bf16_add
bf16_add:
li t0, 0x8000
and t2, a0, t0
srli t2, t2, 15
li t0, 0x7F80
and t3, a0, t0
srli t3, t3, 7
li t0, 0x007F
and t4, a0, t0
li t6, 0x8000
and t5, a1, t6
srli t5, t5, 15
li t6, 0x7F80
and t0, a1, t6
srli t0, t0, 7
li t6, 0x007F
and t1, a1, t6
li t6, 0xFF
beq t3, t6, L_add_a_is_inf_or_nan
beq t0, t6, L_add_b_is_inf_or_nan
beq t3, x0, L_add_a_exp_zero_check
j L_add_a_maybe_norm
L_add_a_exp_zero_check:
beq t4, x0, L_add_ret_b
L_add_a_maybe_norm:
beq t0, x0, L_add_b_exp_zero_check
j L_add_b_maybe_norm
L_add_b_exp_zero_check:
beq t1, x0, L_add_ret_a
L_add_b_maybe_norm:
beq t3, x0, L_add_a_denorm
ori t4, t4, 0x80
j L_add_a_done_norm
L_add_a_denorm:
L_add_a_done_norm:
beq t0, x0, L_add_b_denorm
ori t1, t1, 0x80
j L_add_b_done_norm
L_add_b_denorm:
L_add_b_done_norm:
sub s0, t3, t0
addi s1, t3, 0
slt s2, x0, s0
bne s2, x0, L_add_a_bigger
slt s2, s0, x0
bne s2, x0, L_add_b_bigger
j L_add_same_exp
L_add_a_bigger:
li s2, 8
slt s3, s2, s0
bne s3, x0, L_add_ret_a
srl t1, t1, s0
j L_add_continue
L_add_b_bigger:
addi s1, t0, 0
sub s2, x0, s0
li s3, 8
slt s6, s3, s2
bne s6, x0, L_add_ret_b
srl t4, t4, s2
j L_add_continue
L_add_same_exp:
L_add_continue:
bne t2, t5, L_add_diff_sign
add s5, t4, t1
li s6, 0x100
and s6, s6, s5
beq s6, x0, L_add_same_pack
srli s5, s5, 1
addi s1, s1, 1
li s2, 0xFF
slt s3, s2, s1
bne s3, x0, L_add_ret_inf_same
L_add_same_pack:
andi s5, s5, 0x7F
slli s1, s1, 7
slli t2, t2, 15
or a0, t2, s1
or a0, a0, s5
jalr x0, ra, 0
L_add_diff_sign:
sltu s3, t1, t4
bne s3, x0, L_add_a_ge_b
addi s4, t5, 0
sub s5, t1, t4
j L_add_norm_after_sub
L_add_a_ge_b:
addi s4, t2, 0
sub s5, t4, t1
L_add_norm_after_sub:
beq s5, x0, L_add_ret_zero
L_add_norm_loop:
andi s6, s5, 0x80
bne s6, x0, L_add_pack_sub
slli s5, s5, 1
addi s1, s1, -1
slt s6, x0, s1
beq s6, x0, L_add_ret_zero
j L_add_norm_loop
L_add_pack_sub:
andi s5, s5, 0x7F
slli s1, s1, 7
slli s4, s4, 15
or a0, s4, s1
or a0, a0, s5
jalr x0, ra, 0
L_add_ret_a:
jalr x0, ra, 0
L_add_ret_b:
addi a0, a1, 0
jalr x0, ra, 0
L_add_ret_zero:
li a0, 0
jalr x0, ra, 0
L_add_ret_inf_same:
slli t2, t2, 15
li t0, 0x7F80
or a0, t2, t0
jalr x0, ra, 0
L_add_a_is_inf_or_nan:
li t0, 0x007F
and t1, a0, t0
bne t1, x0, L_add_ret_nan
li t0, 0x7F80
and t1, a1, t0
li t0, 0x7F80
bne t1, t0, L_add_return_a
li t0, 0x007F
and t1, a1, t0
bne t1, x0, L_add_return_a
li t0, 0x8000
and t1, a0, t0
and t2, a1, t0
beq t1, t2, L_add_return_a
L_add_ret_nan:
li a0, 0x7FC0
jalr x0, ra, 0
L_add_return_a:
addi a0, a0, 0
jalr x0, ra, 0
L_add_b_is_inf_or_nan:
li t0, 0x007F
and t1, a1, t0
bne t1, x0, L_add_return_b
addi a0, a1, 0
jalr x0, ra, 0
L_add_return_b:
addi a0, a1, 0
jalr x0, ra, 0
.globl bf16_sub
bf16_sub:
li t0, 0x8000
xor a1, a1, t0
j bf16_add
.globl bf16_mul
bf16_mul:
li t0, 0x8000
and t2, a0, t0
srli t2, t2, 15
and t3, a1, t0
srli t3, t3, 15
xor s4, t2, t3
li t0, 0x7F80
and t4, a0, t0
srli t4, t4, 7
and t5, a1, t0
srli t5, t5, 7
li t0, 0x007F
and s0, a0, t0
and s1, a1, t0
li t6, 0xFF
beq t4, t6, L_mul_a_inf_nan
beq t5, t6, L_mul_b_inf_nan
beq t4, x0, L_mul_check_a_denorm
j L_mul_a_norm
L_mul_check_a_denorm:
beq s0, x0, L_mul_ret_zero
L_mul_a_norm:
beq t5, x0, L_mul_check_b_denorm
j L_mul_b_norm
L_mul_check_b_denorm:
beq s1, x0, L_mul_ret_zero
L_mul_b_norm:
li s2, 0
beq t4, x0, L_mul_a_is_denorm
ori s0, s0, 0x80
j L_mul_a_ready
L_mul_a_is_denorm:
L_mul_a_den_loop:
andi t0, s0, 0x80
bne t0, x0, L_mul_a_set_exp
slli s0, s0, 1
addi s2, s2, -1
j L_mul_a_den_loop
L_mul_a_set_exp:
addi t4, x0, 1
L_mul_a_ready:
beq t5, x0, L_mul_b_is_denorm
ori s1, s1, 0x80
j L_mul_b_ready
L_mul_b_is_denorm:
L_mul_b_den_loop:
andi t0, s1, 0x80
bne t0, x0, L_mul_b_set_exp
slli s1, s1, 1
addi s2, s2, -1
j L_mul_b_den_loop
L_mul_b_set_exp:
addi t5, x0, 1
L_mul_b_ready:
li s5, 0
li t0, 16
L_mul_loop:
andi t1, s1, 1
beq t1, x0, L_mul_skip_add
add s5, s5, s0
L_mul_skip_add:
slli s0, s0, 1
srli s1, s1, 1
addi t0, t0, -1
bne t0, x0, L_mul_loop
add s6, t4, t5
addi s6, s6, -127
add s6, s6, s2
li t0, 0x8000
and t0, t0, s5
beq t0, x0, L_mul_shift7
srli s5, s5, 8
addi s6, s6, 1
j L_mul_pack
L_mul_shift7:
srli s5, s5, 7
L_mul_pack:
andi s5, s5, 0x7F
li t0, 0xFF
slt t1, t0, s6
bne t1, x0, L_mul_ret_inf
slt t1, x0, s6
beq t1, x0, L_mul_underflow
slli s6, s6, 7
slli s4, s4, 15
or a0, s4, s6
or a0, a0, s5
jalr x0, ra, 0
L_mul_underflow:
addi t0, x0, -6
slt t1, s6, t0
bne t1, x0, L_mul_ret_zero
sub t1, x0, s6
addi t1, t1, 1
srl s5, s5, t1
addi s6, x0, 0
slli s4, s4, 15
or a0, s4, s5
jalr x0, ra, 0
L_mul_ret_zero:
slli s4, s4, 15
addi a0, s4, 0
jalr x0, ra, 0
L_mul_ret_inf:
slli s4, s4, 15
li t0, 0x7F80
or a0, s4, t0
jalr x0, ra, 0
L_mul_a_inf_nan:
li t0, 0x007F
and t1, a0, t0
bne t1, x0, L_mul_return_a
beq t5, x0, L_mul_check_b_zero_for_nan
j L_mul_inf_return
L_mul_return_a:
addi a0, a0, 0
jalr x0, ra, 0
L_mul_check_b_zero_for_nan:
beq s1, x0, L_mul_ret_nan
L_mul_inf_return:
slli s4, s4, 15
li t0, 0x7F80
or a0, s4, t0
jalr x0, ra, 0
L_mul_ret_nan:
li a0, 0x7FC0
jalr x0, ra, 0
L_mul_b_inf_nan:
addi t0, a0, 0
addi a0, a1, 0
j L_mul_a_inf_nan
.globl bf16_div
bf16_div:
li t0, 0x8000
and t2, a0, t0
srli t2, t2, 15
and t3, a1, t0
srli t3, t3, 15
xor s4, t2, t3
li t0, 0x7F80
and t4, a0, t0
srli t4, t4, 7
and t5, a1, t0
srli t5, t5, 7
li t0, 0x007F
and s0, a0, t0
and s1, a1, t0
li t6, 0xFF
bne t5, t6, L_div_check_b_zero
li t0, 0x007F
and t1, a1, t0
beq t1, x0, L_div_b_is_inf_no_nan
addi a0, a1, 0
jalr x0, ra, 0
L_div_b_is_inf_no_nan:
bne t4, t6, L_div_ret_signed_zero
li t0, 0x007F
and t1, a0, t0
bne t1, x0, L_div_ret_signed_zero
li a0, 0x7FC0
jalr x0, ra, 0
L_div_check_b_zero:
bne t5, x0, L_div_check_a_inf
bne s1, x0, L_div_check_a_inf
bne t4, x0, L_div_div_by_zero_inf
bne s0, x0, L_div_div_by_zero_inf
li a0, 0x7FC0
jalr x0, ra, 0
L_div_div_by_zero_inf:
slli s4, s4, 15
li t0, 0x7F80
or a0, s4, t0
jalr x0, ra, 0
L_div_check_a_inf:
bne t4, t6, L_div_check_a_zero
li t0, 0x007F
and t1, a0, t0
beq t1, x0, L_div_ret_signed_inf
addi a0, a0, 0
jalr x0, ra, 0
L_div_ret_signed_inf:
slli s4, s4, 15
li t0, 0x7F80
or a0, s4, t0
jalr x0, ra, 0
L_div_check_a_zero:
bne t4, x0, L_div_norm_inputs
bne s0, x0, L_div_norm_inputs
L_div_ret_signed_zero:
slli s4, s4, 15
addi a0, s4, 0
jalr x0, ra, 0
L_div_norm_inputs:
beq t4, x0, L_div_after_norm_a
ori s0, s0, 0x80
L_div_after_norm_a:
beq t5, x0, L_div_after_norm_b
ori s1, s1, 0x80
L_div_after_norm_b:
slli s0, s0, 15
addi s5, x0, 0
addi s7, s1, 0
li t2, 0
L_div_loop:
slli s5, s5, 1
li t3, 15
sub t3, t3, t2
sll t1, s7, t3
blt s0, t1, L_div_skip_sub
sub s0, s0, t1
ori s5, s5, 1
L_div_skip_sub:
addi t2, t2, 1
li t3, 16
blt t2, t3, L_div_loop
add t4, t4, x0
sub t4, t4, t5
addi t4, t4, 127
L_div_apply_adj:
beq t4, x0, L_div_dec_exp
L_div_chk_eb:
beq t5, x0, L_div_inc_exp
j L_div_norm_q
L_div_dec_exp:
addi t4, t4, -1
j L_div_chk_eb
L_div_inc_exp:
addi t4, t4, 1
j L_div_norm_q
L_div_norm_q:
li t0, 0x8000
and t1, s5, t0
bne t1, x0, L_div_q_has_msb
L_div_q_loop:
and t1, s5, t0
bne t1, x0, L_div_q_shift_done
li t2, 1
slt t2, t2, t4
beq t2, x0, L_div_q_shift_done
slli s5, s5, 1
addi t4, t4, -1
j L_div_q_loop
L_div_q_shift_done:
srli s5, s5, 8
j L_div_pack
L_div_q_has_msb:
srli s5, s5, 8
L_div_pack:
andi s5, s5, 0x7F
li t0, 0xFF
slt t1, t0, t4
bne t1, x0, L_div_ret_inf
slt t1, x0, t4
beq t1, x0, L_div_ret_zero
slli t4, t4, 7
slli s4, s4, 15
or a0, s4, t4
or a0, a0, s5
jalr x0, ra, 0
L_div_ret_inf:
slli s4, s4, 15
li t0, 0x7F80
or a0, s4, t0
jalr x0, ra, 0
L_div_ret_zero:
slli s4, s4, 15
addi a0, s4, 0
jalr x0, ra, 0
.data
.align 2
res_add: .word 0
res_mul: .word 0
res_div1: .word 0
res_div2: .word 0
res_div3: .word 0
#
### Problem C — Test Case
| Operation | Input A | Input B | Expected bf16 | Expected Value | Notes |
|----------|----------|----------|----------------|----------------|--------|
| ADD | 0x3F80 (1.0) | 0x4000 (2.0) | 0x4040 | 3.0 | Basic addition |
| MUL | 0x3F80 (1.0) | 0x4000 (2.0) | 0x4000 | 2.0 | Multiplication |
| DIV | 0x4080 (4.0) | 0x4000 (2.0) | 0x4000 | 2.0 | Division |
| DIV | 0x3F80 (1.0) | 0x0000 (0.0) | 0x7F80 | +Inf | Divide by zero |
| DIV | 0x0000 (0.0) | 0x0000 (0.0) | 0x7FC0 | NaN | 0 ÷ 0 |

# Limitations
**題目 B**
1.目前測資結果僅存放於暫存器(如 t0~t2、s0~s2)中觀察,未設計記憶體供外部驗證
2.未對不合法或保留位元組型式進行檢查與例外處理
**題目 C**
1.無四捨五入模式支援:目前的 BF16 運算僅使用簡單截斷法,未實作 IEEE-754 標準中的「最接近偶數」
2.無測試所以例外:雖有處理 NaN、Inf、0 等特殊情況,但未設計 IEEE-754 的例外(「除以零」、「無效運算」)