Skip to main content

Configuration Guide

Parameter System

ParameterSet

Define parameters with automatic registration and YAML support:

struct CPUParams : public ParameterSet {
Param<uint32_t> fetch_width{this, "fetch_width", 4, "Fetch width per cycle"};
Param<uint32_t> rob_size{this, "rob_size", 128, "Reorder buffer size"};
Param<bool> enable_smt{this, "enable_smt", false, "Enable SMT"};
Param<double> frequency{this, "frequency", 3.0, "Frequency in GHz"};
};

Param Template

template<typename T>
class Param : public ParamBase {
public:
Param(ParameterSet* owner, const std::string& name,
T default_value, const std::string& description = "");

operator const T&() const; // Implicit read
const T& value() const; // Explicit read
Param& operator=(const T& v); // Assignment
};

Usage

void MyUnit::tick() {
uint32_t width = params_->fetch_width; // Implicit conversion

if (params_->enable_smt.value()) {
// SMT logic
}
}

Factory System

AutoRegisteredUnit

Automatic factory registration via CRTP:

class MyUnit : public AutoRegisteredUnit<MyUnit> {
public:
using ParameterSet = MyUnitParams;
static constexpr const char* unit_type_name = "MyUnit";
static constexpr const char* unit_description = "My unit";

// CHRONON_UNIT_CONSTRUCTOR generates factory constructor
CHRONON_UNIT_CONSTRUCTOR(MyUnit, ParameterSet,
params->width, params->depth)
(uint32_t width = 4, uint32_t depth = 8)
: AutoRegisteredUnit("my_unit")
, width_(width), depth_(depth) {}
};

Usage

// Direct C++ instantiation
auto* unit = sim.createUnit<MyUnit>(8, 16);
auto* unit2 = sim.createUnit<MyUnit>(); // Defaults

// YAML factory instantiation (automatic)
// type: MyUnit, params: {width: 8, depth: 16}

SimulationApp

Unified entry point with CLI support:

#include "chronon/Chronon.hpp"

int main(int argc, char* argv[]) {
return chronon::SimulationApp("CPU Simulator")
.setDefaultConfig("config.yaml")
.setConfigSearchPaths({".", "../configs"})
.setVersion("0.3.2")
.onPostBuild([](auto& result) {
// Custom setup after build
})
.run(argc, argv);
}

CLI Options

OptionDescription
[config.yaml]Configuration file
-c, --config <path>Configuration file
-p, --param KEY=VALUEOverride YAML value
-o, --output-dir <path>Override output directory
-n, --run-cycles <N>Override run cycles
-t, --threads <N>Override thread count
--epoch-size <N>Override simulation epoch size in cycles
--no-observeDisable observation
-v, --verboseVerbose output

Examples

./my_sim config.yaml
./my_sim --threads=4 --run-cycles=1000000
./my_sim -p simulation.num_workers=4
./my_sim -p simulation.observation.logging.debug.enabled=false
./my_sim -p simulation.observation.timeline.scheduler.enabled=true \
-p simulation.observation.timeline.scheduler.end_cycle=2000

YAML Configuration

Basic Structure

simulation:
name: my_simulation
num_workers: 4
enable_parallel: true # Enable parallel execution (requires num_workers > 1)
enable_lookahead: true # Enable lookahead scheduling
enable_epoch_free_lookahead: true # Drop the per-epoch barrier when safe
epoch_size: 64 # Deprecated: only used by the fallback scheduler
run_cycles: 1000000

unit:
fetch:
type: FetchUnit
params:
fetch_width: 4
port:
out_inst:
to: decode.in_inst
delay: 1

decode:
type: DecodeUnit
params:
decode_width: 4

uart:
type: UART
tick_interval: 1000 # Optional: run tick() only on every 1000th global cycle
params:
poll_interval: 1

tick_interval is a unit-level scheduler hint, not a constructor parameter. It defaults to 1. Off-edge cycles skip the unit's tick() body but still advance the unit's local cycle and scheduler progress. Port arrivals and explicit wakeAt() calls can make a sleeping unit active again; the tick body still runs only on an allowed interval edge. Applying tick_interval from YAML preserves any constructor-established sleepUntil() or sleepForever() target; it only seeds cycle 0 activity when the unit has not already deferred itself.

Observation Configuration

Chronon uses a unified logging model where each event type (debug, info, warn, error, trace) is an independent "channel" with its own enable settings. Categories and temporal filters are shared across all channels. Log channels are text-only (events.log); trace events go to the unified Perfetto timeline (timeline.pftrace).

simulation:
observation:
enabled: true
output_dir: "out"
queue_capacity: 262144

counters:
enabled: true
csv_output: true
periodic_dump_cycles: 10000
dump_on_shutdown: true

timeline: # Unified Perfetto timeline (timeline.pftrace)
enabled: true # Default true
file: timeline.pftrace
trace_events: true # Simulation trace channel -> instant events
counters: true # Counter snapshots -> counter tracks
compress: true # Deflate packet batches (compressed_packets)
scheduler: # Scheduler execution timeline (see below)
enabled: false

logging:
enabled: true

# Per-channel configuration
debug:
enabled: true
file: debug.log # Optional; empty = default events.log
info:
enabled: true
warn:
enabled: true
error:
enabled: true
trace:
enabled: true
text: false # true mirrors trace events into events.log

# Shared temporal filters (apply to all channels)
temporal:
- range: [0, 100000]

# Shared category patterns (apply to all channels)
categories:
- pattern: "icache_*"
temporal:
- range: [0, 50000]
- pattern: "branch_pred"
temporal:
- periodic:
window: 1000
period: 10000
- pattern: "flush"

Output Files

FileContents
events.logText logs (debug/info/warn/error); trace events mirrored when trace.text: true
timeline.pftracePerfetto protobuf timeline: trace instant events, counter tracks, scheduler slices
counters.csvCounter snapshots

Per-channel format: keys (and the removed binary trace output they used to select) are deprecated; they are parsed but ignored with a warning. Logs are always text, and trace events always go to the Perfetto timeline.

Category Format Overrides (Removed)

Category patterns no longer accept per-category format overrides (format, trace_format, debug_format, ...). These keys are deprecated and ignored with a warning. Categories still control enable/disable and temporal filters.

Scheduler Timeline Trace

The scheduler timeline records Chronon scheduler streams, unit tick slices, lazy idle fast-path slices, cross-thread spin waits, epoch spans, and MPSC arbitration slices. It is part of the unified Perfetto timeline: when the observation backend runs with a timeline sink, the scheduler slices merge into timeline.pftrace under a "Chronon Scheduler" process group, with one lane per worker stream (stream 0, stream 1, ...) plus a scheduler lane for epoch, rebalance, and MPSC arbitration spans. Slices carry cycle and detail debug annotations.

simulation:
observation:
timeline:
scheduler:
enabled: false
max_events: 1000000
start_cycle: 0
end_cycle: 2000
trace_units: true
trace_waits: true
trace_epochs: true
trace_arbitration: true
min_duration_ns: 0
FieldDescription
enabledEnable timeline collection. Disabled by default.
max_eventsEvent cap; additional events are reported as dropped.
start_cycle, end_cycleInclusive start and exclusive end cycle capture window.
trace_unitsRecord each unit tick as a duration slice.
trace_waitsRecord spin waits on predecessor cluster progress atomics.
trace_epochsRecord progress-epoch spans on the scheduler lane.
trace_arbitrationRecord MPSC arbitration spans on the scheduler lane.
min_duration_nsDrop events below this duration.

The old top-level simulation.timeline_trace: key is deprecated; it is still parsed but prints a warning. When observation is disabled (e.g. --no-observe), the scheduler timeline falls back to a standalone Perfetto protobuf file at the configured path (default chronon_timeline.pftrace).

See Scheduler Timeline Trace for event semantics and Perfetto usage.

Temporal Filters

TypeFormatDescription
Rangerange: [start, end]Enable within cycle range
Periodicperiodic: {window, period, offset}Enable first window cycles every period

Category Patterns

PatternMatches
"*"All categories
"icache_*"icache_hit, icache_miss, etc.
"*_miss"icache_miss, dcache_miss, etc.
"branch"Exact match

Per-Unit Overrides

simulation:
observation:
unit_overrides:
fetch:
logging:
debug:
enabled: true # Enable debug for fetch only
trace:
enabled: true
categories:
- pattern: "*" # Trace all categories for this unit

Runtime Overrides

./my_sim config.yaml \
-p simulation.num_workers=4 \
-p simulation.observation.logging.debug.enabled=false

Type conversion:

  • true, false → boolean
  • All digits → integer
  • Digits with . → double
  • Otherwise → string

Lifecycle Phases

Tree nodes progress through phases:

BUILDING → CONFIGURING → BINDING → FINALIZED
PhaseAllowed Operations
BUILDINGAdd children, modify params
CONFIGURINGModify params, create resources
BINDINGBind ports
FINALIZEDRead-only, run simulation

TreeNode Hierarchy

Path-based addressing with dot notation:

root
├── cpu → "cpu"
│ ├── core0 → "cpu.core0"
│ │ ├── fetch → "cpu.core0.fetch"
│ │ └── decode → "cpu.core0.decode"
│ └── core1 → "cpu.core1"
└── memory → "memory"
node->getChild("cpu.core0.fetch") // Traverses hierarchy