kopia lustrzana https://github.com/micropython/micropython
Merge branch 'micropython:master' into esp32_temp
commit
a897e95ddf
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -103,6 +103,7 @@ the following libraries.
|
|||
micropython.rst
|
||||
neopixel.rst
|
||||
network.rst
|
||||
openamp.rst
|
||||
uctypes.rst
|
||||
vfs.rst
|
||||
|
||||
|
|
|
@ -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.
|
|
@ -265,3 +265,4 @@ Classes
|
|||
machine.WDT.rst
|
||||
machine.SD.rst
|
||||
machine.SDCard.rst
|
||||
machine.USBDevice.rst
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
|
@ -241,6 +241,7 @@ Classes
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
rp2.DMA.rst
|
||||
rp2.Flash.rst
|
||||
rp2.PIO.rst
|
||||
rp2.StateMachine.rst
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
#include <metal/config.h>
|
|
@ -0,0 +1 @@
|
|||
#include <metal/config.h>
|
|
@ -0,0 +1 @@
|
|||
#include <metal/config.h>
|
|
@ -0,0 +1,5 @@
|
|||
#include <metal/config.h>
|
||||
|
||||
struct metal_state {
|
||||
struct metal_common_state common;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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) },
|
||||
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 0cb7d293a7f25394a06847a28d0f0ace9862936e
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1904dee18da85400e72b8f55c5c99e872a486573
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
22
py/asmarm.c
22
py/asmarm.c
|
@ -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));
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))
|
||||
|
|
10
py/asmx64.c
10
py/asmx64.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
10
py/asmx86.c
10
py/asmx86.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
Ładowanie…
Reference in New Issue