Kiwi0915
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Assignment1: RISC-V Assembly and Instruction Pipeline contributed by < [jgw0915](https://github.com/jgw0915/Computer-Architecture_HW1) > ## Problem (B) UF8 ### UF8 Introduction - **1. Encoding Mathematics** - **Encoding Function** $$ E(v) = \begin{cases} v, & \text{if } v < 16 \\ 16e + \lfloor (v - \text{offset}(e)) / 2^e \rfloor, & \text{otherwise} \end{cases} $$ - **Offset Definition** $$ \text{offset}(e) = (2^e - 1) \times 16 $$ --- From the formula, we can see offset(e) is constant for each exponent e . - **Example 1** Try v = 16 : - e = 1 - offset(1) = 16 $$ E(16) = 16 \times 1 + \lfloor (16 - 16) / 2^1 \rfloor = 16 $$ - **Example 2** Try v = 17 : - e = 1 - offset(1) = 16 $$ E(17) = 16 \times 1 + \lfloor (17 - 16) / 2^1 \rfloor = 16 $$ → Both 16 and 17 have same offset and map to the same encoding value **16**. --- - **Finding Exponent \( e \)** $$ \text{offset}(e) \le v < \text{offset}(e+1) $$ - **Example:** Try v = 51 : - offset(1) = 16 - offset(2) = 48 - offset(3) = 112 → Expoenet of 51 is 2 --- - **Mapping Summary** | Exponent (binary) | Range Start | Encoded Value | |-------------------|-------------|----------------| | 0001 | 16 | 16 | | 0011 | 48 | 32 | For Range Start (Input that equal to its offset(e)), its uf8 encoding $\rightarrow$ $16 \times e$ --- - **2. Decoding Mathematics** - **Decoding Formula** $$ D(b) = m \times 2^e + (2^e - 1) \times 16 $$ where $$ e = \lfloor b / 16 \rfloor, \quad m = b \bmod 16 $$ --- - **Example** Try b = 31 : - e = 1 , m = 15 $$ D(31) = 15 \times 2^1 + (2^1 - 1) \times 16 = 30 + 16 = 46 $$ ⚠️ **Observation:** $\;\;\;\;$ Values like 46 and 47 share the same encoding (31), and decode to same value 46. --- - **Proof 1 - (Derive Decoding Formula by Encoding Formula)** $$ E(v) = 16e + \lfloor (v - (2^e - 1)\times16) / 2^e \rfloor $$ $$ \rightarrow b = 16e + \lfloor (D(b) - (2^e - 1)\times16) / 2^e \rfloor $$ $$ \rightarrow D(b) = 2^e \times b - 16 \times e \times 2^e + (2^e - 1)\times16 $$ $$ \rightarrow D(b) = 2^e \times ( b - 16 \times e ) + (2^e - 1)\times16 $$ $$ \rightarrow D(b) = 2^e \times m + (2^e - 1) \times 16 $$ --- - **Proof 1.1 - (Modulo Definition)** $$ b \bmod 16 = b - 16 \times \lfloor b / 16 \rfloor $$ --- - **3. Error Analysis** - **Absolute Error (max)** - For numbers having same exponent , when decoding, all values sharing the same quotient (divided by $2^e$) map to the same decoded value. Thus, the maximal absolute value for the numbers in the exponent(e) range should be the biggest number substract the smallest number within the same quotient value dividing by $2^e$, which should be: $$ \text{Max Absolute Error} = 2^{e} - 1 $$ --- - **Relative Error (Max)** - Theoritically, Maximal relative error is $$ \text{Relative Error}_{\max}(e) = \frac{\text{Max Absolute Error}}{\text{Smallest Value for the groupt in same quotient value (offset(e), the recoded resulte)}} $$ - So we can apply the formula of Max Absolute Error and offset(e) $$ \text{Relative Error}_{\max} = \frac{2^e - 1}{(2^e - 1)\times16} = \frac{1}{16} = 0.0625 $$ This is the theoretical upper bound; actual test cases will be smaller. --- ### Implementation - CLZ (Count Leading Zero) #### Source C code in Problem B - **Algorithm Logic** - Initialize n = 32, c = 16. - **Iteration for `c`** - `16 → 8 → 4 → 2 → 1 → 0` - **While-loop logic** - For each `c`, right-shift `x` by `c` bits. - If `(x >> c) > 0`: - Subtract `n -= c` to update the possible number of leading zeros from `32` to `n - c` ( `c` is accomulative through interation). - Replace `x = x >> c` so the active bit-width of `x` shrinks from 32 to `n - c` bits ( Again, `c` is accomulative through interation). - Terminate when `c = 0`. - Return `n - x`, where: - `n` is the count of leading zeros plus one (It includes the first non-zero bit). - `x` becomes `1` if the original input was nonzero, otherwise `x = 0`. ```clike= 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); // printf(" n = %d , x = %u \n", n, x); return n - x; } ``` #### RV32 Assembly - Compiler Generate (rv32-gcc (trunk)) - The following rv32 code of `clz` function is generate from rv32-gcc (trunk) in [Compiler Expoler](https://godbolt.org/) - Total code space for `clz` function is 34 instruction lines ```gas= clz: # a0 -> function arg x on entry; return value on exit. # ra -> return address (saved to stack). # s0 -> frame pointer. # a4, a5 -> temporaries. # Stack frame (at s0): # -36(s0) -> x (current working value). # -20(s0) -> n. # -24(s0) -> c. # -28(s0) -> y (temporary). addi sp,sp,-48 # prologue: make 48B stack frame sw ra,44(sp) # save ra sw s0,40(sp) # save s0 addi s0,sp,48 # s0 = frame pointer sw a0,-36(s0) # x = arg li a5,32 sw a5,-20(s0) # n = 32 li a5,16 sw a5,-24(s0) # c = 16 # do { ... } while (c); L3: lw a5,-24(s0) # a5 = c lw a4,-36(s0) # a4 = x srl a5,a4,a5 # a5 = x >> c sw a5,-28(s0) # y = x >> c lw a5,-28(s0) # if (y) beq a5,zero,L2 # skip if y == 0 lw a4,-20(s0) # n -= c; lw a5,-24(s0) sub a5,a4,a5 sw a5,-20(s0) lw a5,-28(s0) # x = y; sw a5,-36(s0) L2: lw a5,-24(s0) # c >>= 1; srai a5,a5,1 sw a5,-24(s0) lw a5,-24(s0) # while (c) loop back bne a5,zero,L3 lw a4,-20(s0) # return n - x; lw a5,-36(s0) sub a5,a4,a5 mv a0,a5 # a0 = result lw ra,44(sp) # epilogue lw s0,40(sp) addi sp,sp,48 jr ra ``` #### RV32 Assembly- Optmized by Using Temp Register ```gas= clz: # a0 -> function arg x on entry; return value on exit. # ra -> return address (saved to stack). # s0 -> frame pointer. # t0 -> x (current working value). # t1 -> n. # t2 -> c. # t3 -> y (temporary, store x >> c). # initialize stack frame addi sp,sp,-8 # prologue: make 48B stack frame sw ra,4(sp) # save ra sw s0,0(sp) # save s0 addi s0,sp,8 # s0 = frame pointer add t0,x0,a0, # x = arg addi t1,x0,32 # n = 32 addi t2,x0,16 # c = 16 # do { ... } while (c); WHILE_LOOP: srl t3,t0,t2 # y = x >> c beq t3,x0,SKIP # if (y) skip if sub t1,t1,t2 # n -= c; add t0,x0,t3 # x = y; SKIP: srai t2,t2,1 # c >>= 1; bne t2,x0,WHILE_LOOP # while (c) loop sub a0,t1,t0 # return n - x; lw ra,4(sp) # epilogue lw s0,0(sp) addi sp,sp,8 jr ra ``` --- ### Implementation - uf8_decode #### Source C code in Problem B ```clike= /* 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; } ``` #### RV32 Assembly - Compiler Generate (rv32-gcc (trunk)) ```gas= uf8_decode: # Register roles: # a0 = argument fl on entry; return value on exit # a4 = temporary (builds 0x7FFF and holds (mantissa<<exponent)) # a5 = temporary (holds fl, exponent, intermediate math, final sum) # s0 = frame pointer (FP) # ra = return address #------------------------------------------------------- # Stack frame (relative to s0): # -33(s0) : byte fl # -21(s0) : byte exponent # -20(s0) : word mantissa # -28(s0) : word offset addi sp,sp,-48 # prologue: allocate 48B frame sw ra,44(sp) # save ra sw s0,40(sp) # save s0 addi s0,sp,48 # s0 = sp + 48 (set FP) mv a5,a0 # a5 = fl (from a0) sb a5,-33(s0) # store fl (byte) lbu a5,-33(s0) # a5 = fl andi a5,a5,15 # a5 = fl & 0x0f sw a5,-20(s0) # mantissa = a5 lbu a5,-33(s0) # a5 = fl srli a5,a5,4 # a5 = fl >> 4 sb a5,-21(s0) # exponent = a5 (byte) # offset = (0x7FFF >> (15 - exponent)) << 4 lbu a5,-21(s0) # a5 = exponent li a4,15 # a4 = 15 sub a5,a4,a5 # a5 = 15 - exponent li a4,32768 # a4 = 0x8000 addi a4,a4,-1 # a4 = 0x7FFF sra a5,a4,a5 # a5 = 0x7FFF >> (15 - exponent) slli a5,a5,4 # a5 = a5 << 4 sw a5,-28(s0) # offset = a5 # return (mantissa << exponent) + offset lbu a5,-21(s0) # a5 = exponent lw a4,-20(s0) # a4 = mantissa sll a4,a4,a5 # a4 = mantissa << exponent lw a5,-28(s0) # a5 = offset add a5,a4,a5 # a5 = (mantissa<<exponent) + offset mv a0,a5 # return value in a0 # epilogue lw ra,44(sp) # restore ra lw s0,40(sp) # restore s0 addi sp,sp,48 # free frame jr ra # return ``` #### RV32 Assembly- Optmized hand-written ```gas= uf8_decode: # Register role: # a0 = input argument fl on entry; return value on exit # s0 = frame pointer (FP) # ra = return address # t0 = mantissa (holds fl & 0x0f) # t1 = exponent (holds fl >> 4) # t2 = offset (holds (0x7FFF >> (15 - exponent)) << 4) #------------------------------------------------------ # Stack frame (relative to s0): # -48(s0) : reserved ra (save area) # -44(s0) : reserved s0 (save area) addi sp,sp,-8 # prologue: allocate 8B frame sw ra,4(sp) # save ra sw s0,0(sp) # save s0 addi s0,sp,8 # s0 = sp + 8 (set FP) andi t0,a0,15 # mantissa = fl & 0x0f srli t1,a0,4 # exponent = fl >> 4 li t2,15 # t2 = 15 sub t2,t2,t1 # t2 = 15 - exponent srli t2,32768,t2 # t2 = 0x7FFF >> (15 - exponent) slli t2,t2,4 # offset = t2 = t2 << 4 sll a0,t0,t1 # a0 = mantissa << exponent add a0,a0,t2 # return (mantissa << exponent) + offset lw ra,4(sp) # epilogue lw s0,0(sp) # restore s0 addi sp,sp,8 # free frame jr ra # return ``` ---- ### Implementation - uf8_encode #### Original C code with Test ```clike= 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; } ``` #### RV32 Assembly - Compiler Generate (rv32-gcc (trunk)) ```gas= uf8_encode: # Register roles: # a0 = arg value on entry; return uf8 on exit # a4 = temporary (ALU, comparisons, shifted intermediates) # a5 = primary temporary (loop vars, intermediates, final byte) # s0 = frame pointer (FP) # ra = return address #------------------------------------------------------------------- # Stack frame (relative to s0): # -52(s0) : uint32_t value # -32(s0) : int lz # -36(s0) : int msb # -24(s0) : uint32_t overflow # -17(s0) : uint8_t exponent # -25(s0) : uint8_t e (loop counter for initial overflow build) # -40(s0) : uint32_t next_overflow # -41(s0) : uint8_t mantissa # Initialize stack frame addi sp,sp,-64 # prologue: allocate 64B sw ra,60(sp) # save ra sw s0,56(sp) # save s0 addi s0,sp,64 # set FP sw a0,-52(s0) # store input value # Quick return for value <= 15 lw a4,-52(s0) # a4 = value li a5,15 # a5 = 15 bgtu a4,a5,L6 # if (value > 15) goto L6 lw a5,-52(s0) # a5 = value andi a5,a5,0xff # zero-extend to byte j L7 # return value # value >= 16 path L6: lw a0,-52(s0) # a0 = value call clz # a0 = clz(value) mv a5,a0 # a5 = lz sw a5,-32(s0) # lz = a5 li a4,31 # a4 = 31 lw a5,-32(s0) # a5 = lz sub a5,a4,a5 # a5 = 31 - lz sw a5,-36(s0) # msb = a5 sb zero,-17(s0) # exponent = 0 sw zero,-24(s0) # overflow = 0 lw a4,-36(s0) # a4 = msb li a5,4 # a5 = 4 ble a4,a5,L14 # if (msb <= 4) skip estimate block # exponent = msb - 4; if (exponent > 15) exponent = 15; lw a5,-36(s0) # a5 = msb andi a5,a5,0xff # keep low byte addi a5,a5,-4 # a5 = msb - 4 sb a5,-17(s0) # exponent = a5 lbu a4,-17(s0) # a4 = exponent li a5,15 # a5 = 15 bleu a4,a5,L9 # if (exponent <= 15) ok li a5,15 # a5 = 15 sb a5,-17(s0) # exponent = 15 L9: sb zero,-25(s0) # e = 0 (byte) # for (e=0; e<exponent; e++) overflow = (overflow<<1)+16; j L10 L11: lw a5,-24(s0) # a5 = overflow slli a5,a5,1 # a5 = overflow << 1 addi a5,a5,16 # a5 += 16 sw a5,-24(s0) # overflow = a5 lbu a5,-25(s0) # a5 = e addi a5,a5,1 # e++ sb a5,-25(s0) # store e L10: lbu a4,-25(s0) # a4 = e lbu a5,-17(s0) # a5 = exponent bltu a4,a5,L11 # loop while e < exponent # while (exponent > 0 && value < overflow) { overflow = (overflow-16)>>1; exponent--; } j L12 L13: lw a5,-24(s0) # a5 = overflow addi a5,a5,-16 # a5 = overflow - 16 srli a5,a5,1 # a5 = (overflow - 16) >> 1 sw a5,-24(s0) # overflow = a5 lbu a5,-17(s0) # a5 = exponent addi a5,a5,-1 # exponent-- sb a5,-17(s0) # store exponent L12: lbu a5,-17(s0) # a5 = exponent beq a5,zero,L14 # if (exponent == 0) stop lw a4,-52(s0) # a4 = value lw a5,-24(s0) # a5 = overflow bltu a4,a5,L13 # if (value < overflow) adjust down j L14 # while (exponent < 15) { next_overflow=(overflow<<1)+16; if (value < next_overflow) break; overflow=next_overflow; exponent++; } L17: lw a5,-24(s0) # a5 = overflow slli a5,a5,1 # a5 = overflow << 1 addi a5,a5,16 # a5 += 16 sw a5,-40(s0) # next_overflow = a5 lw a4,-52(s0) # a4 = value lw a5,-40(s0) # a5 = next_overflow bltu a4,a5,L18 # if (value < next_overflow) break lw a5,-40(s0) # a5 = next_overflow sw a5,-24(s0) # overflow = next_overflow lbu a5,-17(s0) # a5 = exponent addi a5,a5,1 # exponent++ sb a5,-17(s0) # store exponent L14: lbu a4,-17(s0) # a4 = exponent li a5,14 # a5 = 14 bleu a4,a5,L17 # loop while exponent <= 14 (i.e., < 15) j L16 L18: nop # break # mantissa = (value - overflow) >> exponent; return (exponent<<4)|mantissa; L16: lw a4,-52(s0) # a4 = value lw a5,-24(s0) # a5 = overflow sub a4,a4,a5 # a4 = value - overflow lbu a5,-17(s0) # a5 = exponent srl a5,a4,a5 # a5 = (value - overflow) >> exponent sb a5,-41(s0) # mantissa = a5 (byte) lb a5,-17(s0) # a5 = sign-extended exponent byte slli a5,a5,4 # a5 = exponent << 4 slli a4,a5,24 # pack to 32 then srai a4,a4,24 # sign-correct to low byte lb a5,-41(s0) # a5 = mantissa (sign-extended byte) or a5,a4,a5 # a5 = (exponent<<4) | mantissa slli a5,a5,24 # zero-extend to 8-bit in 32-bit reg srai a5,a5,24 andi a5,a5,0xff # ensure 8-bit result L7: mv a0,a5 # return in a0 lw ra,60(sp) # epilogue: restore ra lw s0,56(sp) # restore s0 addi sp,sp,64 # free frame jr ra # return ``` #### RV32 Assembly- Optmized hand-written ```gas= uf8_encode: # register roles: # a0 : uint32_t value (input) # s0 : frame pointer # t0 : input value copy # t1 : msb (most significant bit position) # t2 : exponent # t3 : overflow (offset for current exponent) # t4 : e (temporary for exponent calculation) # t5 : next_overflow (offset for next exponent) # t6 : mantissa # a1 : temporary constant # stack frame (from s0, growing down): # -4(s0) : ra # -8(s0) : saved s0 # -12(s0) : uint32_t value (spill) # initialize stack frame addi sp,sp,-12 sw ra,8(sp) sw s0,4(sp) addi s0,sp,12 # quick return for value <= 15 addi a1,zero,15 # a1 = 15 bgtu a0,a1,init_exponent_guess # if (value <= 15) return value j uf8_encode_return init_exponent_guess: # initialize variables for exponent guessing loop sw a0,0(sp) # save value across call call clz # call clz(value) li t1,31 # t1 = msb = 31 lw t0,0(sp) # t0 = value sub t1,t1,a0 # msb = 31 - clz(value) li t2,0 # t2 = exponent = 0 li t3,0 # t3 = overflow = 0 # Estimate exponent addi a1,x0,4 # a1 = 4 ble t1,a1,find_exact_exponent # if (msb <= 4) goto find_exact_exponent addi t2,t1,-4 # exponent = msb - 4 # Handle exponent overflow addi a1,x0,15 # a1 = 15 bleu t2,a1,estimate_exponent_and_overflow # if (exponent <= 15) goto estimate_exponent addi t2,x0,15 # exponent = 15 estimate_exponent_and_overflow: # Calculate overflow for current exponent addi t4,x0,0 # t4 = e = 0 j Loop_condition_1 Loop_1: slli t3,t3,1 # overflow <<= 1 addi t3,t3,16 # overflow += 16 addi t4,t4,1 # e += 1 Loop_condition_1: bltu t4,t2,Loop_1 adjust_e_o: # adjust exponent and overflow beq t2,x0,find_exact_exponent # if (exponent == 0) goto find_exact_exponent bge t0,t3,find_exact_exponent # if (value >= overflow) go to estimating addi t3,t3,-16 # overflow -= 16 srli t3,t3,1 # overflow >>= 1 addi t2,t2,-1 # exponent -= 1 j adjust_e_o find_exact_exponent: # Find exact exponent addi a1,x0,14 # a1 = 14 bgtu t2,a1,finalize # if (exponent > 14) goto finalize slli t5,t3,1 # next_overflow = overflow << 1 addi t5,t5,16 # next_overflow += 16 bltu t0,t5,finalize # if (value < next_overflow) goto finalize addi t3,t5,0 # overflow = next_overflow addi t2,t2,1 # exponent += 1 j find_exact_exponent finalize: # Calculate mantissa sub t6,t0,t3 # mantissa = value - overflow srl t6,t6,t2 # mantissa >>= exponent andi t6,t6,15 # mantissa &= 0x0f # Combine exponent and mantissa slli t2,t2,4 # exponent <<= 4 slli t2,t2,24 srai t2,t2,24 or a0,t2,t6 # return (exponent << 4) | mantissa slli a0,a0,24 srai a0,a0,24 uf8_encode_return: lw ra,8(sp) # epilogue: restore ra lw s0,4(sp) # restore s0 addi sp,sp,12 # free frame jr ra # return ``` --- ### Analysis #### Improvement comparing to compiler Following comparisons of functions used its own test case, and the comparison is targeting at optimization of RV32 instructions in function not in test case. * **For CLZ function** ![image](https://hackmd.io/_uploads/rkHjEoqTlx.png) * **For Decoding** ![image](https://hackmd.io/_uploads/SJrT4jc6eg.png) * **For Encoding** ![image](https://hackmd.io/_uploads/SkI0No5plx.png) * **For whole code in problem B** ![image](https://hackmd.io/_uploads/S1nMEi9Tlx.png) #### Snapshot context (from Ripes banners) ![Screenshot 2025-10-14 003245](https://hackmd.io/_uploads/ryZ6Jhqpgx.png) - **IF**: `bge x5 x28 0x20 <find_exact_exponent>` ; `value ≥ overflow` - **ID**: `beq x7 x0 0x24 <find_exact_exponent>` ; `exponent == 0` - **EX**: `bltu x29 x7 -0x0c <Loop_1>` ; `e < exponent` - **MEM**: `addi x29 x29 1` ; `e += 1` - **WB**: `addi x28 x28 16` ; `overflow += 16` (paired with prior `slli`) This is inside `estimate_exponent_and_overflow`: builds `overflow = 16 * (2^e − 1)` via repeated `slli` and `addi`. --- #### 5-Stage Pipeline Processor This section is the analysis of [q1-uf8.s](https://github.com/jgw0915/Computer-Architecture/blob/main/uf8/q1-uf8.s) in terms of processor, memory, instruction-level view. ![Screenshot 2025-10-14 003155](https://hackmd.io/_uploads/B1QC1h9pxe.png) * **IF (Instruction Fetch)** - **Role**: Drive `PC` into instruction memory. - **Key signals**: `PCSrc` mux selects `PC+4` or EX branch target. `PC.en=1` unless branch flush. - **Here**: `bltu` in EX is taken → `PCSrc=branch`, IF fetches `Loop_1`. ![image](https://hackmd.io/_uploads/H1K7en96le.png) * **ID (Instruction Decode)** - **Role**: Decode control, read regs, form immediates. - **Key signals**: `RegFile.rs1/rs2`, `ImmGen` (B/I type), `Branch`, `RegWrite`. - **Here**: `beq x7,x0,+0x24` reads `x7` (exponent) and `x0`; `Branch=1`. No stall; forwarding covers hazards. ![image](https://hackmd.io/_uploads/HyiHxh9pgl.png) * **EX (Execution)** - **Role**: ALU ops, branch compare, target add. - **Key signals**: `ALUSrc`, compare unit, `BranchTaken`, target adder. - **Here**: `bltu x29,x7` sets `BranchTaken=1` since `e < exponent`. ![image](https://hackmd.io/_uploads/S1jdxnqpee.png) * **MEM (Memory Read/Write)** - **Role**: Data memory for loads/stores. - **Key signals**: `MemRead`, `MemWrite`. - **Here**: For `addi/branch` both 0. Actual writes appear in prologues/epilogues (`sw/lw`) and test locals (`sb/sw` at `s0` offsets). ![image](https://hackmd.io/_uploads/BJgox256el.png) * **WB (Register Write Back)** - **Role**: Write ALU or load result to `rd`. - **Key signals**: `RegWrite=1` for `addi`, WB mux selects ALU path. - **Here**: `x29 ← x29+1` and `x28 ← x28+16` update loop state. ![Screenshot 2025-10-14 010256](https://hackmd.io/_uploads/ryhSW35Tlx.png) --- #### Instruction-level intent in uf8_encode ![image](https://hackmd.io/_uploads/H1ciZ6cagx.png) - **Estimate** - If `msb ≤ 4` → go refine; else `t2 = msb − 4`, cap at 15. - **Build overflow** (`estimate_exponent_and_overflow`) - Loop while `t4 < t2`: `t3 = (t3<<1) + 16; t4++`. - **Adjust** (`adjust_e_o`) - If `t2 == 0` or `value ≥ overflow` → continue. - Else: `t3 = (t3 − 16) >> 1; t2--` and repeat. - **Refine** (`find_exact_exponent`) - `t5 = (t3<<1) + 16`. - If `value ≥ t5` and `t2 ≤ 14` → `t3=t5; t2++`; repeat. - **Finalize** - `t6 = ((value − overflow) >> t2) & 0x0f`. - Return `(t2<<4) | t6` (sign-extended pairs trimmed by `slli/srai`). --- #### Memory updates and correctness ![Screenshot 2025-10-14 003225](https://hackmd.io/_uploads/SybkZa9pxl.png) - **Stack frames** - `clz`: `addi sp,-8` → `sw ra,s0` → restore with `lw` and `addi sp,+8`. - `uf8_decode/encode`: similar `-8/−12` frames; balanced restores. No leaks. - **Test locals** - `-29(s0)` current byte, `-21(s0)` flag, `-20(s0)` previous value, `-28(s0)` index. - Accessed via matching `lbu/sb` and `lw/sw`. No aliasing. - **.data strings** - Read-only, printed by `ecall 4`. No stores. ---- ## Problem \(C\) BFloat16 ### Implementation - Special Cases Detection #### Source C code in Problem C ```clike= #include <stdbool.h> #include <stdint.h> #include <string.h> #include <stdio.h> typedef struct { uint16_t bits; } bf16_t; #define BF16_EXP_MASK 0x7F80U #define BF16_MANT_MASK 0x007FU static inline bool bf16_isnan(bf16_t a) { return ((a.bits & BF16_EXP_MASK) == BF16_EXP_MASK) && (a.bits & BF16_MANT_MASK); } static inline bool bf16_isinf(bf16_t a) { return ((a.bits & BF16_EXP_MASK) == BF16_EXP_MASK) && !(a.bits & BF16_MANT_MASK); } static inline bool bf16_iszero(bf16_t a) { return !(a.bits & 0x7FFF); } ``` #### RV32 Assembly - Compiler Generate (rv32-gcc (trunk)) ```gas= # RV32 register-variable mapping (across functions) # a0 : function argument / return value # a4 : temporary for masked bits or compares # a5 : primary temporary / boolean result # s0 : frame pointer # ra : return address # stack frame layout (offsets from s0) # -20 : local bf16 input # -24 : s0 saved # -28 : ra saved # bf16_isnan(bf16 a): return ((a & EXP)==EXP) && ((a & MANT)!=0) bf16_isnan: addi sp,sp,-32 sw ra,28(sp) sw s0,24(sp) addi s0,sp,32 # Load input into stack-local; compute (a & 0x7F80) then test equals 0x7F80; then test mantissa != 0; return 1/0 sh a0,-20(s0) lhu a5,-20(s0) mv a4,a5 li a5,32768 addi a5,a5,-128 and a4,a4,a5 li a5,32768 addi a5,a5,-128 bne a4,a5,L2 lhu a5,-20(s0) andi a5,a5,127 beq a5,zero,L2 li a5,1 j L3 L2: li a5,0 L3: # Normalize boolean to 0/1 and return andi a5,a5,1 andi a5,a5,0xff mv a0,a5 lw ra,28(sp) lw s0,24(sp) addi sp,sp,32 jr ra # bf16_isinf(bf16 a): return ((a & EXP)==EXP) && ((a & MANT)==0) bf16_isinf: addi sp,sp,-32 sw ra,28(sp) sw s0,24(sp) addi s0,sp,32 # Load input; test exponent all-ones; then test mantissa equals zero; return 1/0 sh a0,-20(s0) lhu a5,-20(s0) mv a4,a5 li a5,32768 addi a5,a5,-128 and a4,a4,a5 li a5,32768 addi a5,a5,-128 bne a4,a5,L6 lhu a5,-20(s0) andi a5,a5,127 bne a5,zero,L6 li a5,1 j L7 L6: li a5,0 L7: # Normalize boolean to 0/1 and return andi a5,a5,1 andi a5,a5,0xff mv a0,a5 lw ra,28(sp) lw s0,24(sp) addi sp,sp,32 jr ra # bf16_iszero(bf16 a): return (a & 0x7FFF) == 0 bf16_iszero: addi sp,sp,-32 sw ra,28(sp) sw s0,24(sp) addi s0,sp,32 # Load input; clear sign bit; test zero; return 1/0 sh a0,-20(s0) lhu a5,-20(s0) slli a5,a5,17 srli a5,a5,17 seqz a5,a5 andi a5,a5,0xff mv a0,a5 lw ra,28(sp) lw s0,24(sp) addi sp,sp,32 jr ra ``` #### RV32 Assembly- Optmized hand-written ```gas= # RV32 register-variable mapping (across functions) # a0: input bfloat16 number a and return value (1 if NaN, 0 otherwise) # s0: stack frame pointer # ra: return address # Stack frame layout (offsets from s0, across functions): # -8 : saved s0 # -4 : saved ra bf16_isnan: # Register Role: # t0: store result of a & Exp_mask (0x7F80) # t1: store result of a & Mant_mask (0x007F) # t2: store Exp_mask (0x7F80) addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 li t2,32640 # Load Exp_mask into t2 and t0, a0, t2 # t0 = a & Exp_mask beq t0, t2, isnan_check_mantissa # If (a & Exp_mask) == Exp_mask, check mantissa li a0, 0 # Not NaN j end_bf16_isnan isnan_check_mantissa: andi t1, a0, 127 # t1 = a & Mant_mask bne t1, zero, is_nan # If (a & Mant_mask) != 0, it is NaN li a0, 0 # Not NaN j end_bf16_isnan is_nan: li a0, 1 # Is NaN end_bf16_isnan: lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 jr ra bf16_isinf: # Register Role: # t0: store result of a & Exp_mask (0x7F80) # t1: store result of !(a & Mant_mask (0x007F)) # t2: store Exp_mask (0x7F80) addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 li t2,32640 # Load Exp_mask into t2 and t0, a0, t2 # t0 = a & Exp_mask beq t0, t2, isinf_check_mantissa # If (a & Exp_mask) == Exp_mask, check mantissa li a0, 0 # Not Inf j end_bf16_isinf isinf_check_mantissa: andi t1, a0, 127 # t1 = a & Mant_mask beq t1, zero, is_inf # If (a & Mant_mask) == 0, it is Inf li a0, 0 # Not Inf j end_bf16_isinf is_inf: li a0, 1 # Is Inf end_bf16_isinf: lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 jr ra bf16_iszero: # Register Role: # t0: store result of !(a & ~Sign_mask (0x7FFF)) addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 li t0, 32767 # Load ~Sign_mask into t0 and t0, a0, t0 # t0 = a & ~Sign_mask xori t0, t0, 1 # Negate the result for zero check add a0, t0, zero # Move result to a0 lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 jr ra ``` --- ### Implementation - BF16 and FP16 Conversion #### Source C code in Problem C ```clike= #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <math.h> typedef struct { uint16_t bits; } bf16_t; static inline bf16_t f32_to_bf16(float val) { uint32_t f32bits; memcpy(&f32bits, &val, sizeof(float)); if (((f32bits >> 23) & 0xFF) == 0xFF) return (bf16_t) {.bits = (f32bits >> 16) & 0xFFFF}; f32bits += ((f32bits >> 16) & 1) + 0x7FFF; // For Rounding to Nearest, Ties to Even return (bf16_t) {.bits = f32bits >> 16}; } static inline float bf16_to_f32(bf16_t val) { uint32_t f32bits = ((uint32_t) val.bits) << 16; float result; memcpy(&result, &f32bits, sizeof(float)); return result; } ``` #### RV32 Assembly - Compiler Generate (rv32-gc clang (trunk)) ```gas= # ----------------------------------------------------------------------------- # float bf16_to_f32(bf16 val) # Build 32-bit pattern by placing bf16 in high 16 bits, then bit-cast to float. # Registers: # a0 : arg bf16.bits (low 16 in a0) / returns f32bits # Stack frame (32B): # -10(s0) : bf16.bits (int16_t local copy) # -16(s0) : f32bits (uint32_t) # -20(s0) : result copy (uint32_t) bf16_to_f32: addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 # f32bits = (uint32)val.bits << 16; return as float bit pattern sh a0, -10(s0) lh a0, -10(s0) slli a0, a0, 16 sw a0, -16(s0) lw a0, -16(s0) sw a0, -20(s0) lw a0, -20(s0) lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # ----------------------------------------------------------------------------- # bf16_t f32_to_bf16(float val) # If exp==0xFF → just truncate top 16 bits (Inf/NaN preserve payload). # Else round-to-nearest-even: add LSB of cut part plus 0x7FFF, then take top 16. # Registers: # a0 : arg f32bits / returns bf16.bits (in low 16) # a1 : temps (exp calc, constants) # Stack frame (32B): # -16(s0) : f32bits # -20(s0) : f32bits (working) # -10(s0) : bf16.bits (uint16_t out) # -18(s0) : scratch (unused load/store pair for flow) f32_to_bf16: addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 # spill input bits sw a0, -16(s0) lw a0, -16(s0) sw a0, -20(s0) # if (((f32bits>>23)&0xFF)==0xFF) → fast path: return high16 lw a0, -20(s0) slli a0, a0, 1 srli a0, a0, 24 li a1, 255 bne a0, a1, LBB2_2 LBB2_1: # Inf/NaN path: return (f32bits >> 16) lhu a0, -18(s0) # (compiler artifact; final move happens below) sh a0, -10(s0) j LBB2_3 LBB2_2: # RNE rounding path: # f32bits += ((f32bits>>16)&1) + 0x7FFF lw a1, -20(s0) slli a0, a1, 15 srli a0, a0, 31 # ((f32bits>>16)&1) add a0, a0, a1 lui a1, 8 addi a1, a1, -1 # 0x7FFF add a0, a0, a1 sw a0, -20(s0) # produce top 16 bits lhu a0, -18(s0) # (compiler artifact; replaced by final load) sh a0, -10(s0) LBB2_3: # return bf16 bits (top 16 of updated f32bits) lhu a0, -10(s0) lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret ``` #### RV32 Assembly- Optmized hand-written ```gas= bf16_to_f32: # Initialize stack frame (16B) addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 # Shifting 16 bits left to convert bf16 to f32 slli a0, a0, 16 # epilogue lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 jr ra f32_to_bf16: # Register Roles: # t0: saved result of input value of (f32bits >> 23) & 0xFF # t1: saved constant 0xFF # t2: saved result of input value of (f32bits >> 16) & 0x7FFF # Initialize stack frame (16B) addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 add t0, a0, zero # Calculate (f32bits >> 23) & 0xFF srli t0, t0, 23 andi t0, t0, 0xFF li t1, 0xFF beq t0, t1, handling_inf_nan # Not Inf or NaN case # Rounding to Nearest, Ties to Even srli t2, t0, 16 andi t2, t2, 1 li t2, 32767 add a0, a0, t2 # Shift right 16 to convert f32 to bf16 srli a0, a0, 16 j f32_to_bf16_epilogue handling_inf_nan: # Inf or NaN case: just shift right 16 bits to convert f32 to bf16 srli a0, a0, 16 li a0, 65535 f32_to_bf16_epilogue: # epilogue lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 jr ra ``` --- ### Implementation - BF16 Addition and Substraction #### Source C code in Problem C ```clike= #include <stdbool.h> #include <stdint.h> #include <string.h> #include <stdio.h> typedef struct { uint16_t bits; } bf16_t; #define BF16_SIGN_MASK 0x8000U #define BF16_NAN() ((bf16_t) {.bits = 0x7FC0}) #define BF16_ZERO() ((bf16_t) {.bits = 0x0000}) static inline bf16_t bf16_add(bf16_t a, bf16_t b) { uint16_t sign_a = (a.bits >> 15) & 1; uint16_t sign_b = (b.bits >> 15) & 1; int16_t exp_a = ((a.bits >> 7) & 0xFF); int16_t exp_b = ((b.bits >> 7) & 0xFF); uint16_t mant_a = a.bits & 0x7F; uint16_t mant_b = b.bits & 0x7F; if (exp_a == 0xFF) { if (mant_a) return a; if (exp_b == 0xFF) return (mant_b || sign_a == sign_b) ? b : BF16_NAN(); return a; } if (exp_b == 0xFF) return b; if (!exp_a && !mant_a) return b; if (!exp_b && !mant_b) return a; if (exp_a) mant_a |= 0x80; if (exp_b) mant_b |= 0x80; int16_t exp_diff = exp_a - exp_b; uint16_t result_sign; int16_t result_exp; uint32_t result_mant; if (exp_diff > 0) { result_exp = exp_a; if (exp_diff > 8) // When b is too small to a to affect the result, return a return a; mant_b >>= exp_diff; } else if (exp_diff < 0) { result_exp = exp_b; if (exp_diff < -8) return b; mant_a >>= -exp_diff; } else { result_exp = exp_a; } if (sign_a == sign_b) { result_sign = sign_a; result_mant = (uint32_t) mant_a + mant_b; if (result_mant & 0x100) { result_mant >>= 1; if (++result_exp >= 0xFF) return (bf16_t) {.bits = (result_sign << 15) | 0x7F80}; } } else { if (mant_a >= mant_b) { result_sign = sign_a; result_mant = mant_a - mant_b; } else { result_sign = sign_b; result_mant = mant_b - mant_a; } if (!result_mant) return BF16_ZERO(); while (!(result_mant & 0x80)) { result_mant <<= 1; if (--result_exp <= 0) return BF16_ZERO(); } } return (bf16_t) { .bits = (result_sign << 15) | ((result_exp & 0xFF) << 7) | (result_mant & 0x7F), }; } static inline bf16_t bf16_sub(bf16_t a, bf16_t b) { b.bits ^= BF16_SIGN_MASK; return bf16_add(a, b); } ``` #### RV32 Assembly - Compiler Generate (rv32-gc clang (trunk)) * Addition ```gas= ############################################################ # bf16_add # ABI: a0=input a.bits (u16), a1=input b.bits (u16), a0=return bits (u16) # Callee-saved: s0. Caller-saved: a0–a7. # # Stack frame (48 bytes): # [sp+44] ra # [sp+40] s0 # Locals (relative to s0): # -10: u16 result_bits # -12: u16 a_bits # -14: u16 b_bits # -16: u16 sign_a # -18: u16 sign_b # -20: i16 exp_a # -22: i16 exp_b # -24: u16 mant_a (7-bit field, later with implicit 1) # -26: u16 mant_b (7-bit field, later with implicit 1) # -28: i16 exp_diff # -30: u16 result_sign # -32: i16 result_exp # -36..-33: u32 result_mant (temp, uses word ops) # # Sections: # Prologue: create frame, spill a/b. ############################################################ bf16_add: addi sp, sp, -48 sw ra, 44(sp) sw s0, 40(sp) addi s0, sp, 48 sh a0, -12(s0) sh a1, -14(s0) ############################################################ # Unpack fields: # sign_a/sign_b = bits>>15 & 1 # exp_a/exp_b = bits[14:7] # mant_a/mant_b = bits[6:0] ############################################################ lhu a0, -12(s0) srli a0, a0, 15 sh a0, -16(s0) lhu a0, -14(s0) srli a0, a0, 15 sh a0, -18(s0) lhu a0, -12(s0) slli a0, a0, 17 srli a0, a0, 24 sh a0, -20(s0) lhu a0, -14(s0) slli a0, a0, 17 srli a0, a0, 24 sh a0, -22(s0) lhu a0, -12(s0) andi a0, a0, 127 sh a0, -24(s0) lhu a0, -14(s0) andi a0, a0, 127 sh a0, -26(s0) ############################################################ # Special cases on a: # if exp_a==0xFF: # if mant_a!=0 -> return a (NaN) # else if exp_b==0xFF: # if mant_b!=0 or sign_a==sign_b -> return b # else -> return qNaN # else -> return a (Inf) ############################################################ lh a0, -20(s0) li a1, 255 bne a0, a1, LBB1_10 LBB1_1: lhu a0, -24(s0) beqz a0, LBB1_3 LBB1_2: lh a0, -12(s0) sh a0, -10(s0) j LBB1_50 LBB1_3: lh a0, -22(s0) li a1, 255 bne a0, a1, LBB1_9 LBB1_4: lhu a0, -26(s0) bnez a0, LBB1_6 LBB1_5: lhu a0, -16(s0) lhu a1, -18(s0) bne a0, a1, LBB1_7 LBB1_6: lh a0, -14(s0) # return b (Inf or NaN) sh a0, -10(s0) j LBB1_8 LBB1_7: lui a0, 8 # return qNaN 0x7FC0 addi a0, a0, -64 sh a0, -10(s0) LBB1_8: j LBB1_50 LBB1_9: lh a0, -12(s0) # return a (Inf) sh a0, -10(s0) j LBB1_50 ############################################################ # Special cases on b: # if exp_b==0xFF -> return b # if a==0 -> return b # if b==0 -> return a # If normalized, set implicit 1: mant|=0x80 ############################################################ LBB1_10: lh a0, -22(s0) li a1, 255 bne a0, a1, LBB1_12 LBB1_11: lh a0, -14(s0) # return b sh a0, -10(s0) j LBB1_50 LBB1_12: lhu a0, -20(s0) bnez a0, LBB1_15 LBB1_13: lhu a0, -24(s0) bnez a0, LBB1_15 LBB1_14: lh a0, -14(s0) # a==0 -> return b sh a0, -10(s0) j LBB1_50 LBB1_15: lhu a0, -22(s0) bnez a0, LBB1_18 LBB1_16: lhu a0, -26(s0) bnez a0, LBB1_18 LBB1_17: lh a0, -12(s0) # b==0 -> return a sh a0, -10(s0) j LBB1_50 LBB1_18: lhu a0, -20(s0) beqz a0, LBB1_20 LBB1_19: lh a0, -24(s0) # mant_a |= 0x80 ori a0, a0, 128 sh a0, -24(s0) LBB1_20: lhu a0, -22(s0) beqz a0, LBB1_22 LBB1_21: lh a0, -26(s0) # mant_b |= 0x80 ori a0, a0, 128 sh a0, -26(s0) ############################################################ # Exponent alignment: # exp_diff = exp_a - exp_b # If exp_diff>0: # result_exp=exp_a; if exp_diff>=9 -> return a; shift mant_b>>=exp_diff # Else if exp_diff<0: # result_exp=exp_b; if exp_diff<=-9 -> return b; shift mant_a>>=-exp_diff # Else result_exp=exp_a ############################################################ LBB1_22: lh a0, -20(s0) lh a1, -22(s0) sub a0, a0, a1 sh a0, -28(s0) lh a1, -28(s0) li a0, 0 bge a0, a1, LBB1_26 # exp_diff <= 0? LBB1_23: # exp_diff > 0 lh a0, -20(s0) sh a0, -32(s0) # result_exp = exp_a lh a0, -28(s0) li a1, 9 blt a0, a1, LBB1_25 LBB1_24: # gap >= 9 -> return a lh a0, -12(s0) sh a0, -10(s0) j LBB1_50 LBB1_25: lh a1, -28(s0) lhu a0, -26(s0) srl a0, a0, a1 # mant_b >>= exp_diff sh a0, -26(s0) j LBB1_32 LBB1_26: # exp_diff <= 0 lh a0, -28(s0) bgez a0, LBB1_30 # exp_diff == 0 LBB1_27: # exp_diff < 0 lh a0, -22(s0) sh a0, -32(s0) # result_exp = exp_b lh a1, -28(s0) li a0, -9 blt a0, a1, LBB1_29 LBB1_28: # gap <= -9 -> return b lh a0, -14(s0) sh a0, -10(s0) j LBB1_50 LBB1_29: lh a1, -28(s0) li a0, 0 sub a1, a0, a1 # -exp_diff lhu a0, -24(s0) srl a0, a0, a1 # mant_a >>= -exp_diff sh a0, -24(s0) j LBB1_31 LBB1_30: # exp_diff == 0 lh a0, -20(s0) sh a0, -32(s0) # result_exp = exp_a LBB1_31: j LBB1_32 ############################################################ # Same-sign path: # if sign_a==sign_b: # result_sign=sign_a # result_mant=mant_a+mant_b # if carry -> shift right, result_exp++ # if result_exp>=0xFF -> return signed Inf ############################################################ LBB1_32: lhu a0, -16(s0) lhu a1, -18(s0) bne a0, a1, LBB1_38 LBB1_33: lh a0, -16(s0) sh a0, -30(s0) # result_sign lhu a0, -24(s0) lhu a1, -26(s0) add a0, a0, a1 sw a0, -36(s0) # result_mant lbu a0, -35(s0) andi a0, a0, 1 beqz a0, LBB1_37 # no carry LBB1_34: # carry path lw a0, -36(s0) srli a0, a0, 1 # mant >>= 1 sw a0, -36(s0) lh a1, -32(s0) slli a0, a1, 16 addi a1, a1, 1 # result_exp++ sh a1, -32(s0) lui a1, 16 add a0, a0, a1 srai a0, a0, 16 li a1, 255 blt a0, a1, LBB1_36 LBB1_35: # overflow -> signed Inf lh a0, -30(s0) slli a0, a0, 15 lui a1, 8 addi a1, a1, -128 # 0x7F80 or a0, a0, a1 sh a0, -10(s0) j LBB1_50 LBB1_36: j LBB1_37 LBB1_37: j LBB1_49 ############################################################ # Different-sign path: # subtract larger mantissa from smaller # result_sign = sign of larger # if zero -> return +0 # normalize left until bit7 set, decrement result_exp each shift # if result_exp <= 0 -> return 0 ############################################################ LBB1_38: lhu a0, -24(s0) lhu a1, -26(s0) blt a0, a1, LBB1_40 LBB1_39: # mant_a >= mant_b lh a0, -16(s0) sh a0, -30(s0) # result_sign = sign_a lhu a0, -24(s0) lhu a1, -26(s0) sub a0, a0, a1 sw a0, -36(s0) # result_mant j LBB1_41 LBB1_40: # mant_b > mant_a lh a0, -18(s0) sh a0, -30(s0) # result_sign = sign_b lhu a0, -26(s0) lhu a1, -24(s0) sub a0, a0, a1 sw a0, -36(s0) LBB1_41: lw a0, -36(s0) bnez a0, LBB1_43 # non-zero LBB1_42: li a0, 0 # exact cancel -> +0 sh a0, -10(s0) j LBB1_50 LBB1_43: # normalize left LBB1_44: lbu a0, -36(s0) andi a0, a0, 128 # test bit7 bnez a0, LBB1_48 LBB1_45: lw a0, -36(s0) slli a0, a0, 1 sw a0, -36(s0) lh a1, -32(s0) slli a0, a1, 16 addi a1, a1, -1 # result_exp-- sh a1, -32(s0) lui a1, 1048560 # bounds check (<=0) -> zero add a0, a0, a1 srai a1, a0, 16 li a0, 0 blt a0, a1, LBB1_47 LBB1_46: li a0, 0 sh a0, -10(s0) j LBB1_50 LBB1_47: j LBB1_44 ############################################################ # Pack result: # bits = (result_sign<<15) | (result_exp<<7) | (result_mant & 0x7F) ############################################################ LBB1_48: j LBB1_49 LBB1_49: lh a0, -30(s0) slli a0, a0, 15 lbu a1, -32(s0) slli a1, a1, 7 or a0, a0, a1 lw a1, -36(s0) andi a1, a1, 127 or a0, a0, a1 sh a0, -10(s0) ############################################################ # Epilogue: move result to a0 and return. ############################################################ LBB1_50: lhu a0, -10(s0) lw ra, 44(sp) lw s0, 40(sp) addi sp, sp, 48 ret ``` * Substraction ```gas= ############################################################ # bf16_sub # ABI: a0=a.bits, a1=b.bits. Returns a0 = (a - b).bits. # # Stack frame (16 bytes): # [sp+12] ra # [sp+8] s0 # Locals: # -12: u16 a_bits # -14: u16 b_bits (to be negated) # -10: u16 result_bits # # Sections: # Prologue: save inputs. # Flip sign of b (xor with 0x8000). # Call bf16_add(a, -b). # Epilogue: return result. ############################################################ bf16_sub: addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) addi s0, sp, 16 sh a0, -12(s0) sh a1, -14(s0) lh a0, -14(s0) lui a1, 8 # 0x8000 xor a0, a0, a1 # negate b sh a0, -14(s0) lhu a0, -12(s0) lhu a1, -14(s0) call bf16_add sh a0, -10(s0) lhu a0, -10(s0) lw ra, 12(sp) lw s0, 8(sp) addi sp, sp, 16 ret ``` #### RV32 Assembly- Optmized hand-written * Addition ```gas= # bf16_add # ABI: # a0=input a.bits (u16), a1=input b.bits (u16), a0=return bits (u16) # Register Role: # t0: sign_a # t1: sign_b # t2: exp_a # t3: exp_b # t4: mant_a # t5: mant_b # t6: exp_diff # a2: result sign # a3: result exp # a4: result mant # a5: temporary constant/ result bf16_add: addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 # unpack srli t0,a0,15 andi t0,t0,1 srli t1,a1,15 andi t1,t1,1 srli t2,a0,7 andi t2,t2,0xFF srli t3,a1,7 andi t3,t3,0xFF andi t4,a0,0x7F andi t5,a1,0x7F # ---- Special: exp_a==0xFF ---- li a5,0xFF bne t2,a5, L_check_b_ff # exp_a==0xFF bnez t4, L_ret_a # mant_a!=0 => NaN, return a # mant_a==0 => a=Inf bne t3,a5, L_ret_a # b not Special => return a (Inf) # both Special bnez t5, L_ret_b # b NaN => return b # mant_b==0, both Inf: if sign_a==sign_b return b else qNaN bne t0,t1, L_ret_qnan j L_ret_b # ---- Special: exp_b==0xFF ---- L_check_b_ff: li a5,0xFF bne t3,a5, L_check_a_zero L_ret_b: add a0,zero,a1 j L_epilogue L_ret_a: add a0,zero,a0 j L_epilogue L_ret_qnan: li a0,0x7FC0 j L_epilogue # ---- a==0? (!exp_a && !mant_a) -> b ---- L_check_a_zero: beqz t2, 1f j L_check_b_zero 1: beqz t4, L_ret_b j L_check_b_zero # ---- b==0? (!exp_b && !mant_b) -> a ---- L_check_b_zero: beqz t3, 2f j L_set_implicit 2: beqz t5, L_ret_a # ---- implicit leading 1 when normalized ---- L_set_implicit: beqz t2, 3f ori t4,t4,0x80 3: beqz t3, 4f ori t5,t5,0x80 4: # ---- exponent alignment ---- sub t6,t2,t3 # exp_diff bgtz t6, L_diff_pos bltz t6, L_diff_neg add a3,zero,t2 # diff==0 j L_sign_check L_diff_pos: add a3,zero,t2 li a5,9 bge t6,a5, L_ret_a # diff>=9 -> return a srl t5,t5,t6 # mant_b >>= diff j L_sign_check L_diff_neg: add a3,zero,t3 li a5,-8 blt t6,a5, L_ret_b # diff<-8 -> return b neg a5,t6 # a5 = -diff srl t4,t4,a5 # mant_a >>= -diff # ---- sign check ---- L_sign_check: bne t0,t1, L_sign_diff # same sign: add add a2,zero,t0 add a4,t4,t5 # 0..510 andi a5,a4,0x100 # carry? beqz a5, L_pack srli a4,a4,1 # >>1 addi a3,a3,1 # exp++ li a5,0xFF blt a3,a5, L_pack # if exp<255 ok # overflow -> signed Inf slli a0,a2,15 li a5,0x7F80 or a0,a0,a5 j L_epilogue # different sign: subtract larger mant L_sign_diff: blt t4,t5, 5f add a2,zero,t0 sub a4,t4,t5 j 6f 5: add a2,zero,t1 sub a4,t5,t4 6: beqz a4, L_ret_zero # normalize left until bit7 set; exp-- L_norm: andi a5,a4,0x80 bnez a5, L_pack slli a4,a4,1 addi a3,a3,-1 blez a3, L_ret_zero j L_norm # pack result L_pack: slli a0,a2,15 andi a3,a3,0xFF slli a5,a3,7 or a0,a0,a5 andi a4,a4,0x7F or a0,a0,a4 j L_epilogue L_ret_zero: add a0,zero,zero L_epilogue: lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 ret ``` * Substraction ```gas= bf16_sub: addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 xori a1,a1,0x8000 call bf16_add lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 ret ``` --- ### Implementation - BF16 Multiplication, Division, Square Root * Due to limitation number of characters in HackMD, I only show the bp_16 optimzation code and processes, the whole code detail is in [bfloat16](https://github.com/jgw0915/Computer-Architecture/tree/dev/bfloat16) #### Source C code in Problem C * Square Root ```clike= static inline bf16_t bf16_sqrt(bf16_t a) { uint16_t sign = (a.bits >> 15) & 1; int16_t exp = ((a.bits >> 7) & 0xFF); uint16_t mant = a.bits & 0x7F; if (exp == 0xFF) { if (mant) return a; /* NaN */ if (sign) return BF16_NAN(); /* sqrt(-Inf) = NaN */ return a; /* +Inf */ } if (!exp && !mant) return BF16_ZERO(); /* sqrt(0) = 0 */ if (sign) return BF16_NAN(); /* sqrt(negative) = NaN */ if (!exp) return BF16_ZERO(); /* flush denormals */ int32_t e = exp - BF16_EXP_BIAS; int32_t new_exp; uint32_t m = 0x80 | mant; if (e & 1) { m <<= 1; new_exp = ((e - 1) >> 1) + BF16_EXP_BIAS; } else { new_exp = (e >> 1) + BF16_EXP_BIAS; } uint32_t low = 90, high = 256, result = 128; while (low <= high) { uint32_t mid = (low + high) >> 1; uint32_t sq = mul16x16_u32((uint16_t)mid, (uint16_t)mid) >> 7; if (sq <= m) { result = mid; low = mid + 1; } else { high = mid - 1; } } if (result >= 256) { result >>= 1; new_exp++; } else if (result < 128) { while (result < 128 && new_exp > 1) { result <<= 1; new_exp--; } } uint16_t new_mant = result & 0x7F; if (new_exp >= 0xFF) return (bf16_t){ .bits = 0x7F80 }; /* +Inf */ if (new_exp <= 0) return BF16_ZERO(); /* 正常回傳 */ return (bf16_t){ .bits = ((uint16_t)0 << 15) | (((uint16_t)new_exp & 0xFF) << 7) | new_mant }; } ``` #### RV32 Assembly - Compiler Generate (rv32-gcc (trunk)) * Square Root ```gas= bf16_sqrt: addi sp, sp, -64 sw ra, 60(sp) sw s0, 56(sp) addi s0, sp, 64 sh a0, -12(s0) lhu a0, -12(s0) srli a0, a0, 15 sh a0, -14(s0) lhu a0, -12(s0) slli a0, a0, 17 srli a0, a0, 24 sh a0, -16(s0) lhu a0, -12(s0) andi a0, a0, 127 sh a0, -18(s0) lh a0, -16(s0) li a1, 255 bne a0, a1, LBB3_6 j LBB3_1 LBB3_1: lhu a0, -18(s0) beqz a0, LBB3_3 j LBB3_2 LBB3_2: lh a0, -12(s0) sh a0, -10(s0) j LBB3_37 LBB3_3: lhu a0, -14(s0) beqz a0, LBB3_5 j LBB3_4 LBB3_4: lui a0, 8 addi a0, a0, -64 sh a0, -10(s0) j LBB3_37 LBB3_5: lh a0, -12(s0) sh a0, -10(s0) j LBB3_37 LBB3_6: lhu a0, -16(s0) bnez a0, LBB3_9 j LBB3_7 LBB3_7: lhu a0, -18(s0) bnez a0, LBB3_9 j LBB3_8 LBB3_8: li a0, 0 sh a0, -10(s0) j LBB3_37 LBB3_9: lhu a0, -14(s0) beqz a0, LBB3_11 j LBB3_10 LBB3_10: lui a0, 8 addi a0, a0, -64 sh a0, -10(s0) j LBB3_37 LBB3_11: lhu a0, -16(s0) bnez a0, LBB3_13 j LBB3_12 LBB3_12: li a0, 0 sh a0, -10(s0) j LBB3_37 LBB3_13: lh a0, -16(s0) addi a0, a0, -127 sw a0, -24(s0) lhu a0, -18(s0) ori a0, a0, 128 sw a0, -32(s0) lbu a0, -24(s0) andi a0, a0, 1 beqz a0, LBB3_15 j LBB3_14 LBB3_14: lw a0, -32(s0) slli a0, a0, 1 sw a0, -32(s0) lw a0, -24(s0) addi a0, a0, -1 srai a0, a0, 1 addi a0, a0, 127 sw a0, -28(s0) j LBB3_16 LBB3_15: lw a0, -24(s0) srai a0, a0, 1 addi a0, a0, 127 sw a0, -28(s0) j LBB3_16 LBB3_16: li a0, 90 sw a0, -36(s0) li a0, 256 sw a0, -40(s0) li a0, 128 sw a0, -44(s0) j LBB3_17 LBB3_17: lw a1, -36(s0) lw a0, -40(s0) bltu a0, a1, LBB3_22 j LBB3_18 LBB3_18: lw a0, -36(s0) lw a1, -40(s0) add a0, a0, a1 srli a0, a0, 1 sw a0, -48(s0) lhu a1, -48(s0) mv a0, a1 call mul16x16_u32 srli a0, a0, 7 sw a0, -52(s0) lw a1, -52(s0) lw a0, -32(s0) bltu a0, a1, LBB3_20 j LBB3_19 LBB3_19: lw a0, -48(s0) sw a0, -44(s0) lw a0, -48(s0) addi a0, a0, 1 sw a0, -36(s0) j LBB3_21 LBB3_20: lw a0, -48(s0) addi a0, a0, -1 sw a0, -40(s0) j LBB3_21 LBB3_21: j LBB3_17 LBB3_22: lw a0, -44(s0) li a1, 256 bltu a0, a1, LBB3_24 j LBB3_23 LBB3_23: lw a0, -44(s0) srli a0, a0, 1 sw a0, -44(s0) lw a0, -28(s0) addi a0, a0, 1 sw a0, -28(s0) j LBB3_32 LBB3_24: lw a1, -44(s0) li a0, 127 bltu a0, a1, LBB3_31 j LBB3_25 LBB3_25: j LBB3_26 LBB3_26: lw a1, -44(s0) li a2, 0 li a0, 127 sw a2, -60(s0) bltu a0, a1, LBB3_28 j LBB3_27 LBB3_27: lw a0, -28(s0) slti a0, a0, 2 xori a0, a0, 1 sw a0, -60(s0) j LBB3_28 LBB3_28: lw a0, -60(s0) andi a0, a0, 1 beqz a0, LBB3_30 j LBB3_29 LBB3_29: lw a0, -44(s0) slli a0, a0, 1 sw a0, -44(s0) lw a0, -28(s0) addi a0, a0, -1 sw a0, -28(s0) j LBB3_26 LBB3_30: j LBB3_31 LBB3_31: j LBB3_32 LBB3_32: lw a0, -44(s0) andi a0, a0, 127 sh a0, -54(s0) lw a0, -28(s0) li a1, 255 blt a0, a1, LBB3_34 j LBB3_33 LBB3_33: lui a0, 8 addi a0, a0, -128 sh a0, -10(s0) j LBB3_37 LBB3_34: lw a1, -28(s0) li a0, 0 blt a0, a1, LBB3_36 j LBB3_35 LBB3_35: li a0, 0 sh a0, -10(s0) j LBB3_37 LBB3_36: lbu a0, -28(s0) slli a0, a0, 7 lh a1, -54(s0) or a0, a0, a1 sh a0, -10(s0) j LBB3_37 LBB3_37: lhu a0, -10(s0) lw ra, 60(sp) lw s0, 56(sp) addi sp, sp, 64 ret ``` #### RV32 Assembly- Optmized hand-written ```gas= bf16_sqrt: addi sp,sp,-16 sw ra,12(sp) mv a6,a0 # keep original a for NaN/+Inf returns # Extract fields srli t0,a0,15 # sign andi t0,t0,1 srli t1,a0,7 # exp andi t1,t1,255 andi t2,a0,127 # mant # exp == 0xFF ? li t3,255 beq t1,t3, case_exp_ff # zero? (exp==0 && mant==0) or t3,t1,t2 beqz t3, ret_zero # negative finite -> NaN bnez t0, ret_nan # denormal -> 0 beqz t1, ret_zero # e = exp - 127 ; m = 0x80 | mant addi t3,t1,-127 # e ori t4,t2,0x80 # m andi t2,t3,1 beqz t2, exp_even # e odd: m <<= 1 ; new_exp = ((e-1)>>1)+127 slli t4,t4,1 addi t3,t3,-1 srai t5,t3,1 # new_exp addi t5,t5,127 j exp_ready exp_even: srai t5,t3,1 addi t5,t5,127 exp_ready: # Binary search on [90,256], result=128 li a6,90 # low li a7,256 # high li a5,128 # result bin_loop_cond: bltu a7,a6, bin_done # while (low <= high) add a4,a6,a7 srli a4,a4,1 # mid mv a0,a4 # sq = (mid*mid)>>7 mv a1,a4 jal mul16x16_u32 srli a0,a0,7 bltu t4,a0, bin_high # if (m < sq) high=mid-1 mv a5,a4 # else result=mid, low=mid+1 addi a6,a4,1 j bin_loop_cond bin_high: addi a7,a4,-1 j bin_loop_cond bin_done: # Post-adjust result and exponent li t1,256 bltu a5,t1, check_small srli a5,a5,1 # result >>= 1 addi t5,t5,1 # new_exp++ j post_norm check_small: li t1,128 bgeu a5,t1, post_norm norm_small_loop: # while (result<128 && new_exp>1) bgeu a5,t1, post_norm slti t2,t5,2 bnez t2, post_norm slli a5,a5,1 addi t5,t5,-1 j norm_small_loop post_norm: andi a5,a5,127 # new_mant # Overflow/underflow on exponent li t1,255 bge t5,t1, ret_pos_inf # new_exp >= 255 slti t1,t5,1 bnez t1, ret_zero # new_exp <= 0 # Pack: sign=0 slli t5,t5,7 or a0,t5,a5 lw ra,12(sp) addi sp,sp,16 ret # --- Special cases --- case_exp_ff: # exp == 0xFF bnez t2, ret_a # NaN payload -> return a bnez t0, ret_nan # -Inf -> NaN mv a0,a6 # +Inf -> return a lw ra,12(sp) addi sp,sp,16 ret ret_a: mv a0,a6 lw ra,12(sp) addi sp,sp,16 ret ret_pos_inf: li a0,0x7F80 lw ra,12(sp) addi sp,sp,16 ret ret_zero: li a0,0 lw ra,12(sp) addi sp,sp,16 ret ret_nan: li a0,0x7FC0 lw ra,12(sp) addi sp,sp,16 ret ``` ---- ### Analysis #### Improvement comparing to compiler * **BF16 Special Cases Detection** ![image](https://hackmd.io/_uploads/rJ-h5nqTxx.png) * **BF16, FP32 Conversion** ![image](https://hackmd.io/_uploads/ry1i92qTxx.png) * **BF16 add/sub** ![image](https://hackmd.io/_uploads/S1wFc2qple.png) * **BF16 mul/div/sqrt** ![image](https://hackmd.io/_uploads/HkFHs2qagx.png) #### 5-Stage Pipeline Processor This section is the analysis of [bfloat16_mul_div_sqrt.s](https://github.com/jgw0915/Computer-Architecture/blob/main/bfloat16/bfloat16_mul_div_sqrt.s) in terms of processor, memory, instruction-level view. * **Context** - Program: drives three BF16 kernels: `bf16_mul`, `bf16_div`, `bf16_sqrt`. It writes temporaries on the stack, checks ranges, and returns pass/fail in `a0`. - Snapshot (from Ripes, bf16_div inner loop at `no_subtract`): ![Screenshot 2025-10-14 014957](https://hackmd.io/_uploads/rkeYxR3cpex.png) - **WB** `0x00000370`: `srli x14, x14, 1` - **MEM** `0x00000374`: `addi x12, x12, -1` - **EX** `0x00000378`: `bne x12, x0, -24 <div_loop>` - **ID** `0x0000037c`: `sub x6, x7, x28` - **IF** `0x00000380`: `addi x6, x6, 127` * **IF (Instruction Fetch)** - Inputs: `PC+4` vs branch target from EX via `PCSrc` mux. - Behavior: EX asserts **BranchTaken=1** for `bne`, so IF fetches `0x00000360` (target of `div_loop`). - Signals: `PC.en=1`, `PCSrc=branch`, I-mem read valid. ![image](https://hackmd.io/_uploads/H1070hqagx.png) * **ID (Instruction Decode)** - Current: `sub x6,x7,x28` (computing `result_exp = exp_a - exp_b`). - Actions: read `x7(t2)`, `x28(t3)`; generate R-type control; `RegWrite=1` scheduled; immediates idle. - Hazards: forwarding covers `x12` loop counter; no stall shown. ![image](https://hackmd.io/_uploads/H1LhR25alx.png) * **EX (Execution)** - Current: `bne x12,x0,-24`. - ALU/Branch: - Comparator sees `x12(a2) ≠ 0` → **BranchTaken=1**. - Target = `ID/EX.PC + B-imm(-24)` = `0x378 + (-0x18)` = `0x360`. - Control: `RegWrite=0`, `MemRead=0`, `MemWrite=0`. ![image](https://hackmd.io/_uploads/H1tRR29plx.png) * **MEM (Memory Read/Write)** - Current: `addi x12,x12,-1` in MEM stage. No D-mem access; WB source = ALU. - Elsewhere in program (actual memory writers): - Prologues: `sw ra,s0,...` create stack frames in `test_mul_div_sqrt`, `bf16_mul`, `bf16_sqrt`. - Temporaries: `sh`/`sb` to `-12,-14,-16,-18,-20,-22,-9(s0)` store halfwords/byte results and flags. - Epilogues: `lw`/`lhu`/`lbu` read back the same offsets. - Correctness: stores are matched by loads at identical offsets; stack frames are balanced by symmetric restores. ![image](https://hackmd.io/_uploads/BkAJ1a5age.png) * **WB (Register Write Back)** - Current: `srli x14,x14,1` writes normalized divisor (`x14=a4 >> 1`) back to `x14`. - WB mux: ALU result path; `RegWrite=1`; `rd=x14`. - Nearby writes (next cycles): `addi x12,x12,-1` writes the loop counter; `sub x6,x7,x28` then `addi x6,127` build exponent. ![image](https://hackmd.io/_uploads/ry1Ly65Tll.png) ##### Instruction-level intent (bf16_div inner loop) ![Screenshot 2025-10-14 015008](https://hackmd.io/_uploads/BkGKCn5peg.png) - Loop state: - `x14` (divisor_shifted) >>= 1 each iter. - `x12` (iteration counter) += −1; `bne x12,x0,-24` repeats 16 times. - After loop: - `sub x6,x7,x28` computes `exp_a - exp_b`; `addi x6,127` adds bias. - Ripes branch widget shows **taken** while `x12>0`, matching algorithm. --- ##### Memory map and updates (from Ripes “Memory viewer”) ![Screenshot 2025-10-14 015022](https://hackmd.io/_uploads/SJ5VZ6qale.png) - `.text` at `0x00000000–0x00000657` (size ≈ 1624). `.data` and `.bss` empty in this build. - Stack writes: - `test_mul_div_sqrt`: `sw ra,28(sp)`, `sw s0,24(sp)`, locals at `-32..-9(s0)`. - `bf16_mul/bf16_sqrt`: save/restore `ra,s0,s1,s2`; temporaries only in registers. - Endianness: little-endian; `sh` writes appear as low/high byte ordering in viewer. - Correctness: Every `addi sp, -N` is paired with `addi sp, +N`; all callee-saved regs restored before `ret`.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully