on
Programming language design
Embedded programming language design ideas
low-level register access
Kludges are required to access hardware registers in embedded system. They’re usually of the form:
struct UART_t
{
__IO uint32 status;
__IO uint16 data;
__IO uint16 dummy;
};
#define UART1_BASE 0x08000000
#define UART1 ((UART_t *) UART1_BASE)
void sendUART1(uint8 c)
{
UART1->data = static_cast<uint16>(c);
}
In this solution, the preprocessor is used to fix the address of UART1
to 0x08000000
. The storage class __IO
is usually equivalent to volatile
to make sure the compuler does not optimise away certain accesses, which are needed for correct behaviour. Because we’re dealing with a pointer in the above example, the UART registers are accessed through the pointer UART1
.
Languages designed for programming embedded systems should have a clearer way of specifying intent, rather than using the preprocessor. This also allows a debugger and syntax highlighter to more easily extract information from the source code.
The following example shows a possible language implementation to access hardware registers:
struct UART_t
{
uint32 status;
uint16 data;
uint16 dummy;
};
register(0x08000000) UART_t UART1;
register(0x08001000) UART_t UART2;
register(0x08002000) UART_t UART2;
register(0x08100000) uint32 timer;
void sendUART1(uint8 c)
{
UART1.data = static_cast<uint16>(c);
}
Here, the keyword register
tells the compiler a hardware register is involved and to apply the necessary optimisation restrictions. The absolute address is given by the number between parentheses. In this scenario, UART1
is no longer a pointer but a variable placed at a fixed position in the address space. Accessing the UART registers now happens using the member selector .
instead of a pointer.
bit field access
Hardware registers consists of bit-fields which need to be accessed independently. SDK vendors provide non-standard ways of manipulating these bit-fields through preprocessor macros.
The following method for bit-field access is proposed:
void sendUART1(uint8 c)
{
UART1.data.bits(7:0) = c;
}
bool UART1_txEmpty()
{
return static_cast<bool>(UART1.status.bit(3));
}