Undergraduate Winter 2026 Disciplinary Depth Course Grade: #

EECS 370: Introduction to Computer Organization bridges the gap between software and hardware by building a complete computing stack from scratch — designing an ISA, writing a toolchain, and simulating the microarchitecture that executes it.

Topic 1: The LC2K Toolchain

This section covers the full software toolchain for the LC2K instruction set architecture, from raw assembly text to linked, executable machine code. By building each layer myself, I developed a concrete understanding of how compilers and operating system loaders prepare programs for execution on real hardware.

Project #1a: LC2K Assembler
C Two-Pass Assembly Instruction Encoding LC2K ISA
  • Objective: Translating human-readable LC2K assembly into 32-bit hex machine code, handling all instruction formats and symbolic references.
  • Build: Implemented a two-pass assembler in C. The first pass builds a label-to-address lookup table; the second pass encodes R-type (add, nor), I-type (lw, sw, beq), J-type (jalr), and pseudo-ops (halt, noop, .fill) into their 32-bit binary representations using bitwise packing.
  • Functionality: Correctly handles PC-relative branch offsets for beq, validates register ranges [0–7] and 16-bit offset bounds, detects duplicate label definitions, and enforces strict blank-line and unrecognized-opcode error policies.
Project #1s: LC2K Instruction-Level Simulator
C Fetch-Decode-Execute Register File Simulation LC2K ISA
  • Objective: Simulating the full execution of an LC2K machine code program, modeling the processor's architectural state cycle-by-cycle.
  • Build: Implemented a fetch-decode-execute loop in C that maintains a complete architectural state struct — program counter, an 8-register file (reg[0..7]), and a 65,536-word memory. Each cycle, instruction fields are extracted via bitmasks (e.g., mask_opcode = 0x1C00000) and dispatched through a switch statement covering all seven opcodes.
  • Functionality: Correctly implements signed branch arithmetic via 16-bit sign extension (convertNum), handles jalr link-register semantics, and terminates cleanly on halt, printing full machine state and execution statistics.
Project #2a: Multi-File Object Assembler
C Object File Format Symbol Tables Relocation Tables
  • Objective: Extending the single-file assembler to support modular, multi-file compilation by emitting object files — the same format used by real-world compilers like GCC.
  • Build: Modified the assembler to separate output into four sections: a Text section (instructions), a Data section (.fill directives), a Symbol Table recording global labels as type T (text), D (data), or U (undefined/external), and a Relocation Table tracking every instruction that references a symbol needing linker patching.
  • Functionality: Correctly handles all three symbol visibility cases, enforces that instructions must precede .fill data, and leaves relocatable offsets at zero as placeholders for the linker to resolve.
Project #2l: Multi-File Linker
C Symbol Resolution Relocation Patching Static Linking
  • Objective: Combining up to five separately-assembled object files into a single flat executable, resolving all cross-file symbol references — mirroring the job of ld on a real system.
  • Build: Built a linker in C that: (1) assigns absolute textStartingLine and dataStartingLine offsets to each file by concatenating all text sections before all data sections; (2) constructs a Master Symbol Table from all defined global labels, catching duplicate definitions; (3) iterates the relocation tables of every file and patches each instruction's 16-bit offset field with the computed final address, correctly distinguishing global labels, the special Stack symbol, and local address fixups.
  • Functionality: Handles error cases including undefined global labels, duplicate global definitions, and misuse of the reserved Stack label inside object files.
Project #2r: Recursive Combination in LC2K Assembly
LC2K Assembly Stack Frames Recursive Algorithms Calling Conventions
  • Objective: Implementing the binomial combination function $C(n, r) = C(n-1, r) + C(n-1, r-1)$ in pure LC2K assembly, with no hardware stack support — only registers and memory.
  • Build: Manually engineered a call stack using sw/lw with a stack pointer register (r5). Each recursive call saves the return address, live arguments (n, r), and intermediate results (C(n-1,r)) onto the stack before branching, then restores them on return. Used jalr for indirect function dispatch via a pre-loaded function address register.
  • Functionality: Correctly computes $C(7, 3) = 35$ using two levels of mutual recursion, bottoming out at the base cases $r=0$ and $n=r$, and cleanly returning results in r3 through the full call stack.

Topic 2: Microarchitecture & Memory Hierarchy

With the toolchain complete, I moved down the stack to simulate the hardware itself. This section covers the two major techniques that give modern processors their performance: pipelining, which overlaps instruction execution, and caching, which bridges the speed gap between the CPU and main memory.

Project #3: 5-Stage Pipelined Processor Simulator
C Pipeline Registers (IF/ID/EX/MEM/WB) Data Forwarding Hazard Detection & Stalling
  • Objective: Simulating a fully-pipelined LC2K processor that executes up to five instructions simultaneously, while correctly handling all data and control hazards.
  • Build: Implemented five pipeline stages — IF, ID, EX, MEM, WB — as explicit pipeline register structs (IFIDType, IDEXType, EXMEMType, MEMWBType, WBENDType). Each cycle, a newState is computed from the current state and then atomically committed, modeling real clocked flip-flop behavior.
  • Functionality: Handles three classes of hazards: (1) Load-Use Hazards — detected in the ID stage by comparing the LW destination against incoming source registers; resolved by freezing the PC and IF/ID register for one cycle and injecting a NOOP bubble into ID/EX. (2) Data Hazards — resolved by forwarding results from the EX/MEM, MEM/WB, and WB/END pipeline registers directly into the EX stage ALU inputs, with priority given to the most recent write. (3) Control Hazards — a taken BEQ detected in the EX stage flushes the three in-flight instructions behind it by overwriting IF/ID, ID/EX, and EX/MEM with NOOPs.
Project #4: Set-Associative Cache Simulator
C LRU Replacement Policy Write-Back / Write-Allocate Set-Associative Mapping
  • Objective: Implementing a parameterized, set-associative cache that plugs into the LC2K pipeline simulator, modeling the behavior of a real CPU memory hierarchy.
  • Build: Designed a cache simulator in C with fully configurable parameters: blockSize, numSets, and blocksPerSet. Each cache block stores a tag, valid bit, dirty bit, and an lruLabel counter. The address is decomposed as: $\text{tag} = \lfloor \text{addr} / (\text{blockSize} \times \text{numSets}) \rfloor$, $\text{setIndex} = \lfloor \text{addr} / \text{blockSize} \rfloor \bmod \text{numSets}$, $\text{blockOffset} = \text{addr} \bmod \text{blockSize}$.
  • Functionality: On a hit, updates the LRU counters for the set and services the read or write directly from cache. On a miss, selects the LRU block for eviction — writing it back to memory via mem_access only if it is dirty (cacheToMemory), or discarding it silently if clean (cacheToNowhere) — then loads the new block from memory (memoryToCache) before servicing the original request. Tracks hit count, miss count, and writeback count, and reports dirty blocks remaining at halt.

Updated: