Targets and conditional compilation¶
An ik program is compiled for one device at a time, chosen by a target
declaration. The target drives the register map, memory sizes, and interrupt
vectors, and it can be tested at compile time to include device-specific code.
The target declaration¶
Every program must declare its device at top level:
target atmega328p
The identifier is one of the supported device names (see
Supported devices, or run ./ik8b list-devices). The compiler refuses
to build a source file with no target, because nothing about the hardware is
known without it.
There is exactly one effective target per compilation. An imported file does not re-declare a different target; it inherits the target of the program being compiled.
Compile-time conditionals¶
A ? target == <device> { ... } block includes its contents only when the
selected target matches. At top level it gates declarations:
? target == atmega328p {
const %ADMUX: u16 = 0x007C
const %ADCSRA: u16 = 0x007A
}
? target == atmega32a {
const %ADMUX: u16 = 0x0027
const %ADCSRA: u16 = 0x0026
}
This is exactly how the standard library’s peripheral modules define the correct
register addresses for each device: a long series of per-device ? target ==
blocks, only one of which survives compilation. Everything in a non-matching
block is dropped entirely — it is not compiled, so it costs nothing and need not
even be valid for other devices.
A ? target == <device> block may also appear inside a function body,
where it gates statements rather than top-level declarations:
@setup {
? target == atmega328p {
0x07 -> %ADCSRA
}
}
Note the difference from the runtime conditional ? expr { ... } in
Statements: the compile-time form’s condition is specifically
target == <identifier> and is resolved by the compiler, emitting code for
the matching device only.
Writing portable modules¶
Combining register aliases (const %REG) with ? target == blocks is the
idiom for portable, multi-device code:
? target == atmega328p { const %PORTB: u16 = 0x0025 }
? target == atmega32a { const %PORTB: u16 = 0x0038 }
@led_on { 0x20 -> %PORTB }
The same source then compiles correctly for either device by changing only the
target line. The standard library is built this way; its peripheral modules
support the full device table out of the box.