---
# System prepended metadata

title: 'Assignment 1: RISC-V Assembly and Instruction Pipline '

---

---

title: 'Assignment 1: RISC-V Assembly and Instruction Pipline '

---

# Assignment 1: RISC-V Assembly and Instruction Pipline
*Source: [Quiz1 solution (HackMD)](https://hackmd.io/@sysprog/arch2025-quiz1-sol)*
## UF8
### Overview
- **uf8** encodes **20‑bit unsigned integers** (range **0–1,015,792**) into **8 bits** via **logarithmic quantization**.
- Target characteristics: about **2.5:1 compression** with **worst‑case relative error ≤ 6.25%** (typical ≈ **3%**).

### Decoding
Given an encoded byte **b**:
- **Formula:** $\ D(b) = m\cdot 2^{e} + (2^{e} - 1)\cdot 16$
- where $(e = \lfloor b/16 \rfloor), (m = b \bmod 16)$
### Encoding
For a value **v**:
- If **v < 16**, encode **exactly**: \(E(v) = v\).
- Otherwise,
  - Let $\text{offset}(e) = (2^{e} - 1)\cdot 16$
  - **Formula:** $E(v) = 16e + \left\lfloor \dfrac{v - \text{offset}(e)}{2^{e}} \right\rfloor$
### C implementation

::: spoiler code

``` c=
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

typedef uint8_t uf8;

static inline unsigned clz(uint32_t x)
{
    int n = 32, c = 16;
    do {
        uint32_t y = x >> c;
        if (y) {
            n -= c;
            x = y;
        }
        c >>= 1;
    } while (c);
    return n - x;
}

/* Decode uf8 to uint32_t */
uint32_t uf8_decode(uf8 fl)
{
    uint32_t mantissa = fl & 0x0f;
    uint8_t exponent = fl >> 4;
    uint32_t offset = (0x7FFF >> (15 - exponent)) << 4;
    return (mantissa << exponent) + offset;
}

/* Encode uint32_t to uf8 */
uf8 uf8_encode(uint32_t value)
{
    /* Use CLZ for fast exponent calculation */
    if (value < 16)
        return value;

    /* Find appropriate exponent using CLZ hint */
    int lz = clz(value);
    int msb = 31 - lz;

    /* Start from a good initial guess */
    uint8_t exponent = 0;
    uint32_t overflow = 0;

    if (msb >= 5) {
        /* Estimate exponent - the formula is empirical */
        exponent = msb - 4;
        if (exponent > 15)
            exponent = 15;

        /* Calculate overflow for estimated exponent */
        for (uint8_t e = 0; e < exponent; e++)
            overflow = (overflow << 1) + 16;

        /* Adjust if estimate was off */
        while (exponent > 0 && value < overflow) {
            overflow = (overflow - 16) >> 1;
            exponent--;
        }
    }

    /* Find exact exponent */
    while (exponent < 15) {
        uint32_t next_overflow = (overflow << 1) + 16;
        if (value < next_overflow)
            break;
        overflow = next_overflow;
        exponent++;
    }

    uint8_t mantissa = (value - overflow) >> exponent;
    return (exponent << 4) | mantissa;
}

/* Test encode/decode round-trip */
static bool test(void)
{
    int32_t previous_value = -1;
    bool passed = true;

    for (int i = 0; i < 256; i++) {
        uint8_t fl = i;
        int32_t value = uf8_decode(fl);
        uint8_t fl2 = uf8_encode(value);

        if (fl != fl2) {
            printf("%02x: produces value %d but encodes back to %02x\n", fl,
                   value, fl2);
            passed = false;
        }

        if (value <= previous_value) {
            printf("%02x: value %d <= previous_value %d\n", fl, value,
                   previous_value);
            passed = false;
        }

        previous_value = value;
    }

    return passed;
}

int main(void)
{
    if (test()) {
        printf("All tests passed.\n");
        return 0;
    }
    return 1;
}
```
:::


### RV32I implementation

#### CLZ

**AI Assistance notification**
>This RV32I implementation is assited by ChatGPT.
:::spoiler code


```c=
static inline unsigned clz(uint32_t x)
{
    unsigned n, c, y;
    asm volatile(
        "li      %0, 32           \n\t"  // n = 32
        "li      %2, 16           \n\t"  // c = 16
        "1:                       \n\t"
        "srl     %3, %1, %2       \n\t"  // y = x >> c
        "beqz    %3, 2f           \n\t"
        "sub     %0, %0, %2       \n\t"  // n -= c
        "mv      %1, %3           \n\t"  // x = y
        "2:                       \n\t"
        "srli    %2, %2, 1        \n\t"  // c >>= 1
        "bnez    %2, 1b           \n\t"
        "sub     %0, %0, %1       \n\t"  // n -= x (x is 0 or 1)
        : "=&r"(n), "+&r"(x), "=&r"(c), "=&r"(y)
        : 
        : "cc"
    );
    return n;
}
```
:::
This implementation follows identical CLZ behavior defined previously.
>In order to discover the improvemnt in either code size or run time w.r.t original C implementaion and guarantee fairness , this and following implementations of UF8 utilities will apply **asm volatile**.

#### uf8_decode
::: spoiler code
``` c=

static inline uint32_t uf8_decode(uf8 fl)
{
    uint32_t r, mant, exp, off;
    asm volatile(
        "andi   %1, %4, 0x0f      \n\t"  // mant = fl & 0x0f
        "srli   %2, %4, 4         \n\t"  // exp  = fl >> 4
        // off = ((1 << exp) - 1) << 4
        "li     %3, 1             \n\t"  // off = 1
        "sll    %3, %3, %2        \n\t"  // off <<= exp
        "addi   %3, %3, -1        \n\t"  // off = (1<<exp) - 1
        "slli   %3, %3, 4         \n\t"  // off <<= 4
        // r = (mant << exp) + off
        "sll    %0, %1, %2        \n\t"  // r = mant << exp
        "add    %0, %0, %3        \n\t"  // r += off
        : "=&r"(r), "=&r"(mant), "=&r"(exp), "=&r"(off)
        : "r"(fl)
        : "cc"
    );
    return r;
}

```
:::

#### uf8_encode

::: spoiler code
``` c=
static inline uf8 uf8_encode(uint32_t value)
{
    // --- preserved C part ---
    if (value < 16)
        return (uf8)value;

    int lz = clz(value);

    // --- assembly for the rest ---
    uint32_t out, e, ovf, msb, t0, t1;
    asm volatile(
        // msb = 31 - lz
        "li     %3, 31            \n\t"
        "sub    %3, %3, %7        \n\t"

        // e = 0; ovf = 0;
        "mv     %1, x0            \n\t"
        "mv     %2, x0            \n\t"

        // if (msb >= 5) { e = msb - 4; if (e > 15) e = 15; for(i=0;i<e;i++) ovf=(ovf<<1)+16; while(e>0 && value<ovf){ovf=(ovf-16)>>1; e--;}}
        "li     %4, 5             \n\t"
        "bltu   %3, %4, 1f        \n\t"   // if (msb < 5) skip block

        "addi   %1, %3, -4        \n\t"   // e = msb - 4
        "li     %4, 15            \n\t"
        "bltu   %4, %1, 2f        \n\t"   // if (15 < e) clamp
        "j      3f                \n\t"
        "2:                       \n\t"
        "mv     %1, %4            \n\t"   // e = 15
        "3:                       \n\t"
        // for (i=0; i<e; i++) ovf = (ovf<<1) + 16;
        "mv     %5, x0            \n\t"   // i = 0  (t1)
        "4:                       \n\t"
        "beq    %5, %1, 5f        \n\t"
        "slli   %2, %2, 1         \n\t"
        "addi   %2, %2, 16        \n\t"
        "addi   %5, %5, 1         \n\t"
        "j      4b                \n\t"
        "5:                       \n\t"
        // while (e > 0 && value < ovf) { ovf = (ovf - 16) >> 1; e--; }
        "beqz   %1, 1f            \n\t"
        "6:                       \n\t"
        "bltu   %6, %2, 7f        \n\t"   // if (value < ovf) adjust
        "j      1f                \n\t"
        "7:                       \n\t"
        "addi   %2, %2, -16       \n\t"
        "srli   %2, %2, 1         \n\t"
        "addi   %1, %1, -1        \n\t"
        "bnez   %1, 6b            \n\t"

        // } // end if (msb >= 5)
        "1:                       \n\t"

        // while (e < 15) { next=(ovf<<1)+16; if (value < next) break; ovf=next; e++; }
        "li     %4, 15            \n\t"
        "bgeu   %1, %4, 8f        \n\t"
        "9:                       \n\t"
        "slli   %5, %2, 1         \n\t"   // next in t1
        "addi   %5, %5, 16        \n\t"
        "bltu   %6, %5, 8f        \n\t"   // break if value < next
        "mv     %2, %5            \n\t"   // ovf = next
        "addi   %1, %1, 1         \n\t"   // e++
        "bltu   %1, %4, 9b        \n\t"
        "8:                       \n\t"

        // mant = (value - ovf) >> e; out = ((e<<4)|mant) & 0xFF
        "sub    %5, %6, %2        \n\t"
        "srl    %5, %5, %1        \n\t"
        "slli   %0, %1, 4         \n\t"
        "or    %0, %0, %5        \n\t"
        "andi   %0, %0, 0xff      \n\t"
        : "=&r"(out),   // %0
          "=&r"(e),     // %1
          "=&r"(ovf),   // %2
          "=&r"(msb),   // %3
          "=&r"(t0),    // %4
          "=&r"(t1)     // %5
        : "r"(value),   // %6
          "r"(lz)       // %7
        : "cc"
    );

    return (uf8)out;  // narrowed to 8 bits; ABI-safe
}
```

:::

#### Performance

>Note that test and main program are preserved as pure C code to guarantee fairness.

|baseline(Pure C)|C mix RV32I|
|--|--|
|![clz_baseline](https://hackmd.io/_uploads/rkqncUvybg.png)|![image](https://hackmd.io/_uploads/S1GJjUDk-g.png)|


## UF8 optimization

### CLZ
>Notification :
Since CLZ baseline (divide & conquer) already provided a acceptable preformance , hence this optimization didnt stick to RV32I ISA 
in order to dig full potential of 5 stage pipline CPU.

#### baseline
```
static inline unsigned clz(uint32_t x)
{
    int n = 32, c = 16;
    do {
        uint32_t y = x >> c;
        if (y) {
            n -= c;
            x = y;
        }
        c >>= 1;
    } while (c);
    return n - x;
}
```
This implementation conceptually similar to binary search.

There are 2 optimization scheme.

#### CLZ solution 1

solution 2 is based on **De Bruijn multiply (branchless, tiny table)**

implementation
``` c=
static inline unsigned clz(uint32_t x) {
    static const unsigned char lut[32] = {
        0, 9, 1,10,13,21, 2,29,11,14,16,18,22,25, 3,30,
        8,12,20,28,15,17,24, 7,19,27,23, 6,26, 5, 4,31
    };
    if (!x) return 32;
    uint32_t v = x;                     // saturate below MSB to 1s
    v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16;
    unsigned msb = lut[(v * 0x07C4ACDDu) >> 27];
    return 31u - msb;
}
```

:::spoiler RV32I
``` c=
static inline unsigned clz(uint32_t x) {
    static const unsigned char lut[32] = {
        0, 9, 1,10,13,21, 2,29,11,14,16,18,22,25, 3,30,
        8,12,20,28,15,17,24, 7,19,27,23, 6,26, 5, 4,31
    };

    unsigned res, v, tmp, prod, idx, msb;
    asm volatile(
        // if (!x) return 32;
        "beqz   %6, 0f            \n\t"

        // v = x;  // saturate below MSB to 1s
        "mv     %1, %6            \n\t"
        "srli   %2, %1, 1         \n\t" "or   %1, %1, %2 \n\t"
        "srli   %2, %1, 2         \n\t" "or   %1, %1, %2 \n\t"
        "srli   %2, %1, 4         \n\t" "or   %1, %1, %2 \n\t"
        "srli   %2, %1, 8         \n\t" "or   %1, %1, %2 \n\t"
        "srli   %2, %1, 16        \n\t" "or   %1, %1, %2 \n\t"

        // prod = v * 0x07C4ACDDu  (shift-add, modulo 2^32)
        // Bits set in 0x07C4ACDD: 0,2,3,4,6,7,10,11,13,15,18,22,23,24,25,26
        "mv     %3, %1            \n\t"   // prod = (v << 0)
        "slli   %2, %1, 2         \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 3         \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 4         \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 6         \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 7         \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 10        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 11        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 13        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 15        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 18        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 22        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 23        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 24        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 25        \n\t"   "add  %3, %3, %2 \n\t"
        "slli   %2, %1, 26        \n\t"   "add  %3, %3, %2 \n\t"

        // idx = (prod >> 27)
        "srli   %4, %3, 27        \n\t"

        // msb = lut[idx]
        "add    %5, %7, %4        \n\t"
        "lbu    %5, 0(%5)         \n\t"

        // res = 31 - msb
        "li     %0, 31            \n\t"
        "sub    %0, %0, %5        \n\t"
        "j      1f                \n\t"

        // x == 0 case
        "0:                       \n\t"
        "li     %0, 32            \n\t"

        "1:                       \n\t"
        : "=&r"(res),   // %0
          "=&r"(v),     // %1
          "=&r"(tmp),   // %2 (temp for shifts)
          "=&r"(prod),  // %3 (product)
          "=&r"(idx),   // %4
          "=&r"(msb)    // %5
        : "r"(x),       // %6
          "r"(lut)      // %7 (base ptr)
        : "cc", "memory"
    );

    return res;
}
```
:::

Multiply in RV32I version is resolved in **Shift and add** solution.

:::spoiler RV32I/M
```c=
#include <stdint.h>
// result type is unsigned (32-bit)

static inline unsigned clz(uint32_t x) {
    static const unsigned char lut[32] = {
        0, 9, 1,10,13,21, 2,29,11,14,16,18,22,25, 3,30,
        8,12,20,28,15,17,24, 7,19,27,23, 6,26, 5, 4,31
    };

    unsigned res, v, tmp, idx, msb;
    asm volatile(
        // if (!x) return 32;
        "beqz   %5, 0f            \n\t"

        // v = x; saturate below MSB to 1s:
        "mv     %1, %5            \n\t"
        "srli   %2, %1, 1         \n\t" "or %1, %1, %2  \n\t"
        "srli   %2, %1, 2         \n\t" "or %1, %1, %2  \n\t"
        "srli   %2, %1, 4         \n\t" "or %1, %1, %2  \n\t"
        "srli   %2, %1, 8         \n\t" "or %1, %1, %2  \n\t"
        "srli   %2, %1, 16        \n\t" "or %1, %1, %2  \n\t"

        // idx = (v * 0x07C4ACDDu) >> 27;   (needs M-extension)
        "li     %2, 0x07C4ACDD    \n\t"
        "mul    %3, %1, %2        \n\t"
        "srli   %3, %3, 27        \n\t"

        // msb = lut[idx];  (byte table)
        "add    %4, %6, %3        \n\t"
        "lbu    %4, 0(%4)         \n\t"

        // res = 31u - msb;
        "li     %0, 31            \n\t"
        "sub    %0, %0, %4        \n\t"
        "j      1f                \n\t"

        // x == 0 case
        "0:                       \n\t"
        "li     %0, 32            \n\t"

        "1:                       \n\t"
        : "=&r"(res),   // %0
          "=&r"(v),     // %1
          "=&r"(tmp),   // %2 (temps/const)
          "=&r"(idx),   // %3
          "=&r"(msb)    // %4
        : "r"(x),       // %5
          "r"(lut)      // %6 base pointer to LUT
        : "cc", "memory"
    );

    return res;
}
```
:::

Note that since we unlock RV32M , hence we use **mul** resolve mutiplication.


#### CLZ solution 2

solution 3 is based on GCC built-in utilities called **__builtin_clz**

however according to https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html

> Built-in Function: int __builtin_clz (unsigned int x)
Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.

hence we add a **branch** here to handle spcial case of x == 0.

implementation

``` c=
static inline __attribute__((always_inline))
unsigned clz(uint32_t x) {
    return x ? __builtin_clz(x) : 32u;   // UB-free
}
```



Evaluation

> note that UF8 encode/decode all implement in C mix RV32I scheme.
and evaluation and main function are preserved as pure C.

| baseline | De Bruijn RV32I |De Bruijn RV32I/M| GCC built-in|
|:--|:--|:--|:--|
|![baseline code mix](https://hackmd.io/_uploads/r1NGWwDJWe.png)|![bruijn code mix](https://hackmd.io/_uploads/r1QXbvwybe.png)|![Screenshot 2025-11-04 194812](https://hackmd.io/_uploads/HkA7_wPkWl.png)|![compiler code mix](https://hackmd.io/_uploads/rJpEbPDJZe.png)|

Its actually amusing to know that **De Brujin** solution with only RV32I instruction set falls behind basedline.
On the other hand as **RV32C** unlocked **De Brujin** solution outperforms baseline.
and without a doubt , **GCC built-in** solution has best performance.


## BF16

### Overview
**bfloat16** is a 16‑bit floating‑point format that keeps the **8‑bit exponent** of FP32 (same dynamic range) but uses a **7‑bit fraction** (+1 sign bit). It trades precision for speed and memory.

| Field | Bits | Description |
|---|---:|---|
| Sign | 1 | 0 = +, 1 = − |
| Exponent | 8 | Bias = **127** (same as FP32) |
| Fraction (mantissa) | 7 | No hidden bits stored beyond the implicit leading 1 for normals |

**AI Assistance notification**

> Partial work in this note was assisted by **ChatGPT** (OpenAI). The BF16 implementation benefited from design discussion and an evaluation-program scheme.

| Contributor | Contribution |
|---|---|
| Synte-Peng (author of this note) | Implement of BF16 helper & arithmetic functions from scratch |
| ChatGPT (OpenAI) | Designed & proposed evaluation program scheme |



### helper funcitons implementation



#### bf16_isnan
:::spoiler code
```  c=
#
#
#
#   input: a0 = x, return 1 if x is NaN else 0
#
#
bf16_isnan:            #leverage s0 , s1
    addi sp , sp , -32
    sw s0 , 4(sp)
    sw s1 , 8(sp)    
    
    lw s0 , BF16_EXP_MASK
    lw s1 , BF16_MANT_MASK
    
    and s1 , s1 , a0
    and a0 , a0 , s0
    sub a0 , a0 , s0
    
    beq a0 , zero , full_exp
    addi a0 , zero , 0
    cont_compute_nan:
    bne s1 , zero , nan_mant
    addi s1 , zero , 0
    cont_compute_nan1:
    and a0 , s1 , a0
    
    lw s0 , 4(sp)
    lw s1 , 8(sp)
    addi sp , sp , 32
    ret
    
full_exp:
    addi a0 , zero , 1
    beq zero , zero , cont_compute_nan
    
nan_mant:
    addi s1 , zero , 1
    beq zero , zero , cont_compute_nan1

```
:::
#### bf16_isinf
:::spoiler code
```c=
#
#
#
#   input: a0 = x, return 1 if x is inf else 0
#
#
bf16_isinf:            #leverage s0
    addi sp , sp , -8
    sw s0 , 4(sp)
    lw s0 , BF16_EXP_MASK
    slli a0 , a0 , 17
    srli a0 , a0 , 17
    beq a0 , s0 , isinf
    and a0 , a0 , zero
    cont_compute_inf:
    
    lw s0 , 4(sp)
    addi sp , sp , 8
    ret

isinf:
    ori a0 , zero ,1
    beq zero , zero , cont_compute_inf
```
:::
#### bf16_iszero
:::spoiler code
```c=
#
#
#
#   input: a0 = x, return 1 if x is zero else 0
#
#
bf16_iszero:
    slli a0 , a0 , 17
    srli a0 , a0 , 17
    sltu a0 , zero , a0
    xori a0 , a0 , 1
    ret
```
:::
#### f32_to_bf16
:::spoiler code
``` c=
#
#
# convert input a0 from f32 float represenataion into bf16
#
#
f32_to_bf16:            #leverage s0 , s1 , s2
    addi sp , sp , -16
    sw s0 , 12(sp)
    sw s1 , 8(sp)
    sw s2 , 4(sp)
    
    or s0 , a0 , zero
    srli s2 , s0 , 23
    andi s2 , s2 , 0xff
    ori s1 , zero , 0xff
    beq s2 , s1 , f32_to_bf16_is_inf_or_nan
    lui s1 , 0x7fff
    srli s1 , s1 , 12
    srli s0 , s0 , 16
    andi s0 , s0 , 1
    add s0 , s1 , s0
    add a0 , a0 , s0
    srli a0 , a0 , 16
    
    cont_convert:
    lw s0 , 12(sp)
    lw s1 , 8(sp)
    lw s2 , 4(sp)
    addi sp , sp , 16
    
    ret
    

f32_to_bf16_is_inf_or_nan:
    lui s0 , 0xffff
    srli s0 , s0 , 12
    srli a0 , a0 , 16
    and a0 , a0 , s0
    beq zero , zero , cont_convert
```
:::
#### bf16_to_f32
:::spoiler code
```c=
#
#
# convert input a0 from bf16 representation into f32 float
#
#
bf16_to_f32:
    slli a0 , a0 , 16
    ret

```
:::
### arithmetic utilities

#### bf16_add
:::spoiler code
```c=
#
#
# return a0 = a0 + a1 , which all repersented in bf16
#
#
bf16_add:                # s0 : input 1 , s1 : input 2 , s2 : hold 1
    addi sp , sp , -128
    sw ra , 4(sp)
    sw s0 , 8(sp)
    sw s1 , 12(sp)
    sw s2 , 16(sp)
    sw s3 , 20(sp)        # if logic handling / free variable
    
    or s0 , a0 , zero
    or s1 , a1 , zero
    addi s2 , zero , 1
    
    jal bf16_isnan
    beq a0 , s2 , add_a_nan
    or a0 , a1 , zero
    jal bf16_isnan
    beq a0 , s2 , add_a_nan
    
    or a0 , s0 , zero
    jal bf16_isinf
    beq a0 , s2 , add_a_inf # check if a is inf
    
    or a0 , s1 , zero # Place input:2 into a0
    
    jal bf16_isinf    
    beq a0 , s2 , add_b_inf # check if b is inf
    
    or a0 , s0 , zero
    jal bf16_iszero
    beq a0 , s2 , add_a_zero
    
    or s3 , s1 , zero # SWAP s0 , s1
    or s1 , s0 , zero
    or s0 , s3 , zero
    or a0 , s0 , zero
    jal bf16_iszero
    beq a0 , s2 , add_a_zero

    sw s4 , 24(sp) # a_sign
    sw s5 , 28(sp) # b_sign
    sw s6 , 32(sp) # a_exp
    sw s7 , 36(sp) # b_exp
    sw s8 , 40(sp) # a_mant
    sw s9 , 44(sp) # b_mant
    sw s10 , 48(sp) # free to use
    sw s11 , 52(sp) # free to use
    
    srli s4 , s0 , 15
    srli s5 , s1 , 15
    srli s6 , s0 , 7
    srli s7 , s1 , 7
    andi s6 , s6 , 0xff
    andi s7 , s7 , 0xff
    andi s8 , s0 , 0x7f
    andi s9 , s1 , 0x7f

    slt s3 , s2 , s6
    bne zero , s3 , add_a_normalized
    cont_add1:
    slt s3 , s2 , s7
    bne zero , s3 , add_b_normalized
    cont_add2:
    
    sw t0 , 56(sp) # exp_diff
    sw t1 , 60(sp) # result_sign
    sw t2 , 64(sp) # result_exp
    sw t3 , 68(sp) # result_mant
    
    sub t0 , s6 , s7
    bge t0 , zero , result_exp_is_a
    addi s10 , zero , -8
    bge s10 , t0 , b_too_large_add
    or t2 , s7 , zero
    xori s10 , t0 , -1
    addi s10 , s10 , 1
    srl s8 , s8 , s10
    cont_add3:
    beq s4 , s5 , add_same_sign_handling
    or s10 , zero , s9
    addi s10 , s10 , -1
    bge s8 , s10 , result_sign_is_a
    # result sign is b
    or t1 , zero , s5
    sub t3 , s9 , s8    
    cont_add5:
    beq t3 , zero , after_add_is_zero
    ori s11 , zero , 0x80
    final_add_normalization:
        and s3 , t3 , s11     
        beq s3 , s11, cont_add4
        slli t3 , t3 , 1
        addi t2 , t2 , -1
        bge zero , t2 , after_add_is_zero # equivalent to denormalized case
        beq zero , zero , final_add_normalization
    cont_add4: # final return stage
    slli t1 , t1 , 15
    slli t2 , t2 , 7
    andi t3 , t3 , 0x7f
    or a0 , t1 , zero
    or a0 , a0 , t2
    or a0 , a0 , t3
    
    complete:
    lw ra , 4(sp)
    lw s0 , 8(sp)
    lw s1 , 12(sp)
    lw s2 , 16(sp)
    lw s3 , 20(sp)
    lw s4 , 24(sp)
    lw s5 , 28(sp)
    lw s6 , 32(sp)
    lw s7 , 36(sp)
    lw s8 , 40(sp)
    lw s9 , 44(sp)
    lw s10 , 48(sp)
    lw s11 , 52(sp)
    lw t0 , 56(sp)
    lw t1 , 60(sp)
    lw t2 , 64(sp)
    lw t3 , 68(sp)
    addi sp , sp , 128
    ret      
    
add_a_nan:
    lw a0 , BF16_NAN
    lw ra , 4(sp)
    lw s0 , 8(sp)
    lw s1 , 12(sp)
    lw s2 , 16(sp)
    lw s3 , 20(sp)
    addi sp , sp , 128
    ret

add_a_inf:
    or a0 , s1 , zero
    jal bf16_isinf
    beq a0 , s2 , add_2_inf
    or a0 , s0 , zero
    
    beq zero , zero , complete
    
add_2_inf:
    addi s3 , zero , 0x80
    slli s3 , s3 , 8
    xor s1 , s1 , s3
    beq s0 , s1 , inf_minus_inf
    
    or a0 , zero , s0
    
    beq zero , zero , complete
    
inf_minus_inf:
    lw s3 , BF16_NAN
    or a0 , zero , s3
    
    beq zero , zero , complete

add_b_inf:
    or a0 , s1 , zero
    
    beq zero , zero , complete  
    
add_a_zero:
    or a0 , s1 , zero
    
    beq zero , zero , complete
    
add_a_normalized:
    ori s8 , s8 , 0x80
    beq zero , zero , cont_add1

add_b_normalized:
    ori s9 , s9 , 0x80
    beq zero , zero , cont_add2

result_exp_is_a:
    ori s10 , zero , 0x100
    bge t0 , s10 , a_too_large_add
    srl s9 , s9 , t0
    or t2 , zero , s6
    beq zero , zero , cont_add3

a_too_large_add:
    or a0 , s0 , zero
    
    beq zero , zero , complete  

b_too_large_add:
    or a0 , s1 , zero

    beq zero , zero , complete
          
add_same_sign_handling:
    or t1 , s4 , zero
    add t3 , s8 , s9
    andi s10 , t3 , 0x100
    srli s10, s10 , 8
    bne zero , s10 , normalize_after_add
    beq zero , zero , cont_add5
    
normalize_after_add:
    srli t3 , t3 , 1
    addi t2 , t2 , 1
    ori s10 , zero , 0xff
    bge t2 , s10 , add_to_inf
    beq zero , zero , cont_add5
    
add_to_inf:
    lui s10 , 0x7f80
    srli s10 , s10 , 12
    slli t1 , t1 , 15
    or a0 , t1 , s10

    beq zero , zero , complete

result_sign_is_a:
    or t1 , zero , s4
    sub t3 , s8 , s9
    beq zero , zero , cont_add5

after_add_is_zero:
    or a0 , zero , zero

    beq zero , zero , complete  
```
:::
#### bf16_sub
::: spoiler code
```c=
#
#
# leverage idea of a - b = a + (-b)
#
#
bf16_sub:
    addi sp , sp , -12
    sw ra , 8(sp)
    sw s0 , 4(sp)
    lw s0 , BF16_SIGN_MASK
    xor a1  , a1 , s0
    jal bf16_add
    ret
```
:::
#### bf16_mul
::: spoiler code
``` c=
#
#
# a0 = a0 * a1 which all in bf16 representation
# add-shift multiplication algorithm is applied
#
#
bf16_mul: # s0 : a , s1 : b , s2 : 1
    addi sp , sp , -128
    sw ra , 4(sp)
    sw s0 , 8(sp)
    sw s1 , 12(sp)
    sw s2 , 16(sp)
    sw s3 , 20(sp)        # if logic handling / free variable

    sw s4 , 24(sp) # a_sign
    sw s5 , 28(sp) # b_sign

        
    or s0 , a0 , zero
    or s1 , a1 , zero
    addi s2 , zero , 1
    srli s4 , s0 , 15
    srli s5 , s1 , 15
    xor s4 , s5 , s4 # s4 serve for result sign   , s5 for exp_adjust    
    
    jal bf16_isnan
    beq s2  , a0 , mul_a_nan
    or a0 , s0 , zero
    jal bf16_isinf
    beq s2 , a0 , mul_a_inf
    
    or a0 , s1 , zero
    jal bf16_isnan
    beq s2 , a0 , mul_b_nan
    or a0 , s1 , zero
    jal bf16_isinf
    beq s2 , a0 , mul_b_inf
    
    

    sw s6 , 32(sp) # a_exp
    sw s7 , 36(sp) # b_exp
    sw s8 , 40(sp) # a_mant
    sw s9 , 44(sp) # b_mant
    sw s10 , 48(sp) # free to use
    sw s11 , 52(sp) # free to use
    
    srli s4 , s0 , 15
    srli s5 , s1 , 15
    srli s6 , s0 , 7
    srli s7 , s1 , 7
    andi s6 , s6 , 0xff
    andi s7 , s7 , 0xff
    andi s8 , s0 , 0x7f
    andi s9 , s1 , 0x7f
    

    or s5 , zero , zero # s5 serve for exp adjust
    or s3 , zero , zero # if logic handling
    
    normalize_mant_a_mul:
    add s3 , s6 , zero
    slt s3 , s3 , s2
    ori s10 , zero , 0x7f
    beq zero , s3 , normalize_mant_b_mul
        normalizing_a_mul:
        # loop
        andi s3 , s8 , 0x80
        slt s3 , s3 , s10
        bne s2 , s3 , a_normalized_mul
        slli s8 , s8 , 1
        addi s5 ,s5 , -1
        beq zero , zero , normalizing_a_mul
        
    a_normalized_mul:
        addi s6 , zero , 1
        
        
    normalize_mant_b_mul:
    ori s8 , s8 , 0x80
    add s3 , s7 , zero
    slt s3 , s3 , s2
    beq zero , s3 , normalize_mant_b_complete_mul
        normalizing_b_mul:
        # loop
        andi s3 , s9 , 0x80
        slt s3 , s3 , s10
        bne s2 , s3 , b_normalized_mul
        slli s9 , s9 , 1
        addi s5 , s5 , -1
        beq zero , zero , normalizing_b_mul
        
        
    b_normalized_mul:
        addi s7 , zero , 1
    
    normalize_mant_b_complete_mul:
    ori s9 ,s9 , 0x80
    
    # multiplication loop
    ori s3 , zero , 8
    sw t0 , 56(sp) # serve for result mant
    sw t1 , 60(sp) # serve for result exp
    or t0 , zero , s9
    slli s8 , s8 , 8
    start_mant_mul:
        beq zero , s3 , mul_mant_complete
        addi s3 , s3 , -1
        andi s10 , t0 , 1
        bne s2 , s10 , partial_addition_finished
        add t0 , t0 ,s8
        
        partial_addition_finished:
        srli t0 , t0 , 1
        beq zero , zero , start_mant_mul
        
    mul_mant_complete:
    srli s8 , s8 , 16
    add t1 , s6 , s7
    addi t1 , t1 , -127
    add t1 , t1 , s5
    
    lui s10 , 0x00008
    and s10 , s10 , t0
    slt s3 , s10 , s2
    bne zero , s3 , no_adjustment_after_mant_mul
        srli t0 , t0 , 8
        andi t0 , t0 , 0x7f
        addi t1 ,t1 , 1
        beq zero , zero , exp_adjustment_complete
    
    no_adjustment_after_mant_mul:
        srli t0 , t0 ,7
        andi t0 , t0 , 0x7f
    
    exp_adjustment_complete:    
    # mul to inf checking
    addi s3 , zero , 0xfe
    bge t1 , s3 , mul_to_overflow
    bge s2 , t1 , check_mul_to_denormalized
    
    result_normalization_complete:
    or a0 , zero , t0
    andi t1 , t1 , 0xff
    slli t1 , t1 , 7
    or a0 , a0 , t1
    slli s4 , s4 , 15
    or a0 , a0 , s4
    
    mul_final:
    lw ra , 4(sp)
    lw s0 , 8(sp)
    lw s1 , 12(sp)
    lw s2 , 16(sp)
    lw s3 , 20(sp)
    lw s4 , 24(sp)
    lw s5 , 28(sp)
    lw s6 , 32(sp)
    lw s7 , 36(sp)
    lw s8 , 40(sp)
    lw s9 , 44(sp)
    lw s10 , 48(sp)
    lw s11 , 52(sp)
    
    lw t0 , 56(sp)
    lw t1 , 60(sp)
    
    addi sp , sp , 128
    ret      
    
mul_a_nan:
    or a0 , s0 , zero
    beq zero , zero , mul_final    

mul_a_inf:
    slli a0  ,s1 , 17
    srli a0  ,a0 , 17
    beq zero , a0 , mul_a_to_nan
    
    or a0 , s4 , zero
    slli a0 , a0 , 15
    or a0 , s0 , zero
    beq zero , zero , mul_final
    
mul_a_to_nan:
    lw a0 , BF16_NAN
    beq zero , zero, mul_final

mul_b_nan:
    or a0 , s1 , zero
    beq zero , zero , mul_final
    
mul_b_inf:
    slli a0 , s0 , 17
    srli a0 , a0 , 17
    beq  zero , a0 , mul_a_to_nan
    
    or a0 , s4 , zero
    slli a0 , a0 , 15
    or a0 , s1 , zero
    beq zero , zero , mul_final

mul_to_overflow:
    lw a0 , BF16_EXP_MASK
    slli s4 , s4 , 15
    or a0 , a0  ,s4
    beq zero , zero , mul_final

check_mul_to_denormalized:
    addi s3 , zero , -6
    bge s3 , t1 , mul_to_denormalized
    sub s2 , s2 , t1
    sll t0 , t0 , s2
    or t0 , zero , zero
    beq zero , zero , result_normalization_complete
    
mul_to_denormalized:
    or a0 , zero , s4
    slli a0 , a0 , 15
    beq zero , zero , mul_final
```
:::
#### bf16_div
:::spoiler code
```c=
#
#
# a0 = a0 / a1 , which all in bf16 representation
# sub-shift division algorithm is applied
#
#
bf16_div:
    addi sp , sp , -128
    sw ra , 4(sp)
    sw s0 , 8(sp)
    sw s1 , 12(sp)
    sw s2 , 16(sp)
    sw s3 , 20(sp)        # if logic handling / free variable

    or s0 , zero , a0
    or s1 , zero , a1

    sw s4 , 24(sp) # a_sign
    sw s5 , 28(sp) # b_sign

    srli s4 , s0 , 15
    srli s5 , s1 , 15    
    xor s4 , s5 , s4 # s4 serve for result sign   , s5 for exp_adjust
        
    or s0 , a0 , zero
    or s1 , a1 , zero
    addi s2 , zero , 1
    
    jal bf16_isnan
    beq a0 , s2 , div_to_nan
    
    or a0 , s1 , zero
    jal bf16_isnan
    beq a0 , s2 , div_to_nan
    
    or a0 , s1 , zero
    jal bf16_iszero
    beq a0 , s2 , div_b_zero

    or a0 , s1 , zero
    jal bf16_isinf
    beq a0 , s2 , div_b_inf
    
    or a0 , s0 , zero
    jal bf16_isinf
    beq a0 , s2 , div_to_inf
    
    or a0 , s0 , zero
    jal bf16_iszero
    beq a0 , s2 , div_to_zero
    
    
    sw s6 , 32(sp) # a_exp
    sw s7 , 36(sp) # b_exp
    sw s8 , 40(sp) # a_mant
    sw s9 , 44(sp) # b_mant
    sw s10 , 48(sp) # free to use
    sw s11 , 52(sp) # free to use
    
    srli s6 , s0 , 7
    srli s7 , s1 , 7
    andi s6 , s6 , 0xff
    andi s7 , s7 , 0xff
    andi s8 , s0 , 0x7f
    andi s9 , s1 , 0x7f
    
    beq s6 , zero , a_mant_normalized
    ori s8 , s8 , 0x80
    
    a_mant_normalized:
    beq s7 , zero , b_mant_normalized
    ori s9 , s9 , 0x80
    
    b_mant_normalized:
    sw t0 , 56(sp) # serve for dividend
    sw t1 , 60(sp) # serve for divisor
    sw t2 , 64(sp) # serve for quotient
    sw t3 , 68(sp) # free to use
    
    slli t0 , s8 , 15
    or t1 , s9 , zero
    or t2 , zero , zero
    
    addi s10 , zero , 16
    start_division:
        beq s10 , zero , division_complete
        slli t2 ,t2 ,1
        addi s10 , s10 , -1
        sll s11 , t1 , s10
        sub t3 , s11 , t0
        slt s3 , zero , t3
        bne zero , s3 , no_substraction
        sub t0 , t0 , s11
        ori t2 , t2 , 1
        no_substraction:
        beq zero , zero , start_division
        
    division_complete:
    sub t1 , s6 , s7
    addi t1 , t1 , 127 # t1 serve for result exp
        
        
    bne zero , s6 , div_1_finished
    addi t1 , t1 , -1
    div_1_finished:
    bne zero , s7 , div_by_1_finished
    addi  t1 , t1 , 1
    div_by_1_finished:
    lui s10 , 0x00008
    and s3 , s10 , t2
    bne zero , s3 , quotient_normalized
    quotient_normalize_start:
        and s3 , s10 , t2
        slt s3 , s3 ,  s2
        slt s11 , s2 , t1
        and s3 , s3 , s2
        beq zero , s3 , quotient_normalized
        slli t2 , t2 ,1
        addi t1 , t1 , -1
        beq zero , zero , quotient_normalize_start

    quotient_normalize_complete:
    andi t2 , t2 , 0x7f
    
    ori s3 , zero , 0xff
    bge t1 , s3 , div_to_inf
    bge zero , t1 , div_to_zero
    
    slli a0 , s4 , 15
    andi t1 , t1 , 0xff
    slli t1 , t1 , 7
    or a0 , a0 , t1
    andi t2 , t2 , 0x7f
    or a0 , a0 , t2

    div_final:
    lw ra , 4(sp)
    lw s0 , 8(sp)
    lw s1 , 12(sp)
    lw s2 , 16(sp)
    lw s3 , 20(sp)
    lw s4 , 24(sp)
    lw s5 , 28(sp)
    lw s6 , 32(sp)
    lw s7 , 36(sp)
    lw s8 , 40(sp)
    lw s9 , 44(sp)
    lw s10 , 48(sp)
    lw s11 , 52(sp)    
    lw t0 , 56(sp)
    lw t1 , 60(sp)
    lw t2 , 64(sp)
    lw t3 , 68(sp)
    addi sp , sp , 128
    ret      

div_to_nan:
    lw a0 , BF16_NAN
    beq zero , zero , div_final
    
div_b_zero:
    or a0 , s0 , zero
    jal bf16_iszero
    beq a0 , s2 , div_to_nan
    div_to_inf:
    or a0 , s4 , zero
    slli a0 , a0 , 15
    lw s3 , BF16_EXP_MASK
    or a0 , a0 , s3
    beq zero , zero , div_final

div_b_inf:
    or a0 , s0 , zero
    jal bf16_isinf
    beq s2 , a0 , div_to_nan
    div_to_zero:
    or a0 , zero , zero
    beq zero , zero , div_final

quotient_normalized:
    srli t2 , t2 , 8
    beq zero , zero quotient_normalize_complete
```
:::
#### bf16_sqrt
:::spoiler code
```c=
#
#
# a0 = a0 ^ 0.5
# binary search for root square is applied.
#
#
bf16_sqrt:
    
    addi sp , sp , -128
    sw ra , 4(sp)
    sw s0 , 8(sp)
    sw s1 , 12(sp)
    sw s2 , 16(sp)
    sw s3 , 20(sp)
    sw s4 , 24(sp)
    sw s5 , 28(sp)

    
    or s0 , a0 , zero        # s0 reserve a0
    srli s1 , a0 , 15        # s1 reserve sign
    andi s1 , s1 , 1
    srli s2 , s0 , 7         # s2 reserve exp
    andi s2 , s2 , 0xff      
    andi s3 , a0 , 0x7f      # s3 reserve mant

    addi s4 , zero , 1       # s4 hold 1
    jal bf16_iszero
    beq s4 , a0 , sqrt_zero # handling input zero
    bne zero , s1 , sqrt_nan # negative input
    ori s5 , zero , 0xff     # s5 : free to use
    
    beq s2 , s5 , sqrt_special_case # hndling inf or nan
    beq zero , s2 , sqrt_zero # handling denormalized
    
    addi s2 , s2 , -127
    ori s3 , s3 , 0x80
    
    andi s5 , s2 , 1
    beq zero , s5 , sqrt_exp_complete
    slli s3 , s3 , 1
    addi s2 , s2 , -1
    
    sqrt_exp_complete:
    srai s2 , s2 , 1
    addi s2 , s2 , 127        
    
    sw s6 , 32(sp) # low
    sw s7 , 36(sp) # high
    sw s8 , 40(sp) # mid
    sw s9 , 44(sp) # sq
    sw s10, 48(sp) # result
    sw s11, 52(sp) # free to use
    sw t0 , 56(sp) # free to use
        
    addi s6 , zero , 90
    addi s7 , zero , 256
    addi s10 , zero , 128
    
    start_binary_search_for_root:
        blt s7 , s6 , search_finished
        add s8 , s6 , s7
        srli s8 , s8 , 1
        
        # prepare for unsigned multiplication
        addi s5 , zero , 9   #serve for specicial case of 256 as mid = 0x100000000
        or s11 , s8 , zero
        slli s8 , s8 , 9
        start_18bit_mul:     # 18bit multiplier
            beq s5 , zero , bit16_mul_completed
            addi s5 , s5 , -1
            andi t0 , s11 , 1
            beq zero , t0 , partial_add_completed
            add s11 , s11 , s8
            partial_add_completed:
            srli s11 , s11 , 1
            beq zero , zero , start_18bit_mul
    
        bit16_mul_completed:
            srli s8 , s8 , 9
            srli s9 , s11 , 7
            bge s3 , s9 , update_result
            addi s7 , s8 , -1
            beq zero , zero , start_binary_search_for_root
            
        update_result:
            or s10 , zero , s8
            addi s6 , s8 , 1
            beq zero , zero , start_binary_search_for_root
    search_finished:
    ori s5 , zero , 256
    blt s10 , s5 , sqrt_even_exp
    srli s10 , s10 , 1
    addi s2 , s2 ,1
    beq zero , zero , normalize_sqrt_result_complete
    
    sqrt_even_exp:
        ori s5 , zero  , 128
        bge s5 , s10 , normalize_sqrt_result_complete
        slt s11 , s10 , s5
        slt t0 , s4 , s2
        and s11 , s11 , t0
        bne s4 , s11 , normalize_sqrt_result_complete
            slli s10 , s10 , 1
            addi s2 , s2 , -1
    
    normalize_sqrt_result_complete:
        
    bge zero , s2 , sqrt_zero
    andi s10 , s10 , 0x7f
    or a0 , zero , s10
    andi s2 , s2 , 0xff
    slli s2 , s2 , 7
    or a0 , a0 , s2
    
    sqrt_complete:
    lw ra , 4(sp)
    lw s0 , 8(sp)
    lw s1 , 12(sp)
    lw s2 , 16(sp)
    lw s3 , 20(sp)
    lw s4 , 24(sp)
    lw s5 , 28(sp)
    lw s6 , 32(sp)
    lw s7 , 36(sp)
    lw s8 , 40(sp)
    lw s9 , 44(sp)
    lw s10, 48(sp)
    lw s11, 52(sp)
    lw t0 , 56(sp)
    addi sp , sp , 128
    ret
    
    
sqrt_special_case: #included with negative input  , inf
    or a0 , s0 , zero
    beq zero , zero , sqrt_complete
    
sqrt_nan:
    lw a0 , BF16_NAN
    beq zero , zero , sqrt_complete
    
sqrt_zero:
    lw a0 , BF16_ZERO
    beq zero , zero , sqrt_complete

```
:::
## BF16 Evaluation

**AI Assistance notification**

> Partial work in this note was assisted by **ChatGPT** (OpenAI).

### Overview
A tiny, fixed-size test harness for BF16 primitives (Ripes-friendly).  
It validates:
- **Arithmetic**: `bf16_add`, `bf16_sub`, `bf16_mul`, `bf16_div` on **3 fixed cases**
- **Sqrt + Conversion**: `bf16_sqrt` on 3 inputs and **BF16 round-trip**  
  `f32_to_bf16(bf16_to_f32(x_bf16)) == x_bf16`

---

### Test Inputs (Arithmetic)
**3 (a,b) pairs** used for all four arithmetic ops:

| Case | A (bf16) | B (bf16) | Notes |
|---:|:---:|:---:|---|
| 10 | `0x7F7F` | `0x7F7F` | max-finite vs max-finite |
| 15 | `0xBF2A` | `0xC1BB` | negatives |
| 16 | `0xC283` | `0xBF2A` | negatives |

#### Expected Results (Arithmetic)
| Case | ADD | SUB | MUL | DIV |
|---:|:---:|:---:|:---:|:---:|
| 10 | `0x7F80` | `0x0000` | `0x7F80` | `0x3F80` |
| 15 | `0xC1C0` | `0x41B6` | `0x4178` | `0x3CE8` |
| 16 | `0xC284` | `0xC282` | `0x422D` | `0x42C5` |

---

### SQRT & BF16 Roundtrip
**Inputs (bf16):** `0x1234`, `0x5678`, `0x789A`

**Expected `bf16_sqrt` outputs:** `0x28D6`, `0x4AFC`, `0x5C0C`

**Round-trip property (bf16 only):**
f32_to_bf16( bf16_to_f32(x_bf16) ) == x_bf16



This guarantees the conversion pair is consistent for BF16-representable values.

*(Note: the F32→BF16→F32 roundtrip check was removed by design.)*

---

### Data Layout (word-aligned, fixed-size)
- input
    - `TEST_A[3]` — bf16 words: `0x7F7F, 0xBF2A, 0xC283`
    - `TEST_B[3]` — bf16 words: `0x7F7F, 0xC1BB, 0xBF2A`
- corresponding output
    - `EXP_ADD[3]` — bf16 words: `0x7F80, 0xC1C0, 0xC284`
    - `EXP_SUB[3]` — bf16 words: `0x0000, 0x41B6, 0xC282`
    - `EXP_MUL[3]` — bf16 words: `0x7F80, 0x4178, 0x422D`
    - `EXP_DIV[3]` — bf16 words: `0x3F80, 0x3CE8, 0x42C5`
    - `SQRT_IN[3]` — bf16 words: `0x1234, 0x5678, 0x789A`
    - `EXP_SQRT[3]` — bf16 words: `0x28D6, 0x4AFC, 0x5C0C`

---

### Execution Flow
#### `test_arith()`
For each `i in {0,1,2}`:
1. Load `a = TEST_A[i]`, `b = TEST_B[i]`.
2. Compute `add/sub/mul/div` via `bf16_*` routines.
3. Compare each result to `EXP_*[i]` via `exp_cmp16`.
4. Return **1** if all 12 checks pass; otherwise **0**.

#### `test_sqrt_and_convert()`
For each `i in {0,1,2}`:
1. Compute `bf16_sqrt(SQRT_IN[i])` and compare to `EXP_SQRT[i]`.
2. Check **BF16 roundtrip** `f32_to_bf16(bf16_to_f32(SQRT_IN[i])) == SQRT_IN[i]`.
3. Return **1** if all checks pass; otherwise **0**.

#### `main`
- Runs both routines and ANDs the results.
- Ripes syscalls print exactly one line:
  - `"All tests passed.\n"` when all checks pass.
  - `"Some tests failed.\n"` otherwise.
- Exits with code **0** on pass, **1** on fail.

---

### Comparator (bit-exact for bf16)
`exp_cmp16(result, expected)` uses the zero-test idiom:
```asm
sub  t0, a0, a1
sltiu a0, t0, 1   # a0 = 1 if equal, else 0
```

### evaluation program implementation
:::spoiler code
```c=
# ===== 3 arithmetic test pairs: CASE10, CASE15, CASE16 =====
TEST_A:
    .word 0x7F7F     # CASE1-1
    .word 0xBF2A     # CASE1-2
    .word 0xC283     # CASE1-3
TEST_B:
    .word 0x7F7F     # CASE1-1
    .word 0xC1BB     # CASE1-2
    .word 0xBF2A     # CASE1-3

# ===== Expected results (exact bit patterns) =====
# ADD
EXP_ADD:
    .word 0x7F80     # CASE1-1
    .word 0xC1C0     # CASE1-2
    .word 0xC284     # CASE1-3
# SUB
EXP_SUB:
    .word 0x0000     # CASE1-1
    .word 0x41B6     # CASE1-2
    .word 0xC282     # CASE1-3
# MUL
EXP_MUL:
    .word 0x7F80     # CASE1-1
    .word 0x4178     # CASE1-2
    .word 0x422D     # CASE1-3
# DIV
EXP_DIV:
    .word 0x3F80     # CASE1-1
    .word 0x3CE8     # CASE1-2
    .word 0x42C5     # CASE1-3

# ===== 3 sqrt inputs (bf16) and expected sqrt(bf16) =====
SQRT_IN:
    .word 0x1234, 0x5678, 0x789A
EXP_SQRT:
    .word 0x28D6, 0x4AFC, 0x5C0C

# ===== Messages =====
MSG_ALL:   .asciz "All tests passed.\n"
MSG_FAILS: .asciz "Some tests failed.\n"

.text
.globl main
# -----------------------------------------------------------------------------
# main: run both groups and print one line
# -----------------------------------------------------------------------------
main:
    jal  ra, test_arith
    mv   s0, a0
    jal  ra, test_sqrt_and_convert
    and  s0, s0, a0

    bnez s0, AllPass
    la   a0, MSG_FAILS 
    li a7, 4 
    ecall
    li   a7, 10
    li a0, 1
    ecall

AllPass:
    la   a0, MSG_ALL
    li a7, 4
    ecall
    li   a7, 10
    li a0, 0
    ecall

# -----------------------------------------------------------------------------
# exp_cmp16(a0=result16, a1=expected16) -> a0 = 1 pass, 0 fail
# bit-exact compare (finite, ±Inf, subnormals, ±0 with sign)
# -----------------------------------------------------------------------------
exp_cmp16:
    sub  t0, a0, a1
    sltiu a0, t0, 1
    ret

# -----------------------------------------------------------------------------
# exp_cmp32(a0=result32, a1=expected32) -> a0 = 1 pass, 0 fail
# bit-exact compare for f32 (raw bits in integer reg)
# -----------------------------------------------------------------------------
exp_cmp32:
    sub  t0, a0, a1
    sltiu a0, t0, 1
    ret

# -----------------------------------------------------------------------------
# test_arith() -> a0 = 1 if all ADD/SUB/MUL/DIV pass for 3 pairs, else 0
# -----------------------------------------------------------------------------
    .globl test_arith
test_arith:
    addi sp, sp, -4
    sw   ra, 0(sp)

    la   s0, TEST_A
    la   s1, TEST_B
    la   s2, EXP_ADD
    la   s3, EXP_SUB
    la   s4, EXP_MUL
    la   s5, EXP_DIV

    li   s6, 1        # all_pass = 1
    li   t6, 0        # i = 0

L_arith_loop:
    li   t5, 3
    beq  t6, t5, L_arith_done

    # load a,b
    lw   a0, 0(s0)
    lw   a1, 0(s1)

    # --- ADD ---
    jal  ra, bf16_add
    lw   a1, 0(s2)
    jal  ra, exp_cmp16
    bnez a0, 1f
    li   s6, 0
1:
    # --- SUB ---
    lw   a0, 0(s0)
    lw   a1, 0(s1)
    jal  ra, bf16_sub
    lw   a1, 0(s3)
    jal  ra, exp_cmp16
    bnez a0, 2f
    li   s6, 0
2:
    # --- MUL ---
    lw   a0, 0(s0)
    lw   a1, 0(s1)
    jal  ra, bf16_mul
    lw   a1, 0(s4)
    jal  ra, exp_cmp16
    bnez a0, 3f
    li   s6, 0
3:
    # --- DIV ---
    lw   a0, 0(s0)
    lw   a1, 0(s1)
    jal  ra, bf16_div
    lw   a1, 0(s5)
    jal  ra, exp_cmp16
    bnez a0, 4f
    li   s6, 0
4:
    # advance to next case
    addi s0, s0, 4
    addi s1, s1, 4
    addi s2, s2, 4
    addi s3, s3, 4
    addi s4, s4, 4
    addi s5, s5, 4
    addi t6, t6, 1
    j    L_arith_loop

L_arith_done:
    mv   a0, s6
    lw   ra, 0(sp)
    addi sp, sp, 4
    ret

# -----------------------------------------------------------------------------
# test_sqrt_and_convert() -> a0 = 1 if all pass, else 0
#  For each x in SQRT_IN:
#   (1) bf16_sqrt(x) == EXP_SQRT[i]             (bf16 compare)
#   (2) f32_to_bf16(bf16_to_f32(x)) == x        (bf16 round-trip)
# -----------------------------------------------------------------------------
    .globl test_sqrt_and_convert
test_sqrt_and_convert:
    addi sp, sp, -8
    sw   ra, 4(sp)
    sw   s0, 0(sp)

    la   s0, SQRT_IN
    la   s1, EXP_SQRT

    li   s2, 1          # all_pass = 1
    li   t6, 0

L_sc_loop:
    li   t5, 3
    beq  t6, t5, L_sc_done

    # ---- SQRT check ----
    lw   a0, 0(s0)            # x_bf16
    jal  ra, bf16_sqrt        # -> a0 = sqrt_bf16
    lw   a1, 0(s1)            # expected sqrt
    jal  ra, exp_cmp16
    bnez a0, 10f
    li   s2, 0
10:
    # ---- BF16 roundtrip: f32_to_bf16(bf16_to_f32(x)) == x ----
    lw   a0, 0(s0)            # x_bf16
    jal  ra, bf16_to_f32      # -> x_f32_bits
    jal  ra, f32_to_bf16      # -> back_bf16
    lw   a1, 0(s0)            # original x_bf16
    jal  ra, exp_cmp16
    bnez a0, 11f
    li   s2, 0
11:
    # next
    addi s0, s0, 4
    addi s1, s1, 4
    addi t6, t6, 1
    j    L_sc_loop

L_sc_done:
    mv   a0, s2
    lw   s0, 0(sp)
    lw   ra, 4(sp)
    addi sp, sp, 8
    ret
```
:::
### Evaluation result
![image](https://hackmd.io/_uploads/S14G84VJ-g.png)

### Preformance evaluation
![image](https://hackmd.io/_uploads/SymHYVV1Zg.png)

## Assignment use case : CLZ

[Leetcode 201. Bitwise AND of Numbers Range](https://leetcode.com/problems/bitwise-and-of-numbers-range/)

|Baseline|__builtin_clz|
|--|--|
|![image](https://hackmd.io/_uploads/B12P_2tkZx.png)|![Screenshot 2025-11-06 135212](https://hackmd.io/_uploads/Sydmu2t1bl.png)|

Since we already examined performance of **__builtin_clz** previosly, 
hence we adopt this soltion to evaluate performace improvement in either runtime or memory.