Merge branch 'micropython:master' into esp32_temp

Rick Sorensen 2024-03-20 17:08:02 -05:00 zatwierdzone przez GitHub
commit a897e95ddf
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
127 zmienionych plików z 4925 dodań i 430 usunięć

Wyświetl plik

@ -25,6 +25,7 @@ jobs:
ci_func: # names are functions in ci.sh
- stm32_pyb_build
- stm32_nucleo_build
- stm32_misc_build
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

8
.gitmodules vendored
Wyświetl plik

@ -9,7 +9,7 @@
url = https://github.com/lwip-tcpip/lwip.git
[submodule "lib/berkeley-db-1.xx"]
path = lib/berkeley-db-1.xx
url = https://github.com/pfalcon/berkeley-db-1.xx
url = https://github.com/micropython/berkeley-db-1.xx
[submodule "lib/stm32lib"]
path = lib/stm32lib
url = https://github.com/micropython/stm32lib
@ -59,3 +59,9 @@
[submodule "lib/protobuf-c"]
path = lib/protobuf-c
url = https://github.com/protobuf-c/protobuf-c.git
[submodule "lib/open-amp"]
path = lib/open-amp
url = https://github.com/OpenAMP/open-amp.git
[submodule "lib/libmetal"]
path = lib/libmetal
url = https://github.com/OpenAMP/libmetal.git

Wyświetl plik

@ -48,12 +48,14 @@ used during the build process and is not part of the compiled source code.
/cmsis (BSD-3-clause)
/crypto-algorithms (NONE)
/libhydrogen (ISC)
/libmetal (BSD-3-clause)
/littlefs (BSD-3-clause)
/lwip (BSD-3-clause)
/mynewt-nimble (Apache-2.0)
/nrfx (BSD-3-clause)
/nxp_driver (BSD-3-Clause)
/oofatfs (BSD-1-clause)
/open-amp (BSD-3-clause)
/pico-sdk (BSD-3-clause)
/re15 (BSD-3-clause)
/stm32lib (BSD-3-clause)

Wyświetl plik

@ -18,7 +18,9 @@ Classes
appends and pops from either side of the deque. New deques are created
using the following arguments:
- *iterable* must be the empty tuple, and the new deque is created empty.
- *iterable* is an iterable used to populate the deque when it is
created. It can be an empty tuple or list to create a deque that
is initially empty.
- *maxlen* must be specified and the deque will be bounded to this
maximum length. Once the deque is full, any new items added will
@ -26,18 +28,37 @@ Classes
- The optional *flags* can be 1 to check for overflow when adding items.
As well as supporting `bool` and `len`, deque objects have the following
methods:
Deque objects support `bool`, `len`, iteration and subscript load and store.
They also have the following methods:
.. method:: deque.append(x)
Add *x* to the right side of the deque.
Raises IndexError if overflow checking is enabled and there is no more room left.
Raises ``IndexError`` if overflow checking is enabled and there is
no more room in the queue.
.. method:: deque.appendleft(x)
Add *x* to the left side of the deque.
Raises ``IndexError`` if overflow checking is enabled and there is
no more room in the queue.
.. method:: deque.pop()
Remove and return an item from the right side of the deque.
Raises ``IndexError`` if no items are present.
.. method:: deque.popleft()
Remove and return an item from the left side of the deque.
Raises IndexError if no items are present.
Raises ``IndexError`` if no items are present.
.. method:: deque.extend(iterable)
Extend the deque by appending all the items from *iterable* to
the right of the deque.
Raises ``IndexError`` if overflow checking is enabled and there is
no more room in the deque.
.. function:: namedtuple(name, fields)

Wyświetl plik

@ -103,6 +103,7 @@ the following libraries.
micropython.rst
neopixel.rst
network.rst
openamp.rst
uctypes.rst
vfs.rst

Wyświetl plik

@ -0,0 +1,286 @@
.. currentmodule:: machine
.. _machine.USBDevice:
class USBDevice -- USB Device driver
====================================
.. note:: ``machine.USBDevice`` is currently only supported on the rp2 and samd
ports.
USBDevice provides a low-level Python API for implementing USB device functions using
Python code. This low-level API assumes familiarity with the USB standard. It's
not recommended to use this API directly, instead install the high-level usbd
module from micropython-lib.
.. warning:: This functionality is very new and the high-level usbd module is
not yet merged into micropython-lib. It can be found `here on
GitHub <https://github.com/micropython/micropython-lib/pull/558>`_.
Terminology
-----------
- A "Runtime" USB device interface or driver is one which is defined using this
Python API after MicroPython initially starts up.
- A "Built-in" USB device interface or driver is one that is compiled into the
MicroPython firmware, and is always available. Examples are USB-CDC (serial
port) which is usually enabled by default. Built-in USB-MSC (Mass Storage) is an
option on some ports.
Lifecycle
---------
Managing a runtime USB interface can be tricky, especially if you are communicating
with MicroPython over a built-in USB-CDC serial port that's part of the same USB
device.
- A MicroPython soft reset will always clear all runtime USB interfaces, which
results in the entire USB device disconnecting from the host. If MicroPython
is also providing a built-in USB-CDC serial port then this will re-appear
after the soft reset.
This means some functions (like ``mpremote run``) that target the USB-CDC
serial port will immediately fail if a runtime USB interface is active,
because the port goes away when ``mpremote`` triggers a soft reset. The
operation should succeed on the second try, as after the soft reset there is
no more runtime USB interface.
- To configure a runtime USB device on every boot, it's recommended to place the
configuration code in the ``boot.py`` file on the :ref:`device VFS
<filesystem>`. On each reset this file is executed before the USB subsystem is
initialised (and before ``main.py``), so it allows the board to come up with the runtime
USB device immediately.
- For development or debugging, it may be convenient to connect a hardware
serial REPL and disable the built-in USB-CDC serial port entirely. Not all ports
support this (currently only ``rp2``). The custom build should be configured
with ``#define MICROPY_HW_USB_CDC (0)`` and ``#define
MICROPY_HW_ENABLE_UART_REPL (1)``.
Constructors
------------
.. class:: USBDevice()
Construct a USBDevice object.
.. note:: This object is a singleton, each call to this constructor
returns the same object reference.
Methods
-------
.. method:: USBDevice.config(desc_dev, desc_cfg, desc_strs=None, open_itf_cb=None, reset_cb=None, control_xfer_cb=None, xfer_cb=None)
Configures the ``USBDevice`` singleton object with the USB runtime device
state and callback functions:
- ``desc_dev`` - A bytes-like object containing
the new USB device descriptor.
- ``desc_cfg`` - A bytes-like object containing the
new USB configuration descriptor.
- ``desc_strs`` - Optional object holding strings or bytes objects
containing USB string descriptor values. Can be a list, a dict, or any
object which supports subscript indexing with integer keys (USB string
descriptor index).
Strings are an optional USB feature, and this parameter can be unset
(default) if no strings are referenced in the device and configuration
descriptors, or if only built-in strings should be used.
Apart from index 0, all the string values should be plain ASCII. Index 0
is the special "languages" USB descriptor, represented as a bytes object
with a custom format defined in the USB standard. ``None`` can be
returned at index 0 in order to use a default "English" language
descriptor.
To fall back to providing a built-in string value for a given index, a
subscript lookup can return ``None``, raise ``KeyError``, or raise
``IndexError``.
- ``open_itf_cb`` - This callback is called once for each interface
or Interface Association Descriptor in response to a Set
Configuration request from the USB Host (the final stage before
the USB device is available to the host).
The callback takes a single argument, which is a memoryview of the
interface or IAD descriptor that the host is accepting (including
all associated descriptors). It is a view into the same
``desc_cfg`` object that was provided as a separate
argument to this function. The memoryview is only valid until the
callback function returns.
- ``reset_cb`` - This callback is called when the USB host performs
a bus reset. The callback takes no arguments. Any in-progress
transfers will never complete. The USB host will most likely
proceed to re-enumerate the USB device by calling the descriptor
callbacks and then ``open_itf_cb()``.
- ``control_xfer_cb`` - This callback is called one or more times
for each USB control transfer (device Endpoint 0). It takes two
arguments.
The first argument is the control transfer stage. It is one of:
- ``1`` for SETUP stage.
- ``2`` for DATA stage.
- ``3`` for ACK stage.
Second argument is a memoryview to read the USB control request
data for this stage. The memoryview is only valid until the
callback function returns.
The callback should return one of the following values:
- ``False`` to stall the endpoint and reject the transfer.
- ``True`` to continue the transfer to the next stage.
- A buffer object to provide data for this stage of the transfer.
This should be a writable buffer for an ``OUT`` direction transfer, or a
readable buffer with data for an ``IN`` direction transfer.
- ``xfer_cb`` - This callback is called whenever a non-control
transfer submitted by calling :func:`USBDevice.submit_xfer` completes.
The callback has three arguments:
1. The Endpoint number for the completed transfer.
2. Result value: ``True`` if the transfer succeeded, ``False``
otherwise.
3. Number of bytes successfully transferred. In the case of a
"short" transfer, The result is ``True`` and ``xferred_bytes``
will be smaller than the length of the buffer submitted for the
transfer.
.. note:: If a bus reset occurs (see :func:`USBDevice.reset`),
``xfer_cb`` is not called for any transfers that have not
already completed.
.. method:: USBDevice.active(self, [value] /)
Returns the current active state of this runtime USB device as a
boolean. The runtime USB device is "active" when it is available to
interact with the host, it doesn't mean that a USB Host is actually
present.
If the optional ``value`` argument is set to a truthy value, then
the USB device will be activated.
If the optional ``value`` argument is set to a falsey value, then
the USB device is deactivated. While the USB device is deactivated,
it will not be detected by the USB Host.
To simulate a disconnect and a reconnect of the USB device, call
``active(False)`` followed by ``active(True)``. This may be
necessary if the runtime device configuration has changed, so that
the host sees the new device.
.. attribute:: USDBD.builtin_driver
This attribute holds the current built-in driver configuration, and must be
set to one of the ``USBDevice.BUILTIN_`` named constants defined on this object.
By default it holds the value :data:`USBDevice.BUILTIN_NONE`.
Runtime USB device must be inactive when setting this field. Call the
:func:`USBDevice.active` function to deactivate before setting if necessary
(and again to activate after setting).
If this value is set to any value other than :data:`USBDevice.BUILTIN_NONE` then
the following restrictions apply to the :func:`USBDevice.config` arguments:
- ``desc_cfg`` should begin with the built-in USB interface descriptor data
accessible via :data:`USBDevice.builtin_driver` attribute ``desc_cfg``.
Descriptors appended after the built-in configuration descriptors should use
interface, string and endpoint numbers starting from the max built-in values
defined in :data:`USBDevice.builtin_driver` attributes ``itf_max``, ``str_max`` and
``ep_max``.
- The ``bNumInterfaces`` field in the built-in configuration
descriptor will also need to be updated if any new interfaces
are appended to the end of ``desc_cfg``.
- ``desc_strs`` should either be ``None`` or a list/dictionary where index
values less than ``USBDevice.builtin_driver.str_max`` are missing or have
value ``None``. This reserves those string indexes for the built-in
drivers. Placing a different string at any of these indexes overrides that
string in the built-in driver.
.. method:: USBDevice.submit_xfer(self, ep, buffer /)
Submit a USB transfer on endpoint number ``ep``. ``buffer`` must be
an object implementing the buffer interface, with read access for
``IN`` endpoints and write access for ``OUT`` endpoints.
.. note:: ``ep`` cannot be the control Endpoint number 0. Control
transfers are built up through successive executions of
``control_xfer_cb``, see above.
Returns ``True`` if successful, ``False`` if the transfer could not
be queued (as USB device is not configured by host, or because
another transfer is queued on this endpoint.)
When the USB host completes the transfer, the ``xfer_cb`` callback
is called (see above).
Raises ``OSError`` with reason ``MP_EINVAL`` If the USB device is not
active.
.. method:: USBDevice.stall(self, ep, [stall] /)
Calling this function gets or sets the STALL state of a device endpoint.
``ep`` is the number of the endpoint.
If the optional ``stall`` parameter is set, this is a boolean flag
for the STALL state.
The return value is the current stall state of the endpoint (before
any change made by this function).
An endpoint that is set to STALL may remain stalled until this
function is called again, or STALL may be cleared automatically by
the USB host.
Raises ``OSError`` with reason ``MP_EINVAL`` If the USB device is not
active.
Constants
---------
.. data:: USBDevice.BUILTIN_NONE
.. data:: USBDevice.BUILTIN_DEFAULT
.. data:: USBDevice.BUILTIN_CDC
.. data:: USBDevice.BUILTIN_MSC
.. data:: USBDevice.BUILTIN_CDC_MSC
These constant objects hold the built-in descriptor data which is
compiled into the MicroPython firmware. ``USBDevice.BUILTIN_NONE`` and
``USBDevice.BUILTIN_DEFAULT`` are always present. Additional objects may be present
depending on the firmware build configuration and the actual built-in drivers.
.. note:: Currently at most one of ``USBDevice.BUILTIN_CDC``,
``USBDevice.BUILTIN_MSC`` and ``USBDevice.BUILTIN_CDC_MSC`` is defined
and will be the same object as ``USBDevice.BUILTIN_DEFAULT``.
These constants are defined to allow run-time detection of
the built-in driver (if any). Support for selecting one of
multiple built-in driver configurations may be added in the
future.
These values are assigned to :data:`USBDevice.builtin_driver` to get/set the
built-in configuration.
Each object contains the following read-only fields:
- ``itf_max`` - One more than the highest bInterfaceNumber value used
in the built-in configuration descriptor.
- ``ep_max`` - One more than the highest bEndpointAddress value used
in the built-in configuration descriptor. Does not include any
``IN`` flag bit (0x80).
- ``str_max`` - One more than the highest string descriptor index
value used by any built-in descriptor.
- ``desc_dev`` - ``bytes`` object containing the built-in USB device
descriptor.
- ``desc_cfg`` - ``bytes`` object containing the complete built-in USB
configuration descriptor.

Wyświetl plik

@ -265,3 +265,4 @@ Classes
machine.WDT.rst
machine.SD.rst
machine.SDCard.rst
machine.USBDevice.rst

Wyświetl plik

@ -0,0 +1,115 @@
:mod:`openamp` -- provides standard Asymmetric Multiprocessing (AMP) support
============================================================================
.. module:: openamp
:synopsis: provides standard Asymmetric Multiprocessing (AMP) support
The ``openamp`` module provides a standard inter-processor communications infrastructure
for MicroPython. The module handles all of the details of OpenAMP, such as setting up
the shared resource table, initializing vrings, etc. It provides an API for using the
RPMsg bus infrastructure with the `Endpoint` class, and provides processor Life Cycle
Management (LCM) support, such as loading firmware and starting and stopping a remote
core, via the `RemoteProc` class.
Example usage::
import openamp
def ept_recv_callback(src, data):
print("Received message on endpoint", data)
# Create a new RPMsg endpoint to communicate with the remote core.
ept = openamp.Endpoint("vuart-channel", callback=ept_recv_callback)
# Create a RemoteProc object, load its firmware and start it.
rproc = openamp.RemoteProc("virtual_uart.elf") # Or entry point address (ex 0x081E0000)
rproc.start()
while True:
if ept.is_ready():
ept.send("data")
Functions
---------
.. function:: new_service_callback(ns_callback)
Set the new service callback.
The *ns_callback* argument is a function that will be called when the remote processor
announces new services. At that point the host processor can choose to create the
announced endpoint, if this particular service is supported, or ignore it if it's
not. If this function is not set, the host processor should first register the
endpoint locally, and it will be automatically bound when the remote announces
the service.
Endpoint class
--------------
.. class:: Endpoint(name, callback, src=ENDPOINT_ADDR_ANY, dest=ENDPOINT_ADDR_ANY)
Construct a new RPMsg Endpoint. An endpoint is a bidirectional communication
channel between two cores.
Arguments are:
- *name* is the name of the endpoint.
- *callback* is a function that is called when the endpoint receives data with the
source address of the remote point, and the data as bytes passed by reference.
- *src* is the endpoint source address. If none is provided one will be assigned
to the endpoint by the library.
- *dest* is the endpoint destination address. If the endpoint is created from the
new_service_callback, this must be provided and it must match the remote endpoint's
source address. If the endpoint is registered locally, before the announcement, the
destination address will be assigned by the library when the endpoint is bound.
.. method:: Endpoint.deinit()
Destroy the endpoint and release all of its resources.
.. method:: Endpoint.is_ready()
Returns True if the endpoint is ready to send (i.e., has both a source and destination addresses)
.. method:: Endpoint.send(src=-1, dest=-1, timeout=-1)
Send a message to the remote processor over this endpoint.
Arguments are:
- *src* is the source endpoint address of the message. If none is provided, the
source address the endpoint is bound to is used.
- *dest* is the destination endpoint address of the message. If none is provided,
the destination address the endpoint is bound to is used.
- *timeout* specifies the time in milliseconds to wait for a free buffer. By default
the function is blocking.
RemoteProc class
----------------
.. class:: RemoteProc(entry)
The RemoteProc object provides processor Life Cycle Management (LCM) support, such as
loading firmware, starting and stopping a remote core.
The *entry* argument can be a path to firmware image, in which case the firmware is
loaded from file to its target memory, or an entry point address, in which case the
firmware must be loaded already at the given address.
.. method:: RemoteProc.start()
Starts the remote processor.
.. method:: RemoteProc.stop()
Stops the remote processor. The exact behavior is platform-dependent. On the STM32H7 for
example it's not possible to stop and then restart the Cortex-M4 core, so a complete
system reset is performed on a call to this function.
.. method:: RemoteProc.shutdown()
Shutdown stops the remote processor and releases all of its resources. The exact behavior
is platform-dependent, however typically it disables power and clocks to the remote core.
This function is also used as the finaliser (i.e., called when ``RemoteProc`` object is
collected). Note that on the STM32H7, it's not possible to stop and then restart the
Cortex-M4 core, so a complete system reset is performed on a call to this function.

Wyświetl plik

@ -0,0 +1,293 @@
.. currentmodule:: rp2
.. _rp2.DMA:
class DMA -- access to the RP2040's DMA controller
==================================================
The :class:`DMA` class offers access to the RP2040's Direct Memory Access (DMA)
controller, providing the ability move data between memory blocks and/or IO registers. The DMA
controller has its own, separate read and write bus master connections onto the bus fabric and
each DMA channel can independently read data from one address and write it back to another
address, optionally incrementing one or both pointers, allowing it to perform transfers on behalf
of the processor while the processor carries out other tasks or enters a low power state. The
RP2040's DMA controller has 12 independent DMA channels that can run concurrently. For full
details of the RP2040's DMA system see section 2.5 of the `RP2040 Datasheet
<https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf>`_.
Examples
--------
The simplest use of the DMA controller is to move data from one block of memory to another.
This can be accomplished with the following code::
a = bytearray(32*1024)
b = bytearray(32*1024)
d = rp2.DMA()
c = d.pack_ctrl() # Just use the default control value.
# The count is in 'transfers', which defaults to four-byte words, so divide length by 4
d.config(read=a, write=b, count=len(a)//4, ctrl=c, trigger=True)
# Wait for completion
while d.active():
pass
Note that while this example sits in an idle loop while it waits for the transfer to complete,
the program could just as well do some useful work in this time instead.
Another, perhaps more common use of the DMA controller is to transfer between memory and an IO
peripheral. In this situation the address of the IO register does not change for each transfer but
the memory address needs to be incremented. It is also necessary to control the pace of the
transfer so as to not write data before it can be accepted by a peripheral or read it before the
data is ready, and this can be controlled with the ``treq_sel`` field of the DMA channel's control
register. The various fields of the control register for each DMA channel can be packed
using the :meth:`DMA.pack_ctrl()` method and unpacked using the :meth:`DMA.unpack_ctrl()`
static method. Code to transfer data from a byte array to the TX FIFO of a PIO state machine,
one byte at a time, looks like this::
# pio_num is index of the PIO block being used, sm_num is the state machine in that block.
# my_state_machine is an rp2.PIO() instance.
DATA_REQUEST_INDEX = (pio_num << 3) + sm_num
src_data = bytearray(1024)
d = rp2.DMA()
# Transfer bytes, rather than words, don't increment the write address and pace the transfer.
c = d.pack_ctrl(size=0, inc_write=False, treq_sel=DATA_REQUEST_INDEX)
d.config(
read=src_data,
write=my_state_machine,
count=len(src_data),
ctrl=c,
trigger=True
)
Note that in this example the value given for the write address is just the PIO state machine to
which we are sending the data. This works because PIO state machines present the buffer protocol,
allowing direct access to their data FIFO registers.
Constructor
-----------
.. class:: DMA()
Claim one of the DMA controller channels for exclusive use.
Methods
-------
.. method:: DMA.config(read=None, write=None, count=None, ctrl=None, trigger=False)
Configure the DMA registers for the channel and optionally start the transfer.
Parameters are:
- *read*: The address from which the DMA controller will start reading data or
an object that will provide data to be read. It can be an integer or any
object that supports the buffer protocol.
- *write*: The address to which the DMA controller will start writing or an
object into which data will be written. It can be an integer or any object
that supports the buffer protocol.
- *count*: The number of bus transfers that will execute before this channel
stops. Note that this is the number of transfers, not the number of bytes.
If the transfers are 2 or 4 bytes wide then the total amount of data moved
(and thus the size of required buffer) needs to be multiplied accordingly.
- *ctrl*: The value for the DMA control register. This is an integer value
that is typically packed using the :meth:`DMA.pack_ctrl()`.
- *trigger*: Optionally commence the transfer immediately.
.. method:: DMA.irq(handler=None, hard=False)
Returns the IRQ object for this DMA channel and optionally configures it.
.. method:: DMA.close()
Release the claim on the underlying DMA channel and free the interrupt
handler. The :class:`DMA` object can not be used after this operation.
.. method:: DMA.pack_ctrl(default=None, **kwargs)
Pack the values provided in the keyword arguments into the named fields of a new control
register value. Any field that is not provided will be set to a default value. The
default will either be taken from the provided ``default`` value, or if that is not
given, a default suitable for the current channel; setting this to the current value
of the `DMA.ctrl` attribute provides an easy way to override a subset of the fields.
The keys for the keyword arguments can be any key returned by the :meth:`DMA.unpack_ctrl()`
method. The writable values are:
- *enable*: ``bool`` Set to enable the channel (default: ``True``).
- *high_pri*: ``bool`` Make this channel's bus traffic high priority (default: ``False``).
- *size*: ``int`` Transfer size: 0=byte, 1=half word, 2=word (default: 2).
- *inc_read*: ``bool`` Increment the read address after each transfer (default: ``True``).
- *inc_write*: ``bool`` Increment the write address after each transfer (default: ``True``).
- *ring_size*: ``int`` If non-zero, only the bottom ``ring_size`` bits of one
address register will change when an address is incremented, causing the
address to wrap at the next ``1 << ring_size`` byte boundary. Which
address is wrapped is controlled by the ``ring_sel`` flag. A zero value
disables address wrapping.
- *ring_sel*: ``bool`` Set to ``False`` to have the ``ring_size`` apply to the read address
or ``True`` to apply to the write address.
- *chain_to*: ``int`` The channel number for a channel to trigger after this transfer
completes. Setting this value to this DMA object's own channel number
disables chaining (this is the default).
- *treq_sel*: ``int`` Select a Transfer Request signal. See section 2.5.3 in the RP2040
datasheet for details.
- *irq_quiet*: ``bool`` Do not generate interrupt at the end of each transfer. Interrupts
will instead be generated when a zero value is written to the trigger
register, which will halt a sequence of chained transfers (default:
``True``).
- *bswap*: ``bool`` If set to true, bytes in words or half-words will be reversed before
writing (default: ``True``).
- *sniff_en*: ``bool`` Set to ``True`` to allow data to be accessed by the chips sniff
hardware (default: ``False``).
- *write_err*: ``bool`` Setting this to ``True`` will clear a previously reported write
error.
- *read_err*: ``bool`` Setting this to ``True`` will clear a previously reported read
error.
See the description of the ``CH0_CTRL_TRIG`` register in section 2.5.7 of the RP2040
datasheet for details of all of these fields.
.. method:: DMA.unpack_ctrl(value)
Unpack a value for a DMA channel control register into a dictionary with key/value pairs
for each of the fields in the control register. *value* is the ``ctrl`` register value
to unpack.
This method will return values for all the keys that can be passed to ``DMA.pack_ctrl``.
In addition, it will also return the read-only flags in the control register: ``busy``,
which goes high when a transfer starts and low when it ends, and ``ahb_err``, which is
the logical OR of the ``read_err`` and ``write_err`` flags. These values will be ignored
when packing, so that the dictionary created by unpacking a control register can be used
directly as the keyword arguments for packing.
.. method:: DMA.active([value])
Gets or sets whether the DMA channel is currently running.
>>> sm.active()
0
>>> sm.active(1)
>>> while sm.active():
... pass
Attributes
----------
.. attribute:: DMA.read
This attribute reflects the address from which the next bus transfer
will read. It may be written with either an integer or an object
that supports the buffer protocol and doing so has immediate effect.
.. attribute:: DMA.write
This attribute reflects the address to which the next bus transfer
will write. It may be written with either an integer or an object
that supports the buffer protocol and doing so has immediate effect.
.. attribute:: DMA.count
Reading this attribute will return the number of remaining bus
transfers in the *current* transfer sequence. Writing this attribute
sets the total number of transfers to be the *next* transfer sequence.
.. attribute:: DMA.ctrl
This attribute reflects DMA channel control register. It is typically written
with an integer packed using the :meth:`DMA.pack_ctrl()` method. The returned
register value can be unpacked using the :meth:`DMA.unpack_ctrl()` method.
.. attribute:: DMA.channel
The channel number of the DMA channel. This can be passed in the ``chain_to``
argument of `DMA.pack_ctrl()` on another channel to allow DMA chaining.
.. attribute:: DMA.registers
This attribute is an array-like object that allows direct access to
the DMA channel's registers. The index is by word, rather than by byte,
so the register indices are the register address offsets divided by 4.
See the RP2040 data sheet for register details.
Chaining and trigger register access
------------------------------------
The DMA controller in the RP2040 offers a couple advanced features to allow one DMA channel
to initiate a transfer on another channel. One is the use of the ``chain_to`` value in the
control register and the other is writing to one of the DMA channel's registers that has a
trigger effect. When coupled with the ability to have one DMA channel write directly to the
`DMA.registers` of another channel, this allows for complex transactions to be performed
without any CPU intervention.
Below is an example of using both chaining and register
triggering to implement gathering of multiple blocks of data into a single destination. Full
details of these features can be found in section 2.5 of the RP2040 data sheet and the code
below is a Pythonic version of the example in sub-section 2.5.6.2.
.. code-block:: python
from rp2 import DMA
from uctypes import addressof
from array import array
def gather_strings(string_list, buf):
# We use two DMA channels. The first sends lengths and source addresses from the gather
# list to the registers of the second. The second copies the data itself.
gather_dma = DMA()
buffer_dma = DMA()
# Pack up length/address pairs to be sent to the registers.
gather_list = array("I")
for s in string_list:
gather_list.append(len(s))
gather_list.append(addressof(s))
gather_list.append(0)
gather_list.append(0)
# When writing to the registers of the second DMA channel, we need to wrap the
# write address on an 8-byte (1<<3 bytes) boundary. We write to the ``TRANS_COUNT``
# and ``READ_ADD_TRIG`` registers in the last register alias (registers 14 and 15).
gather_ctrl = gather_dma.pack_ctrl(ring_size=3, ring_sel=True)
gather_dma.config(
read=gather_list, write=buffer_dma.registers[14:16],
count=2, ctrl=gather_ctrl
)
# When copying the data, the transfer size is single bytes, and when completed we need
# to chain back to the start another gather DMA transaction.
buffer_ctrl = buffer_dma.pack_ctrl(size=0, chain_to=gather_dma.channel)
# The read and count values will be set by the other DMA channel.
buffer_dma.config(write=buf, ctrl=buffer_ctrl)
# Set the transfer in motion.
gather_dma.active(1)
# Wait until all the register values have been sent
end_address = addressof(gather_list) + 4 * len(gather_list)
while gather_dma.read != end_address:
pass
input = ["This is ", "a ", "test", " of the scatter", " gather", " process"]
output = bytearray(64)
print(output)
gather_strings(input, output)
print(output)
This example idles while waiting for the transfer to complete; alternatively it could
set an interrupt handler and return immediately.

Wyświetl plik

@ -140,3 +140,10 @@ Methods
Optionally configure it.
Buffer protocol
---------------
The StateMachine class supports the `buffer protocol`, allowing direct access to the transmit
and receive FIFOs for each state machine. This is primarily in order to allow StateMachine
objects to be passed directly as the read or write parameters when configuring a `rp2.DMA()`
channel.

Wyświetl plik

@ -241,6 +241,7 @@ Classes
.. toctree::
:maxdepth: 1
rp2.DMA.rst
rp2.Flash.rst
rp2.PIO.rst
rp2.StateMachine.rst

Wyświetl plik

@ -45,6 +45,8 @@ The following data types are supported:
+--------+--------------------+-------------------+---------------+
| Q | unsigned long long | integer (`1<fn>`) | 8 |
+--------+--------------------+-------------------+---------------+
| e | n/a (half-float) | float (`2<fn>`) | 2 |
+--------+--------------------+-------------------+---------------+
| f | float | float (`2<fn>`) | 4 |
+--------+--------------------+-------------------+---------------+
| d | double | float (`2<fn>`) | 8 |

Wyświetl plik

@ -11,9 +11,10 @@ SRC = btree_c.c btree_py.py
ARCH = x64
BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx
BTREE_DEFS = -D__DBINTERFACE_PRIVATE=1 -Dmpool_error="(void)" -Dabort=abort_ "-Dvirt_fd_t=void*" $(BTREE_DEFS_EXTRA)
CFLAGS += -I$(BTREE_DIR)/PORT/include
CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter $(BTREE_DEFS)
BERKELEY_DB_CONFIG_FILE ?= \"extmod/berkeley-db/berkeley_db_config_port.h\"
CFLAGS += -I$(BTREE_DIR)/include
CFLAGS += -DBERKELEY_DB_CONFIG_FILE=$(BERKELEY_DB_CONFIG_FILE)
CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter
SRC += $(addprefix $(realpath $(BTREE_DIR))/,\
btree/bt_close.c \

Wyświetl plik

@ -39,6 +39,10 @@ void abort_(void) {
nlr_raise(mp_obj_new_exception(mp_load_global(MP_QSTR_RuntimeError)));
}
int puts(const char *s) {
return mp_printf(&mp_plat_print, "%s\n", s);
}
int native_errno;
#if defined(__linux__)
int *__errno_location (void)

Wyświetl plik

@ -0,0 +1,16 @@
// Berkeley-db configuration.
#define __DBINTERFACE_PRIVATE 1
#define mpool_error printf
#define abort abort_
#define virt_fd_t void*
#ifdef MICROPY_BERKELEY_DB_DEFPSIZE
#define DEFPSIZE MICROPY_BERKELEY_DB_DEFPSIZE
#endif
#ifdef MICROPY_BERKELEY_DB_MINCACHE
#define MINCACHE MICROPY_BERKELEY_DB_MINCACHE
#endif
__attribute__((noreturn)) void abort_(void);

Wyświetl plik

@ -18,6 +18,7 @@ set(MICROPY_SOURCE_EXTMOD
${MICROPY_EXTMOD_DIR}/machine_signal.c
${MICROPY_EXTMOD_DIR}/machine_spi.c
${MICROPY_EXTMOD_DIR}/machine_uart.c
${MICROPY_EXTMOD_DIR}/machine_usb_device.c
${MICROPY_EXTMOD_DIR}/machine_wdt.c
${MICROPY_EXTMOD_DIR}/modbluetooth.c
${MICROPY_EXTMOD_DIR}/modframebuf.c
@ -131,27 +132,27 @@ if(MICROPY_PY_BTREE)
)
target_include_directories(micropy_extmod_btree PRIVATE
${MICROPY_LIB_BERKELEY_DIR}/PORT/include
${MICROPY_LIB_BERKELEY_DIR}/include
)
if(NOT BERKELEY_DB_CONFIG_FILE)
set(BERKELEY_DB_CONFIG_FILE "${MICROPY_DIR}/extmod/berkeley-db/berkeley_db_config_port.h")
endif()
target_compile_definitions(micropy_extmod_btree PRIVATE
__DBINTERFACE_PRIVATE=1
mpool_error=printf
abort=abort_
"virt_fd_t=void*"
BERKELEY_DB_CONFIG_FILE="${BERKELEY_DB_CONFIG_FILE}"
)
# The include directories and compile definitions below are needed to build
# modbtree.c and should be added to the main MicroPython target.
list(APPEND MICROPY_INC_CORE
"${MICROPY_LIB_BERKELEY_DIR}/PORT/include"
"${MICROPY_LIB_BERKELEY_DIR}/include"
)
list(APPEND MICROPY_DEF_CORE
MICROPY_PY_BTREE=1
__DBINTERFACE_PRIVATE=1
"virt_fd_t=void*"
BERKELEY_DB_CONFIG_FILE="${BERKELEY_DB_CONFIG_FILE}"
)
list(APPEND MICROPY_SOURCE_EXTMOD

Wyświetl plik

@ -15,6 +15,7 @@ SRC_EXTMOD_C += \
extmod/machine_spi.c \
extmod/machine_timer.c \
extmod/machine_uart.c \
extmod/machine_usb_device.c \
extmod/machine_wdt.c \
extmod/modasyncio.c \
extmod/modbinascii.c \
@ -30,6 +31,9 @@ SRC_EXTMOD_C += \
extmod/modmachine.c \
extmod/modnetwork.c \
extmod/modonewire.c \
extmod/modopenamp.c \
extmod/modopenamp_remoteproc.c \
extmod/modopenamp_remoteproc_store.c \
extmod/modos.c \
extmod/modplatform.c\
extmod/modrandom.c \
@ -377,8 +381,10 @@ endif
ifeq ($(MICROPY_PY_BTREE),1)
BTREE_DIR = lib/berkeley-db-1.xx
BTREE_DEFS = -D__DBINTERFACE_PRIVATE=1 -Dmpool_error=printf -Dabort=abort_ "-Dvirt_fd_t=void*" $(BTREE_DEFS_EXTRA)
INC += -I$(TOP)/$(BTREE_DIR)/PORT/include
BERKELEY_DB_CONFIG_FILE ?= \"extmod/berkeley-db/berkeley_db_config_port.h\"
CFLAGS_EXTMOD += -DBERKELEY_DB_CONFIG_FILE=$(BERKELEY_DB_CONFIG_FILE)
CFLAGS_EXTMOD += $(BTREE_DEFS_EXTRA)
INC += -I$(TOP)/$(BTREE_DIR)/include
SRC_THIRDPARTY_C += $(addprefix $(BTREE_DIR)/,\
btree/bt_close.c \
btree/bt_conv.c \
@ -397,9 +403,7 @@ SRC_THIRDPARTY_C += $(addprefix $(BTREE_DIR)/,\
)
CFLAGS_EXTMOD += -DMICROPY_PY_BTREE=1
# we need to suppress certain warnings to get berkeley-db to compile cleanly
# and we have separate BTREE_DEFS so the definitions don't interfere with other source code
$(BUILD)/$(BTREE_DIR)/%.o: CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter -Wno-deprecated-non-prototype -Wno-unknown-warning-option $(BTREE_DEFS)
$(BUILD)/extmod/modbtree.o: CFLAGS += $(BTREE_DEFS)
$(BUILD)/$(BTREE_DIR)/%.o: CFLAGS += -Wno-old-style-definition -Wno-sign-compare -Wno-unused-parameter -Wno-deprecated-non-prototype -Wno-unknown-warning-option
endif
################################################################################
@ -514,3 +518,58 @@ include $(TOP)/extmod/btstack/btstack.mk
endif
endif
################################################################################
# openamp
ifeq ($(MICROPY_PY_OPENAMP),1)
OPENAMP_DIR = lib/open-amp
LIBMETAL_DIR = lib/libmetal
GIT_SUBMODULES += $(LIBMETAL_DIR) $(OPENAMP_DIR)
include $(TOP)/extmod/libmetal/libmetal.mk
INC += -I$(TOP)/$(OPENAMP_DIR)
CFLAGS += -DMICROPY_PY_OPENAMP=1
ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1)
CFLAGS += -DMICROPY_PY_OPENAMP_REMOTEPROC=1
endif
CFLAGS_THIRDPARTY += \
-I$(BUILD)/openamp \
-I$(TOP)/$(OPENAMP_DIR) \
-I$(TOP)/$(OPENAMP_DIR)/lib/include/ \
-DMETAL_INTERNAL \
-DVIRTIO_DRIVER_ONLY \
-DNO_ATOMIC_64_SUPPORT \
-DRPMSG_BUFFER_SIZE=512 \
# OpenAMP's source files.
SRC_OPENAMP_C += $(addprefix $(OPENAMP_DIR)/lib/,\
rpmsg/rpmsg.c \
rpmsg/rpmsg_virtio.c \
virtio/virtio.c \
virtio/virtqueue.c \
virtio_mmio/virtio_mmio_drv.c \
)
# OpenAMP's remoteproc source files.
ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1)
SRC_OPENAMP_C += $(addprefix $(OPENAMP_DIR)/lib/remoteproc/,\
elf_loader.c \
remoteproc.c \
remoteproc_virtio.c \
rsc_table_parser.c \
)
endif # MICROPY_PY_OPENAMP_REMOTEPROC
# Disable compiler warnings in OpenAMP (variables used only for assert).
$(BUILD)/$(OPENAMP_DIR)/lib/rpmsg/rpmsg_virtio.o: CFLAGS += -Wno-unused-but-set-variable
$(BUILD)/$(OPENAMP_DIR)/lib/virtio_mmio/virtio_mmio_drv.o: CFLAGS += -Wno-unused-but-set-variable
# We need to have generated libmetal before compiling OpenAMP.
$(addprefix $(BUILD)/, $(SRC_OPENAMP_C:.c=.o)): $(BUILD)/openamp/metal/config.h
SRC_THIRDPARTY_C += $(SRC_LIBMETAL_C) $(SRC_OPENAMP_C)
endif # MICROPY_PY_OPENAMP

Wyświetl plik

@ -0,0 +1,51 @@
# Makefile directives for libmetal
# libmetal is intended to run through a pre-processor (as part of its CMake-based build system).
# This replicates the basic functionality of the pre-processor, including adding a "micropython"
# platform that is almost identical to the built-in "generic" platform with a few small changes
# provided by the files in extmod/libmetal.
$(BUILD)/openamp: $(BUILD)
$(MKDIR) -p $@
$(BUILD)/openamp/metal: $(BUILD)/openamp
$(MKDIR) -p $@
$(BUILD)/openamp/metal/config.h: $(BUILD)/openamp/metal $(TOP)/$(LIBMETAL_DIR)/lib/config.h
@$(ECHO) "GEN $@"
@for file in $(TOP)/$(LIBMETAL_DIR)/lib/*.c $(TOP)/$(LIBMETAL_DIR)/lib/*.h; do $(SED) -e "s/@PROJECT_SYSTEM@/micropython/g" -e "s/@PROJECT_PROCESSOR@/arm/g" $$file > $(BUILD)/openamp/metal/$$(basename $$file); done
$(MKDIR) -p $(BUILD)/openamp/metal/processor/arm
@$(CP) $(TOP)/$(LIBMETAL_DIR)/lib/processor/arm/*.h $(BUILD)/openamp/metal/processor/arm
$(MKDIR) -p $(BUILD)/openamp/metal/compiler/gcc
@$(CP) $(TOP)/$(LIBMETAL_DIR)/lib/compiler/gcc/*.h $(BUILD)/openamp/metal/compiler/gcc
$(MKDIR) -p $(BUILD)/openamp/metal/system/micropython
@$(CP) $(TOP)/$(LIBMETAL_DIR)/lib/system/generic/*.c $(TOP)/$(LIBMETAL_DIR)/lib/system/generic/*.h $(BUILD)/openamp/metal/system/micropython
@$(CP) $(TOP)/extmod/libmetal/metal/system/micropython/* $(BUILD)/openamp/metal/system/micropython
@$(CP) $(TOP)/extmod/libmetal/metal/config.h $(BUILD)/openamp/metal/config.h
# libmetal's source files.
SRC_LIBMETAL_C := $(addprefix $(BUILD)/openamp/metal/,\
device.c \
dma.c \
init.c \
io.c \
irq.c \
log.c \
shmem.c \
softirq.c \
version.c \
device.c \
system/micropython/condition.c \
system/micropython/device.c \
system/micropython/io.c \
system/micropython/irq.c \
system/micropython/shmem.c \
system/micropython/time.c \
)
# These files are generated by the rule above (along with config.h), so we need to make the .c
# files depend on that step -- can't use the .o files as the target because the .c files don't
# exist yet.
$(SRC_LIBMETAL_C): $(BUILD)/openamp/metal/config.h
# Qstr processing will include the generated libmetal headers, so add them as a qstr requirement.
QSTR_GLOBAL_REQUIREMENTS += $(BUILD)/openamp/metal/config.h

Wyświetl plik

@ -0,0 +1,81 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* MicroPython libmetal config.
*/
#ifndef MICROPY_INCLUDED_METAL_MICROPYTHON_H
#define MICROPY_INCLUDED_METAL_MICROPYTHON_H
#include <stdlib.h>
// Port-specific config.
#include "mpmetalport.h"
// Library major version number.
#define METAL_VER_MAJOR 1
// Library minor version number.
#define METAL_VER_MINOR 5
// Library patch level.
#define METAL_VER_PATCH 0
// Library version string.
#define METAL_VER "1.5.0"
#if METAL_HAVE_STDATOMIC_H
#define HAVE_STDATOMIC_H
#endif
#if METAL_HAVE_FUTEX_H
#define HAVE_FUTEX_H
#endif
#ifndef METAL_MAX_DEVICE_REGIONS
#define METAL_MAX_DEVICE_REGIONS 1
#endif
// generic/log.h
#if METAL_LOG_HANDLER_ENABLE
#include "py/mphal.h"
#undef metal_log
#define metal_log(level, ...) mp_printf(&mp_plat_print, __VA_ARGS__)
#endif
static inline void *__metal_allocate_memory(unsigned int size) {
return m_tracked_calloc(1, size);
}
static inline void __metal_free_memory(void *ptr) {
m_tracked_free(ptr);
}
// The following functions must be provided by the port (in mpmetalport.h / mpmetalport.c).
int __metal_sleep_usec(unsigned int usec);
void sys_irq_enable(unsigned int vector);
void sys_irq_disable(unsigned int vector);
void sys_irq_restore_enable(unsigned int flags);
unsigned int sys_irq_save_disable(void);
#endif // MICROPY_INCLUDED_METAL_MICROPYTHON_H

Wyświetl plik

@ -0,0 +1 @@
#include <metal/config.h>

Wyświetl plik

@ -0,0 +1 @@
#include <metal/config.h>

Wyświetl plik

@ -0,0 +1 @@
#include <metal/config.h>

Wyświetl plik

@ -0,0 +1,5 @@
#include <metal/config.h>
struct metal_state {
struct metal_common_state common;
};

Wyświetl plik

@ -27,7 +27,7 @@
#include "py/runtime.h"
#include "extmod/modmachine.h"
#if MICROPY_PY_MACHINE
#if MICROPY_PY_MACHINE_MEMX
// If you wish to override the functions for mapping the machine_mem read/write
// address, then add a #define for MICROPY_MACHINE_MEM_GET_READ_ADDR and/or
@ -113,4 +113,4 @@ const machine_mem_obj_t machine_mem8_obj = {{&machine_mem_type}, 1};
const machine_mem_obj_t machine_mem16_obj = {{&machine_mem_type}, 2};
const machine_mem_obj_t machine_mem32_obj = {{&machine_mem_type}, 4};
#endif // MICROPY_PY_MACHINE
#endif // MICROPY_PY_MACHINE_MEMX

Wyświetl plik

@ -24,12 +24,11 @@
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_PY_MACHINE
#include <string.h>
#include "py/runtime.h"
#if MICROPY_PY_MACHINE_SIGNAL
#include "extmod/modmachine.h"
#include "extmod/virtpin.h"
@ -181,4 +180,4 @@ MP_DEFINE_CONST_OBJ_TYPE(
locals_dict, &signal_locals_dict
);
#endif // MICROPY_PY_MACHINE
#endif // MICROPY_PY_MACHINE_SIGNAL

Wyświetl plik

@ -0,0 +1,335 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Angus Gratton
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "py/mpconfig.h"
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
#include "mp_usbd.h"
#include "py/mperrno.h"
#include "py/objstr.h"
// Implements the singleton runtime USB object
//
// Currently this implementation references TinyUSB directly.
#ifndef NO_QSTR
#include "device/usbd_pvt.h"
#endif
#define HAS_BUILTIN_DRIVERS (MICROPY_HW_USB_CDC || MICROPY_HW_USB_MSC)
const mp_obj_type_t machine_usb_device_type;
static mp_obj_t usb_device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
(void)type;
(void)n_args;
(void)n_kw;
(void)args;
if (MP_STATE_VM(usbd) == MP_OBJ_NULL) {
mp_obj_usb_device_t *o = m_new0(mp_obj_usb_device_t, 1);
o->base.type = &machine_usb_device_type;
o->desc_dev = mp_const_none;
o->desc_cfg = mp_const_none;
o->desc_strs = mp_const_none;
o->open_itf_cb = mp_const_none;
o->reset_cb = mp_const_none;
o->control_xfer_cb = mp_const_none;
o->xfer_cb = mp_const_none;
for (int i = 0; i < CFG_TUD_ENDPPOINT_MAX; i++) {
o->xfer_data[i][0] = mp_const_none;
o->xfer_data[i][1] = mp_const_none;
}
o->builtin_driver = MP_OBJ_FROM_PTR(&mp_type_usb_device_builtin_none);
o->active = false; // Builtin USB may be active already, but runtime is inactive
o->trigger = false;
o->control_data = MP_OBJ_TO_PTR(mp_obj_new_memoryview('B', 0, NULL));
o->num_pend_excs = 0;
for (int i = 0; i < MP_USBD_MAX_PEND_EXCS; i++) {
o->pend_excs[i] = mp_const_none;
}
MP_STATE_VM(usbd) = MP_OBJ_FROM_PTR(o);
}
return MP_STATE_VM(usbd);
}
// Utility helper to raise an error if USB device is not active
// (or if a change of active state is triggered but not processed.)
static void usb_device_check_active(mp_obj_usb_device_t *usbd) {
if (!usbd->active || usbd->trigger) {
mp_raise_OSError(MP_EINVAL);
}
}
static mp_obj_t usb_device_submit_xfer(mp_obj_t self, mp_obj_t ep, mp_obj_t buffer) {
mp_obj_usb_device_t *usbd = (mp_obj_usb_device_t *)MP_OBJ_TO_PTR(self);
int ep_addr;
mp_buffer_info_t buf_info = { 0 };
bool result;
usb_device_check_active(usbd);
// Unmarshal arguments, raises TypeError if invalid
ep_addr = mp_obj_get_int(ep);
mp_get_buffer_raise(buffer, &buf_info, ep_addr & TUSB_DIR_IN_MASK ? MP_BUFFER_READ : MP_BUFFER_RW);
uint8_t ep_num = tu_edpt_number(ep_addr);
uint8_t ep_dir = tu_edpt_dir(ep_addr);
if (ep_num == 0 || ep_num >= CFG_TUD_ENDPPOINT_MAX) {
// TinyUSB usbd API doesn't range check arguments, so this check avoids
// out of bounds array access, or submitting transfers on the control endpoint.
//
// This C layer doesn't otherwise keep track of which endpoints the host
// is aware of (or not).
mp_raise_ValueError("ep");
}
if (!usbd_edpt_claim(USBD_RHPORT, ep_addr)) {
mp_raise_OSError(MP_EBUSY);
}
result = usbd_edpt_xfer(USBD_RHPORT, ep_addr, buf_info.buf, buf_info.len);
if (result) {
// Store the buffer object until the transfer completes
usbd->xfer_data[ep_num][ep_dir] = buffer;
}
return mp_obj_new_bool(result);
}
static MP_DEFINE_CONST_FUN_OBJ_3(usb_device_submit_xfer_obj, usb_device_submit_xfer);
static mp_obj_t usb_device_active(size_t n_args, const mp_obj_t *args) {
mp_obj_usb_device_t *usbd = (mp_obj_usb_device_t *)MP_OBJ_TO_PTR(args[0]);
bool result = usbd->active;
if (n_args == 2) {
bool value = mp_obj_is_true(args[1]);
if (value != result) {
if (value
&& !mp_usb_device_builtin_enabled(usbd)
&& usbd->desc_dev == mp_const_none) {
// Only allow activating if config() has already been called to set some descriptors, or a
// built-in driver is enabled
mp_raise_OSError(MP_EINVAL);
}
// Any change to active state is triggered here and processed
// from the TinyUSB task.
usbd->active = value;
usbd->trigger = true;
if (value) {
mp_usbd_init(); // Ensure TinyUSB has initialised by this point
}
mp_usbd_schedule_task();
}
}
return mp_obj_new_bool(result);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_active_obj, 1, 2, usb_device_active);
static mp_obj_t usb_device_stall(size_t n_args, const mp_obj_t *args) {
mp_obj_usb_device_t *self = (mp_obj_usb_device_t *)MP_OBJ_TO_PTR(args[0]);
int epnum = mp_obj_get_int(args[1]);
usb_device_check_active(self);
mp_obj_t res = mp_obj_new_bool(usbd_edpt_stalled(USBD_RHPORT, epnum));
if (n_args == 3) { // Set stall state
mp_obj_t stall = args[2];
if (mp_obj_is_true(stall)) {
usbd_edpt_stall(USBD_RHPORT, epnum);
} else {
usbd_edpt_clear_stall(USBD_RHPORT, epnum);
}
}
return res;
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_stall_obj, 2, 3, usb_device_stall);
// Configure the singleton USB device with all of the relevant transfer and descriptor
// callbacks for dynamic devices.
static mp_obj_t usb_device_config(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mp_obj_usb_device_t *self = (mp_obj_usb_device_t *)MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_desc_dev, ARG_desc_cfg, ARG_desc_strs, ARG_open_itf_cb,
ARG_reset_cb, ARG_control_xfer_cb, ARG_xfer_cb, ARG_active };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_desc_dev, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_desc_cfg, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_desc_strs, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_open_itf_cb, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_reset_cb, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_control_xfer_cb, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_xfer_cb, MP_ARG_OBJ, {.u_obj = mp_const_none} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
// Check descriptor arguments
mp_obj_t desc_dev = args[ARG_desc_dev].u_obj;
mp_obj_t desc_cfg = args[ARG_desc_cfg].u_obj;
mp_obj_t desc_strs = args[ARG_desc_strs].u_obj;
if (!MP_OBJ_TYPE_HAS_SLOT(mp_obj_get_type(desc_dev), buffer)) {
mp_raise_ValueError(MP_ERROR_TEXT("desc_dev"));
}
if (!MP_OBJ_TYPE_HAS_SLOT(mp_obj_get_type(desc_cfg), buffer)) {
mp_raise_ValueError(MP_ERROR_TEXT("desc_cfg"));
}
if (desc_strs != mp_const_none
&& !MP_OBJ_TYPE_HAS_SLOT(mp_obj_get_type(desc_strs), subscr)) {
mp_raise_ValueError(MP_ERROR_TEXT("desc_strs"));
}
self->desc_dev = desc_dev;
self->desc_cfg = desc_cfg;
self->desc_strs = desc_strs;
self->open_itf_cb = args[ARG_open_itf_cb].u_obj;
self->reset_cb = args[ARG_reset_cb].u_obj;
self->control_xfer_cb = args[ARG_control_xfer_cb].u_obj;
self->xfer_cb = args[ARG_xfer_cb].u_obj;
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(usb_device_config_obj, 1, usb_device_config);
static const MP_DEFINE_BYTES_OBJ(builtin_default_desc_dev_obj,
&mp_usbd_builtin_desc_dev, sizeof(tusb_desc_device_t));
#if HAS_BUILTIN_DRIVERS
// BUILTIN_DEFAULT Python object holds properties of the built-in USB configuration
// (i.e. values used by the C implementation of TinyUSB devices.)
static const MP_DEFINE_BYTES_OBJ(builtin_default_desc_cfg_obj,
mp_usbd_builtin_desc_cfg, MP_USBD_BUILTIN_DESC_CFG_LEN);
static const mp_rom_map_elem_t usb_device_builtin_default_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_itf_max), MP_OBJ_NEW_SMALL_INT(USBD_ITF_BUILTIN_MAX) },
{ MP_ROM_QSTR(MP_QSTR_ep_max), MP_OBJ_NEW_SMALL_INT(USBD_EP_BUILTIN_MAX) },
{ MP_ROM_QSTR(MP_QSTR_str_max), MP_OBJ_NEW_SMALL_INT(USBD_STR_BUILTIN_MAX) },
{ MP_ROM_QSTR(MP_QSTR_desc_dev), MP_ROM_PTR(&builtin_default_desc_dev_obj) },
{ MP_ROM_QSTR(MP_QSTR_desc_cfg), MP_ROM_PTR(&builtin_default_desc_cfg_obj) },
};
static MP_DEFINE_CONST_DICT(usb_device_builtin_default_dict, usb_device_builtin_default_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
mp_type_usb_device_builtin_default,
MP_QSTR_BUILTIN_DEFAULT,
MP_TYPE_FLAG_NONE,
locals_dict, &usb_device_builtin_default_dict
);
#endif // HAS_BUILTIN_DRIVERS
// BUILTIN_NONE holds properties for no enabled built-in USB device support
static const mp_rom_map_elem_t usb_device_builtin_none_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_itf_max), MP_OBJ_NEW_SMALL_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_ep_max), MP_OBJ_NEW_SMALL_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_str_max), MP_OBJ_NEW_SMALL_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_desc_dev), MP_ROM_PTR(&builtin_default_desc_dev_obj) },
{ MP_ROM_QSTR(MP_QSTR_desc_cfg), mp_const_empty_bytes },
};
static MP_DEFINE_CONST_DICT(usb_device_builtin_none_dict, usb_device_builtin_none_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
mp_type_usb_device_builtin_none,
MP_QSTR_BUILTIN_NONE,
MP_TYPE_FLAG_NONE,
locals_dict, &usb_device_builtin_none_dict
);
static const mp_rom_map_elem_t usb_device_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&usb_device_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_submit_xfer), MP_ROM_PTR(&usb_device_submit_xfer_obj) },
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&usb_device_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_stall), MP_ROM_PTR(&usb_device_stall_obj) },
// Built-in driver constants
{ MP_ROM_QSTR(MP_QSTR_BUILTIN_NONE), MP_ROM_PTR(&mp_type_usb_device_builtin_none) },
#if !HAS_BUILTIN_DRIVERS
// No builtin-in drivers, so BUILTIN_DEFAULT is BUILTIN_NONE
{ MP_ROM_QSTR(MP_QSTR_BUILTIN_DEFAULT), MP_ROM_PTR(&mp_type_usb_device_builtin_none) },
#else
{ MP_ROM_QSTR(MP_QSTR_BUILTIN_DEFAULT), MP_ROM_PTR(&mp_type_usb_device_builtin_default) },
// Specific driver constant names are to support future switching of built-in drivers,
// but currently only one is present and it maps directly to BUILTIN_DEFAULT
#if MICROPY_HW_USB_CDC && !MICROPY_HW_USB_MSC
{ MP_ROM_QSTR(MP_QSTR_BUILTIN_CDC), MP_ROM_PTR(&mp_type_usb_device_builtin_default) },
#endif
#if MICROPY_HW_USB_MSC && !MICROPY_HW_USB_CDC
{ MP_ROM_QSTR(MP_QSTR_BUILTIN_MSC), MP_ROM_PTR(&mp_type_usb_device_builtin_default) },
#endif
#if MICROPY_HW_USB_CDC && MICROPY_HW_USB_MSC
{ MP_ROM_QSTR(MP_QSTR_BUILTIN_CDC_MSC), MP_ROM_PTR(&mp_type_usb_device_builtin_default) },
#endif
#endif // !HAS_BUILTIN_DRIVERS
};
static MP_DEFINE_CONST_DICT(usb_device_locals_dict, usb_device_locals_dict_table);
static void usb_device_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
mp_obj_usb_device_t *self = MP_OBJ_TO_PTR(self_in);
if (dest[0] == MP_OBJ_NULL) {
// Load attribute.
if (attr == MP_QSTR_builtin_driver) {
dest[0] = self->builtin_driver;
} else {
// Continue lookup in locals_dict.
dest[1] = MP_OBJ_SENTINEL;
}
} else if (dest[1] != MP_OBJ_NULL) {
// Store attribute.
if (attr == MP_QSTR_builtin_driver) {
if (self->active) {
mp_raise_OSError(MP_EINVAL); // Need to deactivate first
}
// Note: this value should be one of the BUILTIN_nnn constants,
// but not checked here to save code size in a low level API
self->builtin_driver = dest[1];
dest[0] = MP_OBJ_NULL;
}
}
}
MP_DEFINE_CONST_OBJ_TYPE(
machine_usb_device_type,
MP_QSTR_USBDevice,
MP_TYPE_FLAG_NONE,
make_new, usb_device_make_new,
locals_dict, &usb_device_locals_dict,
attr, &usb_device_attr
);
MP_REGISTER_ROOT_POINTER(mp_obj_t usbd);
#endif

Wyświetl plik

@ -57,8 +57,8 @@
#undef CIRCLEQ_INSERT_TAIL
#undef CIRCLEQ_REMOVE
#include <db.h>
#include <../../btree/btree.h>
#include "berkeley-db/db.h"
#include "berkeley-db/btree.h"
typedef struct _mp_obj_btree_t {
mp_obj_base_t base;

Wyświetl plik

@ -38,6 +38,7 @@
#if MICROPY_PY_LWIP
#include "shared/netutils/netutils.h"
#include "modnetwork.h"
#include "lwip/init.h"
#include "lwip/tcp.h"
@ -353,8 +354,12 @@ static void lwip_socket_free_incoming(lwip_socket_obj_t *socket) {
}
}
#if LWIP_VERSION_MAJOR < 2
#define IPADDR_STRLEN_MAX 46
#endif
mp_obj_t lwip_format_inet_addr(const ip_addr_t *ip, mp_uint_t port) {
char *ipstr = ipaddr_ntoa(ip);
char ipstr[IPADDR_STRLEN_MAX];
ipaddr_ntoa_r(ip, ipstr, sizeof(ipstr));
mp_obj_t tuple[2] = {
tuple[0] = mp_obj_new_str(ipstr, strlen(ipstr)),
tuple[1] = mp_obj_new_int(port),
@ -1750,7 +1755,11 @@ static mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) {
state.status = 0;
MICROPY_PY_LWIP_ENTER
#if LWIP_VERSION_MAJOR < 2
err_t ret = dns_gethostbyname(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state);
#else
err_t ret = dns_gethostbyname_addrtype(host, (ip_addr_t *)&state.ipaddr, lwip_getaddrinfo_cb, &state, mp_mod_network_prefer_dns_use_ip_version == 4 ? LWIP_DNS_ADDRTYPE_IPV4_IPV6 : LWIP_DNS_ADDRTYPE_IPV6_IPV4);
#endif
MICROPY_PY_LWIP_EXIT
switch (ret) {

Wyświetl plik

@ -43,10 +43,13 @@ static void mp_machine_idle(void);
NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args);
#endif
#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS
static mp_obj_t mp_machine_unique_id(void);
#if MICROPY_PY_MACHINE_RESET
NORETURN static void mp_machine_reset(void);
static mp_int_t mp_machine_reset_cause(void);
#endif
#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS
static mp_obj_t mp_machine_unique_id(void);
static mp_obj_t mp_machine_get_freq(void);
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args);
static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args);
@ -77,12 +80,7 @@ static mp_obj_t machine_idle(void) {
}
static MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle);
#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS
static mp_obj_t machine_unique_id(void) {
return mp_machine_unique_id();
}
MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
#if MICROPY_PY_MACHINE_RESET
NORETURN static mp_obj_t machine_reset(void) {
mp_machine_reset();
@ -94,6 +92,15 @@ static mp_obj_t machine_reset_cause(void) {
}
MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause);
#endif
#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS
static mp_obj_t machine_unique_id(void) {
return mp_machine_unique_id();
}
MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id);
static mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) {
if (n_args == 0) {
return mp_machine_get_freq();
@ -138,9 +145,11 @@ static const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_machine) },
// Memory access objects.
#if MICROPY_PY_MACHINE_MEMX
{ MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) },
#endif
// Miscellaneous functions.
#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS
@ -152,7 +161,7 @@ static const mp_rom_map_elem_t machine_module_globals_table[] = {
#if MICROPY_PY_MACHINE_BOOTLOADER
{ MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_obj) },
#endif
#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS
#if MICROPY_PY_MACHINE_RESET
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) },
{ MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) },
#endif
@ -186,7 +195,9 @@ static const mp_rom_map_elem_t machine_module_globals_table[] = {
#if MICROPY_PY_MACHINE_PIN_BASE
{ MP_ROM_QSTR(MP_QSTR_PinBase), MP_ROM_PTR(&machine_pinbase_type) },
#endif
#if MICROPY_PY_MACHINE_SIGNAL
{ MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) },
#endif
// Classes for software bus protocols.
#if MICROPY_PY_MACHINE_SOFTI2C
@ -221,6 +232,9 @@ static const mp_rom_map_elem_t machine_module_globals_table[] = {
#if MICROPY_PY_MACHINE_UART
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) },
#endif
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
{ MP_ROM_QSTR(MP_QSTR_USBDevice), MP_ROM_PTR(&machine_usb_device_type) },
#endif
#if MICROPY_PY_MACHINE_WDT
{ MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) },
#endif

Wyświetl plik

@ -213,6 +213,7 @@ extern const mp_obj_type_t machine_signal_type;
extern const mp_obj_type_t machine_spi_type;
extern const mp_obj_type_t machine_timer_type;
extern const mp_obj_type_t machine_uart_type;
extern const mp_obj_type_t machine_usbd_type;
extern const mp_obj_type_t machine_wdt_type;
#if MICROPY_PY_MACHINE_SOFTI2C
@ -230,6 +231,10 @@ extern const mp_machine_spi_p_t mp_machine_soft_spi_p;
extern const mp_obj_dict_t mp_machine_spi_locals_dict;
#endif
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
extern const mp_obj_type_t machine_usb_device_type;
#endif
#if defined(MICROPY_MACHINE_MEM_GET_READ_ADDR)
uintptr_t MICROPY_MACHINE_MEM_GET_READ_ADDR(mp_obj_t addr_o, uint align);
#endif

Wyświetl plik

@ -141,10 +141,17 @@ mp_obj_t mod_network_hostname(size_t n_args, const mp_obj_t *args) {
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_network_hostname_obj, 0, 1, mod_network_hostname);
#if LWIP_VERSION_MAJOR >= 2
MP_DEFINE_CONST_FUN_OBJ_KW(mod_network_ipconfig_obj, 0, mod_network_ipconfig);
#endif
static const mp_rom_map_elem_t mp_module_network_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) },
{ MP_ROM_QSTR(MP_QSTR_country), MP_ROM_PTR(&mod_network_country_obj) },
{ MP_ROM_QSTR(MP_QSTR_hostname), MP_ROM_PTR(&mod_network_hostname_obj) },
#if LWIP_VERSION_MAJOR >= 2
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&mod_network_ipconfig_obj) },
#endif
// Defined per port in mpconfigport.h
#ifdef MICROPY_PORT_NETWORK_INTERFACES

Wyświetl plik

@ -69,10 +69,18 @@ extern char mod_network_hostname_data[MICROPY_PY_NETWORK_HOSTNAME_MAX_LEN + 1];
mp_obj_t mod_network_hostname(size_t n_args, const mp_obj_t *args);
#if MICROPY_PY_LWIP
#include "lwip/init.h"
struct netif;
void mod_network_lwip_init(void);
void mod_network_lwip_poll_wrapper(uint32_t ticks_ms);
mp_obj_t mod_network_nic_ifconfig(struct netif *netif, size_t n_args, const mp_obj_t *args);
#if LWIP_VERSION_MAJOR >= 2
mp_obj_t mod_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
mp_obj_t mod_network_nic_ipconfig(struct netif *netif, size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
extern int mp_mod_network_prefer_dns_use_ip_version;
#endif
#elif defined(MICROPY_PORT_NETWORK_INTERFACES)
struct _mod_network_socket_obj_t;

402
extmod/modopenamp.c 100644
Wyświetl plik

@ -0,0 +1,402 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023-2024 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's Python module.
*/
#if MICROPY_PY_OPENAMP
#include "py/obj.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "metal/sys.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "metal/device.h"
#include "metal/utilities.h"
#include "openamp/open_amp.h"
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#include "modopenamp.h"
#if !MICROPY_ENABLE_FINALISER
#error "MICROPY_PY_OPENAMP requires MICROPY_ENABLE_FINALISER"
#endif
#if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE
#define VIRTIO_DEV_ID 0xFF
#define VIRTIO_DEV_FEATURES (1 << VIRTIO_RPMSG_F_NS)
#define VRING0_ID 0 // VRING0 ID (host to remote) fixed to 0 for linux compatibility
#define VRING1_ID 1 // VRING1 ID (remote to host) fixed to 1 for linux compatibility
#define VRING_NOTIFY_ID VRING0_ID
#define VRING_COUNT 2
#define VRING_ALIGNMENT 32
// Note the number of buffers must be a power of 2
#define VRING_NUM_BUFFS 64
// The following config should be enough for about 128 descriptors.
// See lib/include/openamp/virtio_ring.h for the layout of vrings
// and vring_size() to calculate the vring size.
#define VRING_RX_ADDR (METAL_SHM_ADDR)
#define VRING_TX_ADDR (METAL_SHM_ADDR + 0x1000)
#define VRING_BUFF_ADDR (METAL_SHM_ADDR + 0x2000)
#define VRING_BUFF_SIZE (METAL_SHM_SIZE - 0x2000)
static const char openamp_trace_buf[128];
#define MICROPY_PY_OPENAMP_TRACE_BUF ((uint32_t)openamp_trace_buf)
#define MICROPY_PY_OPENAMP_TRACE_BUF_LEN sizeof(MICROPY_PY_OPENAMP_TRACE_BUF)
#endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE
#define debug_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
#if MICROPY_PY_OPENAMP_REMOTEPROC
extern mp_obj_type_t openamp_remoteproc_type;
#endif
static struct metal_device shm_device = {
.name = METAL_SHM_NAME,
// The number of IO regions is fixed and must match the number and
// layout of the remote processor's IO regions. The first region is
// used for the vring shared memory, and the second one is used for
// the shared resource table.
.num_regions = METAL_MAX_DEVICE_REGIONS,
.regions = { { 0 } },
.node = { NULL },
.irq_num = 0,
.irq_info = NULL
};
static metal_phys_addr_t shm_physmap[] = { 0 };
// ###################### Virtio device class ######################
typedef struct _virtio_dev_obj_t {
mp_obj_base_t base;
struct rpmsg_virtio_device rvdev;
struct rpmsg_virtio_shm_pool shm_pool;
mp_obj_t ns_callback;
} virtio_dev_obj_t;
static mp_obj_t virtio_dev_deinit(mp_obj_t self_in) {
virtio_dev_obj_t *virtio_device = MP_OBJ_TO_PTR(self_in);
rpmsg_deinit_vdev(&virtio_device->rvdev);
metal_finish();
MP_STATE_PORT(virtio_device) = NULL;
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(virtio_dev_deinit_obj, virtio_dev_deinit);
static const mp_rom_map_elem_t virtio_dev_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_VirtIODev) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&virtio_dev_deinit_obj) },
};
static MP_DEFINE_CONST_DICT(virtio_dev_locals_dict, virtio_dev_locals_dict_table);
static MP_DEFINE_CONST_OBJ_TYPE(
virtio_dev_type,
MP_QSTR_VirtIODev,
MP_TYPE_FLAG_NONE,
locals_dict, &virtio_dev_locals_dict
);
// ###################### RPMsg Endpoint class ######################
typedef struct _endpoint_obj_t {
mp_obj_base_t base;
mp_obj_t name;
mp_obj_t callback;
struct rpmsg_endpoint ep;
} endpoint_obj_t;
static const mp_obj_type_t endpoint_type;
static int endpoint_recv_callback(struct rpmsg_endpoint *ept, void *data, size_t len, uint32_t src, void *priv) {
debug_printf("endpoint_recv_callback() message received src: %lu msg len: %d\n", src, len);
endpoint_obj_t *self = metal_container_of(ept, endpoint_obj_t, ep);
if (self->callback != mp_const_none) {
mp_call_function_2(self->callback, mp_obj_new_int(src), mp_obj_new_bytearray_by_ref(len, data));
}
return 0;
}
static mp_obj_t endpoint_send(uint n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_src, ARG_dest, ARG_timeout };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_src, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_dest, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
{ MP_QSTR_timeout, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = -1 } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
endpoint_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
if (is_rpmsg_ept_ready(&self->ep) == false) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Endpoint not ready"));
}
uint32_t src = self->ep.addr;
if (args[ARG_src].u_int != -1) {
src = args[ARG_src].u_int;
}
uint32_t dest = self->ep.dest_addr;
if (args[ARG_dest].u_int != -1) {
dest = args[ARG_dest].u_int;
}
mp_buffer_info_t rbuf;
mp_get_buffer_raise(pos_args[1], &rbuf, MP_BUFFER_READ);
debug_printf("endpoint_send() msg len: %d\n", rbuf.len);
int bytes = 0;
mp_int_t timeout = args[ARG_timeout].u_int;
for (mp_uint_t start = mp_hal_ticks_ms(); ;) {
bytes = rpmsg_send_offchannel_raw(&self->ep, src, dest, rbuf.buf, rbuf.len, false);
if (bytes > 0 || timeout == 0) {
MICROPY_EVENT_POLL_HOOK
break;
}
if (timeout > 0 && (mp_hal_ticks_ms() - start > timeout)) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("timeout waiting for a free buffer"));
}
MICROPY_EVENT_POLL_HOOK
}
return mp_obj_new_int(bytes);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(endpoint_send_obj, 2, endpoint_send);
static mp_obj_t endpoint_is_ready(mp_obj_t self_in) {
endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in);
return is_rpmsg_ept_ready(&self->ep) ? mp_const_true : mp_const_false;
}
static MP_DEFINE_CONST_FUN_OBJ_1(endpoint_is_ready_obj, endpoint_is_ready);
static mp_obj_t endpoint_deinit(mp_obj_t self_in) {
endpoint_obj_t *self = MP_OBJ_TO_PTR(self_in);
rpmsg_destroy_ept(&self->ep);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(endpoint_deinit_obj, endpoint_deinit);
static mp_obj_t endpoint_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_name, ARG_callback, ARG_src, ARG_dest };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_name, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_callback, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_src, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
{ MP_QSTR_dest, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = RPMSG_ADDR_ANY } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
endpoint_obj_t *self = mp_obj_malloc_with_finaliser(endpoint_obj_t, &endpoint_type);
self->name = args[ARG_name].u_obj;
self->callback = args[ARG_callback].u_obj;
if (MP_STATE_PORT(virtio_device) == NULL) {
openamp_init();
}
if (rpmsg_create_ept(&self->ep, &MP_STATE_PORT(virtio_device)->rvdev.rdev, mp_obj_str_get_str(self->name),
args[ARG_src].u_int, args[ARG_dest].u_int, endpoint_recv_callback, NULL) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create RPMsg endpoint"));
}
return MP_OBJ_FROM_PTR(self);
}
static const mp_rom_map_elem_t endpoint_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_Endpoint) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&endpoint_deinit_obj) },
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&endpoint_send_obj) },
{ MP_ROM_QSTR(MP_QSTR_is_ready), MP_ROM_PTR(&endpoint_is_ready_obj) },
};
static MP_DEFINE_CONST_DICT(endpoint_locals_dict, endpoint_locals_dict_table);
static MP_DEFINE_CONST_OBJ_TYPE(
endpoint_type,
MP_QSTR_Endpoint,
MP_TYPE_FLAG_NONE,
make_new, endpoint_make_new,
locals_dict, &endpoint_locals_dict
);
// ###################### openamp module ######################
void openamp_remoteproc_notified(mp_sched_node_t *node) {
(void)node;
rproc_virtio_notified(MP_STATE_PORT(virtio_device)->rvdev.vdev, VRING_NOTIFY_ID);
}
static void openamp_ns_callback(struct rpmsg_device *rdev, const char *name, uint32_t dest) {
debug_printf("rpmsg_new_service_callback() new service request name: %s dest %lu\n", name, dest);
// The remote processor advertises its presence to the host by sending
// the Name Service (NS) announcement containing the name of the channel.
virtio_dev_obj_t *virtio_device = metal_container_of(rdev, virtio_dev_obj_t, rvdev);
if (virtio_device->ns_callback != mp_const_none) {
mp_call_function_2(virtio_device->ns_callback, mp_obj_new_int(dest), mp_obj_new_str(name, strlen(name)));
}
}
#if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE
// The shared resource table must be initialized manually by the host here,
// because it's not located in the data region, so the startup code doesn't
// know about it.
static void openamp_rsc_table_init(openamp_rsc_table_t **rsc_table_out) {
openamp_rsc_table_t *rsc_table = METAL_RSC_ADDR;
memset(rsc_table, 0, METAL_RSC_SIZE);
rsc_table->version = 1;
rsc_table->num = MP_ARRAY_SIZE(rsc_table->offset);
rsc_table->offset[0] = offsetof(openamp_rsc_table_t, vdev);
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
rsc_table->offset[1] = offsetof(openamp_rsc_table_t, trace);
#endif
rsc_table->vdev = (struct fw_rsc_vdev) {
RSC_VDEV, VIRTIO_ID_RPMSG, 0, VIRTIO_DEV_FEATURES, 0, 0, 0, VRING_COUNT, {0, 0}
};
rsc_table->vring0 = (struct fw_rsc_vdev_vring) {
VRING_TX_ADDR, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING0_ID, 0
};
rsc_table->vring1 = (struct fw_rsc_vdev_vring) {
VRING_RX_ADDR, VRING_ALIGNMENT, VRING_NUM_BUFFS, VRING1_ID, 0
};
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
rsc_table->trace = (struct fw_rsc_trace) {
RSC_TRACE, MICROPY_PY_OPENAMP_TRACE_BUF, MICROPY_PY_OPENAMP_TRACE_BUF_LEN, 0, "trace_buf"
};
#endif
#ifdef VIRTIO_USE_DCACHE
// Flush resource table.
metal_cache_flush((uint32_t *)rsc_table, sizeof(openamp_rsc_table_t));
#endif
*rsc_table_out = rsc_table;
}
#endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE
static mp_obj_t openamp_new_service_callback(mp_obj_t ns_callback) {
if (MP_STATE_PORT(virtio_device) == NULL) {
openamp_init();
}
if (ns_callback != mp_const_none && !mp_obj_is_callable(ns_callback)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid callback"));
}
MP_STATE_PORT(virtio_device)->ns_callback = ns_callback;
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(openamp_new_service_callback_obj, openamp_new_service_callback);
void openamp_init(void) {
if (MP_STATE_PORT(virtio_device) != NULL) {
// Already initialized.
return;
}
struct metal_device *device;
struct metal_init_params metal_params = METAL_INIT_DEFAULTS;
// Initialize libmetal.
metal_init(&metal_params);
// Initialize the shared resource table.
openamp_rsc_table_t *rsc_table;
openamp_rsc_table_init(&rsc_table);
if (metal_register_generic_device(&shm_device) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to register metal device"));
}
if (metal_device_open("generic", METAL_SHM_NAME, &device) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to open metal device"));
}
// Initialize shared memory IO region.
metal_io_init(&device->regions[0], (void *)METAL_SHM_ADDR, (void *)shm_physmap, METAL_SHM_SIZE, -1U, 0, NULL);
struct metal_io_region *shm_io = metal_device_io_region(device, 0);
if (shm_io == NULL) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize device io region"));
}
// Initialize resource table IO region.
metal_io_init(&device->regions[1], (void *)rsc_table, (void *)rsc_table, sizeof(*rsc_table), -1U, 0, NULL);
struct metal_io_region *rsc_io = metal_device_io_region(device, 1);
if (rsc_io == NULL) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize device io region"));
}
// Create virtio device.
struct virtio_device *vdev = rproc_virtio_create_vdev(RPMSG_HOST, VIRTIO_DEV_ID,
&rsc_table->vdev, rsc_io, NULL, metal_rproc_notify, NULL);
if (vdev == NULL) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to create virtio device"));
}
// Initialize vrings.
struct fw_rsc_vdev_vring *vring_rsc = &rsc_table->vring0;
for (int i = 0; i < VRING_COUNT; i++, vring_rsc++) {
if (rproc_virtio_init_vring(vdev, vring_rsc->notifyid, vring_rsc->notifyid,
(void *)vring_rsc->da, shm_io, vring_rsc->num, vring_rsc->align) != 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to initialize vrings"));
}
}
virtio_dev_obj_t *virtio_device = mp_obj_malloc_with_finaliser(virtio_dev_obj_t, &virtio_dev_type);
virtio_device->ns_callback = mp_const_none;
// The remote processor detects that the virtio device is ready by polling
// the status field in the resource table.
rpmsg_virtio_init_shm_pool(&virtio_device->shm_pool, (void *)VRING_BUFF_ADDR, (size_t)VRING_BUFF_SIZE);
rpmsg_init_vdev(&virtio_device->rvdev, vdev, openamp_ns_callback, shm_io, &virtio_device->shm_pool);
MP_STATE_PORT(virtio_device) = virtio_device;
}
static const mp_rom_map_elem_t globals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_openamp) },
{ MP_ROM_QSTR(MP_QSTR_ENDPOINT_ADDR_ANY), MP_ROM_INT(RPMSG_ADDR_ANY) },
{ MP_ROM_QSTR(MP_QSTR_new_service_callback), MP_ROM_PTR(&openamp_new_service_callback_obj) },
{ MP_ROM_QSTR(MP_QSTR_Endpoint), MP_ROM_PTR(&endpoint_type) },
#if MICROPY_PY_OPENAMP_REMOTEPROC
{ MP_ROM_QSTR(MP_QSTR_RemoteProc), MP_ROM_PTR(&openamp_remoteproc_type) },
#endif
};
static MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table);
const mp_obj_module_t openamp_module = {
.base = { &mp_type_module },
.globals = (mp_obj_t)&globals_dict,
};
MP_REGISTER_ROOT_POINTER(struct _virtio_dev_obj_t *virtio_device);
MP_REGISTER_MODULE(MP_QSTR_openamp, openamp_module);
#endif // MICROPY_PY_OPENAMP

Wyświetl plik

@ -0,0 +1,93 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's Python module.
*/
#ifndef MICROPY_INCLUDED_MODOPENAMP_H
#define MICROPY_INCLUDED_MODOPENAMP_H
// Include a port config file if one is defined.
#ifdef MICROPY_PY_OPENAMP_CONFIG_FILE
#include MICROPY_PY_OPENAMP_CONFIG_FILE
#endif
// Use the default resource table layout and initialization code.
// Note ports and boards can override the default table and provide
// a custom one in openamp_rsc_table_t and openamp_rsc_table_init()
#ifndef MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE
#define MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE (1)
#endif
// This enables a trace buffer that can be used by remote processor for
// writing trace logs. This is enabled by default to increase the default
// resource table's compatibility with common OpenAMP examples.
#ifndef MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
#define MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE (1)
#endif
// For ports that don't define a custom image store, this enables a generic
// VFS-based image store that supports loading elf files from storage.
#ifndef MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE
#define MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE (1)
#endif
// Enable or disable support for loading elf files. This option saves
// around 7KBs when disabled.
#ifndef MICROPY_PY_OPENAMP_REMOTEPROC_ELFLD_ENABLE
#define MICROPY_PY_OPENAMP_REMOTEPROC_ELFLD_ENABLE (1)
#endif
// The resource table is used for sharing the configuration of the virtio
// device, vrings and other resources, between the host and remote cores.
// The layout and address the table structure must match the one used in
// the remote processor's firmware.
#if MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE
typedef struct openamp_rsc_table {
unsigned int version;
unsigned int num;
unsigned int reserved[2];
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
unsigned int offset[2];
#else
unsigned int offset[1];
#endif
struct fw_rsc_vdev vdev;
struct fw_rsc_vdev_vring vring0;
struct fw_rsc_vdev_vring vring1;
#if MICROPY_PY_OPENAMP_TRACE_BUF_ENABLE
struct fw_rsc_trace trace;
#endif
} openamp_rsc_table_t;
#endif // MICROPY_PY_OPENAMP_RSC_TABLE_ENABLE
// Performs low-level initialization of OpenAMP, such as initializing libmetal,
// the shared resource table, shared memory regions, vrings and virtio device.
void openamp_init(void);
// Ports should run this callback in scheduler context, when
// the remote processor notifies the host of pending messages.
void openamp_remoteproc_notified(mp_sched_node_t *node);
#endif // MICROPY_INCLUDED_MODOPENAMP_H

Wyświetl plik

@ -0,0 +1,175 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023-2024 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's remoteproc class.
*/
#if MICROPY_PY_OPENAMP_REMOTEPROC
#include "py/obj.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/vfs.h"
#include "metal/sys.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "openamp/open_amp.h"
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#include "modopenamp.h"
#include "modopenamp_remoteproc.h"
#define DEBUG_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
#if !MICROPY_PY_OPENAMP
#error "MICROPY_PY_OPENAMP_REMOTEPROC requires MICROPY_PY_OPENAMP"
#endif
typedef struct openamp_remoteproc_obj {
mp_obj_base_t base;
struct remoteproc rproc;
} openamp_remoteproc_obj_t;
const mp_obj_type_t openamp_remoteproc_type;
// Port-defined image store operations.
extern struct image_store_ops openamp_remoteproc_store_ops;
// Port-defined remote-proc operations.
const struct remoteproc_ops openamp_remoteproc_ops = {
.init = mp_openamp_remoteproc_init,
.mmap = mp_openamp_remoteproc_mmap,
.start = mp_openamp_remoteproc_start,
.stop = mp_openamp_remoteproc_stop,
.config = mp_openamp_remoteproc_config,
.remove = mp_openamp_remoteproc_remove,
.shutdown = mp_openamp_remoteproc_shutdown,
};
static mp_obj_t openamp_remoteproc_start(mp_obj_t self_in) {
openamp_remoteproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Start the processor to run the application.
int error = remoteproc_start(&self->rproc);
if (error != 0) {
self->rproc.state = RPROC_ERROR;
mp_raise_OSError(error);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(openamp_remoteproc_start_obj, openamp_remoteproc_start);
static mp_obj_t openamp_remoteproc_stop(mp_obj_t self_in) {
openamp_remoteproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Stop the processor, but the processor is not powered down.
int error = remoteproc_stop(&self->rproc);
if (error != 0) {
mp_raise_OSError(error);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(openamp_remoteproc_stop_obj, openamp_remoteproc_stop);
static mp_obj_t openamp_remoteproc_shutdown(mp_obj_t self_in) {
openamp_remoteproc_obj_t *self = MP_OBJ_TO_PTR(self_in);
// Shutdown the remoteproc and release its resources.
int error = remoteproc_shutdown(&self->rproc);
if (error != 0) {
mp_raise_OSError(error);
}
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(openamp_remoteproc_shutdown_obj, openamp_remoteproc_shutdown);
mp_obj_t openamp_remoteproc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_entry };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_entry, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_rom_obj = MP_ROM_NONE } },
};
// Parse args.
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
openamp_remoteproc_obj_t *self = mp_obj_malloc_with_finaliser(openamp_remoteproc_obj_t, &openamp_remoteproc_type);
// Make sure OpenAMP is initialized.
if (MP_STATE_PORT(virtio_device) == NULL) {
openamp_init();
}
// Create a remoteproc instance.
// NOTE: ports should use rproc->priv to allocate the image store,
// which gets passed to remoteproc_load(), and all of the store ops.
remoteproc_init(&self->rproc, &openamp_remoteproc_ops, NULL);
// Configure the remote before loading applications (optional).
int error = remoteproc_config(&self->rproc, NULL);
if (error != 0) {
mp_raise_OSError(error);
}
if (mp_obj_is_int(args[ARG_entry].u_obj)) {
self->rproc.bootaddr = mp_obj_get_int(args[ARG_entry].u_obj);
} else {
#if MICROPY_PY_OPENAMP_REMOTEPROC_ELFLD_ENABLE
// Load firmware.
const char *path = mp_obj_str_get_str(args[ARG_entry].u_obj);
int error = remoteproc_load(&self->rproc, path, self->rproc.priv, &openamp_remoteproc_store_ops, NULL);
if (error != 0) {
mp_raise_OSError(error);
}
#else
mp_raise_TypeError(MP_ERROR_TEXT("loading firmware is not supported."));
#endif
}
return MP_OBJ_FROM_PTR(self);
}
static const mp_rom_map_elem_t openamp_remoteproc_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_RemoteProc) },
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&openamp_remoteproc_shutdown_obj) },
{ MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&openamp_remoteproc_start_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&openamp_remoteproc_stop_obj) },
{ MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&openamp_remoteproc_shutdown_obj) },
};
static MP_DEFINE_CONST_DICT(openamp_remoteproc_dict, openamp_remoteproc_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
openamp_remoteproc_type,
MP_QSTR_RemoteProc,
MP_TYPE_FLAG_NONE,
make_new, openamp_remoteproc_make_new,
locals_dict, &openamp_remoteproc_dict
);
#endif // MICROPY_PY_OPENAMP_REMOTEPROC

Wyświetl plik

@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2022 Angus Gratton
* Copyright (c) 2023-2024 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -22,13 +22,25 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's remoteproc class.
*/
#ifndef MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_INTERNAL_H
#define MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_INTERNAL_H
#include "tusb.h"
#ifndef MICROPY_INCLUDED_MODOPENAMP_REMOTEPROC_H
#define MICROPY_INCLUDED_MODOPENAMP_REMOTEPROC_H
// Static USB device descriptor values
extern const tusb_desc_device_t mp_usbd_desc_device_static;
extern const uint8_t mp_usbd_desc_cfg_static[USBD_STATIC_DESC_LEN];
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#endif // MICROPY_INCLUDED_SHARED_TINYUSB_MP_USBD_INTERNAL_H
void *mp_openamp_remoteproc_store_alloc(void);
struct remoteproc *mp_openamp_remoteproc_init(struct remoteproc *rproc,
const struct remoteproc_ops *ops, void *arg);
void *mp_openamp_remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa,
metal_phys_addr_t *da, size_t size, unsigned int attribute,
struct metal_io_region **io);
int mp_openamp_remoteproc_start(struct remoteproc *rproc);
int mp_openamp_remoteproc_stop(struct remoteproc *rproc);
int mp_openamp_remoteproc_config(struct remoteproc *rproc, void *data);
void mp_openamp_remoteproc_remove(struct remoteproc *rproc);
int mp_openamp_remoteproc_shutdown(struct remoteproc *rproc);
#endif // MICROPY_INCLUDED_MODOPENAMP_REMOTEPROC_H

Wyświetl plik

@ -0,0 +1,146 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023-2024 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* OpenAMP's remoteproc store.
*/
#if MICROPY_PY_OPENAMP_REMOTEPROC
#include "py/obj.h"
#include "py/nlr.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "extmod/vfs.h"
#include "metal/sys.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "openamp/remoteproc.h"
#include "openamp/remoteproc_loader.h"
#include "modopenamp.h"
#include "modopenamp_remoteproc.h"
#if MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE
#define DEBUG_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
// Note the initial file buffer size needs to be at least 512 to read
// enough of the elf headers on the first call to store_open(), and on
// subsequent calls to store functions, it gets reallocated if needed.
#define RPROC_FILE_STORE_BUF_SIZE (1024)
typedef struct openamp_remoteproc_filestore {
size_t len;
uint8_t *buf;
mp_obj_t file;
} openamp_remoteproc_filestore_t;
void *mp_openamp_remoteproc_store_alloc(void) {
// Allocate an rproc filestore.
openamp_remoteproc_filestore_t *fstore;
fstore = metal_allocate_memory(sizeof(openamp_remoteproc_filestore_t));
fstore->len = RPROC_FILE_STORE_BUF_SIZE;
fstore->buf = metal_allocate_memory(RPROC_FILE_STORE_BUF_SIZE);
return fstore;
}
static int openamp_remoteproc_store_open(void *store, const char *path, const void **image_data) {
DEBUG_printf("store_open(): %s\n", path);
mp_obj_t args[2] = {
mp_obj_new_str(path, strlen(path)),
MP_OBJ_NEW_QSTR(MP_QSTR_rb),
};
openamp_remoteproc_filestore_t *fstore = store;
fstore->file = mp_vfs_open(MP_ARRAY_SIZE(args), args, (mp_map_t *)&mp_const_empty_map);
int error = 0;
mp_uint_t bytes = mp_stream_read_exactly(fstore->file, fstore->buf, RPROC_FILE_STORE_BUF_SIZE, &error);
if (error != 0 || bytes != RPROC_FILE_STORE_BUF_SIZE) {
return -EINVAL;
}
*image_data = fstore->buf;
return bytes;
}
static void openamp_remoteproc_store_close(void *store) {
DEBUG_printf("store_close()\n");
openamp_remoteproc_filestore_t *fstore = store;
mp_stream_close(fstore->file);
metal_free_memory(fstore->buf);
metal_free_memory(fstore);
}
static int openamp_remoteproc_store_load(void *store, size_t offset, size_t size,
const void **data, metal_phys_addr_t pa,
struct metal_io_region *io,
char is_blocking) {
int error = 0;
openamp_remoteproc_filestore_t *fstore = store;
if (mp_stream_seek(fstore->file, offset, MP_SEEK_SET, &error) == -1) {
return -EINVAL;
}
if (pa == METAL_BAD_PHYS) {
if (size > fstore->len) {
// Note tracked allocs don't support realloc.
fstore->len = size;
fstore->buf = metal_allocate_memory(size);
DEBUG_printf("store_load() realloc to %lu\n", fstore->len);
}
*data = fstore->buf;
DEBUG_printf("store_load(): pa 0x%lx offset %u size %u \n", (uint32_t)pa, offset, size);
} else {
void *va = metal_io_phys_to_virt(io, pa);
if (va == NULL) {
return -EINVAL;
}
*data = va;
DEBUG_printf("store_load(): pa 0x%lx va 0x%p offset %u size %u \n", (uint32_t)pa, va, offset, size);
}
mp_uint_t bytes = mp_stream_read_exactly(fstore->file, (void *)*data, size, &error);
if (bytes != size || error != 0) {
return -EINVAL;
}
return bytes;
}
const struct image_store_ops openamp_remoteproc_store_ops = {
.open = openamp_remoteproc_store_open,
.close = openamp_remoteproc_store_close,
.load = openamp_remoteproc_store_load,
.features = SUPPORT_SEEK,
};
#endif // MICROPY_PY_OPENAMP_REMOTEPROC_STORE_ENABLE
#endif // MICROPY_PY_OPENAMP_REMOTEPROC

Wyświetl plik

@ -321,6 +321,12 @@ static mp_obj_t network_cyw43_ifconfig(size_t n_args, const mp_obj_t *args) {
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_cyw43_ifconfig_obj, 1, 2, network_cyw43_ifconfig);
static mp_obj_t network_cyw43_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
network_cyw43_obj_t *self = MP_OBJ_TO_PTR(args[0]);
return mod_network_nic_ipconfig(&self->cyw->netif[self->itf], n_args - 1, args + 1, kwargs);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(network_cyw43_ipconfig_obj, 1, network_cyw43_ipconfig);
static mp_obj_t network_cyw43_status(size_t n_args, const mp_obj_t *args) {
network_cyw43_obj_t *self = MP_OBJ_TO_PTR(args[0]);
(void)self;
@ -532,6 +538,7 @@ static const mp_rom_map_elem_t network_cyw43_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_cyw43_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_cyw43_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_cyw43_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_cyw43_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_cyw43_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_cyw43_config_obj) },

Wyświetl plik

@ -204,6 +204,13 @@ static mp_obj_t network_esp_hosted_ifconfig(size_t n_args, const mp_obj_t *args)
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_esp_hosted_ifconfig_obj, 1, 2, network_esp_hosted_ifconfig);
static mp_obj_t network_esp_hosted_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
esp_hosted_obj_t *self = MP_OBJ_TO_PTR(args[0]);
void *netif = esp_hosted_wifi_get_netif(self->itf);
return mod_network_nic_ipconfig(netif, n_args - 1, args + 1, kwargs);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(network_esp_hosted_ipconfig_obj, 1, network_esp_hosted_ipconfig);
static mp_obj_t network_esp_hosted_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
esp_hosted_obj_t *self = MP_OBJ_TO_PTR(args[0]);
@ -299,6 +306,7 @@ static const mp_rom_map_elem_t network_esp_hosted_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_esp_hosted_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_esp_hosted_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_esp_hosted_ifconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_esp_hosted_ipconfig_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_esp_hosted_config_obj) },
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_esp_hosted_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_OPEN), MP_ROM_INT(ESP_HOSTED_SEC_OPEN) },

Wyświetl plik

@ -26,6 +26,7 @@
#include "py/runtime.h"
#include "py/mphal.h"
#include "py/parsenum.h"
#if MICROPY_PY_NETWORK && MICROPY_PY_LWIP
@ -40,9 +41,19 @@
#include "lwip/timeouts.h"
#include "lwip/dns.h"
#include "lwip/dhcp.h"
#include "lwip/nd6.h"
#include "lwip/dhcp6.h"
#include "lwip/prot/dhcp.h"
#include "lwip/prot/dhcp6.h"
#include <string.h>
#include <stdlib.h>
int mp_mod_network_prefer_dns_use_ip_version = 4;
// Implementations of network methods that can be used by any interface.
// This function provides the implementation of nic.ifconfig, is deprecated and will be removed.
// Use network.ipconfig and nic.ipconfig instead.
mp_obj_t mod_network_nic_ifconfig(struct netif *netif, size_t n_args, const mp_obj_t *args) {
if (n_args == 0) {
// Get IP addresses
@ -90,6 +101,291 @@ mp_obj_t mod_network_nic_ifconfig(struct netif *netif, size_t n_args, const mp_o
}
}
// This function provides the common implementation of network.ipconfig
mp_obj_t mod_network_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (kwargs->used == 0) {
// Get config value
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[0])) {
case MP_QSTR_dns: {
char addr_str[IPADDR_STRLEN_MAX];
ipaddr_ntoa_r(dns_getserver(0), addr_str, sizeof(addr_str));
return mp_obj_new_str(addr_str, strlen(addr_str));
}
case MP_QSTR_prefer: {
return MP_OBJ_NEW_SMALL_INT(mp_mod_network_prefer_dns_use_ip_version);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
} else {
// Set config value(s)
if (n_args != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dns: {
ip_addr_t dns;
size_t addr_len;
const char *addr_str = mp_obj_str_get_data(e->value, &addr_len);
if (!ipaddr_aton(addr_str, &dns)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments as dns server"));
}
dns_setserver(0, &dns);
break;
}
case MP_QSTR_prefer: {
int value = mp_obj_get_int(e->value);
if (value != 4 && value != 6) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid prefer argument"));
}
mp_mod_network_prefer_dns_use_ip_version = value;
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
mp_obj_t mod_network_nic_ipconfig(struct netif *netif, size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (kwargs->used == 0) {
// Get config value
if (n_args != 1) {
mp_raise_TypeError(MP_ERROR_TEXT("must query one param"));
}
switch (mp_obj_str_get_qstr(args[0])) {
case MP_QSTR_dhcp4: {
struct dhcp *dhcp = netif_dhcp_data(netif);
return mp_obj_new_bool(dhcp != NULL && dhcp->state != DHCP_STATE_OFF);
}
case MP_QSTR_has_dhcp4: {
return mp_obj_new_bool(dhcp_supplied_address(netif));
}
#if LWIP_IPV6_DHCP6
case MP_QSTR_dhcp6: {
struct dhcp6 *dhcp = netif_dhcp6_data(netif);
return mp_obj_new_bool(dhcp != NULL && dhcp->state != DHCP6_STATE_OFF);
}
#endif
#if LWIP_IPV6_AUTOCONFIG
case MP_QSTR_autoconf6: {
return netif->ip6_autoconfig_enabled ? mp_const_true : mp_const_false;
}
case MP_QSTR_has_autoconf6: {
int found = 0;
for (int i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
!netif_ip6_addr_isstatic(netif, i)) {
found = 1;
break;
}
}
if (found) {
break;
}
return mp_obj_new_bool(found);
}
#endif
case MP_QSTR_addr4: {
mp_obj_t tuple[2] = {
netutils_format_ipv4_addr((uint8_t *)&netif->ip_addr, NETUTILS_BIG),
netutils_format_ipv4_addr((uint8_t *)&netif->netmask, NETUTILS_BIG),
};
return mp_obj_new_tuple(2, tuple);
}
#if LWIP_IPV6
case MP_QSTR_addr6: {
mp_obj_t addrs[LWIP_IPV6_NUM_ADDRESSES];
size_t n_addrs = 0;
for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
char addr_str[IPADDR_STRLEN_MAX];
ipaddr_ntoa_r(netif_ip_addr6(netif, i), addr_str, sizeof(addr_str));
mp_obj_t tuple[4] = {
mp_obj_new_str(addr_str, strlen(addr_str)),
MP_OBJ_NEW_SMALL_INT(netif_ip6_addr_state(netif, i)),
MP_OBJ_NEW_SMALL_INT(netif_ip6_addr_pref_life(netif, i)), // preferred
MP_OBJ_NEW_SMALL_INT(netif_ip6_addr_valid_life(netif, i))
};
addrs[n_addrs++] = mp_obj_new_tuple(4, tuple);
}
}
return mp_obj_new_list(n_addrs, addrs);
}
#endif
case MP_QSTR_gw4: {
return netutils_format_ipv4_addr((uint8_t *)&netif->gw, NETUTILS_BIG);
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
return mp_const_none;
} else {
// Set config value(s)
if (n_args != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args"));
}
for (size_t i = 0; i < kwargs->alloc; ++i) {
if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) {
mp_map_elem_t *e = &kwargs->table[i];
switch (mp_obj_str_get_qstr(e->key)) {
case MP_QSTR_dhcp4: {
if (mp_obj_is_true(e->value)) {
if (dhcp_supplied_address(netif)) {
dhcp_renew(netif);
} else {
dhcp_release_and_stop(netif);
dhcp_start(netif);
}
} else {
dhcp_release_and_stop(netif);
}
break;
}
#if LWIP_IPV6_DHCP6
case MP_QSTR_dhcp6: {
dhcp6_disable(netif);
dhcp6_enable_stateless(netif);
break;
}
#endif
#if LWIP_IPV6_AUTOCONFIG
case MP_QSTR_autoconf6: {
netif_set_ip6_autoconfig_enabled(netif, mp_obj_is_true(e->value));
if (mp_obj_is_true(e->value)) {
nd6_restart_netif(netif);
} else {
// Clear out any non-static addresses, skip link-local address in slot 0
for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
!netif_ip6_addr_isstatic(netif, i)) {
netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
}
}
}
break;
}
#endif
case MP_QSTR_addr4:
case MP_QSTR_addr6: {
ip_addr_t ip_addr;
ip_addr_t netmask;
int prefix_bits = 32;
if (e->value != mp_const_none && mp_obj_is_str(e->value)) {
size_t addr_len;
const char *input_str = mp_obj_str_get_data(e->value, &addr_len);
char plain_ip[IPADDR_STRLEN_MAX];
char *split = strchr(input_str, '/');
const char *addr_str = input_str;
if (split) {
int to_copy = sizeof(plain_ip) - 1;
if (split - addr_str < to_copy) {
to_copy = split - addr_str;
}
memcpy(plain_ip, addr_str, to_copy);
mp_obj_t prefix_obj = mp_parse_num_integer(split + 1, strlen(split + 1), 10, NULL);
prefix_bits = mp_obj_get_int(prefix_obj);
}
if (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr4) {
uint32_t mask = -(1u << (32 - prefix_bits));
ip_addr_set_ip4_u32_val(netmask, ((mask & 0xFF) << 24) | ((mask & 0xFF00) << 8) | ((mask >> 8) & 0xFF00) | ((mask >> 24) & 0xFF));
}
if (!ipaddr_aton(addr_str, &ip_addr)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments"));
}
if ((mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr6) != IP_IS_V6(&ip_addr)
|| (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr4) != IP_IS_V4(&ip_addr)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid address type"));
}
} else if (e->value != mp_const_none) {
mp_obj_t *items;
mp_obj_get_array_fixed_n(e->value, 2, &items);
size_t addr_len;
const char *ip_addr_str = mp_obj_str_get_data(items[0], &addr_len);
const char *netmask_str = mp_obj_str_get_data(items[1], &addr_len);
if (!ipaddr_aton(ip_addr_str, &ip_addr)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments"));
}
if (!ipaddr_aton(netmask_str, &netmask)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments"));
}
if (!IP_IS_V4(&ip_addr) || !IP_IS_V4(&netmask)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid address type"));
}
}
if (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr4) {
if (e->value != mp_const_none) {
netif->ip_addr = ip_addr;
netif->netmask = netmask;
} else {
ip4_addr_set_any(ip_2_ip4(&netif->ip_addr));
ip4_addr_set_any(ip_2_ip4(&netif->netmask));
}
#if LWIP_IPV6
} else if (mp_obj_str_get_qstr(args[0]) == MP_QSTR_addr6) {
// Clear out any existing static addresses. Address 0 comes from autoconf.
for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
netif_ip6_addr_isstatic(netif, i)) {
netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
}
}
if (e->value != mp_const_none) {
s8_t free_idx;
netif_add_ip6_address(netif, ip_2_ip6(&ip_addr), &free_idx);
netif_ip6_addr_set_valid_life(netif, free_idx, IP6_ADDR_LIFE_STATIC);
netif_ip6_addr_set_pref_life(netif, free_idx, IP6_ADDR_LIFE_STATIC);
netif_ip6_addr_set_state(netif, free_idx, IP6_ADDR_PREFERRED);
}
#endif
}
break;
}
case MP_QSTR_gw4: {
ip_addr_t ip_addr;
size_t addr_len;
const char *addr_str = mp_obj_str_get_data(e->value, &addr_len);
if (!ipaddr_aton(addr_str, &ip_addr)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments"));
}
if (IP_IS_V4(&ip_addr)) {
netif->gw = ip_addr;
} else {
mp_raise_ValueError(MP_ERROR_TEXT("invalid address type"));
}
break;
}
default: {
mp_raise_ValueError(MP_ERROR_TEXT("unexpected key"));
break;
}
}
}
}
}
return mp_const_none;
}
#endif // LWIP_VERSION_MAJOR >= 2
#endif // MICROPY_PY_NETWORK && MICROPY_PY_LWIP

Wyświetl plik

@ -153,7 +153,7 @@ static void network_ninaw10_poll_connect(mp_sched_node_t *node) {
debug_printf("poll_connect() status: %d reason %d\n", status, reason);
if (nina_connect(self->ssid, self->security, self->key, 0) != 0) {
mp_raise_msg_varg(&mp_type_OSError,
MP_ERROR_TEXT("could not connect to ssid=%s, sec=%d, key=%s\n"),
MP_ERROR_TEXT("could not connect to ssid=%s, sec=%d, key=%s"),
self->ssid, self->security, self->key);
}
} else {
@ -193,19 +193,18 @@ static mp_obj_t network_ninaw10_active(size_t n_args, const mp_obj_t *args) {
nina_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (n_args == 2) {
bool active = mp_obj_is_true(args[1]);
network_ninaw10_deinit();
if (active) {
if (active && !self->active) {
int error = 0;
if ((error = nina_init()) != 0) {
mp_raise_msg_varg(&mp_type_OSError,
MP_ERROR_TEXT("Failed to initialize Nina-W10 module, error: %d\n"), error);
MP_ERROR_TEXT("failed to initialize Nina-W10 module, error: %d"), error);
}
// check firmware version
uint8_t semver[NINA_FW_VER_LEN];
if (nina_fw_version(semver) != 0) {
nina_deinit();
mp_raise_msg_varg(&mp_type_OSError,
MP_ERROR_TEXT("Failed to read firmware version, error: %d\n"), error);
MP_ERROR_TEXT("failed to read firmware version, error: %d"), error);
}
// Check the minimum supported firmware version.
uint32_t fwmin = (NINA_FW_VER_MIN_MAJOR * 100) +
@ -218,12 +217,13 @@ static mp_obj_t network_ninaw10_active(size_t n_args, const mp_obj_t *args) {
if (fwver < fwmin) {
mp_raise_msg_varg(&mp_type_OSError,
MP_ERROR_TEXT("Firmware version mismatch. Minimum supported firmware is v%d.%d.%d found v%d.%d.%d\n"),
MP_ERROR_TEXT("firmware version mismatch, minimum supported firmware is v%d.%d.%d found v%d.%d.%d"),
NINA_FW_VER_MIN_MAJOR, NINA_FW_VER_MIN_MINOR, NINA_FW_VER_MIN_PATCH, semver[NINA_FW_VER_MAJOR_OFFS] - 48,
semver[NINA_FW_VER_MINOR_OFFS] - 48, semver[NINA_FW_VER_PATCH_OFFS] - 48);
}
soft_timer_static_init(&mp_wifi_poll_timer, SOFT_TIMER_MODE_ONE_SHOT, 0, network_ninaw10_timer_callback);
} else {
} else if (!active && self->active) {
network_ninaw10_deinit();
nina_deinit();
}
self->active = active;
@ -260,7 +260,7 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_ssid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_key, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = NINA_SEC_WPA_PSK} },
{ MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
};
@ -273,20 +273,31 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar
const char *ssid = mp_obj_str_get_str(args[ARG_ssid].u_obj);
if (strlen(ssid) == 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("SSID can't be empty!"));
mp_raise_ValueError(MP_ERROR_TEXT("SSID can't be empty"));
}
// get key and sec
// get encryption key
const char *key = NULL;
mp_uint_t security = NINA_SEC_OPEN;
if (args[ARG_key].u_obj != mp_const_none) {
key = mp_obj_str_get_str(args[ARG_key].u_obj);
security = args[ARG_security].u_int;
}
// get security mode
mp_uint_t security = args[ARG_security].u_int;
if (security == -1 && self->itf == MOD_NETWORK_STA_IF) {
security = NINA_SEC_WPA_PSK;
} else if (security == -1 && self->itf == MOD_NETWORK_AP_IF) {
security = NINA_SEC_WEP;
}
// Ensure that the key is not empty if a security mode is used.
if (security != NINA_SEC_OPEN && strlen(key) == 0) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Key can't be empty!"));
mp_raise_ValueError(MP_ERROR_TEXT("key can't be empty"));
}
// Activate the interface if not active.
if (!self->active) {
network_ninaw10_active(2, (mp_obj_t [2]) { pos_args[0], mp_const_true });
}
// Disconnect active connections first.
@ -298,7 +309,7 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar
// Initialize WiFi in Station mode.
if (nina_connect(ssid, security, key, 0) != 0) {
mp_raise_msg_varg(&mp_type_OSError,
MP_ERROR_TEXT("could not connect to ssid=%s, sec=%d, key=%s\n"), ssid, security, key);
MP_ERROR_TEXT("could not connect to ssid=%s, sec=%d, key=%s"), ssid, security, key);
}
// Save connection info to re-connect if needed.
@ -311,7 +322,7 @@ static mp_obj_t network_ninaw10_connect(mp_uint_t n_args, const mp_obj_t *pos_ar
mp_uint_t channel = args[ARG_channel].u_int;
if (security != NINA_SEC_OPEN && security != NINA_SEC_WEP) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("AP mode supports WEP security only."));
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("AP mode only supports WEP or OPEN security modes"));
}
// Initialize WiFi in AP mode.

Wyświetl plik

@ -915,6 +915,12 @@ static mp_obj_t wiznet5k_ifconfig(size_t n_args, const mp_obj_t *args) {
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k_ifconfig);
static mp_obj_t network_wiznet5k_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
wiznet5k_obj_t *self = MP_OBJ_TO_PTR(args[0]);
return mod_network_nic_ipconfig(&self->netif, n_args - 1, args + 1, kwargs);
}
static MP_DEFINE_CONST_FUN_OBJ_KW(wiznet5k_ipconfig_obj, 1, network_wiznet5k_ipconfig);
static mp_obj_t send_ethernet_wrapper(mp_obj_t self_in, mp_obj_t buf_in) {
wiznet5k_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t buf;
@ -1008,6 +1014,9 @@ static const mp_rom_map_elem_t wiznet5k_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&wiznet5k_isconnected_obj) },
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&wiznet5k_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wiznet5k_ifconfig_obj) },
#if WIZNET5K_WITH_LWIP_STACK
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&wiznet5k_ipconfig_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&wiznet5k_status_obj) },
{ MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&wiznet5k_config_obj) },
#if WIZNET5K_WITH_LWIP_STACK

Wyświetl plik

@ -728,6 +728,9 @@ void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) {
}
void mp_bluetooth_set_address_mode(uint8_t addr_mode) {
if (!mp_bluetooth_is_active()) {
mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE);
}
switch (addr_mode) {
case MP_BLUETOOTH_ADDRESS_MODE_PUBLIC:
if (!has_public_address()) {

Wyświetl plik

@ -45,6 +45,10 @@ void mp_os_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) {
if (exc != MP_OBJ_NULL) {
mp_obj_print_exception(&mp_plat_print, exc);
}
if (term == MP_OBJ_NULL) {
// Dupterm was already closed.
return;
}
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_stream_close(term);

@ -1 +1 @@
Subproject commit 35aaec4418ad78628a3b935885dd189d41ce779b
Subproject commit 85373b548f1fb0119a463582570b44189dfb09ae

1
lib/libmetal 160000

@ -0,0 +1 @@
Subproject commit 0cb7d293a7f25394a06847a28d0f0ace9862936e

1
lib/open-amp 160000

@ -0,0 +1 @@
Subproject commit 1904dee18da85400e72b8f55c5c99e872a486573

Wyświetl plik

@ -127,6 +127,7 @@
#define MICROPY_PY_VFS (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/cc3200/mods/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
#define MICROPY_PY_MACHINE_WDT (1)

Wyświetl plik

@ -114,6 +114,7 @@
#define MICROPY_PY_OS_URANDOM (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/esp32/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
#define MICROPY_PY_MACHINE_ADC (1)

Wyświetl plik

@ -38,7 +38,7 @@ MICROPY_ROM_TEXT_COMPRESSION ?= 1
MICROPY_PY_SSL = 1
MICROPY_SSL_AXTLS = 1
AXTLS_DEFS_EXTRA = -Dabort=abort_ -DRT_MAX_PLAIN_LENGTH=1024 -DRT_EXTRA=4096
BTREE_DEFS_EXTRA = -DDEFPSIZE=1024 -DMINCACHE=3
BTREE_DEFS_EXTRA = -DMICROPY_BERKELEY_DB_DEFPSIZE=1024 -DMICROPY_BERKELEY_DB_MINCACHE=3
FROZEN_MANIFEST ?= boards/manifest.py

Wyświetl plik

@ -66,6 +66,7 @@
#define MICROPY_PY_LWIP_SOCK_RAW (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/esp8266/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
#define MICROPY_PY_MACHINE_ADC (1)

Wyświetl plik

@ -115,7 +115,9 @@ int main(void) {
// Execute user scripts.
int ret = pyexec_file_if_exists("boot.py");
#if MICROPY_HW_ENABLE_USBDEV
mp_usbd_init();
#endif
if (ret & PYEXEC_FORCED_EXIT) {
goto soft_reset_exit;

Wyświetl plik

@ -79,6 +79,7 @@ uint32_t trng_random_u32(void);
#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (trng_random_u32())
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/mimxrt/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
@ -130,9 +131,9 @@ uint32_t trng_random_u32(void);
#define MICROPY_PY_WEBSOCKET (MICROPY_PY_LWIP)
#define MICROPY_PY_WEBREPL (MICROPY_PY_LWIP)
#define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP)
// #define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL)
#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL)
#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL)
// #define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL)
#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL)
#ifndef MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1)
@ -146,6 +147,8 @@ uint32_t trng_random_u32(void);
#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-mimxrt"
#endif
#define MICROPY_HW_ENABLE_USBDEV (1)
// Hooks to add builtins
#if defined(IOMUX_TABLE_ENET)

Wyświetl plik

@ -37,6 +37,7 @@
#define MICROPY_HW_ENABLE_RNG (1)
#define MICROPY_HW_ENABLE_USBDEV (1)
#define MICROPY_HW_USB_CDC (1)
#define MICROPY_HW_HAS_LED (1)

Wyświetl plik

@ -37,6 +37,7 @@
#define MICROPY_HW_ENABLE_RNG (1)
#define MICROPY_HW_ENABLE_USBDEV (1)
#define MICROPY_HW_USB_CDC (1)
#define MICROPY_HW_HAS_LED (1)

Wyświetl plik

@ -37,6 +37,7 @@
#define MICROPY_HW_ENABLE_RNG (1)
#define MICROPY_HW_ENABLE_USBDEV (1)
#define MICROPY_HW_USB_CDC (1)
#define MICROPY_HW_HAS_LED (1)

Wyświetl plik

@ -174,6 +174,7 @@
#define MICROPY_PY_TIME (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/nrf/modules/machine/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
#define MICROPY_PY_MACHINE_PULSE (0)

Wyświetl plik

@ -131,6 +131,7 @@
#ifndef MICROPY_PY_MACHINE
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/renesas-ra/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
#define MICROPY_PY_MACHINE_ADC (1)

Wyświetl plik

@ -108,6 +108,7 @@ set(MICROPY_SOURCE_LIB
${MICROPY_DIR}/shared/tinyusb/mp_cdc_common.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd_descriptor.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd_runtime.c
)
set(MICROPY_SOURCE_DRIVERS
@ -146,6 +147,7 @@ set(MICROPY_SOURCE_QSTR
${MICROPY_DIR}/shared/readline/readline.c
${MICROPY_DIR}/shared/runtime/mpirq.c
${MICROPY_DIR}/shared/runtime/sys_stdio_mphal.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd_runtime.c
${MICROPY_PORT_DIR}/machine_adc.c
${MICROPY_PORT_DIR}/machine_i2c.c
${MICROPY_PORT_DIR}/machine_pin.c

Wyświetl plik

@ -221,6 +221,10 @@ int main(int argc, char **argv) {
mp_thread_deinit();
#endif
soft_timer_deinit();
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
mp_usbd_deinit();
#endif
gc_sweep_all();
mp_deinit();
}

Wyświetl plik

@ -52,6 +52,10 @@
#ifndef MICROPY_HW_USB_MSC
#define MICROPY_HW_USB_MSC (0)
#endif
#ifndef MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
#define MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE (1) // Support machine.USBDevice
#endif
#endif
#ifndef MICROPY_CONFIG_ROM_LEVEL
@ -115,6 +119,7 @@
#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (rosc_random_u32())
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/rp2/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)

Wyświetl plik

@ -136,6 +136,7 @@ SHARED_SRC_C += \
shared/tinyusb/mp_cdc_common.c \
shared/tinyusb/mp_usbd.c \
shared/tinyusb/mp_usbd_descriptor.c \
shared/tinyusb/mp_usbd_runtime.c \
ASF4_SRC_C += $(addprefix lib/asf4/$(MCU_SERIES_LOWER)/,\
hal/src/hal_atomic.c \

Wyświetl plik

@ -93,6 +93,9 @@ void samd_main(void) {
pwm_deinit_all();
#endif
soft_timer_deinit();
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
mp_usbd_deinit();
#endif
gc_sweep_all();
#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SPI || MICROPY_PY_MACHINE_UART
sercom_deinit_all();

Wyświetl plik

@ -63,8 +63,13 @@
#ifndef MICROPY_HW_USB_DESC_STR_MAX
#define MICROPY_HW_USB_DESC_STR_MAX (32)
#endif
// Support machine.USBDevice
#ifndef MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
#define MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE (1)
#endif
#endif // MICROPY_HW_ENABLE_USBDEV
#define MICROPY_PY_SYS_PLATFORM "samd"
// Extended modules
@ -73,6 +78,7 @@
#define MICROPY_PY_TIME_INCLUDEFILE "ports/samd/modtime.c"
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/samd/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)

Wyświetl plik

@ -422,6 +422,17 @@ endif
endif # MICROPY_PY_BLUETOOTH
# Add stm32-specific implementation of libmetal (and optionally OpenAMP's rproc).
# Note: libmetal code is generated via a pre-processor so ensure that runs first.
ifeq ($(MICROPY_PY_OPENAMP),1)
SRC_C += mpmetalport.c
$(BUILD)/mpmetalport.o: $(BUILD)/openamp/metal/config.h
ifeq ($(MICROPY_PY_OPENAMP_REMOTEPROC),1)
SRC_C += mpremoteprocport.c
$(BUILD)/mpremoteprocport.o: $(BUILD)/openamp/metal/config.h
endif
endif
# SRC_O should be placed first to work around this LTO bug with binutils <2.35:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83967
OBJ += $(addprefix $(BUILD)/, $(SRC_O))

Wyświetl plik

@ -23,6 +23,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_NETWORK_CYW43 = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
MICROPY_PY_OPENAMP = 1
MICROPY_PY_OPENAMP_REMOTEPROC = 1
FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py
MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"'

Wyświetl plik

@ -14,10 +14,10 @@ MEMORY
SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */
FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1 -> Flash storage */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* Sector 0 -> Arduino Bootloader
Sector 1 -> Reserved for CM4/FS
Sectors 2 -> 15 firmware */
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */
FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */
}
/* produce a link error if there is not this amount of RAM for these sections */
@ -44,4 +44,8 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM);
_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS);
_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS);
/* OpenAMP shared memory region */
_openamp_shm_region_start = ORIGIN(SRAM4);
_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
INCLUDE common_blifs.ld

Wyświetl plik

@ -23,6 +23,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_NETWORK_CYW43 = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
MICROPY_PY_OPENAMP = 1
MICROPY_PY_OPENAMP_REMOTEPROC = 1
FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py
MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"'

Wyświetl plik

@ -14,10 +14,10 @@ MEMORY
SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */
FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1 -> Flash storage */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* Sector 0 -> Arduino Bootloader
Sector 1 -> Reserved for CM4/FS
Sectors 2 -> 15 firmware */
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */
FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */
}
_cm4_ram_start = ORIGIN(SRAM4);
@ -46,4 +46,8 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM);
_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS);
_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS);
/* OpenAMP shared memory region */
_openamp_shm_region_start = ORIGIN(SRAM4);
_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
INCLUDE common_blifs.ld

Wyświetl plik

@ -23,6 +23,8 @@ MICROPY_PY_LWIP = 1
MICROPY_PY_NETWORK_CYW43 = 1
MICROPY_PY_SSL = 1
MICROPY_SSL_MBEDTLS = 1
MICROPY_PY_OPENAMP = 1
MICROPY_PY_OPENAMP_REMOTEPROC = 1
FROZEN_MANIFEST = $(BOARD_DIR)/manifest.py
MBEDTLS_CONFIG_FILE = '"$(BOARD_DIR)/mbedtls_config_board.h"'

Wyświetl plik

@ -14,10 +14,10 @@ MEMORY
SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */
FLASH_EXT (rx) : ORIGIN = 0x90000000, LENGTH = 16384K /* 16MBs external QSPI flash */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* sector 1 -> Flash storage */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1792K /* Sector 0 -> Arduino Bootloader
Sector 1 -> Reserved for CM4/FS
Sectors 2 -> 15 firmware */
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */
FLASH_TEXT (rx) : ORIGIN = 0x08040000, LENGTH = 1280K /* CM7 firmware */
FLASH_CM4 (rx) : ORIGIN = 0x08180000, LENGTH = 512K /* CM4 firmware */
}
/* produce a link error if there is not this amount of RAM for these sections */
@ -44,4 +44,8 @@ _micropy_hw_internal_flash_storage_ram_cache_end = ORIGIN(DTCM) + LENGTH(DTCM);
_micropy_hw_internal_flash_storage_start = ORIGIN(FLASH_FS);
_micropy_hw_internal_flash_storage_end = ORIGIN(FLASH_FS) + LENGTH(FLASH_FS);
/* OpenAMP shared memory region */
_openamp_shm_region_start = ORIGIN(SRAM4);
_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
INCLUDE common_blifs.ld

Wyświetl plik

@ -1,51 +1,61 @@
# Application firmware update function for LEGO_HUB_NO6.
# MIT license; Copyright (c) 2022 Damien P. George
# MIT license; Copyright (c) 2022-2024 Damien P. George
from micropython import const
import struct, machine, fwupdate, spiflash, pyb
import random, struct, machine, fwupdate, spiflash, pyb
_IOCTL_BLOCK_COUNT = const(4)
_IOCTL_BLOCK_SIZE = const(5)
_SPIFLASH_UPDATE_KEY_ADDR = const(1020 * 1024)
_SPIFLASH_UPDATE_KEY_VALUE = const(0x12345678)
# Mboot addresses the external SPI flash at this location.
_MBOOT_SPIFLASH_BASE_ADDR = 0x80000000
_FILESYSTEM_ADDR = const(0x8000_0000 + 1024 * 1024)
# The raw filesystem is in the first 1MiB of external SPI flash,
# but skip the first and last flash sectors.
_RAW_FILESYSTEM_ADDR = const(4 * 1024)
_RAW_FILESYSTEM_LEN = const(1016 * 1024)
# Roundabout way to get actual filesystem size from config.
# This takes into account the 1M "reserved" section of the flash memory.
flash = pyb.Flash(start=0)
_FILESYSTEM_LEN = flash.ioctl(_IOCTL_BLOCK_COUNT, None) * flash.ioctl(_IOCTL_BLOCK_SIZE, None)
def _copy_file_to_raw_filesystem(filename, flash, block):
block_count = flash.ioctl(_IOCTL_BLOCK_COUNT, 0)
block_size = flash.ioctl(_IOCTL_BLOCK_SIZE, 0)
buf = bytearray(block_size)
with open(filename, "rb") as file:
while True:
n = file.readinto(buf)
if not n:
break
flash.writeblocks(block, buf)
block += 1
if block >= block_count:
print("|", end="")
block = 0
print(".", end="")
print()
def update_app(filename):
print(f"Updating application firmware from {filename}")
# Create the elements for the mboot filesystem-load operation.
elems = fwupdate.update_app_elements(filename, _FILESYSTEM_ADDR, _FILESYSTEM_LEN)
if not elems:
return
# Create the update key.
key = struct.pack("<I", _SPIFLASH_UPDATE_KEY_VALUE)
# Create a SPI flash object.
spi = machine.SoftSPI(
sck=machine.Pin.board.FLASH_SCK,
mosi=machine.Pin.board.FLASH_MOSI,
miso=machine.Pin.board.FLASH_MISO,
baudrate=50_000_000,
)
cs = machine.Pin(machine.Pin.board.FLASH_NSS, machine.Pin.OUT, value=1)
# We can't use pyb.Flash() because we need to write to the "reserved" 1M area.
flash = spiflash.SPIFlash(spi, cs)
flash = spiflash.SPIFlash(start=_RAW_FILESYSTEM_ADDR, len=_RAW_FILESYSTEM_LEN)
# Write the update key and elements to the SPI flash.
flash.erase_block(_SPIFLASH_UPDATE_KEY_ADDR)
flash.write(_SPIFLASH_UPDATE_KEY_ADDR, key + elems)
# Partition the raw filesystem into two segments for wear levelling.
block_count = flash.ioctl(_IOCTL_BLOCK_COUNT, 0)
block_size = flash.ioctl(_IOCTL_BLOCK_SIZE, 0)
block_start = random.randrange(0, block_count)
print(f"Raw filesystem block layout: 0 .. {block_start} .. {block_count}")
# Copy the file to the special raw filesystem.
_copy_file_to_raw_filesystem(filename, flash, block_start)
# Enter mboot with a request to do a filesystem-load update.
# If there is a power failure during the update (eg battery removed) then
# mboot will read the SPI flash update key and elements and retry.
machine.bootloader(elems)
# Note: the filename doesn't mean anything here, but still needs to be non-empty.
fwupdate.update_mpy(
filename,
fs_type=fwupdate.VFS_RAW,
fs_base=_MBOOT_SPIFLASH_BASE_ADDR + _RAW_FILESYSTEM_ADDR + block_start * block_size,
fs_len=(block_count - block_start) * block_size,
fs_base2=_MBOOT_SPIFLASH_BASE_ADDR + _RAW_FILESYSTEM_ADDR,
fs_len2=block_start * block_size,
)

Wyświetl plik

@ -194,7 +194,14 @@ int board_mboot_get_reset_mode(uint32_t *initial_r0) {
}
void board_mboot_state_change(int state, uint32_t arg) {
if (state == MBOOT_STATE_FSLOAD_END) {
if (state == MBOOT_STATE_FSLOAD_START) {
// The FS-load update is about to start. Program the update key and FS-load elements
// into the flash so they can be retrieved if there is a power failure during the update.
mp_spiflash_erase_block(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR);
uint32_t key = SPIFLASH_UPDATE_KEY_VALUE;
mp_spiflash_write(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR, 4, (const uint8_t *)&key);
mp_spiflash_write(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR + 4, ELEM_DATA_SIZE, ELEM_DATA_START);
} else if (state == MBOOT_STATE_FSLOAD_END) {
// The FS-load update completed (either with success or failure), so erase the
// update key and write the result of the FS-load operation into flash.
mp_spiflash_erase_block(MBOOT_SPIFLASH_SPIFLASH, SPIFLASH_UPDATE_KEY_ADDR);

Wyświetl plik

@ -4,5 +4,4 @@ include("$(PORT_DIR)/boards/manifest.py")
# Modules for application firmware update.
module("fwupdate.py", base_path="$(PORT_DIR)/mboot", opt=3)
module("spiflash.py", opt=3)
module("appupdate.py", opt=3)

Wyświetl plik

@ -0,0 +1,166 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if !BUILDING_MBOOT
#include "py/runtime.h"
#include "extmod/vfs.h"
#include "storage.h"
// Expose the entire external SPI flash as an object with the block protocol.
#define FLASH_SIZE (MICROPY_HW_SPIFLASH_OFFSET_BYTES + MICROPY_HW_SPIFLASH_SIZE_BITS / 8)
#define BLOCK_SIZE (4096)
typedef struct _spi_flash_obj_t {
mp_obj_base_t base;
uint32_t start; // in bytes
uint32_t len; // in bytes
} spi_flash_obj_t;
static const mp_obj_type_t spi_flash_type;
static void spi_flash_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
spi_flash_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "SPIFlash(start=%u, len=%u)", self->start, self->len);
}
static mp_obj_t spi_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_start, ARG_len };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
spi_flash_obj_t *self = mp_obj_malloc(spi_flash_obj_t, &spi_flash_type);
mp_int_t start = args[ARG_start].u_int;
if (!(0 <= start && start < FLASH_SIZE && start % BLOCK_SIZE == 0)) {
mp_raise_ValueError(NULL);
}
mp_int_t len = args[ARG_len].u_int;
if (len == -1) {
len = FLASH_SIZE - start;
} else if (!(0 < len && start + len <= FLASH_SIZE && len % BLOCK_SIZE == 0)) {
mp_raise_ValueError(NULL);
}
self->start = start;
self->len = len;
return MP_OBJ_FROM_PTR(self);
}
static mp_obj_t spi_flash_readblocks(size_t n_args, const mp_obj_t *args) {
spi_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
uint32_t block_num = self->start / BLOCK_SIZE + mp_obj_get_int(args[1]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE);
int ret = spi_bdev_readblocks_raw(&spi_bdev, bufinfo.buf, block_num, 0, bufinfo.len);
return MP_OBJ_NEW_SMALL_INT(ret);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(spi_flash_readblocks_obj, 3, 3, spi_flash_readblocks);
static mp_obj_t spi_flash_writeblocks(size_t n_args, const mp_obj_t *args) {
spi_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]);
uint32_t block_num = self->start / BLOCK_SIZE + mp_obj_get_int(args[1]);
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ);
int ret = spi_bdev_eraseblocks_raw(&spi_bdev, block_num, bufinfo.len);
if (ret == 0) {
ret = spi_bdev_writeblocks_raw(&spi_bdev, bufinfo.buf, block_num, 0, bufinfo.len);
}
return MP_OBJ_NEW_SMALL_INT(ret);
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(spi_flash_writeblocks_obj, 3, 3, spi_flash_writeblocks);
static mp_obj_t spi_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) {
spi_flash_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_int_t cmd = mp_obj_get_int(cmd_in);
switch (cmd) {
case MP_BLOCKDEV_IOCTL_INIT:
storage_init();
return MP_OBJ_NEW_SMALL_INT(0);
case MP_BLOCKDEV_IOCTL_DEINIT:
return MP_OBJ_NEW_SMALL_INT(0);
case MP_BLOCKDEV_IOCTL_SYNC:
return MP_OBJ_NEW_SMALL_INT(0);
case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: {
mp_int_t n = self->len / BLOCK_SIZE;
return MP_OBJ_NEW_SMALL_INT(n);
}
case MP_BLOCKDEV_IOCTL_BLOCK_SIZE:
return MP_OBJ_NEW_SMALL_INT(BLOCK_SIZE);
default:
return mp_const_none;
}
}
static MP_DEFINE_CONST_FUN_OBJ_3(spi_flash_ioctl_obj, spi_flash_ioctl);
static const mp_rom_map_elem_t spi_flash_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&spi_flash_readblocks_obj) },
{ MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&spi_flash_writeblocks_obj) },
{ MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&spi_flash_ioctl_obj) },
};
static MP_DEFINE_CONST_DICT(spi_flash_locals_dict, spi_flash_locals_dict_table);
static MP_DEFINE_CONST_OBJ_TYPE(
spi_flash_type,
MP_QSTR_SPIFlash,
MP_TYPE_FLAG_NONE,
make_new, spi_flash_make_new,
print, spi_flash_print,
locals_dict, &spi_flash_locals_dict
);
/******************************************************************************/
// The `spiflash` module.
static const mp_rom_map_elem_t spiflash_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_spiflash) },
{ MP_ROM_QSTR(MP_QSTR_SPIFlash), MP_ROM_PTR(&spi_flash_type) },
};
static MP_DEFINE_CONST_DICT(spiflash_module_globals, spiflash_module_globals_table);
const mp_obj_module_t spiflash_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&spiflash_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_spiflash, spiflash_module);
#endif

Wyświetl plik

@ -1,90 +0,0 @@
# MicroPython driver for SPI flash
# MIT license; Copyright (c) 2022 Damien P. George
from micropython import const
_PAGE_SIZE = const(256) # maximum bytes writable in one SPI transfer
_CMD_WRITE = const(0x02)
_CMD_READ = const(0x03)
_CMD_RDSR = const(0x05)
_CMD_WREN = const(0x06)
_CMD_WRITE_32 = const(0x12)
_CMD_READ_32 = const(0x13)
_CMD_SEC_ERASE = const(0x20)
_CMD_SEC_ERASE_32 = const(0x21)
_CMD_JEDEC_ID = const(0x9F)
class SPIFlash:
def __init__(self, spi, cs):
self.spi = spi
self.cs = cs
self.id = self._get_id()
# flash chip on Hub No. 6 uses 32-bit addressing
_32_bit = self.id == b"\xef\x40\x19"
self._READ = _CMD_READ_32 if _32_bit else _CMD_READ
self._WRITE = _CMD_WRITE_32 if _32_bit else _CMD_WRITE
self._ERASE = _CMD_SEC_ERASE_32 if _32_bit else _CMD_SEC_ERASE
def _get_id(self):
self.cs(0)
self.spi.write(bytearray([_CMD_JEDEC_ID]))
buf = self.spi.read(3)
self.cs(1)
return buf
def _wait_wel1(self):
# wait WEL=1
self.cs(0)
self.spi.write(bytearray([_CMD_RDSR]))
buf = bytearray(1)
while True:
self.spi.readinto(buf)
if buf[0] & 2:
break
self.cs(1)
def _wait_wip0(self):
# wait WIP=0
self.cs(0)
self.spi.write(bytearray([_CMD_RDSR]))
buf = bytearray(1)
while True:
self.spi.readinto(buf)
if not (buf[0] & 1):
break
self.cs(1)
def _flash_modify(self, cmd, addr, buf):
self.cs(0)
self.spi.write(bytearray([_CMD_WREN]))
self.cs(1)
self._wait_wel1()
self.cs(0)
self.spi.write(bytearray([cmd, addr >> 24, addr >> 16, addr >> 8, addr]))
if buf:
self.spi.write(buf)
self.cs(1)
self._wait_wip0()
def erase_block(self, addr):
self._flash_modify(self._ERASE, addr, None)
def readinto(self, addr, buf):
self.cs(0)
self.spi.write(bytearray([self._READ, addr >> 16, addr >> 8, addr]))
self.spi.readinto(buf)
self.cs(1)
def write(self, addr, buf):
offset = addr & (_PAGE_SIZE - 1)
remain = len(buf)
buf = memoryview(buf)
buf_offset = 0
while remain:
l = min(_PAGE_SIZE - offset, remain)
self._flash_modify(self._WRITE, addr, buf[buf_offset : buf_offset + l])
remain -= l
addr += l
buf_offset += l
offset = 0

Wyświetl plik

@ -174,6 +174,8 @@ static inline void restore_irq_pri(uint32_t state) {
#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
#define IRQ_PRI_HSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 10, 0)
// Interrupt priority for non-special timers.
#define IRQ_PRI_TIMX NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 13, 0)

Wyświetl plik

@ -120,6 +120,7 @@ SRC_C += \
ui.c \
vfs_fat.c \
vfs_lfs.c \
vfs_raw.c \
drivers/bus/softspi.c \
drivers/bus/softqspi.c \
drivers/memory/spiflash.c \

Wyświetl plik

@ -71,13 +71,23 @@ How to use
#define MBOOT_FSLOAD (1)
and then enable one or more of the following depending on what filesystem
support is required in Mboot (note that the FAT driver is read-only and
quite compact, but littlefs supports both read and write so is rather
large):
support is required in Mboot:
#define MBOOT_VFS_FAT (1)
#define MBOOT_VFS_LFS1 (1)
#define MBOOT_VFS_LFS2 (1)
#define MBOOT_VFS_RAW (1)
Note that the FAT and LFS2 drivers are read-only and quite compact, but
LFS1 supports both read and write so is rather large.
The raw filesystem type is enabled by default and is a flat section of
storage containing a single file without any metadata. The raw filesystem
can either be one regoin, or split over two separate, contiguous regions.
The latter is useful for wear levelling: given a chunk of flash, write the
firmware starting at a random block within that chunk and wrap around to
the beginning of the chunk when the end is reached. Then use a split
raw filesystem to inform mboot of this wrapping.
2. Build the board's main application firmware as usual.

Wyświetl plik

@ -39,7 +39,7 @@
#if MBOOT_FSLOAD
#if !(MBOOT_VFS_FAT || MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2)
#if !(MBOOT_VFS_FAT || MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2 || MBOOT_VFS_RAW)
#error Must enable at least one VFS component
#endif
@ -234,28 +234,51 @@ int fsload_process(void) {
// End of elements.
return -MBOOT_ERRNO_FSLOAD_NO_MOUNT;
}
// Extract element arguments based on the element length:
// - 10 bytes: mount_point fs_type uint32_t uint32_t
// - 14 bytes: mount_point fs_type uint32_t uint32_t uint32_t
// - 18 bytes: mount_point fs_type uint32_t uint32_t uint32_t uint32_t
// - 22 bytes: mount_point fs_type uint64_t uint64_t uint32_t
// - 34 bytes: mount_point fs_type uint64_t uint64_t uint64_t uint64_t
mboot_addr_t base_addr;
mboot_addr_t byte_len;
uint32_t block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE;
if (elem[-1] == 10 || elem[-1] == 14) {
mboot_addr_t arg2 = 0;
mboot_addr_t arg3 = 0;
(void)arg2;
(void)arg3;
uint8_t elem_len = elem[-1];
if (elem_len == 10 || elem_len == 14 || elem_len == 18) {
// 32-bit base and length given, extract them.
base_addr = get_le32(&elem[2]);
byte_len = get_le32(&elem[6]);
if (elem[-1] == 14) {
// Block size given, extract it.
block_size = get_le32(&elem[10]);
if (elem_len >= 14) {
// Argument 2 given, extract it.
arg2 = get_le32(&elem[10]);
if (elem_len == 18) {
// Argument 3 given, extract it.
arg3 = get_le32(&elem[14]);
}
}
#if MBOOT_ADDRESS_SPACE_64BIT
} else if (elem[-1] == 22) {
// 64-bit base and length given, and block size, so extract them.
} else if (elem_len == 22 || elem_len == 34) {
// 64-bit base and length given, so extract them.
base_addr = get_le64(&elem[2]);
byte_len = get_le64(&elem[10]);
block_size = get_le32(&elem[18]);
if (elem_len == 22) {
// 32-bit argument 2 given, extract it.
arg2 = get_le32(&elem[18]);
} else {
// 64-bit argument 2 and 3 given, extract them.
arg2 = get_le64(&elem[18]);
arg3 = get_le64(&elem[26]);
}
#endif
} else {
// Invalid MOUNT element.
return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT;
}
if (elem[0] == mount_point) {
int ret;
union {
@ -268,27 +291,43 @@ int fsload_process(void) {
#if MBOOT_VFS_LFS2
vfs_lfs2_context_t lfs2;
#endif
#if MBOOT_VFS_RAW
vfs_raw_context_t raw;
#endif
} ctx;
const stream_methods_t *methods;
#if MBOOT_VFS_FAT
if (elem[1] == ELEM_MOUNT_FAT) {
(void)block_size;
ret = vfs_fat_mount(&ctx.fat, base_addr, byte_len);
methods = &vfs_fat_stream_methods;
} else
#endif
#if MBOOT_VFS_LFS1
if (elem[1] == ELEM_MOUNT_LFS1) {
uint32_t block_size = arg2;
if (block_size == 0) {
block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE;
}
ret = vfs_lfs1_mount(&ctx.lfs1, base_addr, byte_len, block_size);
methods = &vfs_lfs1_stream_methods;
} else
#endif
#if MBOOT_VFS_LFS2
if (elem[1] == ELEM_MOUNT_LFS2) {
uint32_t block_size = arg2;
if (block_size == 0) {
block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE;
}
ret = vfs_lfs2_mount(&ctx.lfs2, base_addr, byte_len, block_size);
methods = &vfs_lfs2_stream_methods;
} else
#endif
#if MBOOT_VFS_RAW
if (elem[1] == ELEM_MOUNT_RAW) {
ret = vfs_raw_mount(&ctx.raw, base_addr, byte_len, arg2, arg3);
methods = &vfs_raw_stream_methods;
} else
#endif
{
// Unknown filesystem type
return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT;

Wyświetl plik

@ -9,6 +9,7 @@ import deflate, machine, stm
VFS_FAT = 1
VFS_LFS1 = 2
VFS_LFS2 = 3
VFS_RAW = 4
# Constants for creating mboot elements.
_ELEM_TYPE_END = const(1)
@ -226,28 +227,45 @@ def _create_element(kind, body):
def update_app_elements(
filename, fs_base, fs_len, fs_type=VFS_FAT, fs_blocksize=0, status_addr=None, addr_64bit=False
filename,
fs_base,
fs_len,
fs_type=VFS_FAT,
fs_blocksize=0,
status_addr=None,
addr_64bit=False,
*,
fs_base2=0,
fs_len2=0,
):
# Check firmware is of .dfu or .dfu.gz type
try:
with open(filename, "rb") as f:
hdr = deflate.DeflateIO(f, deflate.GZIP).read(6)
except Exception:
with open(filename, "rb") as f:
hdr = f.read(6)
if hdr != b"DfuSe\x01":
print("Firmware must be a .dfu(.gz) file.")
return ()
if fs_type != VFS_RAW:
# Check firmware is of .dfu or .dfu.gz type
try:
with open(filename, "rb") as f:
hdr = deflate.DeflateIO(f, deflate.GZIP).read(6)
except Exception:
with open(filename, "rb") as f:
hdr = f.read(6)
if hdr != b"DfuSe\x01":
print("Firmware must be a .dfu(.gz) file.")
return ()
if fs_type in (VFS_LFS1, VFS_LFS2) and not fs_blocksize:
raise Exception("littlefs requires fs_blocksize parameter")
mount_point = 1
mount_encoding = "<BBQQL" if addr_64bit else "<BBLLL"
elems = _create_element(
_ELEM_TYPE_MOUNT,
struct.pack(mount_encoding, mount_point, fs_type, fs_base, fs_len, fs_blocksize),
)
if fs_type == VFS_RAW:
mount_encoding = "<BBQQQQ" if addr_64bit else "<BBLLLL"
elems = _create_element(
_ELEM_TYPE_MOUNT,
struct.pack(mount_encoding, mount_point, fs_type, fs_base, fs_len, fs_base2, fs_len2),
)
else:
mount_encoding = "<BBQQL" if addr_64bit else "<BBLLL"
elems = _create_element(
_ELEM_TYPE_MOUNT,
struct.pack(mount_encoding, mount_point, fs_type, fs_base, fs_len, fs_blocksize),
)
elems += _create_element(
_ELEM_TYPE_FSLOAD, struct.pack("<B", mount_point) + bytes(filename, "ascii")
)

Wyświetl plik

@ -92,6 +92,31 @@
#define MBOOT_LED_STATE_LED2 (0x04)
#define MBOOT_LED_STATE_LED3 (0x08)
// Whether to support loading firmware from a filesystem.
#ifndef MBOOT_FSLOAD
#define MBOOT_FSLOAD (0)
#endif
// Whether to support FAT filesystems.
#ifndef MBOOT_VFS_FAT
#define MBOOT_VFS_FAT (0)
#endif
// Whether to support Littlefs v1 filesystems.
#ifndef MBOOT_VFS_LFS1
#define MBOOT_VFS_LFS1 (0)
#endif
// Whether to support Littlefs v2 filesystems.
#ifndef MBOOT_VFS_LFS2
#define MBOOT_VFS_LFS2 (0)
#endif
// Whether to support raw filesystems.
#ifndef MBOOT_VFS_RAW
#define MBOOT_VFS_RAW (MBOOT_FSLOAD)
#endif
// These enum values are passed as the first argument to mboot_state_change() to
// notify of a change in state of the bootloader activity. This function has a
// default implementation in ui.c but can be overridden by a board. Some states
@ -158,6 +183,7 @@ enum {
ELEM_MOUNT_FAT = 1,
ELEM_MOUNT_LFS1,
ELEM_MOUNT_LFS2,
ELEM_MOUNT_RAW,
};
// Configure the type used to hold an address in the mboot address space.

Wyświetl plik

@ -93,4 +93,22 @@ int vfs_lfs2_mount(vfs_lfs2_context_t *ctx, mboot_addr_t base_addr, mboot_addr_t
#endif
#if MBOOT_VFS_RAW
// A raw VFS contains a contiguous, single file without any metadata.
typedef struct _vfs_raw_context_t {
mboot_addr_t seg0_base_addr;
mboot_addr_t seg0_byte_len;
mboot_addr_t seg1_base_addr;
mboot_addr_t seg1_byte_len;
mboot_addr_t file_pos;
} vfs_raw_context_t;
extern const stream_methods_t vfs_raw_stream_methods;
int vfs_raw_mount(vfs_raw_context_t *ctx, mboot_addr_t seg0_base_addr, mboot_addr_t seg0_byte_len, mboot_addr_t seg1_base_addr, mboot_addr_t seg1_byte_len);
#endif
#endif // MICROPY_INCLUDED_STM32_MBOOT_VFS_H

Wyświetl plik

@ -0,0 +1,91 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2024 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "mboot.h"
#include "vfs.h"
#if MBOOT_FSLOAD && MBOOT_VFS_RAW
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif
int vfs_raw_mount(vfs_raw_context_t *ctx, mboot_addr_t seg0_base_addr, mboot_addr_t seg0_byte_len, mboot_addr_t seg1_base_addr, mboot_addr_t seg1_byte_len) {
ctx->seg0_base_addr = seg0_base_addr;
ctx->seg0_byte_len = seg0_byte_len;
ctx->seg1_base_addr = seg1_base_addr;
ctx->seg1_byte_len = seg1_byte_len;
return 0;
}
static int vfs_raw_stream_open(void *stream_in, const char *fname) {
vfs_raw_context_t *stream = stream_in;
(void)fname;
stream->file_pos = 0;
return 0;
}
static void vfs_raw_stream_close(void *stream_in) {
(void)stream_in;
}
static int vfs_raw_stream_read(void *stream_in, uint8_t *buf, size_t len) {
vfs_raw_context_t *stream = stream_in;
size_t orig_len = len;
while (len) {
mboot_addr_t addr;
mboot_addr_t remain;
if (stream->file_pos < stream->seg0_byte_len) {
// Reading from segment 0.
mboot_addr_t seg0_pos = stream->file_pos;
addr = stream->seg0_base_addr + seg0_pos;
remain = stream->seg0_byte_len - seg0_pos;
} else {
// Reading from segment 1.
mboot_addr_t seg1_pos = stream->file_pos - stream->seg0_byte_len;
addr = stream->seg1_base_addr + seg1_pos;
remain = stream->seg1_byte_len - seg1_pos;
if (!remain) {
// At the end of segment 1.
break;
}
}
size_t l = MIN(len, remain);
hw_read(addr, l, buf);
stream->file_pos += l;
buf += l;
len -= l;
}
return orig_len - len;
}
const stream_methods_t vfs_raw_stream_methods = {
vfs_raw_stream_open,
vfs_raw_stream_close,
vfs_raw_stream_read,
};
#endif // MBOOT_FSLOAD && MBOOT_VFS_RAW

Wyświetl plik

@ -111,6 +111,7 @@
#ifndef MICROPY_PY_MACHINE
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/stm32/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
#define MICROPY_PY_MACHINE_ADC (1)

Wyświetl plik

@ -0,0 +1,107 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* libmetal stm32 port.
*/
#include "py/mperrno.h"
#include "py/mphal.h"
#include "mpu.h"
#include "metal/sys.h"
#include "metal/utilities.h"
#include "metal/device.h"
struct metal_state _metal;
static mp_sched_node_t rproc_notify_node;
int metal_sys_init(const struct metal_init_params *params) {
metal_unused(params);
// Clear HSEM pending IRQ.
HSEM_COMMON->ICR |= (uint32_t)__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID);
HAL_NVIC_ClearPendingIRQ(HSEM1_IRQn);
// Enable and configure HSEM.
__HAL_RCC_HSEM_CLK_ENABLE();
NVIC_SetPriority(HSEM1_IRQn, IRQ_PRI_HSEM);
HAL_NVIC_EnableIRQ(HSEM1_IRQn);
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID));
#ifndef VIRTIO_USE_DCACHE
// If cache management is not enabled, configure the MPU to disable caching
// for the entire shared memory region.
uint32_t irq_state = mpu_config_start();
mpu_config_region(MPU_REGION_OPENAMP, METAL_MPU_REGION_BASE, MPU_CONFIG_SHARED_UNCACHED(METAL_MPU_REGION_SIZE));
mpu_config_end(irq_state);
#endif
metal_bus_register(&metal_generic_bus);
return 0;
}
void metal_sys_finish(void) {
HAL_NVIC_DisableIRQ(HSEM1_IRQn);
HAL_HSEM_DeactivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID));
__HAL_RCC_HSEM_CLK_DISABLE();
metal_bus_unregister(&metal_generic_bus);
}
unsigned int sys_irq_save_disable(void) {
return disable_irq();
}
void sys_irq_restore_enable(unsigned int state) {
enable_irq(state);
}
void *metal_machine_io_mem_map(void *va, metal_phys_addr_t pa,
size_t size, unsigned int flags) {
metal_unused(pa);
metal_unused(size);
metal_unused(flags);
return va;
}
void metal_machine_cache_flush(void *addr, unsigned int len) {
SCB_CleanDCache_by_Addr(addr, len);
}
void metal_machine_cache_invalidate(void *addr, unsigned int len) {
SCB_InvalidateDCache_by_Addr(addr, len);
}
int metal_rproc_notify(void *priv, uint32_t id) {
HAL_HSEM_FastTake(METAL_HSEM_REMOTE_ID);
HAL_HSEM_Release(METAL_HSEM_REMOTE_ID, 0);
return 0;
}
void HSEM1_IRQHandler(void) {
HAL_HSEM_IRQHandler();
mp_sched_schedule_node(&rproc_notify_node, openamp_remoteproc_notified);
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(METAL_HSEM_MASTER_ID));
}

Wyświetl plik

@ -0,0 +1,76 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* libmetal stm32 port.
*/
#ifndef MICROPY_INCLUDED_STM32_MPMETALPORT_H
#define MICROPY_INCLUDED_STM32_MPMETALPORT_H
#include <stdlib.h>
#include "py/mphal.h"
#include "py/runtime.h"
#define METAL_HAVE_STDATOMIC_H 0
#define METAL_HAVE_FUTEX_H 0
#define METAL_MAX_DEVICE_REGIONS 2
#define METAL_HSEM_REMOTE_ID 0
#define METAL_HSEM_MASTER_ID 1
// Set to 1 to enable log output.
#define METAL_LOG_HANDLER_ENABLE 0
#define metal_cpu_yield()
// Shared memory config
#define METAL_SHM_NAME "OPENAMP_SHM"
// Note 1K must be reserved at the start of the openamp
// shared memory region, for the shared resource table.
#define METAL_RSC_ADDR ((void *)_openamp_shm_region_start)
#define METAL_RSC_SIZE (1024)
#define METAL_SHM_ADDR ((metal_phys_addr_t)(_openamp_shm_region_start + METAL_RSC_SIZE))
#define METAL_SHM_SIZE ((size_t)(_openamp_shm_region_end - _openamp_shm_region_start - METAL_RSC_SIZE))
#define METAL_MPU_REGION_BASE ((uint32_t)_openamp_shm_region_start)
#define METAL_MPU_REGION_SIZE (MPU_REGION_SIZE_64KB)
extern const char _openamp_shm_region_start[];
extern const char _openamp_shm_region_end[];
int metal_rproc_notify(void *priv, uint32_t id);
extern void openamp_remoteproc_notified(mp_sched_node_t *node);
static inline int __metal_sleep_usec(unsigned int usec) {
mp_hal_delay_us(usec);
return 0;
}
static inline void metal_generic_default_poll(void) {
MICROPY_EVENT_POLL_HOOK
}
#endif // MICROPY_INCLUDED_STM32_METAL_PORT_H

Wyświetl plik

@ -0,0 +1,153 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023-2024 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* modremoteproc stm32 port.
*/
#include <stdio.h>
#include <stdint.h>
#include "py/obj.h"
#include "py/runtime.h"
#include "metal/alloc.h"
#include "metal/errno.h"
#include "metal/io.h"
#include "metal/sys.h"
#include "metal/device.h"
#include "metal/utilities.h"
#include "extmod/modopenamp_remoteproc.h"
#define DEBUG_printf(...) // mp_printf(&mp_plat_print, __VA_ARGS__)
struct remoteproc *mp_openamp_remoteproc_init(struct remoteproc *rproc,
const struct remoteproc_ops *ops, void *arg) {
DEBUG_printf("rproc_init()\n");
rproc->ops = ops;
rproc->state = RPROC_OFFLINE;
// Allocate the image store and save it in private data.
rproc->priv = mp_openamp_remoteproc_store_alloc();
return rproc;
}
void *mp_openamp_remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa,
metal_phys_addr_t *da, size_t size, unsigned int attribute,
struct metal_io_region **io) {
DEBUG_printf("rproc_mmap(): pa 0x%p da 0x%p io 0x%p size %u\n", *pa, *da, *io, size);
struct remoteproc_mem *mem;
metal_phys_addr_t lpa = *pa;
metal_phys_addr_t lda = *da;
if (lda == METAL_BAD_PHYS) {
return NULL;
}
if (lpa == METAL_BAD_PHYS) {
lpa = lda;
}
// Currently this port doesn't support loading firmware to flash,
// only SD/SRAM images are supported. Check of load address is in
// the flash region, and if so return NULL.
if (lda >= FLASH_BASE && lda < FLASH_END) {
return NULL;
}
mem = metal_allocate_memory(sizeof(*mem));
if (!mem) {
return NULL;
}
*io = metal_allocate_memory(sizeof(struct metal_io_region));
if (!*io) {
metal_free_memory(mem);
return NULL;
}
remoteproc_init_mem(mem, NULL, lpa, lda, size, *io);
metal_io_init(*io, (void *)mem->da, &mem->pa, size,
sizeof(metal_phys_addr_t) << 3, attribute, NULL);
remoteproc_add_mem(rproc, mem);
*pa = lpa;
*da = lda;
return metal_io_phys_to_virt(*io, mem->pa);
}
int mp_openamp_remoteproc_start(struct remoteproc *rproc) {
DEBUG_printf("rproc_start()\n");
if ((RCC->GCR & RCC_GCR_BOOT_C2) || (FLASH->OPTSR_CUR & FLASH_OPTSR_BCM4)) {
// The CM4 core has already been started manually, or auto-boot is enabled
// via the option bytes, in either case the core can't be restarted.
DEBUG_printf("rproc_start(): CM4 core is already booted.\n");
return -1;
}
// Flush M7 cache.
struct metal_list *node;
metal_list_for_each(&rproc->mems, node) {
struct remoteproc_mem *mem;
mem = metal_container_of(node, struct remoteproc_mem, node);
SCB_CleanDCache_by_Addr((uint32_t *)mem->pa, mem->size);
}
HAL_SYSCFG_CM4BootAddConfig(SYSCFG_BOOT_ADDR0, (uint32_t)rproc->bootaddr);
HAL_RCCEx_EnableBootCore(RCC_BOOT_C2);
return 0;
}
int mp_openamp_remoteproc_stop(struct remoteproc *rproc) {
DEBUG_printf("rproc_stop()\n");
if (rproc->state == RPROC_RUNNING) {
// There's no straightforward way to reset or shut down
// the remote processor, so a full system reset is needed.
NVIC_SystemReset();
}
return 0;
}
int mp_openamp_remoteproc_config(struct remoteproc *rproc, void *data) {
DEBUG_printf("rproc_config()\n");
(void)rproc;
return 0;
}
void mp_openamp_remoteproc_remove(struct remoteproc *rproc) {
DEBUG_printf("rproc_remove()\n");
(void)rproc;
}
int mp_openamp_remoteproc_shutdown(struct remoteproc *rproc) {
DEBUG_printf("rproc_shutdown()\n");
if (rproc->state == RPROC_RUNNING) {
// There's no straightforward way to reset or shut down
// the remote processor, so a full system reset is needed.
NVIC_SystemReset();
}
return 0;
}

Wyświetl plik

@ -36,6 +36,7 @@
#define MPU_REGION_QSPI3 (MPU_REGION_NUMBER3)
#define MPU_REGION_SDRAM1 (MPU_REGION_NUMBER4)
#define MPU_REGION_SDRAM2 (MPU_REGION_NUMBER5)
#define MPU_REGION_OPENAMP (MPU_REGION_NUMBER15)
// Only relevant on CPUs with D-Cache, must be higher priority than SDRAM
#define MPU_REGION_DMA_UNCACHED_1 (MPU_REGION_NUMBER6)
@ -94,6 +95,18 @@
| MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos \
)
#define MPU_CONFIG_SHARED_UNCACHED(size) ( \
MPU_INSTRUCTION_ACCESS_DISABLE << MPU_RASR_XN_Pos \
| MPU_REGION_FULL_ACCESS << MPU_RASR_AP_Pos \
| MPU_TEX_LEVEL1 << MPU_RASR_TEX_Pos \
| MPU_ACCESS_SHAREABLE << MPU_RASR_S_Pos \
| MPU_ACCESS_NOT_CACHEABLE << MPU_RASR_C_Pos \
| MPU_ACCESS_NOT_BUFFERABLE << MPU_RASR_B_Pos \
| 0x00 << MPU_RASR_SRD_Pos \
| (size) << MPU_RASR_SIZE_Pos \
| MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos \
)
static inline void mpu_init(void) {
MPU->CTRL = MPU_PRIVILEGED_DEFAULT | MPU_CTRL_ENABLE_Msk;
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;

Wyświetl plik

@ -44,14 +44,14 @@ ifneq ($(BUILDING_MBOOT),1)
SUPPORTS_HARDWARE_FP_SINGLE = 0
SUPPORTS_HARDWARE_FP_DOUBLE = 0
ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ))
CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard
CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard -mfp16-format=ieee
SUPPORTS_HARDWARE_FP_SINGLE = 1
SUPPORTS_HARDWARE_FP_DOUBLE = 1
else
ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0 l1 wl))
CFLAGS_CORTEX_M += -msoft-float
else
CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard
CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee
SUPPORTS_HARDWARE_FP_SINGLE = 1
endif
endif

Wyświetl plik

@ -41,6 +41,11 @@
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE)
#endif
// Don't use native _Float16 because it increases code size by a lot.
#ifndef MICROPY_FLOAT_USE_NATIVE_FLT16
#define MICROPY_FLOAT_USE_NATIVE_FLT16 (0)
#endif
// Enable arbitrary precision long-int by default.
#ifndef MICROPY_LONGINT_IMPL
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)

Wyświetl plik

@ -117,6 +117,8 @@
#define MICROPY_PY_SYS_STDFILES (1)
#define MICROPY_PY_SYS_EXC_INFO (1)
#define MICROPY_PY_COLLECTIONS_DEQUE (1)
#define MICROPY_PY_COLLECTIONS_DEQUE_ITER (1)
#define MICROPY_PY_COLLECTIONS_DEQUE_SUBSCR (1)
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
#ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)

Wyświetl plik

@ -77,6 +77,11 @@ static uint asm_arm_op_mvn_imm(uint rd, uint imm) {
return 0x3e00000 | (rd << 12) | imm;
}
static uint asm_arm_op_mvn_reg(uint rd, uint rm) {
// mvn rd, rm
return 0x1e00000 | (rd << 12) | rm;
}
static uint asm_arm_op_add_imm(uint rd, uint rn, uint imm) {
// add rd, rn, #imm
return 0x2800000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
@ -97,6 +102,11 @@ static uint asm_arm_op_sub_reg(uint rd, uint rn, uint rm) {
return 0x0400000 | (rn << 16) | (rd << 12) | rm;
}
static uint asm_arm_op_rsb_imm(uint rd, uint rn, uint imm) {
// rsb rd, rn, #imm
return 0x2600000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
}
static uint asm_arm_op_mul_reg(uint rd, uint rm, uint rs) {
// mul rd, rm, rs
assert(rd != rm);
@ -228,11 +238,23 @@ void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond) {
emit(as, asm_arm_op_mov_imm(rd, 0) | (cond ^ (1 << 28))); // mov!COND rd, #0
}
void asm_arm_mvn_reg_reg(asm_arm_t *as, uint rd, uint rm) {
// mvn rd, rm
// computes: rd := ~rm
emit_al(as, asm_arm_op_mvn_reg(rd, rm));
}
void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
// add rd, rn, rm
emit_al(as, asm_arm_op_add_reg(rd, rn, rm));
}
void asm_arm_rsb_reg_reg_imm(asm_arm_t *as, uint rd, uint rn, uint imm) {
// rsb rd, rn, #imm
// computes: rd := #imm - rn
emit_al(as, asm_arm_op_rsb_imm(rd, rn, imm));
}
void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
// sub rd, rn, rm
emit_al(as, asm_arm_op_sub_reg(rd, rn, rm));

Wyświetl plik

@ -94,8 +94,10 @@ void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm);
void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn);
// arithmetic
void asm_arm_mvn_reg_reg(asm_arm_t *as, uint rd, uint rm);
void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
void asm_arm_rsb_reg_reg_imm(asm_arm_t *as, uint rd, uint rn, uint imm);
void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
@ -188,6 +190,8 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src);
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_arm_mov_reg_local_addr((as), (reg_dest), (local_num))
#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_arm_mov_reg_pcrel((as), (reg_dest), (label))
#define ASM_NOT_REG(as, reg_dest) asm_arm_mvn_reg_reg((as), (reg_dest), (reg_dest))
#define ASM_NEG_REG(as, reg_dest) asm_arm_rsb_reg_reg_imm((as), (reg_dest), (reg_dest), 0)
#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift))
#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_arm_lsr_reg_reg((as), (reg_dest), (reg_shift))
#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift))

Wyświetl plik

@ -406,6 +406,8 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel);
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_thumb_mov_reg_local_addr((as), (reg_dest), (local_num))
#define ASM_MOV_REG_PCREL(as, rlo_dest, label) asm_thumb_mov_reg_pcrel((as), (rlo_dest), (label))
#define ASM_NOT_REG(as, reg_dest) asm_thumb_mvn_rlo_rlo((as), (reg_dest), (reg_dest))
#define ASM_NEG_REG(as, reg_dest) asm_thumb_neg_rlo_rlo((as), (reg_dest), (reg_dest))
#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift))
#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSR, (reg_dest), (reg_shift))
#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift))

Wyświetl plik

@ -54,6 +54,8 @@
#define OPCODE_MOVZX_RM8_TO_R64 (0xb6) /* 0x0f 0xb6/r */
#define OPCODE_MOVZX_RM16_TO_R64 (0xb7) /* 0x0f 0xb7/r */
#define OPCODE_LEA_MEM_TO_R64 (0x8d) /* /r */
#define OPCODE_NOT_RM64 (0xf7) /* /2 */
#define OPCODE_NEG_RM64 (0xf7) /* /3 */
#define OPCODE_AND_R64_TO_RM64 (0x21) /* /r */
#define OPCODE_OR_R64_TO_RM64 (0x09) /* /r */
#define OPCODE_XOR_R64_TO_RM64 (0x31) /* /r */
@ -362,6 +364,14 @@ void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r
}
}
void asm_x64_not_r64(asm_x64_t *as, int dest_r64) {
asm_x64_generic_r64_r64(as, dest_r64, 2, OPCODE_NOT_RM64);
}
void asm_x64_neg_r64(asm_x64_t *as, int dest_r64) {
asm_x64_generic_r64_r64(as, dest_r64, 3, OPCODE_NEG_RM64);
}
void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_AND_R64_TO_RM64);
}

Wyświetl plik

@ -97,6 +97,8 @@ void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int des
void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
void asm_x64_not_r64(asm_x64_t *as, int dest_r64);
void asm_x64_neg_r64(asm_x64_t *as, int dest_r64);
void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
@ -191,6 +193,8 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32);
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x64_mov_local_addr_to_r64((as), (local_num), (reg_dest))
#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x64_mov_reg_pcrel((as), (reg_dest), (label))
#define ASM_NOT_REG(as, reg) asm_x64_not_r64((as), (reg))
#define ASM_NEG_REG(as, reg) asm_x64_neg_r64((as), (reg))
#define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg))
#define ASM_LSR_REG(as, reg) asm_x64_shr_r64_cl((as), (reg))
#define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg))

Wyświetl plik

@ -54,6 +54,8 @@
#define OPCODE_MOVZX_RM8_TO_R32 (0xb6) /* 0x0f 0xb6/r */
#define OPCODE_MOVZX_RM16_TO_R32 (0xb7) /* 0x0f 0xb7/r */
#define OPCODE_LEA_MEM_TO_R32 (0x8d) /* /r */
#define OPCODE_NOT_RM32 (0xf7) /* /2 */
#define OPCODE_NEG_RM32 (0xf7) /* /3 */
#define OPCODE_AND_R32_TO_RM32 (0x21) /* /r */
#define OPCODE_OR_R32_TO_RM32 (0x09) /* /r */
#define OPCODE_XOR_R32_TO_RM32 (0x31) /* /r */
@ -244,6 +246,14 @@ size_t asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32) {
return loc;
}
void asm_x86_not_r32(asm_x86_t *as, int dest_r32) {
asm_x86_generic_r32_r32(as, dest_r32, 2, OPCODE_NOT_RM32);
}
void asm_x86_neg_r32(asm_x86_t *as, int dest_r32) {
asm_x86_generic_r32_r32(as, dest_r32, 3, OPCODE_NEG_RM32);
}
void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_AND_R32_TO_RM32);
}

Wyświetl plik

@ -92,6 +92,8 @@ void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest
void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
void asm_x86_not_r32(asm_x86_t *as, int dest_r32);
void asm_x86_neg_r32(asm_x86_t *as, int dest_r32);
void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
@ -186,6 +188,8 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x86_mov_local_addr_to_r32((as), (local_num), (reg_dest))
#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x86_mov_reg_pcrel((as), (reg_dest), (label))
#define ASM_NOT_REG(as, reg) asm_x86_not_r32((as), (reg))
#define ASM_NEG_REG(as, reg) asm_x86_neg_r32((as), (reg))
#define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg))
#define ASM_LSR_REG(as, reg) asm_x86_shr_r32_cl((as), (reg))
#define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg))

Wyświetl plik

@ -185,7 +185,9 @@ size_t asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) {
}
void asm_xtensa_mov_reg_i32_optimised(asm_xtensa_t *as, uint reg_dest, uint32_t i32) {
if (SIGNED_FIT12(i32)) {
if (-32 <= (int)i32 && (int)i32 <= 95) {
asm_xtensa_op_movi_n(as, reg_dest, i32);
} else if (SIGNED_FIT12(i32)) {
asm_xtensa_op_movi(as, reg_dest, i32);
} else {
asm_xtensa_mov_reg_i32(as, reg_dest, i32);

Some files were not shown because too many files have changed in this diff Show More