Skip to content

Why another language ???

For the past fifty years, the C Programming Language has significantly streamlined software development for resource-constrained embedded microcontrollers [MCUs]. Already coming into maturity by 1980, C had proven itself as a practical “medium-level” language for many of the first 8 / 16 / 32-bit MCUs – enabling full entitlement to the underlying silicon, while offering developers greater software productivity and portability over native assembly language.

As a testimony to its staying power, C continues to this day as the dominant programming language for resource-constrained MCUs; and given that we still write embedded software targeting 8 / 16 / 32-bit processors – often with less than 32K of memory – do we really have a practical alternative to C ???   Yes, we do – EM.

Other embedded programming languages

Besides C and assembler, practitioners cite C++, Java, and Python as other languages of choice for embedded software development – with interest in Go and Rust starting to grow. For a variety of reasons, none of these languages come close to overtaking the dominant position of C:  developers often fear a steep learning curve with a new language, offsetting any potential gain in productivity; and the language itself might introduce additional runtime overhead in time and (especially) space that render it impractical for the most resource-constrained MCUs.

The big picture

The EM software platform comprises a novel programming language and run-time environment which targets resource-constrained embedded hardware – on the low-end, systems potentially managed by 8-bit MCUs using as little as 4 K of program memory and 256 B of data. Compared with C, EM features a higher-level programming paradigm centered around highly-reusable software modules which embody the principles of encapsulation, abstraction, and composition.

At the same time, higher-level language constructs introduced by EM don't necessarily cause higher-levels of run-time overhead; modular EM applications designed using modern software techniques will often outperform comparable C programs written in a more conventional style. As you'll learn later on, the EM language translator will in fact generate monolithic C/C++ programs from a set of EM source modules – leveraging the maturity and ubiquity of optimizing C/C++compilers for embedded MCUs.

More than a programming language, the EM platform also features a modular run-time environment (written in EM, of course !!!) which streamlines developer productivity through software re-use as well as increases application portability through abstraction – all without compromising overall system performance. The EM run-time modules include basic services which handle and dispatch real-time application events, as well as device-drivers which manage hardware peripherals typically found within embedded MCUs.

Write once, run anywhere

Not unlike other software platforms ranging from Java and Linux to Ruby and Python, EM offers up a "write once, run anywhere ..." value-proposition for its application developers. The difference, needless to say, lies in the underlying hardware configurations targeted by EM – memory-constrained MCUs otherwise incapable of supporting Java or Linux (let alone Ruby or Python), for which C has remained the dominant programming environment for over a half-century.

Tiny code → Tiny chips

EM programs typically consume less memory than their C counterparts – a critical feature when targeting resource-constrained MCUs. With 3 X – 5 X reductions in program size not uncommon in practice, many real-world EM applications can comfortably fit in (say) a 32K memory space.

The latest 32-bit MCUs deployed in edge-computing applications will often feature generous amounts of flash memory [ ≥ 512K ] for storing program code, as well as large blocks of SRAM [ ≥ 64K ] for reading + writing program data; these MCUs also feature processors with sophisticated pipelines as well as advanced memory caches – all boosting the performance of large (and ever-growing !!!) bodies of legacy C code used in today's applications.

But what if our EM-based applications really did require only 32 K of memory ???

We could take conventional MCU designs "over-the-edge" by embracing a radically simple set of hardware featues which collectively would define the fringe of embedded processing:

 entry-level CPU core    no larger than Arm Cortex-M0+
 tightly-coupled memories (TCMs)   code + data; ≤ 32K per bank; zero wait-state SRAM
 bulk flash and NO cache   TCMs loaded under firmware control
 rudimentary peripherals    no specialized auxiliary CPU cores
 always-on domain    wakeup events from deep-sleep power modes
 ≥ 100MHz system clock   no TCM wait-states → maximize CPU throughput

Features through would collectively minimize the number of logic gates and memory cells required to implement this MCU in silicon – resulting in a smaller chip die compared with a more fully-featured design, which (holding other factors constant) forecasts lower manufacturing costs as well as lower leakage power.

To support "sleepy applications" with short active-duty cycles, feature enables our MCU to execute for years on small batteries – or even indefinitely using harvested energy. Because of its relatively small die-size, our MCU could likely consume ≤ 1μW of power when sleeping; and by retaining all program state in the (small) TCMs of , our MCU can quickly return to its fully-powered active mode when awoken by an event.

Feature , however, seems at odds with achieving an energy-efficient design – faster clocks proportionally increase switching (dynamic) power consumption; but since the CPU can access its TCMs without stalling, increased clock speed also results in proportionally faster software execution – enabling our duty-cycled application to spend more time in deep-sleep. In practice, this approach actually improves overall energy(1)utilization by reducing the (static) overhead of leakage power during active execution periods.

  1. E = ∫  (Pleakage + Pswitching) dt

Given that feature enables faster execution, the peripherals of can now leverage software running on the main CPU core to perform functions that would otherwise require additional hardware – such as advanced encryption modes or the upper layers of a comm stack. Unlike conventional cryto or radio blocks (which often contain their own dedicated cores) our peripherals embody a "RISC-like" approach which efficiently implements only rudimentary primitives in hardware and then relegates higher-levels functions to software.

EM programs that target our idealized MCU will often exhibit a simple, cyclic structure:

wake-up from deep-sleep

acquire data from the environment

analyze this data using an algorithm

transmit results (wirelessly) to the edge

re-enter deep-sleep

Because of their single-threaded design, these programs would only require a single CPU core to maximize application throughput; software functions normally relegated (say) to an auxiliary cyrto or radio core can now execute exclusively on the main CPU. Consolidating all software onto a single core not only maximizes re-use of MCU logic elements, but can further reduce cost and power by minimizing the silicon footprint of certain peripherals.(1)

  1. We'll return to this topic further downstream.

So thanks to EM – 10 X fewer bytes, 10 X fewer transistors, 10 X lower power, 10 X lower cost !!!

The promise of RISC-V

The emergence of RISC-V as an open instruction-set architecture has triggered an abundance of innovation in MCU design; you can literally find dozens of open-source projects containing synthesizable cores expressed in languages like Verilog and VHDL. While much of the RISC-V community has set its sights on high-performance computing from the edge up to the cloud, some practitioners have focused on "tiny cores" suitable for low-power, low-cost applications on the fringe; visit the X-HEEP and NEORV32 projects as examples.

With more degrees of freedom when implementing the entry-level RV32ICM instruction set – often compared against the ARM Cortex-M0+ – MCU designers can truly innovate when realizing our earlier feature – all in the interest of further shrinking silicon and software:

  • by using an internal 8-bit or 16-bit ALU, optionally increasing to maintain throughput;

  • by locating in a compact (17-bit) address-space, further improving RV32ICM code-density; or

  • by adding specialized instructions to accelerate higher-level software functions supporting

Here again, the tiny-code of EM serves as a catalyst which can drive novel RISC-V tiny-chips.

Technical overview

Building upon the tiny code → tiny chips premise behind EM, let's dive into some technical details.  The following chapters each focus on a particular aspect of the language and its runtime enviorment, and collectively provide a technical overview of the EM platform:

1)  EM modules & interfaces ••• The client/server dichotomy  
2)  EM composites & templates ••• Assembling application elements  
3)  EM program life-cycle ••• From build-time to run-time  
4)  EM runtime bundles ••• Software platform content  

We encourage you to read these chapters in sequence, as each subsequent chapter builds on material covered by its predecessors. At the same time, we recognize that this document introduces a lot of (new) information about a (new) programming environment; feel free to proceed iteratively, skimming the content on your first pass before circling back for a more thorough reading.

The technical overivew also includes exemplary source-code fragments, written in the EM programming language and formatted as follows:

# we've omitted a few details here
# but you should get the basic idea

module Hello

def em$run()
    printf "Hello world\n"

Even if you don't plan to (initially) install and use EM, these fragments should give you an intuitive sense of the language – which relies upon familiar programming constructs (such as seen at line 8 above) that you've likely encountered elsewhere.

So with that, let's move onward to Chapter 1 and begin our technical overview of EM.

The history of EM

EM's origin story

The EM programming language first appeared at UC Santa Barbara in 2010, where undergraduate students taking CS190C / ECE1940 would develop “real-world” embedded applications targeting resource-constrained MCUs – with all software written in EM, of course, using this (now outdated) language primer as a guide.

The EM language had emerged through deep discussion and intense interaction with Amichai Amar – a UCSB PhD candidate at that time. Consult his thesis for more details on the outcome of our undergraduate course, as well as a more comprehensive exposition of programming resource-constrained MCUs using EM.

But the true EM backstory actually began decades before its debut at UCSB. In 1998, Texas Instruments acquired Spectron Microsystems – whose SPOX and DSP / BIOS products had already emerged as de facto industry standards. This milestone validated the importance of software technology in further solidifying TI's leadership as a DSP silicon vendor, and in fact triggered a flurry of similar acquisitions during the dot-com boom.

Once inside TI, the Spectron team broadened the application of its patented configuration technology – which had already enabled DSP / BIOS to fit comfortably within the 2 K boot ROM of broadly-deployed, low-power DSPs. This effort culminated in the open-source RTSC project, hosted at the Eclipse Foundation beginning in 2007 and still used today within TI software products. But RTSC fell a bit short in fulfilling its vision – and provided some much needed impetus for the birth of EM a few years later.

EM's coming-of-age began in 2011 with the founding of Emmoco – an early player offering an embedded ↔ mobile connectivity stack targeting new TI wireless MCUs which supported the emerging BLE standard. Acquired by Shelfbucks in late 2015, the Emmoco stack ultimately evolved to support long-range, low-power subGHz radios in which just one cloud-connected HUB could interact with (say) 10,000 TAGs in a 500,000 sq ft venue.

After Shelfbucks ceased operations in 2019 – and thanks to some legacy licensing agreements – EM found its way onto other low-power wireless MCUs from vendors such as NXP, Analog Devices, and ON Semiconductor; EM's foray into the world of RISC-V (as detailed in an earlier note) also began in this time frame. Targeting very high-volume applications for over a decade now, EM's uncanny ability to reduce firmware footprint proved critical in keeping system size, power, and cost in check.

As of today, EM has supported more than twenty 8 / 16 / 32-bit MCUs from almost a dozen silicon vendors. The EM language translator – which ultimately outputs ANSI C/C++ code for portability – has also targeted the most popular toolchains for embedded development [GCC, IAR, Keil, LLVM].  Thanks to a recent rewrite of the translator into TypeScript, EM now enjoys robust language support within the VS Code IDE.

More important, perhaps, just a handful of EM programmers have developed thousands of EM modules used (and often re-used ) across a broad spectrum of IoT applications targeting these MCUs. But due to the proprietary nature of these applications, the EM language and its runtime has remained closed – until now !!!