Getting Started
Start here if you want to install HALO, compose a model, generate code, and extend the framework with your own generators.
Install HALO
To install HALO directly from the wheel hosted in GitHub:
python -m pip install "https://github.com/nikola-winmaker/PhdHaloRSG/blob/main/halo/halo-0.1.1-py3-none-any.whl?raw=1"
After installation, verify the CLI:
halo --help or python -m halo --help
Basic workflow
The typical HALO workflow has two main steps:
halo composereads HALO DSL files, or imports a UML/XMI model, and creates the unified model.halo generateuses that unified model plus installed platform and protocol generators to emit code.
A common first command looks like this:
halo compose --hadls-root ./hadls --output-dir ./halo_out --include-stdlib-profiles
halo generate --composer-output-dir ./halo_out --output-dir ./generated
Example
Let’s look into one example of heterogenious environment:
In system.adl we can define components. The ADL grammar requires:
- Component name: unique identifier
- Type: required attribute (Application, Acceleration, etc.)
- Platform: required attribute (Linux, FreeRTOS, Zephyr, BareMetal, etc.)
- Function: required attribute (GeneralProcessing, RealTimeProcessing, OffloadProcessing, etc.)
A generic ADL file follows this structure:
HALOFramework {
components {
<ComponentName> : Component { Type: <Type>, Platform: <Platform>, Function: <Function> }
// ... more components
}
connections {
connection <ConnectionName> {
from <SourceComponent> to <DestinationComponent>
interface: <InterfaceName>
profile: <ProfileName>
}
// ... more connections
}
}
Here’s a concrete example:
HALOFramework {
components {
Core1 : Component { Type: Application, Platform: Linux, Function: GeneralProcessing }
Core2 : Component { Type: Application, Platform: FreeRTOS, Function: RealTimeProcessing }
Core3 : Component { Type: Application, Platform: Zephyr, Function: RealTimeProcessing }
Core4 : Component { Type: Application, Platform: BareMetal, Function: RealTimeProcessing }
}
}
Connections bind components together. The ADL grammar requires:
- connection name: unique identifier
- from and to: source and destination component names (cross-references)
- interface: interface name that governs the data contract
- profile: profile name that specifies the hardware mapping strategy
Here’s a concrete example:
connections {
connection LinuxToRTOS {
from Core1 to Core2
interface: TstIf_1
profile: sharedMemoryProfile1
}
connection RTOSToLinux {
from Core2 to Core1
interface: TstIf_2
profile: sharedMemoryProfile1
}
}
One of the simplest paths in that sample is:
- connection
LinuxToRTOS - interface
TstIf_1 - profile
sharedMemoryProfile1
Interfaces define the data contract. The IDL grammar requires:
- read: list of type names that can be read (or
*for all) - write: list of type names that can be written (or
*for all) - crc16, crc32, etc.: list of types protected by the corresponding check
Note:
- The
readblock is optional. If omitted, the interface will allow reading all data referenced by the connection’sfromandtocomponents in the ADL. To restrict reads, specify the types explicitly. - The
writeblock must always be specified in the interface to define what data can be written. - The
integrityblock is optional and only needed if you want to enforce integrity checks (like CRC).
interface TstIf_1 {
access {
read {
}
write {
SharedData
SharedData2
SharedData3
}
}
integrity {
crc16 {
}
crc32 {
SharedData
}
}
}
Data types are defined in IDDL (Interface Data Definition Language) files. IDDL provides reusable data structure definitions that are referenced by interfaces. The IDDL grammar supports:
- dataStructures block: contains all data type definitions
- StructDef: named structure with typed fields
IDDL Supported Types
- Primitive types:
bool,byte,int,float,double,unsigned int,string - Array types: Add an array suffix to the field name, e.g.,
fieldName[128]- Example:
byte buffer[256]
- Example:
- Initializers: Arrays and fields can have default values, e.g.,
byte data[16] = 0 - Note:
stringfields represent char array in generated code with size of initial init of the value.
A generic IDDL file follows this structure:
dataStructures {
<StructName> {
<Type> <FieldName>
<Type> <FieldName>[<Size>] = <InitValue>
// ... more fields
}
// ... more structures
}
Here’s a concrete example:
dataStructures {
SharedData {
integer cnt
integer dataId
byte dataPayload[1024] = 0
}
SharedData2 {
integer dataPayload[1024]
}
SharedData3 {
integer someConst = 5
}
FastData {
integer messageId
byte payload[512]
unsigned int timestamp
}
EventData {
byte Ready
byte Start
byte Done
}
}
These data structures are included in IDL files via include statements and referenced in interface access and integrity blocks (like SharedData in the TstIf_1 interface above).
The HML grammar supports:
- SharedMemory: shared-memory based profile with attributes like
memSize,baseAddress, etc. - CustomProfile: user-defined profiles
Each profile instance has a kind (type) and optional attributes:
Profiles {
SharedMemory sharedMemoryProfile1 {
memSize: 64MB
baseAddress: 0x80000000
}
}
Profiles are defined in HMML (Hardware Mapping Markup Language) template files. HMML defines reusable profile templates with common attributes for each profile type. These templates are then instantiated in HML files (e.g., mapping the sharedMemoryProfile1 instance to use attributes from the SharedMemory template).
The HMML grammar supports several profile kinds:
- SharedMemory: with attributes like
memSize,baseAddress,cacheable,policy,coherence,syncType,permissions,priority, etc.
A generic HMML file follows this structure:
Profiles {
<ProfileType> {
<AttributeName>: <Value>
<AttributeName>: <Value>
// ... more attributes
}
// ... more profile templates
}
Here’s a concrete example showing the SharedMemory profile template that backs sharedMemoryProfile1:
Profiles {
SharedMemory {
memSize: 64MB
baseAddress: 0x80000000
cacheable: True
policy: WriteBack
cacheLine: 64B
coherence: Software
syncType: AcquireRelease
permissions: RW
priority: High
syncBlocking: True
syncPrimitive: mutex
}
}
Built-in profiles (via --include-stdlib-profiles) come preconfigured and ready to use. HALO provides three standard protocol generators with the following platform support:
Built-in Profile Attributes
| Profile | Attribute | Meaning |
|---|---|---|
| SharedMemory | memSize | Size of the shared memory region (e.g., 64MB) |
baseAddress | Base address of the shared memory region | |
cacheable | Whether the region is cacheable (True/False) | |
policy | Cache policy: WriteBack, WriteThrough, or NonCacheable | |
cacheLine | Cache line size for software maintenance (e.g., 64B) | |
coherence | Coherence management: Software or Hardware | |
syncType | Synchronization type: AcquireRelease, FullFence, or None | |
permissions | Access permissions: RW (read/write), etc. | |
priority | Access priority: Critical, High, Medium, Low | |
syncBlocking | Whether synchronization is blocking (True/False) | |
syncPrimitive | Synchronization primitive used (e.g., mutex) | |
| Blackboard | maxPayload | Maximum payload size for a single sample |
bufferSize | Total buffer size for the blackboard | |
baseAddress | Base address of the blackboard memory region | |
| EventChannel | eventQueueSize | Size of an event that will be be queued |
maxChannels | Maximum number of event channels - 1 for one event | |
baseAddress | Base address of the event channel region | |
eventType | Type of event (e.g., signal) | |
timeout | Timeout for event operations (e.g., in ms) | |
blocking | Whether event operations are blocking (True/False) |
Select the appropriate profile based on your communication semantics needs and target platform availability.
The interface declares what data is accessible (read/write) and what integrity checks apply.
You can run the example like this:
halo compose --hadls-root _path_to_you_hadls_descriptions_ --output-dir ./output --include-stdlib-profiles
halo generate --composer-output-dir ./output --output-dir ./generated
What HALO generates
After composition, HALO writes the unified model and analysis artifacts into the composer output directory.
After generation, HALO emits platform-scoped packages. A typical layout in this repo looks like:
output/gen/codegen/
├── Core1_linux/
│ ├── include/
│ └── src/
├── Core2_freertos/
├── Core3_zephyr/
├── Core4_baremetal/
└── portable/
- generated HALO API headers such as
halo_api.h,halo_cfg.h,halo_interfaces.h, andhalo_structs.h, - generated HALO sources such as
halo_api.c,halo_channels.c,halo_bindings.c, andhalo_services.c, - platform-specific integration files such as
Linux_init.handLinux_init.c, - protocol-specific files such as
sharedmemory_linux.h,sharedmemory_linux.c, andsharedmemory_crc.h.
The portable/ package contains the common portable support layer, such as halo_types.h and halo_lib.h.
In practice, users add the generated portable and platforms include/ and src/ trees into their application build and call the generated init plus typed send/receive APIs from application code.
Protocol example
The repository includes a shared-memory protocol generator built in HALO Framework.
That generator supports multiple platforms and is responsible for emitting files such as:
sharedmemory_linux.csharedmemory_linux.hsharedmemory_crc.h
In the sample model, the LinuxToRTOS connection uses sharedMemoryProfile1, so the shared-memory protocol generator participates in code generation for the affected targets.
Create a new platform generator
HALO can scaffold a new platform generator for you.
Interactive mode:
halo create-generator
Non-interactive example:
halo create-generator --generator-type platform --no-interactive \
--name my_platform \
--description "My Platform Generator" \
--author "Your Name" \
--email "you@example.com" \
--platform my_platform \
--output-dir ./generators
Then install the generated package with pip:
cd ./generators/halo-generator-my_platform
pip install -e .
Once installed, HALO discovers it automatically through Python entry points and you can target it with:
halo generate --platform my_platform --composer-output-dir ./halo_out
Create a new protocol generator
HALO can also scaffold a new protocol generator.
Example:
halo create-generator --generator-type protocol --no-interactive \
--name my_protocol \
--description "My Protocol Generator" \
--author "Your Name" \
--email "you@example.com" \
--platform my_protocol \
--supported-platforms linux,freertos \
--output-dir ./generators
Then install it:
cd ./generators/halo-proto-my_protocol
pip install -e .
After that, HALO can use the new protocol generator whenever the unified model contains profiles handled by that generator.