[toc] # Assignment1: RISC-V Assembly and Instructon Pipeline contributed by [< jningmin >](https://github.com/jningmin/2025_computer_architecture) ## [Quiz 1](https://hackmd.io/@sysprog/arch2025-quiz1-sol?stext=6284%3A2518%3A0%3A1759746369%3AseANJk) problem B <h3>Implementation_Uf8 and C code </h3> In this problem, we are asked to implement and test a simplified floating-point encoding scheme called UF8 (Unsigned Float 8-bit). The goal is to convert between a 32-bit unsigned integer (uint32_t) and a compact 8-bit representation (uf8), while preserving approximate numeric relationships. C code: https://hackmd.io/@sysprog/arch2025-quiz1-sol?stext=6284%3A2518%3A0%3A1759746369%3AseANJk <h3>Assembly code</h3> My test cases are :` A : 15, B : 125, C : 225.` Below are displaying main functions, if you want to see the full vertion of code, please scoll to the botom. :::info #### Step-by-step Explanation: ::: :::info 1.The program calls `Decode`, which converts the UF8 value (in register a1) into a decoded integer representation returned in a0. 2.After restoring the stack, the decoded integer result is stored in temporary register t0. 3.The result is printed as the decoded output using the system call (ecall) for string and integer printing. 4.The `Encode` subroutine is called to re-encode the integer value back into UF8 format. 5.After encoding, the program compares: Whether the re-encoded value matches the original input (bne t0, a1, error), and Whether the encoded value maintains strict monotonicity (blt t0, t2, error). 6.If both conditions are satisfied, the encoded value is printed in hexadecimal form by calling print_hex. The routine restores the stack pointer and returns control to the main program. ::: **Decoding** >In the decoding process, >→mantissa is obtained by masking the lower four bits of the input. >→exponent is extracted through a right-shift operation. The exponent is then converted to its two’s complement form to prepare for offset adjustment. An offset value is generated by shifting a constant (0x7FFF) proportionally to the adjusted exponent, effectively aligning the decoded number within the representable dynamic range. The mantissa is subsequently scaled by left-shifting it according to the exponent, and the final decoded integer is produced by adding this scaled mantissa to the computed offset, as expressed by $\text{value = ( mantissa ≪ exponent ) + offset}$ ```s Decode: andi t0,a1,0x0f ### store mantissa in t0 srli t1,a1,4 ### store expo in t1 not t2,t1 addi t2,t2,1 ### two's complement of expo addi t2,t2,15 li t3,0x7fff srl t3,t3,t2 slli t3,t3,4 ### store offset in t3 sll t4,t0,t1 add a0,t4,t3 ### store trans_num in a2 xor t0,t0,t0 xor t3,t3,t3 ret ``` **Encoding** >The encoding routine uses a logarithmic quantization approach similar to floating-point representation. It first estimates the exponent by locating the position of the most significant bit (MSB), found through a Count-Leading-Zero (CLZ) operation. This exponent effectively determines the dynamic range in which the integer lies. >Once the exponent is estimated, the algorithm constructs an overflow threshold that represents the smallest value encodable with that exponent. This threshold is iteratively adjusted until it properly bounds the input value. The mantissa is then derived by measuring how far the value exceeds this overflow boundary, scaled by the exponent’s power of two. >Mathematically, the overflow is built as an accumulated sum of power-of-two increments:$$ \text{overflow} = \sum_{i=0}^{e-1} 2^i \times 16 $$ Finally, the mantissa and exponent are combined into a single 8-bit quantity:$$ \text{UF8 = ( exponent ≪ 4 ) ∣ mantissa} $$ ```s Encode: addi sp, sp, -16 sw ra, 12(sp) # store add s4,a0,x0 ### s4=value li t0,16 bltu a0,t0,no_need_more_op ### a0=value jal ra,clz mv t1,a0 ### lz = clz(value) li t2,31 sub s1,t2,t1 ### store msb in s1 li s2,0 ### store exponent in s2 li s3,0 ### store overflow in s3 li t6,5 bltu s1,t6,find_exact_exponent addi s2,s1,-4 li t6,15 bltu s2,t6, Calculate_overflow ### t6=1 if greater than 15 li s2,15 Calculate_overflow: ###for loop li s8,0 ###counting loop1: beq s8,s2,Adjust_if_estimate slli s3,s3,1 addi s3,s3,16 addi s8,s8,1 j loop1 Adjust_if_estimate: bgtz s2,check1 j find_exact_exponent check1: bltu s4,s3,check2 j find_exact_exponent check2: addi,s3,s3,-16 srli s3,s3,1 addi s2,s2,-1 j Adjust_if_estimate find_exact_exponent: li s7,20 bge s2, s7, end_encode li t6,15 bgtu s2,t6,end_encode slli s5,s3,1 ### 7f0 should be in s3 overflow addi s5,s5,16 ### next_overflow store in s5 bltu s4,s5,end_encode mv s3,s5 addi s2,s2,1 j find_exact_exponent end_encode: sub s6,s4,s3 srl s6,s6,s2 ### store other mantissa in s6 slli t6,s2,4 add a0,s6,t6 lw ra, 12(sp) addi sp, sp, 16 ret ``` **Counting leading zeros** >Initialize n = 32 and c = 16. Iteratively shift the input right by c bits and check whether the result is non-zero. If non-zero, decrement n by c and update x with the shifted value. Halve c each iteration to perform a binary search over bit positions. Once c reaches zero, compute the final count as n - x, yielding the total number of leading zeros. Return this count in a0. ```s clz: li t0,32 ### n=32 li t1,16 ### c=16 mv t3,a0 loop0: beqz t1,return0 srl t2,t3,t1 ### y = x >> c beqz t2,devide_c sub t0,t0,t1 mv t3,t2 j loop0 devide_c: srli t1,t1,1 j loop0 return0: sub t0,t0,t3 mv a0,t0 ret no_need_more_op: lw ra, 12(sp) # restore ra stored at start of Encode addi sp, sp, 16 # restore stack ret ``` To display the floating point value in human-readable hexadecimal form without using hardware floating-point instructions or registers. >print_hex divides the 8-bit number into two 4-bit nibbles: The high nibble` (a0 >> 4)` The low nibble `(a0 & 0xF)` Each nibble is passed to the print_nibble routine for ASCII conversion: Values 0–9 are mapped to characters '0'–'9'. Values 10–15 are mapped to 'a'–'f'. System call ecall with code `11` is used to print the` ASCII` character to the console. ```s print_hex: addi sp, sp, -16 sw ra, 8(sp) # store srli t0, a0, 4 # high nibble andi t1, a0, 0xF # low nibble la a0, msg6 li a7, 4 ecall mv a0, t0 jal ra, print_nibble mv a0, t1 jal ra, print_nibble lw ra,8(sp) addi sp,sp,16 ret print_nibble: li t2,10 bltu a0, t2, digit addi a0, a0, 87 j out digit: addi a0, a0, 48 out: li a7, 11 # print_char ecall ret ``` :::info Result display on comsole: ::: ![image](https://hackmd.io/_uploads/SkgbMZzaxl.png =250x) After all these stage are done, the register is updated like this: ![image](https://hackmd.io/_uploads/SkPa9GZpxx.png =250x)![image](https://hackmd.io/_uploads/SkKliGZ6eg.png =250x) :::info Memory viewer: ::: Below denotes the data section of memory. ![image](https://hackmd.io/_uploads/rJONszWpxx.png) :::info The clock cycles of program: ::: ![image](https://hackmd.io/_uploads/SJUz7efagx.png) ## [Quiz 1](https://hackmd.io/@sysprog/arch2025-quiz1-sol?stext=6284%3A2518%3A0%3A1759746369%3AseANJk) problem C <h3>Implementation_bfloat16 and C code </h3> In this project, we implemented and verified a simplified 16-bit floating-point format called Bfloat16 (BF16) entirely in RISC-V assembly language. This project demonstrates the ability to simulate IEEE-like floating-point behavior using only integer and bitwise instructions. C code: https://hackmd.io/@sysprog/arch2025-quiz1-sol?stext=10087%3A7233%3A0%3A1759746474%3Ap5GiEp https://hackmd.io/@sysprog/arch2025-quiz1-sol?stext=18828%3A2804%3A0%3A1759746490%3ABFJGmJ <h3>Assembly code</h3> Below are some main operating functions, if you want to see the full vertion of the assembly code , please scoll down to the bottom. :::info Six main test functions → test basic conversions → test special values → test arithmetic → test comparisons → test edge cases → test rounding ::: **test_basic_conversions()** >**f32_to_bf16**: This function converts a 32-bit IEEE single-precision float (f32) into a 16-bit bfloat16 (bf16) representation. It first extracts the exponent and mantissa, then handles special cases such as NaN and infinity. The conversion keeps the sign and exponent bits while truncating the lower 16 bits of the mantissa. A simple rounding is applied by checking the most significant discarded bit (bit 16). >**bf16_to_f32**: This reverses the previous operation. It converts a 16-bit bfloat16 number back into 32-bit float representation by shifting left 16 bits. The lower bits of the mantissa are filled with zeros. ```s f32_to_bf16: mv t1,a0 # ((f32bits >> 23) & 0xFF) srli t2, t1, 23 andi t2, t2, 0xFF #store expo in t2 li t3, 0xFF beq t2,t3,is_nan_inf srli t4,t1,16 andi t4,t4,1 li t3,0x7FFF add t4,t4,t3 add t1,t1,t4 srli t2,t1,16 mv a0,t2 ret bf16_to_f32: slli t3,a0,16 mv a0,t3 ret ``` **test_special_values()** >This test routine verifies that special BF16 values (Inf, -Inf, NaN, +0, -0) are correctly detected. It calls helper functions like` bf16_isinf`,` bf16_isnan`, `bf16_iszero`, and `bf16_is_neg_zero`. Each case prints a message if it fails the expected check. Essentially, it ensures the system correctly interprets bit patterns for special cases. ```s test_special_values: la a0, msg_start_special li a7, 4 ecall li a0, 0x7F80 #Test +Inf jal ra, bf16_isinf beqz a0, fail_posinf li a0, 0x7F80 jal ra, bf16_isnan bnez a0, fail_inf_nan li a0, 0xFF80 #Test -Inf jal ra, bf16_isinf beqz a0, fail_neginf li a0, 0x7FC0 #Test NaN jal ra, bf16_isnan beqz a0, fail_nan li a0, 0x7FC0 jal ra, bf16_isinf bnez a0, fail_nan_inf li a0, 0x0000 # Test +0 jal ra, bf16_iszero beqz a0, fail_zero li a0, 0x8000 # Test -0 jal ra, bf16_is_neg_zero beqz a0, fail_negzero la a0, Special_msg li a7, 4 ecall ``` **test_arithmetic()** >**Addication :** `bf16_add`:Main function for arithmetic operation, use in addicaion substraction,multiplication. This function performs addition (or subtraction) between two BF16 numbers. It follows IEEE-style floating-point addition logic: 1.Extract signs, exponents, and mantissas. 2.Align the smaller exponent by shifting its mantissa. 3.Depending on signs: →If same → add mantissas, normalize and adjust exponent. →If different → subtract mantissas, normalize result toward zero. 4.Detect overflow (→ set to Inf) and underflow (→ set to 0). 5.Reconstruct the final 16-bit BF16 `result: (sign << 15) | (exp << 7) | mantissa`. ```s bf16_add: addi sp,sp,-8 sw ra,4(sp) li t5,0xFF li t6,0x7F srli t0, a0, 15 # sign_a in t0 andi t0, t0, 1 srli t1, a1, 15 # sign_b in t1 andi t1, t1, 1 srli t2, a0, 7 # exp_a in t2 and t2, t2, t5 srli t3, a1, 7 # exp_b in t3 and t3, t3, t5 and t4, a0, t6 # mant_a in t4 and t5, a1, t6 # mant_b in t5 #s6 = result_sign, s7 = result_expo, s8 = mantissa # if a is zero (exp_a==0 && mant_a==0) => return b beqz a0,return_b beqz a1,return_a beqz t2, skip_a_norm ori t4, t4, 0x80 # mant_a |= 0x80 skip_a_norm: beqz t3, skip_b_norm ori t5, t5, 0x80 # mant_b |= 0x80 skip_b_norm: sub s9, t2, t3 # exp_diff = exp_a - exp_b bgtz s9, exp_a_bigger bltz s9, exp_b_bigger j exp_equal exp_a_bigger: mv s7, t2 # result_exp = exp_a li t6, 8 bgt s9, t6, return_a srl t5, t5, s9 # mant_b >>= exp_diff j continue_add exp_b_bigger: neg s9, s9 # exp_diff = -exp_diff mv s7, t3 # result_exp = exp_b li t6, -8 blt s9, t6, return_b srl t4, t4, s9 # mant_a >>= -exp_diff j continue_add exp_equal: mv s7, t2 continue_add: beq t0, t1, same_sign j diff_sign same_sign: mv s6, t0 # result_sign = sign_a add s8, t4, t5 li t6, 0x100 and s9, s8, t6 beqz s9, normalize_end srli s8, s8, 1 # => mantissa >> 1 addi s7, s7, 1 # exponent++ li t6, 0xFF blt s7, t6, normalize_end slli t6, s6, 15 li s9, 0x7F80 or t6, t6, s9 mv a0, t6 j add_exit diff_sign: bgeu t4, t5, mant_a_ge mv s6, t1 # result_sign = sign_b sub s8, t5, t4 # result_mant = mant_b - mant_a j normalize_check mant_a_ge: mv s6, t0 sub s8, t4, t5 normalize_check: beqz s8, return_zero normalize_loop: li t6, 0x80 and s9, s8, t6 bnez s9, normalize_end slli s8, s8, 1 addi s7, s7, -1 # exponent-- blez s7, return_zero j normalize_loop normalize_end: slli t6, s6, 15 # (sign << 15) andi s9, s7, 0xFF slli s9, s9, 7 # (exp << 7) or t6, t6, s9 andi s9, s8, 0x7F # mantissa (7 bits) or t6, t6, s9 mv a0, t6 j add_exit return_a: mv a0, a0 j add_exit return_b: mv a0, a1 j add_exit return_zero: li a0, 0 j add_exit add_exit: lw ra, 4(sp) addi sp, sp, 8 ret ``` >**Subtraction :** Subtraction is implemented by flipping the sign bit of the second operand (XOR with 0x8000) and then calling bf16_add. This reuses the same addition logic since subtraction is just addition with a negated operand. ```s bf16_sub: addi sp, sp, 4 sw ra, 0(sp) li t0, 0x8000 xor a1, a1, t0 jal ra,bf16_add lw ra,0(sp) addi sp,sp,4 ret ``` >**Multiplication :** 1.Extract sign, exponent, and mantissa from both operands. 2.Result sign = XOR(sign_a, sign_b). 3.Result `exponent = exp_a + exp_b − bias (127)`. 4.Multiply mantissas (8-bit normalized, producing up to 16 bits). 5.Normalize result mantissa (shift if overflow/underflow). 6.Handle special cases: -Overflow → return `±Inf`. -Underflow → return `0`. -Otherwise → combine sign, exponent, mantissa to form BF16 result. ```s bf16_mul: addi sp, sp, -4 sw ra, 0(sp) # constants li t6, 0xFF li s4, 0x7F li s5, 127 # BF16_EXP_BIAS beqz a0,return_zero beqz a1,return_zero # extract sign bits srli t0, a0, 15 andi t0, t0, 1 # sign_a (t0) srli t1, a1, 15 andi t1, t1, 1 # sign_b(t1) # extract exponents (8 bits) srli t2, a0, 7 and t2, t2, t6 # exp_a (t2) srli t3, a1, 7 and t3, t3, t6 # exp_b (t3) # extract mantissas (7 bits) and t4, a0, s4 # mant_a (t4) and t5, a1, s4 # mant_b (t5) # result sign = sign_a ^ sign_b (s7) xor s7, t0, t1 # result expo (s8) # result mant (s9) # exp_adjust = 0 li s6, 0 # normalize mant_a beqz t2, denorm_a norm_a: ori t4, t4, 0x80 j mant_a_done denorm_a: beqz t4, mant_a_done denorm_a_loop: andi t0, t4, 0x80 bnez t0, mant_a_done slli t4, t4, 1 addi s6, s6, -1 j denorm_a_loop mant_a_done: # normalize mant_b beqz t3, denorm_b norm_b: ori t5, t5, 0x80 j mant_b_done denorm_b: beqz t5, mant_b_done denorm_b_loop: andi t0, t5, 0x80 bnez t0, mant_b_done slli t5, t5, 1 addi s6, s6, -1 j denorm_b_loop mant_b_done: # mantissa multiply (8x8 = 16-bit) mul s9, t4, t5 # result_exp = exp_a + exp_b - bias + exp_adjust add s8, t2, t3 add s8, s8, s6 addi s8, s8, -127 # normalize mantissa li t0, 0x8000 and t1, s9, t0 bnez t1, shift8 # no overflow: shift right 7 bits srli s9, s9, 7 andi s9, s9, 0x7F j norm_done shift8: srli s9, s9, 8 andi s9, s9, 0x7F addi s8, s8, 1 norm_done: # overflow check li t0, 0xFF bge s8, t0, set_inf # underflow check blez s8, underflow # ===== normal result ===== slli t0, s7, 15 andi t1, s8, 0xFF slli t1, t1, 7 or t0, t0, t1 andi t1, s9, 0x7F or a0, t0, t1 j mul_done # underflow case underflow: li t0, -6 blt s8, t0, return_zero_mul li t1, 1 sub t1, t1, s8 srl s9, s9, t1 li s8, 0 slli t0, s7, 15 andi t1, s8, 0xFF slli t1, t1, 7 or t0, t0, t1 andi t1, s9, 0x7F or a0, t0, t1 j mul_done # overflow (Inf) set_inf: slli a0, s7, 15 li t6,0x7F80 or a0, a0, t6 j mul_done # zero result return_zero_mul: slli a0, s7, 15 # finish mul_done: lw ra, 0(sp) addi sp, sp, 4 ret ``` >**Divedend :** 1.Result `sign = XOR(sign_a, sign_b)`. 2.Result `exponent = exp_a − exp_b + bias`. 3.Mantissa division (approximated with integer division). 4.Normalize result if `mantissa >= 0x100`. 5.Recombine bits into final BF16 format. It’s a simplified model of floating-point division — precision loss is expected. ```s bf16_div: li t6, 0xFF li s4, 0x7F li s5, 127 # BF16_EXP_BIAS # extract sign bits srli t0, a0, 15 andi t0, t0, 1 # sign_a srli t1, a1, 15 andi t1, t1, 1 # sign_b # extract exponents srli t2, a0, 7 and t2, t2, t6 # exp_a srli t3, a1, 7 and t3, t3, t6 # exp_b # extract mantissas and t4, a0, s4 # mant_a and t5, a1, s4 # mant_b # result sign = sign_a ^ sign_b xor s7, t0, t1 # add hidden 1-bit ori t4, t4, 0x80 # mant_a |= 0x80 ori t5, t5, 0x80 # mant_b |= 0x80 # result_exp = exp_a - exp_b + bias sub s8, t2, t3 add s8, s8, s5 # s8 = exp_a - exp_b + 127 # mantissa division (approx) slli s9, t4, 7 # mant_a << 7 divu s9, s9, t5 # result_mant = mant_a / mant_b # normalization (if mantissa >= 0x100) li t0, 0x100 and t1, s9, t0 beqz t1, skip_norm srli s9, s9, 1 addi s8, s8, 1 skip_norm: # pack result bits slli t0, s7, 15 # sign << 15 slli t1, s8, 7 # exp << 7 or t0, t0, t1 and s9, s9, s4 # mant & 0x7F or a0, t0, s9 ret ``` >**Square root :** 1.Extract exponent and mantissa. 2.Adjust exponent: -If exponent is odd → shift mantissa left and reduce exponent by 1. -Then halve exponent (since $\sqrt{2^e} = 2^{\frac{e}{2}}$). 3.Perform binary search between 90–256 to find mantissa whose square best fits the original value. 4.Normalize and reassemble the BF16 result. This mimics the floating-point sqrt hardware behavior using integer approximation. ```s bf16_sqrt: li s5, 127 # BF16_EXP_BIAS li t6, 0xFF li s4, 0x7F # exponent and mantissa srli t0, a0, 7 and t0, t0, t6 # exp and t1, a0, s4 # mant # e = exp - bias addi t2, t0, -127 # e = exp - 127 li t3, 1 and t4, t2, t3 # t4 = e & 1 ori t5, t1, 0x80 # m = 0x80 | mant beqz t4, sqrt_even_exp slli t5, t5, 1 # m <<= 1 addi t2, t2, -1 sqrt_even_exp: srai t6, t2, 1 add t6, t6, s5 # new_exp = (e>>1)+bias #binary search li s0, 90 # low li s1, 256 # high li s2, 128 # result sqrt_loop: bgt s0, s1, sqrt_done add s3, s0, s1 srli s3, s3, 1 # mid = (low + high) >> 1 mul s4, s3, s3 srli s4, s4, 7 # sq = (mid*mid)/128 ble s4, t5, sqrt_le addi s1, s3, -1 # high = mid - 1 j sqrt_loop sqrt_le: mv s2, s3 # result = mid addi s0, s3, 1 # low = mid + 1 j sqrt_loop sqrt_done: li t0, 256 blt s2, t0, sqrt_check_low srli s2, s2, 1 addi t6, t6, 1 # new_exp++ j sqrt_pack sqrt_check_low: li t1, 128 bge s2, t1, sqrt_pack sqrt_shift_up: blt t6, zero, sqrt_pack slli s2, s2, 1 addi t6, t6, -1 blt s2, t1, sqrt_shift_up sqrt_pack: andi s2, s2, 0x7F # new_mant = result & 0x7F slli t6, t6, 7 or a0, t6, s2 ret ``` **test_comparisons()** >`bf16_eq` : Checks equality. Returns false if either operand is `NaN`. Treats `+0` and `-0` as `equal`. `bf16_lt` : Returns true if a < b, considering sign bits and magnitude. `bf16_gt` : Simply calls bf16_lt(b, a) (reversed operands). ```s bf16_eq: addi sp, sp, -16 sw ra, 8(sp) mv t0, a0 #store a in t0 jal ra, bf16_isnan bnez a0, eq_false # b NaN or not mv t1, a1 #store b in t1 mv a0, t1 jal ra, bf16_isnan bnez a0, eq_false # both zero or not mv a0, t0 # a0 = a jal ra, bf16_iszero mv t1, a0 # t1 = iszero(a) mv a0, a1 jal ra, bf16_iszero and t2, t1, a0 bnez t2, eq_true # bit equality beq t0, a1, eq_true eq_false: li a0, 0 j eq_exit eq_true: li a0, 1 eq_exit: lw ra, 8(sp) addi sp, sp, 16 ret bf16_lt: addi sp, sp, -16 sw ra, 12(sp) mv t0, a0 jal ra, bf16_isnan bnez a0, lt_false mv a0, a1 jal ra, bf16_isnan bnez a0, lt_false # check zero mv a0, t0 jal ra, bf16_iszero mv t1, a0 mv a0, a1 jal ra, bf16_iszero and t2, t1, a0 bnez t2, lt_false # sign_a = (a >> 15) & 1 srli t3, t0, 15 andi t3, t3, 1 # sign_b = (b >> 15) & 1 srli t4, a1, 15 andi t4, t4, 1 # sign_a != sign_b ? bne t3, t4, sign_diff # same sign beqz t3, both_pos # if sign = 0 , positive j both_neg both_pos: blt t0, a1, lt_true # both pos compare with numbers j lt_false both_neg: bgt t0, a1, lt_true j lt_false sign_diff: # sign_a > sign_b ? (1 > 0 ?? neg < pos) bgt t3, t4, lt_true j lt_false lt_true: li a0, 1 j lt_exit lt_false: li a0, 0 lt_exit: lw ra, 12(sp) addi sp, sp, 16 ret bf16_gt: addi sp, sp, -16 sw ra, 4(sp) mv t0, a0 # store a mv a0, a1 # bf16_lt(b, a) mv a1, t0 jal ra, bf16_lt lw ra, 4(sp) addi sp, sp, 16 ret ``` **test_edge_cases();** >`Tiny values` — Check if underflowed to zero properly. `Overflow` — Multiply large values to ensure result saturates to ±Inf. `Underflow` — Divide very small by large number, expect zero. It validates BF16 arithmetic correctness near numerical boundaries. ```s # Test 1: Tiny value handling la a0, msg_testing_edges li a7, 4 ecall li a0, 0x00000001 # tiny 1e-45f jal ra, f32_to_bf16 # -> a0 = bf_tiny(bits) mv s0, a0 jal ra, bf16_to_f32 # -> a0 = tiny_val(bits) mv s1, a0 # bf16_iszero(bf_tiny)? mv a0, s0 jal ra, bf16_iszero bnez a0, test1_pass # abs(tiny_val) li t3, 0x7FFFFFFF and t4, s1, t3 # load threshold (1e-37) li t5, 0x0C2CF59E # 1e-37f # compare abs(tiny_val) < threshold ? bltu t4, t5, test1_pass # fail la a0, msg_fail_tiny li a7, 4 ecall li a0, 1 j test_edge_finish test1_pass: # Test 2: Overflow → Inf li a0, 0x7E967699 # 1e38f jal ra, f32_to_bf16 mv s2, a0 # s2 = bf_huge li a0, 0x41200000 # 10.0f jal ra, f32_to_bf16 mv s3, a0 # s3 = bf10 mv a0, s2 mv a1, s3 jal ra, bf16_mul mv s2, a0 # s2 = bf_huge2 jal ra, bf16_isinf beqz a0, fail_huge j test2_pass fail_huge: la a0, msg_fail_huge li a7, 4 ecall li a0, 1 j test_edge_finish test2_pass: # Test 3: Underflow li a0, 0x007CE666 # 1e-38f jal ra, f32_to_bf16 mv s0, a0 # s0 = bf_small li a0, 0x501502F9 # 1e10f jal ra, f32_to_bf16 mv s1, a0 # s1 = bf_1e10 mv a0, s0 mv a1, s1 jal ra, bf16_div mv s2, a0 # s2 = smaller mv a0, s2 jal ra, bf16_to_f32 mv t4, a0 # t4 = smaller_val f32 bits jal ra, bf16_iszero bnez a0, test3_pass li t3, 0x7FFFFFFF and t4, t4, t3 # clear sign li t6, 0x00000001 # 1e-45f f32 bits bltu t4, t6, test3_pass la a0, msg_fail_underflow li a7, 4 ecall li a0, 1 j test_edge_finish test3_pass: la a0, Edge_msg li a7, 4 ecall li a0, 0 test_edge_finish: ``` **test_rounding();** >1.Checks that 1.5f is exactly representable. 2.Verifies that converting 1.0001f to BF16 and back yields minimal rounding error (< 0.001). Ensures conversion maintains acceptable accuracy within expected precision. ```s la a0, msg_rounding li a7, 4 ecall li a0, 0x3FC00000 # 1.5f jal ra, f32_to_bf16 mv s0, a0 # s0 = bf_exact # back_exact = bf16_to_f32(bf_exact) jal ra, bf16_to_f32 mv t0, a0 # t0 = back_exact f32 bits # check exact representation preserved li t1, 0x3FC00000 # 1.5f bits bne t0, t1, rounding_fail pass_test_rounding_1: li a0, 0x3F800066 # 1.0001f bits jal ra, f32_to_bf16 mv s1, a0 # s1 = bf jal ra, bf16_to_f32 mv t2, a0 # t2 = back f32 bits # diff2 = back - val li t3, 0x3F800066 # val bits sub t4, t3, t2 # t4 = diff2 bits # 取絕對值 li t5, 0x7FFFFFFF and t4, t4, t5 # check rounding error < 0.001 li t6, 0x3A83126F # 0.001f bits bltu t4, t6, rounding_pass rounding_fail: la a0, msg_fail_rounding li a7, 4 ecall li a0, 1 j test_rounding_end rounding_pass: la a0, Rounding_msg li a7, 4 ecall test_rounding_end: j end ``` >A helper that computes the absolute value of a signed integer and checks if it’s less than 10. Used as a verification step for correctness tests (e.g., absolute error check). ```s abs: bltz s3, abs_neg j abs_positive abs_neg: neg s3, s3 # s3 = -s3 abs_positive: li t1, 10 blt s3, t1, abs_pass li a0, 1 # fail ret abs_pass: li a0, 0 # pass ret ``` >These are exception detection helpers that check BF16 bit patterns: `bf16_isinf`: exponent = 0xFF, mantissa = 0 → `Inf` `bf16_isnan`: exponent = 0xFF, mantissa ≠ 0 → `NaN` `bf16_iszero`: exponent = 0, mantissa = 0 → `Zero` `bf16_is_neg_zero`: Zero with sign bit = 1 Used throughout all test functions to handle edge and invalid cases correctly. ```s bf16_isinf: mv t1,a0 li t2,0x7F80 and t3,t1,t2 bne t3,t2, not_inf li t2,0x007F and t4,t1,t2 bnez t4, not_inf li a0,1 ret not_inf: li a0,0 ret is_nan_inf: srli t4,t2,16 li t3 0xff and t4,t4,t3 mv a0,t4 ret bf16_isnan: mv t1,a0 li t2,0x7F80 and t3,t1,t2 bne t3,t2, not_nan li t2,0x007F and t4,t1,t2 beqz t4, not_nan li a0,1 ret not_nan: li a0,0 ret bf16_iszero: mv t1,a0 li t2,0x7FFF and t1,t1,t2 beqz t1, is_zero li a0,0 ret is_zero: li a0,1 ret bf16_is_neg_zero: li t1,0x8000 beq a0,t1,is_negzero li a0,0 ret is_negzero: li a0,1 ret ``` To display the floating point value in human-readable hexadecimal form without using hardware floating-point instructions or registers. ```s print_hex: mv t0, a0 # val la a0,hex li a7,4 ecall mv t1, a1 # digits la t2, hexchars # hex print_hex_loop: beqz t1, print_hex_done addi t1, t1, -1 slli t3, t1, 2 srl t4, t0, t3 # val >> (4*pos) andi t4, t4, 0xF add t4, t2, t4 lbu a0, 0(t4) li a7, 11 # print_char ecall j print_hex_loop print_hex_done: li a0, 10 # newline li a7, 11 ecall ret fail: la a0, fail_msg li a7, 4 ecall li a0, 1 # return 1 if fail ret end: la a0, all_test_pass_msg li a7, 4 ecall li a7,10 ecall ``` :::info Results on console ::: ![image](https://hackmd.io/_uploads/rJN9oROaxx.png) :::info The clock cycles of program: ::: ![image](https://hackmd.io/_uploads/B1yhjROael.png) :::info Memory viewer: ::: ![image](https://hackmd.io/_uploads/H15JhCO6gx.png) ## Leetcode ## [leetcode_70_climbing_stairs](https://leetcode.com/problems/climbing-stairs/description/) You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? <h3>Implementation</h3> Formally, it solves the recurrence: ${f(n)=f(n-1)+f(n-2)}$ which is similar to the Fibonacci sequence in concept. >Example A: Input: n = 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps >Example B: Input: n = 3 Output: 3 Explanation: There are three ways to climb to the top. 1. 1 step + 1 step + 1 step 2. 1 step + 2 steps 3. 2 steps + 1 step <h4> C code </h4> ```c #include <assert.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define VECTOR_MIN_SIZE 16 typedef struct { void **data; size_t size; /* Allocated size */ size_t count; /* Number of elements */ size_t free_slot; /* Index of a known hole */ } vector_t; /* -------- Vector 基本功能 -------- */ void vector_init(vector_t *v) { v->data = NULL; v->size = 0; v->count = 0; v->free_slot = 0; } int32_t vector_push(vector_t *v, void *ptr) { if (!v->size) { v->size = VECTOR_MIN_SIZE; v->data = calloc(v->size, sizeof(void *)); } if (v->free_slot && v->free_slot < v->count) { size_t idx = v->free_slot; v->data[idx] = ptr; v->free_slot = 0; return idx; } if (v->count == v->size) { v->size *= 2; v->data = realloc(v->data, v->size * sizeof(void *)); memset(v->data + v->count, 0, (v->size - v->count) * sizeof(void *)); } v->data[v->count] = ptr; return v->count++; } void *vector_pop(vector_t *v) { if (!v->count) return NULL; void *last = v->data[--v->count]; v->data[v->count] = NULL; return last; } void vector_free(vector_t *v) { if (!v->data) return; free(v->data); v->data = NULL; v->size = 0; v->count = 0; v->free_slot = 0; } /* -------- 遞迴列舉組合 -------- */ void generate_combinations(int n, vector_t *current) { if (n == 0) { printf("["); for (size_t i = 0; i < current->count; i++) { printf("%ld", (intptr_t) current->data[i]); if (i < current->count - 1) printf(","); } printf("]\n"); return; } if (n >= 1) { vector_push(current, (void *)(intptr_t)1); generate_combinations(n - 1, current); vector_pop(current); } if (n >= 2) { vector_push(current, (void *)(intptr_t)2); generate_combinations(n - 2, current); vector_pop(current); } } void stairs(int n) { if (n <= 0) { printf("No stairs to climb.\n"); return; } printf("Climbing %d stairs:\n", n); vector_t path; vector_init(&path); generate_combinations(n, &path); vector_free(&path); } /* -------- 測試 -------- */ void test_case1() { stairs(3); } void test_case2() { stairs(4); } void test_case3() { stairs(5); } int main() { test_case1(); test_case2(); test_case3(); printf("All tests passed !\n"); return 0; } ``` <h3> Assembly code : version_1 </h3> My test cases are : A : 3, B : 4, C : 5 >Output string preparing ```s .data msg: .string "How many stairs?\n" msg1: .string "Step combinations:\n" msg2: .string ": { " msg3: .string "}\n" msg4: .string " " msg5: .string "\n" testcase_a: .string "n = 3" testcase_b: .string "n = 4" testcase_c: .string "n = 5" steps: .word 256 .text ``` >main function ```s main: la a0,msg li a7,4 ecall ##------------------A--------------------------- la a0,testcase_a li a7,4 ecall la a0,msg5 li a7,4 ecall li a2, 3 la a0,msg1 li a7,4 ecall jal ra,start_stairs ##------------------B--------------------------- la a0,testcase_b li a7,4 ecall la a0,msg5 li a7,4 ecall li a2, 4 la a0,msg1 li a7,4 ecall jal ra,start_stairs ##------------------C--------------------------- la a0,testcase_c li a7,4 ecall la a0,msg5 li a7,4 ecall li a2, 5 la a0,msg1 li a7,4 ecall jal ra,start_stairs end: li a7,10 ecall ``` **start_stairs :** >Prepares the environment (stack, registers, and data pointers) for recursive computation of combinations. ```s start_stairs: mv s2,x0 ### s2 is the countCombination (how many ways to do the combination) mv a3,x0 ### a3 is the StepSize la a1 steps beqz a2,end addi sp, sp, -4 sw ra, 0(sp) jal ra,printSteps lw ra, 0(sp) addi sp, sp, 4 ret ``` **printSteps:** >Implements recursion If a2 == 0, print one full combination (printSteps_0). Otherwise, try taking 1 step or 2 steps recursively. Concept: This is the core recursive routine for generating all combinations of steps that sum to n. It explores both possibilities — “take 1 step” and “take 2 steps” — until the total matches the target. ```s printSteps: bnez a2,L1 addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps_0 lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 L1: li t0,1 bltu a2,t0,R addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps_1 lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 L2: li t0,2 bltu a2,t0,R addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps_2 lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 R: ret ``` >Acts as the base case in recursion. When the sum of steps equals n, this routine prints the sequence { 1 2 ... } ```s printSteps_0: addi s2,s2,1 mv a0,s2 li a7,1 ecall la a0,msg2 li a7,4 ecall addi sp,sp,-4 sw ra,0(sp) li t0,0 loop_0: slli t2,t0,2 add t1,a1,t2 ###step[index] lw t3,0(t1) mv a0,t3 li a7,1 ecall la a0,msg4 li a7,4 ecall addi t0,t0,1 bltu t0,a3,loop_0 la a0,msg3 li a7,4 ecall lw ra,0(sp) addi sp,sp,4 ret ``` **printSteps_1 & printSteps_2 :** >Appends a 1-step or 2-step to the current combination, then calls printSteps again. These represent recursive branching — the algorithm explores both paths by appending different step sizes. ```s printSteps_1: slli t2,a3,2 add t1,a1,t2 li t6,1 sw t6,0(t1) addi a2,a2,-1 addi a3,a3,1 addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 ret printSteps_2: slli t2,a3,2 add t1,a1,t2 li t6,2 sw t6,0(t1) addi a2,a2,-2 addi a3,a3,1 addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 ret ``` <h3> Assembly code : version_2 </h3> Do the unloop for test cases and the print out function, as you can see the only different between version_1 and version_2 is the position of the function, but it decrease a lot of cycles. >At first, I want to try dynamic programming for this program, but as long as I need to print all step cases out, it became complicated, so I choose unlooping to optimal my program. ```s .data msg: .string "How many stairs?\n" msg1: .string "Step combinations:\n" msg2: .string ": { " msg3: .string "}\n" msg4: .string " " msg5: .string "\n" testcase_a: .string "n = 3" testcase_b: .string "n = 4" testcase_c: .string "n = 5" steps: .word 256 .text main: la a0,msg li a7,4 ecall ##------------------A--------------------------- la a0,testcase_a li a7,4 ecall la a0,msg5 li a7,4 ecall li a2, 3 ### la a0,msg1 li a7,4 ecall mv s2,x0 ### s2 is the countCombination (how many ways to do the combination) mv a3,x0 ### a3 is the StepSize la a1 steps beqz a2,end jal ra,printSteps ##------------------B--------------------------- la a0,testcase_b li a7,4 ecall la a0,msg5 li a7,4 ecall li a2, 4 ### la a0,msg1 li a7,4 ecall mv s2,x0 ### s2 is the countCombination (how many ways to do the combination) mv a3,x0 ### a3 is the StepSize la a1 steps beqz a2,end jal ra,printSteps ##------------------C--------------------------- la a0,testcase_c li a7,4 ecall la a0,msg5 li a7,4 ecall li a2, 5 la a0,msg1 li a7,4 ecall mv s2,x0 ### s2 is the countCombination (how many ways to do the combination) mv a3,x0 ### a3 is the StepSize la a1 steps beqz a2,end jal ra,printSteps end: li a7,10 ecall printSteps: bnez a2,L1 printSteps_0: addi s2,s2,1 mv a0,s2 li a7,1 ecall la a0,msg2 li a7,4 ecall addi sp,sp,-4 sw ra,0(sp) li t0,0 loop_0: slli t2,t0,2 add t1,a1,t2 ###step[index] lw t3,0(t1) mv a0,t3 li a7,1 ecall la a0,msg4 li a7,4 ecall addi t0,t0,1 bltu t0,a3,loop_0 la a0,msg3 li a7,4 ecall lw ra,0(sp) addi sp,sp,4 L1: li t0,1 bltu a2,t0,R addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps_1 lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 L2: li t0,2 bltu a2,t0,R addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps_2 lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 R: ret printSteps_1: slli t2,a3,2 add t1,a1,t2 li t6,1 sw t6,0(t1) addi a2,a2,-1 addi a3,a3,1 addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 ret printSteps_2: slli t2,a3,2 add t1,a1,t2 li t6,2 sw t6,0(t1) addi a2,a2,-2 addi a3,a3,1 addi sp,sp,-12 sw ra,0(sp) sw a2,4(sp) sw a3,8(sp) jal ra,printSteps lw ra,0(sp) lw a2,4(sp) lw a3,8(sp) addi sp,sp,12 ret ``` :::info Result in console: ::: After all these stage are done, the console is updated like this: ![image](https://hackmd.io/_uploads/B1W_DXWaxe.png =250x) :::info Structure ::: Take three as a example: printSteps(3) ├→ walk `one` stair → printSteps(2) │ ├→ walk `one` stair → printSteps(1) │ │ ├→ walk `one` stair → printSteps(0) → `print` {1,1,1} │ │ └→ walk `two` stairs (not enough) │ └→ walk `two` stairs → printSteps(0) → `print` {1,2} └→ walk `two` stairs → printSteps(1) ├→ walk `one` stair → printSteps(0) → `print` {2,1} :::info After all these stage are done, the register is updated like this: ::: ![image](https://hackmd.io/_uploads/rkoO8Qbagl.png =250x)![image](https://hackmd.io/_uploads/By3FI7bTge.png =280x) :::info Memory viewer: ::: The table below denotes the data section of memory. ![image](https://hackmd.io/_uploads/H1LNwQWTgg.png) :::info The clock cycles of program version_1: ::: Because of the method that I used is recursion, so my cycle is extremly more then others. ![image](https://hackmd.io/_uploads/Syy0mgMTeg.png) :::info The clock cycles of program version_2: ::: This version of program decrease `254` cycles ![image](https://hackmd.io/_uploads/rkefwrqaex.png) ## 5-stage pipelined processor 5-stage pipelined processor I use in ripes is a single cycle processor with hazard detection and forwarding hazard detection. It's block diagram look like this: ![image](https://hackmd.io/_uploads/BkeUG8Kpgl.png) Five stages are : >Instruction fetch (IF) The processor fetches the instruction from memory and updates the program counter (PC) to the next instruction. >Instruction decode and register fetch (ID) The instruction is decoded to determine its type, and the required operands are read from the register file. >Execute (EX) The ALU performs the necessary operation (such as arithmetic, logic, or address calculation). >Memory access (MEM) If the instruction requires reading from or writing to memory (e.g., lw or sw), the memory is accessed here. >Register write back (WB) The result from the ALU or memory is written back to the destination register, completing the instruction’s execution. ## Reference https://hackmd.io/@sysprog/arch2025-quiz1-sol https://hackmd.io/@sysprog/SkkbXLJRR https://leetcode.com/problems/climbing-stairs/description/ https://github.com/sysprog21/ca2025-quizzes ## Full vertion assembly code of problem B ```s .data msg: .string "start decode\n" msg1: .string "start encode\n" msg2: .string "\n" msg3: .string "decode->" msg4: .string "\nencode->" msg5: .string "fail encoding" msg6: .string "0x" testcase_a: .string "test_case_a:n = 0x0f" ###15 testcase_b: .string "test_case_b:n = 0x7d" ###125 testcase_c: .string "test_case_c:n = 0xe1" ###225 pass: .string "All testcases passed" .text main: la a0,msg li a7,4 ecall ##------------------A--------------------------- la a0,testcase_a li a7,4 ecall la a0,msg2 li a7,4 ecall li a1, 0x0f ###n=0x0f jal ra,start_decode_encode ##------------------B--------------------------- la a0,msg2 li a7,4 ecall la a0,testcase_b li a7,4 ecall la a0,msg2 li a7,4 ecall li a1, 0x7d ###n=0x7d jal ra,start_decode_encode ##------------------C--------------------------- la a0,msg2 li a7,4 ecall la a0,testcase_c li a7,4 ecall la a0,msg2 li a7,4 ecall li a1, 0x0e1 ###n=0xe1 jal ra,start_decode_encode j end end: li a7,10 ecall start_decode_encode: addi sp,sp,-4 sw ra, 0(sp) jal ra,Decode lw ra, 0(sp) addi sp,sp,4 mv t0,a0 ###store the return value la a0,msg3 li a7,4 ecall mv a0,t0 li a7,1 ecall addi sp,sp,-4 sw ra, 0(sp) jal ra,Encode lw ra, 0(sp) addi sp,sp,4 mv t0,a0 ### store the return value li t2,-1 ### previous_num bne t0,a1,error blt t0,t2,error mv t2,t0 ### previous_value = value la a0,msg4 li a7,4 ecall mv a0,t2 addi sp,sp,-4 sw ra, 0(sp) jal ra,print_hex lw ra, 0(sp) addi sp,sp,4 ret error: la a0,msg5 li a7,4 ecall print_hex: addi sp, sp, -16 sw ra, 8(sp) # store srli t0, a0, 4 # high nibble andi t1, a0, 0xF # low nibble la a0, msg6 li a7, 4 ecall mv a0, t0 jal ra, print_nibble mv a0, t1 jal ra, print_nibble lw ra,8(sp) addi sp,sp,16 ret print_nibble: li t2,10 bltu a0, t2, digit addi a0, a0, 87 j out digit: addi a0, a0, 48 out: li a7, 11 # print_char ecall ret Decode: andi t0,a1,0x0f ### store mantissa in t0 srli t1,a1,4 ### store expo in t1 not t2,t1 addi t2,t2,1 ### two's complement of expo addi t2,t2,15 li t3,0x7fff srl t3,t3,t2 slli t3,t3,4 ### store offset in t3 sll t4,t0,t1 add a0,t4,t3 ### store trans_num in a2 xor t0,t0,t0 xor t3,t3,t3 ret Encode: addi sp, sp, -16 sw ra, 12(sp) # store add s4,a0,x0 ### s4=value li t0,16 bltu a0,t0,no_need_more_op ### a0=value jal ra,clz mv t1,a0 ### lz = clz(value) li t2,31 sub s1,t2,t1 ### store msb in s1 li s2,0 ### store exponent in s2 li s3,0 ### store overflow in s3 li t6,5 bltu s1,t6,find_exact_exponent addi s2,s1,-4 li t6,15 bltu s2,t6, Calculate_overflow ### t6=1 if greater than 15 li s2,15 Calculate_overflow: ###for loop li s8,0 ###counting loop1: beq s8,s2,Adjust_if_estimate slli s3,s3,1 addi s3,s3,16 addi s8,s8,1 j loop1 Adjust_if_estimate: bgtz s2,check1 j find_exact_exponent check1: bltu s4,s3,check2 j find_exact_exponent check2: addi,s3,s3,-16 srli s3,s3,1 addi s2,s2,-1 j Adjust_if_estimate find_exact_exponent: li s7,20 bge s2, s7, end_encode li t6,15 bgtu s2,t6,end_encode slli s5,s3,1 ### 7f0 should be in s3 overflow addi s5,s5,16 ### next_overflow store in s5 bltu s4,s5,end_encode mv s3,s5 addi s2,s2,1 j find_exact_exponent end_encode: sub s6,s4,s3 srl s6,s6,s2 ### store other mantissa in s6 slli t6,s2,4 add a0,s6,t6 lw ra, 12(sp) addi sp, sp, 16 ret clz: li t0,32 ### n=32 li t1,16 ### c=16 mv t3,a0 loop0: beqz t1,return0 srl t2,t3,t1 ### y = x >> c beqz t2,devide_c sub t0,t0,t1 mv t3,t2 j loop0 devide_c: srli t1,t1,1 j loop0 return0: sub t0,t0,t3 mv a0,t0 ret no_need_more_op: lw ra, 12(sp) # restore ra stored at start of Encode addi sp, sp, 16 # restore stack ret ``` ## Full vertion assembly code of problem C ```s .data Start_msg: .string "Testing basic conversions...\n" Basic_msg: .string "Basic conversions: PASS\n" Special_msg: .string "Special values: PASS\n" Arithmetic_msg: .string "Arithmetic: PASS\n" Comparisons_msg: .string "Comparisons: PASS\n" Edge_msg: .string "Edge cases: PASS\n" Rounding_msg: .string "Rounding: PASS\n" all_test_pass_msg: .string "=== ALL TESTS PASSED ===" #----------------------for_special------------------ msg_start_special: .string "Testing special values...\n" msg_fail_posinf: .string "Positive infinity not detected\n" msg_fail_inf_nan:.string "Infinity detected as NaN\n" msg_fail_neginf: .string "Negative infinity not detected\n" msg_fail_nan: .string "NaN not detected\n" msg_fail_nan_inf:.string "NaN detected as infinity\n" msg_fail_zero: .string "Zero not detected\n" msg_fail_negzero:.string "Negative zero not detected\n" #---------------------for_comparison--------------- msg_start_comparison: .string "Testing comparison...\n" msg_fail_eq: .string "Equality test failed\n" msg_fail_ineq: .string "Inequality test failed\n" msg_fail_lt: .string "Less than test failed\n" msg_fail_nlt: .string "Not less than test failed\n" msg_fail_enlt: .string "Equal not less than test failed\n" msg_fail_gt: .string "Greater than test failed\n" msg_fail_ngt: .string "Not greater than test failed\n" msg_fail_naneq: .string "NaN equality test failed\n" msg_fail_nanlt: .string "Nan less than test failed\n" msg_fail_nangt: .string "Nan greater than test failed\n" #---------------------for_arithmetic--------------- msg_start_arithmetic: .string "Testing arithmetic operations...\n" msg_result: .string "Result = " msg_add_fail:.string "Addition failed\n" msg_add_case1:.string "Addition :1.0f + 2.0f \n" msg_pass_add: .string "PASSED\n" msg_sub_fail:.string "Subtraction failed\n" msg_sub_case1:.string "Subtraction :2.0f - 1.0f \n" msg_mul_fail:.string "Multiplication failed\n" msg_mul_case1:.string "Multiplication :3.0f x 4.0f \n" msg_div_fail:.string "Division failed\n" msg_div_case1:.string "Division :10.0f / 2.0f \n" msg_sqrt_case1: .string "Sqrt :4.0f \n" msg_sqrt_case2: .string "Sqrt :9.0f \n" msg_sqrt4_fail:.string "sqrt(4) failed\n" msg_sqrt9_fail:.string "sqrt(9) failed\n" #---------------------for_edge_cases--------------- msg_testing_edges: .string "Testing edge cases...\n" msg_fail_tiny: .string "Tiny value handling\n" msg_fail_huge: .string "Overflow should produce infinity\n" msg_fail_underflow:.string "Underflow should produce zero or denormal\n" #---------------------for_rounding_cases--------------- msg_rounding: .string "Testing rounding behavior...\n" msg_pass_rounding: .string " Rounding: PASS\n" msg_fail_rounding: .string " Rounding: FAIL\n" #------------------- newline: .string "\n" fail_msg: .string "fail" fail_for_pos_inf_msg: .string "Positive infinity not detected" msg1: .asciz "f32 -> bf16 = " msg2: .asciz "bf16 -> f32 = " hexchars: .asciz "0123456789ABCDEF\n" hex: .string "0x" testcase_a: .string "n = 0.0f\n" ### testcase_b: .string "n = 1.0f\n" ### testcase_c: .string "n = -1.0f\n" ### .text test_values: .word 0x00000000 # 0.0f .word 0x3f800000 # 1.0f .word 0xbf800000 # -1.0f main: la t0, test_values la a0,Start_msg li a7,4 ecall la a0,testcase_a li a7,4 ecall lw s1,0(t1) # 0.0f lw s2,4(t1) # 1.0f lw s3,8(t1) # -1.0f la a0,msg1 li a7,4 ecall mv a0,s1 ##test_case ###----------------------------test_f32<->bf16------------------------------------- jal ra,f32_to_bf16 add s4,a0,x0 # store orig in s4 li a1, 4 # 16-bit = 4 hex digits jal ra, print_hex la a0,msg2 li a7,4 ecall mv a0,s4 jal ra,bf16_to_f32 add s5,a0,x0 li a1, 8 # 16-bit = 8 hex digits jal ra, print_hex la a0,testcase_b li a7,4 ecall la a0,msg1 li a7,4 ecall mv a0,s2 ##test_case jal ra,f32_to_bf16 add s4,a0,x0 # store orig in s4 li a1, 4 # 16-bit = 4 hex digits jal ra, print_hex la a0,msg2 li a7,4 ecall mv a0,s4 jal ra,bf16_to_f32 add s5,a0,x0 li a1, 8 # 16-bit = 8 hex digits jal ra, print_hex la a0,testcase_c li a7,4 ecall la a0,msg1 li a7,4 ecall mv a0,s3 ##test_case jal ra,f32_to_bf16 add s4,a0,x0 # store orig in s4 li a1, 4 # 16-bit = 4 hex digits jal ra, print_hex la a0,msg2 li a7,4 ecall mv a0,s4 jal ra,bf16_to_f32 add s5,a0,x0 li a1, 8 # 16-bit = 8 hex digits jal ra, print_hex ###----------------------------test_basic_conversion------------------------------------- li t3,0x8000 slli t4,s4,16 and t4,t4,t3 and t5,s5,t3 bne t4,t5,fail beq s4,x0,pass_basic_msg srli t3,s5,16 beq s4,t3,pass_basic_msg ##because easy test_cases back_from_basic_msg: ###----------------------------test_spacial_value------------------------------------- test_special_values: la a0, msg_start_special li a7, 4 ecall li a0, 0x7F80 #Test +Inf jal ra, bf16_isinf beqz a0, fail_posinf li a0, 0x7F80 jal ra, bf16_isnan bnez a0, fail_inf_nan li a0, 0xFF80 #Test -Inf jal ra, bf16_isinf beqz a0, fail_neginf li a0, 0x7FC0 #Test NaN jal ra, bf16_isnan beqz a0, fail_nan li a0, 0x7FC0 jal ra, bf16_isinf bnez a0, fail_nan_inf li a0, 0x0000 # Test +0 jal ra, bf16_iszero beqz a0, fail_zero li a0, 0x8000 # Test -0 jal ra, bf16_is_neg_zero beqz a0, fail_negzero la a0, Special_msg li a7, 4 ecall ###----------------------------test_comparison------------------------------------- li s1,0x3F80 li s2,0x4000 li s3,0x3F80 la a0, msg_start_comparison li a7, 4 ecall #equality_test mv a0,s1 mv a1,s3 jal ra,bf16_eq beqz a0, fail_eq mv a0,s1 mv a1,s2 jal ra,bf16_eq bnez a0, fail_ineq #less then_test mv a0,s1 mv a1,s2 jal ra,bf16_lt beqz a0, fail_lt mv a0,s2 mv a1,s1 jal ra,bf16_lt bnez a0, fail_nlt mv a0,s1 mv a1,s3 jal ra,bf16_lt bnez a0, fail_enlt #greater then_test mv a0,s2 mv a1,s1 jal ra,bf16_gt beqz a0, fail_gt mv a0,s1 mv a1,s2 jal ra,bf16_gt bnez a0, fail_ngt li t1,0x7FC0 #nan_f32 mv a0,t1 mv a1,t1 jal ra,bf16_eq bnez a0, fail_naneq mv a0,t1 mv a1,s1 jal ra,bf16_lt bnez a0, fail_nanlt mv a0,t1 mv a1,s1 jal ra,bf16_gt bnez a0, fail_nangt la a0, Comparisons_msg li a7, 4 ecall #------------------------test_arithmetic-------------------------- la a0, msg_start_arithmetic li a7, 4 ecall li s1,0x3f80 #a li s2,0x4000 #b #test_add la a0, msg_add_case1 # 1+2 li a7, 4 ecall mv a0,s1 mv a1,s2 jal ra,bf16_add jal ra,bf16_to_f32 # return a0 = result 40400000 mv t2,a0 li t1,0x40400000 sub s3,t2,t1 #store diff in s3 bltz s3, abs bnez s3,fail_add mv a0,t2 jal ra, print_result #test_sub la a0, msg_sub_case1 # 2-1 li a7, 4 ecall mv a0,s2 mv a1,s1 jal ra,bf16_sub jal ra,bf16_to_f32 # return a0 = result mv t2,a0 li t1,0x3f800000 sub s3,t2,t1 #store diff in s3 bltz s3, abs bnez s3,fail_sub mv a0,t2 jal ra, print_result #test_mul la a0, msg_mul_case1 # 3 * 4 li a7, 4 ecall li s1,0x4040 li s2,0x4080 mv a0,s1 mv a1,s2 jal ra,bf16_mul jal ra,bf16_to_f32 # return a0 = result mv t2,a0 li t1,0x41400000 #12 sub s3,a0,t1 #store diff in s3 bltz s3, abs bnez s3,fail_mul mv a0,t2 jal ra, print_result #test_div la a0, msg_div_case1 # 3 * 4 li a7, 4 ecall li s1,0x4120 #10 li s2,0x4000 #2 mv a0,s1 mv a1,s2 jal ra,bf16_div jal ra,bf16_to_f32 # return a0 = result mv t2,a0 li t1,0x40a00000 #5 sub s3,t2,t1 #store diff in s3 bltz s3, abs bnez s3,fail_div mv a0,t2 jal ra, print_result #test_sqrt4 la a0, msg_sqrt_case1 # 4 li a7, 4 ecall li s1,0x4080 mv a0,s1 jal ra,bf16_sqrt jal ra,bf16_to_f32 # return a0 = result mv t2,a0 li t1,0x40000000 #2 sub s3,a0,t1 #store diff in s3 bltz s3, abs bnez s3,fail_sqrt4 mv a0,t2 jal ra, print_result #test_sqrt9 la a0, msg_sqrt_case2 # 9 li a7, 4 ecall li s1,0x4110 mv a0,s1 jal ra,bf16_sqrt jal ra,bf16_to_f32 # return a0 = result mv t2,a0 li t1,0x40400000 #3 sub s3,a0,t1 #store diff in s3 bltz s3, abs bnez s3,fail_sqrt9 mv a0,t2 jal ra, print_result la a0, Arithmetic_msg li a7, 4 ecall #------------------------test_edge_cases-------------------------- # Test 1: Tiny value handling la a0, msg_testing_edges li a7, 4 ecall li a0, 0x00000001 # tiny 1e-45f jal ra, f32_to_bf16 # -> a0 = bf_tiny(bits) mv s0, a0 jal ra, bf16_to_f32 # -> a0 = tiny_val(bits) mv s1, a0 # bf16_iszero(bf_tiny)? mv a0, s0 jal ra, bf16_iszero bnez a0, test1_pass # abs(tiny_val) li t3, 0x7FFFFFFF and t4, s1, t3 # load threshold (1e-37) li t5, 0x0C2CF59E # 1e-37f # compare abs(tiny_val) < threshold ? bltu t4, t5, test1_pass # fail la a0, msg_fail_tiny li a7, 4 ecall li a0, 1 j test_edge_finish test1_pass: # Test 2: Overflow ?? Inf li a0, 0x7E967699 # 1e38f jal ra, f32_to_bf16 mv s2, a0 # s2 = bf_huge li a0, 0x41200000 # 10.0f jal ra, f32_to_bf16 mv s3, a0 # s3 = bf10 mv a0, s2 mv a1, s3 jal ra, bf16_mul mv s2, a0 # s2 = bf_huge2 jal ra, bf16_isinf beqz a0, fail_huge j test2_pass fail_huge: la a0, msg_fail_huge li a7, 4 ecall li a0, 1 j test_edge_finish test2_pass: # Test 3: Underflow li a0, 0x007CE666 # 1e-38f jal ra, f32_to_bf16 mv s0, a0 # s0 = bf_small li a0, 0x501502F9 # 1e10f jal ra, f32_to_bf16 mv s1, a0 # s1 = bf_1e10 mv a0, s0 mv a1, s1 jal ra, bf16_div mv s2, a0 # s2 = smaller mv a0, s2 jal ra, bf16_to_f32 mv t4, a0 # t4 = smaller_val f32 bits jal ra, bf16_iszero bnez a0, test3_pass li t3, 0x7FFFFFFF and t4, t4, t3 # clear sign li t6, 0x00000001 # 1e-45f f32 bits bltu t4, t6, test3_pass la a0, msg_fail_underflow li a7, 4 ecall li a0, 1 j test_edge_finish test3_pass: la a0, Edge_msg li a7, 4 ecall li a0, 0 test_edge_finish: #------------------------test_rounding------------------------ la a0, msg_rounding li a7, 4 ecall li a0, 0x3FC00000 # 1.5f jal ra, f32_to_bf16 mv s0, a0 # s0 = bf_exact # back_exact = bf16_to_f32(bf_exact) jal ra, bf16_to_f32 mv t0, a0 # t0 = back_exact f32 bits # check exact representation preserved li t1, 0x3FC00000 # 1.5f bits bne t0, t1, rounding_fail pass_test_rounding_1: li a0, 0x3F800066 # 1.0001f bits jal ra, f32_to_bf16 mv s1, a0 # s1 = bf jal ra, bf16_to_f32 mv t2, a0 # t2 = back f32 bits # diff2 = back - val li t3, 0x3F800066 # val bits sub t4, t3, t2 # t4 = diff2 bits # ???????? li t5, 0x7FFFFFFF and t4, t4, t5 # check rounding error < 0.001 li t6, 0x3A83126F # 0.001f bits bltu t4, t6, rounding_pass rounding_fail: la a0, msg_fail_rounding li a7, 4 ecall li a0, 1 j test_rounding_end rounding_pass: la a0, Rounding_msg li a7, 4 ecall test_rounding_end: j end print_result: addi sp,sp,-4 sw ra,0(sp) mv t1,a0 la a0, msg_result # "Result: " li a7, 4 ecall mv a0,t1 li a1,8 jal ra,print_hex pass_add: la a0, msg_pass_add li a7, 4 ecall lw ra,0(sp) addi sp,sp,4 ret end_add: ret abs: bltz s3, abs_neg j abs_positive abs_neg: neg s3, s3 # s3 = -s3 abs_positive: li t1, 10 blt s3, t1, abs_pass li a0, 1 # fail ret abs_pass: li a0, 0 # pass ret bf16_add: addi sp,sp,-8 sw ra,4(sp) li t5,0xFF li t6,0x7F srli t0, a0, 15 # sign_a in t0 andi t0, t0, 1 srli t1, a1, 15 # sign_b in t1 andi t1, t1, 1 srli t2, a0, 7 # exp_a in t2 and t2, t2, t5 srli t3, a1, 7 # exp_b in t3 and t3, t3, t5 and t4, a0, t6 # mant_a in t4 and t5, a1, t6 # mant_b in t5 #s6 = result_sign, s7 = result_expo, s8 = mantissa # if a is zero (exp_a==0 && mant_a==0) => return b beqz a0,return_b beqz a1,return_a beqz t2, skip_a_norm ori t4, t4, 0x80 # mant_a |= 0x80 skip_a_norm: beqz t3, skip_b_norm ori t5, t5, 0x80 # mant_b |= 0x80 skip_b_norm: sub s9, t2, t3 # exp_diff = exp_a - exp_b bgtz s9, exp_a_bigger bltz s9, exp_b_bigger j exp_equal exp_a_bigger: mv s7, t2 # result_exp = exp_a li t6, 8 bgt s9, t6, return_a srl t5, t5, s9 # mant_b >>= exp_diff j continue_add exp_b_bigger: neg s9, s9 # exp_diff = -exp_diff mv s7, t3 # result_exp = exp_b li t6, -8 blt s9, t6, return_b srl t4, t4, s9 # mant_a >>= -exp_diff j continue_add exp_equal: mv s7, t2 continue_add: beq t0, t1, same_sign j diff_sign same_sign: mv s6, t0 # result_sign = sign_a add s8, t4, t5 li t6, 0x100 and s9, s8, t6 beqz s9, normalize_end # overflow => ???????W?? srli s8, s8, 1 # => mantissa >> 1 addi s7, s7, 1 # exponent++ li t6, 0xFF blt s7, t6, normalize_end slli t6, s6, 15 li s9, 0x7F80 or t6, t6, s9 mv a0, t6 j add_exit diff_sign: bgeu t4, t5, mant_a_ge mv s6, t1 # result_sign = sign_b sub s8, t5, t4 # result_mant = mant_b - mant_a j normalize_check mant_a_ge: mv s6, t0 sub s8, t4, t5 normalize_check: beqz s8, return_zero normalize_loop: li t6, 0x80 and s9, s8, t6 bnez s9, normalize_end slli s8, s8, 1 addi s7, s7, -1 # exponent-- blez s7, return_zero j normalize_loop normalize_end: slli t6, s6, 15 # (sign << 15) andi s9, s7, 0xFF slli s9, s9, 7 # (exp << 7) or t6, t6, s9 andi s9, s8, 0x7F # mantissa (7 bits) or t6, t6, s9 mv a0, t6 j add_exit return_a: mv a0, a0 j add_exit return_b: mv a0, a1 j add_exit return_zero: li a0, 0 j add_exit add_exit: lw ra, 4(sp) addi sp, sp, 8 ret bf16_sub: addi sp, sp, 4 sw ra, 0(sp) li t0, 0x8000 xor a1, a1, t0 jal ra,bf16_add lw ra,0(sp) addi sp,sp,4 ret bf16_mul: addi sp, sp, -4 sw ra, 0(sp) # constants li t6, 0xFF li s4, 0x7F li s5, 127 # BF16_EXP_BIAS beqz a0,return_zero beqz a1,return_zero # extract sign bits srli t0, a0, 15 andi t0, t0, 1 # sign_a (t0) srli t1, a1, 15 andi t1, t1, 1 # sign_b(t1) # extract exponents (8 bits) srli t2, a0, 7 and t2, t2, t6 # exp_a (t2) srli t3, a1, 7 and t3, t3, t6 # exp_b (t3) # extract mantissas (7 bits) and t4, a0, s4 # mant_a (t4) and t5, a1, s4 # mant_b (t5) # result sign = sign_a ^ sign_b (s7) xor s7, t0, t1 # result expo (s8) # result mant (s9) # exp_adjust = 0 li s6, 0 # === normalize mant_a === beqz t2, denorm_a norm_a: ori t4, t4, 0x80 j mant_a_done denorm_a: beqz t4, mant_a_done denorm_a_loop: andi t0, t4, 0x80 bnez t0, mant_a_done slli t4, t4, 1 addi s6, s6, -1 j denorm_a_loop mant_a_done: # === normalize mant_b === beqz t3, denorm_b norm_b: ori t5, t5, 0x80 j mant_b_done denorm_b: beqz t5, mant_b_done denorm_b_loop: andi t0, t5, 0x80 bnez t0, mant_b_done slli t5, t5, 1 addi s6, s6, -1 j denorm_b_loop mant_b_done: # mantissa multiply (8x8 = 16-bit) mul s9, t4, t5 # result_exp = exp_a + exp_b - bias + exp_adjust add s8, t2, t3 add s8, s8, s6 addi s8, s8, -127 # normalize mantissa li t0, 0x8000 and t1, s9, t0 bnez t1, shift8 # no overflow: shift right 7 bits srli s9, s9, 7 andi s9, s9, 0x7F j norm_done shift8: srli s9, s9, 8 andi s9, s9, 0x7F addi s8, s8, 1 norm_done: # overflow check li t0, 0xFF bge s8, t0, set_inf # underflow check blez s8, underflow # ===== normal result ===== slli t0, s7, 15 andi t1, s8, 0xFF slli t1, t1, 7 or t0, t0, t1 andi t1, s9, 0x7F or a0, t0, t1 j mul_done # ===== underflow case ===== underflow: li t0, -6 blt s8, t0, return_zero_mul li t1, 1 sub t1, t1, s8 srl s9, s9, t1 li s8, 0 slli t0, s7, 15 andi t1, s8, 0xFF slli t1, t1, 7 or t0, t0, t1 andi t1, s9, 0x7F or a0, t0, t1 j mul_done # ===== overflow (Inf) ===== set_inf: slli a0, s7, 15 li t6,0x7F80 or a0, a0, t6 j mul_done # ===== zero result ===== return_zero_mul: slli a0, s7, 15 # ===== finish ===== mul_done: lw ra, 0(sp) addi sp, sp, 4 ret bf16_div: li t6, 0xFF li s4, 0x7F li s5, 127 # BF16_EXP_BIAS # extract sign bits srli t0, a0, 15 andi t0, t0, 1 # sign_a srli t1, a1, 15 andi t1, t1, 1 # sign_b # extract exponents srli t2, a0, 7 and t2, t2, t6 # exp_a srli t3, a1, 7 and t3, t3, t6 # exp_b # extract mantissas and t4, a0, s4 # mant_a and t5, a1, s4 # mant_b # result sign = sign_a ^ sign_b xor s7, t0, t1 # add hidden 1-bit ori t4, t4, 0x80 # mant_a |= 0x80 ori t5, t5, 0x80 # mant_b |= 0x80 # result_exp = exp_a - exp_b + bias sub s8, t2, t3 add s8, s8, s5 # s8 = exp_a - exp_b + 127 # mantissa division (approx) slli s9, t4, 7 # mant_a << 7 divu s9, s9, t5 # result_mant = mant_a / mant_b # normalization (if mantissa >= 0x100) li t0, 0x100 and t1, s9, t0 beqz t1, skip_norm srli s9, s9, 1 addi s8, s8, 1 skip_norm: # pack result bits slli t0, s7, 15 # sign << 15 slli t1, s8, 7 # exp << 7 or t0, t0, t1 and s9, s9, s4 # mant & 0x7F or a0, t0, s9 ret bf16_sqrt: li s5, 127 # BF16_EXP_BIAS li t6, 0xFF li s4, 0x7F # ?? exponent ?P mantissa srli t0, a0, 7 and t0, t0, t6 # exp and t1, a0, s4 # mant # e = exp - bias addi t2, t0, -127 # e = exp - 127 li t3, 1 and t4, t2, t3 # t4 = e & 1 ori t5, t1, 0x80 # m = 0x80 | mant beqz t4, sqrt_even_exp slli t5, t5, 1 # m <<= 1 addi t2, t2, -1 sqrt_even_exp: srai t6, t2, 1 add t6, t6, s5 # new_exp = (e>>1)+bias # ???l??G???j?M li s0, 90 # low li s1, 256 # high li s2, 128 # result sqrt_loop: bgt s0, s1, sqrt_done add s3, s0, s1 srli s3, s3, 1 # mid = (low + high) >> 1 mul s4, s3, s3 srli s4, s4, 7 # sq = (mid*mid)/128 ble s4, t5, sqrt_le addi s1, s3, -1 # high = mid - 1 j sqrt_loop sqrt_le: mv s2, s3 # result = mid addi s0, s3, 1 # low = mid + 1 j sqrt_loop sqrt_done: li t0, 256 blt s2, t0, sqrt_check_low srli s2, s2, 1 addi t6, t6, 1 # new_exp++ j sqrt_pack sqrt_check_low: li t1, 128 bge s2, t1, sqrt_pack sqrt_shift_up: blt t6, zero, sqrt_pack slli s2, s2, 1 addi t6, t6, -1 blt s2, t1, sqrt_shift_up sqrt_pack: andi s2, s2, 0x7F # new_mant = result & 0x7F slli t6, t6, 7 or a0, t6, s2 ret #------------------------test_edge_classes-------------------------- bf16_eq: addi sp, sp, -16 sw ra, 8(sp) mv t0, a0 #store a in t0 jal ra, bf16_isnan bnez a0, eq_false # b NaN or not mv t1, a1 #store b in t1 mv a0, t1 jal ra, bf16_isnan bnez a0, eq_false # both zero or not mv a0, t0 # a0 = a jal ra, bf16_iszero mv t1, a0 # t1 = iszero(a) mv a0, a1 jal ra, bf16_iszero and t2, t1, a0 bnez t2, eq_true # bit equality beq t0, a1, eq_true eq_false: li a0, 0 j eq_exit eq_true: li a0, 1 eq_exit: lw ra, 8(sp) addi sp, sp, 16 ret bf16_lt: addi sp, sp, -16 sw ra, 12(sp) mv t0, a0 jal ra, bf16_isnan bnez a0, lt_false mv a0, a1 jal ra, bf16_isnan bnez a0, lt_false # check zero mv a0, t0 jal ra, bf16_iszero mv t1, a0 mv a0, a1 jal ra, bf16_iszero and t2, t1, a0 bnez t2, lt_false # sign_a = (a >> 15) & 1 srli t3, t0, 15 andi t3, t3, 1 # sign_b = (b >> 15) & 1 srli t4, a1, 15 andi t4, t4, 1 # sign_a != sign_b ? bne t3, t4, sign_diff # same sign beqz t3, both_pos # if sign = 0 positive j both_neg both_pos: blt t0, a1, lt_true # both pos compare with numbers j lt_false both_neg: bgt t0, a1, lt_true # neg_num need reverse j lt_false sign_diff: # sign_a > sign_b ? bgt t3, t4, lt_true j lt_false lt_true: li a0, 1 j lt_exit lt_false: li a0, 0 lt_exit: lw ra, 12(sp) addi sp, sp, 16 ret bf16_gt: addi sp, sp, -16 sw ra, 4(sp) mv t0, a0 # store a mv a0, a1 # bf16_lt(b, a) mv a1, t0 jal ra, bf16_lt lw ra, 4(sp) addi sp, sp, 16 ret # ---------------- fail_msg_for_special---------------- fail_posinf: la a0, msg_fail_posinf li a7, 4 ecall li a0, 1 ret fail_inf_nan: la a0, msg_fail_inf_nan li a7, 4 ecall li a0, 1 ret fail_neginf: la a0, msg_fail_neginf li a7, 4 ecall li a0, 1 ret fail_nan: la a0, msg_fail_nan li a7, 4 ecall li a0, 1 ret fail_nan_inf: la a0, msg_fail_nan_inf li a7, 4 ecall li a0, 1 ret fail_zero: la a0, msg_fail_zero li a7, 4 ecall li a0, 1 ret fail_negzero: la a0, msg_fail_negzero li a7, 4 ecall li a0, 1 ret ### ---------------- fail_for_comparison---------------- fail_naneq: la a0, msg_fail_naneq li a7, 4 ecall li a0, 1 ret fail_nanlt: la a0, msg_fail_nanlt li a7, 4 ecall li a0, 1 ret fail_nangt: la a0, msg_fail_nangt li a7, 4 ecall li a0, 1 ret fail_eq: la a0, msg_fail_eq li a7, 4 ecall li a0, 1 ret fail_ineq: la a0, msg_fail_ineq li a7, 4 ecall li a0, 1 ret fail_lt: la a0, msg_fail_lt li a7, 4 ecall li a0, 1 ret fail_nlt: la a0, msg_fail_nlt li a7, 4 ecall li a0, 1 ret fail_enlt: la a0, msg_fail_enlt li a7, 4 ecall li a0, 1 ret fail_gt: la a0, msg_fail_gt li a7, 4 ecall li a0, 1 ret fail_ngt: la a0, msg_fail_ngt li a7, 4 ecall li a0, 1 ret #---------------------fail_arithmetic----------------- fail_add: la a0, msg_add_fail li a7, 4 ecall ret fail_sub: la a0, msg_sub_fail li a7, 4 ecall ret fail_mul: la a0, msg_mul_fail li a7, 4 ecall ret fail_div: la a0, msg_div_fail li a7, 4 ecall ret fail_sqrt4: la a0, msg_sqrt4_fail li a7, 4 ecall ret fail_sqrt9: la a0, msg_sqrt9_fail li a7, 4 ecall ret bf16_isinf: mv t1,a0 li t2,0x7F80 and t3,t1,t2 bne t3,t2, not_inf li t2,0x007F and t4,t1,t2 bnez t4, not_inf li a0,1 ret not_inf: li a0,0 ret bf16_isnan: mv t1,a0 li t2,0x7F80 and t3,t1,t2 bne t3,t2, not_nan li t2,0x007F and t4,t1,t2 beqz t4, not_nan li a0,1 ret not_nan: li a0,0 ret bf16_iszero: mv t1,a0 li t2,0x7FFF and t1,t1,t2 beqz t1, is_zero li a0,0 ret is_zero: li a0,1 ret bf16_is_neg_zero: li t1,0x8000 beq a0,t1,is_negzero li a0,0 ret is_negzero: li a0,1 ret pass_basic_msg: la a0,Basic_msg li a7,4 ecall j back_from_basic_msg ## not sure ------------------------------------ j end print_hex: mv t0, a0 # val la a0,hex li a7,4 ecall mv t1, a1 # digits la t2, hexchars # hex print_hex_loop: beqz t1, print_hex_done addi t1, t1, -1 slli t3, t1, 2 srl t4, t0, t3 # val >> (4*pos) andi t4, t4, 0xF add t4, t2, t4 lbu a0, 0(t4) li a7, 11 # print_char ecall j print_hex_loop print_hex_done: li a0, 10 # newline li a7, 11 ecall ret fail: la a0, fail_msg li a7, 4 ecall li a0, 1 # return 1 if fail ret end: la a0, all_test_pass_msg li a7, 4 ecall li a7,10 ecall f32_to_bf16: mv t1,a0 # ((f32bits >> 23) & 0xFF) srli t2, t1, 23 andi t2, t2, 0xFF #store expo in t2 li t3, 0xFF beq t2,t3,is_nan_inf srli t4,t1,16 andi t4,t4,1 li t3,0x7FFF add t4,t4,t3 add t1,t1,t4 srli t2,t1,16 mv a0,t2 ret bf16_to_f32: slli t3,a0,16 mv a0,t3 ret is_nan_inf: srli t4,t2,16 li t3 0xff and t4,t4,t3 mv a0,t4 ret ```