Properties
Properties are JSON-like data attached to a task or network. They configure clocks, reset, simulation tests, external implementations, and a few other knobs.
Two forms, same content
Properties show up in two places, with identical keys, values, and structure:
// 1. In a task or network declaration: properties { … } block.
task T {
properties {
clocks: ["clk_a"]
}
}
// 2. At an instance site: an object literal passed to new.
ram = new DualPortRAM({
clocks: ["clk_recv", "clk_send"]
});The declaration form attaches properties to the entity itself. The instance form overrides or supplies them for one instance. The rules below apply to both; the rest of this chapter uses whichever form fits the example.
Syntax
A property value is one of three things:
- Primitive: string (single or double quoted), number
(decimal,
0b…,0x…, with_separators), boolean, ornull. - Array: square brackets, comma-separated.
- Object: curly braces, identifier keys, comma-separated.
{
clocks: ["din", "dout"],
reset: { type: "synchronous", active: "high" }
}clocks
An array of clock names. Clock names must be valid identifiers.
properties {
clocks: ["din", "dout"]
}clock: "clk" is shorthand for clocks: ["clk"]. To declare no
clocks, write clock: null or clocks: [], or set
type: "combinational" instead.
Default: clocks: ["clock"].
Clock association
When a network instantiates a sub-entity, clocks pair up implicitly in the most common cases.
| Network clocks | Entity clocks | Pairing |
|---|---|---|
| any | 0 (combinational) | nothing to associate |
| N | N | positional, first-to-first |
| 1 | N | the single clock is associated N times |
| N | M, N≠M, M>1 | must be explicit |
Explicit association uses a clocks property at the
instantiation site. Either map by name…
ram = new DualPortRAM({
clocks: { rd_clock: "clk_recv", wr_clock: "clk_send" }
});…or by position:
ram = new DualPortRAM({
clocks: ["clk_recv", "clk_send"]
});Inner tasks
An inner task accepts no instantiation properties. Its
properties block does double duty as both declaration and
instantiation:
network N {
properties { clocks: ["clk_a", "clk_b"] }
inner_a = new task {
properties { clock: "clk_a" }
void loop() { /* … */ }
};
inner_b = new task {
properties { clock: "clk_b" }
void loop() { /* … */ }
};
}reset
Either null (no reset) or an object with type, active, and
name:
properties {
reset: { type: "synchronous", active: "high", name: "reset_p" }
}| Key | Values | Default |
|---|---|---|
type | "asynchronous" | "synchronous" | "asynchronous" |
active | "high" | "low" | "low" |
name | identifier | "reset_n" (active-low) or "reset" (active-high) |
At an instance site, reset: "ready" repurposes the named
signal as the instance's reset:
new SimpleTask({ reset: "ready" })type
Set type: "combinational" to mark an entity as having no clock
or reset:
properties { type: "combinational" }Equivalent to { clocks: [], reset: null }.
test
test drives a task or network through one or more cycles of
stimulus and termination checks. There are two forms.
Termination by expression
properties {
test: { terminate: "monitor.finished" }
}terminate names an expression (typically a state variable on a
monitor task) that ends the simulation when it becomes true.
This is the canonical pattern for tests with a monitor - see
Bytecode simulator.
Cycle-by-cycle stimulus
Each port name maps to an array of one value per cycle. null
means "no value on this cycle":
properties {
test: {
data: [ 6, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2 ],
value: [ null, 6, null, 5, null, null, 4, null, null, null, 3 ],
count: [ null, 1, null, 2, null, null, 3, null, null, null, 4 ]
}
}The simulation runs for as many cycles as the longest array.
Stimulus is interpreted as a black-box test: the testbench writes inputs into the DUT and checks outputs after the DUT has written them. Because output ports are registered in the generated RTL, the check happens one cycle after the corresponding input.
true/false for bool ports, integer literals for integer
ports. A push port that receives null on a given cycle must
actually be quiet on that cycle; writing when not expected is a
test failure.
The two forms can coexist. When both terminate and per-port
stimulus arrays are present, the simulator drives the stimulus
and checks terminate each cycle.
implementation
Tells the code generator what to emit (or not) for an entity.
properties {
implementation: {
type: "external",
file: "../../top.v",
dependencies: ["../../entity_a.v", "../../entity_b.v"]
}
}type | Meaning |
|---|---|
"external" | Body is provided by the named HDL file. Used for wrapping Verilog / VHDL. |
"builtin" | Reserved for entities defined by the compiler itself (std.mem.*, std.fifo.*). |
file is the implementation file path. dependencies lists
other HDL files that must appear before it in generated build
scripts. Paths are absolute or relative to the folder containing
the .cg file.
Embedded inline implementations in HDL are not yet supported. See the Tasks chapter for the external-task workflow, and FPGA integration for how these files enter a downstream toolchain.