This document describes how the GA encodes, evaluates, and evolves CPU configurations. It's the bridge between the cycle-level simulator and the analytical models — the engine that discovers Pareto-optimal microarchitectures.
Every CPU configuration is encoded as a 34-bit binary chromosome. Bits are grouped by microarchitectural domain:
Frontend [0:7] Backend [7:20] Memory [20:30] Pipeline/Process [30:34]
│ │ │ │
├─ 0-1: Fetch ├─ 7-8: ROB ├─ 20: L1I sz ├─ 30-31: Pipe depth
├─ 2: Decode ├─ 9-11: IQ sz ├─ 21: L1D sz └─ 32-33: Process node
├─ 3-4: Pred ├─ 12-13: PhyReg ├─ 22: L1 assoc
└─ 5-6: BTB ├─ 14-15: ALUs ├─ 23-24: L2 sz
├─ 16: LdUnit ├─ 25: MSHR
├─ 17: StUnit └─ 26-27: LdQ sz
└─ 18-19: IssWdth └─ 28-29: StQ sz
| Bits | Parameter | Options |
|---|---|---|
| 0-1 | Fetch width | 4, 6, 8 |
| 2 | Decode width | 4, 6 |
| 3-4 | Branch predictor | Gshare, Tage, Hybrid |
| 5-6 | BTB entries | 256, 512, 1024 |
| 7-8 | ROB size | 256, 320, 384, 448 |
| 9-11 | Issue queue | 24, 32, 40, 48, 64 |
| 12-13 | Int phys regs | 192, 256, 320, 384 |
| 14-15 | ALU count | 3, 4, 5 |
| 16 | Load units | 2, 3 |
| 17 | Store units | 2, 3 |
| 18-19 | Issue width | 3, 4, 5, 6 |
| 20 | L1I size | 32 KB, 64 KB |
| 21 | L1D size | 32 KB, 64 KB |
| 22 | L1 associativity | 4, 8 |
| 23-24 | L2 size | 0, 256 KB, 512 KB, 1 MB |
| 25 | MSHR entries | 8, 12 |
| 26-27 | Load queue | 32, 40, 48, 56 |
| 28-29 | Store queue | 24, 32, 40 |
| 30-31 | Pipeline depth | 14, 16, 18 |
| 32-33 | Process node | 5 nm, 7 nm, 10 nm |
The decoder in src/ga/chromosome.rs extracts fields using bit masking:
fn extract(bits: &[bool], start: usize, count: usize) -> usize {
bits[start..start+count].iter()
.rev().fold(0, |acc, &b| (acc << 1) | b as usize)
} ┌─────────────────────┐
│ Initial Population │ (random 34-bit chromosomes)
└─────────┬───────────┘
↓
┌─── each individual ───┐
│ 1. Decode → VoltConfig │
│ 2. Run CoreMark (500K) │ ← real binary execution
│ 3. Get IPC, BP stats │
│ 4. Run analytical models│ ← freq, area, power
│ 5. Compute fitness │
└────────────────────────┘
↓
┌──────────────────┐
│ Fitness Scoring │ ← best → elite preserve
└─────────┬────────┘
↓
┌─────────────────────────┐
│ Tournament Selection │ (size 3)
│ × Uniform Crossover │ (50% bit-swap)
│ × Adaptive Mutation │ (base 8%, grows to 25%)
└─────────┬───────────────┘
↓
┌──────────────────┐
│ Next Generation │ (40 individuals)
└──────────────────┘
Each individual is evaluated by:
- Decoding the 34-bit chromosome into a
VoltConfig - Running CoreMark for 500,000 instructions through the cycle-level simulator
- Recording IPC and cache/mispredict statistics
- Running analytical models to get frequency, area, and power
- Computing fitness
If area exceeds 35 mm², evaluation is skipped (fitness = 0). This prevents the GA from wasting time on obviously infeasible designs.
let area_penalty = (area_mm2 / 25.0).min(1.0); // target: 25 mm²
let penalty = 1.0 - ALPHA * area_penalty; // ALPHA = 0.7
fitness = ipc * freq_ghz * penalty; // score to maximizeThis is a single-objective reduction of the three objectives (IPC ↑, Freq ↑, Area ↓). The alpha parameter controls the trade-off: higher alpha penalizes area more aggressively.
| Parameter | Value |
|---|---|
| Population | 40 |
| Generations | 15 |
| Tournament size | 3 |
| Crossover rate | 0.8 (uniform) |
| Base mutation | 0.08 (adaptive: grows 1% per gen, max 25%) |
| Elite count | 2 (top individuals preserved) |
| Area cap | 35 mm² (skip above) |
| Instruction limit | 500,000 (CoreMark) |
| Metric | Value |
|---|---|
| Name | GA-8w-256r |
| Fitness | 17.06 |
| IPC | 5.16 |
| Frequency | 4.71 GHz |
| Area | 10.65 mm² |
| Power | 2.42 W |
| Process | 5 nm |
| Fetch width | 8 |
| Decode width | 4 |
| Branch predictor | Hybrid (Gshare + Bimodal) |
| ROB | 256 |
| Issue queue | 32 |
| Issue width | 6 |
| Int phys regs | 192 |
| ALUs | 4 |
| L1D | 32 KB, 4-way |
| L1I | 32 KB, 4-way |
| L2 | None (direct to DRAM) |
| Pipeline depth | 18 |
The GA discovered configurations along the IPC/Frequency/Area trade-off curve:
| IPC | Freq (GHz) | Area (mm²) | Power (W) | Fitness | Config |
|---|---|---|---|---|---|
| 5.16 | 4.71 | 10.65 | 2.42 | 17.06 | 8w-256r |
| 5.16 | 4.71 | 10.81 | 2.42 | 16.95 | 8w-256r |
| 5.16 | 4.67 | 10.59 | 2.41 | 16.93 | 8w-256r |
| 5.16 | 4.71 | 10.88 | 2.46 | 16.91 | 8w-256r |
| 5.16 | 4.67 | 10.77 | 2.51 | 16.81 | 8w-256r |
| 5.16 | 4.57 | 12.07 | 3.23 | 15.60 | 8w-256r |
The Pareto frontier is concentrated around 8-wide fetch / 256-entry ROB / 32 IQ — suggesting this is a sweet spot for CoreMark. The GA never selected 6-wide or 4-wide fetch in the top tier.
The GA converges rapidly — within 5 generations the best fitness reaches >15.5/17.06, and the remaining generations fine-tune around the optimum:
Gen 0: best=11.30 | Gen 5: best=15.75 | Gen 10: best=16.93 | Gen 15: best=17.06
# Quick GA — saves best config only
cargo run --release -- --ga 15
# Full GA — saves all outputs to Volt CPU Designs/
cargo run --release -- --ga-full 15
# Customize the search in src/main.rs:
# - POPULATION_SIZE (line ~120)
# - ALPHA (area penalty weight)
# - INST_LIMIT (instructions per eval)
# - AREA_CAP (max area to consider)Outputs live in Volt CPU Designs/:
| File | Contains |
|---|---|
ga_results.csv |
All 600 evaluations (40 pop × 15 gen) with IPC, freq, area, power, fitness |
config_best_ever.json |
Best config in a loadable JSON format |
config_top01.json … config_top10.json |
Top 10 configs |
report.md |
Full GA report with Pareto frontier and search space description |