Well then: twelve years on since I last updated the hardware, it is finally time for another version of the UltraMod. I guess what triggered it was that silicon has moved on enough that I could do what I always wanted from the beginning: totally replace the EPROM/Flash chip holding the ECU fimware with a processor that was fast enough to just pretend that it was an EPROM. That would make ECU firmware updates trivial, as easy as updating the new processor's flash memory. To that end, the good people at Raspberry Pi Foundation had introduced the RP2040, a dual-core ARM M0+ processor chip for $1. That opened to door to all kinds of possibilities. The one that intrigued me the most is that because of the shared memory model between the two cores, it would mean that one core could potentially updated the ECU tables used by the other core while the ECU was operating. In other words, it would be possible to adjust tuning parameters while literally driving down the road.
It had to happen...
The new version got a major revision number bump from V3 to V4. The big changes:
The old Flash memory chip for ECU firmware would be replaced by a processor core pretending to be a Flash
Wifi support to allow for downloading ride data without physically removing the MicroSD card or plugging in a cable
GPS updated to a more modern uBlox NEO-8
Support for a small LCD display, because why not?
The main technical challenges:
Interfacing the old 5.0V ECU memory bus to a 3.3V processor
Proving that the new processor will be capable of fooling the ECU into thinking that it was a flash memory
The new RP2040 processor is the little square thing in the upper right. The three chips inside the 28-pin EPROM footprint on the right are to mange voltage conversion between the 5 Volt ECU power domain and the 3.3 Volt domain used on the UMod4. The GPS will mount on the upper left. The micro SD card mounts on the center left. The large 40 pin package on the lower left is for a Raspberry Pi Pico W which is where the board will get its wireless interface capabilities.
More details on the first hardware version can be found here.
Finally, there are some practical challenges. Mostly, the new RP2040 processor is just too small for me to solder myself, even using my reflow controller. Therefore, I will be using JLCPCB's fabrication service to not just make the PCB, but to install all the parts too. I'll be figuring out all of that as I go. For now, what it means for the design phase is that I need to limit my choices of parts to things that LCSC.com has in stock.
Before getting started with the PCB, I needed to prove that the RP2040 would be capable of pretending to be an EPROM. Fortunately, I could do those experiments using a Raspberry Pi Pico board. It was good to get back to coding in assembly language again. When I started my career, that was what it was all about. All these years later, it was what I needed again.
The short version is that since the chip has such a well-defined job to do merely pretending to be a memory chip, it means that my software can cut any conceivable corner to get that job done in the required amount of time. For example, there are no subroutines in the software because it's too simple to need any. But not having subroutines means that I can use the processor's Stack Pointer register as an extra general purpose register. Putting a constant in that new general purpose register saves me 8 nanoseconds inside my loop so that I don't have to load the constant from memory. Given that I only have roughly 300 nanoseconds to get read data out on the IO bus, saving every 8 nanoseconds begins to matter.
There were basically 4 things that needed to be proven to make sure everything would work:
The RP2040 would need to respond quickly and deterministically whenever the ECU began a cycle on its external memory bus
The RP2040 would need to detect when it was not the target of an external memory cycle and ignore the remainder of the bus transaction without disturbing the ECU memory bus
The RP2040 would need to detect a Read operation targeting the original EPROM address space, then:
supply the appropriate ECU memory data within the appropriate amount of time to meet the ECU bus specification,
drive the data bus to meet the hold time after the read cycle completed, then
release the bus to complete the cycle
The RP2040 would need to detect a Write operation targeting the original EPROM address space, then:
delay until the write data was known to be present on the bus according to the ECU bus specification, then
grab the write data and address information from the bus, then
send the address and data information to the other processor core to begin the data logging process
All 4 of these things need to occur within strict timing limits.
The test mechanism was to write the software and prove that the timing was correct. I would not be able to completely emulate the EPROM on a Pi Pico because it would not manage being able to tolerate being connected to a 5 volt memory bus. Instead, I would presume that the Address and data lines could be read and driven to the IO bus and just concern myself with the timing aspects of responding to the bus operations.
To begin with, I added a GPIO output pin for debugging the various timing scenarios. The debugging pin would be driven high as soon as the fake EPROM software came to life for any reason. The IO pin would get driven low again as soon as the fake EPROM software finished whatever operation was required of it. Using a scope, I would be able to get a visual indication of things showing the start and end of each possible memory cycle. Of course, this debug signal is strictly for visualization on a scope or logic analyzer. The software needed to be designed and written in a fashion that allowed the timing to be proven correct by construction. The scope would merely confirm that my understanding of the timing relationships were as I believed them to be. And in fact, the debug signal taught me a thing or two about the unexpected timing of certain ARM instructions targeting the NVIC interrupt controller. All good!
The Fake Eprom experiments are documented here.