micropython / micropython-esp32-ulp Goto Github PK
View Code? Open in Web Editor NEWESP32 ULP Co-Processor toolchain implemented in MicroPython
License: MIT License
ESP32 ULP Co-Processor toolchain implemented in MicroPython
License: MIT License
Hmm, AFAICS these 2 instructions only write constant data (given as immediate data, stored inside the instruction) to registers or i2c.
So, how would one write variable data?
Firstly, thanks for contributing this to Micropython. It seems very promising.
Unfortunately, I am unable to run the example code. I get the following error:
Traceback (most recent call last):
File "<stdin>", line 37, in <module>
OSError: 260
This occurs at ulp.load_binary(load_addr, binary)
.
Googling the error it seems to be an issue with the binary.
The binary generated is:
>>> print(binary)
b'ulp\x00\x0c\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x80r\x0e\x00\x00\xd0\x1a\x00\x00r\x0e\x00\x00h\x00\x00\x00\xb0'
Expressions with hex characters in uppercase cause assembly to fail with the error ValueError: Unsupported expression
.
The expected behaviour is that uppercase hex characters are treated identically to lower case hex characters.
This code fails to assemble with "Unsupported expression"
entry:
move r0, 0x7FE - 1
While this code is successfully assembled without error:
entry:
move r0, 0x7fe - 1
The only difference in the two code snippets is the hex value 0x7FE being upper case in one example (the failing one) and lower case in the other (the successful one).
... does not work yet. neither for jumping to labels, nor for anything else.
Greetings!
Thank you for the great lib for micropython.
I could have done many things with your lib, controlling lcd brightness during deepsleep,
communicate with between ULP and main core. It is so great!
I've asked in micropython forum, But I cannot still firgure out hot to get the I2C RD/WR.
I've got some sensors which doesn't have interrupts, I would like to wirte and read a single byte.
Referring the ESP32 technical reference manual,
, intializing I2C connection, but it doesn't work.
Could you please help me to get there?
#define DR_REG_RTCIO_BASE 0x3ff48400
#define DR_REG_RTC_I2C_BASE 0x3ff48C00
#define DR_REG_SENS_BASE 0x3ff48800
#define SENS_SAR_SLAVE_ADDR1_REG 0x3FF4883C
#define SENS_I2C_SLAVE_ADDR0 0x000007FF
#define SENS_I2C_SLAVE_ADDR0_S 11
#define SENS_I2C_SLAVE_ADDR1_S 0
#define RTC_IO_SAR_I2C_IO_REG 0x3FF484C4
#define RTC_IO_SAR_I2C_SDA_SEL_S 30
#define RTC_IO_SAR_I2C_SCL_SEL_S 28
#define RTC_I2C_SCL_LOW_PERIOD_REG 0x3FF48C00
#define RTC_I2C_SCL_HIGH_PERIOD_REG 0x3FF48C38
#define RTC_I2C_SCL_LOW_PERIOD_S 0
#define RTC_I2C_SCL_HIGH_PERIOD_S 0
#define RTC_I2C_SDA_DUTY_REG 0x3FF48C30
#define RTC_I2C_SDA_DUTY_S 0
#define RTC_I2C_SCL_START_PERIOD_REG 0x3FF48C40
#define RTC_I2C_SCL_START_PERIOD_S 0
#define RTC_I2C_SCL_STOP_PERIOD_REG 0x3FF48C44
#define RTC_I2C_SCL_STOP_PERIOD_S 0
#define RTC_I2C_TIMEOUT_REG 0x3FF48C0C
#define RTC_I2C_TIMEOUT_S 0
#define RTC_I2C_CTRL_REG 0x3FF48C04
#define RTC_I2C_MS_MODE_S 4
#define DR_REG_RTCIO_BASE 0x3ff48400
#define RTC_IO_TOUCH_PAD0_REG 0x3ff48494
#define RTC_IO_TOUCH_PAD1_REG 0x3ff48498
#define RTC_IO_TOUCH_PAD2_REG 0x3ff4849c
#define RTC_IO_TOUCH_PAD3_REG 0x3ff484a0
#define RTC_IO_TOUCH_PAD0_FUN_SEL_S 17
#define RTC_IO_TOUCH_PAD1_FUN_SEL_S 17
data: .long 0
value: .long 0
.global entry
entry:
# Select SDA/SCL pins to version 1, TOUCH2 and TOUCH3 (version 0 is TOUCH0 and TOUCH1)
WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SDA_SEL_S, 2, 0)
WRITE_RTC_REG(RTC_IO_SAR_I2C_IO_REG, RTC_IO_SAR_I2C_SCL_SEL_S, 2, 0)
WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_SEL_S, 2, 3)
WRITE_RTC_REG(RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_FUN_SEL_S, 2, 3)
# WRITE_RTC_REG(rtc_reg, low_bit, bit_width, value)
WRITE_RTC_REG(SENS_SAR_SLAVE_ADDR1_REG, SENS_I2C_SLAVE_ADDR0_S, 11, 0x76)
# Set SCL speed to 100khz
WRITE_RTC_REG(RTC_I2C_SCL_LOW_PERIOD_REG, RTC_I2C_SCL_LOW_PERIOD_S, 19, 40)
WRITE_RTC_REG(RTC_I2C_SCL_HIGH_PERIOD_REG, RTC_I2C_SCL_HIGH_PERIOD_S, 20, 40)
# SDA duty (delay) cycles from falling edge of SCL when SDA changes.
WRITE_RTC_REG(RTC_I2C_SDA_DUTY_REG, RTC_I2C_SDA_DUTY_S, 20, 16)
# Number of cycles after start/stop condition
WRITE_RTC_REG(RTC_I2C_SCL_START_PERIOD_REG, RTC_I2C_SCL_START_PERIOD_S, 20, 30)
WRITE_RTC_REG(RTC_I2C_SCL_STOP_PERIOD_REG, RTC_I2C_SCL_STOP_PERIOD_S, 20, 44)
# cycles before timeout
WRITE_RTC_REG(RTC_I2C_TIMEOUT_REG, RTC_I2C_TIMEOUT_S, 20, 200)
# Set mode to master
WRITE_RTC_REG(RTC_I2C_CTRL_REG, RTC_I2C_MS_MODE_S, 1, 1)
I2C_RD 0xa2, 7, 0, 0
while i did create this project and did a lot of the ground work, i didn't work too much on it recently nor did i practically use it too much.
also, i generally think that a project that's not just for personal use but somehow generally useful should rather live at some "neutral" place (like in a github organisation) than in someone's personal github account.
@wnienhaus did a lot of work recently, so this is now much more practically usable than it was before, so i guess this is now ready to be used, enhanced and maintained by a community of interested people.
so, let's collect ideas about where this should live in future (github orgs)...
handling of opcode arguments (like giving registers, immediate numeric values, labels / addresses).
guess the opcode should say what it wants and the value actually given should be validated to comply with the expectation.
stuff like that looks useful:
https://github.com/tomtor/ulp-i2c/blob/master/main/ulp/stack.S
otoh, we could also implement it within the assembler, without macros.
just so users / developers know what features they can use.
we need some example code, like:
examples/counter.py
)examples/counter.py
)Maybe it is best to open a new ticket for each example we are working on.
Hello there and thank you so much for your great library and work. I want to start playing around with the ULP copro and as a first step, I want to get the blinky example getting to run. I am working with micropython 1.18 on Heltec Wifi Kit32. On this board, the onboard LED is on GPIO25, so I have changed the definition to:
#define RTCIO_GPIO25_CHANNEL 6
If I start the program it will run without an error and I still can see the print output changing from 1 to 0 and vice versa, but the LED doesn´t light. Could you give me a hint, of what I am doing wrong?
Thanks in advance and best regards,
Werner
#59 see writeup there.
see LICENSE file. suggestions?
I would like an example of how to use WAKE because I'm not quite sure how it's supposed to work. I'll contribute one if I can, but right now it doesn't work. Should it?
#
# Example of putting the main processor to sleep then waking it up
# via the ULP after a while
#
from esp32 import ULP
from machine import mem32
import machine
from esp32_ulp import src_to_binary
source = """\
#define RTC_CNTL_LOW_POWER_ST_REG 0x3FF480c0
#define RTC_CNTL_RDY_FOR_WAKEUP BIT(19)
entry:
MOVE r0, 2000
loop:
WAIT 40000 // at 8 MHz this is 5 msec, if we do this 1000 times that's 5 seconds
SUB r0, r0, 1
jump is_rdy_for_wakeup, eq
jump loop
is_rdy_for_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP bit
#
# This test was described in https://github.com/espressif/esp-idf/issues/8341
# See reference manual 31.19.
#
# The main issue with this is that it will hang if the main CPU wasn't in deep sleep.
#
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
AND r0, r0, 1
JUMP is_rdy_for_wakeup, eq // Retry until the bit is set
WAKE // Trigger wake up
HALT // Stop the ULP program
"""
#
# Compile the ULP code
#
binary = src_to_binary(source)
load_addr, entry_addr = 0, 0
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits
ulp = ULP()
ulp.load_binary(load_addr, binary)
#
# The ULP starts out asleep (halted), so set a timer to wake it
# up the first time.
#
# ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles
mem32[ULP_MEM_BASE + load_addr] = 0x1000
ulp.run(entry_addr)
print("going to sleep")
# I think this should enable ULP wakeup
mem32[ 0x3ff48038 ] = 0x200 << 10
machine.deepsleep()
#
# We won't ever reach this, because WAKE is going to cause the main
# processor to reset and start over at the beginning.
#
print("I am awake again")
The https://github.com/micropython/micropython-esp32-ulp/blob/master/examples/counter.py I think shows how to get the count from the ulp into the .py program. However to setup a ULP counter that counts pulses from a gpio pin (eg the ulp assembler code at https://esp32.com/viewtopic.php?t=12454 it looks like the .py program has to send some values (like gpio pin number & debounce info) to the ULP? Could we maybe have another example for us novices on how to do that?
Github has deprecated the ubuntu-18.04
image for running Github Actions with (see actions/runner-images#6002).
We will need to ensure our build works with newer Ubuntu versions.
(Last time I tried, the binutils-esp32
build process did not work on ubuntu versions later than 18.04, so this might be more effort than just changing the image name 😞).
To make it easier to publish py-esp32-ulp to PyPI, we can automate it.
This would allow automatic publishing (by triggering a Github Action) every time a new Release is created in Github.
So far I have tested this approach in: https://github.com/wnienhaus/ujqtvms in the publish.yml workflow.
This could also make it easier for various maintainers to publish without needing to share PyPI credentials or documentation on how to manually do this.
espressif's IDF ulp code loader expects some specific format for a loadable binary.
magic, sizes of text/data/bss, ...
we do not generate that yet.
.long value (32bit)
.word value (16bit)
.byte value
note: there can be a comma separated list of values.
the code was developed / tested on the UNIX port of micropython on Linux/x64.
practically run the code on ESP32 hardware, assembling some ULP code.
run the tests on ESP32 hardware.
Can I ask for a clarification on this line of the gpio example? What's got me confused here is that it references bit 13 of this register:
Bit 13 is reserved. I don't know if the intent here really was to write an input enable, like this:
What am I missing?
In examples/counter.py
, I had to import from __main__
which is a bit ugly.
So, move stuff so that importing from dunder modules is not needed.
there are some such comments in espressif's ulp.h
and while they say "not implemented", at least some of them have code in that file.
does it mean the functionality is not implemented in the hardware (ULP coprocessor) yet?
or is it just some leftover that is outdated and should be removed?
I can submit a PR to run some code ULP code through py-esp32-ulp and through binutils-esp32ulp, and compare the generated code, if you think this is useful.
Moved the "assembler macros" discussion to #40.
Below, let's discuss about C-preprocessor-like macros.
I've got the ulp counter in a 14k .mpy file as a def. If the def is not called everything runs OK so the 14k file size is not a problem. There is in fact plenty of ram because the test I'm running is to download a new version of the 14k .mpy file, so there is at least 14k spare.
However if I call the counter def, urequests crashes with "MemoryError: memory allocation failed, allocating 1792 bytes". This is a problem I've struck before on other boards with different defs. For example on ESP32camera boards I have to deinit the camera after taking a pic in the camera def before using urequests or else a similar memory error from urequests.
I'm a bit worried this time though because I can't just deinit the ulp counter, it needs to keep running during deepsleep. Apart from the slow mem it uses for the count & state registers does the counter.py progrm set aside any ram that wouldn't be clawed back automatically when the counter def returns the count to the .mpy?
PS: unlike my trials with with the ESP camera boards, where any attempt to use urequest would crash, my problem with the ulp counter def on this DEVKIT1 board only occurs when I try to download a 14k file from the server, the other urequest tasks (like uploading the count) are working OK.
Usually I use pytest to run tests, but I guess that does not work on MicroPython?
So what is a sane, working test runner?
Currently I do testing using the UNIX port of MicroPython.
as a result of the assembler run, we have a symbol table (name -> offset).
what we do not have (yet?) is type information.
if we had that, we could build a uctypes.struct
to comfortably deal with the result (like x = ulp.name
).
any other ideas?
i suspect this means putting the name (and its value) into the list of "exported" symbols?
Originally posted by @kjm1102 in #61 (comment) :
I can't get it to run
Traceback (most recent call last): File "<stdin>", line 4, in <module> ImportError: no module named 'esp32_ulp'
considering the great work of @wnienhaus, this is much closer to be practically useful than previously.
so i create a milestone for first release - maybe only add very important features and bug fixes into there, so we can do a first release soon.
see TODO in tests/opcodes.py.
Guess we need that to practically work with the ULP Co-Processor from micropython.
.skip amount of bytes (for .bss: just reserve some space of size amount)
fill defaults to zero-bytes.
Usually I am all for case-sensitivity, because it avoids a lot of issues.
But considering the symbol names from espressif have a lot of SYMBOLS_LIKE_THAT, I find that rather ugly, so I am considering just ignoring case, so typing the same stuff in lowercase does also match.
Opinions?
Looks like that for a simple implementation we need a 2-pass assembler:
Symbol table would be like:
symbol/label ---> (global, segment, value)
global: flag set to True by .global
segment: TEXT, DATA, BSS or None if it is not some kind of address
value: relative offset from segment start
py-esp32-ulp is able to assemble about 3kB of source code (when it is running on a standard ESP32 "WROOM" chip) - more gives a MemoryError (out of memory). Bigger amounts of source code can be assembled when running on a device with more RAM (like on a development machine running the UNIX port of micropython).
PR #36 improved this a bit by calling the garbage collector now and then.
The limit looks rather low, but considering that the ULP has only a total of 4kiB of RAM and that we will likely only be allowed to use ~2kiB of it from micropython and that usually some of it is needed as a buffer (thus: not for code), the limit doesn't look too bad.
(moved from #35)
We can collect ideas here about how to improve memory usage in case we need to.
testing on travis-ci is broken - a good opportunity to move it to github actions.
this should be done before merging any other code change.
first just immediate values.
later maybe expressions also.
comment styles:
# until end of line, python style
// until end of line, C style
/* until reaching comment end, C style */
http://esp-idf.readthedocs.io/en/latest/api-guides/ulp_instruction_set.html
looks like the binutils (as) based instruction syntax is a bit different (even in instruction names, also in the way args determine the opcode choice) than the old, deprecated C macro stuff in ulp.h.
micropython-esp32-ulp/examples/blink.py
Line 86 in 8fa7070
Do I understand in this bit of code that you're turning the led on and off by enabling or disabling the GPIO output? I'm curious why you would have done that, rather than always initializing the pin as an output, then writing to RTCIO_RTC_GPIO_OUT_REG, RTCIO_RTC_GPIO_OUT_W1TS_REG, or RTCIO_RTC_GPIO_OUT_W1TC_REG?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.