---
# System prepended metadata

title: Assignment 2

---

<h1> Assignment 2: Complete Applications</h1>

contributed by <[`hsuhsuhs`](https://github.com/hsuhsuhs/2025_NCKU_Computer_Architecture/tree/main/Assignment2)> 
(The complete source code is available on GitHub) 

---

[toc]


## Environment Setup
This project requires a RISC-V (RV32I) toolchain for cross-compilation and a compatible simulator to execute the bare-metal code.
### a. RISC-V GNU Toolchain
This is the cross-compiler, linker, and assembler needed to turn C and Assembly source code into a RISC-V ELF executable.

* **Toolchain:** `riscv-none-elf-gcc`
* **Installation:** I downloaded the pre-built binary for macOS (x86_64-apple-darwin) provided by the course.
* **Configuration:** After unzipping the toolchain to `$HOME/riscv-none-elf-gcc`, I updated my shell's `PATH` environment variable to include it. This step is crucial for the `make` command to find the correct compiler.

    ```bash
    # This command must be run in every new terminal session
    source $HOME/riscv-none-elf-gcc/setenv
    ```

### b. RISC-V Simulator (rv32emu)

This is the "virtual hardware" that will run our `test.elf` executable.

* **Simulator:** `rv32emu`
* **Installation:** I cloned the `rv32emu` repository from GitHub:

    ```bash
    git clone https://github.com/sysprog21/rv32emu.git
    cd rv32emu
    ```

* **Configuration :**
    The project requires System Emulation (for `ecall` support, enabling `printf`) and **Cycle-Accurate Profiling** (for `rdcycle` / `get_cycles()`). These are not enabled by default.

    I built the "deluxe" version of the emulator using the following command, as required by Lab 2 and the project specifications:

    ```bash
    # Clean any previous builds
    make distclean
    
    # Build with System and ELF Loader features enabled
    make ENABLE_SYSTEM=1 ENABLE_ELF_LOADER=1
    ```

* **Verification:** This process created the simulator executable at `build/rv32emu`. I verified the complete setup by successfully compiling and running the `tests/system/playground` example.

# HOMEWORK1_B 
## hw1_B.c
This section describes the process of porting `hw1_B.c` to the `rv32emu` bare-metal environment and establishing a baseline performance metric.
### a. Porting Process 
To run `hw1_B.c` in a bare-metal environment, several modifications were required, as there is no C Standard Library (libc).

1.  **Test Harness Creation:**
    * The `main()` function in `my_project/main.c` was designated as the Test Harness.
    * `hw1_B.c`'s original `main()` was renamed to `hw1_B_main()` to prevent a linker conflict.

2.  **Makefile Configuration:**
    * The `Makefile` was updated to include `hw1_B.o` in the `OBJS` list.
    * All `playground`-specific relative paths (e.g., `../../../build`) were corrected to `../build` to match the `my_project` directory structure.

3.  **Bare-Metal LibC Stubbing :**
    * The linker would fail because standard functions (`printf`, `puts`, `strlen`) are not available.
    * To solve this, I implemented "stubs" in `main.c` for:
        * **`printf(const char *format, ...)`**: A simplified version that only prints the format string.
        * **`puts(const char *s)`**: A version that prints the string and a newline.
        * **`strlen(const char *s)`**: A manual implementation to support `printf` and `puts`.
    * I also included `#include <stddef.h>` to provide the necessary `size_t` type.

4.  **Software Multiplication (RV32M Emulation):**
    * The project is compiled with `-march=rv32i_zicsr`, disabling the `M` extension (hardware multiplier).
    * `gcc` automatically converts `*` operations into calls to a helper function, `__mulsi3`.
    * To satisfy the linker, I retained the `umul` and `__mulsi3` software multiplication functions from the `playground`'s `main.c`.

### b. Baseline Performance 

After porting, the program was compiled with the default **`-g` (debug)** flag to establish a non-optimized baseline.

1. **Compiler Flags:**
`CFLAGS = -g -march=rv32i_zicsr`

2. **Result:**
The following output shows the program running correctly and the resulting cycle count.
   * cycle counts = $71506$
  ![截圖 2025-11-03 晚上11.29.17](https://hackmd.io/_uploads/rkJP9r8kWx.png)

### c-1. Optimization (optimized for speed)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Ofast` (optimize for speed).

1. **Compiler Flags:**
`CFLAGS = -Ofast -march=rv32i_zicsr`

2. **Result:**
After running `make clean` to force a re-compilation, the new results were as follows.
   * cycle counts = $18997$
   ![截圖 2025-11-04 凌晨12.36.01](https://hackmd.io/_uploads/S1NZ9I81Ze.png)


#### Analysis 1: Performance Comparison

This simple flag change resulted in a dramatic performance improvement:

| CFLAGS | Total Cycles | Speedup |
| :--- | :--- | :--- |
| `-g` (Baseline) | 71,506 | 1.00x |
| `-Ofast` (Optimized speed) | 18,997 | 3.76x |

#### Analysis 2: Over-Optimization Challenge (The `volatile` Fix)

A significant challenge occurred during the `-Ofast` test. The compiler was so aggressive that it identified my performance-measuring code (`get_cycles()`, `TEST_LOGGER`) as "Dead Code" and **optimized it away entirely**, resulting in no cycle count output.

Two fixes were required in `main.c` to prevent this:

1.  **`volatile uint64_t`**: I added the `volatile` keyword to `start_cycles` and `end_cycles`. This tells the compiler that these variables are important and must not be optimized away.
2.  **`asm "memory"` clber**: I added `"memory"` to the clobber list of the `printstr` assembly macro. This tells the compiler that the `ecall` has "side effects" (I/O) and that any code related to it (like `TEST_LOGGER`) must not be deleted.

#### Analysis 3: Assembly-Level Comparison (-g vs -Ofast)

To understand *how* the 3.76x speedup was achieved, I saved the assembly output for both versions and compared the `hw1_B_main` function.

**Observation 1: Register Allocation**
* **`-g` (Original):** The debug build is bloated with `sw` (store) and `lw` (load) instructions, especially at the start and end of the function (the "prologue" and "epilogue"). It spills *all* C variables from fast registers to the slow stack (memory) for debugging.
* **`-Ofast` (Optimized):** These `sw`/`lw` instructions are gone. The compiler keeps variables (like the loop counter `i` and `previous_value`) in fast CPU registers, eliminating slow memory acce*ss. This is a primary source of the speedup.

**Observation 2: Function Inlining**
* **`-g` (Original):** The `hw1_B.c` source code has a `for` loop that runs 256 times. Inside this loop, it calls `uf8_decode()` and `uf8_encode()` every single time. This results in at least `256 * 2 = 512` slow `jal` (call) instructions.
* **`-Ofast` (Optimized):** These `jal` instructions are gone. The compiler performed aggressive **"inlining"**—it "pasted" the assembly code from `test()`, `clz()`, `uf8_decode()`, and `uf8_encode()` directly into `hw1_B_main`.
### c-2. Optimization (optimize for Size)

To measure the impact of compiler optimization, I modified the Makefile to change the CFLAGS from -g (debug) to -Os (optimize for size). This flag instructs the compiler to prioritize making the final `test.elf` binary as small as possible, sometimes at the cost of speed.

1.  **Compiler Flags:**
`CFLAGS = -Os -march=rv32i_zicsr`

2.  **Result : Program Failure**
After running `make clean`, the program was re-compiled with `-Os`. However, this version **failed to execute** correctly.
    * Infinite Loop
![截圖 2025-11-04 凌晨2.36.44](https://hackmd.io/_uploads/rJ_ILOUJZl.png)


#### Analysis 1: 

This is the most significant finding. The program successfully printed `=== [Running hw1B] ===` (from `main.c`) but never returned from the `hw1_B_main()` call.

This proves that `gcc -Os` generated faulty  assembly code for `hw1_B.c`.

#### Analysis 2: The Compiler Optimization Bug 

By saving the assembly (`dump_hw1_B_size_Os.txt`) and analyzing `hw1_B_main`, I discovered the cause:

1.  **`-Os` (Optimize for Size)** decided NOT to "inline" the `uf8_decode` and `uf8_encode` functions because inlining would increase the file size. Instead, it kept the slow `jal` (call) instructions inside the 256-iteration `for` loop.
2.  At the same time, to save space, `-Os` also removed the "safe" but "bloated" register-saving code (`sw`/`lw`) that `-g` uses.
3.  **This created a bug:**
  *  The `uf8_encode` function (the "callee") uses the same registers as `hw1_B_main` (the "caller"). When `uf8_encode` runs, it corrupts the registers that `hw1_B_main` was using for its loop counter (`i`).
  *  When the `uf8_encode` function returns, the loop counter `i` is no longer correct, causing the loop condition (`i < 256`) to never be met, resulting in an **infinite loop**.

## hw1_B.S
This section describes the process of porting `hw1_B.S` to the rv32emu bare-metal environment and establishing a baseline performance metric.
### a. Integration Process

1. **Assembly Interface**: 
   * The `.global hw1_B_main` directive was used in `hw1_B.S` to export the label, making it visible to the linker.
   * The original `j` and `ecall`  instructions at the end of the logic were replaced with a `ret`  instruction. This allows control to be passed back to the `main()` C function.
2. **Makefile Configuration:**
   * Makefile was updated by add `hw1_B.o` in the OBJS list.
### b. Baseline Performance
After integration, the program was compiled with the Makefile's default `-g` flag to establish a non-optimized baseline for `hw1_B`

* **Compiler Flags:**
`CFLAGS = -g -march=rv32i_zicsr`
* **Result:**
The following output shows the `hw1_B` program running correctly and the resulting baseline cycle count.
  * cycle counts = $7134$
  * instruction counts = $7134$
  ![截圖 2025-11-08 清晨5.31.16](https://hackmd.io/_uploads/BJ0SUxnkWx.png)
  
### c-1. Optimization (optimized for speed)
To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Ofast` (optimize for speed).

* **Compiler Flags:**
`CFLAGS = -Ofast -march=rv32i_zicsr`
* **Result:**
The following output shows the `hw1_B` program running correctly and the resulting baseline cycle count.
  * cycle counts = $1881$
  * instruction counts = $1881$
  ![截圖 2025-11-08 清晨6.47.39](https://hackmd.io/_uploads/B1j7wlnyZe.png)
#### Analysis 1: Performance Comparison

This simple flag change resulted in a dramatic performance improvement:

| CFLAGS | Total Cycles | Speedup |
| :--- | :--- | :--- |
| `-g` (Baseline) | 7,134 | 1.00x |
| `-Ofast` (Optimized speed) | 1,881 | 3.79x |


## Conclusion
| CFLAGS                                                     | Total Cycles | Speedup   |
|:---------------------------------------------------------- |:------------ |:--------- |
| `-g` (<span style="color:brown">**hw1_B.c**</span>)        | 71,506        | **1.00x** |
| `-Ofast` (<span style="color:brown">**hw1_B.c**</span>)    | 18,997        | **3.76x** |
| `-Os` (<span style="color:brown">**hw1_B.c**</span>)       | fail         | **fail**  |
| -                                                          | -            | -         |
| `-g` (<span style="color:darkblue">**hw1_B.S**</span>)     | 7,134        | **1.00x** |
| `-Ofast` (<span style="color:darkblue">**hw1_B.S**</span>) | 1,881        | **3.79x** |
| `-Os` (<span style="color:darkblue">**hw1_B.S**</span>)    | fail         | **fail**      |

1.  **C vs. Assembly**: The C code was 10x slower, not because C is slow, but because the compiler was forced to use an extremely slow software fallback.
2. **Compiler Intelligence**:`-Ofast` shows a key value of compilers : eliminating the cost of function calls.
    * Similar ~3.7x Speedup
3.  On a highly constrained hardware target, a superior algorithm  written in assembly can easily outperform an optimized C compiler that is forced to rely on a generic, slow, software fallback.
  
---

# QUIZ2_A
## qz2_A.c

This section describes the process of porting Quiz2 A program (`qz2_A.c`) to the rv32emu bare-metal environment and establishing its baseline performance.

### a. Porting Process 

Porting `qz2_A.c` required a *different* set of modifications than `hw1_B.c` due to its use of C intrinsics and formatted printing.

* **Test Harness Creation:**
    * The `main()` function in `main.c` was reconfigured to act as the test harness for Quiz2_A.
    * `qz2_A.c`'s original `main()` was renamed to `qz2_A_main()` to prevent a linker conflict.

* **Makefile Configuration:**
    * The Makefile was updated to include `quiz2_A.o` in the `OBJS` list.

* **Bare-Metal LibC Stubbing (The "printf" Challenge):**
    * `qz2_A.c`'s `print_move` function originally used `printf("Move Disk %d from '%c' to '%c'\n", ...)` for formatted output.
    * Bare-metal `printf` stub cannot handle format specifiers (`%d`, `%c`).
    * **Solution:**
        1.  **Upgrading `main.c`:** I added two new global helper functions, `print_int()` (by removing `static` from `print_dec`) and `print_char()`.
        2.  **Refactoring `qz2_A.c`:** I manually rewrote the `print_move` function to "unroll" the single `printf` call into 7 separate calls to our simpler `printf` (for strings), `print_int` (for the disk number), and `print_char` (for the peg letters).

* **Software Popcount (`libgcc` Stubbing):**
    * The `qz2_A.c` code uses the `__builtin_popcount()` intrinsic (a GCC-specific function) to identify which disk to move.
    * Because we are on `RV32I`  and are not linking the standard C library (`libgcc`), this created an "undefined reference to `__popcountsi2`" linker error.
    * **Solution:** To satisfy the linker, I added a manual C implementation of the `__popcountsi2` function directly to `main.c`.

### b. Baseline Performance 

After porting, the program was compiled with the `-g` (debug) flag to establish the non-optimized baseline for `quiz2_A`.

* **Compiler Flags:**
`CFLAGS = -g -march=rv32i_zicsr`

* **Result:**
The following output shows the `qz2_A` program running correctly and the resulting baseline cycle count.
  * cycle counts = $12817$
  ![截圖 2025-11-06 凌晨1.24.58](https://hackmd.io/_uploads/Bk7wnbKJbe.png)

### c-1. Optimization (optimized for speed)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Ofast` (optimize for speed).

1. **Compiler Flags:**
`CFLAGS = -Ofast -march=rv32i_zicsr`

2. **Result:**
After running `make clean` to force a re-compilation, the new results were as follows.
   * cycle counts = $6400$
![截圖 2025-11-06 凌晨1.50.01](https://hackmd.io/_uploads/H1x0X0YyWg.png)
#### Analysis 1: Performance Comparison 

The optimization provided a significant **2.00x** speedup for the `qz2_A` program.

| CFLAGS | Total Cycles | Speedup |
| :--- | :--- | :--- |
| `-g` (Baseline) | 12,817 | 1.00x |
| `-Ofast` (Optimized speed) | 6,400 | 2.00x |

#### Analysis 2: Assembly-Level Comparison (`-g` vs `-Ofast`)

To understand how the 2.00x speedup was (and was not) achieved, I compared the `dump_qz2_A_original_g.txt` and `dump_qz2_A_optimized_Ofast.txt` files.

**Observation 1: Register Allocation**
* **`-g` (Original):** The `dump_qz2_A_original_g.txt` shows `qz2_A_main` (at `10770`) is extremely bloated. It allocates 112 bytes on the stack and is filled with `sw`  and `lw`  instructions inside the main `for (n_moves ...)` loop and the nested `for (p ...)` / `for (d ...)` loops. It spills all C variables to the slow stack on every iteration.
* **`-Ofast` (Optimized):** The `dump_qz2_A_optimized_Ofast.txt` is much cleaner. `gcc -Ofast` completely removed the `sw`/`lw` instructions inside the loops. It performed aggressive Register Allocation, keeping all loop counters and state variables permanently in fast CPU registers. This is the source of the 2.00x speedup.

**Observation 2: The Inlining Bottleneck (What was "NOT" optimized)**
* **The Problem:** The `qz2_A` (Hanoi) program is very "chatty". Its main bottleneck is `(7 moves * 7 print calls) + 7 popcounts = 56` `jal`  instructions inside its main loop.
* **The `Ofast` Dump:** When looking at `dump_qz2_A_optimized_Ofast.txt`, these `jal` calls are still present:
    ```assembly
    10374:	e8dff0ef	jal	10200 <printf>
    1037c:	cf1ff0ef	jal	1006c <print_int>
    1038c:	da1ff0ef	jal	1012c <print_char>
    103dc:	e95ff0ef	jal	10270 <__popcountsi2>
### c-2. Optimization (optimized for size)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Os` (optimize for size).

1. **Compiler Flags:**
`CFLAGS = -Os -march=rv32i_zicsr`

2.  **Result : Program Failure**
After running `make clean`, the program was re-compiled with `-Os`. However, this version **failed to execute** correctly.
    * Infinite Loop
   ![截圖 2025-11-08 晚上9.06.58](https://hackmd.io/_uploads/BkodPa2yZx.png)


#### Analysis 1:
This is the most significant finding. The program successfully printed `=== [Running qz2_A] ===` but never returned from the `qz2_A_main()` call. 

This proves that `gcc -Os` generated faulty assembly code by breaking the RISC-V calling convention.
  
## qz2_A.S
This section describes the process of porting `qz2_A.S` to the rv32emu bare-metal environment and establishing a baseline performance metric.
### a. Integration Process

1. **Assembly Interface**: 
   * The `.global qz2_A_main` directive was used in `qz2_A.S` to export the label, making it visible to the linker.
2. **Makefile Configuration:**
   * Makefile was updated by add `qz2_A.o` in the OBJS list.
### b. Baseline Performance
After integration, the program was compiled with the Makefile's default `-g` flag to establish a non-optimized baseline for `hw1_B`

* **Compiler Flags:**
`CFLAGS = -g -march=rv32i_zicsr`
* **Result:**
The following output shows the `hw1_B` program running correctly and the resulting baseline cycle count.
  * cycle counts = $1219$
  * instruction counts = $1219$
  ![截圖 2025-11-08 晚上11.11.05](https://hackmd.io/_uploads/S16JkJTJ-g.png)

### c-1. Optimization (optimized for speed)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Ofast` (optimize for speed).

1. **Compiler Flags:**
`CFLAGS = -Ofast -march=rv32i_zicsr`

2. **Result:**
After running `make clean` to force a re-compilation, the new results were as follows.
   * cycle counts = $450$  
   * instruction counts = $450$
   ![截圖 2025-11-08 晚上11.20.25](https://hackmd.io/_uploads/B1rggya1We.png)

### c-2. Optimization (optimized for size)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Os` (optimize for size).

1. **Compiler Flags:**
`CFLAGS = -Os -march=rv32i_zicsr`

2.  **Result : Program Failure**
After running `make clean`, the program was re-compiled with `-Os`. However, this version **failed to execute** correctly.
    * Infinite Loop
    ![截圖 2025-11-08 晚上11.24.08](https://hackmd.io/_uploads/BJ2oxkpyWl.png)
## Conclusion
| CFLAGS                                                     | Total Cycles | Speedup   |
|:---------------------------------------------------------- |:------------ |:--------- |
| `-g` (<span style="color:brown">**qz2_A.c**</span>)        | 12,817       | **1.00x** |
| `-Ofast` (<span style="color:brown">**qz2_A.c**</span>)    | 6,400        | **2.00x** |
| `-Os` (<span style="color:brown">**qz2_A.c**</span>)       | fail         | **fail**  |
| -                                                          | -            | -         |
| `-g` (<span style="color:darkblue">**qz2_A.S**</span>)     | 1,219        | **1.00x** |
| `-Ofast` (<span style="color:darkblue">**qz2_A.S**</span>) | 450        | **2.70x** |
| `-Os` (<span style="color:darkblue">**qz2_A.S**</span>)    | fail         | **fail**      |
# QUIZ3_C
## qz3_C.c

This section describes the process of porting Quiz2 A program (`qz3_C.c`) to the rv32emu bare-metal environment and establishing its baseline performance.
（Similar to the previous approach, the test is performed directly here.）
### a. Baseline Performance
After integration, the program was compiled with the Makefile's default `-g` flag to establish a non-optimized baseline for `qz3_C`

* **Compiler Flags:**
`CFLAGS = -g -march=rv32i_zicsr`
* **Result:**
The following output shows the `qz3_C` program running correctly and the resulting baseline cycle count.
  * cycle counts = $31001$
  * instruction counts = $31001$
  ![截圖 2025-11-09 凌晨1.55.32](https://hackmd.io/_uploads/rJbGHbp1Wx.png)
### b-1. Optimization (optimized for speed)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Ofast` (optimize for speed).

1. **Compiler Flags:**
`CFLAGS = -Ofast -march=rv32i_zicsr`

2. **Result:**
After running `make clean` to force a re-compilation, the new results were as follows.
   * cycle counts = $12962$  
   * instruction counts = $12962$
   ![截圖 2025-11-09 凌晨2.00.34](https://hackmd.io/_uploads/SkcLBZT1Wx.png)


### b-2. Optimization (optimized for size)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Os` (optimize for size).

1. **Compiler Flags:**
`CFLAGS = -Os -march=rv32i_zicsr`

2.  **Result : Program Failure**
After running `make clean`, the program was re-compiled with `-Os`. However, this version **failed to execute** correctly.
    * Infinite Loop
    ![截圖 2025-11-09 凌晨2.01.42](https://hackmd.io/_uploads/HyKqH-TkWx.png)
    
## qz3_C.S
This section describes the process of porting `qz3_C.S` to the rv32emu bare-metal environment and establishing a baseline performance metric.
### a. Baseline Performance
After integration, the program was compiled with the Makefile's default `-g` flag to establish a non-optimized baseline for `qz3_C`

* **Compiler Flags:**
`CFLAGS = -g -march=rv32i_zicsr`
* **Result:**
The following output shows the `qz3_C` program running correctly and the resulting baseline cycle count.
  * cycle counts = $26757$
  * instruction counts = $26757$
  ![截圖 2025-11-09 凌晨2.34.12](https://hackmd.io/_uploads/BJyCTb6Jbg.png)

### c-1. Optimization (optimized for speed)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Ofast` (optimize for speed).

1. **Compiler Flags:**
`CFLAGS = -Ofast -march=rv32i_zicsr`

2. **Result:**
After running `make clean` to force a re-compilation, the new results were as follows.
   * cycle counts = $12332$  
   * instruction counts = $12332$
   ![截圖 2025-11-09 凌晨2.34.52](https://hackmd.io/_uploads/SyGxC-T1-l.png)



### b-2. Optimization (optimized for size)

To measure the impact of compiler optimization, I modified the `Makefile` to change the `CFLAGS` from `-g` (debug) to `-Os` (optimize for size).

1. **Compiler Flags:**
`CFLAGS = -Os -march=rv32i_zicsr`

2.  **Result : Program Failure**
After running `make clean`, the program was re-compiled with `-Os`. However, this version **failed to execute** correctly.
    * Infinite Loop
    ![截圖 2025-11-09 凌晨2.35.22](https://hackmd.io/_uploads/rkyMRZpJWl.png)

## Conclusion
| CFLAGS                                                     | Total Cycles | Speedup   |
|:---------------------------------------------------------- |:------------ |:--------- |
| `-g` (<span style="color:brown">**qz3_C.c**</span>)        | 31.001       | **1.00x** |
| `-Ofast` (<span style="color:brown">**qz3_C.c**</span>)    | 12,962        | **2.39x** |
| `-Os` (<span style="color:brown">**qz3_C.c**</span>)       | fail         | **fail**  |
| -                                                          | -            | -         |
| `-g` (<span style="color:darkblue">**qz3_C.S**</span>)     | 26,757        | **1.00x** |
| `-Ofast` (<span style="color:darkblue">**qz3_C.S**</span>) | 12,332        | **2.16x** |
| `-Os` (<span style="color:darkblue">**qz3_C.S**</span>)    | fail         | **fail**      |    

---
---

<b>Reference</b>

1.  [Assignment2: Complete Applications](https://hackmd.io/@sysprog/2025-arch-homework2)