diff --git a/.gitignore b/.gitignore index 29e4c3062..6091787d6 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,10 @@ Session.vim tags TAGS -# Build directory +# Build directories ###################### build/ +build-*/ # Test failure outputs ###################### diff --git a/.travis.yml b/.travis.yml index e319f095b..72dbe138f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -115,6 +115,17 @@ jobs: - make ${MAKEOPTS} -C ports/unix CC=clang CFLAGS_EXTRA="-DMICROPY_STACKLESS=1 -DMICROPY_STACKLESS_STRICT=1" - make ${MAKEOPTS} -C ports/unix CC=clang test + # unix with sys.settrace + - stage: test + env: NAME="unix port with sys.settrace build and tests" + script: + - make ${MAKEOPTS} -C mpy-cross + - make ${MAKEOPTS} -C ports/unix MICROPY_PY_BTREE=0 MICROPY_PY_FFI=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-DMICROPY_PY_SYS_SETTRACE=1" test + - make ${MAKEOPTS} -C ports/unix clean + - make ${MAKEOPTS} -C ports/unix MICROPY_PY_BTREE=0 MICROPY_PY_FFI=0 MICROPY_PY_USSL=0 CFLAGS_EXTRA="-DMICROPY_STACKLESS=1 -DMICROPY_STACKLESS_STRICT=1 -DMICROPY_PY_SYS_SETTRACE=1" test + after_failure: + - (cd tests && for exp in *.exp; do testbase=$(basename $exp .exp); echo -e "\nFAILURE $testbase"; diff -u $testbase.exp $testbase.out; done) + # windows port via mingw - stage: test env: NAME="windows port build via mingw" diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index c934ef095..a593965ae 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -36,6 +36,51 @@ Functions Read the raw value of the internal Hall sensor, returning an integer. +Flash partitions +---------------- + +This class gives access to the partitions in the device's flash memory. + +.. class:: Partition(id) + + Create an object representing a partition. *id* can be a string which is the label + of the partition to retrieve, or one of the constants: ``BOOT`` or ``RUNNING``. + +.. classmethod:: Partition.find(type=TYPE_APP, subtype=0xff, label=None) + + Find a partition specified by *type*, *subtype* and *label*. Returns a + (possibly empty) list of Partition objects. + +.. method:: Partition.info() + + Returns a 6-tuple ``(type, subtype, addr, size, label, encrypted)``. + +.. method:: Partition.readblocks(block_num, buf) +.. method:: Partition.writeblocks(block_num, buf) +.. method:: Partition.ioctl(cmd, arg) + + These methods implement the block protocol defined by :class:`uos.AbstractBlockDev`. + +.. method:: Partition.set_boot() + + Sets the partition as the boot partition. + +.. method:: Partition.get_next_update() + + Gets the next update partition after this one, and returns a new Partition object. + +Constants +~~~~~~~~~ + +.. data:: Partition.BOOT + Partition.RUNNING + + Used in the `Partition` constructor to fetch various partitions. + +.. data:: Partition.TYPE_APP + Partition.TYPE_DATA + + Used in `Partition.find` to specify the partition type. The Ultra-Low-Power co-processor -------------------------------- diff --git a/docs/library/index.rst b/docs/library/index.rst index b464e85fa..4e23e6e01 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -136,6 +136,7 @@ The following libraries and classes are specific to the WiPy. :maxdepth: 2 wipy.rst + machine.ADCWiPy.rst machine.TimerWiPy.rst diff --git a/docs/library/machine.ADC.rst b/docs/library/machine.ADC.rst index 4c7a04d74..1404b454a 100644 --- a/docs/library/machine.ADC.rst +++ b/docs/library/machine.ADC.rst @@ -4,71 +4,32 @@ class ADC -- analog to digital conversion ========================================= -Usage:: +The ADC class provides an interface to analog-to-digital convertors, and +represents a single endpoint that can sample a continuous voltage and +convert it to a discretised value. + +Example usage:: import machine - adc = machine.ADC() # create an ADC object - apin = adc.channel(pin='GP3') # create an analog pin on GP3 - val = apin() # read an analog value + adc = machine.ADC(pin) # create an ADC object acting on a pin + val = adc.read_u16() # read a raw analog value in the range 0-65535 Constructors ------------ -.. class:: ADC(id=0, \*, bits=12) +.. class:: ADC(id) - Create an ADC object associated with the given pin. - This allows you to then read analog values on that pin. - For more info check the `pinout and alternate functions - table. `_ - - .. warning:: - - ADC pin input range is 0-1.4V (being 1.8V the absolute maximum that it - can withstand). When GP2, GP3, GP4 or GP5 are remapped to the - ADC block, 1.8 V is the maximum. If these pins are used in digital mode, - then the maximum allowed input is 3.6V. + Access the ADC associated with a source identified by *id*. This + *id* may be an integer (usually specifying a channel number), a + :ref:`Pin ` object, or other value supported by the + underlying machine. Methods ------- -.. method:: ADC.channel(id, \*, pin) +.. method:: ADC.read_u16() - Create an analog pin. If only channel ID is given, the correct pin will - be selected. Alternatively, only the pin can be passed and the correct - channel will be selected. Examples:: - - # all of these are equivalent and enable ADC channel 1 on GP3 - apin = adc.channel(1) - apin = adc.channel(pin='GP3') - apin = adc.channel(id=1, pin='GP3') - -.. method:: ADC.init() - - Enable the ADC block. - -.. method:: ADC.deinit() - - Disable the ADC block. - -class ADCChannel --- read analog values from internal or external sources -========================================================================= - -ADC channels can be connected to internal points of the MCU or to GPIO pins. -ADC channels are created using the ADC.channel method. - -.. method:: adcchannel() - - Fast method to read the channel value. - -.. method:: adcchannel.value() - - Read the channel value. - -.. method:: adcchannel.init() - - Re-init (and effectively enable) the ADC channel. - -.. method:: adcchannel.deinit() - - Disable the ADC channel. + Take an analog reading and return an integer in the range 0-65535. + The return value represents the raw reading taken by the ADC, scaled + such that the minimum value is 0 and the maximum value is 65535. diff --git a/docs/library/machine.ADCWiPy.rst b/docs/library/machine.ADCWiPy.rst new file mode 100644 index 000000000..4a4f0524c --- /dev/null +++ b/docs/library/machine.ADCWiPy.rst @@ -0,0 +1,81 @@ +.. currentmodule:: machine +.. _machine.ADCWiPy: + +class ADCWiPy -- analog to digital conversion +============================================= + +.. note:: + + This class is a non-standard ADC implementation for the WiPy. + It is available simply as ``machine.ADC`` on the WiPy but is named in the + documentation below as ``machine.ADCWiPy`` to distinguish it from the + more general :ref:`machine.ADC ` class. + +Usage:: + + import machine + + adc = machine.ADC() # create an ADC object + apin = adc.channel(pin='GP3') # create an analog pin on GP3 + val = apin() # read an analog value + +Constructors +------------ + +.. class:: ADCWiPy(id=0, \*, bits=12) + + Create an ADC object associated with the given pin. + This allows you to then read analog values on that pin. + For more info check the `pinout and alternate functions + table. `_ + + .. warning:: + + ADC pin input range is 0-1.4V (being 1.8V the absolute maximum that it + can withstand). When GP2, GP3, GP4 or GP5 are remapped to the + ADC block, 1.8 V is the maximum. If these pins are used in digital mode, + then the maximum allowed input is 3.6V. + +Methods +------- + +.. method:: ADCWiPy.channel(id, \*, pin) + + Create an analog pin. If only channel ID is given, the correct pin will + be selected. Alternatively, only the pin can be passed and the correct + channel will be selected. Examples:: + + # all of these are equivalent and enable ADC channel 1 on GP3 + apin = adc.channel(1) + apin = adc.channel(pin='GP3') + apin = adc.channel(id=1, pin='GP3') + +.. method:: ADCWiPy.init() + + Enable the ADC block. + +.. method:: ADCWiPy.deinit() + + Disable the ADC block. + +class ADCChannel --- read analog values from internal or external sources +========================================================================= + +ADC channels can be connected to internal points of the MCU or to GPIO pins. +ADC channels are created using the ADC.channel method. + +.. method:: adcchannel() + + Fast method to read the channel value. + +.. method:: adcchannel.value() + + Read the channel value. + +.. method:: adcchannel.init() + + Re-init (and effectively enable) the ADC channel. + +.. method:: adcchannel.deinit() + + Disable the ADC channel. diff --git a/docs/library/pyb.DAC.rst b/docs/library/pyb.DAC.rst index c67603a71..9a465b9ce 100644 --- a/docs/library/pyb.DAC.rst +++ b/docs/library/pyb.DAC.rst @@ -93,9 +93,9 @@ Methods .. method:: DAC.triangle(freq) - Generate a triangle wave. The value on the DAC output changes at - the given frequency, and the frequency of the repeating triangle wave - itself is 2048 times smaller. + Generate a triangle wave. The value on the DAC output changes at the given + frequency and ramps through the full 12-bit range (up and down). Therefore + the frequency of the repeating triangle wave itself is 8192 times smaller. .. method:: DAC.write(value) diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst index 1e1e9ffaa..9ba7e991e 100644 --- a/docs/library/pyb.rst +++ b/docs/library/pyb.rst @@ -259,34 +259,45 @@ Miscellaneous functions Returns a string of 12 bytes (96 bits), which is the unique ID of the MCU. -.. function:: usb_mode([modestr], vid=0xf055, pid=0x9801, hid=pyb.hid_mouse) +.. function:: usb_mode([modestr], port=-1, vid=0xf055, pid=-1, msc=(), hid=pyb.hid_mouse, high_speed=False) If called with no arguments, return the current USB mode as a string. - If called with ``modestr`` provided, attempts to set USB mode. - This can only be done when called from ``boot.py`` before - :meth:`pyb.main()` has been called. The following values of - ``modestr`` are understood: + If called with *modestr* provided, attempts to configure the USB mode. + The following values of *modestr* are understood: - ``None``: disables USB - ``'VCP'``: enable with VCP (Virtual COM Port) interface - ``'MSC'``: enable with MSC (mass storage device class) interface - ``'VCP+MSC'``: enable with VCP and MSC - ``'VCP+HID'``: enable with VCP and HID (human interface device) + - ``'VCP+MSC+HID'``: enabled with VCP, MSC and HID (only available on PYBD boards) For backwards compatibility, ``'CDC'`` is understood to mean ``'VCP'`` (and similarly for ``'CDC+MSC'`` and ``'CDC+HID'``). - The ``vid`` and ``pid`` parameters allow you to specify the VID - (vendor id) and PID (product id). + The *port* parameter should be an integer (0, 1, ...) and selects which + USB port to use if the board supports multiple ports. A value of -1 uses + the default or automatically selected port. + + The *vid* and *pid* parameters allow you to specify the VID (vendor id) + and PID (product id). A *pid* value of -1 will select a PID based on the + value of *modestr*. + + If enabling MSC mode, the *msc* parameter can be used to specify a list + of SCSI LUNs to expose on the mass storage interface. For example + ``msc=(pyb.Flash(), pyb.SDCard())``. If enabling HID mode, you may also specify the HID details by - passing the ``hid`` keyword parameter. It takes a tuple of + passing the *hid* keyword parameter. It takes a tuple of (subclass, protocol, max packet length, polling interval, report descriptor). By default it will set appropriate values for a USB mouse. There is also a ``pyb.hid_keyboard`` constant, which is an appropriate tuple for a USB keyboard. + The *high_speed* parameter, when set to ``True``, enables USB HS mode if + it is supported by the hardware. + Classes ------- diff --git a/docs/library/sys.rst b/docs/library/sys.rst index f2d96cb8c..d3cc308d8 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -15,6 +15,19 @@ Functions function raise as `SystemExit` exception. If an argument is given, its value given as an argument to `SystemExit`. +.. function:: atexit(func) + + Register *func* to be called upon termination. *func* must be a callable + that takes no arguments, or ``None`` to disable the call. The ``atexit`` + function will return the previous value set by this function, which is + initially ``None``. + + .. admonition:: Difference to CPython + :class: attention + + This function is a MicroPython extension intended to provide similar + functionality to the :mod:`atexit` module in CPython. + .. function:: print_exception(exc, file=sys.stdout) Print exception with a traceback to a file-like object *file* (or @@ -121,3 +134,9 @@ Constants .. data:: version_info Python language version that this implementation conforms to, as a tuple of ints. + + .. admonition:: Difference to CPython + :class: attention + + Only the first three version numbers (major, minor, micro) are supported and + they can be referenced only by index, not by name. diff --git a/docs/pyboard/tutorial/script.rst b/docs/pyboard/tutorial/script.rst index 75dd324e3..2d44bbc88 100644 --- a/docs/pyboard/tutorial/script.rst +++ b/docs/pyboard/tutorial/script.rst @@ -31,7 +31,7 @@ have as to what happens next: We will get the serial device working in the next tutorial. - **Mac**: Your pyboard will appear on the desktop as a removable disc. - It will probably be called "NONAME". Click on it to open the pyboard folder. + It will probably be called ``PYBFLASH``. Click on it to open the pyboard folder. - **Linux**: Your pyboard will appear as a removable medium. On Ubuntu it will mount automatically and pop-up a window with the pyboard folder. @@ -46,17 +46,17 @@ a window (or command line) should be showing the files on the pyboard drive. The drive you are looking at is known as ``/flash`` by the pyboard, and should contain the following 4 files: -* `boot.py `_ -- this script is executed when the pyboard boots up. It sets - up various configuration options for the pyboard. +* `boot.py `_ -- the various configuration options for the pyboard. + It is executed when the pyboard boots up. -* `main.py `_ -- this is the main script that will contain your Python program. +* `main.py `_ -- the Python program to be run. It is executed after ``boot.py``. -* `README.txt `_ -- this contains some very basic information about getting - started with the pyboard. +* `README.txt `_ -- basic information about getting started with the pyboard. + This provides pointers for new users and can be safely deleted. -* `pybcdc.inf `_ -- this is a Windows driver file to configure the serial USB - device. More about this in the next tutorial. +* `pybcdc.inf `_ -- the Windows driver file to configure the serial USB device. + More about this in the next tutorial. Editing ``main.py`` ------------------- diff --git a/docs/pyboard/tutorial/usb_mouse.rst b/docs/pyboard/tutorial/usb_mouse.rst index 6f8831edb..8166946ec 100644 --- a/docs/pyboard/tutorial/usb_mouse.rst +++ b/docs/pyboard/tutorial/usb_mouse.rst @@ -39,14 +39,15 @@ Sending mouse events by hand To get the py-mouse to do anything we need to send mouse events to the PC. We will first do this manually using the REPL prompt. Connect to your -pyboard using your serial program and type the following:: +pyboard using your serial program and type the following (no need to type +the ``#`` and text following it):: >>> hid = pyb.USB_HID() - >>> hid.send((0, 10, 0, 0)) + >>> hid.send((0, 100, 0, 0)) # (button status, x-direction, y-direction, scroll) -Your mouse should move 10 pixels to the right! In the command above you -are sending 4 pieces of information: button status, x, y and scroll. The -number 10 is telling the PC that the mouse moved 10 pixels in the x direction. +Your mouse should move 100 pixels to the right! In the command above you +are sending 4 pieces of information: **button status**, **x-direction**, **y-direction**, and **scroll**. The +number 100 is telling the PC that the mouse moved 100 pixels in the x direction. Let's make the mouse oscillate left and right:: diff --git a/docs/reference/speed_python.rst b/docs/reference/speed_python.rst index c5aa80c6e..aa0b54cb5 100644 --- a/docs/reference/speed_python.rst +++ b/docs/reference/speed_python.rst @@ -165,7 +165,7 @@ by caching the object in a local variable: class foo(object): def __init__(self): - ba = bytearray(100) + self.ba = bytearray(100) def bar(self, obj_display): ba_ref = self.ba fb = obj_display.framebuffer @@ -293,10 +293,12 @@ microseconds. The rules for casting are as follows: * The argument to a bool cast must be integral type (boolean or integer); when used as a return type the viper function will return True or False objects. * If the argument is a Python object and the cast is ``ptr``, ``ptr``, ``ptr16`` or ``ptr32``, - then the Python object must either have the buffer protocol with read-write capabilities - (in which case a pointer to the start of the buffer is returned) or it must be of integral - type (in which case the value of that integral object is returned). - + then the Python object must either have the buffer protocol (in which case a pointer to the + start of the buffer is returned) or it must be of integral type (in which case the value of + that integral object is returned). + +Writing to a pointer which points to a read-only object will lead to undefined behaviour. + The following example illustrates the use of a ``ptr16`` cast to toggle pin X1 ``n`` times: .. code:: python diff --git a/docs/wipy/quickref.rst b/docs/wipy/quickref.rst index 7aa832fd2..6349676cf 100644 --- a/docs/wipy/quickref.rst +++ b/docs/wipy/quickref.rst @@ -82,7 +82,7 @@ See :ref:`machine.Pin ` and :ref:`machine.Timer `. : ADC (analog to digital conversion) ---------------------------------- -See :ref:`machine.ADC `. :: +See :ref:`machine.ADCWiPy `. :: from machine import ADC diff --git a/examples/embedding/hello-embed.c b/examples/embedding/hello-embed.c index 965963096..2000b703c 100644 --- a/examples/embedding/hello-embed.c +++ b/examples/embedding/hello-embed.c @@ -41,7 +41,7 @@ mp_obj_t execute_from_str(const char *str) { qstr src_name = 1/*MP_QSTR_*/; mp_lexer_t *lex = mp_lexer_new_from_str_len(src_name, str, strlen(str), false); mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_FILE_INPUT); - mp_obj_t module_fun = mp_compile(&pt, src_name, MP_EMIT_OPT_NONE, false); + mp_obj_t module_fun = mp_compile(&pt, src_name, false); mp_call_function_0(module_fun); nlr_pop(); return 0; diff --git a/extmod/crypto-algorithms/sha256.c b/extmod/crypto-algorithms/sha256.c index 276611cfd..24e964749 100644 --- a/extmod/crypto-algorithms/sha256.c +++ b/extmod/crypto-algorithms/sha256.c @@ -1,7 +1,8 @@ /********************************************************************* +* Source: https://github.com/B-Con/crypto-algorithms * Filename: sha256.c * Author: Brad Conte (brad AT bradconte.com) -* Copyright: +* Copyright: This code is released into the public domain. * Disclaimer: This code is presented "as is" without any guarantees. * Details: Implementation of the SHA-256 hashing algorithm. SHA-256 is one of the three algorithms in the SHA2 diff --git a/extmod/crypto-algorithms/sha256.h b/extmod/crypto-algorithms/sha256.h index caa1f8102..9c19472ea 100644 --- a/extmod/crypto-algorithms/sha256.h +++ b/extmod/crypto-algorithms/sha256.h @@ -1,7 +1,8 @@ /********************************************************************* +* Source: https://github.com/B-Con/crypto-algorithms * Filename: sha256.h * Author: Brad Conte (brad AT bradconte.com) -* Copyright: +* Copyright: This code is released into the public domain. * Disclaimer: This code is presented "as is" without any guarantees. * Details: Defines the API for the corresponding SHA1 implementation. *********************************************************************/ diff --git a/extmod/moducryptolib.c b/extmod/moducryptolib.c index 15cd4535f..fd487a816 100644 --- a/extmod/moducryptolib.c +++ b/extmod/moducryptolib.c @@ -89,7 +89,7 @@ typedef struct _mp_obj_aes_t { uint8_t key_type: 2; } mp_obj_aes_t; -STATIC inline bool is_ctr_mode(int block_mode) { +static inline bool is_ctr_mode(int block_mode) { #if MICROPY_PY_UCRYPTOLIB_CTR return block_mode == UCRYPTOLIB_MODE_CTR; #else @@ -97,7 +97,7 @@ STATIC inline bool is_ctr_mode(int block_mode) { #endif } -STATIC inline struct ctr_params *ctr_params_from_aes(mp_obj_aes_t *o) { +static inline struct ctr_params *ctr_params_from_aes(mp_obj_aes_t *o) { // ctr_params follows aes object struct return (struct ctr_params*)&o[1]; } diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 9b46371f3..cf83f43e3 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -299,13 +299,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, ucty static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) { char struct_type = big_endian ? '>' : '<'; static const char type2char[16] = "BbHhIiQq------fd"; - return mp_binary_get_val(struct_type, type2char[val_type], &p); + return mp_binary_get_val(struct_type, type2char[val_type], p, &p); } static inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) { char struct_type = big_endian ? '>' : '<'; static const char type2char[16] = "BbHhIiQq------fd"; - mp_binary_set_val(struct_type, type2char[val_type], val, &p); + mp_binary_set_val(struct_type, type2char[val_type], val, p, &p); } static inline mp_uint_t get_aligned_basic(uint val_type, void *p) { diff --git a/extmod/moduheapq.c b/extmod/moduheapq.c index bdaf191e9..1574eb862 100644 --- a/extmod/moduheapq.c +++ b/extmod/moduheapq.c @@ -31,14 +31,14 @@ // the algorithm here is modelled on CPython's heapq.py -STATIC mp_obj_list_t *get_heap(mp_obj_t heap_in) { +STATIC mp_obj_list_t *uheapq_get_heap(mp_obj_t heap_in) { if (!mp_obj_is_type(heap_in, &mp_type_list)) { mp_raise_TypeError("heap must be a list"); } return MP_OBJ_TO_PTR(heap_in); } -STATIC void heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) { +STATIC void uheapq_heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos) { mp_obj_t item = heap->items[pos]; while (pos > start_pos) { mp_uint_t parent_pos = (pos - 1) >> 1; @@ -53,7 +53,7 @@ STATIC void heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t po heap->items[pos] = item; } -STATIC void heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) { +STATIC void uheapq_heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) { mp_uint_t start_pos = pos; mp_uint_t end_pos = heap->len; mp_obj_t item = heap->items[pos]; @@ -67,19 +67,19 @@ STATIC void heap_siftup(mp_obj_list_t *heap, mp_uint_t pos) { pos = child_pos; } heap->items[pos] = item; - heap_siftdown(heap, start_pos, pos); + uheapq_heap_siftdown(heap, start_pos, pos); } STATIC mp_obj_t mod_uheapq_heappush(mp_obj_t heap_in, mp_obj_t item) { - mp_obj_list_t *heap = get_heap(heap_in); + mp_obj_list_t *heap = uheapq_get_heap(heap_in); mp_obj_list_append(heap_in, item); - heap_siftdown(heap, 0, heap->len - 1); + uheapq_heap_siftdown(heap, 0, heap->len - 1); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_uheapq_heappush_obj, mod_uheapq_heappush); STATIC mp_obj_t mod_uheapq_heappop(mp_obj_t heap_in) { - mp_obj_list_t *heap = get_heap(heap_in); + mp_obj_list_t *heap = uheapq_get_heap(heap_in); if (heap->len == 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap")); } @@ -88,16 +88,16 @@ STATIC mp_obj_t mod_uheapq_heappop(mp_obj_t heap_in) { heap->items[0] = heap->items[heap->len]; heap->items[heap->len] = MP_OBJ_NULL; // so we don't retain a pointer if (heap->len) { - heap_siftup(heap, 0); + uheapq_heap_siftup(heap, 0); } return item; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_uheapq_heappop_obj, mod_uheapq_heappop); STATIC mp_obj_t mod_uheapq_heapify(mp_obj_t heap_in) { - mp_obj_list_t *heap = get_heap(heap_in); + mp_obj_list_t *heap = uheapq_get_heap(heap_in); for (mp_uint_t i = heap->len / 2; i > 0;) { - heap_siftup(heap, --i); + uheapq_heap_siftup(heap, --i); } return mp_const_none; } diff --git a/extmod/modujson.c b/extmod/modujson.c index f5c6428ef..15ed2f38d 100644 --- a/extmod/modujson.c +++ b/extmod/modujson.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2014-2016 Damien P. George + * Copyright (c) 2014-2019 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 @@ -281,9 +281,9 @@ STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_load_obj, mod_ujson_load); STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) { - size_t len; - const char *buf = mp_obj_str_get_data(obj, &len); - vstr_t vstr = {len, len, (char*)buf, true}; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(obj, &bufinfo, MP_BUFFER_READ); + vstr_t vstr = {bufinfo.len, bufinfo.len, (char*)bufinfo.buf, true}; mp_obj_stringio_t sio = {{&mp_type_stringio}, &vstr, 0, MP_OBJ_NULL}; return mod_ujson_load(MP_OBJ_FROM_PTR(&sio)); } diff --git a/extmod/modure.c b/extmod/modure.c index 0d5330cb5..8a6020705 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -382,6 +382,7 @@ STATIC const mp_obj_type_t re_type = { }; STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { + (void)n_args; const char *re_str = mp_obj_str_get_str(args[0]); int size = re1_5_sizecode(re_str); if (size == -1) { @@ -389,18 +390,22 @@ STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { } mp_obj_re_t *o = m_new_obj_var(mp_obj_re_t, char, size); o->base.type = &re_type; + #if MICROPY_PY_URE_DEBUG int flags = 0; if (n_args > 1) { flags = mp_obj_get_int(args[1]); } + #endif int error = re1_5_compilecode(&o->re, re_str); if (error != 0) { error: mp_raise_ValueError("Error in regex"); } + #if MICROPY_PY_URE_DEBUG if (flags & FLAG_DEBUG) { re1_5_dumpcode(&o->re); } + #endif return MP_OBJ_FROM_PTR(o); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_compile_obj, 1, 2, mod_re_compile); @@ -440,7 +445,9 @@ STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = { #if MICROPY_PY_URE_SUB { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&mod_re_sub_obj) }, #endif + #if MICROPY_PY_URE_DEBUG { MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_re_globals, mp_module_re_globals_table); @@ -455,7 +462,9 @@ const mp_obj_module_t mp_module_ure = { #define re1_5_fatal(x) assert(!x) #include "re1.5/compilecode.c" +#if MICROPY_PY_URE_DEBUG #include "re1.5/dumpcode.c" +#endif #include "re1.5/recursiveloop.c" #include "re1.5/charclass.c" diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index b559b1358..2ea175728 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -54,7 +54,7 @@ struct ssl_args { STATIC const mp_obj_type_t ussl_socket_type; -STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { +STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args) { #if MICROPY_PY_USSL_FINALISER mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); #else @@ -118,13 +118,13 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { return o; } -STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { +STATIC void ussl_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "<_SSLSocket %p>", self->ssl_sock); } -STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { +STATIC mp_uint_t ussl_socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); if (o->ssl_sock == NULL) { @@ -173,7 +173,7 @@ eagain: return size; } -STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { +STATIC mp_uint_t ussl_socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(o_in); if (o->ssl_sock == NULL) { @@ -189,7 +189,7 @@ STATIC mp_uint_t socket_write(mp_obj_t o_in, const void *buf, mp_uint_t size, in return r; } -STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { +STATIC mp_uint_t ussl_socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in); if (request == MP_STREAM_CLOSE && self->ssl_sock != NULL) { ssl_free(self->ssl_sock); @@ -200,7 +200,7 @@ STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, i return mp_get_stream(self->sock)->ioctl(self->sock, request, arg, errcode); } -STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { +STATIC mp_obj_t ussl_socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { mp_obj_ssl_socket_t *o = MP_OBJ_TO_PTR(self_in); mp_obj_t sock = o->sock; mp_obj_t dest[3]; @@ -210,14 +210,14 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { o->blocking = mp_obj_is_true(flag_in); return res; } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(ussl_socket_setblocking_obj, ussl_socket_setblocking); STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, - { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, + { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&ussl_socket_setblocking_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, #if MICROPY_PY_USSL_FINALISER { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, @@ -227,16 +227,16 @@ STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table); STATIC const mp_stream_p_t ussl_socket_stream_p = { - .read = socket_read, - .write = socket_write, - .ioctl = socket_ioctl, + .read = ussl_socket_read, + .write = ussl_socket_write, + .ioctl = ussl_socket_ioctl, }; STATIC const mp_obj_type_t ussl_socket_type = { { &mp_type_type }, // Save on qstr's, reuse same as for module .name = MP_QSTR_ussl, - .print = socket_print, + .print = ussl_socket_print, .getiter = NULL, .iternext = NULL, .protocol = &ussl_socket_stream_p, @@ -260,7 +260,7 @@ STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args); - return MP_OBJ_FROM_PTR(socket_new(sock, &args)); + return MP_OBJ_FROM_PTR(ussl_socket_new(sock, &args)); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ssl_wrap_socket_obj, 1, mod_ssl_wrap_socket); diff --git a/extmod/modutimeq.c b/extmod/modutimeq.c index 26f5a78fa..28a2a70c5 100644 --- a/extmod/modutimeq.c +++ b/extmod/modutimeq.c @@ -55,7 +55,7 @@ typedef struct _mp_obj_utimeq_t { STATIC mp_uint_t utimeq_id; -STATIC mp_obj_utimeq_t *get_heap(mp_obj_t heap_in) { +STATIC mp_obj_utimeq_t *utimeq_get_heap(mp_obj_t heap_in) { return MP_OBJ_TO_PTR(heap_in); } @@ -85,7 +85,7 @@ STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t return MP_OBJ_FROM_PTR(o); } -STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) { +STATIC void utimeq_heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) { struct qentry item = heap->items[pos]; while (pos > start_pos) { mp_uint_t parent_pos = (pos - 1) >> 1; @@ -101,7 +101,7 @@ STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t heap->items[pos] = item; } -STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { +STATIC void utimeq_heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { mp_uint_t start_pos = pos; mp_uint_t end_pos = heap->len; struct qentry item = heap->items[pos]; @@ -118,13 +118,13 @@ STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { pos = child_pos; } heap->items[pos] = item; - heap_siftdown(heap, start_pos, pos); + utimeq_heap_siftdown(heap, start_pos, pos); } STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { (void)n_args; mp_obj_t heap_in = args[0]; - mp_obj_utimeq_t *heap = get_heap(heap_in); + mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); if (heap->len == heap->alloc) { mp_raise_msg(&mp_type_IndexError, "queue overflow"); } @@ -133,14 +133,14 @@ STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { heap->items[l].id = utimeq_id++; heap->items[l].callback = args[2]; heap->items[l].args = args[3]; - heap_siftdown(heap, 0, heap->len); + utimeq_heap_siftdown(heap, 0, heap->len); heap->len++; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush); STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { - mp_obj_utimeq_t *heap = get_heap(heap_in); + mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); if (heap->len == 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap")); } @@ -158,14 +158,14 @@ STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer heap->items[heap->len].args = MP_OBJ_NULL; if (heap->len) { - heap_siftup(heap, 0); + utimeq_heap_siftup(heap, 0); } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop); STATIC mp_obj_t mod_utimeq_peektime(mp_obj_t heap_in) { - mp_obj_utimeq_t *heap = get_heap(heap_in); + mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); if (heap->len == 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap")); } @@ -177,7 +177,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_peektime_obj, mod_utimeq_peektime); #if DEBUG STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) { - mp_obj_utimeq_t *heap = get_heap(heap_in); + mp_obj_utimeq_t *heap = utimeq_get_heap(heap_in); for (int i = 0; i < heap->len; i++) { printf(UINT_FMT "\t%p\t%p\n", heap->items[i].time, MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args)); diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index f7b9331b8..fb1e582f2 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -219,7 +219,7 @@ STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size // TODO gc hook to close the file if not already closed -STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { +STATIC const mp_rom_map_elem_t vfs_fat_rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, @@ -234,10 +234,10 @@ STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&file_obj___exit___obj) }, }; -STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(vfs_fat_rawfile_locals_dict, vfs_fat_rawfile_locals_dict_table); #if MICROPY_PY_IO_FILEIO -STATIC const mp_stream_p_t fileio_stream_p = { +STATIC const mp_stream_p_t vfs_fat_fileio_stream_p = { .read = file_obj_read, .write = file_obj_write, .ioctl = file_obj_ioctl, @@ -250,12 +250,12 @@ const mp_obj_type_t mp_type_vfs_fat_fileio = { .make_new = file_obj_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, - .protocol = &fileio_stream_p, - .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, + .protocol = &vfs_fat_fileio_stream_p, + .locals_dict = (mp_obj_dict_t*)&vfs_fat_rawfile_locals_dict, }; #endif -STATIC const mp_stream_p_t textio_stream_p = { +STATIC const mp_stream_p_t vfs_fat_textio_stream_p = { .read = file_obj_read, .write = file_obj_write, .ioctl = file_obj_ioctl, @@ -269,8 +269,8 @@ const mp_obj_type_t mp_type_vfs_fat_textio = { .make_new = file_obj_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, - .protocol = &textio_stream_p, - .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, + .protocol = &vfs_fat_textio_stream_p, + .locals_dict = (mp_obj_dict_t*)&vfs_fat_rawfile_locals_dict, }; // Factory function for I/O stream classes diff --git a/extmod/vfs_posix_file.c b/extmod/vfs_posix_file.c index 44cb85dcc..6f7ce814f 100644 --- a/extmod/vfs_posix_file.c +++ b/extmod/vfs_posix_file.c @@ -200,7 +200,7 @@ STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_ } } -STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { +STATIC const mp_rom_map_elem_t vfs_posix_rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&vfs_posix_file_fileno_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, @@ -215,10 +215,10 @@ STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&vfs_posix_file___exit___obj) }, }; -STATIC MP_DEFINE_CONST_DICT(rawfile_locals_dict, rawfile_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(vfs_posix_rawfile_locals_dict, vfs_posix_rawfile_locals_dict_table); #if MICROPY_PY_IO_FILEIO -STATIC const mp_stream_p_t fileio_stream_p = { +STATIC const mp_stream_p_t vfs_posix_fileio_stream_p = { .read = vfs_posix_file_read, .write = vfs_posix_file_write, .ioctl = vfs_posix_file_ioctl, @@ -231,12 +231,12 @@ const mp_obj_type_t mp_type_vfs_posix_fileio = { .make_new = vfs_posix_file_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, - .protocol = &fileio_stream_p, - .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, + .protocol = &vfs_posix_fileio_stream_p, + .locals_dict = (mp_obj_dict_t*)&vfs_posix_rawfile_locals_dict, }; #endif -STATIC const mp_stream_p_t textio_stream_p = { +STATIC const mp_stream_p_t vfs_posix_textio_stream_p = { .read = vfs_posix_file_read, .write = vfs_posix_file_write, .ioctl = vfs_posix_file_ioctl, @@ -250,8 +250,8 @@ const mp_obj_type_t mp_type_vfs_posix_textio = { .make_new = vfs_posix_file_make_new, .getiter = mp_identity_getiter, .iternext = mp_stream_unbuffered_iter, - .protocol = &textio_stream_p, - .locals_dict = (mp_obj_dict_t*)&rawfile_locals_dict, + .protocol = &vfs_posix_textio_stream_p, + .locals_dict = (mp_obj_dict_t*)&vfs_posix_rawfile_locals_dict, }; const mp_obj_vfs_posix_file_t mp_sys_stdin_obj = {{&mp_type_textio}, STDIN_FILENO}; diff --git a/lib/lv_bindings b/lib/lv_bindings index a717b4456..a29af2788 160000 --- a/lib/lv_bindings +++ b/lib/lv_bindings @@ -1 +1 @@ -Subproject commit a717b4456b3771ed8a3e1e9aa4802a43e501c003 +Subproject commit a29af27883dacb96e0b7bf1b75573a1d26026696 diff --git a/lib/upytesthelper/upytesthelper.c b/lib/upytesthelper/upytesthelper.c index d28c5b9cc..326172be6 100644 --- a/lib/upytesthelper/upytesthelper.c +++ b/lib/upytesthelper/upytesthelper.c @@ -101,7 +101,7 @@ void upytest_execute_test(const char *src) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, false); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, false); mp_call_function_0(module_fun); nlr_pop(); } else { diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index adb16937d..851b026b6 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -89,7 +89,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input // source is a lexer, parse and compile the script qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, exec_flags & EXEC_FLAG_IS_REPL); + module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); #else mp_raise_msg(&mp_type_RuntimeError, "script compilation not supported"); #endif diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 970ad2d75..afd24ca9f 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -72,7 +72,7 @@ STATIC int compile_and_save(const char *file, const char *output_file, const cha #endif mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); - mp_raw_code_t *rc = mp_compile_to_raw_code(&parse_tree, source_name, emit_opt, false); + mp_raw_code_t *rc = mp_compile_to_raw_code(&parse_tree, source_name, false); vstr_t vstr; vstr_init(&vstr, 16); @@ -115,7 +115,11 @@ STATIC int usage(char **argv) { ); int impl_opts_cnt = 0; printf( +#if MICROPY_EMIT_NATIVE " emit={bytecode,native,viper} -- set the default code emitter\n" +#else +" emit=bytecode -- set the default code emitter\n" +#endif ); impl_opts_cnt++; printf( @@ -140,10 +144,12 @@ STATIC void pre_process_options(int argc, char **argv) { } if (strcmp(argv[a + 1], "emit=bytecode") == 0) { emit_opt = MP_EMIT_OPT_BYTECODE; + #if MICROPY_EMIT_NATIVE } else if (strcmp(argv[a + 1], "emit=native") == 0) { emit_opt = MP_EMIT_OPT_NATIVE_PYTHON; } else if (strcmp(argv[a + 1], "emit=viper") == 0) { emit_opt = MP_EMIT_OPT_VIPER; + #endif } else if (strncmp(argv[a + 1], "heapsize=", sizeof("heapsize=") - 1) == 0) { char *end; heap_size = strtol(argv[a + 1] + sizeof("heapsize=") - 1, &end, 0); @@ -190,6 +196,13 @@ MP_NOINLINE int main_(int argc, char **argv) { mp_obj_list_init(mp_sys_path, 0); mp_obj_list_init(mp_sys_argv, 0); + #if MICROPY_EMIT_NATIVE + // Set default emitter options + MP_STATE_VM(default_emit_opt) = emit_opt; + #else + (void)emit_opt; + #endif + // set default compiler configuration mp_dynamic_compiler.small_int_bits = 31; mp_dynamic_compiler.opt_cache_map_lookup_in_bytecode = 0; diff --git a/ports/bare-arm/main.c b/ports/bare-arm/main.c index b96fb47ac..418394410 100644 --- a/ports/bare-arm/main.c +++ b/ports/bare-arm/main.c @@ -13,7 +13,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); mp_call_function_0(module_fun); nlr_pop(); } else { diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 00fa07925..5335b68ee 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -1,7 +1,31 @@ +# Select the board to build for: if not given on the command line, +# then default to GENERIC. +BOARD ?= GENERIC + +# If the build directory is not given, make it reflect the board name. +BUILD ?= build-$(BOARD) + +BOARD_DIR ?= boards/$(BOARD) +ifeq ($(wildcard $(BOARD_DIR)/.),) +$(error Invalid BOARD specified: $(BOARD_DIR)) +endif + include ../../py/mkenv.mk +# Optional (not currently used for ESP32) +-include mpconfigport.mk + +ifneq ($(SDKCONFIG),) +$(error Use the BOARD variable instead of SDKCONFIG) +endif + +# Expected to set SDKCONFIG +include $(BOARD_DIR)/mpconfigboard.mk + # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h +QSTR_GLOBAL_DEPENDENCIES = $(BOARD_DIR)/mpconfigboard.h +QSTR_GLOBAL_REQUIREMENTS = $(SDKCONFIG_H) MICROPY_PY_USSL = 0 MICROPY_SSL_AXTLS = 0 @@ -23,12 +47,11 @@ FLASH_SIZE ?= 4MB CROSS_COMPILE ?= xtensa-esp32-elf- OBJDUMP = $(CROSS_COMPILE)objdump -# SDKCONFIG should be overridden to get a different configuration -SDKCONFIG ?= boards/sdkconfig +SDKCONFIG_COMBINED = $(BUILD)/sdkconfig.combined SDKCONFIG_H = $(BUILD)/sdkconfig.h # the git hash of the currently supported ESP IDF version -ESPIDF_SUPHASH := 6b3da6b1882f3b72e904cc90be67e9c4e3f369a9 +ESPIDF_SUPHASH := 6ccb4cf5b7d1fdddb8c2492f9cbc926abaf230df # paths to ESP IDF and its components ifeq ($(ESPIDF),) @@ -75,6 +98,7 @@ INC += -I$(BUILD) INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include_bootloader +INC_ESPCOMP += -I$(ESPCOMP)/console INC_ESPCOMP += -I$(ESPCOMP)/driver/include INC_ESPCOMP += -I$(ESPCOMP)/driver/include/driver INC_ESPCOMP += -I$(ESPCOMP)/nghttp/port/include @@ -104,6 +128,9 @@ INC_ESPCOMP += -I$(ESPCOMP)/lwip/port/esp32/include INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/mbedtls/include INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/port/include +INC_ESPCOMP += -I$(ESPCOMP)/mdns/include +INC_ESPCOMP += -I$(ESPCOMP)/mdns/private_include +INC_ESPCOMP += -I$(ESPCOMP)/micro-ecc/micro-ecc INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/include INC_ESPCOMP += -I$(ESPCOMP)/ulp/include INC_ESPCOMP += -I$(ESPCOMP)/vfs/include @@ -128,6 +155,7 @@ CFLAGS_BASE = -std=gnu99 $(CFLAGS_COMMON) -DMBEDTLS_CONFIG_FILE='"mbedtls/esp_co CFLAGS = $(CFLAGS_BASE) $(INC) $(INC_ESPCOMP) CFLAGS += -DIDF_VER=\"$(IDF_VER)\" CFLAGS += $(CFLAGS_MOD) $(CFLAGS_EXTRA) +CFLAGS += -I$(BOARD_DIR) # this is what ESPIDF uses for c++ compilation CXXFLAGS = -std=gnu++11 $(CFLAGS_COMMON) $(INC) $(INC_ESPCOMP) @@ -191,6 +219,7 @@ SRC_C = \ network_ppp.c \ modsocket.c \ modesp.c \ + esp32_partition.c \ esp32_ulp.c \ modesp32.c \ espneopixel.c \ @@ -199,6 +228,7 @@ SRC_C = \ mpthreadport.c \ machine_rtc.c \ machine_sdcard.c \ + $(wildcard $(BOARD_DIR)/*.c) \ $(SRC_MOD) EXTMOD_SRC_C = $(addprefix extmod/,\ @@ -266,7 +296,11 @@ SRC_QSTR_AUTO_DEPS += ################################################################################ # Generate sdkconfig.h from sdkconfig -$(SDKCONFIG_H): $(SDKCONFIG) +$(SDKCONFIG_COMBINED): $(SDKCONFIG) + $(Q)$(MKDIR) -p $(dir $@) + $(Q)$(CAT) $^ > $@ + +$(SDKCONFIG_H): $(SDKCONFIG_COMBINED) $(ECHO) "GEN $@" $(Q)$(MKDIR) -p $(dir $@) $(Q)$(PYTHON) $(ESPIDF)/tools/kconfig_new/confgen.py \ @@ -279,11 +313,15 @@ $(SDKCONFIG_H): $(SDKCONFIG) --env "COMPONENT_KCONFIGS_PROJBUILD=$(ESPCOMP_KCONFIGS_PROJBUILD)" $(Q)touch $@ -$(HEADER_BUILD)/qstrdefs.generated.h: $(SDKCONFIG_H) +$(HEADER_BUILD)/qstrdefs.generated.h: $(SDKCONFIG_H) $(BOARD_DIR)/mpconfigboard.h ################################################################################ # List of object files from the ESP32 IDF components +ESPIDF_BOOTLOADER_SUPPORT_O = $(patsubst %.c,%.o,\ + $(filter-out $(ESPCOMP)/bootloader_support/src/bootloader_init.c,\ + $(wildcard $(ESPCOMP)/bootloader_support/src/*.c))) + ESPIDF_DRIVER_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/driver/*.c)) ESPIDF_EFUSE_O = $(patsubst %.c,%.o,\ @@ -373,6 +411,8 @@ ESPIDF_MBEDTLS_O = $(patsubst %.c,%.o,\ $(wildcard $(ESPCOMP)/mbedtls/port/*.c) \ ) +ESPIDF_MDNS_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/mdns/*.c)) + $(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DEMBEDDED_SUPP -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_MSCHAPv2 -DEAP_TTLS -DEAP_TLS -DEAP_PEAP -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DALLOW_EVEN_MOD -D__ets__ -Wno-strict-aliasing ESPIDF_WPA_SUPPLICANT_O = $(patsubst %.c,%.o,\ $(wildcard $(ESPCOMP)/wpa_supplicant/port/*.c) \ @@ -393,6 +433,7 @@ $(BUILD_ESPIDF_LIB)/$(1)/lib$(1).a: $(addprefix $$(BUILD)/,$(2)) $(Q)$(AR) cru $$@ $$^ endef +$(eval $(call gen_espidf_lib_rule,bootloader_support,$(ESPIDF_BOOTLOADER_SUPPORT_O))) $(eval $(call gen_espidf_lib_rule,driver,$(ESPIDF_DRIVER_O))) $(eval $(call gen_espidf_lib_rule,efuse,$(ESPIDF_EFUSE_O))) $(eval $(call gen_espidf_lib_rule,esp32,$(ESPIDF_ESP32_O))) @@ -417,6 +458,7 @@ $(eval $(call gen_espidf_lib_rule,spi_flash,$(ESPIDF_SPI_FLASH_O))) $(eval $(call gen_espidf_lib_rule,ulp,$(ESPIDF_ULP_O))) $(eval $(call gen_espidf_lib_rule,lwip,$(ESPIDF_LWIP_O))) $(eval $(call gen_espidf_lib_rule,mbedtls,$(ESPIDF_MBEDTLS_O))) +$(eval $(call gen_espidf_lib_rule,mdns,$(ESPIDF_MDNS_O))) $(eval $(call gen_espidf_lib_rule,wpa_supplicant,$(ESPIDF_WPA_SUPPLICANT_O))) $(eval $(call gen_espidf_lib_rule,sdmmc,$(ESPIDF_SDMMC_O))) @@ -450,12 +492,12 @@ $(eval $(foreach lib,$(LIB_ESPIDF),$(eval $(call gen_sections_info_rule,$(BUILD_ $(LDGEN_SECTION_INFOS): $(LDGEN_SECTIONS_INFO) $(ESPIDF)/make/ldgen.mk $(Q)printf "$(foreach info,$(LDGEN_SECTIONS_INFO),$(info)\n)" > $@ -$(BUILD)/esp32.project.ld: $(ESPCOMP)/esp32/ld/esp32.project.ld.in $(LDGEN_FRAGMENTS) $(SDKCONFIG) $(LDGEN_SECTION_INFOS) +$(BUILD)/esp32.project.ld: $(ESPCOMP)/esp32/ld/esp32.project.ld.in $(LDGEN_FRAGMENTS) $(SDKCONFIG_COMBINED) $(LDGEN_SECTION_INFOS) $(ECHO) "GEN $@" $(Q)$(PYTHON) $(ESPIDF)/tools/ldgen/ldgen.py \ --input $< \ --output $@ \ - --config $(SDKCONFIG) \ + --config $(SDKCONFIG_COMBINED) \ --kconfig $(ESPIDF)/Kconfig \ --fragments $(LDGEN_FRAGMENTS) \ --sections $(LDGEN_SECTION_INFOS) \ @@ -493,6 +535,7 @@ OBJ = $(OBJ_MP) APP_LD_ARGS = APP_LD_ARGS += $(LDFLAGS_MOD) +APP_LD_ARGS += $(addprefix -T,$(LD_FILES)) APP_LD_ARGS += --start-group APP_LD_ARGS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc APP_LD_ARGS += -L$(dir $(LIBSTDCXX_FILE_NAME)) -lstdc++ @@ -674,7 +717,9 @@ $(BUILD)/bootloader.elf: $(BOOTLOADER_OBJ) $(addprefix $(BOOTLOADER_LIB_DIR)/lib # Declarations to build the partitions PYTHON2 ?= python2 -PART_SRC = partitions.csv + +# Can be overriden by mkconfigboard.mk. +PART_SRC ?= partitions.csv $(BUILD)/partitions.bin: $(PART_SRC) $(ECHO) "Create $@" diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 12144d822..31347b6a2 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -74,11 +74,11 @@ variables for the build. In that case, create a new file in the esp32 directory called `makefile` and add the following lines to that file: ``` ESPIDF = +BOARD = GENERIC #PORT = /dev/ttyUSB0 #FLASH_MODE = qio #FLASH_SIZE = 4MB #CROSS_COMPILE = xtensa-esp32-elf- -#SDKCONFIG = boards/sdkconfig.spiram include Makefile ``` @@ -92,16 +92,18 @@ are `PORT` for the serial port of your esp32 module, and `FLASH_MODE` (which may need to be `dio` for some modules) and `FLASH_SIZE`. See the Makefile for further information. -The default ESP IDF configuration settings are provided in the file -`boards/sdkconfig`, and this file is specified in the build by the make -variable `SDKCONFIG`. To use a custom configuration either set `SDKCONFIG` -in your custom `makefile` (or `GNUmakefile`) or set this variable on the -command line: +The default ESP IDF configuration settings are provided by the `GENERIC` +board definition in the directory `boards/GENERIC`. For a custom configuration +you can define your own board directory. + +The `BOARD` variable can be set on the make command line: ```bash -$ make SDKCONFIG=sdkconfig.myboard +$ make BOARD=TINYPICO ``` -The file `boards/sdkconfig.spiram` is provided for ESP32 modules that have -external SPIRAM. +or added to your custom `makefile` (or `GNUmakefile`) described above. There +is also a `GENERIC_SPIRAM` board for for ESP32 modules that have external +SPIRAM, but prefer to use a specific board target (or define your own as +necessary). Building the firmware --------------------- diff --git a/ports/esp32/boards/GENERIC/mpconfigboard.h b/ports/esp32/boards/GENERIC/mpconfigboard.h new file mode 100644 index 000000000..644807f78 --- /dev/null +++ b/ports/esp32/boards/GENERIC/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "ESP32 module" +#define MICROPY_HW_MCU_NAME "ESP32" diff --git a/ports/esp32/boards/GENERIC/mpconfigboard.mk b/ports/esp32/boards/GENERIC/mpconfigboard.mk new file mode 100644 index 000000000..fc49d2a8c --- /dev/null +++ b/ports/esp32/boards/GENERIC/mpconfigboard.mk @@ -0,0 +1 @@ +SDKCONFIG += boards/sdkconfig.base diff --git a/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.h b/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.h new file mode 100644 index 000000000..8923d46fd --- /dev/null +++ b/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "Generic ESP32-D2WD module" +#define MICROPY_HW_MCU_NAME "ESP32-D2WD" diff --git a/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.mk b/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.mk new file mode 100644 index 000000000..65de5dcd0 --- /dev/null +++ b/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.mk @@ -0,0 +1,5 @@ +SDKCONFIG += boards/sdkconfig.base +PART_SRC = partitions-2MiB.csv +FLASH_SIZE = 2MB +FLASH_MODE = dio +FLASH_FREQ = 40m diff --git a/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.h b/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.h new file mode 100644 index 000000000..a5982e47e --- /dev/null +++ b/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "ESP32 module (spiram)" +#define MICROPY_HW_MCU_NAME "ESP32" diff --git a/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.mk b/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.mk new file mode 100644 index 000000000..59aa75f85 --- /dev/null +++ b/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.mk @@ -0,0 +1,2 @@ +SDKCONFIG += boards/sdkconfig.base +SDKCONFIG += boards/sdkconfig.spiram diff --git a/ports/esp32/boards/TINYPICO/mpconfigboard.h b/ports/esp32/boards/TINYPICO/mpconfigboard.h new file mode 100644 index 000000000..e63f43ed2 --- /dev/null +++ b/ports/esp32/boards/TINYPICO/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "TinyPICO" +#define MICROPY_HW_MCU_NAME "ESP32-PICO-D4" diff --git a/ports/esp32/boards/TINYPICO/mpconfigboard.mk b/ports/esp32/boards/TINYPICO/mpconfigboard.mk new file mode 100644 index 000000000..2efdba0f3 --- /dev/null +++ b/ports/esp32/boards/TINYPICO/mpconfigboard.mk @@ -0,0 +1,5 @@ +FLASH_FREQ = 80m + +SDKCONFIG += boards/sdkconfig.base +SDKCONFIG += boards/sdkconfig.spiram +SDKCONFIG += boards/TINYPICO/sdkconfig.board diff --git a/ports/esp32/boards/TINYPICO/sdkconfig.board b/ports/esp32/boards/TINYPICO/sdkconfig.board new file mode 100644 index 000000000..ea4fc9b5c --- /dev/null +++ b/ports/esp32/boards/TINYPICO/sdkconfig.board @@ -0,0 +1,3 @@ +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_SPIRAM_SPEED_80M=y diff --git a/ports/esp32/boards/sdkconfig b/ports/esp32/boards/sdkconfig.base similarity index 100% rename from ports/esp32/boards/sdkconfig rename to ports/esp32/boards/sdkconfig.base diff --git a/ports/esp32/boards/sdkconfig.spiram b/ports/esp32/boards/sdkconfig.spiram index 884c48c0f..53950e587 100644 --- a/ports/esp32/boards/sdkconfig.spiram +++ b/ports/esp32/boards/sdkconfig.spiram @@ -1,34 +1,5 @@ # MicroPython on ESP32, ESP IDF configuration with SPIRAM support -# The following options override the defaults -CONFIG_IDF_TARGET="esp32" - -# Application manager -CONFIG_APP_EXCLUDE_PROJECT_VER_VAR=y -CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR=y - -# Bootloader config -CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y - -# ESP32-specific -CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y CONFIG_SPIRAM_SUPPORT=y CONFIG_SPIRAM_IGNORE_NOTFOUND=y CONFIG_SPIRAM_USE_MEMMAP=y -CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=n -CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=n -CONFIG_ESP32_XTAL_FREQ_AUTO=y -CONFIG_FREERTOS_INTERRUPT_BACKTRACE=n - -# Power Management -CONFIG_PM_ENABLE=y - -# FreeRTOS -CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 -CONFIG_SUPPORT_STATIC_ALLOCATION=y -CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK=y - -# UDP -CONFIG_PPP_SUPPORT=y -CONFIG_PPP_PAP_SUPPORT=y -CONFIG_PPP_CHAP_SUPPORT=y diff --git a/ports/esp32/esp32_partition.c b/ports/esp32/esp32_partition.c new file mode 100644 index 000000000..49bb0632e --- /dev/null +++ b/ports/esp32/esp32_partition.c @@ -0,0 +1,223 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "modesp32.h" +#include "esp_ota_ops.h" + +// esp_partition_read and esp_partition_write can operate on arbitrary bytes +// but esp_partition_erase_range operates on 4k blocks. But to make a partition +// implement the standard block protocol all operations are done on 4k blocks. +#define BLOCK_SIZE_BYTES (4096) + +enum { + ESP32_PARTITION_BOOT, + ESP32_PARTITION_RUNNING, +}; + +typedef struct _esp32_partition_obj_t { + mp_obj_base_t base; + const esp_partition_t *part; +} esp32_partition_obj_t; + +static inline void check_esp_err(esp_err_t e) { + if (e != ESP_OK) { + mp_raise_OSError(-e); + } +} + +STATIC esp32_partition_obj_t *esp32_partition_new(const esp_partition_t *part) { + if (part == NULL) { + mp_raise_OSError(MP_ENOENT); + } + esp32_partition_obj_t *self = m_new_obj(esp32_partition_obj_t); + self->base.type = &esp32_partition_type; + self->part = part; + return self; +} + +STATIC void esp32_partition_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", + self->part->type, self->part->subtype, + self->part->address, self->part->size, + &self->part->label[0], self->part->encrypted + ); +} + +STATIC mp_obj_t esp32_partition_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check args + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + // Get requested partition + const esp_partition_t *part; + if (mp_obj_is_int(all_args[0])) { + // Integer given, get that particular partition + switch (mp_obj_get_int(all_args[0])) { + case ESP32_PARTITION_BOOT: + part = esp_ota_get_boot_partition(); + break; + case ESP32_PARTITION_RUNNING: + part = esp_ota_get_running_partition(); + break; + default: + mp_raise_ValueError(NULL); + } + } else { + // String given, search for partition with that label + const char *label = mp_obj_str_get_str(all_args[0]); + part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, label); + if (part == NULL) { + part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, label); + } + } + + // Return new object + return MP_OBJ_FROM_PTR(esp32_partition_new(part)); +} + +STATIC mp_obj_t esp32_partition_find(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // Parse args + enum { ARG_type, ARG_subtype, ARG_label }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_type, MP_ARG_INT, {.u_int = ESP_PARTITION_TYPE_APP} }, + { MP_QSTR_subtype, MP_ARG_INT, {.u_int = ESP_PARTITION_SUBTYPE_ANY} }, + { MP_QSTR_label, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get optional label string + const char *label = NULL; + if (args[ARG_label].u_obj != mp_const_none) { + label = mp_obj_str_get_str(args[ARG_label].u_obj); + } + + // Build list of matching partitions + mp_obj_t list = mp_obj_new_list(0, NULL); + esp_partition_iterator_t iter = esp_partition_find(args[ARG_type].u_int, args[ARG_subtype].u_int, label); + while (iter != NULL) { + mp_obj_list_append(list, MP_OBJ_FROM_PTR(esp32_partition_new(esp_partition_get(iter)))); + iter = esp_partition_next(iter); + } + esp_partition_iterator_release(iter); + + return list; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp32_partition_find_fun_obj, 0, esp32_partition_find); +STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(esp32_partition_find_obj, MP_ROM_PTR(&esp32_partition_find_fun_obj)); + +STATIC mp_obj_t esp32_partition_info(mp_obj_t self_in) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t tuple[] = { + MP_OBJ_NEW_SMALL_INT(self->part->type), + MP_OBJ_NEW_SMALL_INT(self->part->subtype), + mp_obj_new_int_from_uint(self->part->address), + mp_obj_new_int_from_uint(self->part->size), + mp_obj_new_str(&self->part->label[0], strlen(&self->part->label[0])), + mp_obj_new_bool(self->part->encrypted), + }; + return mp_obj_new_tuple(6, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_info_obj, esp32_partition_info); + +STATIC mp_obj_t esp32_partition_readblocks(mp_obj_t self_in, mp_obj_t block_num, mp_obj_t buf_in) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t offset = mp_obj_get_int(block_num) * BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + check_esp_err(esp_partition_read(self->part, offset, bufinfo.buf, bufinfo.len)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp32_partition_readblocks_obj, esp32_partition_readblocks); + +STATIC mp_obj_t esp32_partition_writeblocks(mp_obj_t self_in, mp_obj_t block_num, mp_obj_t buf_in) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t offset = mp_obj_get_int(block_num) * BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + check_esp_err(esp_partition_erase_range(self->part, offset, bufinfo.len)); + check_esp_err(esp_partition_write(self->part, offset, bufinfo.buf, bufinfo.len)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp32_partition_writeblocks_obj, esp32_partition_writeblocks); + +STATIC mp_obj_t esp32_partition_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t cmd = mp_obj_get_int(cmd_in); + switch (cmd) { + case BP_IOCTL_INIT: return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_DEINIT: return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_SYNC: return MP_OBJ_NEW_SMALL_INT(0); + case BP_IOCTL_SEC_COUNT: return MP_OBJ_NEW_SMALL_INT(self->part->size / BLOCK_SIZE_BYTES); + case BP_IOCTL_SEC_SIZE: return MP_OBJ_NEW_SMALL_INT(BLOCK_SIZE_BYTES); + default: return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp32_partition_ioctl_obj, esp32_partition_ioctl); + +STATIC mp_obj_t esp32_partition_set_boot(mp_obj_t self_in) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + esp_ota_set_boot_partition(self->part); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_set_boot_obj, esp32_partition_set_boot); + +STATIC mp_obj_t esp32_partition_get_next_update(mp_obj_t self_in) { + esp32_partition_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(esp32_partition_new(esp_ota_get_next_update_partition(self->part))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_partition_get_next_update_obj, esp32_partition_get_next_update); + +STATIC const mp_rom_map_elem_t esp32_partition_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&esp32_partition_find_obj) }, + + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&esp32_partition_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&esp32_partition_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&esp32_partition_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&esp32_partition_ioctl_obj) }, + + { MP_ROM_QSTR(MP_QSTR_set_boot), MP_ROM_PTR(&esp32_partition_set_boot_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_next_update), MP_ROM_PTR(&esp32_partition_get_next_update_obj) }, + + { MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_INT(ESP32_PARTITION_BOOT) }, + { MP_ROM_QSTR(MP_QSTR_RUNNING), MP_ROM_INT(ESP32_PARTITION_RUNNING) }, + { MP_ROM_QSTR(MP_QSTR_TYPE_APP), MP_ROM_INT(ESP_PARTITION_TYPE_APP) }, + { MP_ROM_QSTR(MP_QSTR_TYPE_DATA), MP_ROM_INT(ESP_PARTITION_TYPE_DATA) }, +}; +STATIC MP_DEFINE_CONST_DICT(esp32_partition_locals_dict, esp32_partition_locals_dict_table); + +const mp_obj_type_t esp32_partition_type = { + { &mp_type_type }, + .name = MP_QSTR_Partition, + .print = esp32_partition_print, + .make_new = esp32_partition_make_new, + .locals_dict = (mp_obj_dict_t*)&esp32_partition_locals_dict, +}; diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index d62f362e9..63e40448b 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -53,12 +53,15 @@ STATIC const madc_obj_t madc_obj[] = { {{&machine_adc_type}, GPIO_NUM_35, ADC1_CHANNEL_7}, }; +STATIC uint8_t adc_bit_width; + STATIC mp_obj_t madc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { static int initialized = 0; if (!initialized) { adc1_config_width(ADC_WIDTH_12Bit); + adc_bit_width = 12; initialized = 1; } @@ -79,6 +82,17 @@ STATIC void madc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ mp_printf(print, "ADC(Pin(%u))", self->gpio_id); } +// read_u16() +STATIC mp_obj_t madc_read_u16(mp_obj_t self_in) { + madc_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t raw = adc1_get_raw(self->adc1_id); + // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) + uint32_t u16 = raw << (16 - adc_bit_width) | raw >> (2 * adc_bit_width - 16); + return MP_OBJ_NEW_SMALL_INT(u16); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(madc_read_u16_obj, madc_read_u16); + +// Legacy method STATIC mp_obj_t madc_read(mp_obj_t self_in) { madc_obj_t *self = self_in; int val = adc1_get_raw(self->adc1_id); @@ -99,13 +113,24 @@ MP_DEFINE_CONST_FUN_OBJ_2(madc_atten_obj, madc_atten); STATIC mp_obj_t madc_width(mp_obj_t cls_in, mp_obj_t width_in) { adc_bits_width_t width = mp_obj_get_int(width_in); esp_err_t err = adc1_config_width(width); - if (err == ESP_OK) return mp_const_none; - mp_raise_ValueError("Parameter Error"); + if (err != ESP_OK) { + mp_raise_ValueError("Parameter Error"); + } + switch (width) { + case ADC_WIDTH_9Bit: adc_bit_width = 9; break; + case ADC_WIDTH_10Bit: adc_bit_width = 10; break; + case ADC_WIDTH_11Bit: adc_bit_width = 11; break; + case ADC_WIDTH_12Bit: adc_bit_width = 12; break; + default: break; + } + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(madc_width_fun_obj, madc_width); MP_DEFINE_CONST_CLASSMETHOD_OBJ(madc_width_obj, MP_ROM_PTR(&madc_width_fun_obj)); STATIC const mp_rom_map_elem_t madc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&madc_read_u16_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&madc_read_obj) }, { MP_ROM_QSTR(MP_QSTR_atten), MP_ROM_PTR(&madc_atten_obj) }, { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&madc_width_obj) }, diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 2e2d8236c..ada881167 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -154,6 +154,7 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_raw_temperature), MP_ROM_PTR(&esp32_raw_temperature_obj) }, { MP_ROM_QSTR(MP_QSTR_hall_sensor), MP_ROM_PTR(&esp32_hall_sensor_obj) }, + { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, { MP_ROM_QSTR(MP_QSTR_ULP), MP_ROM_PTR(&esp32_ulp_type) }, { MP_ROM_QSTR(MP_QSTR_WAKEUP_ALL_LOW), MP_ROM_PTR(&mp_const_false_obj) }, diff --git a/ports/esp32/modesp32.h b/ports/esp32/modesp32.h index 1d18cb41f..26eec8ae6 100644 --- a/ports/esp32/modesp32.h +++ b/ports/esp32/modesp32.h @@ -26,6 +26,7 @@ #define RTC_LAST_EXT_PIN 39 #define RTC_IS_VALID_EXT_PIN(pin_id) ((1ll << (pin_id)) & RTC_VALID_EXT_PINS) +extern const mp_obj_type_t esp32_partition_type; extern const mp_obj_type_t esp32_ulp_type; #endif // MICROPY_INCLUDED_ESP32_MODESP32_H diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 846257605..45ea5139c 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -48,6 +48,7 @@ #include "esp_event_loop.h" #include "lwip/dns.h" #include "tcpip_adapter.h" +#include "mdns.h" #include "modnetwork.h" @@ -128,6 +129,11 @@ static bool wifi_sta_connected = false; // Store the current status. 0 means None here, safe to do so as first enum value is WIFI_REASON_UNSPECIFIED=1. static uint8_t wifi_sta_disconn_reason = 0; +#if MICROPY_HW_ENABLE_MDNS_QUERIES || MICROPY_HW_ENABLE_MDNS_RESPONDER +// Whether mDNS has been initialised or not +static bool mdns_initialised = false; +#endif + // This function is called by the system-event task and so runs in a different // thread to the main MicroPython task. It must not raise any Python exceptions. static esp_err_t event_handler(void *ctx, system_event_t *event) { @@ -142,6 +148,20 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) { ESP_LOGI("network", "GOT_IP"); wifi_sta_connected = true; wifi_sta_disconn_reason = 0; // Success so clear error. (in case of new error will be replaced anyway) + #if MICROPY_HW_ENABLE_MDNS_QUERIES || MICROPY_HW_ENABLE_MDNS_RESPONDER + if (!mdns_initialised) { + mdns_init(); + #if MICROPY_HW_ENABLE_MDNS_RESPONDER + const char *hostname = NULL; + if (tcpip_adapter_get_hostname(WIFI_IF_STA, &hostname) != ESP_OK || hostname == NULL) { + hostname = "esp32"; + } + mdns_hostname_set(hostname); + mdns_instance_name_set(hostname); + #endif + mdns_initialised = true; + } + #endif break; case SYSTEM_EVENT_STA_DISCONNECTED: { // This is a workaround as ESP32 WiFi libs don't currently @@ -530,17 +550,24 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); - // get the config for the interface + bool is_wifi = self->if_id == WIFI_IF_AP || self->if_id == WIFI_IF_STA; + wifi_config_t cfg; - ESP_EXCEPTIONS(esp_wifi_get_config(self->if_id, &cfg)); + if (is_wifi) { + ESP_EXCEPTIONS(esp_wifi_get_config(self->if_id, &cfg)); + } + + #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) if (kwargs->used != 0) { + if (!is_wifi) { + goto unknown; + } for (size_t i = 0; i < kwargs->alloc; i++) { if (mp_map_slot_is_filled(kwargs, i)) { int req_if = -1; - #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) switch ((uintptr_t)kwargs->table[i].key) { case QS(MP_QSTR_mac): { mp_buffer_info_t bufinfo; @@ -592,7 +619,6 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs default: goto unknown; } - #undef QS // We post-check interface requirements to save on code size if (req_if >= 0) { @@ -613,20 +639,34 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs } int req_if = -1; - mp_obj_t val; + mp_obj_t val = mp_const_none; - #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) switch ((uintptr_t)args[1]) { case QS(MP_QSTR_mac): { uint8_t mac[6]; - ESP_EXCEPTIONS(esp_wifi_get_mac(self->if_id, mac)); - return mp_obj_new_bytes(mac, sizeof(mac)); + switch (self->if_id) { + case WIFI_IF_AP: // fallthrough intentional + case WIFI_IF_STA: + ESP_EXCEPTIONS(esp_wifi_get_mac(self->if_id, mac)); + return mp_obj_new_bytes(mac, sizeof(mac)); + + case ESP_IF_ETH: + esp_eth_get_mac(mac); + return mp_obj_new_bytes(mac, sizeof(mac)); + default: + goto unknown; + } } case QS(MP_QSTR_essid): - if (self->if_id == WIFI_IF_STA) { - val = mp_obj_new_str((char*)cfg.sta.ssid, strlen((char*)cfg.sta.ssid)); - } else { - val = mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len); + switch (self->if_id) { + case WIFI_IF_STA: + val = mp_obj_new_str((char*)cfg.sta.ssid, strlen((char*)cfg.sta.ssid)); + break; + case WIFI_IF_AP: + val = mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len); + break; + default: + req_if = WIFI_IF_AP; } break; case QS(MP_QSTR_hidden): @@ -650,6 +690,7 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs default: goto unknown; } + #undef QS // We post-check interface requirements to save on code size @@ -662,8 +703,7 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs unknown: mp_raise_ValueError("unknown config param"); } - -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_config_obj, 1, esp_config); +MP_DEFINE_CONST_FUN_OBJ_KW(esp_config_obj, 1, esp_config); STATIC const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&esp_active_obj) }, diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h index f39a2919d..64d2da018 100644 --- a/ports/esp32/modnetwork.h +++ b/ports/esp32/modnetwork.h @@ -31,6 +31,7 @@ enum { PHY_LAN8720, PHY_TLK110 }; MP_DECLARE_CONST_FUN_OBJ_KW(get_lan_obj); MP_DECLARE_CONST_FUN_OBJ_1(ppp_make_new_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(esp_config_obj); void usocket_events_deinit(void); diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 8b80e631d..a6f29718d 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -47,6 +47,7 @@ #include "py/mperrno.h" #include "lib/netutils/netutils.h" #include "tcpip_adapter.h" +#include "mdns.h" #include "modnetwork.h" #include "lwip/sockets.h" @@ -56,6 +57,8 @@ #include "esp_log.h" #define SOCKET_POLL_US (100000) +#define MDNS_QUERY_TIMEOUT_MS (5000) +#define MDNS_LOCAL_SUFFIX ".local" typedef struct _socket_obj_t { mp_obj_base_t base; @@ -150,6 +153,58 @@ static inline void check_for_exceptions(void) { mp_handle_pending(); } +// This function mimics lwip_getaddrinfo, with added support for mDNS queries +static int _socket_getaddrinfo3(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) { + + #if MICROPY_HW_ENABLE_MDNS_QUERIES + int nodename_len = strlen(nodename); + const int local_len = sizeof(MDNS_LOCAL_SUFFIX) - 1; + if (nodename_len > local_len + && strcasecmp(nodename + nodename_len - local_len, MDNS_LOCAL_SUFFIX) == 0) { + // mDNS query + char nodename_no_local[nodename_len - local_len + 1]; + memcpy(nodename_no_local, nodename, nodename_len - local_len); + nodename_no_local[nodename_len - local_len] = '\0'; + + struct ip4_addr addr = {0}; + esp_err_t err = mdns_query_a(nodename_no_local, MDNS_QUERY_TIMEOUT_MS, &addr); + if (err != ESP_OK) { + if (err == ESP_ERR_NOT_FOUND){ + *res = NULL; + return 0; + } + *res = NULL; + return err; + } + + struct addrinfo *ai = memp_malloc(MEMP_NETDB); + if (ai == NULL) { + *res = NULL; + return EAI_MEMORY; + } + memset(ai, 0, sizeof(struct addrinfo) + sizeof(struct sockaddr_storage)); + + struct sockaddr_in *sa = (struct sockaddr_in*)((uint8_t*)ai + sizeof(struct addrinfo)); + inet_addr_from_ip4addr(&sa->sin_addr, &addr); + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = lwip_htons((u16_t)atoi(servname)); + ai->ai_family = AF_INET; + ai->ai_canonname = ((char*)sa + sizeof(struct sockaddr_storage)); + memcpy(ai->ai_canonname, nodename, nodename_len + 1); + ai->ai_addrlen = sizeof(struct sockaddr_storage); + ai->ai_addr = (struct sockaddr*)sa; + + *res = ai; + return 0; + } + #endif + + // Normal query + return lwip_getaddrinfo(nodename, servname, hints, res); +} + static int _socket_getaddrinfo2(const mp_obj_t host, const mp_obj_t portx, struct addrinfo **resp) { const struct addrinfo hints = { .ai_family = AF_INET, @@ -172,7 +227,7 @@ static int _socket_getaddrinfo2(const mp_obj_t host, const mp_obj_t portx, struc } MP_THREAD_GIL_EXIT(); - int res = lwip_getaddrinfo(host_str, port_str, &hints, resp); + int res = _socket_getaddrinfo3(host_str, port_str, &hints, resp); MP_THREAD_GIL_ENTER(); return res; diff --git a/ports/esp32/modules/flashbdev.py b/ports/esp32/modules/flashbdev.py index 935f5342f..45b8686c5 100644 --- a/ports/esp32/modules/flashbdev.py +++ b/ports/esp32/modules/flashbdev.py @@ -1,34 +1,4 @@ -import esp +from esp32 import Partition -class FlashBdev: - - SEC_SIZE = 4096 - START_SEC = esp.flash_user_start() // SEC_SIZE - - def __init__(self, blocks): - self.blocks = blocks - - def readblocks(self, n, buf): - #print("readblocks(%s, %x(%d))" % (n, id(buf), len(buf))) - esp.flash_read((n + self.START_SEC) * self.SEC_SIZE, buf) - - def writeblocks(self, n, buf): - #print("writeblocks(%s, %x(%d))" % (n, id(buf), len(buf))) - #assert len(buf) <= self.SEC_SIZE, len(buf) - esp.flash_erase(n + self.START_SEC) - esp.flash_write((n + self.START_SEC) * self.SEC_SIZE, buf) - - def ioctl(self, op, arg): - #print("ioctl(%d, %r)" % (op, arg)) - if op == 4: # BP_IOCTL_SEC_COUNT - return self.blocks - if op == 5: # BP_IOCTL_SEC_SIZE - return self.SEC_SIZE - -size = esp.flash_size() -if size < 1024*1024: - # flash too small for a filesystem - bdev = None -else: - # for now we use a fixed size for the filesystem - bdev = FlashBdev(2048 * 1024 // FlashBdev.SEC_SIZE) +bdev = Partition.find(Partition.TYPE_DATA, label='vfs') +bdev = bdev[0] if bdev else None diff --git a/ports/esp32/modules/inisetup.py b/ports/esp32/modules/inisetup.py index 0e9c72fe8..3196f0c6f 100644 --- a/ports/esp32/modules/inisetup.py +++ b/ports/esp32/modules/inisetup.py @@ -2,7 +2,7 @@ import uos from flashbdev import bdev def check_bootsec(): - buf = bytearray(bdev.SEC_SIZE) + buf = bytearray(bdev.ioctl(5, 0)) # 5 is SEC_SIZE bdev.readblocks(0, buf) empty = True for b in buf: @@ -29,8 +29,7 @@ def setup(): print("Performing initial setup") uos.VfsFat.mkfs(bdev) vfs = uos.VfsFat(bdev) - uos.mount(vfs, '/flash') - uos.chdir('/flash') + uos.mount(vfs, '/') with open("boot.py", "w") as f: f.write("""\ # This file is executed on every boot (including wake-boot from deepsleep) diff --git a/ports/esp32/modules/uftpd.py b/ports/esp32/modules/uftpd.py new file mode 100644 index 000000000..7b7b0c9c8 --- /dev/null +++ b/ports/esp32/modules/uftpd.py @@ -0,0 +1,486 @@ +# +# Small ftp server for ESP8266 Micropython +# Based on the work of chrisgp - Christopher Popp and pfalcon - Paul Sokolovsky +# +# The server accepts passive mode only. It runs in background. +# Start the server with: +# +# import uftpd +# uftpd.start([port = 21][, verbose = level]) +# +# port is the port number (default 21) +# verbose controls the level of printed activity messages, values 0, 1, 2 +# +# Copyright (c) 2016 Christopher Popp (initial ftp server framework) +# Copyright (c) 2016 Paul Sokolovsky (background execution control structure) +# Copyright (c) 2016 Robert Hammelrath (putting the pieces together and a +# few extensions) +# Distributed under MIT License +# +import socket +import network +import uos +import gc +from time import sleep_ms, localtime +from micropython import alloc_emergency_exception_buf + +# constant definitions +_CHUNK_SIZE = const(1024) +_SO_REGISTER_HANDLER = const(20) +_COMMAND_TIMEOUT = const(300) +_DATA_TIMEOUT = const(100) +_DATA_PORT = const(13333) + +# Global variables +ftpsocket = None +datasocket = None +client_list = [] +verbose_l = 0 +client_busy = False +# Interfaces: (IP-Address (string), IP-Address (integer), Netmask (integer)) +AP_addr = ("0.0.0.0", 0, 0xffffff00) +STA_addr = ("0.0.0.0", 0, 0xffffff00) + +_month_name = ("", "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") + + +class FTP_client: + + def __init__(self, ftpsocket): + global AP_addr, STA_addr + self.command_client, self.remote_addr = ftpsocket.accept() + self.remote_addr = self.remote_addr[0] + self.command_client.settimeout(_COMMAND_TIMEOUT) + log_msg(1, "FTP Command connection from:", self.remote_addr) + self.command_client.setsockopt(socket.SOL_SOCKET, + _SO_REGISTER_HANDLER, + self.exec_ftp_command) + self.command_client.sendall("220 Hello, this is the ESP8266.\r\n") + self.cwd = '/' + self.fromname = None +# self.logged_in = False + self.act_data_addr = self.remote_addr + self.DATA_PORT = 20 + self.active = True + # check which interface was used by comparing the caller's ip + # adress with the ip adresses of STA and AP; consider netmask; + # select IP address for passive mode + if ((AP_addr[1] & AP_addr[2]) == + (num_ip(self.remote_addr) & AP_addr[2])): + self.pasv_data_addr = AP_addr[0] + elif ((STA_addr[1] & STA_addr[2]) == + (num_ip(self.remote_addr) & STA_addr[2])): + self.pasv_data_addr = STA_addr[0] + else: + self.pasv_data_addr = "0.0.0.0" # Ivalid value + + def send_list_data(self, path, data_client, full): + try: + for fname in uos.listdir(path): + data_client.sendall(self.make_description(path, fname, full)) + except: # path may be a file name or pattern + path, pattern = self.split_path(path) + try: + for fname in uos.listdir(path): + if self.fncmp(fname, pattern): + data_client.sendall( + self.make_description(path, fname, full)) + except: + pass + + def make_description(self, path, fname, full): + global _month_name + if full: + stat = uos.stat(self.get_absolute_path(path, fname)) + file_permissions = ("drwxr-xr-x" + if (stat[0] & 0o170000 == 0o040000) + else "-rw-r--r--") + file_size = stat[6] + tm = localtime(stat[7]) + if tm[0] != localtime()[0]: + description = "{} 1 owner group {:>10} {} {:2} {:>5} {}\r\n".\ + format(file_permissions, file_size, + _month_name[tm[1]], tm[2], tm[0], fname) + else: + description = "{} 1 owner group {:>10} {} {:2} {:02}:{:02} {}\r\n".\ + format(file_permissions, file_size, + _month_name[tm[1]], tm[2], tm[3], tm[4], fname) + else: + description = fname + "\r\n" + return description + + def send_file_data(self, path, data_client): + with open(path, "r") as file: + chunk = file.read(_CHUNK_SIZE) + while len(chunk) > 0: + data_client.sendall(chunk) + chunk = file.read(_CHUNK_SIZE) + data_client.close() + + def save_file_data(self, path, data_client, mode): + with open(path, mode) as file: + chunk = data_client.recv(_CHUNK_SIZE) + while len(chunk) > 0: + file.write(chunk) + chunk = data_client.recv(_CHUNK_SIZE) + data_client.close() + + def get_absolute_path(self, cwd, payload): + # Just a few special cases "..", "." and "" + # If payload start's with /, set cwd to / + # and consider the remainder a relative path + if payload.startswith('/'): + cwd = "/" + for token in payload.split("/"): + if token == '..': + cwd = self.split_path(cwd)[0] + elif token != '.' and token != '': + if cwd == '/': + cwd += token + else: + cwd = cwd + '/' + token + return cwd + + def split_path(self, path): # instead of path.rpartition('/') + tail = path.split('/')[-1] + head = path[:-(len(tail) + 1)] + return ('/' if head == '' else head, tail) + + # compare fname against pattern. Pattern may contain + # the wildcards ? and *. + def fncmp(self, fname, pattern): + pi = 0 + si = 0 + while pi < len(pattern) and si < len(fname): + if (fname[si] == pattern[pi]) or (pattern[pi] == '?'): + si += 1 + pi += 1 + else: + if pattern[pi] == '*': # recurse + if pi == len(pattern.rstrip("*?")): # only wildcards left + return True + while si < len(fname): + if self.fncmp(fname[si:], pattern[pi + 1:]): + return True + else: + si += 1 + return False + else: + return False + if pi == len(pattern.rstrip("*")) and si == len(fname): + return True + else: + return False + + def open_dataclient(self): + if self.active: # active mode + data_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + data_client.settimeout(_DATA_TIMEOUT) + data_client.connect((self.act_data_addr, self.DATA_PORT)) + log_msg(1, "FTP Data connection with:", self.act_data_addr) + else: # passive mode + data_client, data_addr = datasocket.accept() + log_msg(1, "FTP Data connection with:", data_addr[0]) + return data_client + + def exec_ftp_command(self, cl): + global datasocket + global client_busy + global my_ip_addr + + try: + gc.collect() + + data = cl.readline().decode("utf-8").rstrip("\r\n") + + if len(data) <= 0: + # No data, close + # This part is NOT CLEAN; there is still a chance that a + # closing data connection will be signalled as closing + # command connection + log_msg(1, "*** No data, assume QUIT") + close_client(cl) + return + + if client_busy: # check if another client is busy + cl.sendall("400 Device busy.\r\n") # tell so the remote client + return # and quit + client_busy = True # now it's my turn + + # check for log-in state may done here, like + # if self.logged_in == False and not command in\ + # ("USER", "PASS", "QUIT"): + # cl.sendall("530 Not logged in.\r\n") + # return + + command = data.split()[0].upper() + payload = data[len(command):].lstrip() # partition is missing + path = self.get_absolute_path(self.cwd, payload) + log_msg(1, "Command={}, Payload={}".format(command, payload)) + + if command == "USER": + # self.logged_in = True + cl.sendall("230 Logged in.\r\n") + # If you want to see a password,return + # "331 Need password.\r\n" instead + # If you want to reject an user, return + # "530 Not logged in.\r\n" + elif command == "PASS": + # you may check here for a valid password and return + # "530 Not logged in.\r\n" in case it's wrong + # self.logged_in = True + cl.sendall("230 Logged in.\r\n") + elif command == "SYST": + cl.sendall("215 UNIX Type: L8\r\n") + elif command in ("TYPE", "NOOP", "ABOR"): # just accept & ignore + cl.sendall('200 OK\r\n') + elif command == "QUIT": + cl.sendall('221 Bye.\r\n') + close_client(cl) + elif command == "PWD" or command == "XPWD": + cl.sendall('257 "{}"\r\n'.format(self.cwd)) + elif command == "CWD" or command == "XCWD": + try: + if (uos.stat(path)[0] & 0o170000) == 0o040000: + self.cwd = path + cl.sendall('250 OK\r\n') + else: + cl.sendall('550 Fail\r\n') + except: + cl.sendall('550 Fail\r\n') + elif command == "PASV": + cl.sendall('227 Entering Passive Mode ({},{},{}).\r\n'.format( + self.pasv_data_addr.replace('.', ','), + _DATA_PORT >> 8, _DATA_PORT % 256)) + self.active = False + elif command == "PORT": + items = payload.split(",") + if len(items) >= 6: + self.act_data_addr = '.'.join(items[:4]) + if self.act_data_addr == "127.0.1.1": + # replace by command session addr + self.act_data_addr = self.remote_addr + self.DATA_PORT = int(items[4]) * 256 + int(items[5]) + cl.sendall('200 OK\r\n') + self.active = True + else: + cl.sendall('504 Fail\r\n') + elif command == "LIST" or command == "NLST": + if payload.startswith("-"): + option = payload.split()[0].lower() + path = self.get_absolute_path( + self.cwd, payload[len(option):].lstrip()) + else: + option = "" + try: + data_client = self.open_dataclient() + cl.sendall("150 Directory listing:\r\n") + self.send_list_data(path, data_client, + command == "LIST" or 'l' in option) + cl.sendall("226 Done.\r\n") + data_client.close() + except: + cl.sendall('550 Fail\r\n') + if data_client is not None: + data_client.close() + elif command == "RETR": + try: + data_client = self.open_dataclient() + cl.sendall("150 Opened data connection.\r\n") + self.send_file_data(path, data_client) + # if the next statement is reached, + # the data_client was closed. + data_client = None + cl.sendall("226 Done.\r\n") + except: + cl.sendall('550 Fail\r\n') + if data_client is not None: + data_client.close() + elif command == "STOR" or command == "APPE": + try: + data_client = self.open_dataclient() + cl.sendall("150 Opened data connection.\r\n") + self.save_file_data(path, data_client, + "w" if command == "STOR" else "a") + # if the next statement is reached, + # the data_client was closed. + data_client = None + cl.sendall("226 Done.\r\n") + except: + cl.sendall('550 Fail\r\n') + if data_client is not None: + data_client.close() + elif command == "SIZE": + try: + cl.sendall('213 {}\r\n'.format(uos.stat(path)[6])) + except: + cl.sendall('550 Fail\r\n') + elif command == "STAT": + if payload == "": + cl.sendall("211-Connected to ({})\r\n" + " Data address ({})\r\n" + " TYPE: Binary STRU: File MODE: Stream\r\n" + " Session timeout {}\r\n" + "211 Client count is {}\r\n".format( + self.remote_addr, self.pasv_data_addr, + _COMMAND_TIMEOUT, len(client_list))) + else: + cl.sendall("213-Directory listing:\r\n") + self.send_list_data(path, cl, True) + cl.sendall("213 Done.\r\n") + elif command == "DELE": + try: + uos.remove(path) + cl.sendall('250 OK\r\n') + except: + cl.sendall('550 Fail\r\n') + elif command == "RNFR": + try: + # just test if the name exists, exception if not + uos.stat(path) + self.fromname = path + cl.sendall("350 Rename from\r\n") + except: + cl.sendall('550 Fail\r\n') + elif command == "RNTO": + try: + uos.rename(self.fromname, path) + cl.sendall('250 OK\r\n') + except: + cl.sendall('550 Fail\r\n') + self.fromname = None + elif command == "CDUP" or command == "XCUP": + self.cwd = self.get_absolute_path(self.cwd, "..") + cl.sendall('250 OK\r\n') + elif command == "RMD" or command == "XRMD": + try: + uos.rmdir(path) + cl.sendall('250 OK\r\n') + except: + cl.sendall('550 Fail\r\n') + elif command == "MKD" or command == "XMKD": + try: + uos.mkdir(path) + cl.sendall('250 OK\r\n') + except: + cl.sendall('550 Fail\r\n') + else: + cl.sendall("502 Unsupported command.\r\n") + # log_msg(2, + # "Unsupported command {} with payload {}".format(command, + # payload)) + # handle unexpected errors + except Exception as err: + log_msg(1, "Exception in exec_ftp_command: {}".format(err)) + # tidy up before leaving + client_busy = False + + +def log_msg(level, *args): + global verbose_l + if verbose_l >= level: + print(*args) + + +# close client and remove it from the list +def close_client(cl): + cl.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) + cl.close() + for i, client in enumerate(client_list): + if client.command_client == cl: + del client_list[i] + break + + +def accept_ftp_connect(ftpsocket): + # Accept new calls for the server + try: + client_list.append(FTP_client(ftpsocket)) + except: + log_msg(1, "Attempt to connect failed") + # try at least to reject + try: + temp_client, temp_addr = ftpsocket.accept() + temp_client.close() + except: + pass + + +def num_ip(ip): + items = ip.split(".") + return (int(items[0]) << 24 | int(items[1]) << 16 | + int(items[2]) << 8 | int(items[3])) + + +def stop(): + global ftpsocket, datasocket + global client_list + global client_busy + + for client in client_list: + client.command_client.setsockopt(socket.SOL_SOCKET, + _SO_REGISTER_HANDLER, None) + client.command_client.close() + del client_list + client_list = [] + client_busy = False + if ftpsocket is not None: + ftpsocket.setsockopt(socket.SOL_SOCKET, _SO_REGISTER_HANDLER, None) + ftpsocket.close() + if datasocket is not None: + datasocket.close() + + +# start listening for ftp connections on port 21 +def start(port=21, verbose=0, splash=True): + global ftpsocket, datasocket + global verbose_l + global client_list + global client_busy + global AP_addr, STA_addr + + alloc_emergency_exception_buf(100) + verbose_l = verbose + client_list = [] + client_busy = False + + ftpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + datasocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + ftpsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + datasocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + ftpsocket.bind(('0.0.0.0', port)) + datasocket.bind(('0.0.0.0', _DATA_PORT)) + + ftpsocket.listen(0) + datasocket.listen(0) + + datasocket.settimeout(10) + ftpsocket.setsockopt(socket.SOL_SOCKET, + _SO_REGISTER_HANDLER, accept_ftp_connect) + + wlan = network.WLAN(network.AP_IF) + if wlan.active(): + ifconfig = wlan.ifconfig() + # save IP address string and numerical values of IP adress and netmask + AP_addr = (ifconfig[0], num_ip(ifconfig[0]), num_ip(ifconfig[1])) + if splash: + print("FTP server started on {}:{}".format(ifconfig[0], port)) + wlan = network.WLAN(network.STA_IF) + if wlan.active(): + ifconfig = wlan.ifconfig() + # save IP address string and numerical values of IP adress and netmask + STA_addr = (ifconfig[0], num_ip(ifconfig[0]), num_ip(ifconfig[1])) + if splash: + print("FTP server started on {}:{}".format(ifconfig[0], port)) + + +def restart(port=21, verbose=0, splash=True): + stop() + sleep_ms(200) + start(port, verbose, splash) + + +start(splash=True) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 27a57df9b..8398b71c9 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -1,6 +1,9 @@ // Options to control how MicroPython is built for this port, // overriding defaults in py/mpconfig.h. +// Board-specific definitions +#include "mpconfigboard.h" + #include #include #include "rom/ets_sys.h" @@ -96,6 +99,7 @@ #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) #define MICROPY_PY_MATH (1) #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#define MICROPY_PY_MATH_ISCLOSE (1) #define MICROPY_PY_CMATH (1) #define MICROPY_PY_GC (1) #define MICROPY_PY_IO (1) @@ -290,7 +294,12 @@ typedef long mp_off_t; #include // board specifics - -#define MICROPY_HW_BOARD_NAME "ESP32 module" -#define MICROPY_HW_MCU_NAME "ESP32" #define MICROPY_PY_SYS_PLATFORM "esp32" + +#ifndef MICROPY_HW_ENABLE_MDNS_QUERIES +#define MICROPY_HW_ENABLE_MDNS_QUERIES (1) +#endif + +#ifndef MICROPY_HW_ENABLE_MDNS_RESPONDER +#define MICROPY_HW_ENABLE_MDNS_RESPONDER (1) +#endif diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index 10f17ebcf..100894b2e 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -205,6 +205,7 @@ STATIC const mp_rom_map_elem_t lan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&lan_active_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&lan_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&lan_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&esp_config_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&esp_ifconfig_obj) }, }; diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index aca4bbc1e..1a14c09bf 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -129,29 +129,26 @@ STATIC mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) { if (self->pcb == NULL) { mp_raise_msg(&mp_type_RuntimeError, "init failed"); } - pppapi_set_default(self->pcb); - ppp_set_usepeerdns(self->pcb, 1); - pppapi_connect(self->pcb, 0); - - xTaskCreatePinnedToCore(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t*)&self->client_task_handle, MP_TASK_COREID); self->active = true; } else { if (!self->active) { return mp_const_false; } - // Wait for PPPERR_USER, with timeout - pppapi_close(self->pcb, 0); - uint32_t t0 = mp_hal_ticks_ms(); - while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); - } + if (self->client_task_handle != NULL) { // is connecting or connected? + // Wait for PPPERR_USER, with timeout + pppapi_close(self->pcb, 0); + uint32_t t0 = mp_hal_ticks_ms(); + while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { + mp_hal_delay_ms(10); + } - // Shutdown task - xTaskNotifyGive(self->client_task_handle); - t0 = mp_hal_ticks_ms(); - while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); + // Shutdown task + xTaskNotifyGive(self->client_task_handle); + t0 = mp_hal_ticks_ms(); + while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { + mp_hal_delay_ms(10); + } } // Release PPP @@ -166,6 +163,59 @@ STATIC mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_active_obj, 1, 2, ppp_active); +STATIC mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + enum { ARG_authmode, ARG_username, ARG_password }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_authmode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} }, + { MP_QSTR_username, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, + { MP_QSTR_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_const_none_obj)} }, + }; + + mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); + + ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (!self->active) { + mp_raise_msg(&mp_type_OSError, "must be active"); + } + + if (self->client_task_handle != NULL) { + mp_raise_OSError(MP_EALREADY); + } + + switch (parsed_args[ARG_authmode].u_int) { + case PPPAUTHTYPE_NONE: + case PPPAUTHTYPE_PAP: + case PPPAUTHTYPE_CHAP: + break; + default: + mp_raise_msg(&mp_type_ValueError, "invalid auth"); + } + + if (parsed_args[ARG_authmode].u_int != PPPAUTHTYPE_NONE) { + const char* username_str = mp_obj_str_get_str(parsed_args[ARG_username].u_obj); + const char* password_str = mp_obj_str_get_str(parsed_args[ARG_password].u_obj); + pppapi_set_auth(self->pcb, parsed_args[ARG_authmode].u_int, username_str, password_str); + } + if (pppapi_set_default(self->pcb) != ESP_OK) { + mp_raise_msg(&mp_type_OSError, "set default failed"); + } + + ppp_set_usepeerdns(self->pcb, true); + + if (pppapi_connect(self->pcb, 0) != ESP_OK) { + mp_raise_msg(&mp_type_OSError, "connect failed"); + } + + if (xTaskCreatePinnedToCore(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t*)&self->client_task_handle, MP_TASK_COREID) != pdPASS) { + mp_raise_msg(&mp_type_RuntimeError, "failed to create worker task"); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(ppp_connect_obj, 1, ppp_connect_py); + STATIC mp_obj_t ppp_delete(mp_obj_t self_in) { ppp_if_obj_t* self = MP_OBJ_TO_PTR(self_in); mp_obj_t args[] = {self, mp_const_false}; @@ -216,10 +266,14 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(ppp_isconnected_obj, ppp_isconnected); STATIC const mp_rom_map_elem_t ppp_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&ppp_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ppp_connect_obj) }, { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&ppp_isconnected_obj) }, { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) }, { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) }, + { MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, + { MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, + { MP_ROM_QSTR(MP_QSTR_AUTH_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, }; STATIC MP_DEFINE_CONST_DICT(ppp_if_locals_dict, ppp_if_locals_dict_table); diff --git a/ports/esp32/partitions-2MiB.csv b/ports/esp32/partitions-2MiB.csv new file mode 100644 index 000000000..84bc53782 --- /dev/null +++ b/ports/esp32/partitions-2MiB.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 0x110000, +vfs, data, fat, 0x120000, 0xA0000, diff --git a/ports/esp32/partitions.csv b/ports/esp32/partitions.csv index 7167537b7..e8123ee5f 100644 --- a/ports/esp32/partitions.csv +++ b/ports/esp32/partitions.csv @@ -2,4 +2,5 @@ # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x190000, +factory, app, factory, 0x10000, 0x200000, +vfs, data, fat, 0x210000, 0x1f0000, diff --git a/ports/esp8266/esp8266_common.ld b/ports/esp8266/esp8266_common.ld index f4b4207f2..bbbb1325e 100644 --- a/ports/esp8266/esp8266_common.ld +++ b/ports/esp8266/esp8266_common.ld @@ -110,6 +110,7 @@ SECTIONS *py/obj*.o*(.literal* .text*) *py/opmethods.o*(.literal* .text*) *py/parse*.o*(.literal* .text*) + *py/profile*.o*(.literal* .text*) *py/qstr.o*(.literal* .text*) *py/repl.o*(.literal* .text*) *py/runtime.o*(.literal* .text*) diff --git a/ports/esp8266/machine_adc.c b/ports/esp8266/machine_adc.c index b422f0f9e..932e5782f 100644 --- a/ports/esp8266/machine_adc.c +++ b/ports/esp8266/machine_adc.c @@ -28,54 +28,71 @@ #include #include "py/runtime.h" +#include "py/mphal.h" #include "user_interface.h" -const mp_obj_type_t pyb_adc_type; - -typedef struct _pyb_adc_obj_t { +typedef struct _machine_adc_obj_t { mp_obj_base_t base; bool isvdd; -} pyb_adc_obj_t; +} machine_adc_obj_t; -STATIC pyb_adc_obj_t pyb_adc_vdd3 = {{&pyb_adc_type}, true}; -STATIC pyb_adc_obj_t pyb_adc_adc = {{&pyb_adc_type}, false}; +extern const mp_obj_type_t machine_adc_type; -STATIC mp_obj_t pyb_adc_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, - const mp_obj_t *args) { +STATIC machine_adc_obj_t machine_adc_vdd3 = {{&machine_adc_type}, true}; +STATIC machine_adc_obj_t machine_adc_adc = {{&machine_adc_type}, false}; + +STATIC uint16_t adc_read(machine_adc_obj_t *self) { + if (self->isvdd) { + return system_get_vdd33(); + } else { + return system_adc_read(); + } +} +void machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "ADC(%u)", self->isvdd); +} + +mp_obj_t machine_adc_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); mp_int_t chn = mp_obj_get_int(args[0]); switch (chn) { case 0: - return &pyb_adc_adc; + return &machine_adc_adc; case 1: - return &pyb_adc_vdd3; + return &machine_adc_vdd3; default: - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, - "not a valid ADC Channel: %d", chn)); + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "ADC(%d) doesn't exist", chn)); } } -STATIC mp_obj_t pyb_adc_read(mp_obj_t self_in) { - pyb_adc_obj_t *adc = self_in; - - if (adc->isvdd) { - return mp_obj_new_int(system_get_vdd33()); - } else { - return mp_obj_new_int(system_adc_read()); - } +// read_u16() +STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t value = adc_read(self); + return MP_OBJ_NEW_SMALL_INT(value * 65535 / 1024); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_adc_read_obj, pyb_adc_read); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_u16_obj, machine_adc_read_u16); -STATIC const mp_rom_map_elem_t pyb_adc_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&pyb_adc_read_obj) } +// Legacy method +STATIC mp_obj_t machine_adc_read(mp_obj_t self_in) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(adc_read(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_obj, machine_adc_read); + +STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&machine_adc_read_u16_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&machine_adc_read_obj) } }; -STATIC MP_DEFINE_CONST_DICT(pyb_adc_locals_dict, pyb_adc_locals_dict_table); +STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table); -const mp_obj_type_t pyb_adc_type = { +const mp_obj_type_t machine_adc_type = { { &mp_type_type }, .name = MP_QSTR_ADC, - .make_new = pyb_adc_make_new, - .locals_dict = (mp_obj_dict_t*)&pyb_adc_locals_dict, + .print = machine_adc_print, + .make_new = machine_adc_make_new, + .locals_dict = (mp_obj_dict_t*)&machine_adc_locals_dict, }; diff --git a/ports/esp8266/machine_uart.c b/ports/esp8266/machine_uart.c index 21336c7fd..9aaa16092 100644 --- a/ports/esp8266/machine_uart.c +++ b/ports/esp8266/machine_uart.c @@ -29,11 +29,13 @@ #include #include "ets_sys.h" +#include "user_interface.h" #include "uart.h" #include "py/runtime.h" #include "py/stream.h" #include "py/mperrno.h" +#include "py/mphal.h" #include "modmachine.h" // UartDev is defined and initialized in rom code. @@ -63,14 +65,14 @@ STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k } STATIC void pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_rxbuf, ARG_timeout, ARG_timeout_char }; + enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx, ARG_rxbuf, ARG_timeout, ARG_timeout_char }; static const mp_arg_t allowed_args[] = { { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_stop, MP_ARG_INT, {.u_int = 0} }, - //{ MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - //{ MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, @@ -128,6 +130,22 @@ STATIC void pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const mp_o } } + // set tx/rx pins + mp_hal_pin_obj_t tx = 1, rx = 3; + if (args[ARG_tx].u_obj != MP_OBJ_NULL) { + tx = mp_hal_get_pin_obj(args[ARG_tx].u_obj); + } + if (args[ARG_rx].u_obj != MP_OBJ_NULL) { + rx = mp_hal_get_pin_obj(args[ARG_rx].u_obj); + } + if (tx == 1 && rx == 3) { + system_uart_de_swap(); + } else if (tx == 15 && rx == 13) { + system_uart_swap(); + } else { + mp_raise_ValueError("invalid tx/rx"); + } + // set stop bits switch (args[ARG_stop].u_int) { case 0: diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index e20e8cb75..35d4918bd 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -410,7 +410,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pyb_pin_type) }, { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&pyb_pwm_type) }, - { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pyb_adc_type) }, + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, #if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, diff --git a/ports/esp8266/modmachine.h b/ports/esp8266/modmachine.h index eae351f68..cea382d24 100644 --- a/ports/esp8266/modmachine.h +++ b/ports/esp8266/modmachine.h @@ -5,7 +5,7 @@ extern const mp_obj_type_t pyb_pin_type; extern const mp_obj_type_t pyb_pwm_type; -extern const mp_obj_type_t pyb_adc_type; +extern const mp_obj_type_t machine_adc_type; extern const mp_obj_type_t pyb_rtc_type; extern const mp_obj_type_t pyb_uart_type; extern const mp_obj_type_t pyb_i2c_type; diff --git a/ports/javascript/main.c b/ports/javascript/main.c index f8ef0e7c5..77b8b873a 100644 --- a/ports/javascript/main.c +++ b/ports/javascript/main.c @@ -46,7 +46,7 @@ int do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, false); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, false); mp_call_function_0(module_fun); nlr_pop(); } else { diff --git a/ports/javascript/mpconfigport.h b/ports/javascript/mpconfigport.h index 228113c48..02d83f402 100644 --- a/ports/javascript/mpconfigport.h +++ b/ports/javascript/mpconfigport.h @@ -73,6 +73,7 @@ #define MICROPY_PY_COLLECTIONS (1) #define MICROPY_PY_MATH (1) #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#define MICROPY_PY_MATH_ISCLOSE (1) #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO (1) #define MICROPY_PY_STRUCT (1) diff --git a/ports/minimal/main.c b/ports/minimal/main.c index 5e145dc82..adc1dad0c 100644 --- a/ports/minimal/main.c +++ b/ports/minimal/main.c @@ -16,7 +16,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); mp_call_function_0(module_fun); nlr_pop(); } else { diff --git a/ports/nrf/.gitignore b/ports/nrf/.gitignore index ace93515a..4b46e0586 100644 --- a/ports/nrf/.gitignore +++ b/ports/nrf/.gitignore @@ -1,8 +1,3 @@ # Nordic files ##################### drivers/bluetooth/s1*/ - -# Build files -##################### -build-*/ - diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index cc7b4f126..c02109a15 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -101,7 +101,7 @@ LDFLAGS += -Wl,--gc-sections endif -CFLAGS += $(CFLAGS_MCU_$(MCU_SERIES)) +CFLAGS += $(CFLAGS_MCU_$(MCU_SERIES)) CFLAGS += $(INC) -Wall -Werror -g -ansi -std=c11 -nostdlib $(COPT) $(NRF_DEFINES) $(CFLAGS_MOD) CFLAGS += -fno-strict-aliasing CFLAGS += -Iboards/$(BOARD) @@ -254,7 +254,7 @@ OBJ += $(BUILD)/pins_gen.o $(BUILD)/$(FATFS_DIR)/ff.o: COPT += -Os $(filter $(PY_BUILD)/../extmod/vfs_fat_%.o, $(PY_O)): COPT += -Os -.PHONY: all flash sd binary hex +.PHONY: all flash deploy sd binary hex all: binary hex @@ -276,7 +276,7 @@ FLASHER ?= ifeq ($(FLASHER),) -flash: $(BUILD)/$(OUTPUT_FILENAME).hex +deploy: $(BUILD)/$(OUTPUT_FILENAME).hex nrfjprog --program $< --sectorerase -f $(MCU_VARIANT) nrfjprog --reset -f $(MCU_VARIANT) @@ -288,7 +288,7 @@ sd: $(BUILD)/$(OUTPUT_FILENAME).hex else ifeq ($(FLASHER), pyocd) -flash: $(BUILD)/$(OUTPUT_FILENAME).hex +deploy: $(BUILD)/$(OUTPUT_FILENAME).hex pyocd-flashtool -t $(MCU_VARIANT) $< sd: $(BUILD)/$(OUTPUT_FILENAME).hex @@ -298,14 +298,47 @@ sd: $(BUILD)/$(OUTPUT_FILENAME).hex else ifeq ($(FLASHER), idap) -flash: $(BUILD)/$(OUTPUT_FILENAME).hex +deploy: $(BUILD)/$(OUTPUT_FILENAME).hex IDAPnRFPRog $< sd: $(BUILD)/$(OUTPUT_FILENAME).hex IDAPnRFPRog $(SOFTDEV_HEX) $< +else ifeq ($(FLASHER), bmp) + +BMP_PORT ?= /dev/ttyACM0 + +deploy: $(BUILD)/$(OUTPUT_FILENAME).elf + $(Q)$(GDB) \ + -ex 'target extended-remote $(BMP_PORT)' \ + -ex 'monitor tpwr enable' \ + -ex 'monitor swdp_scan' \ + -ex 'attach 1' \ + -ex 'set mem inaccessible-by-default off' \ + -ex 'load' \ + -ex 'kill' \ + -ex 'quit' \ + $< + +sd: $(BUILD)/$(OUTPUT_FILENAME).elf + $(Q)$(GDB) \ + -ex 'target extended-remote $(BMP_PORT)' \ + -ex 'monitor tpwr enable' \ + -ex 'monitor swdp_scan' \ + -ex 'attach 1' \ + -ex 'set mem inaccessible-by-default off' \ + -ex 'monitor erase_mass' \ + -ex 'load' \ + -ex 'file $(SOFTDEV_HEX)' \ + -ex 'load' \ + -ex 'kill' \ + -ex 'quit' \ + $< + endif +flash: deploy + $(BUILD)/$(OUTPUT_FILENAME).elf: $(OBJ) $(ECHO) "LINK $@" $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) diff --git a/ports/nrf/README.md b/ports/nrf/README.md index 2a1667a3e..df5904eb6 100644 --- a/ports/nrf/README.md +++ b/ports/nrf/README.md @@ -30,7 +30,7 @@ This is a port of MicroPython to the Nordic Semiconductor nRF series of chips. * PCA10031 (dongle) * [WT51822-S4AT](http://www.wireless-tag.com/wireless_module/BLE/WT51822-S4AT.html) * nRF52832 - * [PCA10040](http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52%2Fdita%2Fnrf52%2Fdevelopment%2Fnrf52_dev_kit.html) + * [PCA10040](http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52%2Fdita%2Fnrf52%2Fdevelopment%2Fnrf52_dev_kit.html) * [Adafruit Feather nRF52](https://www.adafruit.com/product/3406) * [Thingy:52](http://www.nordicsemi.com/eng/Products/Nordic-Thingy-52) * [Arduino Primo](http://www.arduino.org/products/boards/arduino-primo) @@ -38,6 +38,7 @@ This is a port of MicroPython to the Nordic Semiconductor nRF series of chips. * [BLUEIO-TAG-EVIM BLYST Nano Sensor board](https://www.crowdsupply.com/i-syst/blyst-nano) * nRF52840 * [PCA10056](http://www.nordicsemi.com/eng/Products/nRF52840-Preview-DK) + * [Particle Xenon](https://docs.particle.io/xenon/) ## Compile and Flash @@ -71,7 +72,7 @@ the compilation: GNU ARM Embedded Toolchain 7.2.1/4Q17. It's recommended to use a toolchain after this release, for example 7.3.1/2Q18 or 8.2.1/4Q18. The alternative would be to build the target using the LTO=0 as described above. - + ## Compile and Flash with Bluetooth Stack First prepare the bluetooth folder by downloading Bluetooth LE stacks and headers: @@ -126,6 +127,7 @@ ibk_blyst_nano | s132 | Peripheral and Central | [IDAP] idk_blyst_nano | s132 | Peripheral and Central | [IDAP](#idap-midap-link-targets) blueio_tag_evim | s132 | Peripheral and Central | [IDAP](#idap-midap-link-targets) pca10056 | s140 | Peripheral and Central | [Segger](#segger-targets) +particle_xenon | s140 | Peripheral and Central | [Black Magic Probe](#black-magic-probe-targets) ## IDAP-M/IDAP-Link Targets @@ -153,6 +155,14 @@ Install the necessary tools to flash and debug using OpenOCD: sudo apt-get install openocd sudo pip install pyOCD +## Black Magic Probe Targets + +This requires no further dependencies other than `arm-none-eabi-gdb`. + +`make deploy` will use gdb to load and run new firmware. See +[this guide](https://github.com/blacksphere/blackmagic/wiki/Useful-GDB-commands) +for more tips about using the BMP with GDB. + ## Bluetooth LE REPL The port also implements a BLE REPL driver. This feature is disabled by default, as it will deactivate the UART REPL when activated. As some of the nRF devices only have one UART, using the BLE REPL free's the UART instance such that it can be used as a general UART peripheral not bound to REPL. diff --git a/ports/nrf/boards/common.ld b/ports/nrf/boards/common.ld index 2e1e6f735..6edd33cf0 100644 --- a/ports/nrf/boards/common.ld +++ b/ports/nrf/boards/common.ld @@ -16,7 +16,7 @@ SECTIONS . = ALIGN(4); _etext = .; /* define a global symbol at end of code */ } >FLASH_TEXT - + /* .ARM.extab : { @@ -30,10 +30,10 @@ SECTIONS __exidx_end = .; } >FLASH */ - + /* used by the startup to initialize data */ _sidata = LOADADDR(.data); - + /* This is the initialized data section The program executes knowing that the data is in the RAM but the loader puts the initial values in the FLASH (inidata). @@ -49,7 +49,7 @@ SECTIONS . = ALIGN(4); _edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ } >RAM AT>FLASH_TEXT - + /* Uninitialized data section */ .bss : { diff --git a/ports/nrf/boards/make-pins.py b/ports/nrf/boards/make-pins.py index 023b2161c..1844a0678 100644 --- a/ports/nrf/boards/make-pins.py +++ b/ports/nrf/boards/make-pins.py @@ -93,6 +93,7 @@ class Pin(object): self.adc_num = 0 self.adc_channel = 0 self.board_pin = False + self.board_index = None def cpu_pin_name(self): return '{:s}{:d}'.format("P", self.pin) @@ -103,6 +104,9 @@ class Pin(object): def set_is_board_pin(self): self.board_pin = True + def set_board_index(self, index): + self.board_index = index + def parse_adc(self, adc_str): if (adc_str[:3] != 'ADC'): return @@ -233,20 +237,24 @@ class Pins(object): def print_named(self, label, named_pins): print('STATIC const mp_rom_map_elem_t pin_{:s}_pins_locals_dict_table[] = {{'.format(label)) - index = 0 for named_pin in named_pins: pin = named_pin.pin() if pin.is_board_pin(): - print(' {{ MP_ROM_QSTR(MP_QSTR_{:s}), MP_ROM_PTR(&machine_pin_obj[{:d}]) }},'.format(named_pin.name(), index)) - index += 1 + print(' {{ MP_ROM_QSTR(MP_QSTR_{:s}), MP_ROM_PTR(&machine_board_pin_obj[{:d}]) }},'.format(named_pin.name(), pin.board_index)) print('};') print('MP_DEFINE_CONST_DICT(pin_{:s}_pins_locals_dict, pin_{:s}_pins_locals_dict_table);'.format(label, label)); def print_const_table(self): + num_board_pins = 0 + for named_pin in self.cpu_pins: + pin = named_pin.pin() + if pin.is_board_pin(): + pin.set_board_index(num_board_pins) + num_board_pins += 1 print('') - print('const uint8_t machine_pin_num_of_pins = {:d};'.format(len(self.board_pins))) + print('const uint8_t machine_pin_num_of_board_pins = {:d};'.format(num_board_pins)) print('') - print('const pin_obj_t machine_pin_obj[{:d}] = {{'.format(len(self.board_pins))) + print('const pin_obj_t machine_board_pin_obj[{:d}] = {{'.format(num_board_pins)) for named_pin in self.cpu_pins: pin = named_pin.pin() if pin.is_board_pin(): diff --git a/ports/nrf/boards/microbit/modules/microbitdisplay.c b/ports/nrf/boards/microbit/modules/microbitdisplay.c index 936a3ec97..2d7081750 100644 --- a/ports/nrf/boards/microbit/modules/microbitdisplay.c +++ b/ports/nrf/boards/microbit/modules/microbitdisplay.c @@ -256,15 +256,15 @@ static const uint16_t render_timings[] = // The scale is (approximately) exponential, // each step is approx x1.9 greater than the previous. { 0, // Bright, Ticks Duration, Relative power - 2, // 1, 2, 32µs, inf - 2, // 2, 4, 64µs, 200% - 4, // 3, 8, 128µs, 200% - 7, // 4, 15, 240µs, 187% - 13, // 5, 28, 448µs, 187% - 25, // 6, 53, 848µs, 189% - 49, // 7, 102, 1632µs, 192% - 97, // 8, 199, 3184µs, 195% -// Always on 9, 375, 6000µs, 188% + 2, // 1, 2, 32us, inf + 2, // 2, 4, 64us, 200% + 4, // 3, 8, 128us, 200% + 7, // 4, 15, 240us, 187% + 13, // 5, 28, 448us, 187% + 25, // 6, 53, 848us, 189% + 49, // 7, 102, 1632us, 192% + 97, // 8, 199, 3184us, 195% +// Always on 9, 375, 6000us, 188% }; #define DISPLAY_TICKER_SLOT 1 @@ -281,7 +281,7 @@ static int32_t callback(void) { return -1; } display->previous_brightness = brightness; - // Return interval (in 16µs ticks) until next callback + // Return interval (in 16us ticks) until next callback return render_timings[brightness]; } diff --git a/ports/nrf/boards/microbit/modules/microbitimage.c b/ports/nrf/boards/microbit/modules/microbitimage.c index 9cba30f87..fca507508 100644 --- a/ports/nrf/boards/microbit/modules/microbitimage.c +++ b/ports/nrf/boards/microbit/modules/microbitimage.c @@ -62,7 +62,7 @@ STATIC void microbit_image_print(const mp_print_t *print, mp_obj_t self_in, mp_p uint8_t monochromeGetPixelValue(monochrome_5by5_t * p_mono, mp_int_t x, mp_int_t y) { unsigned int index = y*5+x; - if (index == 24) + if (index == 24) return p_mono->pixel44; return (p_mono->bits24[index>>3] >> (index&7))&1; } @@ -380,7 +380,7 @@ mp_obj_t microbit_image_set_pixel(mp_uint_t n_args, const mp_obj_t *args) { mp_raise_ValueError("index cannot be negative"); } mp_int_t bright = mp_obj_get_int(args[3]); - if (bright < 0 || bright > MAX_BRIGHTNESS) + if (bright < 0 || bright > MAX_BRIGHTNESS) mp_raise_ValueError("brightness out of bounds."); if (x < imageWidth(self) && y < imageHeight(self)) { greyscaleSetPixelValue(&(self->greyscale), x, y, bright); @@ -610,7 +610,7 @@ microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_float_t f #else microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_int_t fval) { #endif - if (fval < 0) + if (fval < 0) mp_raise_ValueError("Brightness multiplier must not be negative."); greyscale_t *result = greyscale_new(imageWidth(lhs), imageHeight(lhs)); for (int x = 0; x < imageWidth(lhs); ++x) { @@ -639,7 +639,7 @@ microbit_image_obj_t *microbit_image_sum(microbit_image_obj_t *lhs, microbit_ima int val; int lval = imageGetPixelValue(lhs, x,y); int rval = imageGetPixelValue(rhs, x,y); - if (add) + if (add) val = min(lval + rval, MAX_BRIGHTNESS); else val = max(0, lval - rval); @@ -647,7 +647,7 @@ microbit_image_obj_t *microbit_image_sum(microbit_image_obj_t *lhs, microbit_ima } } return (microbit_image_obj_t *)result; -} +} STATIC mp_obj_t image_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { if (mp_obj_get_type(lhs_in) != µbit_image_type) { @@ -697,9 +697,9 @@ const mp_obj_type_t microbit_image_type = { .buffer_p = {NULL}, .locals_dict = (mp_obj_dict_t*)µbit_image_locals_dict, }; - + typedef struct _scrolling_string_t { - mp_obj_base_t base; + mp_obj_base_t base; char const *str; mp_uint_t len; mp_obj_t ref; @@ -708,7 +708,7 @@ typedef struct _scrolling_string_t { } scrolling_string_t; typedef struct _scrolling_string_iterator_t { - mp_obj_base_t base; + mp_obj_base_t base; mp_obj_t ref; greyscale_t *img; char const *next_char; @@ -962,7 +962,7 @@ const mp_obj_type_t microbit_facade_iterator_type = { }; mp_obj_t microbit_facade_iterator(mp_obj_t iterable_in, mp_obj_iter_buf_t *iter_buf) { - (void)iter_buf; + (void)iter_buf; facade_iterator_t *result = m_new_obj(facade_iterator_t); string_image_facade_t *iterable = (string_image_facade_t *)iterable_in; result->base.type = µbit_facade_iterator_type; diff --git a/ports/nrf/boards/microbit/modules/microbitimage.h b/ports/nrf/boards/microbit/modules/microbitimage.h index 23edbd6ba..41a6aff4c 100644 --- a/ports/nrf/boards/microbit/modules/microbitimage.h +++ b/ports/nrf/boards/microbit/modules/microbitimage.h @@ -2,15 +2,15 @@ #define __MICROPY_INCLUDED_MICROBIT_IMAGE_H__ #include "py/runtime.h" - + #define MAX_BRIGHTNESS 9 -/** Monochrome images are immutable, which means that +/** Monochrome images are immutable, which means that * we only need one bit per pixel which saves quite a lot * of memory */ /* we reserve a couple of bits, so we won't need to modify the - * layout if we need to add more functionality or subtypes. */ + * layout if we need to add more functionality or subtypes. */ #define TYPE_AND_FLAGS \ mp_obj_base_t base; \ uint8_t five:1; \ diff --git a/ports/nrf/boards/particle_xenon/mpconfigboard.h b/ports/nrf/boards/particle_xenon/mpconfigboard.h new file mode 100644 index 000000000..c2aabce48 --- /dev/null +++ b/ports/nrf/boards/particle_xenon/mpconfigboard.h @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jim Mussared + * + * 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. + */ + +#define MICROPY_HW_BOARD_NAME "XENON" +#define MICROPY_HW_MCU_NAME "NRF52840" +#define MICROPY_PY_SYS_PLATFORM "PARTICLE-XENON" + +#define MICROPY_PY_MACHINE_UART (1) +#define MICROPY_PY_MACHINE_HW_PWM (1) +#define MICROPY_PY_MACHINE_HW_SPI (1) +#define MICROPY_PY_MACHINE_TIMER (1) +#define MICROPY_PY_MACHINE_RTCOUNTER (1) +#define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_ADC (1) +#define MICROPY_PY_MACHINE_TEMP (1) +#define MICROPY_PY_RANDOM_HW_RNG (1) + +#define MICROPY_HW_HAS_LED (1) +#define MICROPY_HW_LED_TRICOLOR (1) +#define MICROPY_HW_LED_PULLUP (1) + +#define MICROPY_HW_LED_RED (13) // LED1 +#define MICROPY_HW_LED_GREEN (14) // LED2 +#define MICROPY_HW_LED_BLUE (15) // LED3 + +// UART config +#define MICROPY_HW_UART1_RX (8) +#define MICROPY_HW_UART1_TX (6) +#define MICROPY_HW_UART1_CTS (32+2) +#define MICROPY_HW_UART1_RTS (32+1) +#define MICROPY_HW_UART1_HWFC (0) + +// SPI0 config +#define MICROPY_HW_SPI0_NAME "SPI0" + +#define MICROPY_HW_SPI0_SCK (32+15) +#define MICROPY_HW_SPI0_MOSI (32+13) +#define MICROPY_HW_SPI0_MISO (32+14) + +#define MICROPY_HW_PWM0_NAME "PWM0" +#define MICROPY_HW_PWM1_NAME "PWM1" +#define MICROPY_HW_PWM2_NAME "PWM2" +#if 0 +#define MICROPY_HW_PWM3_NAME "PWM3" +#endif + +#define HELP_TEXT_BOARD_LED "1,2,3" diff --git a/ports/nrf/boards/particle_xenon/mpconfigboard.mk b/ports/nrf/boards/particle_xenon/mpconfigboard.mk new file mode 100644 index 000000000..ca555d393 --- /dev/null +++ b/ports/nrf/boards/particle_xenon/mpconfigboard.mk @@ -0,0 +1,7 @@ +MCU_SERIES = m4 +MCU_VARIANT = nrf52 +MCU_SUB_VARIANT = nrf52840 +SOFTDEV_VERSION = 6.1.1 +LD_FILES += boards/nrf52840_1M_256k.ld + +NRF_DEFINES += -DNRF52840_XXAA diff --git a/ports/nrf/boards/particle_xenon/pins.csv b/ports/nrf/boards/particle_xenon/pins.csv new file mode 100644 index 000000000..30f05c556 --- /dev/null +++ b/ports/nrf/boards/particle_xenon/pins.csv @@ -0,0 +1,38 @@ +LED1,P13 +LED2,P14 +LED3,P15 +A0,P3,ADC0_IN1 +A1,P4,ADC0_IN2 +A2,P28,ADC0_IN4 +A3,P29,ADC0_IN5 +A4,P30,ADC0_IN6 +A5,P31,ADC0_IN7 +SPI_SS,P31 +SPI_SCK,P47 +SPI_MOSI,P45 +SPI_MISO,P46 +SPI1_SCK,P33 +SPI1_MOSI,P34 +SPI1_MISO,P40 +UART1_RX,P8 +UART1_TX,P6 +UART2_RX,P42 +UART2_TX,P40 +SDA,P26 +SCL,P27 +SDA1,P33 +SCL1,P34 +D0,P26 +D1,P27 +D2,P33 +D3,P34 +D4,P40 +D5,P42 +D6,P43 +D7,P44 +D8,P35 +D9,P6 +D10,P8 +D11,P46 +D12,P45 +D13,P47 diff --git a/ports/nrf/device/startup_nrf51822.c b/ports/nrf/device/startup_nrf51822.c index 2f15f0f49..2063e2727 100644 --- a/ports/nrf/device/startup_nrf51822.c +++ b/ports/nrf/device/startup_nrf51822.c @@ -48,8 +48,8 @@ void Reset_Handler(void) { // RAM on in on-mode *ram_on_addr = 3; // block 0 and 1 *ram_on_b_addr = 3; // block 2 and 3 -#if 0 - // RAM on in off-mode +#if 0 + // RAM on in off-mode ram_on_addr = 1 << 16; ram_on_b_addr = 1 << 17; #endif diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c index ff3c885c1..e01d11848 100644 --- a/ports/nrf/drivers/bluetooth/ble_drv.c +++ b/ports/nrf/drivers/bluetooth/ble_drv.c @@ -418,7 +418,7 @@ bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) { p_adv_params->p_device_name, p_adv_params->device_name_len) != 0) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError, - "Can not apply device name in the stack.")); + "Can not apply device name in the stack.")); } BLE_DRIVER_LOG("Device name applied\n"); diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c index 4a23cd6d2..b94780e26 100644 --- a/ports/nrf/drivers/bluetooth/ble_uart.c +++ b/ports/nrf/drivers/bluetooth/ble_uart.c @@ -190,7 +190,7 @@ void ble_uart_init0(void) { ble_uart_char_tx.service_handle = ble_uart_service.handle; bool retval = ble_drv_characteristic_add(&ble_uart_char_tx); if (retval) { - ble_uart_char_tx.p_service = &ble_uart_service; + ble_uart_char_tx.p_service = &ble_uart_service; } mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_tx)); @@ -198,7 +198,7 @@ void ble_uart_init0(void) { ble_uart_char_rx.service_handle = ble_uart_service.handle; retval = ble_drv_characteristic_add(&ble_uart_char_rx); if (retval) { - ble_uart_char_rx.p_service = &ble_uart_service; + ble_uart_char_rx.p_service = &ble_uart_service; } mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_rx)); diff --git a/ports/nrf/drivers/ticker.h b/ports/nrf/drivers/ticker.h index f6a95a9a2..86f1c2447 100644 --- a/ports/nrf/drivers/ticker.h +++ b/ports/nrf/drivers/ticker.h @@ -35,7 +35,7 @@ #define __MICROPY_INCLUDED_LIB_TICKER_H__ /************************************* - * 62.5kHz (16µs cycle time) ticker. + * 62.5kHz (16us cycle time) ticker. ************************************/ #include "nrf.h" diff --git a/ports/nrf/fatfs_port.c b/ports/nrf/fatfs_port.c index 13ac21fb1..6a17f627b 100644 --- a/ports/nrf/fatfs_port.c +++ b/ports/nrf/fatfs_port.c @@ -28,6 +28,6 @@ #include "lib/oofatfs/ff.h" DWORD get_fattime(void) { - // TODO: Implement this function. For now, fake it. + // TODO: Implement this function. For now, fake it. return ((2016 - 1980) << 25) | ((12) << 21) | ((4) << 16) | ((00) << 11) | ((18) << 5) | (23 / 2); } diff --git a/ports/nrf/main.c b/ports/nrf/main.c index e1ffce938..38d41bd8c 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -81,7 +81,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { if (nlr_push(&nlr) == 0) { qstr source_name = lex->source_name; mp_parse_tree_t pn = mp_parse(lex, input_kind); - mp_obj_t module_fun = mp_compile(&pn, source_name, MP_EMIT_OPT_NONE, true); + mp_obj_t module_fun = mp_compile(&pn, source_name, true); mp_call_function_0(module_fun); nlr_pop(); } else { @@ -94,7 +94,7 @@ extern uint32_t _heap_start; extern uint32_t _heap_end; int main(int argc, char **argv) { - + soft_reset: mp_stack_set_top(&_ram_end); @@ -185,8 +185,8 @@ pin_init0(); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_sd)); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_sd_slash_lib)); - // use SD card as current directory - f_chdrive("/sd"); + // use SD card as current directory + f_chdrive("/sd"); } no_mem_for_sd:; } @@ -293,18 +293,17 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); void HardFault_Handler(void) { #if defined(NRF52_SERIES) - static volatile uint32_t reg; - static volatile uint32_t reg2; - static volatile uint32_t bfar; - reg = SCB->HFSR; - reg2 = SCB->CFSR; - bfar = SCB->BFAR; - for (int i = 0; i < 0; i++) - { - (void)reg; - (void)reg2; - (void)bfar; - } + static volatile uint32_t reg; + static volatile uint32_t reg2; + static volatile uint32_t bfar; + reg = SCB->HFSR; + reg2 = SCB->CFSR; + bfar = SCB->BFAR; + for (int i = 0; i < 0; i++) { + (void)reg; + (void)reg2; + (void)bfar; + } #endif } diff --git a/ports/nrf/modules/ble/help_sd.h b/ports/nrf/modules/ble/help_sd.h index 027bbdd51..69ee5a538 100644 --- a/ports/nrf/modules/ble/help_sd.h +++ b/ports/nrf/modules/ble/help_sd.h @@ -39,7 +39,7 @@ " ble.enabled() -- check whether bluetooth stack is enabled\n" \ " ble.address() -- return device address as text string\n" \ "\n" - + #else #define HELP_TEXT_SD #endif // MICROPY_PY_BLE diff --git a/ports/nrf/modules/machine/adc.c b/ports/nrf/modules/machine/adc.c index 55b68c6b8..fc5305e52 100644 --- a/ports/nrf/modules/machine/adc.c +++ b/ports/nrf/modules/machine/adc.c @@ -91,10 +91,19 @@ void adc_init0(void) { } STATIC int adc_find(mp_obj_t id) { - // given an integer id - int adc_id = mp_obj_get_int(id); - - int adc_idx = adc_id; + int adc_idx; + if (mp_obj_is_int(id)) { + // Given an integer id + adc_idx = mp_obj_get_int(id); + } else { + // Assume it's a pin-compatible object and convert it to an ADC channel number + mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(id); + if (pin->adc_num & PIN_ADC1) { + adc_idx = pin->adc_channel; + } else { + mp_raise_ValueError("invalid Pin for ADC"); + } + } if (adc_idx >= 0 && adc_idx < MP_ARRAY_SIZE(machine_adc_obj) && machine_adc_obj[adc_idx].id != (uint8_t)-1) { @@ -137,7 +146,7 @@ STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, s .acq_time = NRF_SAADC_ACQTIME_3US, .mode = NRF_SAADC_MODE_SINGLE_ENDED, .burst = NRF_SAADC_BURST_DISABLED, - .pin_p = self->id, // 0 - 7 + .pin_p = 1 + self->id, // pin_p=0 is AIN0, pin_p=8 is AIN7 .pin_n = NRF_SAADC_INPUT_DISABLED }; @@ -169,6 +178,20 @@ int16_t machine_adc_value_read(machine_adc_obj_t * adc_obj) { return value; } +// read_u16() +STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in) { + machine_adc_obj_t *self = self_in; + int16_t raw = machine_adc_value_read(self); + #if defined(NRF52_SERIES) + // raw is signed but the channel is in single-ended mode and this method cannot return negative values + if (raw < 0) { + raw = 0; + } + #endif + // raw is an 8-bit value + return MP_OBJ_NEW_SMALL_INT(raw << 8 | raw); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_machine_adc_read_u16_obj, machine_adc_read_u16); /// \method value() /// Read adc level. @@ -263,6 +286,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_machine_adc_battery_level_obj, machine_adc_b STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = { // instance methods + { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&mp_machine_adc_read_u16_obj) }, { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&mp_machine_adc_value_obj) }, // class methods diff --git a/ports/nrf/modules/machine/pin.c b/ports/nrf/modules/machine/pin.c index d56abfd04..f3a0bf07a 100644 --- a/ports/nrf/modules/machine/pin.c +++ b/ports/nrf/modules/machine/pin.c @@ -37,8 +37,8 @@ #include "nrf_gpio.h" #include "nrfx_gpiote.h" -extern const pin_obj_t machine_pin_obj[]; -extern const uint8_t machine_pin_num_of_pins; +extern const pin_obj_t machine_board_pin_obj[]; +extern const uint8_t machine_pin_num_of_board_pins; /// \moduleref machine /// \class Pin - control I/O pins @@ -127,12 +127,12 @@ const pin_obj_t *pin_find(mp_obj_t user_obj) { const pin_obj_t *pin_obj; // If pin is SMALL_INT if (mp_obj_is_small_int(user_obj)) { - uint8_t value = MP_OBJ_SMALL_INT_VALUE(user_obj); - for (uint8_t i = 0; i < machine_pin_num_of_pins; i++) { - if (machine_pin_obj[i].pin == value) { - return &machine_pin_obj[i]; - } - } + uint8_t value = MP_OBJ_SMALL_INT_VALUE(user_obj); + for (uint8_t i = 0; i < machine_pin_num_of_board_pins; i++) { + if (machine_board_pin_obj[i].pin == value) { + return &machine_board_pin_obj[i]; + } + } } // If a pin was provided, then use it @@ -364,8 +364,8 @@ STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *self, mp_uint_t n_args, con nrf_gpio_pin_dir_t mode = (nrf_gpio_pin_dir_t)args[0].u_int; // Connect input or not - nrf_gpio_pin_input_t input = (mode == NRF_GPIO_PIN_DIR_INPUT) ? NRF_GPIO_PIN_INPUT_CONNECT - : NRF_GPIO_PIN_INPUT_DISCONNECT; + nrf_gpio_pin_input_t input = (mode == NRF_GPIO_PIN_DIR_INPUT) ? NRF_GPIO_PIN_INPUT_CONNECT + : NRF_GPIO_PIN_INPUT_DISCONNECT; if (mode == NRF_GPIO_PIN_DIR_OUTPUT || mode == NRF_GPIO_PIN_DIR_INPUT) { nrf_gpio_cfg(self->pin, @@ -496,7 +496,7 @@ STATIC void pin_common_irq_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t } STATIC mp_obj_t pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum {ARG_handler, ARG_trigger, ARG_wake}; + enum {ARG_handler, ARG_trigger, ARG_wake}; static const mp_arg_t allowed_args[] = { { MP_QSTR_handler, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = mp_const_none} }, { MP_QSTR_trigger, MP_ARG_INT, {.u_int = NRF_GPIOTE_POLARITY_LOTOHI | NRF_GPIOTE_POLARITY_HITOLO} }, diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index cf8749302..195d47e6b 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -269,7 +269,7 @@ STATIC mp_obj_t machine_hard_pwm_make_new(mp_arg_val_t *args) { } else { self->p_config->mode = MODE_HIGH_LOW; } - + return MP_OBJ_FROM_PTR(self); } diff --git a/ports/nrf/modules/machine/spi.c b/ports/nrf/modules/machine/spi.c index 1c401b76d..4361a8f8f 100644 --- a/ports/nrf/modules/machine/spi.c +++ b/ports/nrf/modules/machine/spi.c @@ -154,9 +154,9 @@ STATIC int spi_find(mp_obj_t id) { void spi_transfer(const machine_hard_spi_obj_t * self, size_t len, const void * src, void * dest) { nrfx_spi_xfer_desc_t xfer_desc = { .p_tx_buffer = src, - .tx_length = len, - .p_rx_buffer = dest, - .rx_length = len + .tx_length = len, + .p_rx_buffer = dest, + .rx_length = len }; nrfx_spi_xfer(self->p_spi, &xfer_desc, 0); @@ -340,7 +340,7 @@ STATIC void machine_hard_spi_init(mp_obj_t self_in, mp_arg_val_t *args) { // Active high if (args[ARG_INIT_polarity].u_int == 0) { - if (args[ARG_INIT_phase].u_int == 0) { + if (args[ARG_INIT_phase].u_int == 0) { // First clock edge self->p_config->mode = NRF_SPI_MODE_0; } else { @@ -349,7 +349,7 @@ STATIC void machine_hard_spi_init(mp_obj_t self_in, mp_arg_val_t *args) { } // Active low } else { - if (args[ARG_INIT_phase].u_int == 0) { + if (args[ARG_INIT_phase].u_int == 0) { // First clock edge self->p_config->mode = NRF_SPI_MODE_2; } else { diff --git a/ports/nrf/modules/machine/temp.c b/ports/nrf/modules/machine/temp.c index 361d98885..007a7e5fd 100644 --- a/ports/nrf/modules/machine/temp.c +++ b/ports/nrf/modules/machine/temp.c @@ -67,9 +67,9 @@ STATIC mp_obj_t machine_temp_make_new(const mp_obj_type_t *type, size_t n_args, // 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); - + machine_temp_obj_t *self = m_new_obj(machine_temp_obj_t); - + self->base.type = &machine_temp_type; return MP_OBJ_FROM_PTR(self); diff --git a/ports/nrf/modules/random/modrandom.c b/ports/nrf/modules/random/modrandom.c index f67bffb27..a1395bce6 100644 --- a/ports/nrf/modules/random/modrandom.c +++ b/ports/nrf/modules/random/modrandom.c @@ -72,7 +72,7 @@ uint32_t machine_rng_generate_random_word(void) { status = sd_rand_application_vector_get((uint8_t *)&retval, 4); // Extract 4 bytes } while (status != 0); - return retval; + return retval; } #endif diff --git a/ports/nrf/modules/ubluepy/ubluepy_delegate.c b/ports/nrf/modules/ubluepy/ubluepy_delegate.c index 07bb7f492..74ad52bbf 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_delegate.c +++ b/ports/nrf/modules/ubluepy/ubluepy_delegate.c @@ -72,7 +72,7 @@ STATIC const mp_rom_map_elem_t ubluepy_delegate_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_handleConnection), MP_ROM_PTR(&ubluepy_delegate_handle_conn_obj) }, { MP_ROM_QSTR(MP_QSTR_handleNotification), MP_ROM_PTR(&ubluepy_delegate_handle_notif_obj) }, #if 0 - { MP_ROM_QSTR(MP_QSTR_handleDiscovery), MP_ROM_PTR(&ubluepy_delegate_handle_disc_obj) }, + { MP_ROM_QSTR(MP_QSTR_handleDiscovery), MP_ROM_PTR(&ubluepy_delegate_handle_disc_obj) }, #endif }; diff --git a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c b/ports/nrf/modules/ubluepy/ubluepy_peripheral.c index 3a45e56a0..8f07daa2a 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_peripheral.c +++ b/ports/nrf/modules/ubluepy/ubluepy_peripheral.c @@ -481,7 +481,7 @@ STATIC const mp_rom_map_elem_t ubluepy_peripheral_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_advertise), MP_ROM_PTR(&ubluepy_peripheral_advertise_obj) }, #endif #if MICROPY_PY_UBLUEPY_OBSERVER - // Nothing yet. + // Nothing yet. #endif }; diff --git a/ports/nrf/modules/ubluepy/ubluepy_service.c b/ports/nrf/modules/ubluepy/ubluepy_service.c index e5bf42a09..a38136611 100644 --- a/ports/nrf/modules/ubluepy/ubluepy_service.c +++ b/ports/nrf/modules/ubluepy/ubluepy_service.c @@ -162,7 +162,7 @@ STATIC const mp_rom_map_elem_t ubluepy_service_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_addCharacteristic), MP_ROM_PTR(&ubluepy_service_add_char_obj) }, { MP_ROM_QSTR(MP_QSTR_getCharacteristics), MP_ROM_PTR(&ubluepy_service_get_chars_obj) }, #if 0 - // Properties + // Properties { MP_ROM_QSTR(MP_QSTR_peripheral), MP_ROM_PTR(&ubluepy_service_get_peripheral_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&ubluepy_service_get_uuid_obj) }, diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h index 18ff454fe..b374da403 100644 --- a/ports/nrf/mphalport.h +++ b/ports/nrf/mphalport.h @@ -42,7 +42,7 @@ typedef enum } HAL_StatusTypeDef; static inline uint32_t hal_tick_fake(void) { - return 0; + return 0; } #define mp_hal_ticks_ms hal_tick_fake // TODO: implement. Right now, return 0 always diff --git a/ports/nrf/nrf52_af.csv b/ports/nrf/nrf52_af.csv index 59686ff90..bcb05c227 100644 --- a/ports/nrf/nrf52_af.csv +++ b/ports/nrf/nrf52_af.csv @@ -1,9 +1,9 @@ P0,P0 P1,P1 -P2,P2 -P3,P3 -P4,P4 -P5,P5 +P2,P2,ADC1_CH0 +P3,P3,ADC1_CH1 +P4,P4,ADC1_CH2 +P5,P5,ADC1_CH3 P6,P6 P7,P7 P8,P8 @@ -26,10 +26,10 @@ P24,P24 P25,P25 P26,P26 P27,P27 -P28,P28 -P29,P29 -P30,P30 -P31,P31 +P28,P28,ADC1_CH4 +P29,P29,ADC1_CH5 +P30,P30,ADC1_CH6 +P31,P31,ADC1_CH7 P32,P32 P33,P33 P34,P34 diff --git a/ports/nrf/pin_defs_nrf5.h b/ports/nrf/pin_defs_nrf5.h index c84d048a4..db05aef99 100644 --- a/ports/nrf/pin_defs_nrf5.h +++ b/ports/nrf/pin_defs_nrf5.h @@ -53,9 +53,12 @@ enum { }; #define PIN_DEFS_PORT_AF_UNION \ - NRF_UART_Type *UART; -// NRF_SPI_Type *SPIM; -// NRF_SPIS_Type *SPIS; + NRF_UART_Type *UART; +// NRF_SPI_Type *SPIM; +// NRF_SPIS_Type *SPIS; +enum { + PIN_ADC1 = (1 << 0), +}; typedef NRF_GPIO_Type pin_gpio_t; diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 03a8afe77..c730c8297 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -114,6 +114,14 @@ OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST) # List of sources for qstr extraction SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C) +ifneq ($(FROZEN_MPY_DIR),) +# To use frozen bytecode, put your .py files in a subdirectory (eg frozen/) and +# then invoke make with FROZEN_MPY_DIR=frozen (be sure to build from scratch). +CFLAGS += -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool +CFLAGS += -DMICROPY_MODULE_FROZEN_MPY +MPY_CROSS_FLAGS += -march=armv7m +endif + all: run run: $(BUILD)/firmware.elf diff --git a/ports/qemu-arm/Makefile.test b/ports/qemu-arm/Makefile.test index 347c2fefd..32ec95a4f 100644 --- a/ports/qemu-arm/Makefile.test +++ b/ports/qemu-arm/Makefile.test @@ -1,5 +1,7 @@ LIB_SRC_C = lib/upytesthelper/upytesthelper.c +FROZEN_MPY_DIR ?= test-frzmpy + include Makefile CFLAGS += -DTEST @@ -8,7 +10,7 @@ CFLAGS += -DTEST $(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h $(BUILD)/genhdr/tests.h: - (cd $(TOP)/tests; ./run-tests --write-exp) + (cd $(TOP)/tests; ./run-tests --target=qemu-arm --write-exp) $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py) > $@ $(BUILD)/tinytest.o: @@ -18,7 +20,8 @@ $(BUILD)/firmware-test.elf: $(OBJ_COMMON) $(OBJ_TEST) $(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS) $(Q)$(SIZE) $@ +# Note: Using timeout(1) to handle cases where qemu hangs (e.g. this can happen with alignment errors). test: $(BUILD)/firmware-test.elf - qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out + timeout --foreground -k 5s 30s qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out $(Q)tail -n2 $(BUILD)/console.out $(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0" diff --git a/ports/qemu-arm/main.c b/ports/qemu-arm/main.c index 4cdd14828..41bf9457b 100644 --- a/ports/qemu-arm/main.c +++ b/ports/qemu-arm/main.c @@ -18,7 +18,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0); qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, true); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true); mp_call_function_0(module_fun); nlr_pop(); } else { diff --git a/ports/qemu-arm/test-frzmpy/native_frozen_align.py b/ports/qemu-arm/test-frzmpy/native_frozen_align.py new file mode 100644 index 000000000..5c5c0e8d2 --- /dev/null +++ b/ports/qemu-arm/test-frzmpy/native_frozen_align.py @@ -0,0 +1,13 @@ +import micropython + +@micropython.native +def native_x(x): + print(x + 1) + +@micropython.native +def native_y(x): + print(x + 1) + +@micropython.native +def native_z(x): + print(x + 1) diff --git a/ports/samd/Makefile b/ports/samd/Makefile index f4a09ad7c..77a53bd48 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -10,7 +10,7 @@ $(error Invalid BOARD specified: $(BOARD_DIR)) endif include ../../py/mkenv.mk -include $(BOARD_DIR)/board.mk +include $(BOARD_DIR)/mpconfigboard.mk # Qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h new file mode 100644 index 000000000..cec9e9ccd --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "Feather M0 Express" +#define MICROPY_HW_MCU_NAME "SAMD21G18A" diff --git a/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk new file mode 100644 index 000000000..8696c966b --- /dev/null +++ b/ports/samd/boards/ADAFRUIT_FEATHER_M0_EXPRESS/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21G18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/board.mk b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk similarity index 56% rename from ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/board.mk rename to ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk index 8cbd1885e..2e5d7e68d 100644 --- a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/board.mk +++ b/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/mpconfigboard.mk @@ -1,4 +1,4 @@ MCU_SERIES = SAMD51 CMSIS_MCU = SAMD51G19A -LD_FILES = $(BOARD_DIR)/link.ld sections.ld +LD_FILES = boards/samd51g19a.ld sections.ld TEXT0 = 0x4000 diff --git a/ports/samd/boards/ADAFRUIT_TRINKET_M0/board.mk b/ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.mk similarity index 56% rename from ports/samd/boards/ADAFRUIT_TRINKET_M0/board.mk rename to ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.mk index 3955a4f71..5b4d0b63e 100644 --- a/ports/samd/boards/ADAFRUIT_TRINKET_M0/board.mk +++ b/ports/samd/boards/ADAFRUIT_TRINKET_M0/mpconfigboard.mk @@ -1,4 +1,4 @@ MCU_SERIES = SAMD21 CMSIS_MCU = SAMD21E18A -LD_FILES = $(BOARD_DIR)/link.ld sections.ld +LD_FILES = boards/samd21x18a.ld sections.ld TEXT0 = 0x2000 diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.h b/ports/samd/boards/MINISAM_M4/mpconfigboard.h new file mode 100644 index 000000000..0847a45bf --- /dev/null +++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.h @@ -0,0 +1,7 @@ +#define MICROPY_HW_BOARD_NAME "Mini SAM M4" +#define MICROPY_HW_MCU_NAME "SAMD51G19A" + +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_PY_BUILTINS_COMPLEX (0) +#define MICROPY_PY_MATH (0) +#define MICROPY_PY_CMATH (0) diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.mk b/ports/samd/boards/MINISAM_M4/mpconfigboard.mk new file mode 100644 index 000000000..54ed3273d --- /dev/null +++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.mk @@ -0,0 +1,5 @@ +# https://www.minifigboards.com/mini-sam-m4/mini-sam-m4-hardware/ +MCU_SERIES = SAMD51 +CMSIS_MCU = SAMD51G19A +LD_FILES = boards/samd51g19a.ld sections.ld +TEXT0 = 0x4000 diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h new file mode 100644 index 000000000..c69b5b4c1 --- /dev/null +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h @@ -0,0 +1,2 @@ +#define MICROPY_HW_BOARD_NAME "SAMD21-XPLAINED-PRO" +#define MICROPY_HW_MCU_NAME "SAMD21J18A" diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk new file mode 100644 index 000000000..f95c65493 --- /dev/null +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21J18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 diff --git a/ports/samd/boards/ADAFRUIT_TRINKET_M0/link.ld b/ports/samd/boards/samd21x18a.ld similarity index 100% rename from ports/samd/boards/ADAFRUIT_TRINKET_M0/link.ld rename to ports/samd/boards/samd21x18a.ld diff --git a/ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/link.ld b/ports/samd/boards/samd51g19a.ld similarity index 100% rename from ports/samd/boards/ADAFRUIT_ITSYBITSY_M4_EXPRESS/link.ld rename to ports/samd/boards/samd51g19a.ld diff --git a/ports/stm32/.gitignore b/ports/stm32/.gitignore deleted file mode 100644 index 414487d53..000000000 --- a/ports/stm32/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build-*/ diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index e868d6de1..6c0c29572 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -261,6 +261,7 @@ SRC_C = \ eth.c \ gccollect.c \ help.c \ + machine_adc.c \ machine_i2c.c \ machine_spi.c \ machine_uart.c \ diff --git a/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h b/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h new file mode 100644 index 000000000..eb622cd29 --- /dev/null +++ b/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.h @@ -0,0 +1,85 @@ +#define MICROPY_HW_BOARD_NAME "MIKROE_CLICKER2_STM32" +#define MICROPY_HW_MCU_NAME "STM32F407" + +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (1) + +// HSE is 25MHz +#define MICROPY_HW_CLK_PLLM (25) +#define MICROPY_HW_CLK_PLLN (336) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (7) +#define MICROPY_HW_CLK_LAST_FREQ (1) + +// The board has a 32kHz crystal for the RTC +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (0) +#define MICROPY_HW_RTC_USE_CALOUT (0) // turn on/off PC13 512Hz output + +// UART config +// mikroBUS slot 1 +#define MICROPY_HW_UART2_NAME "SLOT1" +#define MICROPY_HW_UART2_TX (pin_D5) +#define MICROPY_HW_UART2_RX (pin_D6) +// mikroBUS slot 2 +#define MICROPY_HW_UART3_NAME "SLOT2" +#define MICROPY_HW_UART3_TX (pin_D8) +#define MICROPY_HW_UART3_RX (pin_D9) +// HDR2 +#define MICROPY_HW_UART4_NAME "HDR2" +#define MICROPY_HW_UART4_TX (pin_A0) +#define MICROPY_HW_UART4_RX (pin_A1) + +// I2C buses +// mikroBUS slot 2 / HDR2 +#define MICROPY_HW_I2C2_NAME "SLOT2" +#define MICROPY_HW_I2C2_SCL (pin_B10) +#define MICROPY_HW_I2C2_SDA (pin_B11) +// mikroBUS slot 1 +#define MICROPY_HW_I2C3_NAME "SLOT1" +#define MICROPY_HW_I2C3_SCL (pin_A8) +#define MICROPY_HW_I2C3_SDA (pin_C9) + +// SPI buses +// mikroBUS slot 2 / HDR1 +#define MICROPY_HW_SPI2_NAME "SLOT2" +#define MICROPY_HW_SPI2_NSS (pin_E11) +#define MICROPY_HW_SPI2_SCK (pin_B13) +#define MICROPY_HW_SPI2_MISO (pin_B14) +#define MICROPY_HW_SPI2_MOSI (pin_B15) +// mikroBUS slot 1 +#define MICROPY_HW_SPI3_NAME "SLOT1" +#define MICROPY_HW_SPI3_NSS (pin_E8) +#define MICROPY_HW_SPI3_SCK (pin_C10) +#define MICROPY_HW_SPI3_MISO (pin_C11) +#define MICROPY_HW_SPI3_MOSI (pin_C12) + +// USRSW is pulled high; pressing the button makes the input go low +#define MICROPY_HW_USRSW_PIN (pin_E0) +#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING) +#define MICROPY_HW_USRSW_PRESSED (0) + +// LEDs +#define MICROPY_HW_LED1 (pin_E12) // red +#define MICROPY_HW_LED2 (pin_E15) // red +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_high(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) + +// USB config +#define MICROPY_HW_USB_FS (1) +#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) + +// Bootloader configuration (only needed if Mboot is used) +#define MBOOT_I2C_PERIPH_ID 2 +#define MBOOT_I2C_SCL (pin_B10) +#define MBOOT_I2C_SDA (pin_B11) +#define MBOOT_I2C_ALTFUNC (4) +#define MBOOT_BOOTPIN_PIN (pin_A10) +#define MBOOT_BOOTPIN_PULL (MP_HAL_PIN_PULL_NONE) +#define MBOOT_BOOTPIN_ACTIVE (0) +#define MBOOT_FSLOAD (1) diff --git a/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.mk b/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.mk new file mode 100644 index 000000000..aa52f2116 --- /dev/null +++ b/ports/stm32/boards/MIKROE_CLICKER2_STM32/mpconfigboard.mk @@ -0,0 +1,13 @@ +MCU_SERIES = f4 +CMSIS_MCU = STM32F407xx +AF_FILE = boards/stm32f405_af.csv +ifeq ($(USE_MBOOT),1) +# When using Mboot all the text goes together after the filesystem +LD_FILES = boards/stm32f405.ld boards/common_blifs.ld +TEXT0_ADDR = 0x08020000 +else +# When not using Mboot the ISR text goes first, then the rest after the filesystem +LD_FILES = boards/stm32f405.ld boards/common_ifs.ld +TEXT0_ADDR = 0x08000000 +TEXT1_ADDR = 0x08020000 +endif diff --git a/ports/stm32/boards/MIKROE_CLICKER2_STM32/pins.csv b/ports/stm32/boards/MIKROE_CLICKER2_STM32/pins.csv new file mode 100644 index 000000000..09e1ccfb4 --- /dev/null +++ b/ports/stm32/boards/MIKROE_CLICKER2_STM32/pins.csv @@ -0,0 +1,88 @@ +MB1_AN,PA2 +MB1_RST,PE7 +MB1_CS,PE8 +MB1_SCK,PC10 +MB1_MISO,PC11 +MB1_MOSI,PC12 +MB1_PWM,PE9 +MB1_INT,PE10 +MB1_RX,PD6 +MB1_TX,PD5 +MB1_SCL,PA8 +MB1_SDA,PC9 +MB2_AN,PA3 +MB2_RST,PE13 +MB2_CS,PE11 +MB2_SCK,PB13 +MB2_MISO,PB14 +MB2_MOSI,PB15 +MB2_PWM,PD12 +MB2_INT,PE14 +MB2_RX,PD9 +MB2_TX,PD8 +MB2_SCL,PB10 +MB2_SDA,PB11 +PIN1,VSYS +PIN2,GND +PIN3,PC0 +PIN4,PC1 +PIN5,PC2 +PIN6,PC3 +PIN7,PB1 +PIN8,PA4 +PIN9,PC4 +PIN10,PD3 +PIN11,PD1 +PIN12,PD2 +PIN13,PD0 +PIN14,PC8 +PIN15,PD15 +PIN16,PD14 +PIN17,PD13 +PIN18,PB7 +PIN19,PC7 +PIN20,PD11 +PIN21,PD10 +PIN22,PB13 +PIN23,PB14 +PIN24,PB15 +PIN25,3.3V +PIN26,GND +PIN27,RST +PIN28,GND +PIN30,NC +PIN31,PB9 +PIN32,PB8 +PIN33,PE5 +PIN34,PB0 +PIN35,PA5 +PIN36,PA6 +PIN37,PA7 +PIN38,PE1 +PIN39,PE2 +PIN40,PE3 +PIN41,PE4 +PIN42,PE6 +PIN43,PB6 +PIN44,PB5 +PIN45,PD7 +PIN46,PC13 +PIN47,PA1 +PIN48,PA0 +PIN49,PB10 +PIN50,PB11 +PIN51,3.3V +PIN52,GND +OSC32_OUT,PC15 +OSC32_IN,PC14 +VSENSE,PC5 +SENSEL,PB12 +FAULT,PC6 +BATSTAT,PD4 +LD1,PE12 +LD2,PE15 +T2,PE0 +T3,PA10 +USB_VBUS,PA9 +USB_DM,PA11 +USB_DP,PA12 \ No newline at end of file diff --git a/ports/stm32/boards/MIKROE_CLICKER2_STM32/stm32f4xx_hal_conf.h b/ports/stm32/boards/MIKROE_CLICKER2_STM32/stm32f4xx_hal_conf.h new file mode 100644 index 000000000..f186d5a29 --- /dev/null +++ b/ports/stm32/boards/MIKROE_CLICKER2_STM32/stm32f4xx_hal_conf.h @@ -0,0 +1,19 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H + +#include "boards/stm32f4xx_hal_conf_base.h" + +// Oscillator values in Hz +#define HSE_VALUE (25000000) +#define LSE_VALUE (32768) +#define EXTERNAL_CLOCK_VALUE (12288000) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#endif // MICROPY_INCLUDED_STM32F4XX_HAL_CONF_H diff --git a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h index a9fbea576..c9b0c60f2 100644 --- a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h @@ -75,3 +75,14 @@ #define MICROPY_HW_USB_FS (1) #define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) #define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) + +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_C1) +#define MICROPY_HW_ETH_MDIO (pin_A2) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_A1) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_A7) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_C4) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_C5) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_G11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_G13) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_B13) diff --git a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk index 160218fd3..8b54dc84e 100644 --- a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk @@ -4,3 +4,8 @@ AF_FILE = boards/stm32f746_af.csv LD_FILES = boards/stm32f746.ld boards/common_ifs.ld TEXT0_ADDR = 0x08000000 TEXT1_ADDR = 0x08020000 + +# MicroPython settings +MICROPY_PY_LWIP = 1 +MICROPY_PY_USSL = 1 +MICROPY_SSL_MBEDTLS = 1 diff --git a/ports/stm32/boards/NUCLEO_F746ZG/pins.csv b/ports/stm32/boards/NUCLEO_F746ZG/pins.csv index aa5143e8c..c129f7417 100644 --- a/ports/stm32/boards/NUCLEO_F746ZG/pins.csv +++ b/ports/stm32/boards/NUCLEO_F746ZG/pins.csv @@ -66,3 +66,12 @@ UART6_RX,PG9 SPI_B_NSS,PA4 SPI_B_SCK,PB3 SPI_B_MOSI,PB5 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PB13 diff --git a/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h b/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h index ff4be4a27..e20dff677 100644 --- a/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h @@ -3,7 +3,7 @@ */ #define MICROPY_HW_BOARD_NAME "NUCLEO-L073RZ" -#define MICROPY_HW_MCU_NAME "STM32F073RZT6" +#define MICROPY_HW_MCU_NAME "STM32L073RZT6" #define MICROPY_EMIT_THUMB (0) #define MICROPY_EMIT_INLINE_THUMB (0) diff --git a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h index 7f2ebbad5..60e053f44 100644 --- a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h @@ -14,6 +14,7 @@ #define MICROPY_HW_ENABLE_RTC (1) #define MICROPY_HW_ENABLE_ADC (1) #define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_USB (0) // requires a custom USB connector on PA11/PA12 #define MICROPY_HW_ENABLE_TIMER (1) #define MICROPY_HW_HAS_SWITCH (0) @@ -57,4 +58,8 @@ #define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_low(pin)) // USB config -#define MICROPY_HW_USB_FS (0) +#define MICROPY_HW_USB_FS (MICROPY_HW_ENABLE_USB) +#define MICROPY_HW_USB_MSC (0) +#define MICROPY_HW_USB_HID (0) +#define USBD_CDC_RX_DATA_SIZE (256) +#define USBD_CDC_TX_DATA_SIZE (256) diff --git a/ports/stm32/boards/PYBD_SF2/board_init.c b/ports/stm32/boards/PYBD_SF2/board_init.c index a8cf10f3a..d1a0a09e7 100644 --- a/ports/stm32/boards/PYBD_SF2/board_init.c +++ b/ports/stm32/boards/PYBD_SF2/board_init.c @@ -24,9 +24,23 @@ * THE SOFTWARE. */ +#include #include "py/mphal.h" #include "storage.h" +#if defined(STM32F722xx) || defined(STM32F723xx) || defined(STM32F732xx) || defined(STM32F733xx) +#define OTP_ADDR (0x1ff079e0) +#else +#define OTP_ADDR (0x1ff0f3c0) +#endif +#define OTP ((pyb_otp_t*)OTP_ADDR) + +typedef struct _pyb_otp_t { + uint16_t series; + uint16_t rev; + uint8_t mac[6]; +} pyb_otp_t; + void mboot_board_early_init(void) { // Enable 500mA on WBUS-DIP28 mp_hal_pin_config(pyb_pin_W23, MP_HAL_PIN_MODE_INPUT, MP_HAL_PIN_PULL_UP, 0); @@ -40,7 +54,23 @@ void board_early_init(void) { spi_bdev_ioctl(&spi_bdev2, BDEV_IOCTL_INIT, (uint32_t)&spiflash2_config); } +#if !BUILDING_MBOOT + void board_sleep(int value) { mp_spiflash_deepsleep(&spi_bdev.spiflash, value); mp_spiflash_deepsleep(&spi_bdev2.spiflash, value); } + +void mp_hal_get_mac(int idx, uint8_t buf[6]) { + // Check if OTP region has a valid MAC address, and use it if it does + if (OTP->series == 0x00d1 && OTP->mac[0] == 'H' && OTP->mac[1] == 'J' && OTP->mac[2] == '0') { + memcpy(buf, OTP->mac, 6); + buf[5] += idx; + return; + } + + // Generate a random locally administered MAC address (LAA) + mp_hal_generate_laa_mac(idx, buf); +} + +#endif diff --git a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h index 9a700d8e4..a34b58e1b 100644 --- a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h @@ -100,7 +100,14 @@ void board_early_init(void); #define MICROPY_HW_ETH_RMII_TXD1 (pin_G14) #if 0 -// Optional SDRAM configuration; requires SYSCLK <= 200MHz +// Optional SDRAM configuration. + +// Note: This requires SYSCLK <= 200MHz. 192MHz example below: +// #define MICROPY_HW_CLK_PLLM (25) +// #define MICROPY_HW_CLK_PLLN (384) +// #define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +// #define MICROPY_HW_CLK_PLLQ (8) + #define MICROPY_HW_SDRAM_SIZE (128 * 1024 * 1024 / 8) // 128 Mbit #define MICROPY_HW_SDRAM_STARTUP_TEST (0) #define MICROPY_HEAP_START sdram_start() @@ -119,7 +126,7 @@ void board_early_init(void); #define MICROPY_HW_SDRAM_BURST_LENGTH 1 #define MICROPY_HW_SDRAM_CAS_LATENCY 2 #define MICROPY_HW_SDRAM_COLUMN_BITS_NUM 8 -#define MICROPY_HW_SDRAM_ROW_BITS_NUM 13 +#define MICROPY_HW_SDRAM_ROW_BITS_NUM 12 #define MICROPY_HW_SDRAM_MEM_BUS_WIDTH 32 #define MICROPY_HW_SDRAM_INTERN_BANKS_NUM 4 #define MICROPY_HW_SDRAM_CLOCK_PERIOD 2 diff --git a/ports/stm32/boards/stm32wb55_af.csv b/ports/stm32/boards/stm32wb55_af.csv index 6e4ddd960..074c33089 100644 --- a/ports/stm32/boards/stm32wb55_af.csv +++ b/ports/stm32/boards/stm32wb55_af.csv @@ -1,23 +1,23 @@ Port,,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF14,AF15, ,,SYS_AF,TIM1/TIM2/LPTIM1,TIM1/TIM2,SPI2/SAI1/TIM1,I2C1/I2C3,SPI1/SPI2,RF,USART1,LPUART1,TSC,USB/QUADSPI,LCD,COMP1/COMP2/TIM1,SAI1,TIM2/TIM16/TIM17/LPTIM2,EVENTOUT,ADC -PortA,PA0,,TIM2_CH1,,,,,,,,,,,COMP1_OUT,SAI1_EXTCLK,TIM2_ETR,EVENTOUT,ADC123_IN0 -PortA,PA1,,TIM2_CH2,,,I2C1_SMBA,SPI1_SCK,,,,,,LCD_SEG0,,,,EVENTOUT,ADC123_IN1 -PortA,PA2,LSCO,TIM2_CH3,,,,,,,LPUART1_TX,,QUADSPI_BK1_NCS,LCD_SEG1,COMP2_OUT,,,EVENTOUT,ADC123_IN2 -PortA,PA3,,TIM2_CH4,,SAI1_PDM_CK1,,,,,LPUART1_RX,,QUADSPI_CLK,LCD_SEG2,,SAI1_MCLK_A,,EVENTOUT,ADC123_IN3 -PortA,PA4,,,,,,SPI1_NSS,,,,,,LCD_SEG5,,SAI1_FS_B,LPTIM2_OUT,EVENTOUT,ADC12_IN4 -PortA,PA5,,TIM2_CH1,TIM2_ETR,,,SPI1_SCK,,,,,,,,SAI1_SD_B,LPTIM2_ETR,EVENTOUT,ADC12_IN5 -PortA,PA6,,TIM1_BKIN,,,,SPI1_MISO,,,LPUART1_CTS,,QUADSPI_BK1_IO3,LCD_SEG3,TIM1_BKIN,,TIM16_CH1,EVENTOUT,ADC12_IN6 -PortA,PA7,,TIM1_CH1N,,,I2C3_SCL,SPI1_MOSI,,,,,QUADSPI_BK1_IO2,LCD_SEG4,COMP2_OUT,,TIM17_CH1,EVENTOUT,ADC12_IN7 -PortA,PA8,MCO,TIM1_CH1,,SAI1_PDM_CK2,,,,USART1_CK,,,,LCD_COM0,,SAI1_SCK_A,LPTIM2_OUT,EVENTOUT, -PortA,PA9,,TIM1_CH2,,SAI1_PDM_DI2,I2C1_SCL,SPI2_SCK,,USART1_TX,,,,LCD_COM1,,SAI1_FS_A,,EVENTOUT, +PortA,PA0,,TIM2_CH1,,,,,,,,,,,COMP1_OUT,SAI1_EXTCLK,TIM2_ETR,EVENTOUT,ADC1_IN5 +PortA,PA1,,TIM2_CH2,,,I2C1_SMBA,SPI1_SCK,,,,,,LCD_SEG0,,,,EVENTOUT,ADC1_IN6 +PortA,PA2,LSCO,TIM2_CH3,,,,,,,LPUART1_TX,,QUADSPI_BK1_NCS,LCD_SEG1,COMP2_OUT,,,EVENTOUT,ADC1_IN7 +PortA,PA3,,TIM2_CH4,,SAI1_PDM_CK1,,,,,LPUART1_RX,,QUADSPI_CLK,LCD_SEG2,,SAI1_MCLK_A,,EVENTOUT,ADC1_IN8 +PortA,PA4,,,,,,SPI1_NSS,,,,,,LCD_SEG5,,SAI1_FS_B,LPTIM2_OUT,EVENTOUT,ADC1_IN9 +PortA,PA5,,TIM2_CH1,TIM2_ETR,,,SPI1_SCK,,,,,,,,SAI1_SD_B,LPTIM2_ETR,EVENTOUT,ADC1_IN10 +PortA,PA6,,TIM1_BKIN,,,,SPI1_MISO,,,LPUART1_CTS,,QUADSPI_BK1_IO3,LCD_SEG3,TIM1_BKIN,,TIM16_CH1,EVENTOUT,ADC1_IN11 +PortA,PA7,,TIM1_CH1N,,,I2C3_SCL,SPI1_MOSI,,,,,QUADSPI_BK1_IO2,LCD_SEG4,COMP2_OUT,,TIM17_CH1,EVENTOUT,ADC1_IN12 +PortA,PA8,MCO,TIM1_CH1,,SAI1_PDM_CK2,,,,USART1_CK,,,,LCD_COM0,,SAI1_SCK_A,LPTIM2_OUT,EVENTOUT,ADC1_IN15 +PortA,PA9,,TIM1_CH2,,SAI1_PDM_DI2,I2C1_SCL,SPI2_SCK,,USART1_TX,,,,LCD_COM1,,SAI1_FS_A,,EVENTOUT,ADC1_IN16 PortA,PA10,,TIM1_CH3,,SAI1_PDM_DI1,I2C1_SDA,,,USART1_RX,,,USB_CRS_SYNC,LCD_COM2,,SAI1_SD_A,TIM17_BKIN,EVENTOUT, PortA,PA11,,TIM1_CH4,TIM1_BKIN2,,,SPI1_MISO,,USART1_CTS,,,USB_DM,,TIM1_BKIN2,,,EVENTOUT, PortA,PA12,,TIM1_ETR,,,,SPI1_MOSI,,USART1_RTS_DE,LPUART1_RX,,USB_DP,,,,,EVENTOUT, PortA,PA13,JTMS/SWDIO,,,,,,,,IR_OUT,,USB_NOE,,,SAI1_SD_B,,EVENTOUT, PortA,PA14,JTCK/SWCLK,LPTIM1_OUT,,,I2C1_SMBA,,,,,,,LCD_SEG5,,SAI1_FS_B,,EVENTOUT, PortA,PA15,JTDI,TIM2_CH1,TIM2_ETR,,,SPI1_NSS,,,,TSC_G3_IO1,,LCD_SEG17,,,,EVENTOUT, -PortB,PB0,,,,,,,EXT_PA_TX,,,,,,COMP1_OUT,,,EVENTOUT,ADC12_IN8 -PortB,PB1,,,,,,,,,LPUART1_RTS_DE,,,,,,LPTIM2_IN1,EVENTOUT,ADC12_IN9 +PortB,PB0,,,,,,,EXT_PA_TX,,,,,,COMP1_OUT,,,EVENTOUT, +PortB,PB1,,,,,,,,,LPUART1_RTS_DE,,,,,,LPTIM2_IN1,EVENTOUT, PortB,PB2,RTC_OUT,LPTIM1_OUT,,,I2C3_SMBA,SPI1_NSS,,,,,,LCD_VLCD,,SAI1_EXTCLK,,EVENTOUT, PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK,,USART1_RTS_DE,,,,LCD_SEG7,,SAI1_SCK_B,,EVENTOUT, PortB,PB4,NJTRST,,,,I2C3_SDA,SPI1_MISO,,USART1_CTS,,TSC_G2_IO1,,LCD_SEG8,,SAI1_MCLK_B,TIM17_BKIN,EVENTOUT, @@ -32,12 +32,12 @@ PortB,PB12,,TIM1_BKIN,,TIM1_BKIN,I2C3_SMBA,SPI2_NSS,,,LPUART1_RTS,TSC_G1_IO1,,LC PortB,PB13,,TIM1_CH1N,,,I2C3_SCL,SPI2_SCK,,,LPUART1_CTS,TSC_G1_IO2,,LCD_SEG13,,SAI1_SCK_A,,EVENTOUT, PortB,PB14,,TIM1_CH2N,,,I2C3_SDA,SPI2_MISO,,,,TSC_G1_IO3,,LCD_SEG14,,SAI1_MCLK_A,,EVENTOUT, PortB,PB15,RTC_REFIN,TIM1_CH3N,,,,SPI2_MOSI,,,,TSC_G1_IO4,,LCD_SEG15,,SAI1_SD_A,,EVENTOUT, -PortC,PC0,,LPTIM1_IN1,,,I2C3_SCL,,,,LPUART1_RX,,,LCD_SEG18,,,LPTIM2_IN1,EVENTOUT,ADC123_IN10 -PortC,PC1,,LPTIM1_OUT,,SPI2_MOSI,I2C3_SDA,,,,LPUART1_TX,,,LCD_SEG19,,,,EVENTOUT,ADC123_IN11 -PortC,PC2,,LPTIM1_IN2,,,,SPI2_MISO,,,,,,LCD_SEG20,,,,EVENTOUT,ADC123_IN12 -PortC,PC3,,LPTIM1_ETR,,SAI1_PDM_DI1,,SPI2_MOSI,,,,,,LCD_VLCD,,SAI1_SD_A,LPTIM2_ETR,EVENTOUT,ADC123_IN13 -PortC,PC4,,,,,,,,,,,,LCD_SEG22,,,,EVENTOUT,ADC12_IN14 -PortC,PC5,,,,SAI1_PDM_DI3,,,,,,,,LCD_SEG23,,,,EVENTOUT,ADC12_IN15 +PortC,PC0,,LPTIM1_IN1,,,I2C3_SCL,,,,LPUART1_RX,,,LCD_SEG18,,,LPTIM2_IN1,EVENTOUT,ADC1_IN1 +PortC,PC1,,LPTIM1_OUT,,SPI2_MOSI,I2C3_SDA,,,,LPUART1_TX,,,LCD_SEG19,,,,EVENTOUT,ADC1_IN2 +PortC,PC2,,LPTIM1_IN2,,,,SPI2_MISO,,,,,,LCD_SEG20,,,,EVENTOUT,ADC1_IN3 +PortC,PC3,,LPTIM1_ETR,,SAI1_PDM_DI1,,SPI2_MOSI,,,,,,LCD_VLCD,,SAI1_SD_A,LPTIM2_ETR,EVENTOUT,ADC1_IN4 +PortC,PC4,,,,,,,,,,,,LCD_SEG22,,,,EVENTOUT,ADC1_IN13 +PortC,PC5,,,,SAI1_PDM_DI3,,,,,,,,LCD_SEG23,,,,EVENTOUT,ADC1_IN14 PortC,PC6,,,,,,,,,,TSC_G4_IO1,,LCD_SEG24,,,,EVENTOUT, PortC,PC7,,,,,,,,,,TSC_G4_IO2,,LCD_SEG25,,,,EVENTOUT, PortC,PC8,,,,,,,,,,TSC_G4_IO3,,LCD_SEG26,,,,EVENTOUT, diff --git a/ports/stm32/dac.c b/ports/stm32/dac.c index f3dcccb3d..485829c59 100644 --- a/ports/stm32/dac.c +++ b/ports/stm32/dac.c @@ -373,15 +373,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_dac_noise_obj, pyb_dac_noise); #if defined(TIM6) /// \method triangle(freq) /// Generate a triangle wave. The value on the DAC output changes at -/// the given frequency, and the frequence of the repeating triangle wave -/// itself is 256 (or 1024, need to check) times smaller. +/// the given frequency, and the frequency of the repeating triangle wave +/// itself is 8192 times smaller. STATIC mp_obj_t pyb_dac_triangle(mp_obj_t self_in, mp_obj_t freq) { pyb_dac_obj_t *self = MP_OBJ_TO_PTR(self_in); // set TIM6 to trigger the DAC at the given frequency TIM6_Config(mp_obj_get_int(freq)); - // Configure DAC in triangle mode with trigger via TIM6 + // Configure DAC in full-scale triangle mode with trigger via TIM6 uint32_t cr = DAC_TRIANGLEAMPLITUDE_4095 | DAC_CR_WAVE1_1 | DAC_TRIGGER_T6_TRGO; pyb_dac_reconfigure(self, cr, self->outbuf_waveform, 0); diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index 3d275684d..734cf2e98 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -97,7 +97,7 @@ struct _dma_descr_t { static const DMA_InitTypeDef dma_init_struct_spi_i2c = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32H7) || defined(STM32L4) + #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) .Request = 0, #endif .Direction = 0, @@ -120,7 +120,7 @@ static const DMA_InitTypeDef dma_init_struct_spi_i2c = { static const DMA_InitTypeDef dma_init_struct_sdio = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32L4) + #elif defined(STM32L0) || defined(STM32L4) .Request = 0, #endif .Direction = 0, @@ -130,7 +130,7 @@ static const DMA_InitTypeDef dma_init_struct_sdio = { .MemDataAlignment = DMA_MDATAALIGN_WORD, #if defined(STM32F4) || defined(STM32F7) .Mode = DMA_PFCTRL, - #elif defined(STM32L4) + #elif defined(STM32L0) || defined(STM32L4) .Mode = DMA_NORMAL, #endif .Priority = DMA_PRIORITY_VERY_HIGH, @@ -148,7 +148,7 @@ static const DMA_InitTypeDef dma_init_struct_sdio = { static const DMA_InitTypeDef dma_init_struct_dac = { #if defined(STM32F4) || defined(STM32F7) .Channel = 0, - #elif defined(STM32H7) || defined(STM32L4) + #elif defined(STM32H7) || defined(STM32L0) || defined(STM32L4) .Request = 0, #endif .Direction = 0, @@ -336,31 +336,31 @@ static const uint8_t dma_irqn[NSTREAM] = { // number. The duplicate streams are ok as long as they aren't used at the same time. // DMA1 streams -const dma_descr_t dma_SPI_1_RX = { DMA1_Channel2, DMA_REQUEST_1, dma_id_1, &dma_init_struct_spi_i2c }; -const dma_descr_t dma_I2C_3_TX = { DMA1_Channel2, DMA_REQUEST_3, dma_id_1, &dma_init_struct_spi_i2c }; -const dma_descr_t dma_SPI_1_TX = { DMA1_Channel3, DMA_REQUEST_1, dma_id_2, &dma_init_struct_spi_i2c }; -const dma_descr_t dma_I2C_3_RX = { DMA1_Channel3, DMA_REQUEST_3, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_1_RX = { DMA1_Channel2, DMA_REQUEST_1, dma_id_1, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_3_TX = { DMA1_Channel2, DMA_REQUEST_14, dma_id_1, &dma_init_struct_spi_i2c }; #if MICROPY_HW_ENABLE_DAC -const dma_descr_t dma_DAC_1_TX = { DMA1_Channel3, DMA_REQUEST_6, dma_id_2, &dma_init_struct_dac }; +const dma_descr_t dma_DAC_1_TX = { DMA1_Channel2, DMA_REQUEST_9, dma_id_1, &dma_init_struct_dac }; #endif -const dma_descr_t dma_SPI_2_RX = { DMA1_Channel4, DMA_REQUEST_1, dma_id_3, &dma_init_struct_spi_i2c }; -const dma_descr_t dma_I2C_2_TX = { DMA1_Channel4, DMA_REQUEST_3, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_1_TX = { DMA1_Channel3, DMA_REQUEST_1, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_3_RX = { DMA1_Channel3, DMA_REQUEST_14, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_RX = { DMA1_Channel4, DMA_REQUEST_2, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_2_TX = { DMA1_Channel4, DMA_REQUEST_7, dma_id_3, &dma_init_struct_spi_i2c }; #if MICROPY_HW_ENABLE_DAC -const dma_descr_t dma_DAC_2_TX = { DMA1_Channel4, DMA_REQUEST_5, dma_id_3, &dma_init_struct_dac }; +const dma_descr_t dma_DAC_2_TX = { DMA1_Channel4, DMA_REQUEST_15, dma_id_3, &dma_init_struct_dac }; #endif -const dma_descr_t dma_SPI_2_TX = { DMA1_Channel5, DMA_REQUEST_1, dma_id_4, &dma_init_struct_spi_i2c }; -const dma_descr_t dma_I2C_2_RX = { DMA1_Channel5, DMA_REQUEST_3, dma_id_4, &dma_init_struct_spi_i2c }; -const dma_descr_t dma_I2C_1_TX = { DMA1_Channel6, DMA_REQUEST_3, dma_id_5, &dma_init_struct_spi_i2c }; -const dma_descr_t dma_I2C_1_RX = { DMA1_Channel7, DMA_REQUEST_3, dma_id_6, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_TX = { DMA1_Channel5, DMA_REQUEST_2, dma_id_4, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_2_RX = { DMA1_Channel5, DMA_REQUEST_7, dma_id_4, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_1_TX = { DMA1_Channel6, DMA_REQUEST_6, dma_id_5, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_I2C_1_RX = { DMA1_Channel7, DMA_REQUEST_6, dma_id_6, &dma_init_struct_spi_i2c }; static const uint8_t dma_irqn[NSTREAM] = { DMA1_Channel1_IRQn, DMA1_Channel2_3_IRQn, + DMA1_Channel2_3_IRQn, + DMA1_Channel4_5_6_7_IRQn, + DMA1_Channel4_5_6_7_IRQn, + DMA1_Channel4_5_6_7_IRQn, DMA1_Channel4_5_6_7_IRQn, - 0, - 0, - 0, - 0, }; #elif defined(STM32L4) @@ -724,10 +724,10 @@ void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir dma_enable_clock(dma_id); - #if defined(STM32H7) || defined(STM32L4) - // Always reset and configure the H7 and L4 DMA peripheral + #if defined(STM32H7) || defined(STM32L0) || defined(STM32L4) + // Always reset and configure the H7 and L0/L4 DMA peripheral // (dma->State is set to HAL_DMA_STATE_RESET by memset above) - // TODO: understand how L4 DMA works so this is not needed + // TODO: understand how L0/L4 DMA works so this is not needed HAL_DMA_DeInit(dma); HAL_DMA_Init(dma); NVIC_SetPriority(IRQn_NONNEG(dma_irqn[dma_id]), IRQ_PRI_DMA); diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c index 836b0c595..c5b3b0c6a 100644 --- a/ports/stm32/extint.c +++ b/ports/stm32/extint.c @@ -163,7 +163,12 @@ STATIC const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, - #if defined(STM32WB) + #if defined(STM32H7) + PVD_AVD_IRQn, + RTC_Alarm_IRQn, + TAMP_STAMP_IRQn, + RTC_WKUP_IRQn, + #elif defined(STM32WB) PVD_PVM_IRQn, RTC_Alarm_IRQn, TAMP_STAMP_LSECSS_IRQn, diff --git a/ports/stm32/i2c.c b/ports/stm32/i2c.c index 06e26d912..5981df11c 100644 --- a/ports/stm32/i2c.c +++ b/ports/stm32/i2c.c @@ -340,8 +340,7 @@ STATIC int i2c_wait_isr_set(i2c_t *i2c, uint32_t mask) { int i2c_start_addr(i2c_t *i2c, int rd_wrn, uint16_t addr, size_t len, bool stop) { // Enable the peripheral and send the START condition with slave address i2c->CR1 |= I2C_CR1_PE; - i2c->CR2 = stop << I2C_CR2_AUTOEND_Pos - | (len > 1) << I2C_CR2_RELOAD_Pos + i2c->CR2 = (len > 1) << I2C_CR2_RELOAD_Pos | (len > 0) << I2C_CR2_NBYTES_Pos | rd_wrn << I2C_CR2_RD_WRN_Pos | (addr & 0x7f) << 1; @@ -361,6 +360,11 @@ int i2c_start_addr(i2c_t *i2c, int rd_wrn, uint16_t addr, size_t len, bool stop) return -MP_ENODEV; } + // Configure automatic STOP if needed + if (stop) { + i2c->CR2 |= I2C_CR2_AUTOEND; + } + // Repurpose OAR1 to indicate that we loaded CR2 i2c->OAR1 = 1; diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index c9bbde92f..7b2460fa8 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -22,6 +22,7 @@ #define LWIP_SOCKET 0 #define LWIP_STATS 0 #define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 #define LWIP_IPV6 0 #define LWIP_DHCP 1 @@ -32,8 +33,9 @@ #define LWIP_MDNS_RESPONDER 1 #define LWIP_IGMP 1 -#define LWIP_NUM_NETIF_CLIENT_DATA 1 // mDNS responder requires 1 -#define MEMP_NUM_UDP_PCB 5 // mDNS responder requires 1 +#define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER +#define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) +#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + LWIP_MDNS_RESPONDER) #define SO_REUSE 1 #define TCP_LISTEN_BACKLOG 1 diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c new file mode 100644 index 000000000..3aacae77b --- /dev/null +++ b/ports/stm32/machine_adc.c @@ -0,0 +1,450 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 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 "py/runtime.h" +#include "py/mphal.h" + +#if defined(STM32F0) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) +#define ADC_V2 (1) +#else +#define ADC_V2 (0) +#endif + +#if defined(STM32F4) || defined(STM32L4) +#define ADCx_COMMON ADC_COMMON_REGISTER(0) +#elif defined(STM32F7) +#define ADCx_COMMON ADC123_COMMON +#endif + +#if defined(STM32F0) || defined(STM32L0) +#define ADC_STAB_DELAY_US (1) +#define ADC_TEMPSENSOR_DELAY_US (10) +#elif defined(STM32L4) +#define ADC_STAB_DELAY_US (10) +#elif defined(STM32WB) +#define ADC_STAB_DELAY_US (1) +#endif + +#if defined(STM32F0) +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_71CYCLES_5 +#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_239CYCLES_5 +#elif defined(STM32F4) || defined(STM32F7) +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_15CYCLES +#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_480CYCLES +#elif defined(STM32H7) +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_8CYCLES_5 +#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_387CYCLES_5 +#elif defined(STM32L0) +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5 +#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_160CYCLES_5 +#elif defined(STM32L4) || defined(STM32WB) +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5 +#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_247CYCLES_5 +#endif + +// Timeout for waiting for end-of-conversion +#define ADC_EOC_TIMEOUT_MS (10) + +// This is a synthesised channel representing the maximum ADC reading (useful to scale other channels) +#define ADC_CHANNEL_VREF (0xffff) + +static inline void adc_stabilisation_delay_us(uint32_t us) { + mp_hal_delay_us(us + 1); +} + +STATIC void adc_wait_eoc(ADC_TypeDef *adc, int32_t timeout_ms) { + uint32_t t0 = mp_hal_ticks_ms(); + #if ADC_V2 + while (!(adc->ISR & ADC_ISR_EOC)) + #else + while (!(adc->SR & ADC_SR_EOC)) + #endif + { + if (mp_hal_ticks_ms() - t0 > timeout_ms) { + break; // timeout + } + } +} + +#if defined(STM32H7) +STATIC const uint8_t adc_cr_to_bits_table[] = {16, 14, 12, 10, 8, 8, 8, 8}; +#else +STATIC const uint8_t adc_cr_to_bits_table[] = {12, 10, 8, 6}; +#endif + +STATIC void adc_config(ADC_TypeDef *adc, uint32_t bits) { + // Configure ADC clock source and enable ADC clock + #if defined(STM32L4) || defined(STM32WB) + __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_SYSCLK); + __HAL_RCC_ADC_CLK_ENABLE(); + #else + if (adc == ADC1) { + #if defined(STM32H7) + __HAL_RCC_ADC12_CLK_ENABLE(); + #else + __HAL_RCC_ADC1_CLK_ENABLE(); + #endif + } + #if defined(ADC2) + if (adc == ADC2) { + #if defined(STM32H7) + __HAL_RCC_ADC12_CLK_ENABLE(); + #else + __HAL_RCC_ADC2_CLK_ENABLE(); + #endif + } + #endif + #if defined(ADC3) + if (adc == ADC3) { + __HAL_RCC_ADC3_CLK_ENABLE(); + } + #endif + #endif + + // Configure clock mode + #if defined(STM32F0) + adc->CFGR2 = 1 << ADC_CFGR2_CKMODE_Pos; // PCLK/2 (synchronous clock mode) + #elif defined(STM32F4) || defined(STM32F7) || defined(STM32L4) + ADCx_COMMON->CCR = 0; // ADCPR=PCLK/2 + #elif defined(STM32H7) + ADC12_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos; + ADC3_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos; + #elif defined(STM32L0) || defined(STM32WB) + ADC1_COMMON->CCR = 0; // ADCPR=PCLK/2 + #endif + + #if defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + if (adc->CR & ADC_CR_DEEPPWD) { + adc->CR = 0; // disable deep powerdown + } + #endif + + #if defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) + if (!(adc->CR & ADC_CR_ADVREGEN)) { + adc->CR = ADC_CR_ADVREGEN; // enable VREG + #if defined(STM32H7) + mp_hal_delay_us(10); // T_ADCVREG_STUP + #elif defined(STM32L4) || defined(STM32WB) + mp_hal_delay_us(20); // T_ADCVREG_STUP + #endif + } + #endif + + #if ADC_V2 + if (adc->CR == 0) { + // ADC hasn't been enabled so calibrate it + adc->CR |= ADC_CR_ADCAL; + while (adc->CR & ADC_CR_ADCAL) { + } + } + + if (adc->CR & ADC_CR_ADEN) { + // ADC enabled, need to disable it to change configuration + if (adc->CR & ADC_CR_ADSTART) { + adc->CR |= ADC_CR_ADSTP; + while (adc->CR & ADC_CR_ADSTP) { + } + } + adc->CR |= ADC_CR_ADDIS; + while (adc->CR & ADC_CR_ADDIS) { + } + } + #endif + + // Find resolution, defaulting to last element in table + uint32_t res; + for (res = 0; res <= MP_ARRAY_SIZE(adc_cr_to_bits_table); ++res) { + if (adc_cr_to_bits_table[res] == bits) { + break; + } + } + + #if defined(STM32F0) || defined(STM32L0) + + uint32_t cfgr1_clr = ADC_CFGR1_CONT | ADC_CFGR1_EXTEN | ADC_CFGR1_ALIGN | ADC_CFGR1_RES | ADC_CFGR1_DMAEN; + uint32_t cfgr1 = res << ADC_CFGR1_RES_Pos; + adc->CFGR1 = (adc->CFGR1 & ~cfgr1_clr) | cfgr1; + + #elif defined(STM32F4) || defined(STM32F7) + + uint32_t cr1_clr = ADC_CR1_RES; + uint32_t cr1 = res << ADC_CR1_RES_Pos; + adc->CR1 = (adc->CR1 & ~cr1_clr) | cr1; + uint32_t cr2_clr = ADC_CR2_EXTEN | ADC_CR2_ALIGN | ADC_CR2_DMA | ADC_CR2_CONT; + uint32_t cr2 = 0; + adc->CR2 = (adc->CR2 & ~cr2_clr) | cr2; + adc->SQR1 = 1 << ADC_SQR1_L_Pos; // 1 conversion in regular sequence + + #elif defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + + uint32_t cfgr_clr = ADC_CFGR_CONT | ADC_CFGR_EXTEN | ADC_CFGR_RES; + #if defined(STM32H7) + cfgr_clr |= ADC_CFGR_DMNGT; + #else + cfgr_clr |= ADC_CFGR_ALIGN | ADC_CFGR_DMAEN; + #endif + uint32_t cfgr = res << ADC_CFGR_RES_Pos; + adc->CFGR = (adc->CFGR & ~cfgr_clr) | cfgr; + + #endif +} + +STATIC int adc_get_bits(ADC_TypeDef *adc) { + #if defined(STM32F0) || defined(STM32L0) + uint32_t res = (adc->CFGR1 & ADC_CFGR1_RES) >> ADC_CFGR1_RES_Pos; + #elif defined(STM32F4) || defined(STM32F7) + uint32_t res = (adc->CR1 & ADC_CR1_RES) >> ADC_CR1_RES_Pos; + #elif defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + uint32_t res = (adc->CFGR & ADC_CFGR_RES) >> ADC_CFGR_RES_Pos; + #endif + return adc_cr_to_bits_table[res]; +} + +STATIC void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t sample_time) { + #if ADC_V2 + if (!(adc->CR & ADC_CR_ADEN)) { + if (adc->CR & 0x3f) { + // Cannot enable ADC with CR!=0 + return; + } + adc->ISR = ADC_ISR_ADRDY; // clear ADRDY + adc->CR |= ADC_CR_ADEN; + adc_stabilisation_delay_us(ADC_STAB_DELAY_US); + while (!(adc->ISR & ADC_ISR_ADRDY)) { + } + } + #else + if (!(adc->CR2 & ADC_CR2_ADON)) { + adc->CR2 |= ADC_CR2_ADON; + adc_stabilisation_delay_us(ADC_STAB_DELAY_US); + } + #endif + + #if defined(STM32F0) || defined(STM32L0) + + if (channel == ADC_CHANNEL_VREFINT) { + ADC1_COMMON->CCR |= ADC_CCR_VREFEN; + } else if (channel == ADC_CHANNEL_TEMPSENSOR) { + ADC1_COMMON->CCR |= ADC_CCR_TSEN; + adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US); + #if defined(ADC_CHANNEL_VBAT) + } else if (channel == ADC_CHANNEL_VBAT) { + ADC1_COMMON->CCR |= ADC_CCR_VBATEN; + #endif + } + adc->SMPR = sample_time << ADC_SMPR_SMP_Pos; // select sample time + adc->CHSELR = 1 << channel; // select channel for conversion + + #elif defined(STM32F4) || defined(STM32F7) + + if (channel == ADC_CHANNEL_VREFINT || channel == ADC_CHANNEL_TEMPSENSOR) { + ADCx_COMMON->CCR = (ADCx_COMMON->CCR & ~ADC_CCR_VBATE) | ADC_CCR_TSVREFE; + if (channel == ADC_CHANNEL_TEMPSENSOR) { + adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US); + } + } else if (channel == ADC_CHANNEL_VBAT) { + ADCx_COMMON->CCR |= ADC_CCR_VBATE; + } + + adc->SQR3 = (channel & 0x1f) << ADC_SQR3_SQ1_Pos; // select channel for first conversion + + __IO uint32_t *smpr; + if (channel <= 9) { + smpr = &adc->SMPR2; + } else { + smpr = &adc->SMPR1; + channel -= 10; + } + *smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time + + #elif defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + + #if defined(STM32H7) + adc->PCSEL |= 1 << channel; + ADC_Common_TypeDef *adc_common = adc == ADC3 ? ADC3_COMMON : ADC12_COMMON; + #elif defined(STM32L4) + ADC_Common_TypeDef *adc_common = ADCx_COMMON; + #elif defined(STM32WB) + ADC_Common_TypeDef *adc_common = ADC1_COMMON; + #endif + if (channel == ADC_CHANNEL_VREFINT) { + adc_common->CCR |= ADC_CCR_VREFEN; + } else if (channel == ADC_CHANNEL_TEMPSENSOR) { + adc_common->CCR |= ADC_CCR_TSEN; + adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US); + } else if (channel == ADC_CHANNEL_VBAT) { + adc_common->CCR |= ADC_CCR_VBATEN; + } + adc->SQR1 = (channel & 0x1f) << ADC_SQR1_SQ1_Pos | (1 - 1) << ADC_SQR1_L_Pos; + __IO uint32_t *smpr; + if (channel <= 9) { + smpr = &adc->SMPR1; + } else { + smpr = &adc->SMPR2; + channel -= 10; + } + *smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time + + #endif +} + +STATIC uint32_t adc_read_channel(ADC_TypeDef *adc) { + #if ADC_V2 + adc->CR |= ADC_CR_ADSTART; + #else + adc->CR2 |= ADC_CR2_SWSTART; + #endif + adc_wait_eoc(adc, ADC_EOC_TIMEOUT_MS); + uint32_t value = adc->DR; + return value; +} + +STATIC uint32_t adc_config_and_read_u16(ADC_TypeDef *adc, uint32_t channel, uint32_t sample_time) { + if (channel == ADC_CHANNEL_VREF) { + return 0xffff; + } + + adc_config_channel(adc, channel, sample_time); + uint32_t raw = adc_read_channel(adc); + uint32_t bits = adc_get_bits(adc); + // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) + #if defined(STM32H7) + if (bits < 8) { + // For 6 and 7 bits + return raw << (16 - bits) | raw << (16 - 2 * bits) | raw >> (3 * bits - 16); + } + #endif + return raw << (16 - bits) | raw >> (2 * bits - 16); +} + +/******************************************************************************/ +// MicroPython bindings for machine.ADC + +const mp_obj_type_t machine_adc_type; + +typedef struct _machine_adc_obj_t { + mp_obj_base_t base; + ADC_TypeDef *adc; + uint32_t channel; + uint32_t sample_time; +} machine_adc_obj_t; + +STATIC void machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + unsigned adc_id = 1; + #if defined(ADC2) + if (self->adc == ADC2) { + adc_id = 2; + } + #endif + #if defined(ADC3) + if (self->adc == ADC3) { + adc_id = 3; + } + #endif + mp_printf(print, "", adc_id, self->channel); +} + +// ADC(id) +STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t source = all_args[0]; + + uint32_t channel; + uint32_t sample_time = ADC_SAMPLETIME_DEFAULT; + ADC_TypeDef *adc; + if (mp_obj_is_int(source)) { + adc = ADC1; + channel = mp_obj_get_int(source); + if (channel == ADC_CHANNEL_VREFINT + || channel == ADC_CHANNEL_TEMPSENSOR + #if defined(ADC_CHANNEL_VBAT) + || channel == ADC_CHANNEL_VBAT + #endif + ) { + sample_time = ADC_SAMPLETIME_DEFAULT_INT; + } + } else { + const pin_obj_t *pin = pin_find(source); + if (pin->adc_num & PIN_ADC1) { + adc = ADC1; + #if defined(ADC2) + } else if (pin->adc_num & PIN_ADC2) { + adc = ADC2; + #endif + #if defined(ADC2) + } else if (pin->adc_num & PIN_ADC3) { + adc = ADC3; + #endif + } else { + // No ADC function on given pin + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Pin(%q) does not have ADC capabilities", pin->name)); + } + channel = pin->adc_channel; + + // Configure the GPIO pin in ADC mode + mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ADC, MP_HAL_PIN_PULL_NONE, 0); + } + + adc_config(adc, 12); + + machine_adc_obj_t *o = m_new_obj(machine_adc_obj_t); + o->base.type = &machine_adc_type; + o->adc = adc; + o->channel = channel; + o->sample_time = sample_time; + + return MP_OBJ_FROM_PTR(o); +} + +// read_u16() +STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(adc_config_and_read_u16(self->adc, self->channel, self->sample_time)); +} +MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_u16_obj, machine_adc_read_u16); + +STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&machine_adc_read_u16_obj) }, + + { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_INT(ADC_CHANNEL_VREF) }, + { MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(ADC_CHANNEL_VREFINT) }, + { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR) }, + #if defined(ADC_CHANNEL_VBAT) + { MP_ROM_QSTR(MP_QSTR_CORE_VBAT), MP_ROM_INT(ADC_CHANNEL_VBAT) }, + #endif +}; +STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table); + +const mp_obj_type_t machine_adc_type = { + { &mp_type_type }, + .name = MP_QSTR_ADC, + .print = machine_adc_print, + .make_new = machine_adc_make_new, + .locals_dict = (mp_obj_dict_t*)&machine_adc_locals_dict, +}; diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index f2c3baecc..19635b918 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -63,6 +63,7 @@ CFLAGS += -DSTM32_HAL_H='' CFLAGS += -DBOARD_$(BOARD) CFLAGS += -DAPPLICATION_ADDR=$(TEXT0_ADDR) CFLAGS += -DFFCONF_H=\"ports/stm32/mboot/ffconf.h\" +CFLAGS += -DBUILDING_MBOOT=1 LDFLAGS = -nostdlib -L . -T stm32_generic.ld -Map=$(@:.elf=.map) --cref LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index f6b89004d..a0f1f1eac 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -408,7 +408,9 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { #define LED0 MICROPY_HW_LED1 #define LED1 MICROPY_HW_LED2 +#ifdef MICROPY_HW_LED3 #define LED2 MICROPY_HW_LED3 +#endif #ifdef MICROPY_HW_LED4 #define LED3 MICROPY_HW_LED4 #endif @@ -416,7 +418,9 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { void led_init(void) { mp_hal_pin_output(LED0); mp_hal_pin_output(LED1); + #ifdef LED2 mp_hal_pin_output(LED2); + #endif #ifdef LED3 mp_hal_pin_output(LED3); #endif @@ -436,7 +440,9 @@ void led_state(int led, int val) { void led_state_all(unsigned int mask) { led_state(LED0, mask & 1); led_state(LED1, mask & 2); + #ifdef LED2 led_state(LED2, mask & 4); + #endif #ifdef LED3 led_state(LED3, mask & 8); #endif @@ -1088,6 +1094,13 @@ typedef struct _pyb_usbdd_obj_t { #define MBOOT_USB_PID 0xDF11 #endif +static const uint8_t usbd_fifo_size[] = { + 32, 8, 16, 8, 16, 0, 0, // FS: RX, EP0(in), 5x IN endpoints + #if MICROPY_HW_USB_HS + 116, 8, 64, 4, 64, 0, 0, 0, 0, 0, // HS: RX, EP0(in), 8x IN endpoints + #endif +}; + __ALIGN_BEGIN static const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = { USB_LEN_LANGID_STR_DESC, USB_DESC_TYPE_STRING, @@ -1315,7 +1328,7 @@ static void pyb_usbdd_start(pyb_usbdd_obj_t *self) { while (!(PWR->CR3 & PWR_CR3_USB33RDY)) { } #endif - USBD_LL_Init(&self->hUSBDDevice, 0); + USBD_LL_Init(&self->hUSBDDevice, 0, usbd_fifo_size); USBD_LL_Start(&self->hUSBDDevice); self->started = true; } @@ -1338,11 +1351,15 @@ static int pyb_usbdd_shutdown(void) { #define RESET_MODE_NUM_STATES (4) #define RESET_MODE_TIMEOUT_CYCLES (8) +#ifdef LED2 #ifdef LED3 #define RESET_MODE_LED_STATES 0x8421 #else #define RESET_MODE_LED_STATES 0x7421 #endif +#else +#define RESET_MODE_LED_STATES 0x3210 +#endif static int get_reset_mode(void) { usrbtn_init(); diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index e45f81479..eaa536a1d 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -392,9 +392,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) }, -#if 0 - { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pyb_adc_type) }, -#endif + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, #if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, #endif diff --git a/ports/stm32/modmachine.h b/ports/stm32/modmachine.h index 414e5b37c..4b727d3cb 100644 --- a/ports/stm32/modmachine.h +++ b/ports/stm32/modmachine.h @@ -28,6 +28,8 @@ #include "py/obj.h" +extern const mp_obj_type_t machine_adc_type; + void machine_init(void); void machine_deinit(void); diff --git a/ports/stm32/modnetwork.c b/ports/stm32/modnetwork.c index 80e5a5a16..13ecf444f 100644 --- a/ports/stm32/modnetwork.c +++ b/ports/stm32/modnetwork.c @@ -189,10 +189,6 @@ mp_obj_t mod_network_nic_ifconfig(struct netif *netif, size_t n_args, const mp_o mp_hal_delay_ms(100); } - #if LWIP_MDNS_RESPONDER - mdns_resp_netif_settings_changed(netif); - #endif - return mp_const_none; } else { // Release and stop any existing DHCP @@ -207,9 +203,6 @@ mp_obj_t mod_network_nic_ifconfig(struct netif *netif, size_t n_args, const mp_o ip_addr_t dns; netutils_parse_ipv4_addr(items[3], (uint8_t*)&dns, NETUTILS_BIG); dns_setserver(0, &dns); - #if LWIP_MDNS_RESPONDER - mdns_resp_netif_settings_changed(netif); - #endif return mp_const_none; } } diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index 139defc53..1a1f567a5 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -170,9 +170,11 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { #if MICROPY_PY_PYB_LEGACY // these 2 are deprecated; use USB_VCP.isconnected and USB_HID.send instead { MP_ROM_QSTR(MP_QSTR_have_cdc), MP_ROM_PTR(&pyb_have_cdc_obj) }, + #if MICROPY_HW_USB_HID { MP_ROM_QSTR(MP_QSTR_hid), MP_ROM_PTR(&pyb_hid_send_report_obj) }, #endif #endif + #endif #if MICROPY_PY_PYB_LEGACY { MP_ROM_QSTR(MP_QSTR_millis), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 121b64d03..d353468f9 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -102,6 +102,11 @@ #define MICROPY_HW_ENABLE_MMCARD (0) #endif +// SD/MMC interface bus width (defaults to 4 bits) +#ifndef MICROPY_HW_SDMMC_BUS_WIDTH +#define MICROPY_HW_SDMMC_BUS_WIDTH (4) +#endif + // Whether to automatically mount (and boot from) the SD card if it's present #ifndef MICROPY_HW_SDCARD_MOUNT_AT_BOOT #define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (MICROPY_HW_ENABLE_SDCARD) @@ -282,6 +287,13 @@ #define MICROPY_HW_MAX_CAN (1) #endif +// Whether the USB peripheral is device-only, or multiple OTG +#if defined(STM32L0) || defined(STM32L432xx) || defined(STM32WB) +#define MICROPY_HW_USB_IS_MULTI_OTG (0) +#else +#define MICROPY_HW_USB_IS_MULTI_OTG (1) +#endif + // Configure maximum number of CDC VCP interfaces, and whether MSC/HID are supported #ifndef MICROPY_HW_USB_CDC_NUM #define MICROPY_HW_USB_CDC_NUM (1) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index dbb6fa2d5..5eb44bd46 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -114,6 +114,7 @@ #define MICROPY_PY_COLLECTIONS_DEQUE (1) #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#define MICROPY_PY_MATH_ISCLOSE (1) #define MICROPY_PY_MATH_FACTORIAL (1) #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO (1) diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index ec4590b06..aa5dc3397 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -168,28 +168,8 @@ void mp_hal_pin_config_speed(mp_hal_pin_obj_t pin_obj, uint32_t speed) { /*******************************************************************************/ // MAC address -typedef struct _pyb_otp_t { - uint16_t series; - uint16_t rev; - uint8_t mac[6]; -} pyb_otp_t; - -#if defined(STM32F722xx) || defined(STM32F723xx) || defined(STM32F732xx) || defined(STM32F733xx) -#define OTP_ADDR (0x1ff079e0) -#else -#define OTP_ADDR (0x1ff0f3c0) -#endif -#define OTP ((pyb_otp_t*)OTP_ADDR) - -MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { - // Check if OTP region has a valid MAC address, and use it if it does - if (OTP->series == 0x00d1 && OTP->mac[0] == 'H' && OTP->mac[1] == 'J' && OTP->mac[2] == '0') { - memcpy(buf, OTP->mac, 6); - buf[5] += idx; - return; - } - - // Generate a random locally administered MAC address (LAA) +// Generate a random locally administered MAC address (LAA) +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]) { uint8_t *id = (uint8_t *)MP_HAL_UNIQUE_ID_ADDRESS; buf[0] = 0x02; // LAA range buf[1] = (id[11] << 4) | (id[10] & 0xf); @@ -199,6 +179,11 @@ MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { buf[5] = (id[0] << 2) | idx; } +// A board can override this if needed +MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { + mp_hal_generate_laa_mac(idx, buf); +} + void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest) { static const char hexchr[16] = "0123456789ABCDEF"; uint8_t mac[6]; diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h index bd71adf77..d73ff8bff 100644 --- a/ports/stm32/mphalport.h +++ b/ports/stm32/mphalport.h @@ -82,5 +82,6 @@ enum { MP_HAL_MAC_ETH0, }; +void mp_hal_generate_laa_mac(int idx, uint8_t buf[6]); void mp_hal_get_mac(int idx, uint8_t buf[6]); void mp_hal_get_mac_ascii(int idx, size_t chr_off, size_t chr_len, char *dest); diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index 68a8bfdaf..dfd9e8262 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -76,7 +76,7 @@ void powerctrl_check_enter_bootloader(void) { if ((bl_addr & 0xfff) == 0 && (RCC->RCC_SR & RCC_SR_SFTRSTF)) { // Reset by NVIC_SystemReset with bootloader data set -> branch to bootloader RCC->RCC_SR = RCC_SR_RMVF; - #if defined(STM32F0) || defined(STM32F4) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32F0) || defined(STM32F4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH(); #endif uint32_t r0 = BL_STATE[0]; diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index 0cb09b818..f54d67ea4 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -156,17 +156,21 @@ void sdcard_init(void) { mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC2_CK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC2_CK); mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC2_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC2_CMD); mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC2_D0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC2_D0); + #if MICROPY_HW_SDMMC_BUS_WIDTH == 4 mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC2_D1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC2_D1); mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC2_D2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC2_D2); mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC2_D3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC2_D3); + #endif #else // Default SDIO/SDMMC1 config + mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_CK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CK); + mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CMD); mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_D0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_D0); + #if MICROPY_HW_SDMMC_BUS_WIDTH == 4 mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_D1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_D1); mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_D2, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_D2); mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_D3, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_D3); - mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_CK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CK); - mp_hal_pin_config_alt_static(MICROPY_HW_SDMMC_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CMD); + #endif #endif // configure the SD card detect pin @@ -252,12 +256,14 @@ STATIC HAL_StatusTypeDef sdmmc_init_sd(void) { mp_hal_delay_ms(50); } - // configure the SD bus width for wide operation + #if MICROPY_HW_SDMMC_BUS_WIDTH == 4 + // configure the SD bus width for 4-bit wide operation status = HAL_SD_ConfigWideBusOperation(&sdmmc_handle.sd, SDIO_BUS_WIDE_4B); if (status != HAL_OK) { HAL_SD_DeInit(&sdmmc_handle.sd); return status; } + #endif return HAL_OK; } @@ -285,7 +291,8 @@ STATIC HAL_StatusTypeDef sdmmc_init_mmc(void) { // As this is an eMMC card, overwrite LogBlockNbr with actual value sdmmc_handle.mmc.MmcCard.LogBlockNbr = 7469056 + 2048; - // Configure the SDIO bus width for wide operation + #if MICROPY_HW_SDMMC_BUS_WIDTH == 4 + // Configure the SDIO bus width for 4-bit wide operation #ifdef STM32F7 sdmmc_handle.mmc.Init.ClockBypass = SDIO_CLOCK_BYPASS_ENABLE; #endif @@ -294,6 +301,7 @@ STATIC HAL_StatusTypeDef sdmmc_init_mmc(void) { HAL_MMC_DeInit(&sdmmc_handle.mmc); return status; } + #endif return HAL_OK; } diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index 16db92d1d..9f5a886f3 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -298,7 +298,7 @@ void DebugMon_Handler(void) { /* file (startup_stm32f4xx.s). */ /******************************************************************************/ -#if defined(STM32L0) +#if defined(STM32L0) || defined(STM32L432xx) #if MICROPY_HW_USB_FS void USB_IRQHandler(void) { @@ -531,7 +531,7 @@ void RTC_WKUP_IRQHandler(void) { IRQ_EXIT(RTC_WKUP_IRQn); } -#if defined(STM32F0) +#if defined(STM32F0) || defined(STM32L0) void RTC_IRQHandler(void) { IRQ_ENTER(RTC_IRQn); diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 3308f0744..ca5cc682e 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -57,6 +57,19 @@ #endif #endif +// Maximum number of endpoints (excluding EP0) +#if defined(STM32L0) || defined(STM32WB) +#define MAX_ENDPOINT(dev_id) (7) +#elif defined(STM32L4) +#define MAX_ENDPOINT(dev_id) (5) +#elif defined(STM32F4) +#define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 3 : 5) +#elif defined(STM32F7) +#define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 5 : 8) +#elif defined(STM32H7) +#define MAX_ENDPOINT(dev_id) (8) +#endif + STATIC void pyb_usb_vcp_init0(void); // this will be persistent across a soft-reset @@ -75,6 +88,74 @@ typedef struct _usb_device_t { usb_device_t usb_device = {0}; pyb_usb_storage_medium_t pyb_usb_storage_medium = PYB_USB_STORAGE_MEDIUM_NONE; +#if !MICROPY_HW_USB_IS_MULTI_OTG + +// Units of FIFO size arrays below are 4x 16-bit words = 8 bytes +// There are 512x 16-bit words it total to use here (when using PCD_SNG_BUF) + +// EP0(out), EP0(in), MSC/HID(out), MSC/HID(in), unused, CDC_CMD(in), CDC_DATA(out), CDC_DATA(in) +STATIC const uint8_t usbd_fifo_size_cdc1[] = {16, 16, 16, 16, 0, 16, 16, 16}; + +#else + +// Units of FIFO size arrays below are 4x 32-bit words = 16 bytes +// FS: there are 320x 32-bit words in total to use here +// HS: there are 1024x 32-bit words in total to use here + +// RX; EP0(in), MSC/HID, CDC_CMD, CDC_DATA +STATIC const uint8_t usbd_fifo_size_cdc1[] = { + 32, 8, 16, 8, 16, 0, 0, // FS: RX, EP0(in), 5x IN endpoints + #if MICROPY_HW_USB_HS + 116, 8, 64, 4, 64, 0, 0, 0, 0, 0, // HS: RX, EP0(in), 8x IN endpoints + #endif +}; + +// RX; EP0(in), MSC/HID, CDC_CMD, CDC_DATA, HID +STATIC const uint8_t usbd_fifo_size_cdc1_msc_hid[] = { + 32, 8, 16, 4, 12, 8, 0, + #if MICROPY_HW_USB_HS + 116, 8, 64, 4, 56, 8, 0, 0, 0, 0, + #endif +}; + +#if MICROPY_HW_USB_CDC_NUM >= 2 +// RX; EP0(in), MSC/HID, CDC_CMD, CDC_DATA, CDC2_CMD, CDC2_DATA +STATIC const uint8_t usbd_fifo_size_cdc2[] = { + 32, 8, 16, 4, 8, 4, 8, + #if MICROPY_HW_USB_HS + 116, 8, 64, 2, 32, 2, 32, 0, 0, 0, + #endif +}; + +// RX; EP0(in), MSC/HID, CDC_CMD, CDC_DATA, CDC2_CMD/HID, CDC2_DATA, HID +STATIC const uint8_t usbd_fifo_size_cdc2_msc_hid[] = { + 0, 0, 0, 0, 0, 0, 0, // FS: can't support 2xVCP+MSC+HID + #if MICROPY_HW_USB_HS + 102, 8, 64, 2, 32, 8, 32, 8, 0, 0, + #endif +}; +#endif + +#if MICROPY_HW_USB_CDC_NUM >= 3 +// RX; EP0(in), MSC/HID, CDC_CMD, CDC_DATA, CDC2_CMD, CDC2_DATA, CDC3_CMD, CDC3_DATA +STATIC const uint8_t usbd_fifo_size_cdc3[] = { + 0, 0, 0, 0, 0, 0, 0, // FS: can't support 3x VCP mode + #if MICROPY_HW_USB_HS + 82, 8, 64, 2, 32, 2, 32, 2, 32, 0, + #endif +}; + +// RX; EP0(in), MSC/HID, CDC_CMD, CDC_DATA, CDC2_CMD/HID, CDC2_DATA, CDC3_CMD/HID, CDC3_DATA, HID +STATIC const uint8_t usbd_fifo_size_cdc3_msc_hid[] = { + 0, 0, 0, 0, 0, 0, 0, // FS: can't support 3x VCP mode + #if MICROPY_HW_USB_HS + 82, 8, 64, 2, 25, 8, 25, 8, 25, 8, + #endif +}; +#endif + +#endif + #if MICROPY_HW_USB_HID // predefined hid mouse data STATIC const mp_obj_str_t pyb_usb_hid_mouse_desc_obj = { @@ -172,7 +253,7 @@ bool pyb_usb_dev_init(int dev_id, uint16_t vid, uint16_t pid, uint8_t mode, size // configure the VID, PID and the USBD mode (interfaces it will expose) int cdc_only = (mode & USBD_MODE_IFACE_MASK) == USBD_MODE_CDC; USBD_SetVIDPIDRelease(&usb_dev->usbd_cdc_msc_hid_state, vid, pid, 0x0200, cdc_only); - if (USBD_SelectMode(&usb_dev->usbd_cdc_msc_hid_state, mode, hid_info) != 0) { + if (USBD_SelectMode(&usb_dev->usbd_cdc_msc_hid_state, mode, hid_info, MAX_ENDPOINT(dev_id)) != 0) { return false; } @@ -197,8 +278,33 @@ bool pyb_usb_dev_init(int dev_id, uint16_t vid, uint16_t pid, uint8_t mode, size USBD_MSC_RegisterStorage(&usb_dev->usbd_cdc_msc_hid_state, (USBD_StorageTypeDef*)&usbd_msc_fops); #endif + const uint8_t *fifo_size = usbd_fifo_size_cdc1; + #if MICROPY_HW_USB_IS_MULTI_OTG + if ((mode & USBD_MODE_MSC_HID) == USBD_MODE_MSC_HID) { + fifo_size = usbd_fifo_size_cdc1_msc_hid; + } + #endif + #if MICROPY_HW_USB_CDC_NUM >= 3 + if (mode & USBD_MODE_IFACE_CDC(2)) { + if ((mode & USBD_MODE_MSC_HID) == USBD_MODE_MSC_HID) { + fifo_size = usbd_fifo_size_cdc3_msc_hid; + } else { + fifo_size = usbd_fifo_size_cdc3; + } + } else + #endif + #if MICROPY_HW_USB_CDC_NUM >= 2 + if (mode & USBD_MODE_IFACE_CDC(1)) { + if ((mode & USBD_MODE_MSC_HID) == USBD_MODE_MSC_HID) { + fifo_size = usbd_fifo_size_cdc2_msc_hid; + } else { + fifo_size = usbd_fifo_size_cdc2; + } + } + #endif + // start the USB device - USBD_LL_Init(usbd, (mode & USBD_MODE_HIGH_SPEED) != 0); + USBD_LL_Init(usbd, (mode & USBD_MODE_HIGH_SPEED) != 0, fifo_size); USBD_LL_Start(usbd); usb_dev->enabled = true; } @@ -358,6 +464,11 @@ STATIC mp_obj_t pyb_usb_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t * pid = USBD_PID_CDC_MSC; } mode = USBD_MODE_CDC_MSC; + } else if (strcmp(mode_str, "VCP+MSC+HID") == 0) { + if (pid == -1) { + pid = USBD_PID_CDC_MSC_HID; + } + mode = USBD_MODE_CDC_MSC_HID; #if MICROPY_HW_USB_CDC_NUM >= 2 } else if (strcmp(mode_str, "VCP+VCP") == 0) { if (pid == -1) { @@ -369,6 +480,11 @@ STATIC mp_obj_t pyb_usb_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t * pid = USBD_PID_CDC2_MSC; } mode = USBD_MODE_CDC2_MSC; + } else if (strcmp(mode_str, "2xVCP+MSC+HID") == 0) { + if (pid == -1) { + pid = USBD_PID_CDC2_MSC_HID; + } + mode = USBD_MODE_CDC2_MSC_HID; #endif #if MICROPY_HW_USB_CDC_NUM >= 3 } else if (strcmp(mode_str, "3xVCP") == 0) { @@ -381,6 +497,11 @@ STATIC mp_obj_t pyb_usb_mode(size_t n_args, const mp_obj_t *pos_args, mp_map_t * pid = USBD_PID_CDC3_MSC; } mode = USBD_MODE_CDC3_MSC; + } else if (strcmp(mode_str, "3xVCP+MSC+HID") == 0) { + if (pid == -1) { + pid = USBD_PID_CDC3_MSC_HID; + } + mode = USBD_MODE_CDC3_MSC_HID; #endif } else if (strcmp(mode_str, "CDC+HID") == 0 || strcmp(mode_str, "VCP+HID") == 0) { if (pid == -1) { diff --git a/ports/stm32/usb.h b/ports/stm32/usb.h index cb017902e..457c7313c 100644 --- a/ports/stm32/usb.h +++ b/ports/stm32/usb.h @@ -40,6 +40,9 @@ #define USBD_PID_CDC2 (0x9805) #define USBD_PID_CDC3 (0x9806) #define USBD_PID_CDC3_MSC (0x9807) +#define USBD_PID_CDC_MSC_HID (0x9808) +#define USBD_PID_CDC2_MSC_HID (0x9809) +#define USBD_PID_CDC3_MSC_HID (0x980a) typedef enum { PYB_USB_STORAGE_MEDIUM_NONE = 0, diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index e23423019..51d29b30f 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -142,7 +142,7 @@ int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t* pbuf, ui // configure its serial port (in most cases to disable local echo) cdc->connect_state = USBD_CDC_CONNECT_STATE_CONNECTING; usbd_cdc_connect_tx_timer = 8; // wait for 8 SOF IRQs - #if defined(STM32L0) || defined(STM32WB) + #if !MICROPY_HW_USB_IS_MULTI_OTG USB->CNTR |= USB_CNTR_SOFM; #else PCD_HandleTypeDef *hpcd = cdc->base.usbd->pdev->pData; @@ -219,7 +219,7 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) { --usbd_cdc_connect_tx_timer; } else { usbd_cdc_msc_hid_state_t *usbd = ((USBD_HandleTypeDef*)hpcd->pData)->pClassData; - #if defined(STM32L0) || defined(STM32WB) + #if !MICROPY_HW_USB_IS_MULTI_OTG USB->CNTR &= ~USB_CNTR_SOFM; #else hpcd->Instance->GINTMSK &= ~USB_OTG_GINTMSK_SOFM; diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index 437d96ae7..d30f28b2f 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -44,8 +44,8 @@ PCD_HandleTypeDef pcd_fs_handle; PCD_HandleTypeDef pcd_hs_handle; #endif -#if defined(STM32L0) || defined(STM32WB) -// The STM32L0xx has a single USB device-only instance +#if !MICROPY_HW_USB_IS_MULTI_OTG +// The MCU has a single USB device-only instance #define USB_OTG_FS USB #endif @@ -64,6 +64,8 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { const uint32_t otg_alt = GPIO_AF10_OTG1_FS; #elif defined(STM32L0) const uint32_t otg_alt = GPIO_AF0_USB; + #elif defined(STM32L432xx) + const uint32_t otg_alt = GPIO_AF10_USB_FS; #elif defined(STM32WB) const uint32_t otg_alt = GPIO_AF10_USB; #else @@ -92,7 +94,7 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { #endif // Enable USB FS Clocks - #if defined(STM32L0) || defined(STM32WB) + #if !MICROPY_HW_USB_IS_MULTI_OTG __HAL_RCC_USB_CLK_ENABLE(); #else __USB_OTG_FS_CLK_ENABLE(); @@ -113,6 +115,9 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { #if defined(STM32L0) NVIC_SetPriority(USB_IRQn, IRQ_PRI_OTG_FS); HAL_NVIC_EnableIRQ(USB_IRQn); + #elif defined(STM32L432xx) + NVIC_SetPriority(USB_FS_IRQn, IRQ_PRI_OTG_FS); + HAL_NVIC_EnableIRQ(USB_FS_IRQn); #elif defined(STM32WB) NVIC_SetPriority(USB_LP_IRQn, IRQ_PRI_OTG_FS); HAL_NVIC_EnableIRQ(USB_LP_IRQn); @@ -195,7 +200,7 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { * @retval None */ void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) { - #if defined(STM32L0) || defined(STM32WB) + #if !MICROPY_HW_USB_IS_MULTI_OTG __HAL_RCC_USB_CLK_DISABLE(); #else @@ -356,7 +361,7 @@ void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) { * @param pdev: Device handle * @retval USBD Status */ -USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed) { +USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed, const uint8_t *fifo_size) { #if MICROPY_HW_USB_FS if (pdev->id == USB_PHY_FS_ID) { #if defined(STM32WB) @@ -379,7 +384,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed) { pcd_fs_handle.Init.lpm_enable = DISABLE; pcd_fs_handle.Init.battery_charging_enable = DISABLE; #endif - #if !defined(STM32L0) && !defined(STM32WB) + #if MICROPY_HW_USB_IS_MULTI_OTG pcd_fs_handle.Init.use_dedicated_ep1 = 0; pcd_fs_handle.Init.dma_enable = 0; #if !defined(MICROPY_HW_USB_VBUS_DETECT_PIN) @@ -396,35 +401,19 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed) { // Initialize LL Driver HAL_PCD_Init(&pcd_fs_handle); - #if defined(STM32L0) || defined(STM32WB) - // We have 512 16-bit words it total to use here (when using PCD_SNG_BUF) - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x00, PCD_SNG_BUF, 64); // EP0 - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x80, PCD_SNG_BUF, 128); // EP0 - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x01, PCD_SNG_BUF, 192); // MSC / HID - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x81, PCD_SNG_BUF, 256); // MSC / HID - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x02, PCD_SNG_BUF, 320); // unused - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x82, PCD_SNG_BUF, 320); // CDC CMD - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x03, PCD_SNG_BUF, 384); // CDC DATA - HAL_PCDEx_PMAConfig(&pcd_fs_handle, 0x83, PCD_SNG_BUF, 448); // CDC DATA + // Set FIFO buffer sizes + #if !MICROPY_HW_USB_IS_MULTI_OTG + uint32_t fifo_offset = USBD_PMA_RESERVE; // need to reserve some data at start of FIFO + for (size_t i = 0; i < USBD_PMA_NUM_FIFO; ++i) { + uint16_t ep_addr = ((i & 1) * 0x80) | (i >> 1); + HAL_PCDEx_PMAConfig(&pcd_fs_handle, ep_addr, PCD_SNG_BUF, fifo_offset); + fifo_offset += fifo_size[i] * 4; + } #else - - // We have 320 32-bit words in total to use here - #if MICROPY_HW_USB_CDC_NUM == 2 - HAL_PCD_SetRxFiFo(&pcd_fs_handle, 128); - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 0, 32); // EP0 - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 1, 64); // MSC / HID - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 2, 16); // CDC CMD - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 3, 32); // CDC DATA - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 4, 16); // CDC2 CMD - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 5, 32); // CDC2 DATA - #else - HAL_PCD_SetRxFiFo(&pcd_fs_handle, 128); - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 0, 32); // EP0 - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 1, 64); // MSC / HID - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 2, 32); // CDC CMD - HAL_PCD_SetTxFiFo(&pcd_fs_handle, 3, 64); // CDC DATA - #endif - + HAL_PCD_SetRxFiFo(&pcd_fs_handle, fifo_size[0] * 4); + for (size_t i = 0; i < USBD_FS_NUM_TX_FIFO; ++i) { + HAL_PCD_SetTxFiFo(&pcd_fs_handle, i, fifo_size[1 + i] * 4); + } #endif } #endif @@ -481,22 +470,12 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed) { // Initialize LL Driver HAL_PCD_Init(&pcd_hs_handle); - // We have 1024 32-bit words in total to use here - #if MICROPY_HW_USB_CDC_NUM == 3 - HAL_PCD_SetRxFiFo(&pcd_hs_handle, 328); - #else - HAL_PCD_SetRxFiFo(&pcd_hs_handle, 464); - #endif - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 0, 32); // EP0 - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 1, 256); // MSC / HID - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 2, 8); // CDC CMD - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 3, 128); // CDC DATA - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 4, 8); // CDC2 CMD - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 5, 128); // CDC2 DATA - #if MICROPY_HW_USB_CDC_NUM == 3 - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 6, 8); // CDC3 CMD - HAL_PCD_SetTxFiFo(&pcd_hs_handle, 7, 128); // CDC3 DATA - #endif + // Set FIFO buffer sizes + fifo_size += USBD_FS_NUM_FIFO; // skip over FS FIFO size values + HAL_PCD_SetRxFiFo(&pcd_hs_handle, fifo_size[0] * 4); + for (size_t i = 0; i < USBD_HS_NUM_TX_FIFO; ++i) { + HAL_PCD_SetTxFiFo(&pcd_hs_handle, i, fifo_size[1 + i] * 4); + } } #endif // MICROPY_HW_USB_HS diff --git a/ports/stm32/usbd_conf.h b/ports/stm32/usbd_conf.h index 639b54d9f..5237ba3a9 100644 --- a/ports/stm32/usbd_conf.h +++ b/ports/stm32/usbd_conf.h @@ -39,7 +39,7 @@ #include "py/mpconfig.h" -#define USBD_MAX_NUM_INTERFACES 5 +#define USBD_MAX_NUM_INTERFACES 8 #define USBD_MAX_NUM_CONFIGURATION 1 #define USBD_MAX_STR_DESC_SIZ 0x100 #if MICROPY_HW_USB_SELF_POWERED @@ -49,6 +49,16 @@ #endif #define USBD_DEBUG_LEVEL 0 +// For MCUs with a device-only USB peripheral +#define USBD_PMA_RESERVE (64) +#define USBD_PMA_NUM_FIFO (8) + +// For MCUs with multiple OTG USB peripherals +#define USBD_FS_NUM_TX_FIFO (6) +#define USBD_FS_NUM_FIFO (1 + USBD_FS_NUM_TX_FIFO) +#define USBD_HS_NUM_TX_FIFO (9) +#define USBD_HS_NUM_FIFO (1 + USBD_HS_NUM_TX_FIFO) + #endif // MICROPY_INCLUDED_STM32_USBD_CONF_H /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h index a01e75bed..d934f4676 100644 --- a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h +++ b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h @@ -18,16 +18,8 @@ #endif // Should be maximum of possible config descriptors that might be configured -#if MICROPY_HW_USB_CDC_NUM == 3 -// Maximum is MSC+CDC+CDC+CDC -#define MAX_TEMPLATE_CONFIG_DESC_SIZE (9 + 23 + (8 + 58) + (8 + 58) + (8 + 58)) -#elif MICROPY_HW_USB_CDC_NUM == 2 -// Maximum is MSC+CDC+CDC -#define MAX_TEMPLATE_CONFIG_DESC_SIZE (9 + 23 + (8 + 58) + (8 + 58)) -#else -// Maximum is HID+CDC -#define MAX_TEMPLATE_CONFIG_DESC_SIZE (9 + 32 + (8 + 58)) -#endif +// Maximum is: 9 + MSC + NxCDC + HID +#define MAX_TEMPLATE_CONFIG_DESC_SIZE (9 + (23) + MICROPY_HW_USB_CDC_NUM * (8 + 58) + (9 + 9 + 7 + 7)) // CDC, MSC and HID packet sizes #define MSC_FS_MAX_PACKET (64) @@ -118,7 +110,7 @@ typedef struct _usbd_cdc_msc_hid_state_t { USBD_HandleTypeDef *pdev; uint8_t usbd_mode; - uint8_t usbd_config_desc_size; + uint16_t usbd_config_desc_size; #if MICROPY_HW_USB_MSC USBD_MSC_BOT_HandleTypeDef MSC_BOT_ClassData; @@ -173,7 +165,7 @@ static inline uint32_t usbd_cdc_max_packet(USBD_HandleTypeDef *pdev) { } // returns 0 on success, -1 on failure -int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_ModeInfoTypeDef *hid_info); +int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_ModeInfoTypeDef *hid_info, uint8_t max_endpoint); // returns the current usb mode uint8_t USBD_GetMode(usbd_cdc_msc_hid_state_t *usbd); diff --git a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid0.h b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid0.h index 63fe8ecef..7614dde69 100644 --- a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid0.h +++ b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid0.h @@ -44,6 +44,9 @@ #define USBD_MODE_CDC_MSC (USBD_MODE_IFACE_CDC(0) | USBD_MODE_IFACE_MSC) #define USBD_MODE_CDC2_MSC (USBD_MODE_IFACE_CDC(0) | USBD_MODE_IFACE_CDC(1) | USBD_MODE_IFACE_MSC) #define USBD_MODE_CDC3_MSC (USBD_MODE_IFACE_CDC(0) | USBD_MODE_IFACE_CDC(1) | USBD_MODE_IFACE_CDC(2) | USBD_MODE_IFACE_MSC) +#define USBD_MODE_CDC_MSC_HID (USBD_MODE_IFACE_CDC(0) | USBD_MODE_IFACE_MSC | USBD_MODE_IFACE_HID) +#define USBD_MODE_CDC2_MSC_HID (USBD_MODE_IFACE_CDC(0) | USBD_MODE_IFACE_CDC(1) | USBD_MODE_IFACE_MSC | USBD_MODE_IFACE_HID) +#define USBD_MODE_CDC3_MSC_HID (USBD_MODE_IFACE_CDC(0) | USBD_MODE_IFACE_CDC(1) | USBD_MODE_IFACE_CDC(2) | USBD_MODE_IFACE_MSC | USBD_MODE_IFACE_HID) #define USBD_MODE_HID (USBD_MODE_IFACE_HID) #define USBD_MODE_MSC (USBD_MODE_IFACE_MSC) #define USBD_MODE_MSC_HID (USBD_MODE_IFACE_MSC | USBD_MODE_IFACE_HID) diff --git a/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c b/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c index e13a93ddb..0b9407654 100644 --- a/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c +++ b/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c @@ -61,9 +61,11 @@ #define HID_DESC_OFFSET_PROTOCOL (7) #define HID_DESC_OFFSET_SUBDESC (9) #define HID_DESC_OFFSET_REPORT_DESC_LEN (16) +#define HID_DESC_OFFSET_IN_EP (20) #define HID_DESC_OFFSET_MAX_PACKET_LO (22) #define HID_DESC_OFFSET_MAX_PACKET_HI (23) #define HID_DESC_OFFSET_POLLING_INTERVAL (24) +#define HID_DESC_OFFSET_OUT_EP (27) #define HID_DESC_OFFSET_MAX_PACKET_OUT_LO (29) #define HID_DESC_OFFSET_MAX_PACKET_OUT_HI (30) #define HID_DESC_OFFSET_POLLING_INTERVAL_OUT (31) @@ -79,6 +81,9 @@ #define MSC_IFACE_NUM_WITH_CDC (0) #define HID_IFACE_NUM_WITH_CDC (0) #define HID_IFACE_NUM_WITH_MSC (1) +#define HID_IFACE_NUM_WITH_CDC_MSC (3) +#define HID_IFACE_NUM_WITH_CDC2_MSC (5) +#define HID_IFACE_NUM_WITH_CDC3_MSC (7) #define CDC_IN_EP(i) (0x83 + 2 * (i)) #define CDC_OUT_EP(i) (0x03 + 2 * (i)) @@ -88,6 +93,12 @@ #define HID_OUT_EP_WITH_CDC (0x01) #define HID_IN_EP_WITH_MSC (0x83) #define HID_OUT_EP_WITH_MSC (0x03) +#define HID_IN_EP_WITH_CDC_MSC (0x84) +#define HID_OUT_EP_WITH_CDC_MSC (0x04) +#define HID_IN_EP_WITH_CDC2_MSC (0x86) +#define HID_OUT_EP_WITH_CDC2_MSC (0x06) +#define HID_IN_EP_WITH_CDC3_MSC (0x88) +#define HID_OUT_EP_WITH_CDC3_MSC (0x08) #define USB_DESC_TYPE_ASSOCIATION (0x0b) @@ -448,8 +459,9 @@ static size_t make_cdc_desc_ep(uint8_t *dest, int need_iad, uint8_t iface_num, u #endif #if MICROPY_HW_USB_HID -static size_t make_hid_desc(uint8_t *dest, USBD_HID_ModeInfoTypeDef *hid_info) { +static size_t make_hid_desc(uint8_t *dest, USBD_HID_ModeInfoTypeDef *hid_info, uint8_t iface_num) { memcpy(dest, hid_class_desc_data, sizeof(hid_class_desc_data)); + dest[2] = iface_num; dest[HID_DESC_OFFSET_SUBCLASS] = hid_info->subclass; dest[HID_DESC_OFFSET_PROTOCOL] = hid_info->protocol; dest[HID_DESC_OFFSET_REPORT_DESC_LEN] = hid_info->report_desc_len; @@ -461,6 +473,15 @@ static size_t make_hid_desc(uint8_t *dest, USBD_HID_ModeInfoTypeDef *hid_info) { dest[HID_DESC_OFFSET_POLLING_INTERVAL_OUT] = hid_info->polling_interval; return sizeof(hid_class_desc_data); } + +#if MICROPY_HW_USB_MSC +static size_t make_hid_desc_ep(uint8_t *dest, USBD_HID_ModeInfoTypeDef *hid_info, uint8_t iface_num, uint8_t in_ep, uint8_t out_ep) { + size_t n = make_hid_desc(dest, hid_info, iface_num); + dest[HID_DESC_OFFSET_IN_EP] = in_ep; + dest[HID_DESC_OFFSET_OUT_EP] = out_ep; + return n; +} +#endif #endif // return the saved usb mode @@ -468,7 +489,7 @@ uint8_t USBD_GetMode(usbd_cdc_msc_hid_state_t *usbd) { return usbd->usbd_mode; } -int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_ModeInfoTypeDef *hid_info) { +int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_ModeInfoTypeDef *hid_info, uint8_t max_endpoint) { // save mode usbd->usbd_mode = mode; @@ -489,6 +510,21 @@ int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_Mode usbd->cdc[0]->iface_num = CDC_IFACE_NUM_WITH_MSC; num_itf = 3; break; + + #if MICROPY_HW_USB_HID + case USBD_MODE_CDC_MSC_HID: + n += make_msc_desc(d + n); + n += make_cdc_desc(d + n, 1, CDC_IFACE_NUM_WITH_MSC); + usbd->hid->desc = d + n; + n += make_hid_desc_ep(d + n, hid_info, HID_IFACE_NUM_WITH_CDC_MSC, HID_IN_EP_WITH_CDC_MSC, HID_OUT_EP_WITH_CDC_MSC); + usbd->cdc[0]->iface_num = CDC_IFACE_NUM_WITH_MSC; + usbd->hid->in_ep = HID_IN_EP_WITH_CDC_MSC; + usbd->hid->out_ep = HID_OUT_EP_WITH_CDC_MSC; + usbd->hid->iface_num = HID_IFACE_NUM_WITH_CDC_MSC; + usbd->hid->report_desc = hid_info->report_desc; + num_itf = 4; + break; + #endif #endif #if MICROPY_HW_USB_CDC_NUM >= 2 @@ -511,6 +547,23 @@ int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_Mode num_itf = 5; break; } + + case USBD_MODE_CDC2_MSC_HID: { + n += make_msc_desc(d + n); + n += make_cdc_desc(d + n, 1, CDC_IFACE_NUM_WITH_MSC); + n += make_cdc_desc_ep(d + n, 1, CDC2_IFACE_NUM_WITH_MSC, CDC_CMD_EP(1), CDC_OUT_EP(1), CDC_IN_EP(1)); + usbd->hid->desc = d + n; + n += make_hid_desc_ep(d + n, hid_info, HID_IFACE_NUM_WITH_CDC2_MSC, HID_IN_EP_WITH_CDC2_MSC, HID_OUT_EP_WITH_CDC2_MSC); + usbd->cdc[0]->iface_num = CDC_IFACE_NUM_WITH_MSC; + usbd->cdc[1]->iface_num = CDC2_IFACE_NUM_WITH_MSC; + usbd->cdc[2]->iface_num = CDC3_IFACE_NUM_WITH_MSC; + usbd->hid->in_ep = HID_IN_EP_WITH_CDC2_MSC; + usbd->hid->out_ep = HID_OUT_EP_WITH_CDC2_MSC; + usbd->hid->iface_num = HID_IFACE_NUM_WITH_CDC2_MSC; + usbd->hid->report_desc = hid_info->report_desc; + num_itf = 6; + break; + } #endif #endif @@ -538,13 +591,31 @@ int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_Mode num_itf = 7; break; } + + case USBD_MODE_CDC3_MSC_HID: { + n += make_msc_desc(d + n); + n += make_cdc_desc(d + n, 1, CDC_IFACE_NUM_WITH_MSC); + n += make_cdc_desc_ep(d + n, 1, CDC2_IFACE_NUM_WITH_MSC, CDC_CMD_EP(1), CDC_OUT_EP(1), CDC_IN_EP(1)); + n += make_cdc_desc_ep(d + n, 1, CDC3_IFACE_NUM_WITH_MSC, CDC_CMD_EP(2), CDC_OUT_EP(2), CDC_IN_EP(2)); + usbd->hid->desc = d + n; + n += make_hid_desc_ep(d + n, hid_info, HID_IFACE_NUM_WITH_CDC3_MSC, HID_IN_EP_WITH_CDC3_MSC, HID_OUT_EP_WITH_CDC3_MSC); + usbd->cdc[0]->iface_num = CDC_IFACE_NUM_WITH_MSC; + usbd->cdc[1]->iface_num = CDC2_IFACE_NUM_WITH_MSC; + usbd->cdc[2]->iface_num = CDC3_IFACE_NUM_WITH_MSC; + usbd->hid->in_ep = HID_IN_EP_WITH_CDC3_MSC; + usbd->hid->out_ep = HID_OUT_EP_WITH_CDC3_MSC; + usbd->hid->iface_num = HID_IFACE_NUM_WITH_CDC3_MSC; + usbd->hid->report_desc = hid_info->report_desc; + num_itf = 8; + break; + } #endif #endif #if MICROPY_HW_USB_HID case USBD_MODE_CDC_HID: usbd->hid->desc = d + n; - n += make_hid_desc(d + n, hid_info); + n += make_hid_desc(d + n, hid_info, HID_IFACE_NUM_WITH_CDC); n += make_cdc_desc(d + n, 1, CDC_IFACE_NUM_WITH_HID); usbd->cdc[0]->iface_num = CDC_IFACE_NUM_WITH_HID; usbd->hid->in_ep = HID_IN_EP_WITH_CDC; @@ -585,6 +656,17 @@ int USBD_SelectMode(usbd_cdc_msc_hid_state_t *usbd, uint32_t mode, USBD_HID_Mode } } + // Verify that the endpoints that are used fit within the maximum number + d = usbd->usbd_config_desc; + const uint8_t *d_top = d + n; + while (d < d_top) { + if (d[0] == 7 && d[1] == USB_DESC_TYPE_ENDPOINT && (d[2] & 0x7f) > max_endpoint) { + // Endpoint out of range of hardware + return -1; + } + d += d[0]; + } + return 0; } diff --git a/ports/stm32/usbdev/core/inc/usbd_core.h b/ports/stm32/usbdev/core/inc/usbd_core.h index 5494be3a2..d3925fc6b 100644 --- a/ports/stm32/usbdev/core/inc/usbd_core.h +++ b/ports/stm32/usbdev/core/inc/usbd_core.h @@ -111,7 +111,7 @@ USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev); /* USBD Low Level Driver */ -USBD_StatusTypeDef USBD_LL_Init (USBD_HandleTypeDef *pdev, int high_speed); +USBD_StatusTypeDef USBD_LL_Init (USBD_HandleTypeDef *pdev, int high_speed, const uint8_t *fifo_size); USBD_StatusTypeDef USBD_LL_DeInit (USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_Stop (USBD_HandleTypeDef *pdev); diff --git a/ports/stm32/usbdev/core/src/usbd_core.c b/ports/stm32/usbdev/core/src/usbd_core.c index f235b24ee..4c69a77eb 100644 --- a/ports/stm32/usbdev/core/src/usbd_core.c +++ b/ports/stm32/usbdev/core/src/usbd_core.c @@ -85,6 +85,7 @@ * @{ */ +#if 0 /** * @brief USBD_Init * Initailizes the device stack and load the class driver @@ -122,6 +123,7 @@ USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef * return USBD_OK; } +#endif /** * @brief USBD_DeInit diff --git a/ports/unix/.gitignore b/ports/unix/.gitignore index 706b7732d..7179e7bde 100644 --- a/ports/unix/.gitignore +++ b/ports/unix/.gitignore @@ -1,9 +1,3 @@ -build -build-fast -build-minimal -build-coverage -build-nanbox -build-freedos micropython micropython_fast micropython_minimal diff --git a/ports/unix/main.c b/ports/unix/main.c index cd2dc49a5..9147feb32 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -138,7 +138,7 @@ STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu } #endif - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, emit_opt, is_repl); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl); if (!compile_only) { // execute it @@ -313,7 +313,11 @@ STATIC int usage(char **argv) { int impl_opts_cnt = 0; printf( " compile-only -- parse and compile only\n" +#if MICROPY_EMIT_NATIVE " emit={bytecode,native,viper} -- set the default code emitter\n" +#else +" emit=bytecode -- set the default code emitter\n" +#endif ); impl_opts_cnt++; #if MICROPY_ENABLE_GC @@ -343,10 +347,12 @@ STATIC void pre_process_options(int argc, char **argv) { compile_only = true; } else if (strcmp(argv[a + 1], "emit=bytecode") == 0) { emit_opt = MP_EMIT_OPT_BYTECODE; + #if MICROPY_EMIT_NATIVE } else if (strcmp(argv[a + 1], "emit=native") == 0) { emit_opt = MP_EMIT_OPT_NATIVE_PYTHON; } else if (strcmp(argv[a + 1], "emit=viper") == 0) { emit_opt = MP_EMIT_OPT_VIPER; + #endif #if MICROPY_ENABLE_GC } else if (strncmp(argv[a + 1], "heapsize=", sizeof("heapsize=") - 1) == 0) { char *end; @@ -450,6 +456,13 @@ MP_NOINLINE int main_(int argc, char **argv) { mp_init(); + #if MICROPY_EMIT_NATIVE + // Set default emitter options + MP_STATE_VM(default_emit_opt) = emit_opt; + #else + (void)emit_opt; + #endif + #if MICROPY_VFS_POSIX { // Mount the host FS at the root of our internal VFS @@ -642,6 +655,17 @@ MP_NOINLINE int main_(int argc, char **argv) { } } + #if MICROPY_PY_SYS_SETTRACE + MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL; + #endif + + #if MICROPY_PY_SYS_ATEXIT + // Beware, the sys.settrace callback should be disabled before running sys.atexit. + if (mp_obj_is_callable(MP_STATE_VM(sys_exitfunc))) { + mp_call_function_0(MP_STATE_VM(sys_exitfunc)); + } + #endif + #if MICROPY_PY_MICROPYTHON_MEM_INFO if (mp_verbose_flag) { mp_micropython_mem_info(0, NULL); diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index f81785d5f..a434cfe2b 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -92,6 +92,11 @@ #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) #define MICROPY_PY_SYS_EXIT (1) +#define MICROPY_PY_SYS_ATEXIT (1) +#if MICROPY_PY_SYS_SETTRACE +#define MICROPY_PERSISTENT_CODE_SAVE (1) +#define MICROPY_COMP_CONST (0) +#endif #if defined(__APPLE__) && defined(__MACH__) #define MICROPY_PY_SYS_PLATFORM "darwin" #else @@ -105,6 +110,7 @@ #ifndef MICROPY_PY_MATH_SPECIAL_FUNCTIONS #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) #endif +#define MICROPY_PY_MATH_ISCLOSE (MICROPY_PY_MATH_SPECIAL_FUNCTIONS) #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO_IOBASE (1) #define MICROPY_PY_IO_FILEIO (1) diff --git a/ports/unix/mpconfigport_coverage.h b/ports/unix/mpconfigport_coverage.h index b2f1d6e88..afd364649 100644 --- a/ports/unix/mpconfigport_coverage.h +++ b/ports/unix/mpconfigport_coverage.h @@ -50,6 +50,7 @@ #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_IO_BUFFEREDWRITER (1) #define MICROPY_PY_IO_RESOURCE_STREAM (1) +#define MICROPY_PY_URE_DEBUG (1) #define MICROPY_PY_URE_MATCH_GROUPS (1) #define MICROPY_PY_URE_MATCH_SPAN_START_END (1) #define MICROPY_PY_URE_SUB (1) diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 88d103e7b..e65c09c53 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -15,8 +15,8 @@ INC += -I$(TOP) INC += -I$(BUILD) # compiler settings -CFLAGS = $(INC) -Wall -Wpointer-arith -Werror -std=gnu99 -DUNIX -D__USE_MINGW_ANSI_STDIO=1 $(CFLAGS_MOD) $(COPT) -LDFLAGS = $(LDFLAGS_MOD) -lm +CFLAGS = $(INC) -Wall -Wpointer-arith -Werror -std=gnu99 -DUNIX -D__USE_MINGW_ANSI_STDIO=1 $(CFLAGS_MOD) $(COPT) $(CFLAGS_EXTRA) +LDFLAGS = $(LDFLAGS_MOD) -lm $(LDFLAGS_EXTRA) # Debugging/Optimization ifdef DEBUG @@ -40,6 +40,7 @@ SRC_C = \ realpath.c \ init.c \ sleep.c \ + $(SRC_MOD) OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index ffe7ae144..1a9842609 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -86,6 +86,7 @@ #define MICROPY_PY_COLLECTIONS_DEQUE (1) #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#define MICROPY_PY_MATH_ISCLOSE (1) #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO_FILEIO (1) #define MICROPY_PY_GC_COLLECT_RETVAL (1) diff --git a/ports/windows/msvc/common.props b/ports/windows/msvc/common.props index 26ea78e7e..5fe1b45fe 100644 --- a/ports/windows/msvc/common.props +++ b/ports/windows/msvc/common.props @@ -18,6 +18,7 @@ false true false + true true diff --git a/py/bc.c b/py/bc.c index b178e7c20..5625da8cf 100644 --- a/py/bc.c +++ b/py/bc.c @@ -119,6 +119,11 @@ void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw code_state->prev = NULL; #endif + #if MICROPY_PY_SYS_SETTRACE + code_state->prev_state = NULL; + code_state->frame = NULL; + #endif + // get params size_t n_state = mp_decode_uint(&code_state->ip); code_state->ip = mp_decode_uint_skip(code_state->ip); // skip n_exc_stack @@ -287,7 +292,8 @@ continue2:; #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE // The following table encodes the number of bytes that a specific opcode -// takes up. There are 3 special opcodes that always have an extra byte: +// takes up. There are 4 special opcodes that always have an extra byte: +// MP_BC_UNWIND_JUMP // MP_BC_MAKE_CLOSURE // MP_BC_MAKE_CLOSURE_DEFARGS // MP_BC_RAISE_VARARGS @@ -397,7 +403,8 @@ uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint) ip += 3; } else { int extra_byte = ( - *ip == MP_BC_RAISE_VARARGS + *ip == MP_BC_UNWIND_JUMP + || *ip == MP_BC_RAISE_VARARGS || *ip == MP_BC_MAKE_CLOSURE || *ip == MP_BC_MAKE_CLOSURE_DEFARGS ); diff --git a/py/bc.h b/py/bc.h index 0aadfa8a3..ac9105a55 100644 --- a/py/bc.h +++ b/py/bc.h @@ -60,6 +60,20 @@ // const0 : obj // constN : obj +typedef struct _mp_bytecode_prelude_t { + uint n_state; + uint n_exc_stack; + uint scope_flags; + uint n_pos_args; + uint n_kwonly_args; + uint n_def_pos_args; + qstr qstr_block_name; + qstr qstr_source_file; + const byte *line_info; + const byte *locals; + const byte *opcodes; +} mp_bytecode_prelude_t; + // Exception stack entry typedef struct _mp_exc_stack_t { const byte *handler; @@ -84,6 +98,10 @@ typedef struct _mp_code_state_t { #if MICROPY_STACKLESS struct _mp_code_state_t *prev; #endif + #if MICROPY_PY_SYS_SETTRACE + struct _mp_code_state_t *prev_state; + struct _mp_obj_frame_t *frame; + #endif // Variable-length mp_obj_t state[0]; // Variable-length, never accessed by name, only as (void*)(state + n_state) @@ -119,4 +137,31 @@ uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint); #endif +static inline size_t mp_bytecode_get_source_line(const byte *line_info, size_t bc_offset) { + size_t source_line = 1; + size_t c; + while ((c = *line_info)) { + size_t b, l; + if ((c & 0x80) == 0) { + // 0b0LLBBBBB encoding + b = c & 0x1f; + l = c >> 5; + line_info += 1; + } else { + // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + b = c & 0xf; + l = ((c << 4) & 0x700) | line_info[1]; + line_info += 2; + } + if (bc_offset >= b) { + bc_offset -= b; + source_line += l; + } else { + // found source line corresponding to bytecode offset + break; + } + } + return source_line; +} + #endif // MICROPY_INCLUDED_PY_BC_H diff --git a/py/bc0.h b/py/bc0.h index 175ee263a..fcb6eead5 100644 --- a/py/bc0.h +++ b/py/bc0.h @@ -98,8 +98,8 @@ #define MP_BC_MAKE_FUNCTION (0x60) // uint #define MP_BC_MAKE_FUNCTION_DEFARGS (0x61) // uint -#define MP_BC_MAKE_CLOSURE (0x62) // uint -#define MP_BC_MAKE_CLOSURE_DEFARGS (0x63) // uint +#define MP_BC_MAKE_CLOSURE (0x62) // uint; byte +#define MP_BC_MAKE_CLOSURE_DEFARGS (0x63) // uint; byte #define MP_BC_CALL_FUNCTION (0x64) // uint #define MP_BC_CALL_FUNCTION_VAR_KW (0x65) // uint #define MP_BC_CALL_METHOD (0x66) // uint diff --git a/py/binary.c b/py/binary.c index a142776c3..83f28db91 100644 --- a/py/binary.c +++ b/py/binary.c @@ -42,7 +42,7 @@ #define alignof(type) offsetof(struct { char c; type t; }, t) #endif -size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign) { +size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { size_t size = 0; int align = 1; switch (struct_type) { @@ -113,7 +113,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign) { return size; } -mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index) { +mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { mp_int_t val = 0; switch (typecode) { case 'b': @@ -162,7 +162,7 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index) { // The long long type is guaranteed to hold at least 64 bits, and size is at // most 8 (for q and Q), so we will always be able to parse the given data // and fit it into a long long. -long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, const byte *src) { +long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src) { int delta; if (!big_endian) { delta = -1; @@ -185,14 +185,14 @@ long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, con } #define is_signed(typecode) (typecode > 'Z') -mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) { +mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr) { byte *p = *ptr; - mp_uint_t align; + size_t align; size_t size = mp_binary_get_size(struct_type, val_type, &align); if (struct_type == '@') { - // Make pointer aligned - p = (byte*)MP_ALIGN(p, (size_t)align); + // Align p relative to p_base + p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align); #if MP_ENDIANNESS_LITTLE struct_type = '<'; #else @@ -231,7 +231,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) { } } -void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t val) { +void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val) { if (MP_ENDIANNESS_LITTLE && !big_endian) { memcpy(dest, &val, val_sz); } else if (MP_ENDIANNESS_BIG && big_endian) { @@ -250,14 +250,14 @@ void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t } } -void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr) { +void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr) { byte *p = *ptr; - mp_uint_t align; + size_t align; size_t size = mp_binary_get_size(struct_type, val_type, &align); if (struct_type == '@') { - // Make pointer aligned - p = (byte*)MP_ALIGN(p, (size_t)align); + // Align p relative to p_base + p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align); if (MP_ENDIANNESS_LITTLE) { struct_type = '<'; } else { @@ -315,7 +315,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte ** mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val); } -void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t val_in) { +void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in) { switch (typecode) { #if MICROPY_PY_BUILTINS_FLOAT case 'f': @@ -342,7 +342,7 @@ void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t v } } -void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, mp_int_t val) { +void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val) { switch (typecode) { case 'b': ((signed char*)p)[index] = val; diff --git a/py/binary.h b/py/binary.h index 71182042f..5c645bcaa 100644 --- a/py/binary.h +++ b/py/binary.h @@ -34,13 +34,13 @@ // type-specification errors due to end-of-string. #define BYTEARRAY_TYPECODE 1 -size_t mp_binary_get_size(char struct_type, char val_type, mp_uint_t *palign); -mp_obj_t mp_binary_get_val_array(char typecode, void *p, mp_uint_t index); -void mp_binary_set_val_array(char typecode, void *p, mp_uint_t index, mp_obj_t val_in); -void mp_binary_set_val_array_from_int(char typecode, void *p, mp_uint_t index, mp_int_t val); -mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr); -void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte **ptr); -long long mp_binary_get_int(mp_uint_t size, bool is_signed, bool big_endian, const byte *src); -void mp_binary_set_int(mp_uint_t val_sz, bool big_endian, byte *dest, mp_uint_t val); +size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign); +mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index); +void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in); +void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val); +mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr); +void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr); +long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src); +void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val); #endif // MICROPY_INCLUDED_PY_BINARY_H diff --git a/py/compile.c b/py/compile.c index 27b706c8f..8b2d820b3 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1608,6 +1608,9 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_ qstr qstr_exception_local = 0; uint end_finally_label = comp_next_label(comp); + #if MICROPY_PY_SYS_SETTRACE + EMIT_ARG(set_source_line, pns_except->source_line); + #endif if (MP_PARSE_NODE_IS_NULL(pns_except->nodes[0])) { // this is a catch all exception handler @@ -3078,6 +3081,9 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)scope->pn; assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 3); + // Set the source line number for the start of the lambda + EMIT_ARG(set_source_line, pns->source_line); + // work out number of parameters, keywords and default parameters, and add them to the id_info array // must be done before compiling the body so that arguments are numbered first (for LOAD_FAST etc) if (comp->pass == MP_PASS_SCOPE) { @@ -3112,6 +3118,9 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { scope->num_pos_args = 1; } + // Set the source line number for the start of the comprehension + EMIT_ARG(set_source_line, pns->source_line); + if (scope->kind == SCOPE_LIST_COMP) { EMIT_ARG(build, 0, MP_EMIT_BUILD_LIST); } else if (scope->kind == SCOPE_DICT_COMP) { @@ -3151,6 +3160,9 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { scope_find_or_add_id(scope, MP_QSTR___class__, ID_INFO_KIND_LOCAL); } + #if MICROPY_PY_SYS_SETTRACE + EMIT_ARG(set_source_line, pns->source_line); + #endif compile_load_id(comp, MP_QSTR___name__); compile_store_id(comp, MP_QSTR___module__); EMIT_ARG(load_const_str, MP_PARSE_NODE_LEAF_ARG(pns->nodes[0])); // 0 is class name @@ -3434,7 +3446,7 @@ STATIC void scope_compute_things(scope_t *scope) { #if !MICROPY_PERSISTENT_CODE_SAVE STATIC #endif -mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl) { +mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { // put compiler state on the stack, it's relatively small compiler_t comp_state = {0}; compiler_t *comp = &comp_state; @@ -3445,6 +3457,11 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f comp->continue_label = INVALID_LABEL; // create the module scope + #if MICROPY_EMIT_NATIVE + const uint emit_opt = MP_STATE_VM(default_emit_opt); + #else + const uint emit_opt = MP_EMIT_OPT_NONE; + #endif scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt); // create standard emitter; it's used at least for MP_PASS_SCOPE @@ -3599,8 +3616,8 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f } } -mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl) { - mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, emit_opt, is_repl); +mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { + mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, is_repl); // return function that executes the outer module return mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL); } diff --git a/py/compile.h b/py/compile.h index 99a17a8d1..1ad1f5e9c 100644 --- a/py/compile.h +++ b/py/compile.h @@ -32,11 +32,11 @@ // the compiler will raise an exception if an error occurred // the compiler will clear the parse tree before it returns -mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl); +mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); #if MICROPY_PERSISTENT_CODE_SAVE // this has the same semantics as mp_compile -mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt, bool is_repl); +mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl); #endif // this is implemented in runtime.c diff --git a/py/emitbc.c b/py/emitbc.c index 35eb6df9c..ef0d20a10 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2019 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 @@ -182,20 +182,27 @@ STATIC byte *emit_get_cur_to_write_bytecode(emit_t *emit, int num_bytes_to_write } } -STATIC void emit_write_bytecode_byte(emit_t *emit, byte b1) { +STATIC void emit_write_bytecode_raw_byte(emit_t *emit, byte b1) { byte *c = emit_get_cur_to_write_bytecode(emit, 1); c[0] = b1; } -STATIC void emit_write_bytecode_byte_byte(emit_t* emit, byte b1, byte b2) { +STATIC void emit_write_bytecode_byte(emit_t *emit, int stack_adj, byte b1) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); + byte *c = emit_get_cur_to_write_bytecode(emit, 1); + c[0] = b1; +} + +STATIC void emit_write_bytecode_byte_byte(emit_t* emit, int stack_adj, byte b1, byte b2) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); byte *c = emit_get_cur_to_write_bytecode(emit, 2); c[0] = b1; c[1] = b2; } // Similar to emit_write_bytecode_uint(), just some extra handling to encode sign -STATIC void emit_write_bytecode_byte_int(emit_t *emit, byte b1, mp_int_t num) { - emit_write_bytecode_byte(emit, b1); +STATIC void emit_write_bytecode_byte_int(emit_t *emit, int stack_adj, byte b1, mp_int_t num) { + emit_write_bytecode_byte(emit, stack_adj, b1); // We store each 7 bits in a separate byte, and that's how many bytes needed byte buf[BYTES_FOR_INT]; @@ -220,40 +227,41 @@ STATIC void emit_write_bytecode_byte_int(emit_t *emit, byte b1, mp_int_t num) { *c = *p; } -STATIC void emit_write_bytecode_byte_uint(emit_t *emit, byte b, mp_uint_t val) { - emit_write_bytecode_byte(emit, b); +STATIC void emit_write_bytecode_byte_uint(emit_t *emit, int stack_adj, byte b, mp_uint_t val) { + emit_write_bytecode_byte(emit, stack_adj, b); emit_write_uint(emit, emit_get_cur_to_write_bytecode, val); } #if MICROPY_PERSISTENT_CODE -STATIC void emit_write_bytecode_byte_const(emit_t *emit, byte b, mp_uint_t n, mp_uint_t c) { +STATIC void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n, mp_uint_t c) { if (emit->pass == MP_PASS_EMIT) { emit->const_table[n] = c; } - emit_write_bytecode_byte_uint(emit, b, n); + emit_write_bytecode_byte_uint(emit, stack_adj, b, n); } #endif -STATIC void emit_write_bytecode_byte_qstr(emit_t* emit, byte b, qstr qst) { +STATIC void emit_write_bytecode_byte_qstr(emit_t* emit, int stack_adj, byte b, qstr qst) { #if MICROPY_PERSISTENT_CODE assert((qst >> 16) == 0); + mp_emit_bc_adjust_stack_size(emit, stack_adj); byte *c = emit_get_cur_to_write_bytecode(emit, 3); c[0] = b; c[1] = qst; c[2] = qst >> 8; #else - emit_write_bytecode_byte_uint(emit, b, qst); + emit_write_bytecode_byte_uint(emit, stack_adj, b, qst); #endif } -STATIC void emit_write_bytecode_byte_obj(emit_t *emit, byte b, mp_obj_t obj) { +STATIC void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) { #if MICROPY_PERSISTENT_CODE - emit_write_bytecode_byte_const(emit, b, + emit_write_bytecode_byte_const(emit, stack_adj, b, emit->scope->num_pos_args + emit->scope->num_kwonly_args + emit->ct_cur_obj++, (mp_uint_t)obj); #else // aligns the pointer so it is friendly to GC - emit_write_bytecode_byte(emit, b); + emit_write_bytecode_byte(emit, stack_adj, b); emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(mp_obj_t)); mp_obj_t *c = (mp_obj_t*)emit_get_cur_to_write_bytecode(emit, sizeof(mp_obj_t)); // Verify thar c is already uint-aligned @@ -262,24 +270,28 @@ STATIC void emit_write_bytecode_byte_obj(emit_t *emit, byte b, mp_obj_t obj) { #endif } -STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, byte b, mp_raw_code_t *rc) { +STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) { #if MICROPY_PERSISTENT_CODE - emit_write_bytecode_byte_const(emit, b, + emit_write_bytecode_byte_const(emit, stack_adj, b, emit->scope->num_pos_args + emit->scope->num_kwonly_args + emit->ct_num_obj + emit->ct_cur_raw_code++, (mp_uint_t)(uintptr_t)rc); #else // aligns the pointer so it is friendly to GC - emit_write_bytecode_byte(emit, b); + emit_write_bytecode_byte(emit, stack_adj, b); emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(void*)); void **c = (void**)emit_get_cur_to_write_bytecode(emit, sizeof(void*)); // Verify thar c is already uint-aligned assert(c == MP_ALIGN(c, sizeof(void*))); *c = rc; #endif + #if MICROPY_PY_SYS_SETTRACE + rc->line_of_definition = emit->last_source_line; + #endif } // unsigned labels are relative to ip following this instruction, stored as 16 bits -STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, byte b1, mp_uint_t label) { +STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); mp_uint_t bytecode_offset; if (emit->pass < MP_PASS_EMIT) { bytecode_offset = 0; @@ -293,7 +305,8 @@ STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, byte b1, mp_ui } // signed labels are relative to ip following this instruction, stored as 16 bits, in excess -STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, byte b1, mp_uint_t label) { +STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) { + mp_emit_bc_adjust_stack_size(emit, stack_adj); int bytecode_offset; if (emit->pass < MP_PASS_EMIT) { bytecode_offset = 0; @@ -364,10 +377,10 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { id_info_t *id = &scope->id_info[i]; if (id->kind == ID_INFO_KIND_CELL) { assert(id->local_num < 255); - emit_write_bytecode_byte(emit, id->local_num); // write the local which should be converted to a cell + emit_write_bytecode_raw_byte(emit, id->local_num); // write the local which should be converted to a cell } } - emit_write_bytecode_byte(emit, 255); // end of list sentinel + emit_write_bytecode_raw_byte(emit, 255); // end of list sentinel #if MICROPY_PERSISTENT_CODE emit->ct_cur_obj = 0; @@ -468,10 +481,6 @@ void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) { emit->last_emit_was_return_value = false; } -static inline void emit_bc_pre(emit_t *emit, mp_int_t stack_size_delta) { - mp_emit_bc_adjust_stack_size(emit, stack_size_delta); -} - void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { //printf("source: line %d -> %d offset %d -> %d\n", emit->last_source_line, source_line, emit->last_source_line_offset, emit->bytecode_offset); #if MICROPY_ENABLE_SOURCE_LINE @@ -493,7 +502,7 @@ void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { } void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { - emit_bc_pre(emit, 0); + mp_emit_bc_adjust_stack_size(emit, 0); if (emit->pass == MP_PASS_SCOPE) { return; } @@ -511,64 +520,52 @@ void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { void mp_emit_bc_import(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_NAME == MP_BC_IMPORT_NAME); MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_FROM == MP_BC_IMPORT_FROM); - if (kind == MP_EMIT_IMPORT_FROM) { - emit_bc_pre(emit, 1); - } else { - emit_bc_pre(emit, -1); - } + int stack_adj = kind == MP_EMIT_IMPORT_FROM ? 1 : -1; if (kind == MP_EMIT_IMPORT_STAR) { - emit_write_bytecode_byte(emit, MP_BC_IMPORT_STAR); + emit_write_bytecode_byte(emit, stack_adj, MP_BC_IMPORT_STAR); } else { - emit_write_bytecode_byte_qstr(emit, MP_BC_IMPORT_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, stack_adj, MP_BC_IMPORT_NAME + kind, qst); } } void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) { - emit_bc_pre(emit, 1); - switch (tok) { - case MP_TOKEN_KW_FALSE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_FALSE); break; - case MP_TOKEN_KW_NONE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_NONE); break; - case MP_TOKEN_KW_TRUE: emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_TRUE); break; - default: - assert(tok == MP_TOKEN_ELLIPSIS); - emit_write_bytecode_byte_obj(emit, MP_BC_LOAD_CONST_OBJ, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); - break; + MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_NONE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_NONE); + MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_TRUE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_TRUE); + if (tok == MP_TOKEN_ELLIPSIS) { + emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj)); + } else { + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_CONST_FALSE + (tok - MP_TOKEN_KW_FALSE)); } } void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg) { - emit_bc_pre(emit, 1); if (-16 <= arg && arg <= 47) { - emit_write_bytecode_byte(emit, MP_BC_LOAD_CONST_SMALL_INT_MULTI + 16 + arg); + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_CONST_SMALL_INT_MULTI + 16 + arg); } else { - emit_write_bytecode_byte_int(emit, MP_BC_LOAD_CONST_SMALL_INT, arg); + emit_write_bytecode_byte_int(emit, 1, MP_BC_LOAD_CONST_SMALL_INT, arg); } } void mp_emit_bc_load_const_str(emit_t *emit, qstr qst) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_CONST_STRING, qst); + emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_CONST_STRING, qst); } void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_obj(emit, MP_BC_LOAD_CONST_OBJ, obj); + emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, obj); } void mp_emit_bc_load_null(emit_t *emit) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte(emit, MP_BC_LOAD_NULL); + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_NULL); } void mp_emit_bc_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_LOAD_FAST_N); MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_LOAD_DEREF); (void)qst; - emit_bc_pre(emit, 1); if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) { - emit_write_bytecode_byte(emit, MP_BC_LOAD_FAST_MULTI + local_num); + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_FAST_MULTI + local_num); } else { - emit_write_bytecode_byte_uint(emit, MP_BC_LOAD_FAST_N + kind, local_num); + emit_write_bytecode_byte_uint(emit, 1, MP_BC_LOAD_FAST_N + kind, local_num); } } @@ -576,51 +573,45 @@ void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_LOAD_NAME); MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_LOAD_GLOBAL); (void)qst; - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_NAME + kind, qst); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { - emit_write_bytecode_byte(emit, 0); + emit_write_bytecode_raw_byte(emit, 0); } } void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super) { - emit_bc_pre(emit, 1 - 2 * is_super); - emit_write_bytecode_byte_qstr(emit, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst); + int stack_adj = 1 - 2 * is_super; + emit_write_bytecode_byte_qstr(emit, stack_adj, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst); } void mp_emit_bc_load_build_class(emit_t *emit) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte(emit, MP_BC_LOAD_BUILD_CLASS); + emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_BUILD_CLASS); } void mp_emit_bc_subscr(emit_t *emit, int kind) { if (kind == MP_EMIT_SUBSCR_LOAD) { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_LOAD_SUBSCR); + emit_write_bytecode_byte(emit, -1, MP_BC_LOAD_SUBSCR); } else { if (kind == MP_EMIT_SUBSCR_DELETE) { mp_emit_bc_load_null(emit); mp_emit_bc_rot_three(emit); } - emit_bc_pre(emit, -3); - emit_write_bytecode_byte(emit, MP_BC_STORE_SUBSCR); + emit_write_bytecode_byte(emit, -3, MP_BC_STORE_SUBSCR); } } void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind) { if (kind == MP_EMIT_ATTR_LOAD) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_ATTR, qst); + emit_write_bytecode_byte_qstr(emit, 0, MP_BC_LOAD_ATTR, qst); } else { if (kind == MP_EMIT_ATTR_DELETE) { mp_emit_bc_load_null(emit); mp_emit_bc_rot_two(emit); } - emit_bc_pre(emit, -2); - emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_ATTR, qst); + emit_write_bytecode_byte_qstr(emit, -2, MP_BC_STORE_ATTR, qst); } if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) { - emit_write_bytecode_byte(emit, 0); + emit_write_bytecode_raw_byte(emit, 0); } } @@ -628,98 +619,86 @@ void mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kin MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_STORE_FAST_N); MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_STORE_DEREF); (void)qst; - emit_bc_pre(emit, -1); if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) { - emit_write_bytecode_byte(emit, MP_BC_STORE_FAST_MULTI + local_num); + emit_write_bytecode_byte(emit, -1, MP_BC_STORE_FAST_MULTI + local_num); } else { - emit_write_bytecode_byte_uint(emit, MP_BC_STORE_FAST_N + kind, local_num); + emit_write_bytecode_byte_uint(emit, -1, MP_BC_STORE_FAST_N + kind, local_num); } } void mp_emit_bc_store_global(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_STORE_NAME); MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_STORE_GLOBAL); - emit_bc_pre(emit, -1); - emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, -1, MP_BC_STORE_NAME + kind, qst); } void mp_emit_bc_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) { MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_DELETE_FAST); MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_DELETE_DEREF); (void)qst; - emit_write_bytecode_byte_uint(emit, MP_BC_DELETE_FAST + kind, local_num); + emit_write_bytecode_byte_uint(emit, 0, MP_BC_DELETE_FAST + kind, local_num); } void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind) { MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_DELETE_NAME); MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_DELETE_GLOBAL); - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_qstr(emit, MP_BC_DELETE_NAME + kind, qst); + emit_write_bytecode_byte_qstr(emit, 0, MP_BC_DELETE_NAME + kind, qst); } void mp_emit_bc_dup_top(emit_t *emit) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte(emit, MP_BC_DUP_TOP); + emit_write_bytecode_byte(emit, 1, MP_BC_DUP_TOP); } void mp_emit_bc_dup_top_two(emit_t *emit) { - emit_bc_pre(emit, 2); - emit_write_bytecode_byte(emit, MP_BC_DUP_TOP_TWO); + emit_write_bytecode_byte(emit, 2, MP_BC_DUP_TOP_TWO); } void mp_emit_bc_pop_top(emit_t *emit) { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_POP_TOP); + emit_write_bytecode_byte(emit, -1, MP_BC_POP_TOP); } void mp_emit_bc_rot_two(emit_t *emit) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_ROT_TWO); + emit_write_bytecode_byte(emit, 0, MP_BC_ROT_TWO); } void mp_emit_bc_rot_three(emit_t *emit) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_ROT_THREE); + emit_write_bytecode_byte(emit, 0, MP_BC_ROT_THREE); } void mp_emit_bc_jump(emit_t *emit, mp_uint_t label) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP, label); + emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label); } void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) { - emit_bc_pre(emit, -1); if (cond) { - emit_write_bytecode_byte_signed_label(emit, MP_BC_POP_JUMP_IF_TRUE, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label); } else { - emit_write_bytecode_byte_signed_label(emit, MP_BC_POP_JUMP_IF_FALSE, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_FALSE, label); } } void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label) { - emit_bc_pre(emit, -1); if (cond) { - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP_IF_TRUE_OR_POP, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label); } else { - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP_IF_FALSE_OR_POP, label); + emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_FALSE_OR_POP, label); } } void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth) { if (except_depth == 0) { - emit_bc_pre(emit, 0); if (label & MP_EMIT_BREAK_FROM_FOR) { // need to pop the iterator if we are breaking out of a for loop - emit_write_bytecode_byte(emit, MP_BC_POP_TOP); + emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP); // also pop the iter_buf for (size_t i = 0; i < MP_OBJ_ITER_BUF_NSLOTS - 1; ++i) { - emit_write_bytecode_byte(emit, MP_BC_POP_TOP); + emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP); } } - emit_write_bytecode_byte_signed_label(emit, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); } else { - emit_write_bytecode_byte_signed_label(emit, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); - emit_write_bytecode_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth); + emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR); + emit_write_bytecode_raw_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth); } } @@ -727,52 +706,45 @@ void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) { MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_WITH == MP_BC_SETUP_WITH); MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_EXCEPT == MP_BC_SETUP_EXCEPT); MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_FINALLY == MP_BC_SETUP_FINALLY); - if (kind == MP_EMIT_SETUP_BLOCK_WITH) { // The SETUP_WITH opcode pops ctx_mgr from the top of the stack // and then pushes 3 entries: __exit__, ctx_mgr, as_value. - emit_bc_pre(emit, 2); - } else { - emit_bc_pre(emit, 0); - } - emit_write_bytecode_byte_unsigned_label(emit, MP_BC_SETUP_WITH + kind, label); + int stack_adj = kind == MP_EMIT_SETUP_BLOCK_WITH ? 2 : 0; + emit_write_bytecode_byte_unsigned_label(emit, stack_adj, MP_BC_SETUP_WITH + kind, label); } void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) { mp_emit_bc_load_const_tok(emit, MP_TOKEN_KW_NONE); mp_emit_bc_label_assign(emit, label); - emit_bc_pre(emit, 2); // ensure we have enough stack space to call the __exit__ method - emit_write_bytecode_byte(emit, MP_BC_WITH_CLEANUP); - emit_bc_pre(emit, -4); // cancel the 2 above, plus the 2 from mp_emit_bc_setup_block(MP_EMIT_SETUP_BLOCK_WITH) + // The +2 is to ensure we have enough stack space to call the __exit__ method + emit_write_bytecode_byte(emit, 2, MP_BC_WITH_CLEANUP); + // Cancel the +2 above, plus the +2 from mp_emit_bc_setup_block(MP_EMIT_SETUP_BLOCK_WITH) + mp_emit_bc_adjust_stack_size(emit, -4); } void mp_emit_bc_end_finally(emit_t *emit) { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_END_FINALLY); + emit_write_bytecode_byte(emit, -1, MP_BC_END_FINALLY); } void mp_emit_bc_get_iter(emit_t *emit, bool use_stack) { - emit_bc_pre(emit, use_stack ? MP_OBJ_ITER_BUF_NSLOTS - 1 : 0); - emit_write_bytecode_byte(emit, use_stack ? MP_BC_GET_ITER_STACK : MP_BC_GET_ITER); + int stack_adj = use_stack ? MP_OBJ_ITER_BUF_NSLOTS - 1 : 0; + emit_write_bytecode_byte(emit, stack_adj, use_stack ? MP_BC_GET_ITER_STACK : MP_BC_GET_ITER); } void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_unsigned_label(emit, MP_BC_FOR_ITER, label); + emit_write_bytecode_byte_unsigned_label(emit, 1, MP_BC_FOR_ITER, label); } void mp_emit_bc_for_iter_end(emit_t *emit) { - emit_bc_pre(emit, -MP_OBJ_ITER_BUF_NSLOTS); + mp_emit_bc_adjust_stack_size(emit, -MP_OBJ_ITER_BUF_NSLOTS); } void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { (void)within_exc_handler; - emit_bc_pre(emit, 0); - emit_write_bytecode_byte_unsigned_label(emit, MP_BC_POP_EXCEPT_JUMP, label); + emit_write_bytecode_byte_unsigned_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label); } void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_UNARY_OP_MULTI + op); + emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + op); } void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op) { @@ -784,11 +756,9 @@ void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op) { invert = true; op = MP_BINARY_OP_IS; } - emit_bc_pre(emit, -1); - emit_write_bytecode_byte(emit, MP_BC_BINARY_OP_MULTI + op); + emit_write_bytecode_byte(emit, -1, MP_BC_BINARY_OP_MULTI + op); if (invert) { - emit_bc_pre(emit, 0); - emit_write_bytecode_byte(emit, MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NOT); + emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NOT); } } @@ -798,17 +768,12 @@ void mp_emit_bc_build(emit_t *emit, mp_uint_t n_args, int kind) { MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_MAP == MP_BC_BUILD_MAP); MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SET == MP_BC_BUILD_SET); MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SLICE == MP_BC_BUILD_SLICE); - if (kind == MP_EMIT_BUILD_MAP) { - emit_bc_pre(emit, 1); - } else { - emit_bc_pre(emit, 1 - n_args); - } - emit_write_bytecode_byte_uint(emit, MP_BC_BUILD_TUPLE + kind, n_args); + int stack_adj = kind == MP_EMIT_BUILD_MAP ? 1 : 1 - n_args; + emit_write_bytecode_byte_uint(emit, stack_adj, MP_BC_BUILD_TUPLE + kind, n_args); } void mp_emit_bc_store_map(emit_t *emit) { - emit_bc_pre(emit, -2); - emit_write_bytecode_byte(emit, MP_BC_STORE_MAP); + emit_write_bytecode_byte(emit, -2, MP_BC_STORE_MAP); } void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection_stack_index) { @@ -824,51 +789,46 @@ void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection n = 0; t = 2; } - emit_bc_pre(emit, -1 - n); // the lower 2 bits of the opcode argument indicate the collection type - emit_write_bytecode_byte_uint(emit, MP_BC_STORE_COMP, ((collection_stack_index + n) << 2) | t); + emit_write_bytecode_byte_uint(emit, -1 - n, MP_BC_STORE_COMP, ((collection_stack_index + n) << 2) | t); } void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args) { - emit_bc_pre(emit, -1 + n_args); - emit_write_bytecode_byte_uint(emit, MP_BC_UNPACK_SEQUENCE, n_args); + emit_write_bytecode_byte_uint(emit, -1 + n_args, MP_BC_UNPACK_SEQUENCE, n_args); } void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right) { - emit_bc_pre(emit, -1 + n_left + n_right + 1); - emit_write_bytecode_byte_uint(emit, MP_BC_UNPACK_EX, n_left | (n_right << 8)); + emit_write_bytecode_byte_uint(emit, -1 + n_left + n_right + 1, MP_BC_UNPACK_EX, n_left | (n_right << 8)); } void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { if (n_pos_defaults == 0 && n_kw_defaults == 0) { - emit_bc_pre(emit, 1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_FUNCTION, scope->raw_code); + emit_write_bytecode_byte_raw_code(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code); } else { - emit_bc_pre(emit, -1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code); + emit_write_bytecode_byte_raw_code(emit, -1, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code); } } void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) { if (n_pos_defaults == 0 && n_kw_defaults == 0) { - emit_bc_pre(emit, -n_closed_over + 1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_CLOSURE, scope->raw_code); - emit_write_bytecode_byte(emit, n_closed_over); + int stack_adj = -n_closed_over + 1; + emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE, scope->raw_code); + emit_write_bytecode_raw_byte(emit, n_closed_over); } else { assert(n_closed_over <= 255); - emit_bc_pre(emit, -2 - (mp_int_t)n_closed_over + 1); - emit_write_bytecode_byte_raw_code(emit, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code); - emit_write_bytecode_byte(emit, n_closed_over); + int stack_adj = -2 - (mp_int_t)n_closed_over + 1; + emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code); + emit_write_bytecode_raw_byte(emit, n_closed_over); } } -STATIC void emit_bc_call_function_method_helper(emit_t *emit, mp_int_t stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { +STATIC void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) { if (star_flags) { - emit_bc_pre(emit, stack_adj - (mp_int_t)n_positional - 2 * (mp_int_t)n_keyword - 2); - emit_write_bytecode_byte_uint(emit, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + stack_adj -= (int)n_positional + 2 * (int)n_keyword + 2; + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? } else { - emit_bc_pre(emit, stack_adj - (mp_int_t)n_positional - 2 * (mp_int_t)n_keyword); - emit_write_bytecode_byte_uint(emit, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? + stack_adj -= (int)n_positional + 2 * (int)n_keyword; + emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints? } } @@ -881,22 +841,19 @@ void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_ke } void mp_emit_bc_return_value(emit_t *emit) { - emit_bc_pre(emit, -1); + emit_write_bytecode_byte(emit, -1, MP_BC_RETURN_VALUE); emit->last_emit_was_return_value = true; - emit_write_bytecode_byte(emit, MP_BC_RETURN_VALUE); } void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args) { assert(n_args <= 2); - emit_bc_pre(emit, -n_args); - emit_write_bytecode_byte_byte(emit, MP_BC_RAISE_VARARGS, n_args); + emit_write_bytecode_byte_byte(emit, -n_args, MP_BC_RAISE_VARARGS, n_args); } void mp_emit_bc_yield(emit_t *emit, int kind) { MP_STATIC_ASSERT(MP_BC_YIELD_VALUE + 1 == MP_BC_YIELD_FROM); - emit_bc_pre(emit, -kind); + emit_write_bytecode_byte(emit, -kind, MP_BC_YIELD_VALUE + kind); emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR; - emit_write_bytecode_byte(emit, MP_BC_YIELD_VALUE + kind); } void mp_emit_bc_start_except_handler(emit_t *emit) { diff --git a/py/emitglue.c b/py/emitglue.c index 483a47025..d30a1e674 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -34,6 +34,7 @@ #include "py/emitglue.h" #include "py/runtime0.h" #include "py/bc.h" +#include "py/profile.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -52,6 +53,9 @@ mp_uint_t mp_verbose_flag = 0; mp_raw_code_t *mp_emit_glue_new_raw_code(void) { mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1); rc->kind = MP_CODE_RESERVED; + #if MICROPY_PY_SYS_SETTRACE + rc->line_of_definition = 0; + #endif return rc; } @@ -75,6 +79,11 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, rc->n_raw_code = n_raw_code; #endif + #if MICROPY_PY_SYS_SETTRACE + mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_prof_extract_prelude(code, prelude); + #endif + #ifdef DEBUG_PRINT #if !MICROPY_DEBUG_PRINTERS const size_t len = 0; @@ -172,6 +181,12 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) { ((mp_obj_base_t*)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap; } + + #if MICROPY_PY_SYS_SETTRACE + mp_obj_fun_bc_t *self_fun = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(fun); + self_fun->rc = rc; + #endif + break; } diff --git a/py/emitglue.h b/py/emitglue.h index b67d49ed6..a5411dc2e 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_EMITGLUE_H #include "py/obj.h" +#include "py/bc.h" // These variables and functions glue the code emitters to the runtime. @@ -63,6 +64,14 @@ typedef struct _mp_raw_code_t { size_t fun_data_len; uint16_t n_obj; uint16_t n_raw_code; + #if MICROPY_PY_SYS_SETTRACE + mp_bytecode_prelude_t prelude; + // line_of_definition is a Python source line where the raw_code was + // created e.g. MP_BC_MAKE_FUNCTION. This is different from lineno info + // stored in prelude, which provides line number for first statement of + // a function. Required to properly implement "call" trace event. + mp_uint_t line_of_definition; + #endif #if MICROPY_EMIT_MACHINE_CODE uint16_t prelude_offset; uint16_t n_qstr; diff --git a/py/mkenv.mk b/py/mkenv.mk index 46eedf988..0a59c2ac7 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -49,6 +49,7 @@ PYTHON = python3 AS = $(CROSS_COMPILE)as CC = $(CROSS_COMPILE)gcc CXX = $(CROSS_COMPILE)g++ +GDB = $(CROSS_COMPILE)gdb LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy SIZE = $(CROSS_COMPILE)size diff --git a/py/mkrules.mk b/py/mkrules.mk index 7690c5409..b74dd4549 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -70,7 +70,7 @@ $(OBJ): | $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/mpversion.h # - if anything in QSTR_GLOBAL_DEPENDENCIES is newer, then process all source files ($^) # - else, if list of newer prerequisites ($?) is not empty, then process just these ($?) # - else, process all source files ($^) [this covers "make -B" which can set $? to empty] -$(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(HEADER_BUILD)/mpversion.h +$(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(QSTR_GLOBAL_REQUIREMENTS) $(ECHO) "GEN $@" $(Q)$(CPP) $(QSTR_GEN_EXTRA_CFLAGS) $(CFLAGS) $(if $(filter $?,$(QSTR_GLOBAL_DEPENDENCIES)),$^,$(if $?,$?,$^)) >$(HEADER_BUILD)/qstr.i.last diff --git a/py/modmath.c b/py/modmath.c index d106f240c..35bb44bea 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -171,6 +171,42 @@ MATH_FUN_1(lgamma, lgamma) #endif //TODO: fsum +#if MICROPY_PY_MATH_ISCLOSE +STATIC mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_a, ARG_b, ARG_rel_tol, ARG_abs_tol }; + static const mp_arg_t allowed_args[] = { + {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + const mp_float_t a = mp_obj_get_float(args[ARG_a].u_obj); + const mp_float_t b = mp_obj_get_float(args[ARG_b].u_obj); + const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL + ? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj); + const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj); + if (rel_tol < (mp_float_t)0.0 || abs_tol < (mp_float_t)0.0) { + math_error(); + } + if (a == b) { + return mp_const_true; + } + const mp_float_t difference = MICROPY_FLOAT_C_FUN(fabs)(a - b); + if (isinf(difference)) { // Either a or b is inf + return mp_const_false; + } + if ((difference <= abs_tol) || + (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * a)) || + (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * b))) { + return mp_const_true; + } + return mp_const_false; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_math_isclose_obj, 2, mp_math_isclose); +#endif + // Function that takes a variable number of arguments // log(x[, base]) @@ -335,6 +371,9 @@ STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) }, { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) }, { MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) }, + #if MICROPY_PY_MATH_ISCLOSE + { MP_ROM_QSTR(MP_QSTR_isclose), MP_ROM_PTR(&mp_math_isclose_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) }, { MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) }, { MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) }, diff --git a/py/modstruct.c b/py/modstruct.c index 8617a8e0d..36af4260e 100644 --- a/py/modstruct.c +++ b/py/modstruct.c @@ -97,7 +97,7 @@ STATIC size_t calc_size_items(const char *fmt, size_t *total_sz) { size += cnt; } else { total_cnt += cnt; - mp_uint_t align; + size_t align; size_t sz = mp_binary_get_size(fmt_type, *fmt, &align); while (cnt--) { // Apply alignment @@ -146,6 +146,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { } p += offset; } + byte *p_base = p; // Check that the input buffer is big enough to unpack all the values if (p + total_sz > end_p) { @@ -164,7 +165,7 @@ STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) { res->items[i++] = item; } else { while (cnt--) { - item = mp_binary_get_val(fmt_type, *fmt, &p); + item = mp_binary_get_val(fmt_type, *fmt, p_base, &p); res->items[i++] = item; } } @@ -179,6 +180,7 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, c const char *fmt = mp_obj_str_get_str(fmt_in); char fmt_type = get_fmt_type(&fmt); + byte *p_base = p; size_t i; for (i = 0; i < n_args;) { mp_uint_t cnt = 1; @@ -203,7 +205,7 @@ STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, c } else { // If we run out of args then we just finish; CPython would raise struct.error while (cnt-- && i < n_args) { - mp_binary_set_val(fmt_type, *fmt, args[i++], &p); + mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p); } } fmt++; diff --git a/py/modsys.c b/py/modsys.c index 343451732..4886419d0 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -35,6 +35,11 @@ #include "py/smallint.h" #include "py/runtime.h" +#if MICROPY_PY_SYS_SETTRACE +#include "py/objmodule.h" +#include "py/profile.h" +#endif + #if MICROPY_PY_SYS // defined per port; type of these is irrelevant, just need pointer @@ -146,6 +151,24 @@ STATIC mp_obj_t mp_sys_getsizeof(mp_obj_t obj) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof); #endif +#if MICROPY_PY_SYS_ATEXIT +// atexit(callback): Callback is called when sys.exit is called. +STATIC mp_obj_t mp_sys_atexit(mp_obj_t obj) { + mp_obj_t old = MP_STATE_VM(sys_exitfunc); + MP_STATE_VM(sys_exitfunc) = obj; + return old; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_atexit_obj, mp_sys_atexit); +#endif + +#if MICROPY_PY_SYS_SETTRACE +// settrace(tracefunc): Set the system’s trace function. +STATIC mp_obj_t mp_sys_settrace(mp_obj_t obj) { + return mp_prof_settrace(obj); +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace); +#endif // MICROPY_PY_SYS_SETTRACE + STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) }, @@ -180,6 +203,10 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mp_sys_exit_obj) }, #endif + #if MICROPY_PY_SYS_SETTRACE + { MP_ROM_QSTR(MP_QSTR_settrace), MP_ROM_PTR(&mp_sys_settrace_obj) }, + #endif + #if MICROPY_PY_SYS_STDFILES { MP_ROM_QSTR(MP_QSTR_stdin), MP_ROM_PTR(&mp_sys_stdin_obj) }, { MP_ROM_QSTR(MP_QSTR_stdout), MP_ROM_PTR(&mp_sys_stdout_obj) }, @@ -201,6 +228,9 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { */ { MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&mp_sys_print_exception_obj) }, + #if MICROPY_PY_SYS_ATEXIT + { MP_ROM_QSTR(MP_QSTR_atexit), MP_ROM_PTR(&mp_sys_atexit_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(mp_module_sys_globals, mp_module_sys_globals_table); diff --git a/py/mpconfig.h b/py/mpconfig.h index a111b27ae..64dadde92 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1079,6 +1079,11 @@ typedef double mp_float_t; #define MICROPY_PY_MATH_FACTORIAL (0) #endif +// Whether to provide math.isclose function +#ifndef MICROPY_PY_MATH_ISCLOSE +#define MICROPY_PY_MATH_ISCLOSE (0) +#endif + // Whether to provide "cmath" module #ifndef MICROPY_PY_CMATH #define MICROPY_PY_CMATH (0) @@ -1161,6 +1166,16 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_EXIT (1) #endif +// Whether to provide "sys.atexit" function (MicroPython extension) +#ifndef MICROPY_PY_SYS_ATEXIT +#define MICROPY_PY_SYS_ATEXIT (0) +#endif + +// Whether to provide "sys.settrace" function +#ifndef MICROPY_PY_SYS_SETTRACE +#define MICROPY_PY_SYS_SETTRACE (0) +#endif + // Whether to provide "sys.getsizeof" function #ifndef MICROPY_PY_SYS_GETSIZEOF #define MICROPY_PY_SYS_GETSIZEOF (0) @@ -1249,6 +1264,10 @@ typedef double mp_float_t; #define MICROPY_PY_URE (0) #endif +#ifndef MICROPY_PY_URE_DEBUG +#define MICROPY_PY_URE_DEBUG (0) +#endif + #ifndef MICROPY_PY_URE_MATCH_GROUPS #define MICROPY_PY_URE_MATCH_GROUPS (0) #endif @@ -1517,6 +1536,15 @@ typedef double mp_float_t; #define MP_UNLIKELY(x) __builtin_expect((x), 0) #endif +// To annotate that code is unreachable +#ifndef MP_UNREACHABLE +#if defined(__GNUC__) +#define MP_UNREACHABLE __builtin_unreachable(); +#else +#define MP_UNREACHABLE for (;;); +#endif +#endif + #ifndef MP_HTOBE16 #if MP_ENDIANNESS_LITTLE # define MP_HTOBE16(x) ((uint16_t)( (((x) & 0xff) << 8) | (((x) >> 8) & 0xff) )) @@ -1548,4 +1576,14 @@ typedef double mp_float_t; # define MP_WARN_CAT(x) (NULL) #endif +// Feature dependency check. +#if MICROPY_PY_SYS_SETTRACE +#if !MICROPY_PERSISTENT_CODE_SAVE +#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_PERSISTENT_CODE_SAVE to be enabled" +#endif +#if MICROPY_COMP_CONST +#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_COMP_CONST to be disabled" +#endif +#endif + #endif // MICROPY_INCLUDED_PY_MPCONFIG_H diff --git a/py/mpstate.h b/py/mpstate.h index b7eb6bdeb..aba32084d 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -149,6 +149,11 @@ typedef struct _mp_state_vm_t { mp_obj_base_t *cur_exception; #endif + #if MICROPY_PY_SYS_ATEXIT + // exposed through sys.atexit function + mp_obj_t sys_exitfunc; + #endif + // dictionary for the __main__ module mp_obj_dict_t dict_main; @@ -200,6 +205,9 @@ typedef struct _mp_state_vm_t { #if MICROPY_ENABLE_COMPILER mp_uint_t mp_optimise_value; + #if MICROPY_EMIT_NATIVE + uint8_t default_emit_opt; // one of MP_EMIT_OPT_xxx + #endif #endif // size of the emergency exception buf, if it's dynamically allocated @@ -245,6 +253,12 @@ typedef struct _mp_state_thread_t { mp_obj_dict_t *dict_globals; nlr_buf_t *nlr_top; + + #if MICROPY_PY_SYS_SETTRACE + mp_obj_t prof_trace_callback; + bool prof_callback_is_executing; + struct _mp_code_state_t *current_code_state; + #endif } mp_state_thread_t; // This structure combines the above 3 structures. diff --git a/py/nlrthumb.c b/py/nlrthumb.c index eef05229d..bc3038827 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -135,11 +135,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - #if defined(__GNUC__) - __builtin_unreachable(); - #else - for (;;); // needed to silence compiler warning - #endif + MP_UNREACHABLE } #endif // MICROPY_NLR_THUMB diff --git a/py/nlrx64.c b/py/nlrx64.c index a3a1cf341..95496b380 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -108,7 +108,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_X64 diff --git a/py/nlrx86.c b/py/nlrx86.c index 59b97d8ee..6195db63c 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -100,7 +100,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_X86 diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index cd3dee364..895b2029e 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -77,7 +77,7 @@ NORETURN void nlr_jump(void *val) { : // clobbered registers ); - for (;;); // needed to silence compiler warning + MP_UNREACHABLE } #endif // MICROPY_NLR_XTENSA diff --git a/py/objarray.c b/py/objarray.c index 4e58d8e5d..c19617d4e 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -445,7 +445,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value if (len_adj > o->free) { // TODO: alloc policy; at the moment we go conservative o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz); - o->free = 0; + o->free = len_adj; dest_items = o->items; } mp_seq_replace_slice_grow_inplace(dest_items, o->len, @@ -458,6 +458,7 @@ STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz); // TODO: alloc policy after shrinking } + o->free -= len_adj; o->len += len_adj; return mp_const_none; #else diff --git a/py/objexcept.c b/py/objexcept.c index 1fb636f66..7e3fdcc27 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -100,11 +100,6 @@ mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { #endif #endif // MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF -// Instance of GeneratorExit exception - needed by generator.close() -// This would belong to objgenerator.c, but to keep mp_obj_exception_t -// definition module-private so far, have it here. -const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t*)&mp_const_empty_tuple_obj}; - void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { mp_obj_exception_t *o = MP_OBJ_TO_PTR(o_in); mp_print_kind_t k = kind & ~PRINT_EXC_SUBCLASS; diff --git a/py/objfun.h b/py/objfun.h index 257b8a65a..905b5dbca 100644 --- a/py/objfun.h +++ b/py/objfun.h @@ -33,6 +33,9 @@ typedef struct _mp_obj_fun_bc_t { mp_obj_dict_t *globals; // the context within which this function was defined const byte *bytecode; // bytecode for the function const mp_uint_t *const_table; // constant table + #if MICROPY_PY_SYS_SETTRACE + const struct _mp_raw_code_t *rc; + #endif // the following extra_args array is allocated space to take (in order): // - values of positional default args (if any) // - a single slot for default kw args dict (if it has them) diff --git a/py/objgenerator.c b/py/objgenerator.c index b7186b8d0..62e644616 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -34,6 +34,9 @@ #include "py/objfun.h" #include "py/stackctrl.h" +// Instance of GeneratorExit exception - needed by generator.close() +const mp_obj_exception_t mp_const_GeneratorExit_obj = {{&mp_type_GeneratorExit}, 0, 0, NULL, (mp_obj_tuple_t*)&mp_const_empty_tuple_obj}; + /******************************************************************************/ /* generator wrapper */ diff --git a/py/objtuple.c b/py/objtuple.c index 39eb94023..740e0795b 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -31,6 +31,9 @@ #include "py/objtuple.h" #include "py/runtime.h" +// type check is done on getiter method to allow tuple, namedtuple, attrtuple +#define mp_obj_is_tuple_compatible(o) (mp_obj_get_type(o)->getiter == mp_obj_tuple_getiter) + /******************************************************************************/ /* tuple */ @@ -101,8 +104,7 @@ STATIC mp_obj_t mp_obj_tuple_make_new(const mp_obj_type_t *type_in, size_t n_arg // Don't pass MP_BINARY_OP_NOT_EQUAL here STATIC mp_obj_t tuple_cmp_helper(mp_uint_t op, mp_obj_t self_in, mp_obj_t another_in) { - // type check is done on getiter method to allow tuple, namedtuple, attrtuple - mp_check_self(mp_obj_get_type(self_in)->getiter == mp_obj_tuple_getiter); + mp_check_self(mp_obj_is_tuple_compatible(self_in)); mp_obj_type_t *another_type = mp_obj_get_type(another_in); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); if (another_type->getiter != mp_obj_tuple_getiter) { @@ -249,7 +251,7 @@ mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items) { } void mp_obj_tuple_get(mp_obj_t self_in, size_t *len, mp_obj_t **items) { - assert(mp_obj_is_type(self_in, &mp_type_tuple)); + assert(mp_obj_is_tuple_compatible(self_in)); mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in); *len = self->len; *items = &self->items[0]; diff --git a/py/profile.c b/py/profile.c new file mode 100644 index 000000000..8c4feaa11 --- /dev/null +++ b/py/profile.c @@ -0,0 +1,984 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) SatoshiLabs + * + * 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/profile.h" +#include "py/bc0.h" +#include "py/gc.h" + +#if MICROPY_PY_SYS_SETTRACE + +#define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) + +STATIC uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) { + const mp_bytecode_prelude_t *prelude = &rc->prelude; + return mp_bytecode_get_source_line(prelude->line_info, bc + prelude->opcodes - prelude->locals); +} + +void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude) { + const byte *ip = bytecode; + + prelude->n_state = mp_decode_uint(&ip); + prelude->n_exc_stack = mp_decode_uint(&ip); + prelude->scope_flags = *ip++; + prelude->n_pos_args = *ip++; + prelude->n_kwonly_args = *ip++; + prelude->n_def_pos_args = *ip++; + + const byte *code_info = ip; + size_t code_info_size = mp_decode_uint(&ip); + + qstr block_name = ip[0] | (ip[1] << 8); + qstr source_file = ip[2] | (ip[3] << 8); + ip += 4; + prelude->qstr_block_name = block_name; + prelude->qstr_source_file = source_file; + + prelude->line_info = ip; + prelude->locals = code_info + code_info_size; + + ip = prelude->locals; + while (*ip++ != 255) { + } + prelude->opcodes = ip; +} + +/******************************************************************************/ +// code object + +STATIC void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_printf(print, + "", + prelude->qstr_block_name, + o, + prelude->qstr_source_file, + rc->line_of_definition + ); +} + +STATIC mp_obj_tuple_t* code_consts(const mp_raw_code_t *rc) { + const mp_bytecode_prelude_t *prelude = &rc->prelude; + int start = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj; + int stop = prelude->n_pos_args + prelude->n_kwonly_args + rc->n_obj + rc->n_raw_code; + mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(stop - start + 1, NULL)); + + size_t const_no = 0; + for (int i = start; i < stop; ++i) { + mp_obj_t code = mp_obj_new_code((const mp_raw_code_t*)MP_OBJ_TO_PTR(rc->const_table[i])); + if (code == MP_OBJ_NULL) { + m_malloc_fail(sizeof(mp_obj_code_t)); + } + consts->items[const_no++] = code; + } + consts->items[const_no++] = mp_const_none; + + return consts; +} + +STATIC mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { + // const mp_bytecode_prelude_t *prelude = &rc->prelude; + uint start = 0; + uint stop = rc->fun_data_len - start; + + uint last_lineno = mp_prof_bytecode_lineno(rc, start); + uint lasti = 0; + + const uint buffer_chunk_size = (stop-start) >> 2; // heuristic magic + uint buffer_size = buffer_chunk_size; + byte *buffer = m_new(byte, buffer_size); + uint buffer_index = 0; + + for (uint i = start; i < stop; ++i) { + uint lineno = mp_prof_bytecode_lineno(rc, i); + size_t line_diff = lineno - last_lineno; + if (line_diff > 0) { + uint instr_diff = (i - start) - lasti; + + assert(instr_diff < 256); + assert(line_diff < 256); + + if (buffer_index + 2 > buffer_size) { + buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size); + buffer_size = buffer_size + buffer_chunk_size; + } + last_lineno = lineno; + lasti = i - start; + buffer[buffer_index++] = instr_diff; + buffer[buffer_index++] = line_diff; + } + } + + mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index); + m_del(byte, buffer, buffer_size); + return o; +} + +STATIC void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in); + const mp_raw_code_t *rc = o->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + switch(attr) { + case MP_QSTR_co_code: + dest[0] = mp_obj_new_bytes( + (void*)prelude->opcodes, + rc->fun_data_len - (prelude->opcodes - (const byte*)rc->fun_data) + ); + break; + case MP_QSTR_co_consts: + dest[0] = MP_OBJ_FROM_PTR(code_consts(rc)); + break; + case MP_QSTR_co_filename: + dest[0] = MP_OBJ_NEW_QSTR(prelude->qstr_source_file); + break; + case MP_QSTR_co_firstlineno: + dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0)); + break; + case MP_QSTR_co_name: + dest[0] = MP_OBJ_NEW_QSTR(prelude->qstr_block_name); + break; + case MP_QSTR_co_names: + dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); + break; + case MP_QSTR_co_lnotab: + if (!o->lnotab) { + o->lnotab = raw_code_lnotab(rc); + } + dest[0] = o->lnotab; + break; + } +} + +const mp_obj_type_t mp_type_code = { + { &mp_type_type }, + .name = MP_QSTR_code, + .print = code_print, + .unary_op = mp_generic_unary_op, + .attr = code_attr, +}; + +mp_obj_t mp_obj_new_code(const mp_raw_code_t *rc) { + mp_obj_code_t *o = m_new_obj_maybe(mp_obj_code_t); + if (o == NULL) { + return MP_OBJ_NULL; + } + o->base.type = &mp_type_code; + o->rc = rc; + o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? + o->lnotab = MP_OBJ_NULL; + return MP_OBJ_FROM_PTR(o); +} + +/******************************************************************************/ +// frame object + +STATIC void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { + (void)kind; + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(o_in); + mp_obj_code_t *code = frame->code; + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + mp_printf(print, + "", + frame, + prelude->qstr_source_file, + frame->lineno, + prelude->qstr_block_name + ); +} + +STATIC void frame_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] != MP_OBJ_NULL) { + // not load attribute + return; + } + + mp_obj_frame_t *o = MP_OBJ_TO_PTR(self_in); + + switch(attr) { + case MP_QSTR_f_back: + dest[0] = mp_const_none; + if (o->code_state->prev_state) { + dest[0] = MP_OBJ_FROM_PTR(o->code_state->prev_state->frame); + } + break; + case MP_QSTR_f_code: + dest[0] = MP_OBJ_FROM_PTR(o->code); + break; + case MP_QSTR_f_globals: + dest[0] = MP_OBJ_FROM_PTR(o->code_state->fun_bc->globals); + break; + case MP_QSTR_f_lasti: + dest[0] = MP_OBJ_NEW_SMALL_INT(o->lasti); + break; + case MP_QSTR_f_lineno: + dest[0] = MP_OBJ_NEW_SMALL_INT(o->lineno); + break; + } +} + +const mp_obj_type_t mp_type_frame = { + { &mp_type_type }, + .name = MP_QSTR_frame, + .print = frame_print, + .unary_op = mp_generic_unary_op, + .attr = frame_attr, +}; + +mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) { + if (gc_is_locked()) { + return MP_OBJ_NULL; + } + + mp_obj_frame_t *o = m_new_obj_maybe(mp_obj_frame_t); + if (o == NULL) { + return MP_OBJ_NULL; + } + + mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->rc)); + if (code == NULL) { + return MP_OBJ_NULL; + } + + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + o->code_state = code_state; + o->base.type = &mp_type_frame; + o->back = NULL; + o->code = code; + o->lasti = code_state->ip - prelude->opcodes; + o->lineno = mp_prof_bytecode_lineno(rc, o->lasti); + o->trace_opcodes = false; + o->callback = MP_OBJ_NULL; + + return MP_OBJ_FROM_PTR(o); +} + + +/******************************************************************************/ +// Trace logic + +typedef struct { + struct _mp_obj_frame_t * frame; + mp_obj_t event; + mp_obj_t arg; +} prof_callback_args_t; + +STATIC mp_obj_t mp_prof_callback_invoke(mp_obj_t callback, prof_callback_args_t *args) { + assert(mp_obj_is_callable(callback)); + + mp_prof_is_executing = true; + + mp_obj_t a[3] = {MP_OBJ_FROM_PTR(args->frame), args->event, args->arg}; + mp_obj_t top = mp_call_function_n_kw(callback, 3, 0, a); + + mp_prof_is_executing = false; + + if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + nlr_raise(obj); + } + return top; +} + +mp_obj_t mp_prof_settrace(mp_obj_t callback) { + if (mp_obj_is_callable(callback)) { + prof_trace_cb = callback; + } else { + prof_trace_cb = MP_OBJ_NULL; + } + return mp_const_none; +} + +mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state) { + assert(!mp_prof_is_executing); + + mp_obj_frame_t *frame = MP_OBJ_TO_PTR(mp_obj_new_frame(code_state)); + if (frame == NULL) { + // Couldn't allocate a frame object + return MP_OBJ_NULL; + } + + if (code_state->prev_state && code_state->frame == NULL) { + // We are entering not-yet-traced frame + // which means it's a CALL event (not a GENERATOR) + // so set the function definition line. + const mp_raw_code_t *rc = code_state->fun_bc->rc; + frame->lineno = rc->line_of_definition; + if (!rc->line_of_definition) { + frame->lineno = mp_prof_bytecode_lineno(rc, 0); + } + } + code_state->frame = frame; + + if (!prof_trace_cb) { + return MP_OBJ_NULL; + } + + mp_obj_t top; + prof_callback_args_t _args, *args=&_args; + args->frame = code_state->frame; + + // SETTRACE event CALL + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_call); + args->arg = mp_const_none; + top = mp_prof_callback_invoke(prof_trace_cb, args); + + code_state->frame->callback = mp_obj_is_callable(top) ? top : MP_OBJ_NULL; + + // Invalidate the last executed line number so the LINE trace can trigger after this CALL. + frame->lineno = 0; + + return top; +} + +mp_obj_t mp_prof_frame_update(const mp_code_state_t *code_state) { + mp_obj_frame_t *frame = code_state->frame; + if (frame == NULL) { + // Frame was not allocated (eg because there was no memory available) + return MP_OBJ_NULL; + } + + mp_obj_frame_t *o = frame; + mp_obj_code_t *code = o->code; + const mp_raw_code_t *rc = code->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + + assert(o->code_state == code_state); + + o->lasti = code_state->ip - prelude->opcodes; + o->lineno = mp_prof_bytecode_lineno(rc, o->lasti); + + return MP_OBJ_FROM_PTR(o); +} + +mp_obj_t mp_prof_instr_tick(mp_code_state_t *code_state, bool is_exception) { + // Detect execution recursion + assert(!mp_prof_is_executing); + assert(code_state->frame); + assert(mp_obj_get_type(code_state->frame) == &mp_type_frame); + + // Detect data recursion + assert(code_state != code_state->prev_state); + + mp_obj_t top = mp_const_none; + mp_obj_t callback = code_state->frame->callback; + + prof_callback_args_t _args, *args=&_args; + args->frame = code_state->frame; + args->event = mp_const_none; + args->arg = mp_const_none; + + // Call event's are handled inside mp_prof_frame_enter + + // SETTRACE event EXCEPTION + if (is_exception) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_exception); + top = mp_prof_callback_invoke(callback, args); + return top; + } + + // SETTRACE event LINE + const mp_raw_code_t *rc = code_state->fun_bc->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + size_t prev_line_no = args->frame->lineno; + size_t current_line_no = mp_prof_bytecode_lineno(rc, code_state->ip - prelude->opcodes); + if (prev_line_no != current_line_no) { + args->frame->lineno = current_line_no; + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_line); + top = mp_prof_callback_invoke(callback, args); + } + + // SETTRACE event RETURN + const byte *ip = code_state->ip; + if (*ip == MP_BC_RETURN_VALUE || *ip == MP_BC_YIELD_VALUE) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_return); + top = mp_prof_callback_invoke(callback, args); + if (code_state->prev_state && *ip == MP_BC_RETURN_VALUE) { + code_state->frame->callback = MP_OBJ_NULL; + } + } + + // SETTRACE event OPCODE + // TODO: frame.f_trace_opcodes=True + if (false) { + args->event = MP_OBJ_NEW_QSTR(MP_QSTR_opcode); + } + + return top; +} + +/******************************************************************************/ +// DEBUG + +// This section is for debugging the settrace feature itself, and is not intended +// to be included in production/release builds. The code structure for this block +// was taken from py/showbc.c and should not be used as a reference. To enable +// this debug feature enable MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE in py/profile.h. +#if MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE + +#include "runtime0.h" + +#define DECODE_UINT { \ + unum = 0; \ + do { \ + unum = (unum << 7) + (*ip & 0x7f); \ + } while ((*ip++ & 0x80) != 0); \ +} +#define DECODE_ULABEL do { unum = (ip[0] | (ip[1] << 8)); ip += 2; } while (0) +#define DECODE_SLABEL do { unum = (ip[0] | (ip[1] << 8)) - 0x8000; ip += 2; } while (0) + +#define DECODE_QSTR \ + qst = ip[0] | ip[1] << 8; \ + ip += 2; +#define DECODE_PTR \ + DECODE_UINT; \ + ptr = (const byte*)const_table[unum] +#define DECODE_OBJ \ + DECODE_UINT; \ + obj = (mp_obj_t)const_table[unum] + +typedef struct _mp_dis_instruction_t { + mp_uint_t qstr_opname; + mp_uint_t arg; + mp_obj_t argobj; + mp_obj_t argobjex_cache; +} mp_dis_instruction_t; + +STATIC const byte *mp_prof_opcode_decode(const byte *ip, const mp_uint_t *const_table, mp_dis_instruction_t *instruction) { + mp_uint_t unum; + const byte* ptr; + mp_obj_t obj; + qstr qst; + + instruction->qstr_opname = MP_QSTR_; + instruction->arg = 0; + instruction->argobj= mp_const_none; + instruction->argobjex_cache = mp_const_none; + + switch (*ip++) { + case MP_BC_LOAD_CONST_FALSE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_FALSE; + break; + + case MP_BC_LOAD_CONST_NONE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_NONE; + break; + + case MP_BC_LOAD_CONST_TRUE: + instruction->qstr_opname = MP_QSTR_LOAD_CONST_TRUE; + break; + + case MP_BC_LOAD_CONST_SMALL_INT: { + mp_int_t num = 0; + if ((ip[0] & 0x40) != 0) { + // Number is negative + num--; + } + do { + num = (num << 7) | (*ip & 0x7f); + } while ((*ip++ & 0x80) != 0); + instruction->qstr_opname = MP_QSTR_LOAD_CONST_SMALL_INT; + instruction->arg = num; + break; + } + + case MP_BC_LOAD_CONST_STRING: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_CONST_STRING; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_CONST_OBJ: + DECODE_OBJ; + instruction->qstr_opname = MP_QSTR_LOAD_CONST_OBJ; + instruction->arg = unum; + instruction->argobj= obj; + break; + + case MP_BC_LOAD_NULL: + instruction->qstr_opname = MP_QSTR_LOAD_NULL; + break; + + case MP_BC_LOAD_FAST_N: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_LOAD_FAST_N; + instruction->arg = unum; + break; + + case MP_BC_LOAD_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_LOAD_DEREF; + instruction->arg = unum; + break; + + case MP_BC_LOAD_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_LOAD_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_GLOBAL; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_LOAD_ATTR: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_ATTR; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_LOAD_METHOD: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_METHOD; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_SUPER_METHOD: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_LOAD_SUPER_METHOD; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_LOAD_BUILD_CLASS: + instruction->qstr_opname = MP_QSTR_LOAD_BUILD_CLASS; + break; + + case MP_BC_LOAD_SUBSCR: + instruction->qstr_opname = MP_QSTR_LOAD_SUBSCR; + break; + + case MP_BC_STORE_FAST_N: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_FAST_N; + instruction->arg = unum; + break; + + case MP_BC_STORE_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_DEREF; + instruction->arg = unum; + break; + + case MP_BC_STORE_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_STORE_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_GLOBAL; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_STORE_ATTR: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_STORE_ATTR; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(*ip++); + } + break; + + case MP_BC_STORE_SUBSCR: + instruction->qstr_opname = MP_QSTR_STORE_SUBSCR; + break; + + case MP_BC_DELETE_FAST: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_DELETE_FAST; + instruction->arg = unum; + break; + + case MP_BC_DELETE_DEREF: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_DELETE_DEREF; + instruction->arg = unum; + break; + + case MP_BC_DELETE_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_DELETE_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_DELETE_GLOBAL: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_DELETE_GLOBAL; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_DUP_TOP: + instruction->qstr_opname = MP_QSTR_DUP_TOP; + break; + + case MP_BC_DUP_TOP_TWO: + instruction->qstr_opname = MP_QSTR_DUP_TOP_TWO; + break; + + case MP_BC_POP_TOP: + instruction->qstr_opname = MP_QSTR_POP_TOP; + break; + + case MP_BC_ROT_TWO: + instruction->qstr_opname = MP_QSTR_ROT_TWO; + break; + + case MP_BC_ROT_THREE: + instruction->qstr_opname = MP_QSTR_ROT_THREE; + break; + + case MP_BC_JUMP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP; + instruction->arg = unum; + break; + + case MP_BC_POP_JUMP_IF_TRUE: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_POP_JUMP_IF_TRUE; + instruction->arg = unum; + break; + + case MP_BC_POP_JUMP_IF_FALSE: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_POP_JUMP_IF_FALSE; + instruction->arg = unum; + break; + + case MP_BC_JUMP_IF_TRUE_OR_POP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP_IF_TRUE_OR_POP; + instruction->arg = unum; + break; + + case MP_BC_JUMP_IF_FALSE_OR_POP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_JUMP_IF_FALSE_OR_POP; + instruction->arg = unum; + break; + + case MP_BC_SETUP_WITH: + DECODE_ULABEL; // loop-like labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_WITH; + instruction->arg = unum; + break; + + case MP_BC_WITH_CLEANUP: + instruction->qstr_opname = MP_QSTR_WITH_CLEANUP; + break; + + case MP_BC_UNWIND_JUMP: + DECODE_SLABEL; + instruction->qstr_opname = MP_QSTR_UNWIND_JUMP; + instruction->arg = unum; + break; + + case MP_BC_SETUP_EXCEPT: + DECODE_ULABEL; // except labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_EXCEPT; + instruction->arg = unum; + break; + + case MP_BC_SETUP_FINALLY: + DECODE_ULABEL; // except labels are always forward + instruction->qstr_opname = MP_QSTR_SETUP_FINALLY; + instruction->arg = unum; + break; + + case MP_BC_END_FINALLY: + // if TOS is an exception, reraises the exception (3 values on TOS) + // if TOS is an integer, does something else + // if TOS is None, just pops it and continues + // else error + instruction->qstr_opname = MP_QSTR_END_FINALLY; + break; + + case MP_BC_GET_ITER: + instruction->qstr_opname = MP_QSTR_GET_ITER; + break; + + case MP_BC_GET_ITER_STACK: + instruction->qstr_opname = MP_QSTR_GET_ITER_STACK; + break; + + case MP_BC_FOR_ITER: + DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward + instruction->qstr_opname = MP_QSTR_FOR_ITER; + instruction->arg = unum; + break; + + case MP_BC_BUILD_TUPLE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_TUPLE; + instruction->arg = unum; + break; + + case MP_BC_BUILD_LIST: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_LIST; + instruction->arg = unum; + break; + + case MP_BC_BUILD_MAP: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_MAP; + instruction->arg = unum; + break; + + case MP_BC_STORE_MAP: + instruction->qstr_opname = MP_QSTR_STORE_MAP; + break; + + case MP_BC_BUILD_SET: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_SET; + instruction->arg = unum; + break; + + #if MICROPY_PY_BUILTINS_SLICE + case MP_BC_BUILD_SLICE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_BUILD_SLICE; + instruction->arg = unum; + break; + #endif + + case MP_BC_STORE_COMP: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_STORE_COMP; + instruction->arg = unum; + break; + + case MP_BC_UNPACK_SEQUENCE: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_UNPACK_SEQUENCE; + instruction->arg = unum; + break; + + case MP_BC_UNPACK_EX: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_UNPACK_EX; + instruction->arg = unum; + break; + + case MP_BC_MAKE_FUNCTION: + DECODE_PTR; + instruction->qstr_opname = MP_QSTR_MAKE_FUNCTION; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + break; + + case MP_BC_MAKE_FUNCTION_DEFARGS: + DECODE_PTR; + instruction->qstr_opname = MP_QSTR_MAKE_FUNCTION_DEFARGS; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + break; + + case MP_BC_MAKE_CLOSURE: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + instruction->qstr_opname = MP_QSTR_MAKE_CLOSURE; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(n_closed_over); + break; + } + + case MP_BC_MAKE_CLOSURE_DEFARGS: { + DECODE_PTR; + mp_uint_t n_closed_over = *ip++; + instruction->qstr_opname = MP_QSTR_MAKE_CLOSURE_DEFARGS; + instruction->arg = unum; + instruction->argobj= mp_obj_new_int_from_ull((uint64_t)ptr); + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT(n_closed_over); + break; + } + + case MP_BC_CALL_FUNCTION: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_FUNCTION; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_FUNCTION_VAR_KW: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_FUNCTION_VAR_KW; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_METHOD; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_CALL_METHOD_VAR_KW: + DECODE_UINT; + instruction->qstr_opname = MP_QSTR_CALL_METHOD_VAR_KW; + instruction->arg = unum & 0xff; + instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff); + break; + + case MP_BC_RETURN_VALUE: + instruction->qstr_opname = MP_QSTR_RETURN_VALUE; + break; + + case MP_BC_RAISE_VARARGS: + unum = *ip++; + instruction->qstr_opname = MP_QSTR_RAISE_VARARGS; + instruction->arg = unum; + break; + + case MP_BC_YIELD_VALUE: + instruction->qstr_opname = MP_QSTR_YIELD_VALUE; + break; + + case MP_BC_YIELD_FROM: + instruction->qstr_opname = MP_QSTR_YIELD_FROM; + break; + + case MP_BC_IMPORT_NAME: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_IMPORT_NAME; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_IMPORT_FROM: + DECODE_QSTR; + instruction->qstr_opname = MP_QSTR_IMPORT_FROM; + instruction->arg = qst; + instruction->argobj= MP_OBJ_NEW_QSTR(qst); + break; + + case MP_BC_IMPORT_STAR: + instruction->qstr_opname = MP_QSTR_IMPORT_STAR; + break; + + default: + if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + 64) { + instruction->qstr_opname = MP_QSTR_LOAD_CONST_SMALL_INT; + instruction->arg = (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16; + } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + 16) { + instruction->qstr_opname = MP_QSTR_LOAD_FAST; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI; + } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { + instruction->qstr_opname = MP_QSTR_STORE_FAST; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI; + } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) { + instruction->qstr_opname = MP_QSTR_UNARY_OP; + instruction->arg = (mp_uint_t)ip[-1] - MP_BC_UNARY_OP_MULTI; + } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) { + mp_uint_t op = ip[-1] - MP_BC_BINARY_OP_MULTI; + instruction->qstr_opname = MP_QSTR_BINARY_OP; + instruction->arg = op; + } else { + mp_printf(&mp_plat_print, "code %p, opcode 0x%02x not implemented\n", ip-1, ip[-1]); + assert(0); + return ip; + } + break; + } + + return ip; +} + +void mp_prof_print_instr(const byte* ip, mp_code_state_t *code_state) { + mp_dis_instruction_t _instruction, *instruction = &_instruction; + mp_prof_opcode_decode(ip, code_state->fun_bc->rc->const_table, instruction); + const mp_raw_code_t *rc = code_state->fun_bc->rc; + const mp_bytecode_prelude_t *prelude = &rc->prelude; + + mp_uint_t offset = ip - prelude->opcodes; + mp_printf(&mp_plat_print, "instr"); + + /* long path */ if (1) { + mp_printf(&mp_plat_print, + "@0x%p:%q:%q+0x%04x:%d", + ip, + prelude->qstr_source_file, + prelude->qstr_block_name, + offset, + mp_prof_bytecode_lineno(rc, offset) + ); + } + + /* bytecode */ if (0) { + mp_printf(&mp_plat_print, " %02x %02x %02x %02x", ip[0], ip[1], ip[2], ip[3]); + } + + mp_printf(&mp_plat_print, " 0x%02x %q [%d]", *ip, instruction->qstr_opname, instruction->arg); + + if (instruction->argobj != mp_const_none) { + mp_printf(&mp_plat_print, " $"); + mp_obj_print_helper(&mp_plat_print, instruction->argobj, PRINT_REPR); + } + if (instruction->argobjex_cache != mp_const_none) { + mp_printf(&mp_plat_print, " #"); + mp_obj_print_helper(&mp_plat_print, instruction->argobjex_cache, PRINT_REPR); + } + + mp_printf(&mp_plat_print, "\n"); +} + +#endif // MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE + +#endif // MICROPY_PY_SYS_SETTRACE diff --git a/py/profile.h b/py/profile.h new file mode 100644 index 000000000..0293e262f --- /dev/null +++ b/py/profile.h @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) SatoshiLabs + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_PY_PROFILING_H +#define MICROPY_INCLUDED_PY_PROFILING_H + +#include "py/emitglue.h" + +#if MICROPY_PY_SYS_SETTRACE + +#define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing) + +typedef struct _mp_obj_code_t { + mp_obj_base_t base; + const mp_raw_code_t *rc; + mp_obj_dict_t *dict_locals; + mp_obj_t lnotab; +} mp_obj_code_t; + +typedef struct _mp_obj_frame_t { + mp_obj_base_t base; + const mp_code_state_t *code_state; + struct _mp_obj_frame_t *back; + mp_obj_t callback; + mp_obj_code_t *code; + mp_uint_t lasti; + mp_uint_t lineno; + bool trace_opcodes; +} mp_obj_frame_t; + +void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude); + +mp_obj_t mp_obj_new_code(const mp_raw_code_t *rc); +mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state); + +// This is the implementation for the sys.settrace +mp_obj_t mp_prof_settrace(mp_obj_t callback); + +mp_obj_t mp_prof_frame_enter(mp_code_state_t *code_state); +mp_obj_t mp_prof_frame_update(const mp_code_state_t *code_state); + +// For every VM instruction tick this function deduces events from the state +mp_obj_t mp_prof_instr_tick(mp_code_state_t *code_state, bool is_exception); + +// This section is for debugging the settrace feature itself, and is not intended +// to be included in production/release builds. +#define MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE 0 +#if MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE +void mp_prof_print_instr(const byte* ip, mp_code_state_t *code_state); +#define MP_PROF_INSTR_DEBUG_PRINT(current_ip) mp_prof_print_instr((current_ip), code_state) +#else +#define MP_PROF_INSTR_DEBUG_PRINT(current_ip) +#endif + +#endif // MICROPY_PY_SYS_SETTRACE +#endif // MICROPY_INCLUDED_PY_PROFILING_H diff --git a/py/py.mk b/py/py.mk index 2a7e29b4d..491492df7 100644 --- a/py/py.mk +++ b/py/py.mk @@ -13,8 +13,10 @@ ifneq ($(QSTR_AUTOGEN_DISABLE),1) QSTR_DEFS_COLLECTED = $(HEADER_BUILD)/qstrdefs.collected.h endif -# Any files listed by this variable will cause a full regeneration of qstrs +# Any files listed by these variables will cause a full regeneration of qstrs +# DEPENDENCIES: included in qstr processing; REQUIREMENTS: not included QSTR_GLOBAL_DEPENDENCIES += $(PY_SRC)/mpconfig.h mpconfigport.h +QSTR_GLOBAL_REQUIREMENTS += $(HEADER_BUILD)/mpversion.h # some code is performance bottleneck and compiled with other optimization options CSUPEROPT = -O3 @@ -42,9 +44,9 @@ SRC_MOD += $(subst $(TOP)/,,$(shell find $(LVGL_DIR)/src $(LVGL_GENERIC_DRV_DIR) # External modules written in C. ifneq ($(USER_C_MODULES),) -# pre-define USERMOD variables as expanded so that variables are immediate +# pre-define USERMOD variables as expanded so that variables are immediate # expanded as they're added to them -SRC_USERMOD := +SRC_USERMOD := CFLAGS_USERMOD := LDFLAGS_USERMOD := $(foreach module, $(wildcard $(USER_C_MODULES)/*/micropython.mk), \ @@ -127,6 +129,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ stackctrl.o \ argcheck.o \ warning.o \ + profile.o \ map.o \ obj.o \ objarray.o \ diff --git a/py/runtime.c b/py/runtime.c index 70d795719..ecbdff2ba 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -88,6 +88,9 @@ void mp_init(void) { #if MICROPY_ENABLE_COMPILER // optimization disabled by default MP_STATE_VM(mp_optimise_value) = 0; + #if MICROPY_EMIT_NATIVE + MP_STATE_VM(default_emit_opt) = MP_EMIT_OPT_NONE; + #endif #endif // init global module dict @@ -118,6 +121,16 @@ void mp_init(void) { MP_STATE_VM(vfs_mount_table) = NULL; #endif + #if MICROPY_PY_SYS_ATEXIT + MP_STATE_VM(sys_exitfunc) = mp_const_none; + #endif + + #if MICROPY_PY_SYS_SETTRACE + MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL; + MP_STATE_THREAD(prof_callback_is_executing) = false; + MP_STATE_THREAD(current_code_state) = NULL; + #endif + #if MICROPY_PY_THREAD_GIL mp_thread_mutex_init(&MP_STATE_VM(gil_mutex)); #endif @@ -1417,7 +1430,6 @@ void mp_import_all(mp_obj_t module) { #if MICROPY_ENABLE_COMPILER -// this is implemented in this file so it can optimise access to locals/globals mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals) { // save context mp_obj_dict_t *volatile old_globals = mp_globals_get(); @@ -1431,7 +1443,7 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i if (nlr_push(&nlr) == 0) { qstr source_name = lex->source_name; mp_parse_tree_t parse_tree = mp_parse(lex, parse_input_kind); - mp_obj_t module_fun = mp_compile(&parse_tree, source_name, MP_EMIT_OPT_NONE, false); + mp_obj_t module_fun = mp_compile(&parse_tree, source_name, false); mp_obj_t ret; if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { diff --git a/py/vm.c b/py/vm.c index 260a7f38b..63869d926 100644 --- a/py/vm.c +++ b/py/vm.c @@ -34,6 +34,7 @@ #include "py/runtime.h" #include "py/bc0.h" #include "py/bc.h" +#include "py/profile.h" #if 0 #define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(ip, 1, code_state->fun_bc->const_table); @@ -108,6 +109,72 @@ exc_sp--; /* pop back to previous exception handler */ \ CLEAR_SYS_EXC_INFO() /* just clear sys.exc_info(), not compliant, but it shouldn't be used in 1st place */ +#if MICROPY_PY_SYS_SETTRACE + +#define FRAME_SETUP() do { \ + assert(code_state != code_state->prev_state); \ + MP_STATE_THREAD(current_code_state) = code_state; \ + assert(code_state != code_state->prev_state); \ +} while(0) + +#define FRAME_ENTER() do { \ + assert(code_state != code_state->prev_state); \ + code_state->prev_state = MP_STATE_THREAD(current_code_state); \ + assert(code_state != code_state->prev_state); \ + if (!mp_prof_is_executing) { \ + mp_prof_frame_enter(code_state); \ + } \ +} while(0) + +#define FRAME_LEAVE() do { \ + assert(code_state != code_state->prev_state); \ + MP_STATE_THREAD(current_code_state) = code_state->prev_state; \ + assert(code_state != code_state->prev_state); \ +} while(0) + +#define FRAME_UPDATE() do { \ + assert(MP_STATE_THREAD(current_code_state) == code_state); \ + if (!mp_prof_is_executing) { \ + code_state->frame = MP_OBJ_TO_PTR(mp_prof_frame_update(code_state)); \ + } \ +} while(0) + +#define TRACE_TICK(current_ip, current_sp, is_exception) do { \ + assert(code_state != code_state->prev_state); \ + assert(MP_STATE_THREAD(current_code_state) == code_state); \ + if (!mp_prof_is_executing && code_state->frame && MP_STATE_THREAD(prof_trace_callback)) { \ + MP_PROF_INSTR_DEBUG_PRINT(code_state->ip); \ + } \ + if (!mp_prof_is_executing && code_state->frame && code_state->frame->callback) { \ + mp_prof_instr_tick(code_state, is_exception); \ + } \ +} while(0) + +#else // MICROPY_PY_SYS_SETTRACE +#define FRAME_SETUP() +#define FRAME_ENTER() +#define FRAME_LEAVE() +#define FRAME_UPDATE() +#define TRACE_TICK(current_ip, current_sp, is_exception) +#endif // MICROPY_PY_SYS_SETTRACE + +#if MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE +static inline mp_map_elem_t *mp_map_cached_lookup(mp_map_t *map, qstr qst, uint8_t *idx_cache) { + size_t idx = *idx_cache; + mp_obj_t key = MP_OBJ_NEW_QSTR(qst); + mp_map_elem_t *elem = NULL; + if (idx < map->alloc && map->table[idx].key == key) { + elem = &map->table[idx]; + } else { + elem = mp_map_lookup(map, key, MP_MAP_LOOKUP); + if (elem != NULL) { + *idx_cache = (elem - &map->table[0]) & 0xff; + } + } + return elem; +} +#endif + // fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc) // sp points to bottom of stack which grows up // returns: @@ -128,6 +195,7 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp #define DISPATCH() do { \ TRACE(ip); \ MARK_EXC_IP_GLOBAL(); \ + TRACE_TICK(ip, sp, false); \ goto *entry_table[*ip++]; \ } while (0) #define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check @@ -149,6 +217,13 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp #if MICROPY_STACKLESS run_code_state: ; #endif +FRAME_ENTER(); + +#if MICROPY_STACKLESS +run_code_state_from_return: ; +#endif +FRAME_SETUP(); + // Pointers which are constant for particular invocation of mp_execute_bytecode() mp_obj_t * /*const*/ fastn; mp_exc_stack_t * /*const*/ exc_stack; @@ -198,6 +273,7 @@ dispatch_loop: #else TRACE(ip); MARK_EXC_IP_GLOBAL(); + TRACE_TICK(ip, sp, false); switch (*ip++) { #endif @@ -274,19 +350,14 @@ dispatch_loop: ENTRY(MP_BC_LOAD_NAME): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_uint_t x = *ip; - if (x < mp_locals_get()->map.alloc && mp_locals_get()->map.table[x].key == key) { - PUSH(mp_locals_get()->map.table[x].value); + mp_map_elem_t *elem = mp_map_cached_lookup(&mp_locals_get()->map, qst, (uint8_t*)ip); + mp_obj_t obj; + if (elem != NULL) { + obj = elem->value; } else { - mp_map_elem_t *elem = mp_map_lookup(&mp_locals_get()->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = (elem - &mp_locals_get()->map.table[0]) & 0xff; - PUSH(elem->value); - } else { - PUSH(mp_load_name(MP_OBJ_QSTR_VALUE(key))); - } + obj = mp_load_name(qst); } + PUSH(obj); ip++; DISPATCH(); } @@ -303,19 +374,14 @@ dispatch_loop: ENTRY(MP_BC_LOAD_GLOBAL): { MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_uint_t x = *ip; - if (x < mp_globals_get()->map.alloc && mp_globals_get()->map.table[x].key == key) { - PUSH(mp_globals_get()->map.table[x].value); + mp_map_elem_t *elem = mp_map_cached_lookup(&mp_globals_get()->map, qst, (uint8_t*)ip); + mp_obj_t obj; + if (elem != NULL) { + obj = elem->value; } else { - mp_map_elem_t *elem = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = (elem - &mp_globals_get()->map.table[0]) & 0xff; - PUSH(elem->value); - } else { - PUSH(mp_load_global(MP_OBJ_QSTR_VALUE(key))); - } + obj = mp_load_global(qst); } + PUSH(obj); ip++; DISPATCH(); } @@ -323,6 +389,7 @@ dispatch_loop: #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_LOAD_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; SET_TOP(mp_load_attr(TOP(), qst)); @@ -330,30 +397,22 @@ dispatch_loop: } #else ENTRY(MP_BC_LOAD_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t top = TOP(); + mp_map_elem_t *elem = NULL; if (mp_obj_is_instance_type(mp_obj_get_type(top))) { mp_obj_instance_t *self = MP_OBJ_TO_PTR(top); - mp_uint_t x = *ip; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_map_elem_t *elem; - if (x < self->members.alloc && self->members.table[x].key == key) { - elem = &self->members.table[x]; - } else { - elem = mp_map_lookup(&self->members, key, MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = elem - &self->members.table[0]; - } else { - goto load_attr_cache_fail; - } - } - SET_TOP(elem->value); - ip++; - DISPATCH(); + elem = mp_map_cached_lookup(&self->members, qst, (uint8_t*)ip); } - load_attr_cache_fail: - SET_TOP(mp_load_attr(top, qst)); + mp_obj_t obj; + if (elem != NULL) { + obj = elem->value; + } else { + obj = mp_load_attr(top, qst); + } + SET_TOP(obj); ip++; DISPATCH(); } @@ -415,6 +474,7 @@ dispatch_loop: #if !MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE ENTRY(MP_BC_STORE_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_store_attr(sp[0], qst, sp[-1]); @@ -428,31 +488,20 @@ dispatch_loop: // consequence of this is that we can't use MP_MAP_LOOKUP_ADD_IF_NOT_FOUND // in the fast-path below, because that store could override a property. ENTRY(MP_BC_STORE_ATTR): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; + mp_map_elem_t *elem = NULL; mp_obj_t top = TOP(); if (mp_obj_is_instance_type(mp_obj_get_type(top)) && sp[-1] != MP_OBJ_NULL) { mp_obj_instance_t *self = MP_OBJ_TO_PTR(top); - mp_uint_t x = *ip; - mp_obj_t key = MP_OBJ_NEW_QSTR(qst); - mp_map_elem_t *elem; - if (x < self->members.alloc && self->members.table[x].key == key) { - elem = &self->members.table[x]; - } else { - elem = mp_map_lookup(&self->members, key, MP_MAP_LOOKUP); - if (elem != NULL) { - *(byte*)ip = elem - &self->members.table[0]; - } else { - goto store_attr_cache_fail; - } - } - elem->value = sp[-1]; - sp -= 2; - ip++; - DISPATCH(); + elem = mp_map_cached_lookup(&self->members, qst, (uint8_t*)ip); + } + if (elem != NULL) { + elem->value = sp[-1]; + } else { + mp_store_attr(sp[0], qst, sp[-1]); } - store_attr_cache_fail: - mp_store_attr(sp[0], qst, sp[-1]); sp -= 2; ip++; DISPATCH(); @@ -738,6 +787,7 @@ unwind_jump:; } ENTRY(MP_BC_FOR_ITER): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward code_state->sp = sp; @@ -753,6 +803,12 @@ unwind_jump:; ip += ulab; // jump to after for-block } else { PUSH(value); // push the next iteration value + #if MICROPY_PY_SYS_SETTRACE + // LINE event should trigger for every iteration so invalidate last trigger + if (code_state->frame) { + code_state->frame->lineno = 0; + } + #endif } DISPATCH(); } @@ -887,6 +943,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_FUNCTION): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -921,6 +978,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_FUNCTION_VAR_KW): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -966,6 +1024,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_METHOD): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -1004,6 +1063,7 @@ unwind_jump:; } ENTRY(MP_BC_CALL_METHOD_VAR_KW): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_UINT; // unum & 0xff == n_positional @@ -1096,9 +1156,10 @@ unwind_return: #endif code_state = new_code_state; *code_state->sp = res; - goto run_code_state; + goto run_code_state_from_return; } #endif + FRAME_LEAVE(); return MP_VM_RETURN_NORMAL; ENTRY(MP_BC_RAISE_VARARGS): { @@ -1136,6 +1197,7 @@ yield: code_state->ip = ip; code_state->sp = sp; code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, 0); + FRAME_LEAVE(); return MP_VM_RETURN_YIELD; ENTRY(MP_BC_YIELD_FROM): { @@ -1192,6 +1254,7 @@ yield: } ENTRY(MP_BC_IMPORT_NAME): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t obj = POP(); @@ -1200,6 +1263,7 @@ yield: } ENTRY(MP_BC_IMPORT_FROM): { + FRAME_UPDATE(); MARK_EXC_IP_SELECTIVE(); DECODE_QSTR; mp_obj_t obj = mp_import_from(TOP(), qst); @@ -1262,9 +1326,11 @@ yield: } else #endif { - mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NotImplementedError, "byte code not implemented"); + + mp_obj_t obj = mp_obj_new_exception_msg(&mp_type_NotImplementedError, "opcode"); nlr_pop(); code_state->state[0] = obj; + FRAME_LEAVE(); return MP_VM_RETURN_EXCEPTION; } @@ -1355,13 +1421,23 @@ exception_handler: } } + #if MICROPY_PY_SYS_SETTRACE + // Exceptions are traced here + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_Exception))) { + TRACE_TICK(code_state->ip, code_state->sp, true /* yes, it's an exception */); + } + #endif + #if MICROPY_STACKLESS unwind_loop: #endif - // set file and line number that the exception occurred at - // TODO: don't set traceback for exceptions re-raised by END_FINALLY. - // But consider how to handle nested exceptions. - if (nlr.ret_val != &mp_const_GeneratorExit_obj) { + // Set traceback info (file and line number) where the exception occurred, but not for: + // - constant GeneratorExit object, because it's const + // - exceptions re-raised by END_FINALLY + // - exceptions re-raised explicitly by "raise" + if (nlr.ret_val != &mp_const_GeneratorExit_obj + && *code_state->ip != MP_BC_END_FINALLY + && !(*code_state->ip == MP_BC_RAISE_VARARGS && code_state->ip[1] == 0)) { const byte *ip = code_state->fun_bc->bytecode; ip = mp_decode_uint_skip(ip); // skip n_state ip = mp_decode_uint_skip(ip); // skip n_exc_stack @@ -1383,29 +1459,7 @@ unwind_loop: qstr source_file = mp_decode_uint_value(ip); ip = mp_decode_uint_skip(ip); #endif - size_t source_line = 1; - size_t c; - while ((c = *ip)) { - size_t b, l; - if ((c & 0x80) == 0) { - // 0b0LLBBBBB encoding - b = c & 0x1f; - l = c >> 5; - ip += 1; - } else { - // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) - b = c & 0xf; - l = ((c << 4) & 0x700) | ip[1]; - ip += 2; - } - if (bc >= b) { - bc -= b; - source_line += l; - } else { - // found source line corresponding to bytecode offset - break; - } - } + size_t source_line = mp_bytecode_get_source_line(ip, bc); mp_obj_exception_add_traceback(MP_OBJ_FROM_PTR(nlr.ret_val), source_file, source_line, block_name); } @@ -1456,6 +1510,7 @@ unwind_loop: // propagate exception to higher level // Note: ip and sp don't have usable values at this point code_state->state[0] = MP_OBJ_FROM_PTR(nlr.ret_val); // put exception here because sp is invalid + FRAME_LEAVE(); return MP_VM_RETURN_EXCEPTION; } } diff --git a/tests/basics/bytearray_slice_assign.py b/tests/basics/bytearray_slice_assign.py index 7f7d1d119..fa7878e10 100644 --- a/tests/basics/bytearray_slice_assign.py +++ b/tests/basics/bytearray_slice_assign.py @@ -59,3 +59,10 @@ print(b) b = bytearray(2) b[1:1] = b"12345" print(b) + +# Growth of bytearray via slice extension +b = bytearray(b'12345678') +b.append(57) # expand and add a bit of unused space at end of the bytearray +for i in range(400): + b[-1:] = b'ab' # grow slowly into the unused space +print(len(b), b) diff --git a/tests/basics/struct_endian.py b/tests/basics/struct_endian.py new file mode 100644 index 000000000..91f5539c1 --- /dev/null +++ b/tests/basics/struct_endian.py @@ -0,0 +1,24 @@ +# test ustruct and endian specific things + +try: + import ustruct as struct +except: + try: + import struct + except ImportError: + print("SKIP") + raise SystemExit + +# unpack/unpack_from with unaligned native type +buf = b'0123456789' +print(struct.unpack('h', memoryview(buf)[1:3])) +print(struct.unpack_from('i', buf, 1)) +print(struct.unpack_from('@i', buf, 1)) +print(struct.unpack_from('@ii', buf, 1)) + +# pack_into with unaligned native type +buf = bytearray(b'>----<<<<<<<') +struct.pack_into('i', buf, 1, 0x30313233) +print(buf) +struct.pack_into('@ii', buf, 3, 0x34353637, 0x41424344) +print(buf) diff --git a/tests/basics/try_except_break.py b/tests/basics/try_except_break.py new file mode 100644 index 000000000..a7683f218 --- /dev/null +++ b/tests/basics/try_except_break.py @@ -0,0 +1,73 @@ +# test deep unwind via break from nested try-except (22 of them) +while True: + print(1) + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + try: + print(2) + break + print(3) + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass + except: + pass +print(4) diff --git a/tests/basics/try_except_break.py.exp b/tests/basics/try_except_break.py.exp new file mode 100644 index 000000000..e8a01cd98 --- /dev/null +++ b/tests/basics/try_except_break.py.exp @@ -0,0 +1,3 @@ +1 +2 +4 diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 8d36b89df..36f040438 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -417,6 +417,7 @@ arg names: (N_STATE 1) (N_EXC_STACK 0) bc=-1 line=1 +######## bc=13 line=149 00 LOAD_NAME __name__ (cache=0) 04 STORE_NAME __module__ @@ -449,6 +450,8 @@ arg names: * * * (N_STATE 9) (N_EXC_STACK 0) bc=-\\d\+ line=1 + bc=0 line=59 +######## 00 LOAD_NULL 01 LOAD_FAST 2 02 LOAD_NULL @@ -471,6 +474,8 @@ arg names: * * * (N_STATE 10) (N_EXC_STACK 0) bc=-\\d\+ line=1 + bc=0 line=60 +######## 00 BUILD_LIST 0 02 LOAD_FAST 2 03 GET_ITER_STACK diff --git a/tests/extmod/ujson_loads_bytes.py b/tests/extmod/ujson_loads_bytes.py new file mode 100644 index 000000000..507e4ca88 --- /dev/null +++ b/tests/extmod/ujson_loads_bytes.py @@ -0,0 +1,13 @@ +# test loading from bytes and bytearray (introduced in Python 3.6) + +try: + import ujson as json +except ImportError: + try: + import json + except ImportError: + print("SKIP") + raise SystemExit + +print(json.loads(b'[1,2]')) +print(json.loads(bytearray(b'[null]'))) diff --git a/tests/extmod/ujson_loads_bytes.py.exp b/tests/extmod/ujson_loads_bytes.py.exp new file mode 100644 index 000000000..c2735a990 --- /dev/null +++ b/tests/extmod/ujson_loads_bytes.py.exp @@ -0,0 +1,2 @@ +[1, 2] +[None] diff --git a/tests/extmod/ure_debug.py b/tests/extmod/ure_debug.py index cfb264bb6..621fc8d50 100644 --- a/tests/extmod/ure_debug.py +++ b/tests/extmod/ure_debug.py @@ -1,7 +1,8 @@ # test printing debugging info when compiling try: import ure -except ImportError: + ure.DEBUG +except (ImportError, AttributeError): print("SKIP") raise SystemExit diff --git a/tests/float/math_isclose.py b/tests/float/math_isclose.py new file mode 100644 index 000000000..13dfff75f --- /dev/null +++ b/tests/float/math_isclose.py @@ -0,0 +1,47 @@ +# test math.isclose (appeared in Python 3.5) + +try: + from math import isclose +except ImportError: + print("SKIP") + raise SystemExit + +def test(a, b, **kwargs): + print(isclose(a, b, **kwargs)) + +def test_combinations(a, b, **kwargs): + test(a, a, **kwargs) + test(a, b, **kwargs) + test(b, a, **kwargs) + test(b, b, **kwargs) + +# Special numbers +test_combinations(float('nan'), 1) +test_combinations(float('inf'), 1) +test_combinations(float('-inf'), 1) + +# Equality +test(1.0, 1.0, rel_tol=0.0, abs_tol=0.0) +test(2.35e-100, 2.35e-100, rel_tol=0.0, abs_tol=0.0) +test(2.1234e100, 2.1234e100, rel_tol=0.0, abs_tol=0.0) + +# Relative tolerance +test(1000.0, 1001.0, rel_tol=1e-3) +test(1000.0, 1001.0, rel_tol=1e-4) +test(1000, 1001, rel_tol=1e-3) +test(1000, 1001, rel_tol=1e-4) +test_combinations(0, 1, rel_tol=1.0) + +# Absolute tolerance +test(0.0, 1e-10, abs_tol=1e-10, rel_tol=0.1) +test(0.0, 1e-10, abs_tol=0.0, rel_tol=0.1) + +# Bad parameters +try: + isclose(0, 0, abs_tol=-1) +except ValueError: + print('ValueError') +try: + isclose(0, 0, rel_tol=-1) +except ValueError: + print('ValueError') diff --git a/tests/float/math_isclose.py.exp b/tests/float/math_isclose.py.exp new file mode 100644 index 000000000..02974666c --- /dev/null +++ b/tests/float/math_isclose.py.exp @@ -0,0 +1,27 @@ +False +False +False +True +True +False +False +True +True +False +False +True +True +True +True +True +False +True +False +True +True +True +True +True +False +ValueError +ValueError diff --git a/tests/micropython/opt_level.py b/tests/micropython/opt_level.py index 5a10047f0..4e2f2f4ea 100644 --- a/tests/micropython/opt_level.py +++ b/tests/micropython/opt_level.py @@ -12,8 +12,3 @@ exec('print(__debug__)') micropython.opt_level(1) exec('print(__debug__)') exec('assert 0') - -# check that level 3 doesn't store line numbers -# the expected output is that any line is printed as "line 1" -micropython.opt_level(3) -exec('try:\n xyz\nexcept NameError as er:\n import sys\n sys.print_exception(er)') diff --git a/tests/micropython/opt_level.py.exp b/tests/micropython/opt_level.py.exp index 6372f6c5d..74b3dd74e 100644 --- a/tests/micropython/opt_level.py.exp +++ b/tests/micropython/opt_level.py.exp @@ -2,6 +2,3 @@ 1 True False -Traceback (most recent call last): - File "", line 1, in -NameError: name 'xyz' isn't defined diff --git a/tests/micropython/opt_level_lineno.py b/tests/micropython/opt_level_lineno.py new file mode 100644 index 000000000..00e573960 --- /dev/null +++ b/tests/micropython/opt_level_lineno.py @@ -0,0 +1,6 @@ +import micropython as micropython + +# check that level 3 doesn't store line numbers +# the expected output is that any line is printed as "line 1" +micropython.opt_level(3) +exec('try:\n xyz\nexcept NameError as er:\n import sys\n sys.print_exception(er)') diff --git a/tests/micropython/opt_level_lineno.py.exp b/tests/micropython/opt_level_lineno.py.exp new file mode 100644 index 000000000..469b90ba7 --- /dev/null +++ b/tests/micropython/opt_level_lineno.py.exp @@ -0,0 +1,3 @@ +Traceback (most recent call last): + File "", line 1, in +NameError: name 'xyz' isn't defined diff --git a/tests/misc/print_exception.py b/tests/misc/print_exception.py index 95431632f..2067030bf 100644 --- a/tests/misc/print_exception.py +++ b/tests/misc/print_exception.py @@ -47,6 +47,28 @@ except Exception as e: print('caught') print_exc(e) +# Test that an exception propagated through a finally doesn't have a traceback added there +try: + try: + f() + finally: + print('finally') +except Exception as e: + print('caught') + print_exc(e) + +# Test that re-raising an exception doesn't add traceback info +try: + try: + f() + except Exception as e: + print('reraise') + print_exc(e) + raise +except Exception as e: + print('caught') + print_exc(e) + # Here we have a function with lots of bytecode generated for a single source-line, and # there is an error right at the end of the bytecode. It should report the correct line. def f(): diff --git a/tests/misc/sys_atexit.py b/tests/misc/sys_atexit.py new file mode 100644 index 000000000..f5317953c --- /dev/null +++ b/tests/misc/sys_atexit.py @@ -0,0 +1,18 @@ +# test sys.atexit() function + +import sys +try: + sys.atexit +except AttributeError: + print('SKIP') + raise SystemExit + +some_var = None + +def do_at_exit(): + print("done at exit:", some_var) + +sys.atexit(do_at_exit) + +some_var = "ok" +print("done before exit") diff --git a/tests/misc/sys_atexit.py.exp b/tests/misc/sys_atexit.py.exp new file mode 100644 index 000000000..3cbdae9a5 --- /dev/null +++ b/tests/misc/sys_atexit.py.exp @@ -0,0 +1,2 @@ +done before exit +done at exit: ok diff --git a/tests/misc/sys_settrace_features.py b/tests/misc/sys_settrace_features.py new file mode 100644 index 000000000..932e430de --- /dev/null +++ b/tests/misc/sys_settrace_features.py @@ -0,0 +1,91 @@ +import sys + +try: + sys.settrace +except AttributeError: + print("SKIP") + raise SystemExit + +def print_stacktrace(frame, level=0): + # Ignore CPython specific helpers. + if frame.f_globals['__name__'].find('importlib') != -1: + print_stacktrace(frame.f_back, level) + return + + print("%2d: %s@%s:%s => %s:%d" % ( + level, " ", + frame.f_globals['__name__'], + frame.f_code.co_name, + # reduce full path to some pseudo-relative + 'misc' + ''.join(frame.f_code.co_filename.split('tests/misc')[-1:]), + frame.f_lineno, + )) + + if frame.f_back: + print_stacktrace(frame.f_back, level + 1) + +class _Prof: + trace_count = 0; + + def trace_tick(self, frame, event, arg): + self.trace_count += 1 + print_stacktrace(frame) + +__prof__ = _Prof() + +alice_handler_set = False +def trace_tick_handler_alice(frame, event, arg): + print("### trace_handler::Alice event:", event) + __prof__.trace_tick(frame, event, arg) + return trace_tick_handler_alice + +bob_handler_set = False +def trace_tick_handler_bob(frame, event, arg): + print("### trace_handler::Bob event:", event) + __prof__.trace_tick(frame, event, arg) + return trace_tick_handler_bob + +def trace_tick_handler(frame, event, arg): + # Ignore CPython specific helpers. + if frame.f_globals['__name__'].find('importlib') != -1: + return + + print("### trace_handler::main event:", event) + __prof__.trace_tick(frame, event, arg) + + if frame.f_code.co_name != 'factorial': + return trace_tick_handler + + global alice_handler_set + if event == 'call' and not alice_handler_set: + alice_handler_set = True + return trace_tick_handler_alice + + global bob_handler_set + if event == 'call' and not bob_handler_set: + bob_handler_set = True + return trace_tick_handler_bob + + return trace_tick_handler + +def factorial(n): + if n == 0: + return 1 + else: + return n * factorial(n - 1) + +def do_tests(): + # These commands are here to demonstrate some execution being traced. + print("Who loves the sun?") + print("Not every-", factorial(3)) + + from sys_settrace_subdir import trace_generic + trace_generic.run_tests() + return + +sys.settrace(trace_tick_handler) +do_tests() +sys.settrace(None) + +print("\n------------------ script exited ------------------") +print("Total traces executed: ", __prof__.trace_count) diff --git a/tests/misc/sys_settrace_generator.py b/tests/misc/sys_settrace_generator.py new file mode 100644 index 000000000..e955d6b62 --- /dev/null +++ b/tests/misc/sys_settrace_generator.py @@ -0,0 +1,62 @@ +# test sys.settrace with generators + +import sys + +try: + sys.settrace +except AttributeError: + print("SKIP") + raise SystemExit + +def print_stacktrace(frame, level=0): + print("%2d: %s@%s:%s => %s:%d" % ( + level, " ", + frame.f_globals['__name__'], + frame.f_code.co_name, + # reduce full path to some pseudo-relative + 'misc' + ''.join(frame.f_code.co_filename.split('tests/misc')[-1:]), + frame.f_lineno, + )) + + if frame.f_back: + print_stacktrace(frame.f_back, level + 1) + +trace_count = 0 + +def trace_tick_handler(frame, event, arg): + global trace_count + print("### trace_handler::main event:", event) + trace_count += 1 + print_stacktrace(frame) + return trace_tick_handler + +def test_generator(): + def make_gen(): + yield 1<<0 + yield 1<<1 + yield 1<<2 + return 1<<3 + + gen = make_gen() + r = 0 + try: + + r += gen.send(None) + + while True: + + r += gen.send(None) + + except StopIteration as e: + print("test_generator", r, e) + + gen = make_gen() + r = 0 + for i in gen: + r += i + print(r) + +sys.settrace(trace_tick_handler) +test_generator() +sys.settrace(None) +print("Total traces executed: ", trace_count) diff --git a/tests/misc/sys_settrace_generator.py.exp b/tests/misc/sys_settrace_generator.py.exp new file mode 100644 index 000000000..5329cdfe7 --- /dev/null +++ b/tests/misc/sys_settrace_generator.py.exp @@ -0,0 +1,195 @@ +### trace_handler::main event: call + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:33 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:34 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:40 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:41 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:42 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:44 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:34 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:44 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:44 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:44 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:38 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:38 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: exception + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:50 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:51 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +test_generator 7 8 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:53 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:54 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:34 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:35 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:36 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: call + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:37 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:38 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: return + 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:38 + 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:55 + 2: @__main__: => miscmisc/sys_settrace_generator.py:60 +### trace_handler::main event: line + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:57 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +7 +### trace_handler::main event: return + 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:57 + 1: @__main__: => miscmisc/sys_settrace_generator.py:60 +Total traces executed: 54 diff --git a/tests/misc/sys_settrace_loop.py b/tests/misc/sys_settrace_loop.py new file mode 100644 index 000000000..9ae0f41a1 --- /dev/null +++ b/tests/misc/sys_settrace_loop.py @@ -0,0 +1,51 @@ +# test sys.settrace with while and for loops + +import sys + +try: + sys.settrace +except AttributeError: + print("SKIP") + raise SystemExit + +def print_stacktrace(frame, level=0): + print("%2d: %s@%s:%s => %s:%d" % ( + level, " ", + frame.f_globals['__name__'], + frame.f_code.co_name, + # reduce full path to some pseudo-relative + 'misc' + ''.join(frame.f_code.co_filename.split('tests/misc')[-1:]), + frame.f_lineno, + )) + + if frame.f_back: + print_stacktrace(frame.f_back, level + 1) + +trace_count = 0 + +def trace_tick_handler(frame, event, arg): + global trace_count + print("### trace_handler::main event:", event) + trace_count += 1 + print_stacktrace(frame) + return trace_tick_handler + +def test_loop(): + # for loop + r = 0 + for i in range(3): + r += i + print("test_for_loop", r) + + # while loop + r = 0 + i = 0 + while i < 3: + r += i + i += 1 + print("test_while_loop", i) + +sys.settrace(trace_tick_handler) +test_loop() +sys.settrace(None) +print("Total traces executed: ", trace_count) diff --git a/tests/misc/sys_settrace_loop.py.exp b/tests/misc/sys_settrace_loop.py.exp new file mode 100644 index 000000000..3d3da5b6f --- /dev/null +++ b/tests/misc/sys_settrace_loop.py.exp @@ -0,0 +1,72 @@ +### trace_handler::main event: call + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:33 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:35 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:36 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:37 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:36 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:37 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:36 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:37 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:36 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:37 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:38 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +test_for_loop 3 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:41 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:42 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:43 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:44 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:44 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:44 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +### trace_handler::main event: line + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:46 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +test_while_loop 3 +### trace_handler::main event: return + 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:46 + 1: @__main__: => miscmisc/sys_settrace_loop.py:49 +Total traces executed: 23 diff --git a/tests/misc/sys_settrace_subdir/trace_generic.py b/tests/misc/sys_settrace_subdir/trace_generic.py new file mode 100644 index 000000000..3239a019c --- /dev/null +++ b/tests/misc/sys_settrace_subdir/trace_generic.py @@ -0,0 +1,82 @@ +print("Now comes the language constructions tests.") + +# function +def test_func(): + def test_sub_func(): + print("test_function") + + test_sub_func() + +# closure +def test_closure(msg): + + def make_closure(): + print(msg) + + return make_closure + +# exception +def test_exception(): + try: + raise Exception("test_exception") + + except Exception: + pass + + finally: + pass + +# listcomp +def test_listcomp(): + print("test_listcomp", [x for x in range(3)]) + +# lambda +def test_lambda(): + func_obj_1 = lambda a, b: a + b + print(func_obj_1(10, 20)) + +# import +def test_import(): + from sys_settrace_subdir import trace_importme + trace_importme.dummy() + trace_importme.saysomething() + +# class +class TLClass(): + def method(): + pass + pass + +def test_class(): + class TestClass: + __anynum = -9 + def method(self): + print("test_class_method") + self.__anynum += 1 + + def prprty_getter(self): + return self.__anynum + + def prprty_setter(self, what): + self.__anynum = what + + prprty = property(prprty_getter, prprty_setter) + + cls = TestClass() + cls.method() + print("test_class_property", cls.prprty) + cls.prprty = 12 + print("test_class_property", cls.prprty) + + +def run_tests(): + test_func() + test_closure_inst = test_closure("test_closure") + test_closure_inst() + test_exception() + test_listcomp() + test_lambda() + test_class() + test_import() + +print("And it's done!") diff --git a/tests/misc/sys_settrace_subdir/trace_importme.py b/tests/misc/sys_settrace_subdir/trace_importme.py new file mode 100644 index 000000000..0ff7c6d5b --- /dev/null +++ b/tests/misc/sys_settrace_subdir/trace_importme.py @@ -0,0 +1,24 @@ +print("Yep, I got imported.") + +try: + x = const(1) +except NameError: + print('const not defined') + +const = lambda x: x + +_CNT01 = "CONST01" +_CNT02 = const(123) +A123 = const(123) +a123 = const(123) + +def dummy(): + return False + +def saysomething(): + print("There, I said it.") + +def neverexecuted(): + print("Never got here!") + +print("Yep, got here") diff --git a/tests/qemu-arm/native_test.py b/tests/qemu-arm/native_test.py new file mode 100644 index 000000000..0b58433d9 --- /dev/null +++ b/tests/qemu-arm/native_test.py @@ -0,0 +1,5 @@ +import native_frozen_align + +native_frozen_align.native_x(1) +native_frozen_align.native_y(2) +native_frozen_align.native_z(3) diff --git a/tests/qemu-arm/native_test.py.exp b/tests/qemu-arm/native_test.py.exp new file mode 100644 index 000000000..dcf37cd5e --- /dev/null +++ b/tests/qemu-arm/native_test.py.exp @@ -0,0 +1,3 @@ +2 +3 +4 diff --git a/tests/run-tests b/tests/run-tests index 9f74c1cfc..b22d06719 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -349,6 +349,8 @@ def run_tests(pyb, tests, args, base_path="."): for t in tests: if t.startswith('basics/io_'): skip_tests.add(t) + elif args.target == 'qemu-arm': + skip_tests.add('misc/print_exception.py') # requires sys stdfiles # Some tests are known to fail on 64-bit machines if pyb is None and platform.architecture()[0] == '64bit': @@ -375,6 +377,7 @@ def run_tests(pyb, tests, args, base_path="."): skip_tests.add('misc/sys_exc_info.py') # sys.exc_info() is not supported for native skip_tests.add('micropython/emg_exc.py') # because native doesn't have proper traceback info skip_tests.add('micropython/heapalloc_traceback.py') # because native doesn't have proper traceback info + skip_tests.add('micropython/opt_level_lineno.py') # native doesn't have proper traceback info skip_tests.add('micropython/schedule.py') # native code doesn't check pending events for test_file in tests: @@ -527,8 +530,9 @@ the last matching regex is used: cmd_parser.add_argument('files', nargs='*', help='input test files') args = cmd_parser.parse_args() + LOCAL_TARGETS = ('unix', 'qemu-arm',) EXTERNAL_TARGETS = ('pyboard', 'wipy', 'esp8266', 'esp32', 'minimal', 'nrf') - if args.target == 'unix' or args.list_tests: + if args.target in LOCAL_TARGETS or args.list_tests: pyb = None elif args.target in EXTERNAL_TARGETS: global pyboard @@ -537,24 +541,28 @@ the last matching regex is used: pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password) pyb.enter_raw_repl() else: - raise ValueError('target must be either %s or unix' % ", ".join(EXTERNAL_TARGETS)) + raise ValueError('target must be one of %s' % ", ".join(LOCAL_TARGETS + EXTERNAL_TARGETS)) if len(args.files) == 0: if args.test_dirs is None: + test_dirs = ('basics', 'micropython', 'misc', 'extmod',) if args.target == 'pyboard': # run pyboard tests - test_dirs = ('basics', 'micropython', 'float', 'misc', 'stress', 'extmod', 'pyb', 'pybnative', 'inlineasm') + test_dirs += ('float', 'stress', 'pyb', 'pybnative', 'inlineasm') elif args.target in ('esp8266', 'esp32', 'minimal', 'nrf'): - test_dirs = ('basics', 'micropython', 'float', 'misc', 'extmod') + test_dirs += ('float',) elif args.target == 'wipy': # run WiPy tests - test_dirs = ('basics', 'micropython', 'misc', 'extmod', 'wipy') - else: + test_dirs += ('wipy',) + elif args.target == 'unix': # run PC tests - test_dirs = ( - 'basics', 'micropython', 'float', 'import', 'io', 'misc', - 'stress', 'unicode', 'extmod', 'unix', 'cmdline', - ) + test_dirs += ('float', 'import', 'io', 'stress', 'unicode', 'unix', 'cmdline',) + elif args.target == 'qemu-arm': + if not args.write_exp: + raise ValueError('--target=qemu-arm must be used with --write-exp') + # Generate expected output files for qemu run. + # This list should match the test_dirs tuple in tinytest-codegen.py. + test_dirs += ('float', 'inlineasm', 'qemu-arm',) else: # run tests from these directories test_dirs = args.test_dirs diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index a9889c0e9..07fde6c09 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -28,11 +28,11 @@ RuntimeError: # repl ame__ -__class__ __name__ argv byteorder -exc_info exit getsizeof implementation -maxsize modules path platform -print_exception stderr stdin -stdout version version_info +__class__ __name__ argv atexit +byteorder exc_info exit getsizeof +implementation maxsize modules path +platform print_exception stderr +stdin stdout version version_info ementation # attrtuple (start=1, stop=2, step=3) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 648d56fe0..e159165f1 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -109,6 +109,7 @@ MP_OPCODE_VAR_UINT = 2 MP_OPCODE_OFFSET = 3 # extra bytes: +MP_BC_UNWIND_JUMP = 0x46 MP_BC_MAKE_CLOSURE = 0x62 MP_BC_MAKE_CLOSURE_DEFARGS = 0x63 MP_BC_RAISE_VARARGS = 0x5c @@ -215,7 +216,8 @@ def mp_opcode_format(bytecode, ip, count_var_uint, opcode_format=make_opcode_for ip += 3 else: extra_byte = ( - opcode == MP_BC_RAISE_VARARGS + opcode == MP_BC_UNWIND_JUMP + or opcode == MP_BC_RAISE_VARARGS or opcode == MP_BC_MAKE_CLOSURE or opcode == MP_BC_MAKE_CLOSURE_DEFARGS ) @@ -410,6 +412,23 @@ class RawCode(object): print(' .fun_data_len = %u,' % len(self.bytecode)) print(' .n_obj = %u,' % len(self.objs)) print(' .n_raw_code = %u,' % len(self.raw_codes)) + if self.code_kind == MP_CODE_BYTECODE: + print(' #if MICROPY_PY_SYS_SETTRACE') + print(' .prelude = {') + print(' .n_state = %u,' % self.prelude[0]) + print(' .n_exc_stack = %u,' % self.prelude[1]) + print(' .scope_flags = %u,' % self.prelude[2]) + print(' .n_pos_args = %u,' % self.prelude[3]) + print(' .n_kwonly_args = %u,' % self.prelude[4]) + print(' .n_def_pos_args = %u,' % self.prelude[5]) + print(' .qstr_block_name = %s,' % self.simple_name.qstr_id) + print(' .qstr_source_file = %s,' % self.source_file.qstr_id) + print(' .line_info = fun_data_%s + %u,' % (self.escaped_name, 0)) # TODO + print(' .locals = fun_data_%s + %u,' % (self.escaped_name, 0)) # TODO + print(' .opcodes = fun_data_%s + %u,' % (self.escaped_name, self.ip)) + print(' },') + print(' .line_of_definition = %u,' % 0) # TODO + print(' #endif') print(' #if MICROPY_EMIT_MACHINE_CODE') print(' .prelude_offset = %u,' % self.prelude_offset) print(' .n_qstr = %u,' % len(qstr_links)) @@ -471,6 +490,14 @@ class RawCodeNative(RawCode): else: self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",%progbits @ ")))' + # Allow single-byte alignment by default for x86/x64/xtensa, but on ARM we need halfword- or word- alignment. + if config.native_arch == MP_NATIVE_ARCH_ARMV6: + # ARMV6 -- four byte align. + self.fun_data_attributes += ' __attribute__ ((aligned (4)))' + elif MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP: + # ARMVxxM -- two byte align. + self.fun_data_attributes += ' __attribute__ ((aligned (2)))' + def _asm_thumb_rewrite_mov(self, pc, val): print(' (%u & 0xf0) | (%s >> 12),' % (self.bytecode[pc], val), end='') print(' (%u & 0xfb) | (%s >> 9 & 0x04),' % (self.bytecode[pc + 1], val), end='') diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index ad3b3bbec..7580522ee 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -54,7 +54,7 @@ testgroup_member = ( ## XXX: may be we could have `--without ` argument... # currently these tests are selected because they pass on qemu-arm -test_dirs = ('basics', 'micropython', 'float', 'extmod', 'inlineasm') # 'import', 'io', 'misc') +test_dirs = ('basics', 'micropython', 'misc', 'extmod', 'float', 'inlineasm', 'qemu-arm',) # 'import', 'io',) exclude_tests = ( # pattern matching in .exp 'basics/bytes_compare3.py', @@ -81,6 +81,12 @@ exclude_tests = ( 'micropython/heapalloc_traceback.py', # pattern matching in .exp 'micropython/meminfo.py', + # needs sys stdfiles + 'misc/print_exception.py', + # settrace .exp files are too large + 'misc/sys_settrace_loop.py', + 'misc/sys_settrace_generator.py', + 'misc/sys_settrace_features.py', ) output = []