Small Livt examples fit in one file. Real projects do not. As soon as a design has multiple protocols, test components, reusable helpers, and top-level integration code, organization becomes part of the design.
Good organization makes hardware easier to review. A reader should be able to find the top level, identify component boundaries, understand which modules are reusable, and see where tests live.
Project Layout
A typical Livt project separates source code from tests:
MyProject/
|-- livt.toml
|-- src/
| |-- App.lvt
| |-- PacketParser.lvt
| `-- RegisterBank.lvt
`-- tests/
|-- PacketParserTest.lvt
`-- RegisterBankTest.lvt
Use src/ for synthesizable design code and shared design helpers. Use tests/ for test components, simulation scenarios, and assertions.
Keeping this separation clear helps the build and test workflow stay predictable. It also makes code review easier: a test file can use simulation-only APIs, while a source file should be more careful about synthesis boundaries.
Namespaces at Scale
Namespaces should communicate ownership and purpose:
namespace Livt.App.Net
namespace Livt.App.Registers
namespace Livt.App.Tests
Avoid putting all components into one large namespace. Instead, group code by domain: networking, register access, memory, protocol helpers, tests, simulation fixtures, and top-level integration.
Imports should stay boring:
namespace Livt.App.Tests
using Livt.App.Net
using Livt.App.Registers
If a file needs many unrelated namespaces, that is often a sign that the component is doing too much.
Component Boundaries
A component should have one clear responsibility. Good component names usually describe a role:
PacketParserChecksumVerifierRegisterBankUartTransmitterAxiLiteSlaveHttpRequestRecognizer
Avoid components that only wrap a primitive operation. Livt lets you write high-level expressions directly; you do not need a component for every operator.
A useful component boundary often appears where one of these is true:
- The code owns state.
- The code exposes a public API.
- The code connects to a protocol boundary.
- The code deserves independent tests.
- The code may be reused in another design.
Public API Style
The public surface of a component is the part other code will depend on. Keep it small and intentional:
component PacketStatistics
{
public acceptedCount: int
public droppedCount: int
public fn Accept()
{
this.acceptedCount = this.acceptedCount + 1
}
public fn Drop()
{
this.droppedCount = this.droppedCount + 1
}
}
Here the counters are public because they are status values. The update behavior is exposed through named functions. That is easier to read than allowing callers to modify several implementation fields directly.
Use private fields for implementation details. Use public stored fields for observable state. Use public signal fields for hardware ports and directed signals.
File Size and Reviewability
A file should usually contain one primary component or interface. Small helper interfaces or constants can live nearby, but large files become difficult to review.
Prefer several focused files over one broad file:
src/
|-- IByteStream.lvt
|-- ByteStreamParser.lvt
|-- PacketStatistics.lvt
`-- PacketPipeline.lvt
This makes generated VHDL easier to map back to source and helps tests target one behavior at a time.
Naming
Use names that describe intent, not implementation accidents.
Good names:
IsAsciiDigitTryReadHasDataAcceptPacketpayloadLengthrxFrame
Weak names:
DoItHandletmp2flagdata1
Short names are fine for local loop counters. Public fields, functions, interfaces, and components should be descriptive.
Top-Level Code
The top-level component is the boundary to the outside world. Keep it direct:
- expose physical signals clearly
- instantiate major subcomponents
- wire interfaces and buses
- avoid burying protocol behavior in the top level
The top level should read like a system map. Detailed behavior belongs in the components it connects.
Summary
Organized Livt code has clear namespaces, focused files, meaningful components, small public APIs, and tests close to the behavior they verify. The structure of the code should help the reader understand the hardware architecture before they read the details.