micropython / micropython-esp32

Old port of MicroPython to the ESP32 -- new port is at https://github.com/micropython/micropython
MIT License
673 stars 216 forks source link

Support for ULP processor #154

Closed nickzoic closed 6 years ago

nickzoic commented 7 years ago

There's no support for the ULP processor yet. API docs: https://github.com/espressif/esp-idf/blob/master/docs/api-guides/ulp.rst

nickzoic commented 7 years ago

The CPU is different to the main CPU cores and is unlikely to ever run Python bytecode, but I was thinking it might be programmable from Python as a kind of a DSL.

eduvik commented 7 years ago

I'd suggest coming up with a bunch of use cases for it, then determining how much to generalise from that. If there's a small number, perhaps just implement some python hooks to those cases (might also be useful to do this for the most common cases, implemented in a DSL).

I'll get started with some use cases:

mattytrentini commented 7 years ago

I'm interested in that 3rd use case; logging data from a low-power sensor.

The ULP has access to the 8K of RTC memory (see the ULP coprocessor instruction set). So the idea would be to put the main processor to sleep, use the ULP to read the sensor periodically, fill up the 8K and wake the main processor to offload the 8K to persistent storage. Should use only a tiny amount of power.

I'm not sure what peripherals the ULP has access to exactly but that document above indicates it can work with at least I2C - which covers most of the sensors I typically deal with...

MrSurly commented 7 years ago

@nickzoic

In the deepsleep PR, I added a stubby for RTC memory -- the IDF wasn't ready then, either. I do seem to remember some examples, though?

STATIC mp_obj_t machine_rtc_memory(mp_uint_t n_args, const mp_obj_t *args) {

    if (n_args == 1) {
        // read RTC memory

        // FIXME; need to update IDF

        return mp_const_none;
    } else {
        // write RTC memory

        // FIXME; need to update IDF

        return mp_const_none;
    }
}
nickzoic commented 7 years ago

So I was thinking about this earlier: the ULP just needs a little bytestring of machine code sent to it to configure it, so as a first step having an esp.ulp.load(bytes) would work I think. Then the question becomes: from where do we get the bytes?

What I'd really like is a python bytecode -> ULP assembly translator but that might be a bit ambitious.

There's some precendent here in the Thumb assembler https://docs.micropython.org/en/latest/pyboard/reference/asm_thumb2_hints_tips.html and maybe we could do something similar for the ULP assembler. I'm not a fan of the syntax though.

I was messing around with with syntax, so you could create and load a ULP program something like:

with esp.ulp.ULP() as u:
    u.ADC(u.r1, 0, 0)
    u.ST(u.r1, u.r0, 0)
    u.ADD(u.r0, u.r0, 1)
    u.JUMPR('end', 1024, u.LT)
    u.WAKE()
    u.label('end')

(yeah, I haven't looked much this yet, so the above is just an idea of how it might work)

nickzoic commented 6 years ago

See https://github.com/micropython/micropython/pull/3578 for work on this. I'd still like to implement an interface like the above.

ThomasWaldmann commented 6 years ago

@nickzoic btw, I implemented an ULP assembler in micropython:

https://github.com/ThomasWaldmann/py-esp32-ulp/

Just can't test it yet on ESP32 without the load/run infrastructure of https://github.com/micropython/micropython/pull/3578 .