Hacker News new | ask | show | jobs
by AYoung010 1466 days ago
Call me old-fashioned, but I personally do not like the idea of using a garbage-collected language for embedded development in general, and it's just flat out non feasible to use for anything which is timing-sensitive...
8 comments

Astrobe, PTC, Aicas and microEJ disagree on that front.

https://www.astrobe.com/

https://www.ptc.com/en/products/developer-tools/perc

https://www.aicas.com/wp/products-services/jamaicavm/

https://www.microej.com/

Also I should note that many embedded computers in 2022 are supercomputers when compared to the 1961 IBM mainframes running Lisp.

In big parts that's true, but there are some good arguments in favor of a GC for these small devices:

- the GC is a sliding compacting GC. As such there is no fragmentation for heap-allocated memory.

- the memory on these devices is tiny. The GC thus doesn't take long time.

- the ESP32 has really good peripherals, making timing sensitive operations very rare. In most cases there is a hardware module that does the job for you. The timing requirements then usually become significant less important.

What do you think is the ratio between projects that need precise timing and projects that publish some sensor data every 60 seconds?

I think you can easily do 90% of iot projects with this. Which means you have more time for the 10% of projects that are actually hard.

From a functional PoV, yes, timing seems to be relaxed by publishing sensor data every 60 seconds.

From an embedded system PoV, your sensor might be a 1-Wire protocol device with a super strict signal timing. How do you mix both timing requirements? How do you differentiate embedded projects requiring "precise timing" from those others requiring "every 60 seconds"?

Unless Toit provides drivers for all kind of buses, sensors and peripherals AND it orchestrates the GC with the strict timing requirements. Otherwise it's probably a good fit only if it just runs on a known controlled development board/architecture/platform.

We rely heavily on the ESP32 peripherals for strict timings.

For example the 1-wire protocol is implemented using the RMT (remote control) peripheral. The pixel strips driver uses the UART or i2s.

If really necessary, we could drop down to C, but the hardware is usually better (more precise and leaves us to do other things in the meantime).

Exactly, that's why "it's probably a good fit only if it just runs on a known controlled development board/architecture/platform".

Maybe the title should be "Toit is a modern high-level language designed specifically for ESP32".

Literally every uC has hardware blocks for that, 99% of the stuff you will ever encounter is a standard. Nobody bitbangs protocols. And even if, the ws2816 driver on the raspberry pi is bitbanged from Linux user-space.

How often are you bitbanging non standard protocols? And if so, which field are you in?

> Literally every uC

Not true at all.

> How often are you bitbanging non standard protocols?

Regarding protocols: bitbanging 1-Wire. Not all microcontrollers have this peripheral. You might be lucky if you can adapt some other peripheral like an UART (with due effort). Or you can use for example an external ds2482 and drive it through I2C.

WS2812B: sometimes you can emulate it with some other peripheral, sometimes you have to do it by bitbanging some GPIO.

I don't bitbang so often in general. But it's not just about available "hardware blocks": introducing undesidered delays in embedded applications is a terrible, terrible idea.

You can have a perfectly working sensor through I2C with DMA, super optimized with 0 CPU intervention. This sensor has an ODR of 1kHz. A GC stopping the microcontroller for 400ms makes you stand behind 400 samples you might be processing in real time (FIFO aside, and because you know you shouldn't be doing any processing in an interrupt handler, right?). You don't lose the buffered samples of course, but your processing is now delayed.

Or perhaps you are doing some 44100, 16-bit stereo audio processing (in or out). You have implemented the perfect I2S/DMA mechanism and all that, combined with perfect SDIO reading. What would happen in a real-time processing audio application if the GC introduces even 20ms delays now and then? Horrible latency and/or artifacts... And because your MCU has 32KB of RAM you don't have the luxury of big buffers, so you have to be quick reading, processing and outputting. Stopping the processing = stopping the output.

Or you might be dealing with some external peripheral that needs an answer within a time limit...

You can see it's not just about "hardware blocks".

Following what I said before, even if "60 seconds" is the application required timing, there are other things that might be happening in the background that require precise timing.

Is Toit a bad language? I don't know. But as I said before, it only makes senses if ran under a known platform like ESP32 where the language developers have tuned everything, and the programer doesn't pretend real-time applications. A generic language for every microcontroller? I am not so sure...

If you're using LoRaWAN to publish and don't want to blow up your power budget, you need to get somewhat good timing.
Sub-millisecond?
You're old-fashioned. It's not like malloc/free provide real-time guarantees either, but somehow we don't get complaints about C being unsuitable.
That's why you don't use it on small microcontrollers (8/16 bit). And if you absolutely have to, use it once on startup to implement a dynamic memory pool.
Many (most?) embedded C projects have a policy of no dynamic memory allocation.
I just searched for esp32 projects/libraries on github, the first 3 had malloc calls.

Maybe they don't do[1] dynamic memory allocation in code segments that have real time constraints, or after initialization, etc. (Most embedded projects actually don't have hard real-time constraints, or have them in extremely localized places like bit banging loops...) This is sounds like a good idea and is orthogonal to whether the language's dynamic allocation support is via malloc/free or gc.

Yeah fair. But I think that any gc language for microcontrollers should have a couple features:

1) A compiler flag or similar optional static check that throws a warning if you have dynamic memory allocation. In scripting languages it can be easy to do accidentally

2) A way to block the gc from happening in a time critical section of code, like how you can disable interrupts

3) Some guarantee on the maximum time gc will take

MicroPython provides a way to stop the GC (and an exception will be raised if memory is attempted to be allocated). This provides us with most of the control we require...
I have sympathy for the sentiment, but e.g. MicroPython on the Raspberry Pico allows cycle-correct deterministic timing using its eight state machines (and a state machine assembler in Python): https://docs.micropython.org/en/latest/library/rp2.StateMach...
I don't know about Toit, but many embedded applications will not allocate post-initialization time, in which case GC or not GC behave exactly the same with regards to timing.
There are algorithms for doing garbage collection in real-time systems, but these tend to have more overhead in total than stop-the-world GC.

On microcontrollers, which often have very limited RAM, I'd prefer avoiding doing any heap allocation at all. If there is any need for any dynamic allocations, they are within fixed-size memory pools — each with only fixed-size objects or with objects on a stack.

depends on how the system works. What are the timing requirements? How powerful is the CPU? How much memory do you have? How often will the system be reset? Embedded isn't just 8/16 bit CPUs with < 128k of memory these days.