Open mlewus opened 3 years ago
It isn't a short-term goal. We have many other APIs to implement before we do multi-core.
I'm considering an API to facilitate running native code on the second core instead of additional Python. What are you using it to do?
Not sure how relevant this is to CircuitPython, but I did some multicore testing with MicroPython on a Pico back in February. I found that global variables require about 1mSec to get updated between cores. The only element that updates faster is a lock. These must be interrupt driven as they update very fast, on the order of a few microseconds.
I'd really like this for a project I'm building to allow the 2nd core to handle a bunch of additional IO tasks while the first core is on input detection.
My limited understanding is that Circuitpython is, or started out as, a fork of Micropython. Micropython supports multitasking, locks, and inter-task globals on the Pico. Can a Circuitpython developer comment as to the difficulty of implementing this on Circuitpython?
My limited understanding is that Circuitpython is, or started out as, a fork of Micropython. Micropython supports multitasking, locks, and inter-task globals on the Pico. Can a Circuitpython developer comment as to the difficulty of implementing this on Circuitpython?
It's really hard to say because none of us have looked into it.
Right now we're based on MicroPython ~1.9.4 (https://github.com/micropython/micropython/commit/25ae98f07cb3c4488cb955403dfe56b8bb8db6f0) which is a year or two old. I think upstream has made some concurrency improvements since then so the first step I'd take is to merge in newer MicroPython. There is an outstanding PR to do that here: #4280
Once that is done, I'd try enabling the settings in CircuitPython to see if they work. You'd also need to look at the MP changes for the RP2040's second core. That'll likely expose many areas where there are concurrency issues that need to be fixed.
Personally, I'd prefer if CircuitPython offered "more difficult" to use features such as _thread
because I really just want to get this done and I know how to handle myself with concurrency and memory access.
I'd really want Python on the 2nd core because frankly all my IO tasks are laid out with CircuitPython libraries, so if I had to write C for it I might instead just move to MicroPython instead and re-implement the libraries I'm currently using in MicroPython.
The problem for me is that using circuitpython on the Pico means throwing away half of the machine. I don't really see C++ on the 2nd core as a solution. Most people who use circuitpython do so for the advantages that it offers over C++. I'm not sure how the decision making works as to which features get to the top of the list but it would be great to see this get prioritized. I've been using micropython, but that means rewriting a lot of the hardware drivers that already exist in Circuitpython. So that is not ideal.
Would this then be better suited to being tracked along/after 7.x has a release so that there is more control around concurrency?
@Red-M if its just for keymatrix management, it would probably be better to adapt _gamepad to be a matrix scanning helper...? then it isnt multi-core dependant and could run on other chips :)
@ladyada Thanks for the reply, but I've gotten the keymatrix poll/scan down to just interrupt pin polling which is way faster. I'm making a full sized keyboard with RGB and other features so I'm really wanting access to the other core with all the IO libraries.
if the keymatrix was handled in the background - what other IO would you need?
dotstar and displayio.
kk - displayio is also handled in the background with DMA and dotstars are near instantaneous - you can drive them at 8mhz. im not convinced you'd gain anything with dual core support and its not going to be adding to circuitpython soon
Frankly I think that's a little short-sighted. But it's your football. Closing this issue.
reopening because its a valid issue. for some purposes - a keyboard, for example - we really do think it does not need a dual core. we are trying to help get projects done and not have to wait for something that does not exist!
I have to agree with @mlewus. You can't just wait for another cycle to get more feedback to handle other tasks, near instant isn't nearly as fast as real time.
in python the garbage collector can run at anytime - theres no such thing as true guaranteed real time task handling in interpretted python which is why you'd have to do it C.
i'd like to find a solution to the problem, however the problem isnt clear yet - USB tasks out every 1ms, its not 'instant' either. some really specific numbers or complete example code would be useful. if it cannot be measured, we cannot improve it :)
The adafruit-blinka
Python library, which is mostly used for Linux, emulates many of the native modules provided by CircuitPython. But besides supporting Linux, it also has a MicroPython wrapper. So you can still use CircuitPython libraries, on top of MicroPython, if you do indeed want to take advantage of something only MicroPython provides.
Some keyboard projects done in CircuitPython: https://github.com/KMKfw/kmk_firmware https://github.com/tannewt/ckd63 (4 years old)
If you'd like to discuss this in more detail, this issue is probably not the place. Discord or the forums are good.
@dhalbert, I'll take a look at adafruit-blinka and continue this in a more appropriate forum. Thanks for your help!
@dhalbert Just letting you know and others in case they see this issue but that suggestion doesn't work due to adafruit/Adafruit_Python_PlatformDetect#144.
EDIT: @ladyada For giggles I tried using gamepad
(_gamepad
doesn't exist) and my usage of pins from the MCP IO expanders means that gamepad reliably hard crashes the pico and its so bad that I can't even get tracebacks, I'm guessing its something to do with supvisor not being able to access the pins over the I2C bus.
yeah you cannot use gamepad (yet) because it doesnt support an expander. blinka
is great but will not add hid support to micropython-on-pico. if you need help getting your project optimized you can come by discord
The problem for me is that using circuitpython on the Pico means throwing away half of the machine. I don't really see C++ on the 2nd core as a solution. Most people who use circuitpython do so for the advantages that it offers over C++. I'm not sure how the decision making works as to which features get to the top of the list but it would be great to see this get prioritized. I've been using micropython, but that means rewriting a lot of the hardware drivers that already exist in Circuitpython. So that is not ideal.
The RP2040 isn't our only platform and the second core isn't the only thing we can't do that the hardware supports. It's all a question of prioritization and Adafruit-funded folks are prioritizing other work. (We discuss this in places like this issue and the weekly meeting.) We'd love to have threading and multicore support but it's just not clear to us that the benefit is worth time cost. I always prioritize PRs so no one outside of Adafruit would be blocked on implementing this.
I'd encourage you to try CircuitPython for your specific project. There may be other ways to optimize the code so that you can do what you want from CircuitPython even with just one core. (I know @Red-M got their keyboard code going by optimizing I2C transactions when scanning.)
@tannewt I completely understand that Adafruit has limited resources and has to make the best use of them to support business & community goals. I think Adafruit is a great example of a well-run tech company. It's the reason that I buy products from you whenever I can.
I understand that there is limited time and resource for concurrency but more and more devices/MCUs are supporting/providing more than 1 core.
It would be great to have this in general and not just for the RP2040 also for various reasons/usecases. I'd honestly prefer some planning go into getting concurrency as a whole (if it isn't/hasn't been done yet) because more devices in the future will have a multicore offering.
Offering even just async tasks to be placed onto other cores that share the same IO would be excellent, as much as I'd prefer to do threading workloads.
EDIT: A Cython like superset language would be excellent for this exact kind of problem to allow python code to be on the 2nd core as well.
@Red-M, a week ago I would have agreed with you. But one of the Adafruit developers brought up a valid point. Which is, they support 100s of boards with circuitpython. Only a few of those are multi-core. Given the vastly greater scope of requested functionality with respect to available development labor, I understand why this has not been done.
@mlewus I'm not saying it isn't going to be a lot of work but some form of concurrency planning should be done soon as more MCUs are going to come out with more than 1 core.
Adding to the application conversation, I have a Feather RP2040 with a Pimoroni Envior+ wing, an AMG8833 wing, an adalogger wing, and a PoE wing all attached to a single trippler that also has an IR tranceiver and rotary encoder on it. I would like to be able to use the 2nd core to process the analog audio from the mic on the enviro+ and possibly stream it over the network to do voice command recognition while the first core handles the rest of the sensors and U/I.
@Red-M, yes of course you're right. I think we're going to see more low end multi-core microcontrollers. And there are some tasks that are too complex for a Pio but for which you really don't want to be interrupted.
@Red-M, yes of course you're right. I think we're going to see more low end multi-core microcontrollers. And there are some tasks that are too complex for a Pio but for which you really don't want to be interrupted.
Agreed. I'm happy to see multi-core support is being considered!
up-vote for native code on the second processor, I have a project that requires 4 rotary-encoders and presents a HID interface.
Possibly this is an example of an app that needs dual-cores: circuitpython on Proc0, native (driver) code on Proc1?
@nmorse did you try circuitpython as is? 4 rotary encoders and HID is not processor intensive.
I will try that, (on this project) up till now I had been doing all 'C-sdk', thanks
I also would vote for this - even some kind of port/wrapper for _threads from MicroPython would be nice.
Another vote from me
My FWIW if this is to be prioritized: We need to show demonstrable instances where a real project, preferably with publicly posted code, that can't be made to work with the current circuitpython, but could be done with multiple core support. Or, ones that could be made to perform meaningfully better with multiple core support.
So far there have been a lot of what if kind of requests here, including one of mine, but none that meet that standard. Adafruit has limited resources and many demands on them. If we want those resources applied to this feature, we need to describe a better use case than had been shown so far. Or, live without it until either one of us takes it on ourselves or more processors come out with multiple cores, which would provide more use cases for this code.
hiya folks, lets keep comments positive so they cant be misread - we're here to solve issues, sometimes we can solve issues without waiting for the circuitpython team to do a lot of work.
we recently added asyncio
, working with micropython team so that our codebases and libraries could be compatible. there is work happening with adding more advanced features: circuitpython has a lot of async stuff going on and as we gain more dual
core processors, there'll be more work on it.
we want to make sure that what we write is well-supported and usable. adding use cases will help us greatly to make sure we have coverage - we think we captured the most common async/irq needs so far and there will be more!
I think if you want to meet coverage of end-user usecases, you might want to look at how Python handles that in their unit tests.
EDIT: Most of the frustration from users is that micropython doesn't have a good or wide spread USB stack to work with (limited number of boards and supported modes of operation, micropython has gotten vastly better in both respects over the 2 years I've been interested in this space of software but they still have a ways to go to match circuitpython for USB support).
But circuitpython's lack of _thread
and IRQ support/callbacks makes a lot of logic and integration harder because most of that has to be supported by the HALs (or similar structures) instead which makes things much harder as someone coming into this space of micro controllers who want to get firmware made much more quickly.
Also noting from this comment as an update:
(I know @Red-M got their keyboard code going by optimizing I2C transactions when scanning.)
I've since moved away from those MCP IO expanders because they were too slow (even in the faster SPI mode that I added to the library to support the SPI variant of those chips), to using 2 controllers with 1 controller being optional, making my PCBs more complex due to the lack of software support for the 2nd core on the pico (which the pico doesn't have enough pins for my goals).
I say this as a statement of the work being done for python on micro-controllers being in a good state but I'd love to have more advanced methods to interact with hardware.
@ladyada Use Case: I would like to use one core for monitoring a Loconet (from Digitrax) communication channel and a DCC reception (using PIO for the low level work) and use the other core for managing 16 GPIO (servos/buttons/current block detection/IR sensors, maybe other uses later), updating a neopixel bus, i2c communication (RFID, eeprom, display, etc) and possibly a bit more. I want to keep the loconet and DCC on it's own core to help prevent data loss in the PIO I/O buffers. I'm just getting started now, so I haven't tried to load everything on one core yet.. It may handle it, but as the RP2040 has dual cores I would like to leverage one just for communications and one for "local work".
@rmilby13 That is a good idea. However I would recommend you look into using two RP2040s. Do you want to create a repo with discussions enabled where we could chat about it? If you want to use a single RP2040 this gets more complicated. There is a lot to talk about and they do not like that stuff here. I honestly am not sure they will get around to this, since its the only multicore MCU they support. Any meaningful API for building this would need their support, so that is too bad. They could make some money on this, but I doubt they want the baggage.
Hi, I came here looking for a solution to a problem, where RP2040 communicates with LTE/WiFi to send data. At this time pretty much any library is using time.sleep
. Meaning while I am sending data over UART the code is blocking. The device does not react to buttons, etc. My understanding is if a thread
would be used for offloading data another thread
could be used to keep the device "operational". I am happy to be educated on different approach to resolve this scenario.
Thank you
The use case I have is very superficial but I want to run animations on the macropad screen, whilst having the LEDs fade smoothly. Unfortunately if I am writing big changes to the screen the rate the LEDs update slows down and it looks a little off.
So I want to run my displayio things in the other thread so that the standard keyboard features can run independently, such as detecting keypresses and LED animations.
Possible use case: we have a sensor logger application that reads from a 6dof and writes to the onboard flash of the RP2040. We poll the sensor as many times as possible in a span of 1 to 10 seconds and have tried various methods of saving the data:
We would love to see a library that allowed utilization of the second core so we can offload the IO write to another core while the sensor data collection continues.
@agrabbs you are not CPU bound, you are flash-memory-speed bound - writing to any flash memory requires erasing blocks, which will take a long time AND the same internal flash memory is being read from for the RP2040 to run.
this is very hard to do. ideally you can buffer into an array - you should have ~ 100k available. or, FRAM is the easiest external-memory solution as it has instantaneous writes. ESP32-Sx with PSRAM might be reasonable because you get SRAM speed but 2MB of space
I'm building a small step sequencer + synthesizer using raspberry pico. This project involves keypad+encoder, screen and audio output. Currently, everything is running in just 1 core and sometimes the audio glitch because of heavy UI rendering. It's will be best if I can utilize the second core for just audio processing while the first core is dedicated to handling user input and UI rendering. By utilizing the second core, maybe more advanced audio processing can be archivable.
My usecase is mostly an up vote of @AdaRoseCannon example:
Display update in one thread while “everything else” is in another thread.
(I know of a micropython project where they went through macerations to get this working but it made world of difference in the end result.)
I have another usecase which may be niche …
charlieplexing required an infinite loop to keep the LEDs updated. It would be convenient to run this on a second core. It would require some form of shared memory or communications to choose which LEDs need to be active.
It may be possible to do charlieplexing with features now in CircuitPython, without multicore. It's possible to repeatedly send a buffer to a PIO peripheral forever with background_write
, which opens a ton of interesting possibilities.
There's an example on the Learn system to use this to drive a 4-digit 7-segment LED directly.
In an example I coded but didn't demonstrate in the guide, I combined the LED segment driving code with the advanced PWM code and controlled the brightness of segments individually: https://github.com/adafruit/Adafruit_CircuitPython_PIOASM/blob/main/examples/pioasm_7seg_fader.py
I think the same structure would adapt readily to charlieplexed LEDs.
The _pew
module does LED matrix updating without either a second core or pio, as it does it on samd21. The matrix happens to not be charlieplexed, but I have successfully used variants of the same code with a charlieplexed matrix as well.
My project is also audio synthesis. Timing is critical, and I'm trying to find a way to do it with the PIO, but the FIFO is extremely small for buffering audio, and with CircuitPython I don't have the option of triggering an interrupt to run the generation code as needed. I haven't written any code yet, so I'm not sure exactly how limited I'm going to be yet, but the ideal situation would be to be able to use the second core to generate the audio and the first to handle user input and the display.
Interestingly, while I was looking for way to just run Python code on the second core, native C would be even better for this particular project, because timing would be far easier to manage, and speed wouldn't be as much of an issue. (Also, I have some basic audio synth software written in C already...) Long term, I would love to see both Python and C/C++ as options for this. The ability to run native code directly from CircuitPython on the RP2040 would open up a ton of options, but not every multi-threaded project needs that.
But yeah, I would really like to write an audio synth using the RP2040, and it would be ideal if it was a "second core" module that just uses the second core so that the first is free for whatever else. And I'm not sure how feasible this is with just the one core.
I do have some things I can try. PyRTOS might be able to manage scheduling in a way that isn't too heavy handed. (I added service routines a while back that might be ideal for this...) I can use a simpler timing loop algorithm. I might be able to use asyncio, but it is designed for I/O bound tasks, and audio synthesis is CPU bound. I don't think any of these solutions would allow the synthesizer code to be released as a solid standalone synth module though, since they would be locked into a serial timing algorithm that any user would have to work within. (And I really would like to turn the synth portion specifically into an open source project, but I don't want to force the user to adhere to a restrictive scheduling regimen.)
Anyhow, I figured if you are looking for applications where this kind of feature would be critical, I've got one! I can always go with MicroPython instead, but if I'm going to write a full on synth module, I would really rather do it for CircuitPython.
The Pi Pico has 2 physical cores, but only one core is usable in CircuitPython. Micropython has limited multicore functionality when used with the pico, allowing the user to start a separate task while passing variables in the task call, like this:
mytask runs independently of the main mcu core, and runs until it returns or mcu reset.
Is this planned for inclusion in Circuitpython?