A Livt context describes the timing environment of a component. Most small designs use one context everywhere. Larger systems often need more than one: a fast system side, a slower peripheral side, or a test-only worker context.
Livt keeps those boundaries visible. Components can share a context, or a subcomponent can be placed in a different context. When one component communicates with another component across that boundary, mark the access with sync.
Component Contexts
When a subcomponent is constructed normally, it uses the same context as the component that owns it:
component Counter
{
public value: int
process Count()
{
this.value = this.value + 1
}
}
component App
{
counter: Counter
new()
{
this.counter = new Counter()
}
public fn ReadCounter() int
{
return this.counter.value
}
}
Because App and Counter share the same context in this example, the public field access is ordinary component communication.
Separate Contexts
A component can also be assigned to another context. Tests often use this to exercise a boundary that is intentionally timed differently:
component Worker
{
public value: int
public fn GetValue() int
{
return this.value
}
}
@Test
@Context(ClockFrequency=100MHz)
component WorkerTest
{
@Context(ClockFrequency=50MHz)
workerContext: IContext
worker: Worker
new()
{
this.worker = new Worker()
this.worker.context = this.workerContext
}
}
The test component has a default 100 MHz context. workerContext is a separate 50 MHz context, so accesses from the test component to the worker cross a context boundary.
Context identity matters. Two contexts can use the same frequency and still be separate timing domains. If components use the same context reference, they are in the same domain.
The sync Marker
Use sync exactly where a cross-context access happens:
var value = sync this.worker.GetValue()
var status = sync this.worker.status
sync this.worker.command = nextCommand
The marker belongs at the access site. The same worker function may be same-context in one design and cross-context in another, depending on how the components are connected.
Supported Accesses
Use sync for public component-boundary communication:
- calling a public function on another component;
- reading a public primitive field from another component;
- writing a public primitive field on another component.
Do not use sync around local arithmetic, literals, or private implementation details. It is for communication between components, not ordinary expressions inside one component.
Status Reader Example
Keep the crossing at a clear component boundary. In this example, all cross-context communication is visible in the functions that read from the worker:
component StatusWorker
{
public ready: logic
public status: byte
public fn GetStatus() byte
{
return this.status
}
}
component StatusReader
{
worker: StatusWorker
new(worker: StatusWorker)
{
this.worker = worker
}
public fn IsReady() bool
{
var ready = sync this.worker.ready
return ready == 0b1
}
public fn ReadStatus() byte
{
return sync this.worker.GetStatus()
}
}
Testing with Contexts
Add an extra context in a test when the timing boundary is part of the behavior you want to verify:
@Test
@Context(ClockFrequency=100MHz)
component StatusReaderTest
{
@Context(ClockFrequency=25MHz)
workerContext: IContext
worker: StatusWorker
reader: StatusReader
new()
{
this.worker = new StatusWorker()
this.worker.context = this.workerContext
this.reader = new StatusReader(this.worker)
}
@Test
fn ReadsStatusAcrossContext()
{
var status = sync this.worker.GetStatus()
assert status == 0x00
}
}
Keep the fixture small: one default context, one named additional context, and one or two synchronized accesses are often enough to document the boundary.
Design Guidance
Use names such as sensorContext, busContext, or workerContext so the reason for the boundary is visible. Reserve sync for the places where a boundary is part of the architecture.
A small status value, command field, or function result is easier to reason about than a large bundle of unrelated state. For continuous high-throughput data flow, prefer a dedicated buffering or streaming component with its own tests.
Summary
A context is the timing environment of a component. Components normally share the owning context, but tests and larger systems can assign separate contexts where needed. Use sync at the exact public component access that crosses from one context to another.