Skip to content

Latest commit

 

History

650 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Multi-Cycle Harvard Processor

The processor follows a Harvard architecture with separate program and data memories, a custom Instruction Set Architecture (ISA), a hardwired control unit, and a multiplexer-based internal data bus for datapath communication.

Demonstrated at 8-bit but configurable to any data width at instantiation.

🛠️ Tools & Technologies

Icarus Verilog Verilator Cocotb GTKWave Yosys OpenSTA

🧩 System Organization

📚 Documentation

⚖️ Maximum of Two Numbers

This program compares two unsigned 8-bit values stored in RAM address 0x08 and 0x09 and writes the larger value back to RAM address 0x0A.

Max.asm

        LDA 0x08        ; Load first number
        LDB 0x09        ; Load second number

        PASS A          ; Update status Flags
        JLT STORE_B     ; If A < B, branch to store second number

        STA 0x0A        ; Store first number as maximum
        JMP END         ; Skip alternate path

STORE_B:
        STB 0x0A        ; Store second number as maximum

END:
        HLT             ; End program

Program Source: Max.hex

The waveform captures the branch-taken execution path, where the processor skips the alternate instruction sequence after evaluating the Negative flag, demonstrating conditional control flow.

[Max[0x17(23), 0x3A(58)] = 0x3A(58)]


RTL simulation of the processor executing the Maximum of Two Numbers program, illustrating instruction fetch, ALU computation, memory operations, and control flow .

✖️ Multiplication Kernel

This program multiplies two unsigned 8-bit values using repeated addition. The multiplicand is stored in RAM address 0x07 and the multiplier stored in RAM address 0x08 acts as the loop counter. A constant value of 1 is stored in RAM address 0x06 for decrementing the counter, and the accumulated product is written to RAM address 0x09.

Mult.asm

LOOP:
    LDB 0x06          ; Load constant 1
    LDA 0x08          ; Load multiplier (loop counter)

    PASS A            ; Check if counter is zero
    JZ DONE           ; Jump if counter is zero

    SUB               ; Decrement counter
    STA 0x08          ; Store updated counter

    LDA 0x09          ; Load accumulated result
    LDB 0x07          ; Load multiplicand
    ADD               ; Add multiplicand to result
    STA 0x09          ; Store updated result

    LDA 0x08          ; Reload counter
    PASS A            ; Update status flags
    JNZ LOOP          ; Repeat until counter becomes zero

DONE:
    LDA 0x09          ; Load final product
    HLT               ; End program

Program Source: Mult.hex

The waveform below shows the execution of the Integer Multiplication program implemented using repeated addition. The processor repeatedly executes the fetch-decode-execute cycle, decrementing the multiplier while accumulating the multiplicand.

[0x11(17) x 0x0D(13) = 0xDD(221)]


RTL simulation of the processor executing the Multiplication program, illustrating iterative execution until final product is produced.

🔢 2×2 Matrix Multiplication

This program implements unsigned 2×2 matrix multiplication entirely in software using the custom ISA. It is built by invoking multiplication kernel 8 times, followed by additions to combine the partial products. The input matrices are stored in RAM locations 0x00-0x03 and 0x04-0x07, while the resulting matrix is written to 0x10-0x13.

 A = [ A  B ]   B = [ E  F ]   A × B = [ C00 = AE + BG  C01 = AF + BH ]
     [ C  D ]       [ G  H ]           [ C10 = CE + DG  C11 = CF + DH ]

Memory Layout

A B C D E F G H AE BG
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
AF BH CE DG CF DH C00 C01 C10 C11
0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13

Matmul.asm

; For brevity, the multiplication kernel is abstracted as multiply(x, y).

; Compute C00 = A×E + B×G
multiply(0x00, 0x04)      ; Compute A×E
STA 0x08                  ; Store AE
multiply(0x01, 0x06)      ; Compute B×G
STA 0x09                  ; Store BG
LDA 0x08                  ; Load AE
LDB 0x09                  ; Load BG
ADD                       ; Compute AE + BG
STA 0x10                  ; Store C00

; Compute C01 = A×F + B×H
multiply(0x00, 0x05)      ; Compute A×F
STA 0x0A                  ; Store AF
multiply(0x01, 0x07)      ; Compute B×H
STA 0x0B                  ; Store BH
LDA 0x0A                  ; Load AF
LDB 0x0B                  ; Load BH
ADD                       ; Compute AF + BH
STA 0x11                  ; Store C01

; Compute C10 = C×E + D×G
multiply(0x02, 0x04)      ; Compute C×E
STA 0x0C                  ; Store CE
multiply(0x03, 0x06)      ; Compute D×G
STA 0x0D                  ; Store DG
LDA 0x0C                  ; Load CE
LDB 0x0D                  ; Load DG
ADD                       ; Compute CE + DG
STA 0x12                  ; Store C10

; Compute C11 = C×F + D×H
multiply(0x02, 0x05)      ; Compute C×F
STA 0x0E                  ; Store CF
multiply(0x03, 0x07)      ; Compute D×H
STA 0x0F                  ; Store DH
LDA 0x0E                  ; Load CF
LDB 0x0F                  ; Load DH
ADD                       ; Compute CF + DH
STA 0x13                  ; Store C11

; Load output matrix
LDA 0x10                  ; Load C00
LDB 0x11                  ; Load C01
LDA 0x12                  ; Load C10
LDB 0x13                  ; Load C11

HLT                       ; End program

Program Source: Matmul.hex

This program demonstrates that non-trivial linear algebra can be implemented entirely in software using a minimal instruction set consisting of arithmetic, memory operations, conditional branching, and loops.

A = [07 09]   B = [02 03]   A × B = [3B 54]
    [0B 0D]       [05 07]           [57 7C]

Waveform showing execution of the software-based 2×2 matrix multiplication program. The final values loaded into the A and B registers correspond to the computed output matrix stored at RAM locations 0x10-0x13.

📐 Square Root

This program computes the integer square root of an unsigned 8-bit number. The current candidate (i) is stored in RAM address 0x00. The accumulated square (i²) is stored in RAM address 0x02, and a copy of the current candidate is preserved in RAM address 0x03 during multiplication. The input number N is stored in RAM address 0x04.

Sqrt.asm

    LOAD B 0x01       ; Load Constant 1
    STB 0x00          ; Initialize i
    LOAD A 0x00       ; Load Constant 0
    STA 0x02          ; Initialize accumulator

START:
    LDB 0x00          ; Load current i
    STB 0x03          ; Save current i

MULTIPLY:
    LOAD B 0x01       ; Load constant 1
    LDA 0x00          ; Load multiplier
    SUB               ; Decrement multiplier
    STA 0x00          ; Store updated multiplier
    LDA 0x02          ; Load accumulated square
    LDB 0x03          ; Load current i
    ADD               ; square += i
    JC PREVIOUS       ; Overflow => i² > N
    STA 0x02          ; Store accumulated square
    LDA 0x00          ; Reload multiplier
    PASS A            ; Update status flags
    JNZ MULTIPLY      ; Repeat until counter becomes zero

    LDA 0x02          ; Load computed square
    LDB 0x04          ; Load input N
    PASS A            ; Compare i² and N
    JEQ DONE          ; Exact square found
    JGT PREVIOUS      ; i² > N

    LDB 0x03          ; Load current i
    LOAD A 0x01       ; Load constant 1
    ADD               ; i = i + 1
    STA 0x00          ; Store next candidate
    LOAD A 0x00       ; Load constant 0
    STA 0x02          ; Clear square accumulator
    JMP START         ; Repeat

PREVIOUS:
    LDA 0x03          ; Load current i
    LOAD B 0x01       ; Load constant 1
    SUB               ; Compute i - 1
    STA 0x03          ; Store answer

DONE:
    LDA 0x03          ; Load integer square root
    HLT               ; End program

Program Source: Sqrt.hex

√FF(255)= floor(15.9687) = 0F(15)

Waveform showing execution of the software-based integer square root program. The input value 0xFF is stored at RAM location 0x04, and the final result 0x0F is produced .

🔬 Physical Characterization

The following table summarizes post-synthesis implementation results obtained using the Sky130 HD standard-cell library. Timing results correspond to constrained static timing analysis using a 10 ns clock period, 1 ns input delay, and 1 ns output delay.

Technology: Sky130HD

Module Estimated Area Critical Path Estimated Fmax Estimated Total Power
General Purpose Registers 320.3072 µm² 1.41 ns ~709 MHz 39.8 µW
Arithmetic and Logic Unit 877.0912 µm² 3.21 ns ~311 MHz 349 µW
Program Counter 444.176 µm² 1.78 ns ~562 MHz 48.1 µW
ROM (256x8) 2277.184 µm² 2.85 ns ~351 MHz 888 µW
RAM (256x8) 75862.7584 µm² 5.18 ns ~208 MHz 9.88 mW
Memory Address Register 320.3072 µm² 1.41 ns ~709 MHz 39.8 µW
Flags Register 200.192 µm² 1.41 ns ~709 MHz 24.9 µW
Instruction Register 640.6144 µm² 1.41 ns ~709 MHz 79.7 µW
T-State Counter 125.12 µm² 1.38 ns ~725 MHz 15.6 µW
Control Unit 359.0944 µm² 2.29 ns ~437 MHz 41.4 µW
Computer 80347.06 µm² 25.55 ns ~39 MHz 8.67 mW

Note: The reported RAM area and timing correspond to a behavioral Verilog memory synthesized entirely using Sky130 HD standard cells. In the absence of a dedicated SRAM macro, the memory is implemented using flip-flops together with hierarchical multiplexing logic. This register-based memory dominates the processor's area and critical path, with the worst-case path originating from the Memory Address Register (MAR) through the synthesized memory network, where the lower address bits experience the highest effective capacitive loading due to the hierarchical multiplexer structure resulting in a significantly increased Clock-to-Q delay through the synthesized memory network. Further discussion and experimental characterization are provided in processor documentation folder.

📊 Performance Evaluation

An architectural optimization eliminating the Memory Address Register (MAR) increased the proportion of the ISA executing in 2 T-states from 84% to 100%, resulting in a uniform 2-cycle instruction execution across the ISA.

The processor was evaluated using three benchmark programs: Maximum of Two Numbers, Unsigned Multiplication, and 2×2 Matrix Multiplication.

Benchmark Maximum Speedup Maximum Clock Cycles Saved
Maximum 1.250× 3
Unsigned Multiplication 1.269× 1786
2×2 Matrix Multiplication 1.231× 12256

🧪 Post-Synthesis Evaluation

The optimized processor was synthesized using the Sky130 HD standard cell library and compared against the baseline implementation.

Metric Baseline Optimized Change
Area 80347.06 µm² 80091.81 µm² -0.32%
Critical Path 25.55 ns 25.65 ns Essentially Unchanged
Maximum Frequency ~39 MHz ~39 MHz Unchanged
Total Power 8.67 mW 8.64 mW -0.35%
Steady-State CPI ~2.54 2.00 -21.3%

The optimization therefore represents a favorable architectural trade-off: a negligible impact on physical implementation (area, timing, and power) in exchange for substantial reductions in execution cycles and up to 1.269× speedup on memory-intensive workloads.

📝 Observation

Removing the Memory Address Register (MAR) produced a modest reduction in silicon area and power while leaving the processor's critical path and maximum operating frequency essentially unchanged. Since the MAR was not part of the critical timing path, eliminating it primarily reduced the latency of memory instructions rather than increasing the clock frequency.

Consequently, the optimization preserves the processor's physical implementation characteristics while substantially improving architectural performance. Memory-intensive workloads execute with significantly fewer clock cycles saving thousands of cycles for larger benchmarks without requiring additional hardware resources or sacrificing operating frequency.

Detailed analytical performance models, Amdahl's Law validation, workload analysis, and experimental methodology are documented in the Architectural Studies folder.

🔍 Verification

The RTL is verified using both traditional Verilog testbenches and modern Cocotb self-checking testbenches.

Verification methodology includes:

  • Directed Testing - Handcrafted test cases covering reset, load, hold, increment, and control priority.
  • Random Testing - Python-generated randomized inputs validated against a software reference model using self-checking assertions.
  • Exhaustive Testing - Complete input-space verification for combinational modules such as the ALU by testing every possible input combination.

⚙️ Implemented Modules

Module Description Status
ALU Arithmetic and logical operations with status flags
A Register Loadable general-purpose register
B Register Loadable general-purpose register
Program Counter Instruction address generation
ROM Program storage subsystem
RAM Data storage subsystem
Memory Address Register Stores RAM address that needs to be accessed
Flags Register Stores status flags of computation
Instruction Register Stores current instruction
T-State Counter Tracks the T-state of an instruction
Control Unit Generates control signals
Computer Multi-Cycle Harvard Processor

📜License

  • Source code and HDL files are licensed under the MIT License.
  • Documentation, diagrams, images, and PDFs are licensed under Creative Commons Attribution 4.0 (CC BY 4.0).

About

A parameterized multi-cycle Harvard architecture processor designed from the ground up in Verilog. Developed using a complete RTL design workflow including simulation, linting, synthesis, Sky130 technology mapping, area estimation, static timing analysis, cocotb self-checking testbenches and post synthesis GLS 🖥️.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages