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));
}