The CPU I've chosen for the project is the ESP32. Of course, this chip is normally used in an embedded context, not as a CPU in a general-purpose PC. This may seem like an odd choice - and it is, part of the fun is trying to get this to work. But when you consider the non-goals of the project, it starts to make sense:
- The ESP32 uses little power.
- The ESP32 has WiFi capabilities tightly integrated; adding an external wireless radio would consume more power and couldn't be controlled as granularly.
- The ESP32 is cheap - a single chip is $3!
- The ESP32 is hackable (in the good way). It has lots sensors integrated (touch, ADC, DAC, amplifier, Hall effect, temperature...), good I/O (SPI, I2C, Ethernet, WiFi (duh), 3x serial...) and is simple due to its position as a microcontroller and not necessarily a CPU.
- The ESP32 has an integrated low-power coprocessor and can sleep with WiFi on.
- The ESP32 has a detailed datasheet and technical reference manual.
- I can get my hands on a single ESP32; I can't get a single BCM2837.
Of course, there are some obvious downsides to trying to use a microcontroller as a general-purpose user-facing CPU:
- It will be slower than an e.g. ARM chip designed to be used with GNU/Linux. Although it might not actually be that bad with only a terminal and 240MHz. Of course, for actual work one would probably want to just run ssh/mosh to a server somewhere.
- Of course, the lack of out-of-order execution means the Spectre vulnerabilities are not present. I am pretty sure Meltdown is not a problem for the xtensa architecture, either, but I will ensure it is safe from both once Linux is ported.
- The Flash and RAM are very limited. Some of the docs (ESP-PSRAM, esp-idf, and hardware datasheet) contradict each other, but according to Sprite_TM "[the ESP32 can] support 16MiB of flash and 8MiB of SPI RAM. However, we don't have address ranges to keep that all mapped at the same time: the psram can have maximally 4MiB mapped at the same time, the flash can has 4MiB mapped as data memory and (iirc) 4MiB as instruction memory... I think there are 2 other 4MiB regions for the flash, but they are mappings for instruction memory and I'm not 100% sure if they're usable out of the box."
- The Linux kernel, including the Xtensa port, includes support for XIP allowing most of the kernel code to remain in flash. Especially if I can get any of the unused instruction memory regions mentioned at the end of Sprite_tm's quote above working, it should be enough space for the kernel. As the LWN articles below point out, the ongoing "constification" effort helps not only security and code correctness but has the side benefit of allowing more variables to be stored in static Flash instead of loading them into RAM.
- The ESP32 has an SD/MMC host controller that should allow it to read from SD cards, so the rootfs could probably be stored on there.
- Even if esp-idf doesn't have drivers for everything, one of the most appealing features of GNU/Linux are its large set of drivers. It will probably be easier to port from other SD & sensor drivers than to try to port from esp-idf.
- LWN has an interesting guide on shrinking the Linux kernel that covers techniques such as LTO and XIP with a step-by-step tutorial.
- While the ESP32 technically has an MMU, it is not advanced enough to be compatible with GNU/Linux. If we dig into the ESP32 OpenOCD code, we find the processor is a variant of the Diamond 108Mini which is described by Cadence as being very low power (good!) but below the 232L, the first officially GNU/Linux-capable one on the list.
- Fortunately, obscure kernel features come to the rescue yet again! MMU-less GNU/Linux is possible in mainline (ergo with linux-xtensa which has also been mainlined). That comes with some problems as detailed in the link above, most notably the lack of fork().
- Something I'm not sure about is the security impact of MMU-less GNU/Linux - although I haven't tested it yet, the lack of virtual paging would imply that every process can see and modify any memory, even that of the kernel. I can't find much information online about this (not even an admonishment of running untrusted software on no-MMU GNU/Linux) probably because this is mostly used on embedded platforms that would only ever run code from the manufacturer (not the case with a PC).
- MMU-less Linux has the drawback that it can't run normal ELF binaries. If everything is built as FDPIC (essentially ELF but with PIE enforced everywhere) stuff can run though, and if I use Alpine Linux (which seems like it might work given its minimalism) I will be using musl, so I can use the musl cross toolchain that supports FDPIC.
- Fortunately, obscure kernel features come to the rescue yet again! MMU-less GNU/Linux is possible in mainline (ergo with linux-xtensa which has also been mainlined). That comes with some problems as detailed in the link above, most notably the lack of fork().
- The ESP32 doesn't come with a native USB port. The MAX3421E can handle the USB host side and send back the data over SPI (which the ESP32 does have), but it costs $10 per chip - 3 times more than the CPU it's connecting to! I will definitely try to find a cheaper, similar chip before making boards.
- Considering how much that chip costs, I definitely will not integrate a standard USB keyboard chip into the custom keyboard. Maybe I'll use PS/2 or just scan manually using the ULP coprocessor? I don't know, but I kind of want to find a way to use PS/2 simply because "ESPS/2" is a great name for a library ;)
What I might do during prototyping is first design an "ESPi" that breaks out all the stuff I want to use (USB port, Ethernet, SPI, etc.) in a Raspberry Pi (Zero?)-esque form factor and run tests on that, perhaps selling a few to raise money for the final prototype. In fact, I'd be willing to give them away to people who'd help me with kernel & driver development.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.