Latest Update: Device definition language fully operational. C header generation from .bitn device files produces register structures, bit field macros, and inline accessor functions.
cd ~/bit-n
bash bitN_setup.sh./build/bitN --compile device.bitnCreates: device.h with register structures and bit field accessors
Define hardware peripherals with registers and bitfields:
peripheral UART {
base_address: 0x40000000
register CONTROL {
type: u32
offset: 0x0
field ENABLE {
start_bit: 0
end_bit: 1
access: rw
}
field BAUDRATE {
start_bit: 1
end_bit: 16
access: rw
}
}
register STATUS {
type: u32
offset: 0x4
field TX_READY {
start_bit: 0
end_bit: 1
access: ro
}
field RX_READY {
start_bit: 1
end_bit: 2
access: ro
}
}
}
#define UART ((volatile UART_t *)0x40000000)
typedef struct {
uint32_t CONTROL; // @ offset 0x0
uint32_t STATUS; // @ offset 0x4
} UART_t;// Read operations
#define CONTROL_ENABLE_GET(reg) (((reg) >> 0) & 0x1)
#define CONTROL_BAUDRATE_GET(reg) (((reg) >> 1) & 0x7fff)
#define STATUS_TX_READY_GET(reg) (((reg) >> 0) & 0x1)
// Modify operations
#define CONTROL_ENABLE_SET(reg, val) (((reg) & ~(0x1 << 0)) | ((val & 0x1) << 0))
#define CONTROL_BAUDRATE_SET(reg, val) (((reg) & ~(0x7fff << 1)) | ((val & 0x7fff) << 1))static inline uint32_t CONTROL_ENABLE_read(uint32_t reg) {
return (reg >> 0) & 0x1;
}
static inline uint32_t CONTROL_ENABLE_write(uint32_t reg, uint32_t val) {
return (reg & ~(0x1 << 0)) | ((val & 0x1) << 0);
}
static inline uint32_t CONTROL_BAUDRATE_read(uint32_t reg) {
return (reg >> 1) & 0x7fff;
}
static inline uint32_t CONTROL_BAUDRATE_write(uint32_t reg, uint32_t val) {
return (reg & ~(0x7fff << 1)) | ((val & 0x7fff) << 1);
}Define a hardware peripheral with memory-mapped base address:
peripheral NAME {
base_address: 0xADDRESS
...
}
Example:
peripheral GPIO {
base_address: 0x50000000
...
}
peripheral I2C {
base_address: 0x60000000
...
}
Define registers within a peripheral:
register NAME {
type: u32 # u8, u16, u32, or u64
offset: 0x0 # Offset from base_address
...
}
Example:
register CONTROL {
type: u32
offset: 0x0
}
register STATUS {
type: u32
offset: 0x4
}
register DATA {
type: u16
offset: 0x8
}
Define individual bit fields within registers:
field NAME {
start_bit: 0 # Starting bit (inclusive)
end_bit: 8 # Ending bit (exclusive)
access: rw # ro, wo, rw, w1c
}
Example:
field ENABLE {
start_bit: 0
end_bit: 1
access: rw
}
field BAUDRATE {
start_bit: 1
end_bit: 16
access: rw
}
field ERROR {
start_bit: 16
end_bit: 32
access: ro
}
- ro - Read-only
- wo - Write-only
- rw - Read-write
- w1c - Write-1-to-clear
peripheral UART {
base_address: 0x40000000
register CONTROL {
type: u32
offset: 0x0
field ENABLE { start_bit: 0, end_bit: 1, access: rw }
field TX_EN { start_bit: 1, end_bit: 2, access: rw }
field RX_EN { start_bit: 2, end_bit: 3, access: rw }
}
register STATUS {
type: u32
offset: 0x4
field TX_READY { start_bit: 0, end_bit: 1, access: ro }
field RX_READY { start_bit: 1, end_bit: 2, access: ro }
}
register DATA {
type: u8
offset: 0x8
}
}
peripheral GPIO {
base_address: 0x50000000
register DIR {
type: u32
offset: 0x0
field OUTPUT {
start_bit: 0
end_bit: 32
access: rw
}
}
register OUT {
type: u32
offset: 0x4
field PIN_STATE {
start_bit: 0
end_bit: 32
access: rw
}
}
register IN {
type: u32
offset: 0x8
field PIN_STATE {
start_bit: 0
end_bit: 32
access: ro
}
}
}
peripheral TIMER {
base_address: 0x40001000
register COUNTER {
type: u32
offset: 0x0
field VALUE {
start_bit: 0
end_bit: 32
access: rw
}
}
}
peripheral I2C {
base_address: 0x40002000
register CONTROL {
type: u8
offset: 0x0
field START { start_bit: 0, end_bit: 1, access: wo }
field STOP { start_bit: 1, end_bit: 2, access: wo }
field ACK { start_bit: 2, end_bit: 3, access: rw }
}
}
cat > my_device.bitn << 'EOF'
peripheral UART {
base_address: 0x40000000
register CONTROL {
type: u32
offset: 0x0
field ENABLE { start_bit: 0, end_bit: 1, access: rw }
}
}
EOF./build/bitN --compile my_device.bitn#include "my_device.h"
int main() {
volatile UART_t *uart = UART;
// Enable UART
uart->CONTROL = CONTROL_ENABLE_SET(uart->CONTROL, 1);
// Check if enabled
if (CONTROL_ENABLE_GET(uart->CONTROL)) {
// UART is enabled
}
return 0;
}./build/bitN device.bitnParse and display device structure
./build/bitN --compile device.bitnGenerate C headers (creates device.h)
./build/bitN --compile device.bitn --verboseGenerate with detailed output
./build/bitN -c 'peripheral TEST { base_address: 0x1000 }'Parse inline device definition
File Contents:
peripheral UART { ... }
Generated C header with:
- Memory-mapped structure
- Base address macro
- Register definitions
- Bit field getter macros
- Bit field setter macros
- Inline accessor functions
For each bitfield, the compiler generates:
Read Function:
static inline TYPE REGISTER_FIELD_read(uint32_t reg) {
return (reg >> start_bit) & MASK;
}Write Function:
static inline uint32_t REGISTER_FIELD_write(uint32_t reg, TYPE val) {
return (reg & ~(MASK << start_bit)) | ((val & MASK) << start_bit);
}These compile to optimal inline code with zero overhead.
Supported register types:
u8- 8-bit unsignedu16- 16-bit unsignedu32- 32-bit unsignedu64- 64-bit unsigned
Bitfield types are automatically inferred from bit ranges.
# ✅ Good
field TX_READY { start_bit: 0, end_bit: 1, access: ro }
field RX_ERROR { start_bit: 8, end_bit: 16, access: ro }
# ❌ Avoid
field F1 { start_bit: 0, end_bit: 1, access: ro }
field F2 { start_bit: 8, end_bit: 16, access: ro }
# ✅ Organize by function
field ENABLE { start_bit: 0, end_bit: 1, access: rw }
field MODE { start_bit: 1, end_bit: 4, access: rw }
field STATUS { start_bit: 4, end_bit: 8, access: ro }
# ❌ Scattered layout
field ENABLE { start_bit: 15, end_bit: 16, access: rw }
field MODE { start_bit: 0, end_bit: 3, access: rw }
# ✅ Accurate access control
field CONTROL { start_bit: 0, end_bit: 8, access: rw }
field STATUS { start_bit: 8, end_bit: 16, access: ro }
field CLEAR { start_bit: 16, end_bit: 32, access: w1c }
# ❌ Overly permissive
field STATUS { start_bit: 8, end_bit: 16, access: rw } # Should be ro
This means your .bitn file contains functions instead of peripheral definitions.
Wrong:
proc main(): u32 = return 42
Correct:
peripheral UART {
base_address: 0x40000000
...
}
- Check file permissions
- Ensure --compile flag is used
- Verify peripheral definitions exist
- Check for parse errors in output
Ensure bit ranges don't overlap and are within register width:
# ✅ Valid
field FIELD1 { start_bit: 0, end_bit: 8, access: rw }
field FIELD2 { start_bit: 8, end_bit: 16, access: rw }
# ❌ Invalid (overlapping)
field FIELD1 { start_bit: 0, end_bit: 8, access: rw }
field FIELD2 { start_bit: 4, end_bit: 12, access: rw }
# Add code generation as build step
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/device.h
COMMAND ${BITN_COMPILER} --compile ${CMAKE_CURRENT_SOURCE_DIR}/device.bitn
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/device.bitn
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
add_library(device_headers ${CMAKE_CURRENT_BINARY_DIR}/device.h)project/
├── bitn/
│ ├── uart.bitn
│ ├── gpio.bitn
│ └── timer.bitn
├── include/
│ ├── uart.h # Generated
│ ├── gpio.h # Generated
│ └── timer.h # Generated
└── src/
├── main.c
└── drivers/
For each peripheral definition, you get:
✅ Memory-mapped structure - Correctly sized and aligned ✅ Base address macro - Easy peripheral access ✅ Register fields - Proper offsets ✅ Bit field getters - Safe field reading ✅ Bit field setters - Safe field modification ✅ Inline functions - Zero-overhead abstractions
Generated code:
- Compiles to optimal machine instructions
- No runtime overhead
- Inlined by C compiler
- Zero-cost abstractions
Example optimization:
// What you write:
uart->CONTROL = CONTROL_BAUDRATE_SET(uart->CONTROL, 9600);
// What assembler sees (approximate):
mov eax, DWORD PTR [rsi] // Load CONTROL
and eax, 0xfffffe // Clear baudrate bits
or eax, 0x25b8 // Set new baudrate
mov DWORD PTR [rsi], eax // Store back- Parse peripheral definitions
- Generate C headers
- Create register structures
- Generate bit field macros
- Create accessor functions
- Handle multiple devices
- Automatic file naming
- Command-line compilation
- Interrupt configuration
- DMA descriptors
- Advanced bitfield patterns
- Code templates
- Documentation generation
See EXAMPLE_uart_device.bitn in workspace for a complete working example.
- Implementation Details: See
IMPLEMENTATION.md - Development Roadmap: See
ROADMAP.md - Strategic Vision: See
VISION.md
Ready to generate C headers from device definitions! 🚀
