Skip to content

Latest commit

 

History

History
264 lines (190 loc) · 6.46 KB

File metadata and controls

264 lines (190 loc) · 6.46 KB

From Zero to Pareto-Optimal CPU

A hands-on guide to building, running, and extending VOLT.


Prerequisites

  • Rust (edition 2024, requires rustc 1.85+): curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • Optional: RISC-V GNU toolchain if you want to compile your own benchmarks (see Custom Binaries)

Build

git clone <this-repo>
cd volt-cpu
cargo build --release

The only dependencies are rand and serde — everything else is hand-rolled. Expect a <30 second build.


Step 1: Run the Synthetic Workload

The synthetic workload is a self-contained microbenchmark that tests ILP extraction, branch prediction, and memory latency:

cargo run --release -- --synth

You'll see output like:

=== Synthetic Workload ===
Instructions: 3000000
Cycles:       1015234
IPC:          2.9548
Branch pred:  83.2% (12456/14962)
L1I:          hit=14962  miss=12  (99.92%)
L1D:          hit=5023   miss=234 (95.55%)

This verifies your build is working and gives you a baseline for comparison.


Step 2: Run a Real RISC-V Binary

Run CoreMark — a real embedded benchmark compiled to RISC-V:

cargo run --release -- --bin benchmarks/coremark/coremark.bin

Output includes IPC, branch prediction accuracy, and cache hit rates:

Instructions: 500000
Cycles:       96940
IPC:          5.1576
BP:           cor=42410  mis=3505  acc=92.37%
L1I:          hit=13173  miss=4    (99.97%)
L1D:          hit=25694  miss=34   (99.87%)

Compare with Dhrystone:

cargo run --release -- --bin benchmarks/dse.bin

Step 3: Explore the Default Config's Models

See what the analytical models predict for the default high-performance config:

cargo run --release -- --freq

Output:

=== VOLT Frequency/Analytical Model ===
Process node:   5.0 nm
FO4 delay:      2.00 ps
Pipeline depth: 18
Fmax:           4.71 GHz
Total area:     10.65 mm²
  Frontend:     4.13 mm²
  Backend:      3.16 mm²
  Memory:       3.36 mm²
Total power:    2.42 W
  Dynamic:      1.74 W
  Leakage:      0.68 W

Step 4: Run a Quick GA

Run a 5-generation GA to see VOLT in exploration mode:

cargo run --release -- --ga 5

The GA will:

  1. Generate 40 random CPU configurations
  2. Run each through CoreMark (500K instructions)
  3. Score each with IPC × Freq × area_penalty
  4. Evolve the population for 5 generations
  5. Print the best config at the end

Output (abbreviated):

Gen 0: best=11.30 | Gen 1: best=11.30 | Gen 2: best=12.25
Gen 3: best=14.49 | Gen 4: best=15.51 | Gen 5: best=15.75
All-time best: GA-8w-384r  IPC=5.16  Freq=4.59GHz  Area=11.95mm²

Step 5: Run the Full GA

For a thorough exploration (15 generations, 40 individuals = 600 evaluations, ~5-10 minutes):

cargo run --release -- --ga-full 15

This produces the full output suite in Volt CPU Designs/:

ls "Volt CPU Designs/"

Output files:

File Description
ga_results.csv All 600 evaluations with IPC, freq, area, power
config_best_ever.json Best config found
config_top01.jsonconfig_top10.json Top 10 configurations
report.md Full GA report with Pareto frontier

Reading the CSV

index,generation,fitness,ipc,freq_ghz,area_mm2,power_w
0,0,17.06,5.16,4.71,10.65,2.42
1,0,16.93,5.16,4.67,10.59,2.41
...

Each row is one evaluated individual. You can sort by fitness, filter by process node, or plot the Pareto frontier.

Reading a Config JSON

{
  "name": "GA-8w-256r",
  "process_node_nm": 5.0,
  "frontend": { "fetch_width": 8, "decode_width": 4, "branch_pred_type": "Hybrid", ... },
  "backend": { "rob_size": 256, "issue_queue_size": 32, "int_phy_regs": 192, ... },
  "memory": { "l1i_size": 32768, "l1d_size": 32768, "l2_size": 0, ... },
  "pipeline": { "pipeline_depth": 18, "bypass_depth": 2 },
  "ga_results": { "fitness": 17.06, "ipc": 5.16, "freq_ghz": 4.71, "area_mm2": 10.65, "power_w": 2.42 }
}

Step 6: Trace Execution

See exactly what the CPU is doing, instruction by instruction:

cargo run --release -- --trace --bin benchmarks/coremark/coremark.bin

This prints the first 50 decoded instructions with their pipeline transitions:

[RENAME] PC=0x80000000 addi x2, x0, 0x1000  →  rob=0  phys_rd=32
[EXEC]   PC=0x80000004 lui  x2, 0x80001     →  rob=1  phys_rd=33
[COMMIT] PC=0x80000000 addi x2, x0, 0x1000  ✓
...

For a deeper view, dump full ROB/IQ/PC state each cycle:

cargo run --release -- --dump-cycle --bin benchmarks/coremark/coremark.bin

Step 7: Run the Self-Test

A quick sanity check that the core is functionally correct:

cargo run --release -- --self-test

This runs a hardcoded 17-instruction sequence (loads, stores, ALU ops, branches) and verifies the results. If it passes, the pipeline is working correctly.


Custom Binaries

You can compile your own RISC-V programs and run them through VOLT:

  1. Install a RISC-V toolchain:

    # Using apt/brew
    apt install gcc-riscv64-linux-gnu    # Linux
    brew install riscv-tools             # macOS (if available)
  2. Write my_prog.S:

    .globl _start
    _start:
        li x5, 42
        li x6, 7
        add x7, x5, x6
        # exit
        li x17, 93
        ecall
  3. Compile:

    riscv64-linux-gnu-gcc -nostdlib -static -o my_prog.elf my_prog.S
    riscv64-linux-gnu-objcopy -O binary my_prog.elf my_prog.bin
  4. Run:

    cargo run --release -- --bin my_prog.bin

The binary is loaded at 0x8000_0000. ECALL with a17=93 exits, and a17=64 writes a null-terminated string (a10=addr, a11=len) to stdout.


Performance Notes

  • Release mode is essential: Debug builds run 50-100× slower and may hit the stall timeout for CoreMark.
  • 500K instructions ≈ 1-2 seconds in release mode on a modern laptop.
  • The GA evaluates 40 individuals per generation. At 1-2 seconds each, a 15-generation run takes ~10-15 minutes.
  • The stall detector automatically breaks if IPC drops below 0.0005 for 5M cycles, or if no progress is made for 100M cycles.

Next Steps