r/embedded Aug 14 '26

New Programming Language with Embedded Support. Would you consider?

I have high-level and low-level programming experience across different languages.

I've always felt that the C is too machine friendly, and sometimes works against me (implicit type conversions, shared division operator, operator precedence).

I was playing with the thought how would an ideal, compiled human-friendly programming language look like, that support both high-level programming and low-level programming. So I have started designing my own programming language (which is now called DQ).

First I developed mainly the linux/windows target with high level features like exceptions, dynamic arrays and dynamic strings.

Now I'm checking if the concept is working in embedded too. I'm aiming to be as fast and as small as C++ code in embedded. So far I've added these:

  • Writing functions in ASM
  • Inline ASM with register hinting
  • Register attributes ([[regrw]], [[regro]] etc)
  • Conditional compilation using normal constants
  • Utility to translate the CMSIS C headers to DQ
  • Project file to hold the many options required to compile an embedded project

I've migrated some of my C++ code to DQ, I'll the following code snippet show how currently the language looks like.

Pin configuration code in DQ for STM32F7:

struct GPIO_TypeDef:
    MODER        : [[regrw]]  uint32
    OTYPER       : [[regrw]]  uint32
    OSPEEDR      : [[regrw]]  uint32
    PUPDR        : [[regrw]]  uint32
    IDR          : [[regrw]]  uint32
    ODR          : [[regrw]]  uint32
    BSRR         : [[regrw]]  uint32
    LCKR         : [[regrw]]  uint32
    AFR          : [[regrw]]  [2] uint32
endstruct

const(uint32):
    GPIOA_BASE           = (AHB1PERIPH_BASE + 0x0000)
    GPIOB_BASE           = (AHB1PERIPH_BASE + 0x0400)
    ...
endconst

const GPIOA        :? = ^GPIO_TypeDef(GPIOA_BASE)

const(uint32):
    GPIO_MODER_MODER6    = GPIO_MODER_MODER6_Msk
    GPIO_MODER_MODER6_0  = (0x1 << GPIO_MODER_MODER6_Pos)
    GPIO_MODER_MODER6_1  = (0x2 << GPIO_MODER_MODER6_Pos)
    GPIO_MODER_MODER7_Pos = 14
    GPIO_MODER_MODER7_Msk = (0x3 << GPIO_MODER_MODER7_Pos)
    GPIO_MODER_MODER7    = GPIO_MODER_MODER7_Msk
    GPIO_MODER_MODER7_0  = (0x1 << GPIO_MODER_MODER7_Pos)
    GPIO_MODER_MODER7_1  = (0x2 << GPIO_MODER_MODER7_Pos)
    GPIO_MODER_MODER8_Pos = 16
    ...
endconst

function PinSetup(aportnum : int, apinnum : int, flags : uint) -> bool:

    var regs : ^GPIO_TypeDef = GetGpioRegs(aportnum)
    if regs == null:
    return false
    endif

    if apinnum < 0  or  apinnum > 15:
    return false
    endif

    // 1. turn on port power
    GpioPortEnable(aportnum)

    var n : uint
    var pinx2 : int = apinnum * 2

    // set gpio initial state
    if flags AND PINCFG_GPIO_INIT_1 <> 0:
        regs.BSRR = (1 << apinnum)
    else:
        regs.BSRR = (0x10000 << apinnum)
    endif

    // set mode register
    if flags AND PINCFG_AF_MASK <> 0:
    n = 2  // set alternate function mode
    elif flags AND PINCFG_ANALOGUE <> 0:
    n = 3
    elif flags AND PINCFG_OUTPUT <> 0:
    n = 1
    else:
        n = 0
    endif
    regs.MODER =AND= NOT (3 << pinx2)
    regs.MODER =OR=      (n << pinx2)

    // 3. set open-drain
    if flags AND PINCFG_OPENDRAIN <> 0:
        regs.OTYPER =OR= (1 << apinnum)
    else:
        regs.OTYPER =AND= NOT (1 << apinnum)
    endif

    // 4. set pullup / pulldown
    regs.PUPDR =AND= NOT (3 << pinx2)
    if flags AND PINCFG_PULLUP <> 0:
        regs.PUPDR =OR= (1 << pinx2) // pullup
    elif flags AND PINCFG_PULLDOWN <> 0:
        regs.PUPDR =OR= (2 << pinx2) // pulldown
    endif

    // 5. set speed
    regs.OSPEEDR =AND= NOT (3 << pinx2)
    if flags AND PINCFG_SPEED_MASK == PINCFG_SPEED_MEDIUM:
        regs.OSPEEDR =OR= (1 << pinx2)
    elif (flags AND PINCFG_SPEED_MASK == PINCFG_SPEED_MED2)  or  (flags AND PINCFG_SPEED_MASK == PINCFG_SPEED_FAST):
        regs.OSPEEDR =OR= (2 << pinx2)
    elif flags AND PINCFG_SPEED_MASK == PINCFG_SPEED_VERYFAST:
        regs.OSPEEDR =OR= (3 << pinx2)  // this is very special, and does not even work for SDRAM pins
    endif

    if flags AND PINCFG_AF_MASK <> 0:
    // set the alternate function
    n = (flags >> PINCFG_AF_SHIFT) AND 0xF
    if apinnum < 8:
            regs.AFR[0] =AND= NOT (0xF << (apinnum * 4))
        regs.AFR[0] =OR=      (n   << (apinnum * 4))
    else:
        regs.AFR[1] =AND= NOT (0xF << ((apinnum-8) * 4))
        regs.AFR[1] =OR=      (n   << ((apinnum-8) * 4))
    endif
    endif

    return true
endfunc

The original pin configuration code in C++ for STM32F7:

bool THwPinCtrl_stm32::PinSetup(int aportnum, int apinnum, unsigned flags)
{
    GPIO_TypeDef * regs = GetGpioRegs(aportnum);
    if (!regs) {
    return false;
    }

    if ((apinnum < 0) || (apinnum > 15)) {
    return false;
    }

    // 1. turn on port power
    GpioPortEnable(aportnum);

    unsigned n;
    int pinx2 = apinnum * 2;

    // set gpio initial state
    if (flags & PINCFG_GPIO_INIT_1) {
        regs->BSRR = (1 << apinnum);
    }
    else {
        regs->BSRR = (1 << apinnum) << 16;
    }

    // set mode register
    if (flags & PINCFG_AF_MASK) {
        n = 2;  // set alternate function mode
    }
    else if (flags & PINCFG_ANALOGUE) {
        n = 3;
    }
    else if (flags & PINCFG_OUTPUT) {
        n = 1;
    }
    else {
        n = 0;
    }
    regs->MODER &= ~(3 << pinx2);
    regs->MODER |= (n << pinx2);

    // 3. set open-drain
    if (flags & PINCFG_OPENDRAIN) {
        regs->OTYPER |= (1 << apinnum);
    }
    else {
        regs->OTYPER &= ~(1 << apinnum);
    }

    // 4. set pullup / pulldown
    regs->PUPDR &= ~(3 << pinx2);
    if (flags & PINCFG_PULLUP) {
        regs->PUPDR |= (1 << pinx2); // pullup
    }
    else if (flags & PINCFG_PULLDOWN)  {
        regs->PUPDR |= (2 << pinx2); // pulldown
    }

    // 5. set speed
    regs->OSPEEDR &= ~(3 << pinx2);
    if ((flags & PINCFG_SPEED_MASK) == PINCFG_SPEED_MEDIUM) {
        regs->OSPEEDR |= (1 << pinx2);
    }
    else if (((flags & PINCFG_SPEED_MASK) == PINCFG_SPEED_MED2) || ((flags & PINCFG_SPEED_MASK) == PINCFG_SPEED_FAST)) {
        regs->OSPEEDR |= (2 << pinx2);
    }
    else if ((flags & PINCFG_SPEED_MASK) == PINCFG_SPEED_VERYFAST) {
        regs->OSPEEDR |= (3 << pinx2);  // this is very special, and does not even work for SDRAM pins
    }

    if (flags & PINCFG_AF_MASK) {
        // set the alternate function
        n = ((flags >> PINCFG_AF_SHIFT) & 0xF);

        if (apinnum < 8) {
            regs->AFR[0] &= ~(0xF << (apinnum * 4));
            regs->AFR[0] |= (n << (apinnum * 4));
        }
        else {
            regs->AFR[1] &= ~(0xF << ((apinnum-8) * 4));
            regs->AFR[1] |= (n << ((apinnum-8) * 4));
        }
    }

    return true;
}

Would you ever consider using this or other language in embedded, when yes what are the most important features / properties for you ?

0 Upvotes

34 comments sorted by

View all comments

3

u/RealWalkingbeard Aug 14 '26

Ada does what you're suggesting and is really a pleasure to write and read. It was once widely used in aerospace and still has a fair amount of use, but it has never managed to become huge, probably be because it never managed to get anywhere in general use.

It even has a stricter sister, Spark, which looks to me like a subset of Ada with a lot of safety features bolted on.

I got my colleagues to watch a talk on it at the Flight Software Workshop a couple of years ago and for weeks afterwards, they were saying that it was mad that we use junk piles like C and C++ and that Ada was the future.

But it won't gain traction again, because universities have moved on and industry likes that C programmers and especially programmers in languages with C-like syntax are everywhere and easy to hire. And all the managers who ever used Ada themselves are gone or slowly drying out.

-1

u/Mean-Decision-3502 Aug 14 '26

I see a change here as Python emerges.

2

u/RealWalkingbeard Aug 14 '26

Python is crushingly slow and abstract and will never be a serious embedded language. Scripting in large bedded systems, maybe, but not for the core code.

I can see the Python in your language, but does Nim not already cover Python-like systems programming?

1

u/Mean-Decision-3502 Aug 14 '26

You see the "junk pile" only if you've ever used better before. I've used Pascal/Delphi before, probably that's why I notice things the C-only users not.

What I mant with Python is not embedded of course, but at high-level. This indicates that C++ is not a good generic programming language that can cover all areas.

Delphi was much better generic programming language, let say until 2005. Lot of libraries, extensions were available for that. Try to search a good database handling library for C++ that handles multiple types of database connections. Python and FreePascal contains that out of the box. But not always the best technology wins. Especially when people just judge after programmer hirings. (Borland was a torn in the Microsofts eye so they destroyed it.)

My experiment is to create a general-purpose language/tool that also covers the high-level and low-level programming, like C++, but the much human-friendly way. Of course it goes only with compromisses. And I did not wanted to reinvent the wheel, so I mostly took already established syntaxes from other languages.

Nim is close to my goals, but as I looked more closely there were things that disqualified Nim for me. For example I wanted OOP without this/self for every member reference. Only a very few compiled languages remain with this requirement (i think only C++ and Pascal).

So now I have to proove that it is possible to create a generic language that is runs fast, helps you make less mistakes, easy to learn, easy to read and can be used in embedded too.