From 487dbdb26748b86cf247600af187437310311145 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 1 Nov 2017 13:16:16 +1100 Subject: [PATCH 001/828] py/compile: Use alloca instead of qstr_build when compiling import name. The technique of using alloca is how dotted import names are composed in mp_import_from and mp_builtin___import__, so use the same technique in the compiler. This puts less pressure on the heap (only the stack is used if the qstr already exists, and if it doesn't exist then the standard qstr block memory is used for the new qstr rather than a separate chunk of the heap) and reduces overall code size. --- py/compile.c | 6 +++--- py/qstr.c | 23 ----------------------- py/qstr.h | 3 --- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/py/compile.c b/py/compile.c index 4e704abfb..ee017498a 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1050,8 +1050,8 @@ STATIC void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) { for (int i = 0; i < n; i++) { len += qstr_len(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])); } - byte *q_ptr; - byte *str_dest = qstr_build_start(len, &q_ptr); + char *q_ptr = alloca(len); + char *str_dest = q_ptr; for (int i = 0; i < n; i++) { if (i > 0) { *str_dest++ = '.'; @@ -1061,7 +1061,7 @@ STATIC void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) { memcpy(str_dest, str_src, str_src_len); str_dest += str_src_len; } - qstr q_full = qstr_build_end(q_ptr); + qstr q_full = qstr_from_strn(q_ptr, len); EMIT_ARG(import_name, q_full); if (is_as) { for (int i = 1; i < n; i++) { diff --git a/py/qstr.c b/py/qstr.c index 95c9b6835..a3c9612c6 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -243,29 +243,6 @@ qstr qstr_from_strn(const char *str, size_t len) { return q; } -byte *qstr_build_start(size_t len, byte **q_ptr) { - assert(len < (1 << (8 * MICROPY_QSTR_BYTES_IN_LEN))); - *q_ptr = m_new(byte, MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + len + 1); - Q_SET_LENGTH(*q_ptr, len); - return Q_GET_DATA(*q_ptr); -} - -qstr qstr_build_end(byte *q_ptr) { - QSTR_ENTER(); - qstr q = qstr_find_strn((const char*)Q_GET_DATA(q_ptr), Q_GET_LENGTH(q_ptr)); - if (q == 0) { - size_t len = Q_GET_LENGTH(q_ptr); - mp_uint_t hash = qstr_compute_hash(Q_GET_DATA(q_ptr), len); - Q_SET_HASH(q_ptr, hash); - q_ptr[MICROPY_QSTR_BYTES_IN_HASH + MICROPY_QSTR_BYTES_IN_LEN + len] = '\0'; - q = qstr_add(q_ptr); - } else { - m_del(byte, q_ptr, Q_GET_ALLOC(q_ptr)); - } - QSTR_EXIT(); - return q; -} - mp_uint_t qstr_hash(qstr q) { return Q_GET_HASH(find_qstr(q)); } diff --git a/py/qstr.h b/py/qstr.h index e2bdcc351..63fd0c369 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -65,9 +65,6 @@ qstr qstr_find_strn(const char *str, size_t str_len); // returns MP_QSTR_NULL if qstr qstr_from_str(const char *str); qstr qstr_from_strn(const char *str, size_t len); -byte *qstr_build_start(size_t len, byte **q_ptr); -qstr qstr_build_end(byte *q_ptr); - mp_uint_t qstr_hash(qstr q); const char *qstr_str(qstr q); size_t qstr_len(qstr q); From 58c785632fc7c4e16885eeb1440b8272069f5190 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 1 Nov 2017 08:59:42 +0200 Subject: [PATCH 002/828] docs/esp8266/general: TLS limitations: Mention also "ussl" module limitations. --- docs/esp8266/general.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/esp8266/general.rst b/docs/esp8266/general.rst index 96a454532..01eec5b48 100644 --- a/docs/esp8266/general.rst +++ b/docs/esp8266/general.rst @@ -185,3 +185,11 @@ limitation with usage of TLS on the low-memory devices: time, taking as a reference being able to access https://google.com . The smaller buffer hower means that some sites can't be accessed using it, and it's not possible to stream large amounts of data. + +There are also some not implemented features specifically in MicroPython's +``ussl`` module based on axTLS: + +6. Certificates are not validated (this may make connections susceptible + to man-in-the-middle attacks). +7. There is no support for client certificates (scheduled to be fixed in + 1.9.4 release). From 3a9b15fd79c1d1692d529b2b281e2862e9b31179 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 1 Nov 2017 15:16:01 +0200 Subject: [PATCH 003/828] zephyr/README: "make qemu" was replaced with "make run". --- ports/zephyr/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index 018b4ce71..6e1ea274b 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -46,7 +46,7 @@ Running To run the resulting firmware in QEMU (for BOARDs like qemu_x86, qemu_cortex_m3): - make qemu + make run With the default configuration, networking is now enabled, so you need to follow instructions in https://wiki.zephyrproject.org/view/Networking-with-Qemu @@ -110,4 +110,4 @@ To make a minimal build: To run a minimal build in QEMU without requiring TAP networking setup run the following after you built image with the previous command: - ./make-minimal BOARD= qemu + ./make-minimal BOARD= run From 0719c936fb036f0dd3107cd8bd262d80f95c3ca7 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 2 Nov 2017 00:14:11 +0200 Subject: [PATCH 004/828] extmod/modussl_axtls: socket_read: Handle EAGAIN. If SSL_EAGAIN is returned (which is a feature of MicroPython's axTLS fork), return EAGAIN. Original axTLS returns SSL_OK both when there's no data to return to user yet and when the underlying stream returns EAGAIN. That's not distinctive enough, for example, original module code works well for blocking stream, but will infinite-loop for non-blocking socket with EAGAIN. But if we fix non-blocking case, blocking calls to .read() will return few None's initially (while axTLS progresses thru handshake). Using SSL_EAGAIN allows to fix non-blocking case without regressing the blocking one. Note that this only handles case of non-blocking reads of application data. Initial handshake and writes still don't support non-blocking mode and must be done in the blocking way. --- extmod/modussl_axtls.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 3ad65ebf3..9c5722b12 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -121,6 +121,9 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc // EOF return 0; } + if (r == SSL_EAGAIN) { + r = MP_EAGAIN; + } *errcode = r; return MP_STREAM_ERROR; } From 1cf6d488b3e6b18b21f13f489ded35a47cf3b37d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 2 Nov 2017 00:16:03 +0200 Subject: [PATCH 005/828] extmod/modussl_axtls: Typo fix in comment. --- extmod/modussl_axtls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 9c5722b12..4d8440a89 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -113,7 +113,7 @@ STATIC mp_uint_t socket_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errc mp_int_t r = ssl_read(o->ssl_sock, &o->buf); if (r == SSL_OK) { // SSL_OK from ssl_read() means "everything is ok, but there's - // not user data yet. So, we just keep reading. + // no user data yet". So, we just keep reading. continue; } if (r < 0) { From 1742ab265392bde711f4debcd49a42c586540929 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 2 Nov 2017 00:38:58 +0200 Subject: [PATCH 006/828] docs/esp8266/general: Minor grammar fixes. --- docs/esp8266/general.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/esp8266/general.rst b/docs/esp8266/general.rst index 01eec5b48..08d8b4756 100644 --- a/docs/esp8266/general.rst +++ b/docs/esp8266/general.rst @@ -6,16 +6,16 @@ ESP8266 is a popular WiFi-enabled System-on-Chip (SoC) by Espressif Systems. Multitude of boards ------------------- -There are a multitude of modules and boards from different sources which carry +There is a multitude of modules and boards from different sources which carry the ESP8266 chip. MicroPython tries to provide a generic port which would run on as many boards/modules as possible, but there may be limitations. Adafruit Feather HUZZAH board is taken as a reference board for the port (for example, testing is performed on it). If you have another board, please make sure you -have datasheet, schematics and other reference materials for your board +have a datasheet, schematics and other reference materials for your board handy to look up various aspects of your board functioning. To make a generic ESP8266 port and support as many boards as possible, -following design and implementation decision were made: +the following design and implementation decision were made: * GPIO pin numbering is based on ESP8266 chip numbering, not some "logical" numbering of a particular board. Please have the manual/pin diagram of your board From ad5a6f591796c2dec6e91e9da147eaa7e9ee72a7 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 4 Nov 2017 00:26:31 +0200 Subject: [PATCH 007/828] docs/ure: Add flags arg to ure.compile(), mention that ure.DEBUG is optional. --- docs/library/ure.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/library/ure.rst b/docs/library/ure.rst index ebae1db5f..9a5a87fb6 100644 --- a/docs/library/ure.rst +++ b/docs/library/ure.rst @@ -47,7 +47,7 @@ etc. are not supported. Functions --------- -.. function:: compile(regex_str) +.. function:: compile(regex_str, [flags]) Compile regular expression, return `regex ` object. @@ -65,6 +65,7 @@ Functions .. data:: DEBUG Flag value, display debug information about compiled expression. + (Availability depends on `MicroPython port`.) .. _regex: From e766a4af4a336816d42d65c01ee6c3e4cc207b7e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 5 Nov 2017 00:27:21 +0200 Subject: [PATCH 008/828] esp8266/etshal.h: Make function prototypes compatible with ESP SDK 2.1.0+. In the vendor SDK 2.1.0, some of the functions which previously didn't have prototypes, finally acquired them. Change prototypes on our side to match those in vendor headers, to avoid warnings-as-errors. --- ports/esp8266/etshal.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ports/esp8266/etshal.h b/ports/esp8266/etshal.h index 34787779f..8d6457311 100644 --- a/ports/esp8266/etshal.h +++ b/ports/esp8266/etshal.h @@ -6,14 +6,14 @@ // see http://esp8266-re.foogod.com/wiki/Random_Number_Generator #define WDEV_HWRNG ((volatile uint32_t*)0x3ff20e44) -void ets_delay_us(); +void ets_delay_us(uint16_t us); void ets_intr_lock(void); void ets_intr_unlock(void); void ets_isr_mask(uint32_t mask); void ets_isr_unmask(uint32_t mask); void ets_isr_attach(int irq_no, void (*handler)(void *), void *arg); void ets_install_putc1(); -void uart_div_modify(); +void uart_div_modify(uint8_t uart, uint32_t divisor); void ets_set_idle_cb(void (*handler)(void *), void *arg); void ets_timer_arm_new(os_timer_t *tim, uint32_t millis, bool repeat, bool is_milli_timer); @@ -33,10 +33,10 @@ void MD5Update(MD5_CTX *context, const void *data, unsigned int len); void MD5Final(unsigned char digest[16], MD5_CTX *context); // These prototypes are for recent SDKs with "malloc tracking" -void *pvPortMalloc(unsigned sz, const char *fname, int line); -void *pvPortZalloc(unsigned sz, const char *fname, int line); -void *pvPortRealloc(void *p, unsigned sz, const char *fname, int line); -void vPortFree(void *p, const char *fname, int line); +void *pvPortMalloc(size_t sz, const char *fname, unsigned line); +void *pvPortZalloc(size_t sz, const char *fname, unsigned line); +void *pvPortRealloc(void *p, unsigned sz, const char *fname, unsigned line); +void vPortFree(void *p, const char *fname, unsigned line); uint32_t SPIRead(uint32_t offset, void *buf, uint32_t len); uint32_t SPIWrite(uint32_t offset, const void *buf, uint32_t len); From 99bf5448bd02f9b97f55025725e7d2d4ba9aced8 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 5 Nov 2017 11:37:05 +0200 Subject: [PATCH 009/828] axtls: Update, exposes AES functions to implement ECB chiper mode. --- lib/axtls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/axtls b/lib/axtls index dac9176ca..43a6e6bd3 160000 --- a/lib/axtls +++ b/lib/axtls @@ -1 +1 @@ -Subproject commit dac9176cac58cc5e49669a9a4d404a6f6dd7cc10 +Subproject commit 43a6e6bd3bbc03dc501e16b89fba0ef042ed3ea0 From cb910c6a0c5338737e5fb7f6f39be0600eb32b72 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 7 Nov 2017 00:43:21 +0200 Subject: [PATCH 010/828] unix/moduselect: Add .dump() method for debugging. Commented out by default. --- ports/unix/moduselect.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ports/unix/moduselect.c b/ports/unix/moduselect.c index ba1c195ef..45a5c321c 100644 --- a/ports/unix/moduselect.c +++ b/ports/unix/moduselect.c @@ -277,12 +277,30 @@ STATIC mp_obj_t poll_iternext(mp_obj_t self_in) { return MP_OBJ_STOP_ITERATION; } +STATIC mp_obj_t poll_dump(mp_obj_t self_in) { + mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in); + + struct pollfd *entries = self->entries; + for (int i = self->len - 1; i >= 0; i--) { + printf("fd: %d ev: %x rev: %x", entries->fd, entries->events, entries->revents); + if (self->obj_map) { + printf(" obj: %p", self->obj_map[entries - self->entries]); + } + printf("\n"); + entries++; + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(poll_dump_obj, poll_dump); + STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) }, { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) }, { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) }, { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) }, { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) }, +// { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&poll_dump_obj) }, }; STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); From b9580b85a86a1bae12e31b9734631243cd781014 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 7 Nov 2017 01:13:19 +0200 Subject: [PATCH 011/828] unix/moduselect: Fix nanbox build after adding .dump() method. --- ports/unix/moduselect.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/unix/moduselect.c b/ports/unix/moduselect.c index 45a5c321c..1ea7dc19a 100644 --- a/ports/unix/moduselect.c +++ b/ports/unix/moduselect.c @@ -40,6 +40,8 @@ #include "py/mphal.h" #include "fdfile.h" +#define DEBUG 0 + #if MICROPY_PY_SOCKET extern const mp_obj_type_t mp_type_socket; #endif @@ -277,6 +279,7 @@ STATIC mp_obj_t poll_iternext(mp_obj_t self_in) { return MP_OBJ_STOP_ITERATION; } +#if DEBUG STATIC mp_obj_t poll_dump(mp_obj_t self_in) { mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in); @@ -293,6 +296,7 @@ STATIC mp_obj_t poll_dump(mp_obj_t self_in) { return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_1(poll_dump_obj, poll_dump); +#endif STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) }, @@ -300,7 +304,9 @@ STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) }, { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) }, { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) }, -// { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&poll_dump_obj) }, + #if DEBUG + { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&poll_dump_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); From 5b1b80a8dbceb9be67dfd894cdf2a49a7426fd68 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 8 Nov 2017 00:24:39 +0200 Subject: [PATCH 012/828] docs/ure: Emphasize not supported features more. Plus, additional descriptions/formatting. --- docs/library/ure.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/library/ure.rst b/docs/library/ure.rst index 9a5a87fb6..f54614f04 100644 --- a/docs/library/ure.rst +++ b/docs/library/ure.rst @@ -15,8 +15,9 @@ Supported operators are: ``'.'`` Match any character. -``'[]'`` - Match set of characters. Individual characters and ranges are supported. +``'[...]'`` + Match set of characters. Individual characters and ranges are supported, + including negated sets (e.g. ``[^a-c]``). ``'^'`` @@ -36,12 +37,13 @@ Supported operators are: ``'|'`` -``'()'`` +``'(...)'`` Grouping. Each group is capturing (a substring it captures can be accessed with `match.group()` method). -Counted repetitions (``{m,n}``), more advanced assertions, named groups, -etc. are not supported. +**NOT SUPPORTED**: Counted repetitions (``{m,n}``), more advanced assertions +(``\b``, ``\B``), named groups (``(?P...)``), non-capturing groups +(``(?:...)``), etc. Functions From 1b146e9de97708b70c3ad96022886e2ce44d246d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 8 Nov 2017 19:45:18 +0200 Subject: [PATCH 013/828] py/mpconfig: Introduce reusable MP_HTOBE32(), etc. macros. Macros to convert big-endian values to host byte order and vice-versa. These were defined in adhoc way for some ports (e.g. esp8266), allow reuse, provide default implementations, while allow ports to override. --- ports/esp8266/posix_helpers.c | 5 ++--- py/mpconfig.h | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ports/esp8266/posix_helpers.c b/ports/esp8266/posix_helpers.c index 1fc677c5c..b72c4ff9d 100644 --- a/ports/esp8266/posix_helpers.c +++ b/ports/esp8266/posix_helpers.c @@ -55,14 +55,13 @@ void *realloc(void *ptr, size_t size) { return p; } -#define PLATFORM_HTONL(_n) ((uint32_t)( (((_n) & 0xff) << 24) | (((_n) & 0xff00) << 8) | (((_n) >> 8) & 0xff00) | (((_n) >> 24) & 0xff) )) #undef htonl #undef ntohl uint32_t ntohl(uint32_t netlong) { - return PLATFORM_HTONL(netlong); + return MP_BE32TOH(netlong); } uint32_t htonl(uint32_t netlong) { - return PLATFORM_HTONL(netlong); + return MP_HTOBE32(netlong); } time_t time(time_t *t) { diff --git a/py/mpconfig.h b/py/mpconfig.h index 6a32ea2a6..fa1094bfc 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1293,4 +1293,24 @@ typedef double mp_float_t; #define MP_UNLIKELY(x) __builtin_expect((x), 0) #endif +#ifndef MP_HTOBE16 +#if MP_ENDIANNESS_LITTLE +# define MP_HTOBE16(x) ((uint16_t)( (((x) & 0xff) << 8) | (((x) >> 8) & 0xff) )) +# define MP_BE16TOH(x) MP_HTOBE16(x) +#else +# define MP_HTOBE16(x) (x) +# define MP_BE16TOH(x) (x) +#endif +#endif + +#ifndef MP_HTOBE32 +#if MP_ENDIANNESS_LITTLE +# define MP_HTOBE32(x) ((uint32_t)( (((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) >> 8) & 0xff00) | (((x) >> 24) & 0xff) )) +# define MP_BE32TOH(x) MP_HTOBE32(x) +#else +# define MP_HTOBE32(x) (x) +# define MP_BE32TOH(x) (x) +#endif +#endif + #endif // MICROPY_INCLUDED_PY_MPCONFIG_H From 579b86451dba202d573c4c22790ebe3d8ddac060 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 10 Nov 2017 00:09:43 +0200 Subject: [PATCH 014/828] docs/_thread: Add a placeholder docs for _thread module. Doesn't list specific API calls yet, the purpose is to let user know that the module exists. --- docs/library/_thread.rst | 12 ++++++++++++ docs/library/index.rst | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 docs/library/_thread.rst diff --git a/docs/library/_thread.rst b/docs/library/_thread.rst new file mode 100644 index 000000000..47c1c2392 --- /dev/null +++ b/docs/library/_thread.rst @@ -0,0 +1,12 @@ +:mod:`_thread` -- multithreading support +======================================== + +.. module:: _thread + :synopsis: multithreading support + +|see_cpython_module| :mod:`python:_thread`. + +This module implements multithreading support. + +This module is highly experimental and its API is not yet fully settled +and not yet described in this documentation. diff --git a/docs/library/index.rst b/docs/library/index.rst index 0789ea43d..b141abf1e 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -88,6 +88,7 @@ it will fallback to loading the built-in ``ujson`` module. ustruct.rst utime.rst uzlib.rst + _thread.rst .. only:: port_pyboard @@ -114,6 +115,7 @@ it will fallback to loading the built-in ``ujson`` module. ustruct.rst utime.rst uzlib.rst + _thread.rst .. only:: port_wipy From cada971113e6db0cf9e0751e95dbe9217dd707b5 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 11 Nov 2017 00:11:24 +0200 Subject: [PATCH 015/828] py/objtype: mp_obj_new_type: Name base types related vars more clearly. As vars contains array of base types and its length, name them as such, avoid generic "items" and "len" names. --- py/objtype.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/py/objtype.c b/py/objtype.c index 6e2ab6c9a..a8376a306 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -983,12 +983,12 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) // TODO might need to make a copy of locals_dict; at least that's how CPython does it // Basic validation of base classes - size_t len; - mp_obj_t *items; - mp_obj_tuple_get(bases_tuple, &len, &items); - for (size_t i = 0; i < len; i++) { - assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type)); - mp_obj_type_t *t = MP_OBJ_TO_PTR(items[i]); + size_t bases_len; + mp_obj_t *bases_items; + mp_obj_tuple_get(bases_tuple, &bases_len, &bases_items); + for (size_t i = 0; i < bases_len; i++) { + assert(MP_OBJ_IS_TYPE(bases_items[i], &mp_type_type)); + mp_obj_type_t *t = MP_OBJ_TO_PTR(bases_items[i]); // TODO: Verify with CPy, tested on function type if (t->make_new == NULL) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { @@ -1014,17 +1014,17 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) //o->iternext = ; not implemented o->buffer_p.get_buffer = instance_get_buffer; - if (len > 0) { + if (bases_len > 0) { // Inherit protocol from a base class. This allows to define an // abstract base class which would translate C-level protocol to // Python method calls, and any subclass inheriting from it will // support this feature. - o->protocol = ((mp_obj_type_t*)MP_OBJ_TO_PTR(items[0]))->protocol; + o->protocol = ((mp_obj_type_t*)MP_OBJ_TO_PTR(bases_items[0]))->protocol; - if (len >= 2) { + if (bases_len >= 2) { o->parent = MP_OBJ_TO_PTR(bases_tuple); } else { - o->parent = MP_OBJ_TO_PTR(items[0]); + o->parent = MP_OBJ_TO_PTR(bases_items[0]); } } From 79ed58f87b008ed1ad75c611686ca8bebfe2a817 Mon Sep 17 00:00:00 2001 From: stijn Date: Mon, 6 Nov 2017 12:21:53 +0100 Subject: [PATCH 016/828] py/objnamedtuple: Add _asdict function if OrderedDict is supported --- ports/unix/mpconfigport_coverage.h | 1 + py/mpconfig.h | 5 +++++ py/objnamedtuple.c | 24 ++++++++++++++++++++++++ tests/basics/namedtuple_asdict.py | 20 ++++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/basics/namedtuple_asdict.py diff --git a/ports/unix/mpconfigport_coverage.h b/ports/unix/mpconfigport_coverage.h index 367b4853a..0dcfdd5fd 100644 --- a/ports/unix/mpconfigport_coverage.h +++ b/ports/unix/mpconfigport_coverage.h @@ -44,3 +44,4 @@ #undef MICROPY_VFS_FAT #define MICROPY_VFS_FAT (1) #define MICROPY_PY_FRAMEBUF (1) +#define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (1) diff --git a/py/mpconfig.h b/py/mpconfig.h index fa1094bfc..4209b32c6 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -894,6 +894,11 @@ typedef double mp_float_t; #define MICROPY_PY_COLLECTIONS_ORDEREDDICT (0) #endif +// Whether to provide the _asdict function for namedtuple +#ifndef MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT +#define MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT (0) +#endif + // Whether to provide "math" module #ifndef MICROPY_PY_MATH #define MICROPY_PY_MATH (1) diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index 38daccdf2..94f02dd69 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -52,6 +52,23 @@ STATIC size_t namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr n return (size_t)-1; } +#if MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT +STATIC mp_obj_t namedtuple_asdict(mp_obj_t self_in) { + mp_obj_namedtuple_t *self = MP_OBJ_TO_PTR(self_in); + const qstr *fields = ((mp_obj_namedtuple_type_t*)self->tuple.base.type)->fields; + mp_obj_t dict = mp_obj_new_dict(self->tuple.len); + //make it an OrderedDict + mp_obj_dict_t *dictObj = MP_OBJ_TO_PTR(dict); + dictObj->base.type = &mp_type_ordereddict; + dictObj->map.is_ordered = 1; + for (size_t i = 0; i < self->tuple.len; ++i) { + mp_obj_dict_store(dict, MP_OBJ_NEW_QSTR(fields[i]), self->tuple.items[i]); + } + return dict; +} +MP_DEFINE_CONST_FUN_OBJ_1(namedtuple_asdict_obj, namedtuple_asdict); +#endif + STATIC void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_namedtuple_t *o = MP_OBJ_TO_PTR(o_in); @@ -64,6 +81,13 @@ STATIC void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] == MP_OBJ_NULL) { // load attribute mp_obj_namedtuple_t *self = MP_OBJ_TO_PTR(self_in); + #if MICROPY_PY_COLLECTIONS_NAMEDTUPLE__ASDICT + if (attr == MP_QSTR__asdict) { + dest[0] = MP_OBJ_FROM_PTR(&namedtuple_asdict_obj); + dest[1] = self_in; + return; + } + #endif size_t id = namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr); if (id == (size_t)-1) { return; diff --git a/tests/basics/namedtuple_asdict.py b/tests/basics/namedtuple_asdict.py new file mode 100644 index 000000000..c5681376f --- /dev/null +++ b/tests/basics/namedtuple_asdict.py @@ -0,0 +1,20 @@ +try: + try: + from collections import namedtuple + except ImportError: + from ucollections import namedtuple +except ImportError: + print("SKIP") + raise SystemExit + +t = namedtuple("Tup", ["baz", "foo", "bar"])(3, 2, 5) + +try: + t._asdict +except AttributeError: + print("SKIP") + raise SystemExit + +d = t._asdict() +print(list(d.keys())) +print(list(d.values())) From 9c209e4d09424e6a180276e1fcdddf8f640e8804 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 12 Nov 2017 18:29:27 +0200 Subject: [PATCH 017/828] esp8266/README: Emphasize the need to change default WiFi password. --- ports/esp8266/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ports/esp8266/README.md b/ports/esp8266/README.md index 252e195d8..04a6039d0 100644 --- a/ports/esp8266/README.md +++ b/ports/esp8266/README.md @@ -92,12 +92,16 @@ of FlashROM. First start ----------- +Be sure to change ESP8266's WiFi access point password ASAP, see below. + __Serial prompt__ You can access the REPL (Python prompt) over UART (the same as used for programming). - Baudrate: 115200 +Run `help()` for some basic information. + __WiFi__ Initially, the device configures itself as a WiFi access point (AP). @@ -105,14 +109,15 @@ Initially, the device configures itself as a WiFi access point (AP). - Password: micropythoN (note the upper-case N). - IP address of the board: 192.168.4.1. - DHCP-server is activated. +- Please be sure to change the password to something non-guessable + immediately. `help()` gives information how. __WebREPL__ Python prompt over WiFi, connecting through a browser. - Hosted at http://micropython.org/webrepl. - GitHub repository https://github.com/micropython/webrepl. - -Please follow the instructions there. + Please follow the instructions there. Documentation ------------- From 0535d0337042e2c33352aa58ef3f685c0124acab Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 12 Nov 2017 18:34:18 +0200 Subject: [PATCH 018/828] esp8266/README: Add section on using upip. --- ports/esp8266/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ports/esp8266/README.md b/ports/esp8266/README.md index 04a6039d0..99e256016 100644 --- a/ports/esp8266/README.md +++ b/ports/esp8266/README.md @@ -119,6 +119,23 @@ Python prompt over WiFi, connecting through a browser. - GitHub repository https://github.com/micropython/webrepl. Please follow the instructions there. +__upip__ + +The ESP8266 port comes with builtin `upip` package manager, which can +be used to install additional modules (see the main README for more +information): + +``` +>>> import upip +>>> upip.install("micropython-pystone_lowmem") +[...] +>>> import pystone_lowmem +>>> pystone_lowmem.main() +``` + +Downloading and installing packages may requite a lot of free memory, +if you get an error, retry immediately after the hard reset. + Documentation ------------- From 7413b3ce3e344a6a916cd55508dfda8f009df598 Mon Sep 17 00:00:00 2001 From: Christopher Cooper Date: Fri, 10 Nov 2017 21:57:34 +0000 Subject: [PATCH 019/828] extmod/moduhashlib: Enable SHA1 hashing when using "mbedtls" library. The SHA1 hashing functionality is provided via the "axtls" library's implementation, and hence is unavailable when the "axtls" library is not being used. This change provides the same SHA1 hashing functionality when using the "mbedtls" library by using its implementation instead. --- extmod/moduhashlib.c | 55 +++++++++++++++++++++++++++++++++++++++ ports/unix/mpconfigport.h | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/extmod/moduhashlib.c b/extmod/moduhashlib.c index 3fad69247..6469dcfa3 100644 --- a/extmod/moduhashlib.c +++ b/extmod/moduhashlib.c @@ -32,10 +32,19 @@ #if MICROPY_PY_UHASHLIB #include "crypto-algorithms/sha256.h" + #if MICROPY_PY_UHASHLIB_SHA1 + +#if MICROPY_SSL_AXTLS #include "lib/axtls/crypto/crypto.h" #endif +#if MICROPY_SSL_MBEDTLS +#include "mbedtls/sha1.h" +#endif + +#endif + typedef struct _mp_obj_hash_t { mp_obj_base_t base; char state[0]; @@ -57,6 +66,7 @@ STATIC mp_obj_t hash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n #if MICROPY_PY_UHASHLIB_SHA1 STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg); +#if MICROPY_SSL_AXTLS STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, 1, false); mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(SHA1_CTX)); @@ -69,6 +79,22 @@ STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n } #endif +#if MICROPY_SSL_MBEDTLS +STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(mbedtls_sha1_context)); + o->base.type = type; + mbedtls_sha1_init((mbedtls_sha1_context*)o->state); + mbedtls_sha1_starts((mbedtls_sha1_context*)o->state); + if (n_args == 1) { + sha1_update(MP_OBJ_FROM_PTR(o), args[0]); + } + return MP_OBJ_FROM_PTR(o); +} +#endif + +#endif + STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; @@ -79,6 +105,8 @@ STATIC mp_obj_t hash_update(mp_obj_t self_in, mp_obj_t arg) { MP_DEFINE_CONST_FUN_OBJ_2(hash_update_obj, hash_update); #if MICROPY_PY_UHASHLIB_SHA1 + +#if MICROPY_SSL_AXTLS STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); mp_buffer_info_t bufinfo; @@ -86,6 +114,18 @@ STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) { SHA1_Update((SHA1_CTX*)self->state, bufinfo.buf, bufinfo.len); return mp_const_none; } +#endif + +#if MICROPY_SSL_MBEDTLS +STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); + mbedtls_sha1_update((mbedtls_sha1_context*)self->state, bufinfo.buf, bufinfo.len); + return mp_const_none; +} +#endif + MP_DEFINE_CONST_FUN_OBJ_2(sha1_update_obj, sha1_update); #endif @@ -99,6 +139,8 @@ STATIC mp_obj_t hash_digest(mp_obj_t self_in) { MP_DEFINE_CONST_FUN_OBJ_1(hash_digest_obj, hash_digest); #if MICROPY_PY_UHASHLIB_SHA1 + +#if MICROPY_SSL_AXTLS STATIC mp_obj_t sha1_digest(mp_obj_t self_in) { mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); vstr_t vstr; @@ -106,6 +148,19 @@ STATIC mp_obj_t sha1_digest(mp_obj_t self_in) { SHA1_Final((byte*)vstr.buf, (SHA1_CTX*)self->state); return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } +#endif + +#if MICROPY_SSL_MBEDTLS +STATIC mp_obj_t sha1_digest(mp_obj_t self_in) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + vstr_t vstr; + vstr_init_len(&vstr, 20); + mbedtls_sha1_finish((mbedtls_sha1_context*)self->state, (byte*)vstr.buf); + mbedtls_sha1_free((mbedtls_sha1_context*)self->state); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +#endif + MP_DEFINE_CONST_FUN_OBJ_1(sha1_digest_obj, sha1_digest); #endif diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index db382e0a7..a3d2bb7db 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -121,7 +121,7 @@ #define MICROPY_PY_UHEAPQ (1) #define MICROPY_PY_UTIMEQ (1) #define MICROPY_PY_UHASHLIB (1) -#if MICROPY_PY_USSL && MICROPY_SSL_AXTLS +#if MICROPY_PY_USSL #define MICROPY_PY_UHASHLIB_SHA1 (1) #endif #define MICROPY_PY_UBINASCII (1) From 964bf935a39498e7224657787b0be277279d2bae Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 14 Nov 2017 09:24:33 +0200 Subject: [PATCH 020/828] esp8266/esp8266_common.ld: Put .text of more libs into .irom0.text . Recent vendor SDKs ship libs with code in .text section, which previously was going into .irom0.text. Adjust the linker script to route these sections back to iROM (follows upstream change). --- ports/esp8266/esp8266_common.ld | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ports/esp8266/esp8266_common.ld b/ports/esp8266/esp8266_common.ld index de5268c8f..08da02869 100644 --- a/ports/esp8266/esp8266_common.ld +++ b/ports/esp8266/esp8266_common.ld @@ -73,6 +73,17 @@ SECTIONS _irom0_text_start = ABSOLUTE(.); *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) + /* Vendor SDK in v2.1.0-7-gb8fd588 started to build these with + -ffunction-sections -fdata-sections, and require routing to + irom via linker: + https://github.com/espressif/ESP8266_NONOS_SDK/commit/b8fd588a33f0319dc135523b51655e97b483b205 + */ + + *libcrypto.a:(.literal.* .text.*) + *libnet80211.a:(.literal.* .text.*) + *libwpa.a:(.literal.* .text.*) + *libwpa2.a:(.literal.* .text.*) + /* we put some specific text in this section */ *py/argcheck.o*(.literal* .text*) From 564a95cb040fc425b08a9d3696700364185c3e23 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Nov 2017 11:46:49 +1100 Subject: [PATCH 021/828] py/emitnative: Clean up asm macro names so they have dest as first arg. All the asm macro names that convert a particular architecture to a generic interface now follow the convention whereby the "destination" (usually a register) is specified first. --- py/asmarm.h | 15 +++---- py/asmthumb.h | 15 +++---- py/asmx64.h | 15 +++---- py/asmx86.h | 15 +++---- py/asmxtensa.h | 15 +++---- py/emitnative.c | 105 ++++++++++++++++++++++++++---------------------- 6 files changed, 81 insertions(+), 99 deletions(-) diff --git a/py/asmarm.h b/py/asmarm.h index a302b1590..871e35820 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -167,17 +167,12 @@ void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp); } while (0) #define ASM_CALL_IND(as, ptr, idx) asm_arm_bl_ind(as, ptr, idx, ASM_ARM_REG_R3) -#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_arm_mov_local_reg(as, (local_num), (reg)) -#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm)) -#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm)) -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_arm_mov_reg_i32(as, (reg_temp), (imm)); \ - asm_arm_mov_local_reg(as, (local_num), (reg_temp)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_arm_mov_reg_local(as, (reg), (local_num)) +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_arm_mov_local_reg((as), (local_num), (reg_src)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm)) +#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_arm_mov_reg_local((as), (reg_dest), (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_arm_mov_reg_local_addr(as, (reg), (local_num)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_arm_mov_reg_local_addr((as), (reg_dest), (local_num)) #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift)) #define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift)) diff --git a/py/asmthumb.h b/py/asmthumb.h index 7070e03ac..552ad75fc 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -283,17 +283,12 @@ void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp } while (0) #define ASM_CALL_IND(as, ptr, idx) asm_thumb_bl_ind(as, ptr, idx, ASM_THUMB_REG_R3) -#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_thumb_mov_local_reg(as, (local_num), (reg)) -#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_optimised(as, (reg), (imm)) -#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_aligned(as, (reg), (imm)) -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_thumb_mov_reg_i32_optimised(as, (reg_temp), (imm)); \ - asm_thumb_mov_local_reg(as, (local_num), (reg_temp)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local(as, (reg), (local_num)) +#define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm)) +#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_aligned((as), (reg_dest), (imm)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local_addr(as, (reg), (local_num)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_thumb_mov_reg_local_addr((as), (reg_dest), (local_num)) #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift)) #define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift)) diff --git a/py/asmx64.h b/py/asmx64.h index 425bdf2d3..2fbbfa9ff 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -162,17 +162,12 @@ void asm_x64_call_ind(asm_x64_t* as, void* ptr, int temp_r32); } while (0) #define ASM_CALL_IND(as, ptr, idx) asm_x64_call_ind(as, ptr, ASM_X64_REG_RAX) -#define ASM_MOV_REG_TO_LOCAL asm_x64_mov_r64_to_local -#define ASM_MOV_IMM_TO_REG asm_x64_mov_i64_to_r64_optimised -#define ASM_MOV_ALIGNED_IMM_TO_REG asm_x64_mov_i64_to_r64_aligned -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_x64_mov_i64_to_r64_optimised(as, (imm), (reg_temp)); \ - asm_x64_mov_r64_to_local(as, (reg_temp), (local_num)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG asm_x64_mov_local_to_r64 +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x64_mov_r64_to_local((as), (reg_src), (local_num)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_optimised((as), (imm), (reg_dest)) +#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_aligned((as), (imm), (reg_dest)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x64_mov_local_to_r64((as), (local_num), (reg_dest)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG asm_x64_mov_local_addr_to_r64 +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x64_mov_local_addr_to_r64((as), (local_num), (reg_dest)) #define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg)) #define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg)) diff --git a/py/asmx86.h b/py/asmx86.h index 0a00e2e7c..bd5895453 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -160,17 +160,12 @@ void asm_x86_call_ind(asm_x86_t* as, void* ptr, mp_uint_t n_args, int temp_r32); } while (0) #define ASM_CALL_IND(as, ptr, idx) asm_x86_call_ind(as, ptr, mp_f_n_args[idx], ASM_X86_REG_EAX) -#define ASM_MOV_REG_TO_LOCAL asm_x86_mov_r32_to_local -#define ASM_MOV_IMM_TO_REG asm_x86_mov_i32_to_r32 -#define ASM_MOV_ALIGNED_IMM_TO_REG asm_x86_mov_i32_to_r32_aligned -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_x86_mov_i32_to_r32(as, (imm), (reg_temp)); \ - asm_x86_mov_r32_to_local(as, (reg_temp), (local_num)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG asm_x86_mov_local_to_r32 +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x86_mov_r32_to_local((as), (reg_src), (local_num)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest)) +#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32_aligned((as), (imm), (reg_dest)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x86_mov_local_to_r32((as), (local_num), (reg_dest)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG asm_x86_mov_local_addr_to_r32 +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x86_mov_local_addr_to_r32((as), (local_num), (reg_dest)) #define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg)) #define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg)) diff --git a/py/asmxtensa.h b/py/asmxtensa.h index 7db6c0d3d..e6d4158cb 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -280,17 +280,12 @@ void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_nu asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0); \ } while (0) -#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_xtensa_mov_local_reg(as, (local_num), (reg)) -#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm)) -#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm)) -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_xtensa_mov_reg_i32(as, (reg_temp), (imm)); \ - asm_xtensa_mov_local_reg(as, (local_num), (reg_temp)); \ - } while (0) -#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local(as, (reg), (local_num)) +#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), (local_num), (reg_src)) +#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm)) +#define ASM_MOV_REG_ALIGNED_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm)) +#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local_addr(as, (reg), (local_num)) +#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), (local_num)) #define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \ do { \ diff --git a/py/emitnative.c b/py/emitnative.c index 8e97dda11..964db9552 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -66,6 +66,13 @@ // this is defined so that the assembler exports generic assembler API macros #define GENERIC_ASM_API (1) +// define additional generic helper macros +#define ASM_MOV_LOCAL_IMM_VIA(as, local_num, imm, reg_temp) \ + do { \ + ASM_MOV_REG_IMM((as), (reg_temp), (imm)); \ + ASM_MOV_LOCAL_REG((as), (local_num), (reg_temp)); \ + } while (false) + #if N_X64 // x64 specific stuff @@ -389,7 +396,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_3); } else { assert(i == 3); // should be true; max 4 args is checked above - ASM_MOV_REG_TO_LOCAL(emit->as, REG_ARG_4, i - REG_LOCAL_NUM); + ASM_MOV_LOCAL_REG(emit->as, i - REG_LOCAL_NUM, REG_ARG_4); } } #endif @@ -418,14 +425,14 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop #endif // set code_state.fun_bc - ASM_MOV_REG_TO_LOCAL(emit->as, REG_ARG_1, offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t)); + ASM_MOV_LOCAL_REG(emit->as, offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t), REG_ARG_1); // set code_state.ip (offset from start of this function to prelude info) // XXX this encoding may change size - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->prelude_offset, offsetof(mp_code_state_t, ip) / sizeof(uintptr_t), REG_ARG_1); + ASM_MOV_LOCAL_IMM_VIA(emit->as, offsetof(mp_code_state_t, ip) / sizeof(uintptr_t), emit->prelude_offset, REG_ARG_1); // put address of code_state into first arg - ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, 0, REG_ARG_1); + ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 0); // call mp_setup_code_state to prepare code_state structure #if N_THUMB @@ -438,11 +445,11 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop // cache some locals in registers if (scope->num_locals > 0) { - ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 0, REG_LOCAL_1); + ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_1, STATE_START + emit->n_state - 1 - 0); if (scope->num_locals > 1) { - ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 1, REG_LOCAL_2); + ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_2, STATE_START + emit->n_state - 1 - 1); if (scope->num_locals > 2) { - ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 2, REG_LOCAL_3); + ASM_MOV_REG_LOCAL(emit->as, REG_LOCAL_3, STATE_START + emit->n_state - 1 - 2); } } } @@ -606,7 +613,7 @@ STATIC void need_reg_single(emit_t *emit, int reg_needed, int skip_stack_pos) { stack_info_t *si = &emit->stack_info[i]; if (si->kind == STACK_REG && si->data.u_reg == reg_needed) { si->kind = STACK_VALUE; - ASM_MOV_REG_TO_LOCAL(emit->as, si->data.u_reg, emit->stack_start + i); + ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg); } } } @@ -617,7 +624,7 @@ STATIC void need_reg_all(emit_t *emit) { stack_info_t *si = &emit->stack_info[i]; if (si->kind == STACK_REG) { si->kind = STACK_VALUE; - ASM_MOV_REG_TO_LOCAL(emit->as, si->data.u_reg, emit->stack_start + i); + ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg); } } } @@ -629,7 +636,7 @@ STATIC void need_stack_settled(emit_t *emit) { if (si->kind == STACK_REG) { DEBUG_printf(" reg(%u) to local(%u)\n", si->data.u_reg, emit->stack_start + i); si->kind = STACK_VALUE; - ASM_MOV_REG_TO_LOCAL(emit->as, si->data.u_reg, emit->stack_start + i); + ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg); } } for (int i = 0; i < emit->stack_size; i++) { @@ -637,7 +644,7 @@ STATIC void need_stack_settled(emit_t *emit) { if (si->kind == STACK_IMM) { DEBUG_printf(" imm(" INT_FMT ") to local(%u)\n", si->data.u_imm, emit->stack_start + i); si->kind = STACK_VALUE; - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, si->data.u_imm, emit->stack_start + i, REG_TEMP0); + ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + i, si->data.u_imm, REG_TEMP0); } } } @@ -649,7 +656,7 @@ STATIC void emit_access_stack(emit_t *emit, int pos, vtype_kind_t *vtype, int re *vtype = si->vtype; switch (si->kind) { case STACK_VALUE: - ASM_MOV_LOCAL_TO_REG(emit->as, emit->stack_start + emit->stack_size - pos, reg_dest); + ASM_MOV_REG_LOCAL(emit->as, reg_dest, emit->stack_start + emit->stack_size - pos); break; case STACK_REG: @@ -659,7 +666,7 @@ STATIC void emit_access_stack(emit_t *emit, int pos, vtype_kind_t *vtype, int re break; case STACK_IMM: - ASM_MOV_IMM_TO_REG(emit->as, si->data.u_imm, reg_dest); + ASM_MOV_REG_IMM(emit->as, reg_dest, si->data.u_imm); break; } } @@ -671,7 +678,7 @@ STATIC void emit_fold_stack_top(emit_t *emit, int reg_dest) { si[0] = si[1]; if (si->kind == STACK_VALUE) { // if folded element was on the stack we need to put it in a register - ASM_MOV_LOCAL_TO_REG(emit->as, emit->stack_start + emit->stack_size - 1, reg_dest); + ASM_MOV_REG_LOCAL(emit->as, reg_dest, emit->stack_start + emit->stack_size - 1); si->kind = STACK_REG; si->data.u_reg = reg_dest; } @@ -765,30 +772,30 @@ STATIC void emit_call(emit_t *emit, mp_fun_kind_t fun_kind) { STATIC void emit_call_with_imm_arg(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val, int arg_reg) { need_reg_all(emit); - ASM_MOV_IMM_TO_REG(emit->as, arg_val, arg_reg); + ASM_MOV_REG_IMM(emit->as, arg_reg, arg_val); ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind); } // the first arg is stored in the code aligned on a mp_uint_t boundary STATIC void emit_call_with_imm_arg_aligned(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val, int arg_reg) { need_reg_all(emit); - ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, arg_val, arg_reg); + ASM_MOV_REG_ALIGNED_IMM(emit->as, arg_reg, arg_val); ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind); } STATIC void emit_call_with_2_imm_args(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val1, int arg_reg1, mp_int_t arg_val2, int arg_reg2) { need_reg_all(emit); - ASM_MOV_IMM_TO_REG(emit->as, arg_val1, arg_reg1); - ASM_MOV_IMM_TO_REG(emit->as, arg_val2, arg_reg2); + ASM_MOV_REG_IMM(emit->as, arg_reg1, arg_val1); + ASM_MOV_REG_IMM(emit->as, arg_reg2, arg_val2); ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind); } // the first arg is stored in the code aligned on a mp_uint_t boundary STATIC void emit_call_with_3_imm_args_and_first_aligned(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_t arg_val1, int arg_reg1, mp_int_t arg_val2, int arg_reg2, mp_int_t arg_val3, int arg_reg3) { need_reg_all(emit); - ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, arg_val1, arg_reg1); - ASM_MOV_IMM_TO_REG(emit->as, arg_val2, arg_reg2); - ASM_MOV_IMM_TO_REG(emit->as, arg_val3, arg_reg3); + ASM_MOV_REG_ALIGNED_IMM(emit->as, arg_reg1, arg_val1); + ASM_MOV_REG_IMM(emit->as, arg_reg2, arg_val2); + ASM_MOV_REG_IMM(emit->as, arg_reg3, arg_val3); ASM_CALL_IND(emit->as, mp_fun_table[fun_kind], fun_kind); } @@ -808,19 +815,19 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de si->kind = STACK_VALUE; switch (si->vtype) { case VTYPE_PYOBJ: - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, si->data.u_imm, emit->stack_start + emit->stack_size - 1 - i, reg_dest); + ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, si->data.u_imm, reg_dest); break; case VTYPE_BOOL: if (si->data.u_imm == 0) { - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, (mp_uint_t)mp_const_false, emit->stack_start + emit->stack_size - 1 - i, reg_dest); + ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_false, reg_dest); } else { - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, (mp_uint_t)mp_const_true, emit->stack_start + emit->stack_size - 1 - i, reg_dest); + ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_true, reg_dest); } si->vtype = VTYPE_PYOBJ; break; case VTYPE_INT: case VTYPE_UINT: - ASM_MOV_IMM_TO_LOCAL_USING(emit->as, (uintptr_t)MP_OBJ_NEW_SMALL_INT(si->data.u_imm), emit->stack_start + emit->stack_size - 1 - i, reg_dest); + ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (uintptr_t)MP_OBJ_NEW_SMALL_INT(si->data.u_imm), reg_dest); si->vtype = VTYPE_PYOBJ; break; default: @@ -838,9 +845,9 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i]; if (si->vtype != VTYPE_PYOBJ) { mp_uint_t local_num = emit->stack_start + emit->stack_size - 1 - i; - ASM_MOV_LOCAL_TO_REG(emit->as, local_num, REG_ARG_1); + ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, local_num); emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, si->vtype, REG_ARG_2); // arg2 = type - ASM_MOV_REG_TO_LOCAL(emit->as, REG_RET, local_num); + ASM_MOV_LOCAL_REG(emit->as, local_num, REG_RET); si->vtype = VTYPE_PYOBJ; DEBUG_printf(" convert_native_to_obj(local_num=" UINT_FMT ")\n", local_num); } @@ -848,7 +855,7 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de // Adujust the stack for a pop of n_pop items, and load the stack pointer into reg_dest. adjust_stack(emit, -n_pop); - ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, emit->stack_start + emit->stack_size, reg_dest); + ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, emit->stack_start + emit->stack_size); } // vtype of all n_push objects is VTYPE_PYOBJ @@ -858,7 +865,7 @@ STATIC void emit_get_stack_pointer_to_reg_for_push(emit_t *emit, mp_uint_t reg_d emit->stack_info[emit->stack_size + i].kind = STACK_VALUE; emit->stack_info[emit->stack_size + i].vtype = VTYPE_PYOBJ; } - ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, emit->stack_start + emit->stack_size, reg_dest); + ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, emit->stack_start + emit->stack_size); adjust_stack(emit, n_push); } @@ -881,7 +888,7 @@ STATIC void emit_native_import_name(emit_t *emit, qstr qst) { stack_info_t *top = peek_stack(emit, 0); if (top->vtype == VTYPE_PTR_NONE) { emit_pre_pop_discard(emit); - ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)mp_const_none, REG_ARG_2); + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)mp_const_none); } else { vtype_kind_t vtype_fromlist; emit_pre_pop_reg(emit, &vtype_fromlist, REG_ARG_2); @@ -891,7 +898,7 @@ STATIC void emit_native_import_name(emit_t *emit, qstr qst) { // level argument should be an immediate integer top = peek_stack(emit, 0); assert(top->vtype == VTYPE_INT && top->kind == STACK_IMM); - ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)MP_OBJ_NEW_SMALL_INT(top->data.u_imm), REG_ARG_3); + ASM_MOV_REG_IMM(emit->as, REG_ARG_3, (mp_uint_t)MP_OBJ_NEW_SMALL_INT(top->data.u_imm)); emit_pre_pop_discard(emit); } else { @@ -981,7 +988,7 @@ STATIC void emit_native_load_const_str(emit_t *emit, qstr qst) { STATIC void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj) { emit_native_pre(emit); need_reg_single(emit, REG_RET, 0); - ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, (mp_uint_t)obj, REG_RET); + ASM_MOV_REG_ALIGNED_IMM(emit->as, REG_RET, (mp_uint_t)obj); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } @@ -1006,9 +1013,9 @@ STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) { } else { need_reg_single(emit, REG_TEMP0, 0); if (emit->do_viper_types) { - ASM_MOV_LOCAL_TO_REG(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0); + ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM); } else { - ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - local_num, REG_TEMP0); + ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, STATE_START + emit->n_state - 1 - local_num); } emit_post_push_reg(emit, vtype, REG_TEMP0); } @@ -1134,7 +1141,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) { break; } #endif - ASM_MOV_IMM_TO_REG(emit->as, index_value, reg_index); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base reg_base = reg_index; } @@ -1151,7 +1158,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) { break; } #endif - ASM_MOV_IMM_TO_REG(emit->as, index_value << 1, reg_index); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base reg_base = reg_index; } @@ -1168,7 +1175,7 @@ STATIC void emit_native_load_subscr(emit_t *emit) { break; } #endif - ASM_MOV_IMM_TO_REG(emit->as, index_value << 2, reg_index); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base reg_base = reg_index; } @@ -1233,9 +1240,9 @@ STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num) } else { emit_pre_pop_reg(emit, &vtype, REG_TEMP0); if (emit->do_viper_types) { - ASM_MOV_REG_TO_LOCAL(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM); + ASM_MOV_LOCAL_REG(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0); } else { - ASM_MOV_REG_TO_LOCAL(emit->as, REG_TEMP0, STATE_START + emit->n_state - 1 - local_num); + ASM_MOV_LOCAL_REG(emit->as, STATE_START + emit->n_state - 1 - local_num, REG_TEMP0); } } emit_post(emit); @@ -1354,7 +1361,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) { break; } #endif - ASM_MOV_IMM_TO_REG(emit->as, index_value, reg_index); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value); #if N_ARM asm_arm_strb_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); return; @@ -1375,7 +1382,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) { break; } #endif - ASM_MOV_IMM_TO_REG(emit->as, index_value << 1, reg_index); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); #if N_ARM asm_arm_strh_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); return; @@ -1396,7 +1403,7 @@ STATIC void emit_native_store_subscr(emit_t *emit) { break; } #endif - ASM_MOV_IMM_TO_REG(emit->as, index_value << 2, reg_index); + ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); #if N_ARM asm_arm_str_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); return; @@ -1773,7 +1780,7 @@ STATIC void emit_native_get_iter(emit_t *emit, bool use_stack) { emit_call(emit, MP_F_NATIVE_GETITER); } else { // mp_getiter will allocate the iter_buf on the heap - ASM_MOV_IMM_TO_REG(emit->as, 0, REG_ARG_2); + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, 0); emit_call(emit, MP_F_NATIVE_GETITER); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } @@ -1784,7 +1791,7 @@ STATIC void emit_native_for_iter(emit_t *emit, mp_uint_t label) { emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_1, MP_OBJ_ITER_BUF_NSLOTS); adjust_stack(emit, MP_OBJ_ITER_BUF_NSLOTS); emit_call(emit, MP_F_NATIVE_ITERNEXT); - ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)MP_OBJ_STOP_ITERATION, REG_TEMP1); + ASM_MOV_REG_IMM(emit->as, REG_TEMP1, (mp_uint_t)MP_OBJ_STOP_ITERATION); ASM_JUMP_IF_REG_EQ(emit->as, REG_RET, REG_TEMP1, label); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } @@ -2128,12 +2135,12 @@ STATIC void emit_native_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_c emit_native_pre(emit); if (n_pos_defaults == 0 && n_kw_defaults == 0) { emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over); - ASM_MOV_IMM_TO_REG(emit->as, n_closed_over, REG_ARG_2); + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, n_closed_over); } else { emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_closed_over + 2); - ASM_MOV_IMM_TO_REG(emit->as, 0x100 | n_closed_over, REG_ARG_2); + ASM_MOV_REG_IMM(emit->as, REG_ARG_2, 0x100 | n_closed_over); } - ASM_MOV_ALIGNED_IMM_TO_REG(emit->as, (mp_uint_t)scope->raw_code, REG_ARG_1); + ASM_MOV_REG_ALIGNED_IMM(emit->as, REG_ARG_1, (mp_uint_t)scope->raw_code); ASM_CALL_IND(emit->as, mp_fun_table[MP_F_MAKE_CLOSURE_FROM_RAW_CODE], MP_F_MAKE_CLOSURE_FROM_RAW_CODE); emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } @@ -2212,9 +2219,9 @@ STATIC void emit_native_return_value(emit_t *emit) { if (peek_vtype(emit, 0) == VTYPE_PTR_NONE) { emit_pre_pop_discard(emit); if (emit->return_vtype == VTYPE_PYOBJ) { - ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)mp_const_none, REG_RET); + ASM_MOV_REG_IMM(emit->as, REG_RET, (mp_uint_t)mp_const_none); } else { - ASM_MOV_IMM_TO_REG(emit->as, 0, REG_RET); + ASM_MOV_REG_IMM(emit->as, REG_RET, 0); } } else { vtype_kind_t vtype; From 1871a924c97cec16d0670d136e9f7056f99865df Mon Sep 17 00:00:00 2001 From: Christopher Arndt Date: Tue, 31 Oct 2017 17:19:12 +0100 Subject: [PATCH 022/828] py/mkenv.mk: Use $(PYTHON) consistently when calling Python tools. Rationale: * Calling Python build tool scripts from makefiles should be done consistently using `python `, instead of relying on the correct she-bang line in the script [1] and the executable bit on the script being set. This is more platform-independent. * The name/path of the Python executable should always be used via the makefile variable `PYTHON` set in `py/mkenv.mk`. This way it can be easily overwritten by the user with `make PYTHON=/path/to/my/python`. * The Python executable name should be part of the value of the makefile variable, which stands for the build tool command (e.g. `MAKE_FROZEN` and `MPY_TOOL`), not part of the command line where it is used. If a Python tool is substituted by another (non-python) program, no change to the Makefiles is necessary, except in `py/mkenv.mk`. * This also solves #3369 and #1616. [1] There are systems, where even the assumption that `/usr/bin/env` always exists, doesn't hold true, for example on Android (where otherwise the unix port compiles perfectly well). --- py/mkenv.mk | 4 ++-- py/mkrules.mk | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/py/mkenv.mk b/py/mkenv.mk index b167b2533..2c9c86a7a 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -58,9 +58,9 @@ CXX += -m32 LD += -m32 endif -MAKE_FROZEN = $(TOP)/tools/make-frozen.py +MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py MPY_CROSS = $(TOP)/mpy-cross/mpy-cross -MPY_TOOL = $(TOP)/tools/mpy-tool.py +MPY_TOOL = $(PYTHON) $(TOP)/tools/mpy-tool.py all: .PHONY: all diff --git a/py/mkrules.mk b/py/mkrules.mk index 13545eb6f..72a5c3de0 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -119,7 +119,7 @@ $(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py $(TOP)/mpy-cross/mpy-cross # to build frozen_mpy.c from all .mpy files $(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h @$(ECHO) "Creating $@" - $(Q)$(PYTHON) $(MPY_TOOL) -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h $(FROZEN_MPY_MPY_FILES) > $@ + $(Q)$(MPY_TOOL) -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h $(FROZEN_MPY_MPY_FILES) > $@ endif ifneq ($(PROG),) From 2cafef857e357bbfcf3bb3c6d10e62b778642695 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Nov 2017 12:23:29 +1100 Subject: [PATCH 023/828] stm32/boards/NUCLEO_F429ZI: Incr CPU freq to 168MHz to get USB working. At the original frequency of 90MHz there's no way to get a 48MHz USB clock. These new setting mirror those of the STM32F429DISC board. --- ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h index 42cc9d68c..608beef67 100644 --- a/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h @@ -16,9 +16,9 @@ // HSE is 8MHz #define MICROPY_HW_CLK_PLLM (8) -#define MICROPY_HW_CLK_PLLN (180) +#define MICROPY_HW_CLK_PLLN (336) #define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) -#define MICROPY_HW_CLK_PLLQ (4) +#define MICROPY_HW_CLK_PLLQ (7) // From the reference manual, for 2.7V to 3.6V // 151-180 MHz => 5 wait states From 9ba3de6ea19a7c86f7ecd9ceefabbf732949e662 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 15 Nov 2017 12:46:08 +1100 Subject: [PATCH 024/828] tools/mpy-tool.py: Implement freezing of Ellipsis const object. --- tools/mpy-tool.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index ac7b2c1cc..eeb760a5f 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -287,7 +287,9 @@ class RawCode: # generate constant objects for i, obj in enumerate(self.objs): obj_name = 'const_obj_%s_%u' % (self.escaped_name, i) - if is_str_type(obj) or is_bytes_type(obj): + if obj is Ellipsis: + print('#define %s mp_const_ellipsis_obj' % obj_name) + elif is_str_type(obj) or is_bytes_type(obj): if is_str_type(obj): obj = bytes_cons(obj, 'utf8') obj_type = 'mp_type_str' @@ -328,7 +330,6 @@ class RawCode: print('STATIC const mp_obj_complex_t %s = {{&mp_type_complex}, %.16g, %.16g};' % (obj_name, obj.real, obj.imag)) else: - # TODO raise FreezeError(self, 'freezing of object %r is not implemented' % (obj,)) # generate constant table, if it has any entries From 6bc55b657b900dd92ebb8b4a8c393644a30dd7e6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Nov 2017 13:13:24 +1100 Subject: [PATCH 025/828] extmod/vfs: Use existing qstr for forward-slash string object. --- extmod/vfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extmod/vfs.c b/extmod/vfs.c index 44ad8ffad..105a80a8d 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -261,7 +261,7 @@ mp_obj_t mp_vfs_chdir(mp_obj_t path_in) { // subsequent relative paths begin at the root of that VFS. for (vfs = MP_STATE_VM(vfs_mount_table); vfs != NULL; vfs = vfs->next) { if (vfs->len == 1) { - mp_obj_t root = mp_obj_new_str("/", 1, false); + mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_); mp_vfs_proxy_call(vfs, MP_QSTR_chdir, 1, &root); break; } @@ -318,7 +318,7 @@ STATIC mp_obj_t mp_vfs_ilistdir_it_iternext(mp_obj_t self_in) { self->cur.vfs = vfs->next; if (vfs->len == 1) { // vfs is mounted at root dir, delegate to it - mp_obj_t root = mp_obj_new_str("/", 1, false); + mp_obj_t root = MP_OBJ_NEW_QSTR(MP_QSTR__slash_); self->is_iter = true; self->cur.iter = mp_vfs_proxy_call(vfs, MP_QSTR_ilistdir, 1, &root); return mp_iternext(self->cur.iter); From 4601759bf59e16b860a3f082e9aa4ea78356bf92 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Nov 2017 13:17:51 +1100 Subject: [PATCH 026/828] py/objstr: Remove "make_qstr_if_not_already" arg from mp_obj_new_str. This patch simplifies the str creation API to favour the common case of creating a str object that is not forced to be interned. To force interning of a new str the new mp_obj_new_str_via_qstr function is added, and should only be used if warranted. Apart from simplifying the mp_obj_new_str function (and making it have the same signature as mp_obj_new_bytes), this patch also reduces code size by a bit (-16 bytes for bare-arm and roughly -40 bytes on the bare-metal archs). --- extmod/modujson.c | 2 +- extmod/modwebrepl.c | 2 +- extmod/vfs_fat.c | 2 +- extmod/vfs_fat_misc.c | 2 +- extmod/vfs_reader.c | 2 +- lib/netutils/netutils.c | 2 +- ports/cc3200/mods/modwlan.c | 10 +++++----- ports/esp8266/modnetwork.c | 4 ++-- ports/esp8266/moduos.c | 2 +- ports/stm32/modnwcc3k.c | 4 ++-- ports/unix/coverage.c | 6 +++--- ports/unix/main.c | 6 +++--- ports/unix/modffi.c | 2 +- ports/unix/modjni.c | 2 +- ports/unix/modos.c | 4 ++-- ports/zephyr/modusocket.c | 2 +- py/binary.c | 2 +- py/builtinhelp.c | 2 +- py/builtinimport.c | 2 +- py/modbuiltins.c | 4 ++-- py/modio.c | 2 +- py/obj.h | 3 ++- py/objstr.c | 36 ++++++++++++++++++------------------ py/objstrunicode.c | 4 ++-- 24 files changed, 55 insertions(+), 54 deletions(-) diff --git a/extmod/modujson.c b/extmod/modujson.c index f14682d26..6eeba4ed6 100644 --- a/extmod/modujson.c +++ b/extmod/modujson.c @@ -166,7 +166,7 @@ STATIC mp_obj_t mod_ujson_load(mp_obj_t stream_obj) { goto fail; } S_NEXT(s); - next = mp_obj_new_str(vstr.buf, vstr.len, false); + next = mp_obj_new_str(vstr.buf, vstr.len); break; case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { diff --git a/extmod/modwebrepl.c b/extmod/modwebrepl.c index 3aba5c0f1..42b30f5ea 100644 --- a/extmod/modwebrepl.c +++ b/extmod/modwebrepl.c @@ -141,7 +141,7 @@ STATIC void handle_op(mp_obj_webrepl_t *self) { // Handle operations requiring opened file mp_obj_t open_args[2] = { - mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname), false), + mp_obj_new_str(self->hdr.fname, strlen(self->hdr.fname)), MP_OBJ_NEW_QSTR(MP_QSTR_rb) }; diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 22346bdf1..9942ddeb5 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -197,7 +197,7 @@ STATIC mp_obj_t fat_vfs_getcwd(mp_obj_t vfs_in) { if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } - return mp_obj_new_str(buf, strlen(buf), false); + return mp_obj_new_str(buf, strlen(buf)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getcwd_obj, fat_vfs_getcwd); diff --git a/extmod/vfs_fat_misc.c b/extmod/vfs_fat_misc.c index 9a26b4a2f..1f90ac14c 100644 --- a/extmod/vfs_fat_misc.c +++ b/extmod/vfs_fat_misc.c @@ -57,7 +57,7 @@ STATIC mp_obj_t mp_vfs_fat_ilistdir_it_iternext(mp_obj_t self_in) { // make 3-tuple with info about this entry mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); if (self->is_str) { - t->items[0] = mp_obj_new_str(fn, strlen(fn), false); + t->items[0] = mp_obj_new_str(fn, strlen(fn)); } else { t->items[0] = mp_obj_new_bytes((const byte*)fn, strlen(fn)); } diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index 891098aa1..e1ee45a3c 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -71,7 +71,7 @@ STATIC void mp_reader_vfs_close(void *data) { void mp_reader_new_file(mp_reader_t *reader, const char *filename) { mp_reader_vfs_t *rf = m_new_obj(mp_reader_vfs_t); - mp_obj_t arg = mp_obj_new_str(filename, strlen(filename), false); + mp_obj_t arg = mp_obj_new_str(filename, strlen(filename)); rf->file = mp_vfs_open(1, &arg, (mp_map_t*)&mp_const_empty_map); int errcode; rf->len = mp_stream_rw(rf->file, rf->buf, sizeof(rf->buf), &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); diff --git a/lib/netutils/netutils.c b/lib/netutils/netutils.c index 06c3ff9b0..073f46b19 100644 --- a/lib/netutils/netutils.c +++ b/lib/netutils/netutils.c @@ -41,7 +41,7 @@ mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian) { } else { ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); } - return mp_obj_new_str(ip_str, ip_len, false); + return mp_obj_new_str(ip_str, ip_len); } // Takes an array with a raw IP address, and a port, and returns a net-address diff --git a/ports/cc3200/mods/modwlan.c b/ports/cc3200/mods/modwlan.c index f9c7111b3..8acc89da3 100644 --- a/ports/cc3200/mods/modwlan.c +++ b/ports/cc3200/mods/modwlan.c @@ -885,7 +885,7 @@ STATIC mp_obj_t wlan_scan(mp_obj_t self_in) { } mp_obj_t tuple[5]; - tuple[0] = mp_obj_new_str((const char *)wlanEntry.ssid, wlanEntry.ssid_len, false); + tuple[0] = mp_obj_new_str((const char *)wlanEntry.ssid, wlanEntry.ssid_len); tuple[1] = mp_obj_new_bytes((const byte *)wlanEntry.bssid, SL_BSSID_LENGTH); // 'normalize' the security type if (wlanEntry.sec_type > 2) { @@ -1075,7 +1075,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wlan_mode_obj, 1, 2, wlan_mode); STATIC mp_obj_t wlan_ssid(size_t n_args, const mp_obj_t *args) { wlan_obj_t *self = args[0]; if (n_args == 1) { - return mp_obj_new_str((const char *)self->ssid, strlen((const char *)self->ssid), false); + return mp_obj_new_str((const char *)self->ssid, strlen((const char *)self->ssid)); } else { size_t len; const char *ssid = mp_obj_str_get_data(args[1], &len); @@ -1095,7 +1095,7 @@ STATIC mp_obj_t wlan_auth(size_t n_args, const mp_obj_t *args) { } else { mp_obj_t security[2]; security[0] = mp_obj_new_int(self->auth); - security[1] = mp_obj_new_str((const char *)self->key, strlen((const char *)self->key), false); + security[1] = mp_obj_new_str((const char *)self->key, strlen((const char *)self->key)); return mp_obj_new_tuple(2, security); } } else { @@ -1199,7 +1199,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wlan_irq_obj, 1, wlan_irq); // mp_obj_t connections = mp_obj_new_list(0, NULL); // // if (wlan_is_connected()) { -// device[0] = mp_obj_new_str((const char *)wlan_obj.ssid_o, strlen((const char *)wlan_obj.ssid_o), false); +// device[0] = mp_obj_new_str((const char *)wlan_obj.ssid_o, strlen((const char *)wlan_obj.ssid_o)); // device[1] = mp_obj_new_bytes((const byte *)wlan_obj.bssid, SL_BSSID_LENGTH); // // add the device to the list // mp_obj_list_append(connections, mp_obj_new_tuple(MP_ARRAY_SIZE(device), device)); @@ -1232,7 +1232,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wlan_irq_obj, 1, wlan_irq); // if (sl_NetAppGet(SL_NET_APP_DEVICE_CONFIG_ID, NETAPP_SET_GET_DEV_CONF_OPT_DEVICE_URN, &len, (uint8_t *)urn) < 0) { // mp_raise_OSError(MP_EIO); // } -// return mp_obj_new_str(urn, (len - 1), false); +// return mp_obj_new_str(urn, (len - 1)); // } // // return mp_const_none; diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index b41a11f59..4066c969c 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -411,7 +411,7 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs } case QS(MP_QSTR_essid): req_if = SOFTAP_IF; - val = mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len, false); + val = mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len); break; case QS(MP_QSTR_hidden): req_if = SOFTAP_IF; @@ -428,7 +428,7 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs case QS(MP_QSTR_dhcp_hostname): { req_if = STATION_IF; char* s = wifi_station_get_hostname(); - val = mp_obj_new_str(s, strlen(s), false); + val = mp_obj_new_str(s, strlen(s)); break; } default: diff --git a/ports/esp8266/moduos.c b/ports/esp8266/moduos.c index d0554096e..93f7aa712 100644 --- a/ports/esp8266/moduos.c +++ b/ports/esp8266/moduos.c @@ -60,7 +60,7 @@ STATIC mp_obj_tuple_t os_uname_info_obj = { STATIC mp_obj_t os_uname(void) { // We must populate the "release" field each time in case it was GC'd since the last call. const char *ver = system_get_sdk_version(); - os_uname_info_obj.items[2] = mp_obj_new_str(ver, strlen(ver), false); + os_uname_info_obj.items[2] = mp_obj_new_str(ver, strlen(ver)); return (mp_obj_t)&os_uname_info_obj; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); diff --git a/ports/stm32/modnwcc3k.c b/ports/stm32/modnwcc3k.c index 8cc0a613d..4f1af7354 100644 --- a/ports/stm32/modnwcc3k.c +++ b/ports/stm32/modnwcc3k.c @@ -531,8 +531,8 @@ STATIC mp_obj_t cc3k_ifconfig(mp_obj_t self_in) { netutils_format_ipv4_addr(ipconfig.aucDefaultGateway, NETUTILS_LITTLE), netutils_format_ipv4_addr(ipconfig.aucDNSServer, NETUTILS_LITTLE), netutils_format_ipv4_addr(ipconfig.aucDHCPServer, NETUTILS_LITTLE), - mp_obj_new_str(mac_vstr.buf, mac_vstr.len, false), - mp_obj_new_str((const char*)ipconfig.uaSSID, strlen((const char*)ipconfig.uaSSID), false), + mp_obj_new_str(mac_vstr.buf, mac_vstr.len), + mp_obj_new_str((const char*)ipconfig.uaSSID, strlen((const char*)ipconfig.uaSSID)), }; return mp_obj_new_tuple(MP_ARRAY_SIZE(tuple), tuple); } diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 651db0a94..e2896c2dd 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -227,7 +227,7 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# str\n"); // intern string - mp_printf(&mp_plat_print, "%d\n", MP_OBJ_IS_QSTR(mp_obj_str_intern(mp_obj_new_str("intern me", 9, false)))); + mp_printf(&mp_plat_print, "%d\n", MP_OBJ_IS_QSTR(mp_obj_str_intern(mp_obj_new_str("intern me", 9)))); } // mpz @@ -260,12 +260,12 @@ STATIC mp_obj_t extra_coverage(void) { // call mp_call_function_1_protected mp_call_function_1_protected(MP_OBJ_FROM_PTR(&mp_builtin_abs_obj), MP_OBJ_NEW_SMALL_INT(1)); // call mp_call_function_1_protected with invalid args - mp_call_function_1_protected(MP_OBJ_FROM_PTR(&mp_builtin_abs_obj), mp_obj_new_str("abc", 3, false)); + mp_call_function_1_protected(MP_OBJ_FROM_PTR(&mp_builtin_abs_obj), mp_obj_new_str("abc", 3)); // call mp_call_function_2_protected mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), MP_OBJ_NEW_SMALL_INT(1), MP_OBJ_NEW_SMALL_INT(1)); // call mp_call_function_2_protected with invalid args - mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str("abc", 3, false), mp_obj_new_str("abc", 3, false)); + mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str("abc", 3), mp_obj_new_str("abc", 3)); } // warning diff --git a/ports/unix/main.c b/ports/unix/main.c index e1cd33fc1..25f3e04fb 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -481,7 +481,7 @@ MP_NOINLINE int main_(int argc, char **argv) { vstr_add_strn(&vstr, p + 1, p1 - p - 1); path_items[i] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr); } else { - path_items[i] = MP_OBJ_NEW_QSTR(qstr_from_strn(p, p1 - p)); + path_items[i] = mp_obj_new_str_via_qstr(p, p1 - p); } p = p1 + 1; } @@ -537,7 +537,7 @@ MP_NOINLINE int main_(int argc, char **argv) { return usage(argv); } mp_obj_t import_args[4]; - import_args[0] = mp_obj_new_str(argv[a + 1], strlen(argv[a + 1]), false); + import_args[0] = mp_obj_new_str(argv[a + 1], strlen(argv[a + 1])); import_args[1] = import_args[2] = mp_const_none; // Ask __import__ to handle imported module specially - set its __name__ // to __main__, and also return this leaf module, not top-level package @@ -603,7 +603,7 @@ MP_NOINLINE int main_(int argc, char **argv) { // Set base dir of the script as first entry in sys.path char *p = strrchr(basedir, '/'); - path_items[0] = MP_OBJ_NEW_QSTR(qstr_from_strn(basedir, p - basedir)); + path_items[0] = mp_obj_new_str_via_qstr(basedir, p - basedir); free(pathbuf); set_sys_argv(argv, argc, a); diff --git a/ports/unix/modffi.c b/ports/unix/modffi.c index 78adccac1..024f83c14 100644 --- a/ports/unix/modffi.c +++ b/ports/unix/modffi.c @@ -144,7 +144,7 @@ STATIC mp_obj_t return_ffi_value(ffi_arg val, char type) if (!s) { return mp_const_none; } - return mp_obj_new_str(s, strlen(s), false); + return mp_obj_new_str(s, strlen(s)); } case 'v': return mp_const_none; diff --git a/ports/unix/modjni.c b/ports/unix/modjni.c index f29c095cf..8ec5ae54d 100644 --- a/ports/unix/modjni.c +++ b/ports/unix/modjni.c @@ -337,7 +337,7 @@ STATIC mp_obj_t new_jobject(jobject jo) { return mp_const_none; } else if (JJ(IsInstanceOf, jo, String_class)) { const char *s = JJ(GetStringUTFChars, jo, NULL); - mp_obj_t ret = mp_obj_new_str(s, strlen(s), false); + mp_obj_t ret = mp_obj_new_str(s, strlen(s)); JJ(ReleaseStringUTFChars, jo, s); return ret; } else if (JJ(IsInstanceOf, jo, Class_class)) { diff --git a/ports/unix/modos.c b/ports/unix/modos.c index 327116a0a..808d12adb 100644 --- a/ports/unix/modos.c +++ b/ports/unix/modos.c @@ -133,7 +133,7 @@ STATIC mp_obj_t mod_os_getenv(mp_obj_t var_in) { if (s == NULL) { return mp_const_none; } - return mp_obj_new_str(s, strlen(s), false); + return mp_obj_new_str(s, strlen(s)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_obj, mod_os_getenv); @@ -171,7 +171,7 @@ STATIC mp_obj_t listdir_next(mp_obj_t self_in) { } mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL)); - t->items[0] = mp_obj_new_str(dirent->d_name, strlen(dirent->d_name), false); + t->items[0] = mp_obj_new_str(dirent->d_name, strlen(dirent->d_name)); #ifdef _DIRENT_HAVE_D_TYPE t->items[1] = MP_OBJ_NEW_SMALL_INT(dirent->d_type); #else diff --git a/ports/zephyr/modusocket.c b/ports/zephyr/modusocket.c index e021c3a44..95414a49b 100644 --- a/ports/zephyr/modusocket.c +++ b/ports/zephyr/modusocket.c @@ -91,7 +91,7 @@ STATIC mp_obj_t format_inet_addr(struct sockaddr *addr, mp_obj_t port) { net_addr_ntop(addr->sa_family, &sockaddr_in6->sin6_addr, buf, sizeof(buf)); mp_obj_tuple_t *tuple = mp_obj_new_tuple(addr->sa_family == AF_INET ? 2 : 4, NULL); - tuple->items[0] = mp_obj_new_str(buf, strlen(buf), false); + tuple->items[0] = mp_obj_new_str(buf, strlen(buf)); // We employ the fact that port offset is the same for IPv4 & IPv6 // not filled in //tuple->items[1] = mp_obj_new_int(ntohs(((struct sockaddr_in*)addr)->sin_port)); diff --git a/py/binary.c b/py/binary.c index 870a0942b..f509ff010 100644 --- a/py/binary.c +++ b/py/binary.c @@ -206,7 +206,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte **ptr) { return (mp_obj_t)(mp_uint_t)val; } else if (val_type == 'S') { const char *s_val = (const char*)(uintptr_t)(mp_uint_t)val; - return mp_obj_new_str(s_val, strlen(s_val), false); + return mp_obj_new_str(s_val, strlen(s_val)); #if MICROPY_PY_BUILTINS_FLOAT } else if (val_type == 'f') { union { uint32_t i; float f; } fpu = {val}; diff --git a/py/builtinhelp.c b/py/builtinhelp.c index c9992906d..7106f3ced 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -69,7 +69,7 @@ STATIC void mp_help_add_from_names(mp_obj_t list, const char *name) { while (*name) { size_t l = strlen(name); // name should end in '.py' and we strip it off - mp_obj_list_append(list, mp_obj_new_str(name, l - 3, false)); + mp_obj_list_append(list, mp_obj_new_str(name, l - 3)); name += l + 1; } } diff --git a/py/builtinimport.c b/py/builtinimport.c index 04ce66723..9235e946c 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -434,7 +434,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path)); // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." - mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false)); + mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path))); size_t orig_path_len = path.len; vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 65c03d523..ebff5f5c0 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -159,12 +159,12 @@ STATIC mp_obj_t mp_builtin_chr(mp_obj_t o_in) { } else { mp_raise_ValueError("chr() arg not in range(0x110000)"); } - return mp_obj_new_str(str, len, true); + return mp_obj_new_str_via_qstr(str, len); #else mp_int_t ord = mp_obj_get_int(o_in); if (0 <= ord && ord <= 0xff) { char str[1] = {ord}; - return mp_obj_new_str(str, 1, true); + return mp_obj_new_str_via_qstr(str, 1); } else { mp_raise_ValueError("chr() arg not in range(256)"); } diff --git a/py/modio.c b/py/modio.c index 353a00286..828bcec46 100644 --- a/py/modio.c +++ b/py/modio.c @@ -176,7 +176,7 @@ STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) { return MP_OBJ_FROM_PTR(o); } - mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len, false); + mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len); return mp_builtin_open(1, &path_out, (mp_map_t*)&mp_const_empty_map); } MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream); diff --git a/py/obj.h b/py/obj.h index 77f0f2298..31c3ce95c 100644 --- a/py/obj.h +++ b/py/obj.h @@ -637,7 +637,8 @@ mp_obj_t mp_obj_new_int_from_uint(mp_uint_t value); mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base); mp_obj_t mp_obj_new_int_from_ll(long long val); // this must return a multi-precision integer object (or raise an overflow exception) mp_obj_t mp_obj_new_int_from_ull(unsigned long long val); // this must return a multi-precision integer object (or raise an overflow exception) -mp_obj_t mp_obj_new_str(const char* data, size_t len, bool make_qstr_if_not_already); +mp_obj_t mp_obj_new_str(const char* data, size_t len); +mp_obj_t mp_obj_new_str_via_qstr(const char* data, size_t len); mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr); mp_obj_t mp_obj_new_bytes(const byte* data, size_t len); mp_obj_t mp_obj_new_bytearray(size_t n, void *items); diff --git a/py/objstr.c b/py/objstr.c index 51da7a418..5c464ba7d 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -176,7 +176,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ mp_raise_msg(&mp_type_UnicodeError, NULL); } #endif - return mp_obj_new_str(bufinfo.buf, bufinfo.len, false); + return mp_obj_new_str(bufinfo.buf, bufinfo.len); } } } @@ -423,7 +423,7 @@ STATIC mp_obj_t bytes_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { if (MICROPY_PY_BUILTINS_STR_UNICODE || type == &mp_type_bytes) { return MP_OBJ_NEW_SMALL_INT(self_data[index_val]); } else { - return mp_obj_new_str((char*)&self_data[index_val], 1, true); + return mp_obj_new_str_via_qstr((char*)&self_data[index_val], 1); } } else { return MP_OBJ_NULL; // op not supported @@ -1046,7 +1046,7 @@ STATIC vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } else { const char *lookup; for (lookup = field_name; lookup < field_name_top && *lookup != '.' && *lookup != '['; lookup++); - mp_obj_t field_q = mp_obj_new_str(field_name, lookup - field_name, true/*?*/); + mp_obj_t field_q = mp_obj_new_str_via_qstr(field_name, lookup - field_name); // should it be via qstr? field_name = lookup; mp_map_elem_t *key_elem = mp_map_lookup(kwargs, field_q, MP_MAP_LOOKUP); if (key_elem == NULL) { @@ -1413,7 +1413,7 @@ STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ } ++str; } - mp_obj_t k_obj = mp_obj_new_str((const char*)key, str - key, true); + mp_obj_t k_obj = mp_obj_new_str_via_qstr((const char*)key, str - key); arg = mp_obj_dict_get(dict, k_obj); str++; } @@ -1992,6 +1992,11 @@ mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, siz return MP_OBJ_FROM_PTR(o); } +// Create a str using a qstr to store the data; may use existing or new qstr. +mp_obj_t mp_obj_new_str_via_qstr(const char* data, size_t len) { + return MP_OBJ_NEW_QSTR(qstr_from_strn(data, len)); +} + // Create a str/bytes object from the given vstr. The vstr buffer is resized to // the exact length required and then reused for the str/bytes object. The vstr // is cleared and can safely be passed to vstr_free if it was heap allocated. @@ -2022,25 +2027,20 @@ mp_obj_t mp_obj_new_str_from_vstr(const mp_obj_type_t *type, vstr_t *vstr) { return MP_OBJ_FROM_PTR(o); } -mp_obj_t mp_obj_new_str(const char* data, size_t len, bool make_qstr_if_not_already) { - if (make_qstr_if_not_already) { - // use existing, or make a new qstr - return MP_OBJ_NEW_QSTR(qstr_from_strn(data, len)); +mp_obj_t mp_obj_new_str(const char* data, size_t len) { + qstr q = qstr_find_strn(data, len); + if (q != MP_QSTR_NULL) { + // qstr with this data already exists + return MP_OBJ_NEW_QSTR(q); } else { - qstr q = qstr_find_strn(data, len); - if (q != MP_QSTR_NULL) { - // qstr with this data already exists - return MP_OBJ_NEW_QSTR(q); - } else { - // no existing qstr, don't make one - return mp_obj_new_str_of_type(&mp_type_str, (const byte*)data, len); - } + // no existing qstr, don't make one + return mp_obj_new_str_of_type(&mp_type_str, (const byte*)data, len); } } mp_obj_t mp_obj_str_intern(mp_obj_t str) { GET_STR_DATA_LEN(str, data, len); - return MP_OBJ_NEW_QSTR(qstr_from_strn((const char*)data, len)); + return mp_obj_new_str_via_qstr((const char*)data, len); } mp_obj_t mp_obj_new_bytes(const byte* data, size_t len) { @@ -2138,7 +2138,7 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) { mp_obj_str8_it_t *self = MP_OBJ_TO_PTR(self_in); GET_STR_DATA_LEN(self->str, str, len); if (self->cur < len) { - mp_obj_t o_out = mp_obj_new_str((const char*)str + self->cur, 1, true); + mp_obj_t o_out = mp_obj_new_str_via_qstr((const char*)str + self->cur, 1); self->cur += 1; return o_out; } else { diff --git a/py/objstrunicode.c b/py/objstrunicode.c index 29f7695b7..a1f54b8a2 100644 --- a/py/objstrunicode.c +++ b/py/objstrunicode.c @@ -216,7 +216,7 @@ STATIC mp_obj_t str_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { ++len; } } - return mp_obj_new_str((const char*)s, len, true); // This will create a one-character string + return mp_obj_new_str_via_qstr((const char*)s, len); // This will create a one-character string } else { return MP_OBJ_NULL; // op not supported } @@ -291,7 +291,7 @@ STATIC mp_obj_t str_it_iternext(mp_obj_t self_in) { if (self->cur < len) { const byte *cur = str + self->cur; const byte *end = utf8_next_char(str + self->cur); - mp_obj_t o_out = mp_obj_new_str((const char*)cur, end - cur, true); + mp_obj_t o_out = mp_obj_new_str_via_qstr((const char*)cur, end - cur); self->cur += end - cur; return o_out; } else { From 1f1d5194d775ad996f1d341c1a44b56af7ea4d4c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Nov 2017 13:53:04 +1100 Subject: [PATCH 027/828] py/objstr: Make mp_obj_new_str_of_type check for existing interned qstr. The function mp_obj_new_str_of_type is a general str object constructor used in many places in the code to create either a str or bytes object. When creating a str it should first check if the string data already exists as an interned qstr, and if so then return the qstr object. This patch makes the function have such behaviour, which helps to reduce heap usage by reusing existing interned data where possible. The old behaviour of mp_obj_new_str_of_type (which didn't check for existing interned data) is made available through the function mp_obj_new_str_copy, but should only be used in very special cases. One consequence of this patch is that the following expression is now True: 'abc' is ' abc '.split()[0] --- py/objstr.c | 26 +++++++++++++++++++------- py/objstr.h | 1 + py/parse.c | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/py/objstr.c b/py/objstr.c index 5c464ba7d..bca2af801 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -164,7 +164,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ mp_raise_msg(&mp_type_UnicodeError, NULL); } #endif - mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_of_type(type, NULL, str_len)); + mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_copy(type, NULL, str_len)); o->data = str_data; o->hash = str_hash; return MP_OBJ_FROM_PTR(o); @@ -205,7 +205,7 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size if (str_hash == 0) { str_hash = qstr_compute_hash(str_data, str_len); } - mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_of_type(&mp_type_bytes, NULL, str_len)); + mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_copy(&mp_type_bytes, NULL, str_len)); o->data = str_data; o->hash = str_hash; return MP_OBJ_FROM_PTR(o); @@ -226,7 +226,7 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, size // check if argument has the buffer protocol mp_buffer_info_t bufinfo; if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { - return mp_obj_new_str_of_type(&mp_type_bytes, bufinfo.buf, bufinfo.len); + return mp_obj_new_bytes(bufinfo.buf, bufinfo.len); } vstr_t vstr; @@ -1977,8 +1977,9 @@ const mp_obj_type_t mp_type_bytes = { const mp_obj_str_t mp_const_empty_bytes_obj = {{&mp_type_bytes}, 0, 0, (const byte*)""}; // Create a str/bytes object using the given data. New memory is allocated and -// the data is copied across. -mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, size_t len) { +// the data is copied across. This function should only be used if the type is bytes, +// or if the type is str and the string data is known to be not interned. +mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte* data, size_t len) { mp_obj_str_t *o = m_new_obj(mp_obj_str_t); o->base.type = type; o->len = len; @@ -1992,6 +1993,17 @@ mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, siz return MP_OBJ_FROM_PTR(o); } +// Create a str/bytes object using the given data. If the type is str and the string +// data is already interned, then a qstr object is returned. Otherwise new memory is +// allocated for the object and the data is copied across. +mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, size_t len) { + if (type == &mp_type_str) { + return mp_obj_new_str((const char*)data, len); + } else { + return mp_obj_new_bytes(data, len); + } +} + // Create a str using a qstr to store the data; may use existing or new qstr. mp_obj_t mp_obj_new_str_via_qstr(const char* data, size_t len) { return MP_OBJ_NEW_QSTR(qstr_from_strn(data, len)); @@ -2034,7 +2046,7 @@ mp_obj_t mp_obj_new_str(const char* data, size_t len) { return MP_OBJ_NEW_QSTR(q); } else { // no existing qstr, don't make one - return mp_obj_new_str_of_type(&mp_type_str, (const byte*)data, len); + return mp_obj_new_str_copy(&mp_type_str, (const byte*)data, len); } } @@ -2044,7 +2056,7 @@ mp_obj_t mp_obj_str_intern(mp_obj_t str) { } mp_obj_t mp_obj_new_bytes(const byte* data, size_t len) { - return mp_obj_new_str_of_type(&mp_type_bytes, data, len); + return mp_obj_new_str_copy(&mp_type_bytes, data, len); } bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) { diff --git a/py/objstr.h b/py/objstr.h index 82501a763..4e55cad09 100644 --- a/py/objstr.h +++ b/py/objstr.h @@ -65,6 +65,7 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t void mp_str_print_json(const mp_print_t *print, const byte *str_data, size_t str_len); mp_obj_t mp_obj_str_format(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); mp_obj_t mp_obj_str_split(size_t n_args, const mp_obj_t *args); +mp_obj_t mp_obj_new_str_copy(const mp_obj_type_t *type, const byte* data, size_t len); mp_obj_t mp_obj_new_str_of_type(const mp_obj_type_t *type, const byte* data, size_t len); mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); diff --git a/py/parse.c b/py/parse.c index 8c51b0349..f7fe30418 100644 --- a/py/parse.c +++ b/py/parse.c @@ -417,7 +417,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) { pn = mp_parse_node_new_leaf(lex->tok_kind == MP_TOKEN_STRING ? MP_PARSE_NODE_STRING : MP_PARSE_NODE_BYTES, qst); } else { // not interned, make a node holding a pointer to the string/bytes object - mp_obj_t o = mp_obj_new_str_of_type( + mp_obj_t o = mp_obj_new_str_copy( lex->tok_kind == MP_TOKEN_STRING ? &mp_type_str : &mp_type_bytes, (const byte*)lex->vstr.buf, lex->vstr.len); pn = make_node_const_object(parser, lex->tok_line, o); From 8d956c26d150749375115346f4ca319455107587 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Nov 2017 14:02:28 +1100 Subject: [PATCH 028/828] py/objstr: When constructing str from bytes, check for existing qstr. This patch uses existing qstr data where possible when constructing a str from a bytes object. --- py/objstr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/py/objstr.c b/py/objstr.c index bca2af801..1ff5132d2 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -164,6 +164,13 @@ mp_obj_t mp_obj_str_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ mp_raise_msg(&mp_type_UnicodeError, NULL); } #endif + + // Check if a qstr with this data already exists + qstr q = qstr_find_strn((const char*)str_data, str_len); + if (q != MP_QSTR_NULL) { + return MP_OBJ_NEW_QSTR(q); + } + mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_str_copy(type, NULL, str_len)); o->data = str_data; o->hash = str_hash; From 31550a52e4b9ff5797755b54c415e365ab1d64d7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 6 Oct 2017 10:57:51 +1100 Subject: [PATCH 029/828] docs/library/network: Enhance AbstractNIC.status to take an argument. The argument is optional and if given should be a string naming the status variable to query. --- docs/library/network.rst | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/library/network.rst b/docs/library/network.rst index 99a7c242c..a1190a574 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -98,10 +98,20 @@ parameter should be `id`. duration and other parameters. Where possible, parameter names should match those in connect(). - .. method:: status() + .. method:: status([param]) - Return detailed status of the interface, values are dependent - on the network medium/technology. + Query dynamic status information of the interface. When called with no + argument the return value describes the network link status. Otherwise + *param* should be a string naming the particular status parameter to + retrieve. + + The return types and values are dependent on the network + medium/technology. Some of the parameters that may be supported are: + + * WiFi STA: use ``'rssi'`` to retrieve the RSSI of the AP signal + * WiFi AP: use ``'stations'`` to retrieve a list of all the STAs + connected to the AP. The list contains tuples of the form + (MAC, RSSI). .. method:: ifconfig([(ip, subnet, gateway, dns)]) @@ -118,7 +128,7 @@ parameter should be `id`. Get or set general network interface parameters. These methods allow to work with additional parameters beyond standard IP configuration (as dealt with by `ifconfig()`). These include network-specific and hardware-specific - parameters and status values. For setting parameters, the keyword argument + parameters. For setting parameters, the keyword argument syntax should be used, and multiple parameters can be set at once. For querying, a parameter name should be quoted as a string, and only one parameter can be queried at a time:: @@ -128,8 +138,6 @@ parameter should be `id`. # Query params one by one print(ap.config('essid')) print(ap.config('channel')) - # Extended status information also available this way - print(sta.config('rssi')) .. only:: port_pyboard From ccaa5f5b0bad69f0f0ea81151e34c218fe13ce66 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sun, 15 Oct 2017 18:26:58 +0100 Subject: [PATCH 030/828] drivers/nrf24l01: Make driver and test run on pyboard, ESP8266, ESP32. --- drivers/nrf24l01/nrf24l01.py | 9 +++--- drivers/nrf24l01/nrf24l01test.py | 53 +++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/drivers/nrf24l01/nrf24l01.py b/drivers/nrf24l01/nrf24l01.py index 7274a7927..a95d2b5ca 100644 --- a/drivers/nrf24l01/nrf24l01.py +++ b/drivers/nrf24l01/nrf24l01.py @@ -62,12 +62,11 @@ class NRF24L01: # init the SPI bus and pins self.init_spi(4000000) - cs.init(cs.OUT, value=1) - ce.init(ce.OUT, value=0) # reset everything - self.ce(0) - self.cs(1) + ce.init(ce.OUT, value=0) + cs.init(cs.OUT, value=1) + self.payload_size = payload_size self.pipe0_read_addr = None utime.sleep_ms(5) @@ -215,7 +214,7 @@ class NRF24L01: # blocking wait for tx complete def send(self, buf, timeout=500): - send_nonblock = self.send_start(buf) + self.send_start(buf) start = utime.ticks_ms() result = None while result is None and utime.ticks_diff(utime.ticks_ms(), start) < timeout: diff --git a/drivers/nrf24l01/nrf24l01test.py b/drivers/nrf24l01/nrf24l01test.py index 5413511c3..876b2bbfa 100644 --- a/drivers/nrf24l01/nrf24l01test.py +++ b/drivers/nrf24l01/nrf24l01test.py @@ -1,14 +1,38 @@ -"""Test for nrf24l01 module.""" +"""Test for nrf24l01 module. Portable between MicroPython targets.""" -import struct +import sys +import ustruct as struct import utime from machine import Pin, SPI from nrf24l01 import NRF24L01 +from micropython import const + +# Slave pause between receiving data and checking for further packets. +_RX_POLL_DELAY = const(15) +# Slave pauses an additional _SLAVE_SEND_DELAY ms after receiving data and before +# transmitting to allow the (remote) master time to get into receive mode. The +# master may be a slow device. Value tested with Pyboard, ESP32 and ESP8266. +_SLAVE_SEND_DELAY = const(10) + +if sys.platform == 'pyboard': + cfg = {'spi': 2, 'miso': 'Y7', 'mosi': 'Y8', 'sck': 'Y6', 'csn': 'Y5', 'ce': 'Y4'} +elif sys.platform == 'esp8266': # Hardware SPI + cfg = {'spi': 1, 'miso': 12, 'mosi': 13, 'sck': 14, 'csn': 4, 'ce': 5} +elif sys.platform == 'esp32': # Software SPI + cfg = {'spi': -1, 'miso': 32, 'mosi': 33, 'sck': 25, 'csn': 26, 'ce': 27} +else: + raise ValueError('Unsupported platform {}'.format(sys.platform)) pipes = (b'\xf0\xf0\xf0\xf0\xe1', b'\xf0\xf0\xf0\xf0\xd2') def master(): - nrf = NRF24L01(SPI(2), Pin('Y5'), Pin('Y4'), payload_size=8) + csn = Pin(cfg['csn'], mode=Pin.OUT, value=1) + ce = Pin(cfg['ce'], mode=Pin.OUT, value=0) + if cfg['spi'] == -1: + spi = SPI(-1, sck=Pin(cfg['sck']), mosi=Pin(cfg['mosi']), miso=Pin(cfg['miso'])) + nrf = NRF24L01(spi, csn, ce, payload_size=8) + else: + nrf = NRF24L01(SPI(cfg['spi']), csn, ce, payload_size=8) nrf.open_tx_pipe(pipes[0]) nrf.open_rx_pipe(1, pipes[1]) @@ -60,7 +84,13 @@ def master(): print('master finished sending; successes=%d, failures=%d' % (num_successes, num_failures)) def slave(): - nrf = NRF24L01(SPI(2), Pin('Y5'), Pin('Y4'), payload_size=8) + csn = Pin(cfg['csn'], mode=Pin.OUT, value=1) + ce = Pin(cfg['ce'], mode=Pin.OUT, value=0) + if cfg['spi'] == -1: + spi = SPI(-1, sck=Pin(cfg['sck']), mosi=Pin(cfg['mosi']), miso=Pin(cfg['miso'])) + nrf = NRF24L01(spi, csn, ce, payload_size=8) + else: + nrf = NRF24L01(SPI(cfg['spi']), csn, ce, payload_size=8) nrf.open_tx_pipe(pipes[1]) nrf.open_rx_pipe(1, pipes[0]) @@ -69,7 +99,6 @@ def slave(): print('NRF24L01 slave mode, waiting for packets... (ctrl-C to stop)') while True: - machine.idle() if nrf.any(): while nrf.any(): buf = nrf.recv() @@ -81,8 +110,10 @@ def slave(): else: led.off() led_state >>= 1 - utime.sleep_ms(15) + utime.sleep_ms(_RX_POLL_DELAY) + # Give master time to get into receive mode. + utime.sleep_ms(_SLAVE_SEND_DELAY) nrf.stop_listening() try: nrf.send(struct.pack('i', millis)) @@ -99,9 +130,9 @@ except: print('NRF24L01 test module loaded') print('NRF24L01 pinout for test:') -print(' CE on Y4') -print(' CSN on Y5') -print(' SCK on Y6') -print(' MISO on Y7') -print(' MOSI on Y8') +print(' CE on', cfg['ce']) +print(' CSN on', cfg['csn']) +print(' SCK on', cfg['sck']) +print(' MISO on', cfg['miso']) +print(' MOSI on', cfg['mosi']) print('run nrf24l01test.slave() on slave, then nrf24l01test.master() on master') From 12ad64bc554fe33772c492efc55eb9290463048a Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 16 Nov 2017 16:01:47 +1100 Subject: [PATCH 031/828] extmod/vfs_fat: Mount FatFS on creation so VFS methods can be used. It's possible to use the methods (eg ilistdir) of a VFS FatFS object without it being mounted in the VFS itself. This previously worked but only because FatFS was "mounting" the filesystem automatically when any function (eg f_opendir) was called. But it didn't work for ports that used synchronisation objects (_FS_REENTRANT) because they are only initialised via a call to f_mount. So, call f_mount explicitly when creating a new FatFS object so that everything is set up correctly. Then also provide a finaliser to do the f_umount call, but only if synchronisation objects are enabled (since otherwise the f_umount call does nothing). --- extmod/vfs_fat.c | 34 ++++++++++++++++++++++++++-------- extmod/vfs_fat.h | 1 + 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 9942ddeb5..0076df262 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -69,9 +69,28 @@ STATIC mp_obj_t fat_vfs_make_new(const mp_obj_type_t *type, size_t n_args, size_ mp_load_method(args[0], MP_QSTR_count, vfs->u.old.count); } + // mount the block device so the VFS methods can be used + FRESULT res = f_mount(&vfs->fatfs); + if (res == FR_NO_FILESYSTEM) { + // don't error out if no filesystem, to let mkfs()/mount() create one if wanted + vfs->flags |= FSUSER_NO_FILESYSTEM; + } else if (res != FR_OK) { + mp_raise_OSError(fresult_to_errno_table[res]); + } + return MP_OBJ_FROM_PTR(vfs); } +#if _FS_REENTRANT +STATIC mp_obj_t fat_vfs_del(mp_obj_t self_in) { + mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(self_in); + // f_umount only needs to be called to release the sync object + f_umount(&self->fatfs); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_del_obj, fat_vfs_del); +#endif + STATIC mp_obj_t fat_vfs_mkfs(mp_obj_t bdev_in) { // create new object fs_user_mount_t *vfs = MP_OBJ_TO_PTR(fat_vfs_make_new(&mp_fat_vfs_type, 1, 0, &bdev_in)); @@ -291,10 +310,8 @@ STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs self->writeblocks[0] = MP_OBJ_NULL; } - // mount the block device - FRESULT res = f_mount(&self->fatfs); - // check if we need to make the filesystem + FRESULT res = (self->flags & FSUSER_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK; if (res == FR_NO_FILESYSTEM && mp_obj_is_true(mkfs)) { uint8_t working_buf[_MAX_SS]; res = f_mkfs(&self->fatfs, FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf)); @@ -302,22 +319,23 @@ STATIC mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs if (res != FR_OK) { mp_raise_OSError(fresult_to_errno_table[res]); } + self->flags &= ~FSUSER_NO_FILESYSTEM; return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_3(vfs_fat_mount_obj, vfs_fat_mount); STATIC mp_obj_t vfs_fat_umount(mp_obj_t self_in) { - fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); - FRESULT res = f_umount(&self->fatfs); - if (res != FR_OK) { - mp_raise_OSError(fresult_to_errno_table[res]); - } + (void)self_in; + // keep the FAT filesystem mounted internally so the VFS methods can still be used return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount); STATIC const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { + #if _FS_REENTRANT + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&fat_vfs_del_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_mkfs), MP_ROM_PTR(&fat_vfs_mkfs_obj) }, { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&fat_vfs_open_obj) }, { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&fat_vfs_ilistdir_obj) }, diff --git a/extmod/vfs_fat.h b/extmod/vfs_fat.h index 443e4eda8..688452973 100644 --- a/extmod/vfs_fat.h +++ b/extmod/vfs_fat.h @@ -35,6 +35,7 @@ #define FSUSER_NATIVE (0x0001) // readblocks[2]/writeblocks[2] contain native func #define FSUSER_FREE_OBJ (0x0002) // fs_user_mount_t obj should be freed on umount #define FSUSER_HAVE_IOCTL (0x0004) // new protocol with ioctl +#define FSUSER_NO_FILESYSTEM (0x0008) // the block device has no filesystem on it typedef struct _fs_user_mount_t { mp_obj_base_t base; From bbac2df0cfde1a8ff9d203decf0ecf5a443a2eb7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Nov 2017 14:19:12 +1100 Subject: [PATCH 032/828] stm32/boards/stm32f746_af.csv: Fix typos in AF table. --- ports/stm32/boards/stm32f746_af.csv | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ports/stm32/boards/stm32f746_af.csv b/ports/stm32/boards/stm32f746_af.csv index eabc9ab3b..8069edc7b 100644 --- a/ports/stm32/boards/stm32f746_af.csv +++ b/ports/stm32/boards/stm32f746_af.csv @@ -5,7 +5,7 @@ PortA,PA1,,TIM2_CH2,TIM5_CH2,,,,,USART2_RTS,UART4_RX,QUADSPI_BK1_IO3,SAI2_MCK_B, PortA,PA2,,TIM2_CH3,TIM5_CH3,TIM9_CH1,,,,USART2_TX,SAI2_SCK_B,,,ETH_MDIO,,,LCD_R1,EVENTOUT PortA,PA3,,TIM2_CH4,TIM5_CH4,TIM9_CH2,,,,USART2_RX,,,OTG_HS_ULPI_D0,ETH_MII_COL,,,LCD_B5,EVENTOUT PortA,PA4,,,,,,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,USART2_CK,,,,,OTG_HS_SOF,DCMI_HSYNC,LCD_VSYNC,EVENTOUT -PortA,PA5,,TIM2_CH1/TIM2_ETR,TIM8_CH1N,SPI1_SCK/I2S1_CK,,,,,,,OTG_HS_ULPI_CK,,,,LCD_R4,EVENTOUT +PortA,PA5,,TIM2_CH1/TIM2_ETR,,TIM8_CH1N,,SPI1_SCK/I2S1_CK,,,,,OTG_HS_ULPI_CK,,,,LCD_R4,EVENTOUT PortA,PA6,,TIM1_BKIN,TIM3_CH1,TIM8_BKIN,,SPI1_MISO,,,,TIM13_CH1,,,,DCMI_PIXCLK,LCD_G2,EVENTOUT PortA,PA7,,TIM1_CH1N,TIM3_CH2,TIM8_CH1N,,SPI1_MOSI/I2S1_SD,,,,TIM14_CH1,,ETH_MII_RX_DV/ETH_RMII_CRS_DV,FMC_SDNWE,,,EVENTOUT PortA,PA8,MCO1,TIM1_CH1,,TIM8_BKIN2,I2C3_SCL,,,USART1_CK,,,OTG_FS_SOF,,,,LCD_R6,EVENTOUT @@ -13,16 +13,16 @@ PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,SPI2_SCK/I2S2_CK,,USART1_TX,,,,,,DCMI_D0,,EVENTO PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,,,DCMI_D1,,EVENTOUT PortA,PA11,,TIM1_CH4,,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,,,LCD_R4,EVENTOUT PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS,SAI2_FS_B,CAN1_TX,OTG_FS_DP,,,,LCD_R5,EVENTOUT -PortA,PA13,JTMS,SWDIO,,,,,,,,,,,,,,EVENTOUT -PortA,PA14,JTCK,SWCLK,,,,,,,,,,,,,,EVENTOUT -PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,HDMICE,CSPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,,UART4_RTS,,,,,,,EVENTOUT -PortB,PB0,,TIM1_CH2N,TIM3_CH3T,IM8_CH2N,,,,,UART4_CTS,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT -PortB,PB1,,TIM1_CH3N,TIM3_CH4T,IM8_CH3N,,,,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT +PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,HDMI_CEC,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,,UART4_RTS,,,,,,,EVENTOUT +PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,,,,,UART4_CTS,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT +PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,,,,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT PortB,PB2,,,,,,,SAI1_SD_A,SPI3_MOSI/I2S3_SD,,QUADSPI_CLK,,,,,,EVENTOUT PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK/I2S1_CK,SPI3_SCK/I2S3_CK,,,,,,,,,EVENTOUT PortB,PB4,NJTRST,,TIM3_CH1,,,SPI1_MISO,SPI3_MISO,SPI2_NSS/I2S2_WS,,,,,,,,EVENTOUT PortB,PB5,,,TIM3_CH2,,I2C1_SMBA,SPI1_MOSI/I2S1_SD,SPI3_MOSI/I2S3_SD,,,CAN2_RX,OTG_HS_ULPI_D7,ETH_PPS_OUT,FMC_SDCKE1,DCMI_D10,,EVENTOUT -PortB,PB6,,,TIM4_CH1,HDMICEC,I2C1_SCL,,,USART1_TX,,CAN2_TX,QUADSPI_BK1_NCS,,FMC_SDNE1,DCMI_D5,,EVENTOUT +PortB,PB6,,,TIM4_CH1,HDMI_CEC,I2C1_SCL,,,USART1_TX,,CAN2_TX,QUADSPI_BK1_NCS,,FMC_SDNE1,DCMI_D5,,EVENTOUT PortB,PB7,,,TIM4_CH2,,I2C1_SDA,,,USART1_RX,,,,,FMC_NL,DCMI_VSYNC,,EVENTOUT PortB,PB8,,,TIM4_CH3,TIM10_CH1,I2C1_SCL,,,,,CAN1_RX,,ETH_MII_TXD3,SDMMC1_D4,DCMI_D6,LCD_B6,EVENTOUT PortB,PB9,,,TIM4_CH4,TIM11_CH1,I2C1_SDA,SPI2_NSS/I2S2_WS,,,,CAN1_TX,,,SDMMC1_D5,DCMI_D7,LCD_B7,EVENTOUT @@ -168,4 +168,3 @@ PortK,PK4,,,,,,,,,,,,,,,LCD_B5,EVENTOUT PortK,PK5,,,,,,,,,,,,,,,LCD_B6,EVENTOUT PortK,PK6,,,,,,,,,,,,,,,LCD_B7,EVENTOUT PortK,PK7,,,,,,,,,,,,,,,LCD_DE,EVENTOUT - From 3e9e9b07bac2d09b0db812d3b31fb6c1af3d5c95 Mon Sep 17 00:00:00 2001 From: Jaroslav Sykora Date: Sun, 19 Nov 2017 15:27:31 +0100 Subject: [PATCH 033/828] stm32/boards: Add support for NUCLEO-F746ZG evaluation board. This is a low-cost evaluation kit board from ST based on the STM32 Nucleo-144 form factor. It uses the STM32F746ZG MCU in the LQFP144 package. The MCU has 1MB of flash and 320kB of System RAM. Cortex-M7 runs at up to 216MHz. --- .../boards/NUCLEO_F746ZG/mpconfigboard.h | 76 ++++ .../boards/NUCLEO_F746ZG/mpconfigboard.mk | 4 + ports/stm32/boards/NUCLEO_F746ZG/pins.csv | 68 +++ .../boards/NUCLEO_F746ZG/stm32f7xx_hal_conf.h | 429 ++++++++++++++++++ 4 files changed, 577 insertions(+) create mode 100644 ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h create mode 100644 ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk create mode 100644 ports/stm32/boards/NUCLEO_F746ZG/pins.csv create mode 100644 ports/stm32/boards/NUCLEO_F746ZG/stm32f7xx_hal_conf.h diff --git a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h new file mode 100644 index 000000000..c5b84938c --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.h @@ -0,0 +1,76 @@ +// This board is only confirmed to operate using DFU mode and openocd. +// DFU mode can be accessed by setting BOOT0 (see schematics) +// To use openocd run "OPENOCD_CONFIG=boards/openocd_stm32f7.cfg" in +// the make command. + +#define MICROPY_HW_BOARD_NAME "NUCLEO-F746ZG" +#define MICROPY_HW_MCU_NAME "STM32F746" + +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_HAS_SDCARD (0) +#define MICROPY_HW_HAS_MMA7660 (0) +#define MICROPY_HW_HAS_LIS3DSH (0) +#define MICROPY_HW_HAS_LCD (0) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_TIMER (1) +#define MICROPY_HW_ENABLE_SERVO (0) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_HW_ENABLE_CAN (1) + +// HSE is 8MHz +// VCOClock = HSE * PLLN / PLLM = 8 MHz * 216 / 4 = 432 MHz +// SYSCLK = VCOClock / PLLP = 432 MHz / 2 = 216 MHz +// USB/SDMMC/RNG Clock = VCOClock / PLLQ = 432 MHz / 9 = 48 MHz +#define MICROPY_HW_CLK_PLLM (4) +#define MICROPY_HW_CLK_PLLN (216) +#define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) +#define MICROPY_HW_CLK_PLLQ (9) + +// From the reference manual, for 2.7V to 3.6V +// 151-180 MHz => 5 wait states +// 181-210 MHz => 6 wait states +// 211-216 MHz => 7 wait states +#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_7 // 210-216 MHz needs 7 wait states + +// UART config +#define MICROPY_HW_UART2_TX (pin_D5) +#define MICROPY_HW_UART2_RX (pin_D6) +#define MICROPY_HW_UART2_RTS (pin_D4) +#define MICROPY_HW_UART2_CTS (pin_D3) +#define MICROPY_HW_UART3_TX (pin_D8) +#define MICROPY_HW_UART3_RX (pin_D9) +#define MICROPY_HW_UART6_TX (pin_G14) +#define MICROPY_HW_UART6_RX (pin_G9) +#define MICROPY_HW_UART_REPL PYB_UART_3 +#define MICROPY_HW_UART_REPL_BAUD 115200 + +// I2C busses +#define MICROPY_HW_I2C1_SCL (pin_B8) +#define MICROPY_HW_I2C1_SDA (pin_B9) +#define MICROPY_HW_I2C3_SCL (pin_H7) +#define MICROPY_HW_I2C3_SDA (pin_H8) + +// SPI +#define MICROPY_HW_SPI3_NSS (pin_A4) +#define MICROPY_HW_SPI3_SCK (pin_B3) +#define MICROPY_HW_SPI3_MISO (pin_B4) +#define MICROPY_HW_SPI3_MOSI (pin_B5) + +// USRSW is pulled low. Pressing the button makes the input go high. +#define MICROPY_HW_USRSW_PIN (pin_C13) +#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING) +#define MICROPY_HW_USRSW_PRESSED (1) + +// LEDs +#define MICROPY_HW_LED1 (pin_B0) // green +#define MICROPY_HW_LED2 (pin_B7) // blue +#define MICROPY_HW_LED3 (pin_B14) // 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 (CN13 - USB OTG FS) +#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) +#define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) diff --git a/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk new file mode 100644 index 000000000..7c6bc4584 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F746ZG/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = f7 +CMSIS_MCU = STM32F746xx +AF_FILE = boards/stm32f746_af.csv +LD_FILE = boards/stm32f746.ld diff --git a/ports/stm32/boards/NUCLEO_F746ZG/pins.csv b/ports/stm32/boards/NUCLEO_F746ZG/pins.csv new file mode 100644 index 000000000..897b1473e --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F746ZG/pins.csv @@ -0,0 +1,68 @@ +A0,PA3 +A1,PC0 +A2,PC3 +A3,PF3 +A4,PF5 +A5,PF10 +D0,PG9 +D1,PG14 +D2,PF15 +D3,PE13 +D4,PF14 +D5,PE11 +D6,PE9 +D7,PF13 +D8,PF12 +D9,PD15 +D10,PD14 +D11,PA7 +D12,PA6 +D13,PA5 +D14,PB9 +D15,PB8 +D16,PC6 +D17,PB15 +D18,PB13 +D19,PB12 +D20,PA15 +D21,PC7 +D22,PB5 +D23,PB3 +D24,PA4 +D25,PB4 +LED1,PB0 +LED2,PB7 +LED3,PB14 +SW,PC13 +TP1,PH2 +TP2,PI8 +TP3,PH15 +AUDIO_INT,PD6 +AUDIO_SDA,PH8 +AUDIO_SCL,PH7 +EXT_SDA,PB9 +EXT_SCL,PB8 +EXT_RST,PG3 +SD_SW,PC13 +LCD_BL_CTRL,PK3 +LCD_INT,PI13 +LCD_SDA,PH8 +LCD_SCL,PH7 +OTG_FS_POWER,PD5 +OTG_FS_OVER_CURRENT,PD4 +OTG_HS_OVER_CURRENT,PE3 +USB_VBUS,PJ12 +USB_ID,PA10 +USB_DM,PA11 +USB_DP,PA12 +VCP_TX,PD8 +VCP_RX,PD9 +UART2_TX,PD5 +UART2_RX,PD6 +UART2_RTS,PD4 +UART2_CTS,PD3 +UART6_TX,PG14 +UART6_RX,PG9 +SPI_B_NSS,PA4 +SPI_B_SCK,PB3 +SPI_B_MOSI,PB5 diff --git a/ports/stm32/boards/NUCLEO_F746ZG/stm32f7xx_hal_conf.h b/ports/stm32/boards/NUCLEO_F746ZG/stm32f7xx_hal_conf.h new file mode 100644 index 000000000..e1aa4578d --- /dev/null +++ b/ports/stm32/boards/NUCLEO_F746ZG/stm32f7xx_hal_conf.h @@ -0,0 +1,429 @@ +/** + ****************************************************************************** + * @file stm32f7xx_hal_conf.h + * @author MCD Application Team + * @version V1.0.1 + * @date 25-June-2015 + * @brief HAL configuration file. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2015 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F7xx_HAL_CONF_H +#define __STM32F7xx_HAL_CONF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ + +#define USE_USB_FS + +/* ########################## Module Selection ############################## */ +/** + * @brief This is the list of modules to be used in the HAL driver + */ +#define HAL_MODULE_ENABLED +#define HAL_ADC_MODULE_ENABLED +#define HAL_CAN_MODULE_ENABLED +/* #define HAL_CEC_MODULE_ENABLED */ +/* #define HAL_CRC_MODULE_ENABLED */ +/* #define HAL_CRYP_MODULE_ENABLED */ +#define HAL_DAC_MODULE_ENABLED +/* #define HAL_DCMI_MODULE_ENABLED */ +#define HAL_DMA_MODULE_ENABLED +/* #define HAL_DMA2D_MODULE_ENABLED */ +/* #define HAL_ETH_MODULE_ENABLED */ +#define HAL_FLASH_MODULE_ENABLED +/* #define HAL_NAND_MODULE_ENABLED */ +/* #define HAL_NOR_MODULE_ENABLED */ +/* #define HAL_SRAM_MODULE_ENABLED */ +/* #define HAL_SDRAM_MODULE_ENABLED */ +/* #define HAL_HASH_MODULE_ENABLED */ +#define HAL_GPIO_MODULE_ENABLED +#define HAL_I2C_MODULE_ENABLED +#define HAL_I2S_MODULE_ENABLED +/* #define HAL_IWDG_MODULE_ENABLED */ +/* #define HAL_LPTIM_MODULE_ENABLED */ +/* #define HAL_LTDC_MODULE_ENABLED */ +#define HAL_PWR_MODULE_ENABLED +/* #define HAL_QSPI_MODULE_ENABLED */ +#define HAL_RCC_MODULE_ENABLED +#define HAL_RNG_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +/* #define HAL_SAI_MODULE_ENABLED */ +#define HAL_SD_MODULE_ENABLED +/* #define HAL_SPDIFRX_MODULE_ENABLED */ +#define HAL_SPI_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +/* #define HAL_USART_MODULE_ENABLED */ +/* #define HAL_IRDA_MODULE_ENABLED */ +/* #define HAL_SMARTCARD_MODULE_ENABLED */ +/* #define HAL_WWDG_MODULE_ENABLED */ +#define HAL_CORTEX_MODULE_ENABLED +#define HAL_PCD_MODULE_ENABLED +/* #define HAL_HCD_MODULE_ENABLED */ + + +/* ########################## Timeout Configuration ######################### */ +/** + * @brief This is the HAL configuration section + */ +#define HAL_ACCURATE_TIMEOUT_ENABLED 0 +#define HAL_TIMEOUT_VALUE 0x1FFFFFF + +/* ########################## HSE/HSI Values adaptation ##################### */ +/** + * @brief Adjust the value of External High Speed oscillator (HSE) used in your application. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSE is used as system clock source, directly or through the PLL). + */ +#if !defined (HSE_VALUE) + #define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */ +#endif /* HSE_VALUE */ + +#if !defined (HSE_STARTUP_TIMEOUT) + #define HSE_STARTUP_TIMEOUT ((uint32_t)5000) /*!< Time out for HSE start up, in ms */ +#endif /* HSE_STARTUP_TIMEOUT */ + +/** + * @brief Internal High Speed oscillator (HSI) value. + * This value is used by the RCC HAL module to compute the system frequency + * (when HSI is used as system clock source, directly or through the PLL). + */ +#if !defined (HSI_VALUE) + #define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/ +#endif /* HSI_VALUE */ + +/** + * @brief Internal Low Speed oscillator (LSI) value. + */ +#if !defined (LSI_VALUE) + #define LSI_VALUE ((uint32_t)32000) +#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz + The real value may vary depending on the variations + in voltage and temperature. */ +/** + * @brief External Low Speed oscillator (LSE) value. + */ +#if !defined (LSE_VALUE) + #define LSE_VALUE ((uint32_t)32768) /*!< Value of the External Low Speed oscillator in Hz */ +#endif /* LSE_VALUE */ + +#if !defined (LSE_STARTUP_TIMEOUT) + #define LSE_STARTUP_TIMEOUT ((uint32_t)5000U) /*!< Time out for LSE start up, in ms */ +#endif /* LSE_STARTUP_TIMEOUT */ + +/** + * @brief External clock source for I2S peripheral + * This value is used by the I2S HAL module to compute the I2S clock source + * frequency, this source is inserted directly through I2S_CKIN pad. + */ +#if !defined (EXTERNAL_CLOCK_VALUE) + #define EXTERNAL_CLOCK_VALUE ((uint32_t)12288000) /*!< Value of the Internal oscillator in Hz*/ +#endif /* EXTERNAL_CLOCK_VALUE */ + +/* Tip: To avoid modifying this file each time you need to use different HSE, + === you can define the HSE value in your toolchain compiler preprocessor. */ + +/* ########################### System Configuration ######################### */ +/** + * @brief This is the HAL system configuration section + */ +#define VDD_VALUE ((uint32_t)3300) /*!< Value of VDD in mv */ +#define TICK_INT_PRIORITY ((uint32_t)0x00) /*!< tick interrupt priority */ +#define USE_RTOS 0 +#define ART_ACCLERATOR_ENABLE 1 /* To enable instruction cache and prefetch */ + +/* ########################## Assert Selection ############################## */ +/** + * @brief Uncomment the line below to expanse the "assert_param" macro in the + * HAL drivers code + */ +/* #define USE_FULL_ASSERT 1 */ + +/* ################## Ethernet peripheral configuration ##################### */ + +/* Section 1 : Ethernet peripheral configuration */ + +/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */ +#define MAC_ADDR0 2 +#define MAC_ADDR1 1 +#define MAC_ADDR2 0 +#define MAC_ADDR3 0 +#define MAC_ADDR4 0 +#define MAC_ADDR5 0 + +/* Definition of the Ethernet driver buffers size and count */ +#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */ +#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */ +#define ETH_RXBUFNB ((uint32_t)5) /* 5 Rx buffers of size ETH_RX_BUF_SIZE */ +#define ETH_TXBUFNB ((uint32_t)5) /* 5 Tx buffers of size ETH_TX_BUF_SIZE */ + +/* Section 2: PHY configuration section */ +/* LAN8742A PHY Address*/ +#define LAN8742A_PHY_ADDRESS 0x00 +/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/ +#define PHY_RESET_DELAY ((uint32_t)0x00000FFF) +/* PHY Configuration delay */ +#define PHY_CONFIG_DELAY ((uint32_t)0x00000FFFF) + +#define PHY_READ_TO ((uint32_t)0x0000FFFF) +#define PHY_WRITE_TO ((uint32_t)0x0000FFFF) + +/* Section 3: Common PHY Registers */ + +#define PHY_BCR ((uint16_t)0x00) /*!< Transceiver Basic Control Register */ +#define PHY_BSR ((uint16_t)0x01) /*!< Transceiver Basic Status Register */ + +#define PHY_RESET ((uint16_t)0x8000) /*!< PHY Reset */ +#define PHY_LOOPBACK ((uint16_t)0x4000) /*!< Select loop-back mode */ +#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100) /*!< Set the full-duplex mode at 100 Mb/s */ +#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000) /*!< Set the half-duplex mode at 100 Mb/s */ +#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100) /*!< Set the full-duplex mode at 10 Mb/s */ +#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000) /*!< Set the half-duplex mode at 10 Mb/s */ +#define PHY_AUTONEGOTIATION ((uint16_t)0x1000) /*!< Enable auto-negotiation function */ +#define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200) /*!< Restart auto-negotiation function */ +#define PHY_POWERDOWN ((uint16_t)0x0800) /*!< Select the power down mode */ +#define PHY_ISOLATE ((uint16_t)0x0400) /*!< Isolate PHY from MII */ + +#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */ +#define PHY_LINKED_STATUS ((uint16_t)0x0004) /*!< Valid link established */ +#define PHY_JABBER_DETECTION ((uint16_t)0x0002) /*!< Jabber condition detected */ + +/* Section 4: Extended PHY Registers */ + +#define PHY_SR ((uint16_t)0x10) /*!< PHY status register Offset */ +#define PHY_MICR ((uint16_t)0x11) /*!< MII Interrupt Control Register */ +#define PHY_MISR ((uint16_t)0x12) /*!< MII Interrupt Status and Misc. Control Register */ + +#define PHY_LINK_STATUS ((uint16_t)0x0001) /*!< PHY Link mask */ +#define PHY_SPEED_STATUS ((uint16_t)0x0002) /*!< PHY Speed mask */ +#define PHY_DUPLEX_STATUS ((uint16_t)0x0004) /*!< PHY Duplex mask */ + +#define PHY_MICR_INT_EN ((uint16_t)0x0002) /*!< PHY Enable interrupts */ +#define PHY_MICR_INT_OE ((uint16_t)0x0001) /*!< PHY Enable output interrupt events */ + +#define PHY_MISR_LINK_INT_EN ((uint16_t)0x0020) /*!< Enable Interrupt on change of link status */ +#define PHY_LINK_INTERRUPT ((uint16_t)0x2000) /*!< PHY link status interrupt mask */ + +/* Includes ------------------------------------------------------------------*/ +/** + * @brief Include module's header file + */ + +#ifdef HAL_RCC_MODULE_ENABLED + #include "stm32f7xx_hal_rcc.h" +#endif /* HAL_RCC_MODULE_ENABLED */ + +#ifdef HAL_GPIO_MODULE_ENABLED + #include "stm32f7xx_hal_gpio.h" +#endif /* HAL_GPIO_MODULE_ENABLED */ + +#ifdef HAL_DMA_MODULE_ENABLED + #include "stm32f7xx_hal_dma.h" +#endif /* HAL_DMA_MODULE_ENABLED */ + +#ifdef HAL_CORTEX_MODULE_ENABLED + #include "stm32f7xx_hal_cortex.h" +#endif /* HAL_CORTEX_MODULE_ENABLED */ + +#ifdef HAL_ADC_MODULE_ENABLED + #include "stm32f7xx_hal_adc.h" +#endif /* HAL_ADC_MODULE_ENABLED */ + +#ifdef HAL_CAN_MODULE_ENABLED + #include "stm32f7xx_hal_can.h" +#endif /* HAL_CAN_MODULE_ENABLED */ + +#ifdef HAL_CEC_MODULE_ENABLED + #include "stm32f7xx_hal_cec.h" +#endif /* HAL_CEC_MODULE_ENABLED */ + +#ifdef HAL_CRC_MODULE_ENABLED + #include "stm32f7xx_hal_crc.h" +#endif /* HAL_CRC_MODULE_ENABLED */ + +#ifdef HAL_CRYP_MODULE_ENABLED + #include "stm32f7xx_hal_cryp.h" +#endif /* HAL_CRYP_MODULE_ENABLED */ + +#ifdef HAL_DMA2D_MODULE_ENABLED + #include "stm32f7xx_hal_dma2d.h" +#endif /* HAL_DMA2D_MODULE_ENABLED */ + +#ifdef HAL_DAC_MODULE_ENABLED + #include "stm32f7xx_hal_dac.h" +#endif /* HAL_DAC_MODULE_ENABLED */ + +#ifdef HAL_DCMI_MODULE_ENABLED + #include "stm32f7xx_hal_dcmi.h" +#endif /* HAL_DCMI_MODULE_ENABLED */ + +#ifdef HAL_ETH_MODULE_ENABLED + #include "stm32f7xx_hal_eth.h" +#endif /* HAL_ETH_MODULE_ENABLED */ + +#ifdef HAL_FLASH_MODULE_ENABLED + #include "stm32f7xx_hal_flash.h" +#endif /* HAL_FLASH_MODULE_ENABLED */ + +#ifdef HAL_SRAM_MODULE_ENABLED + #include "stm32f7xx_hal_sram.h" +#endif /* HAL_SRAM_MODULE_ENABLED */ + +#ifdef HAL_NOR_MODULE_ENABLED + #include "stm32f7xx_hal_nor.h" +#endif /* HAL_NOR_MODULE_ENABLED */ + +#ifdef HAL_NAND_MODULE_ENABLED + #include "stm32f7xx_hal_nand.h" +#endif /* HAL_NAND_MODULE_ENABLED */ + +#ifdef HAL_SDRAM_MODULE_ENABLED + #include "stm32f7xx_hal_sdram.h" +#endif /* HAL_SDRAM_MODULE_ENABLED */ + +#ifdef HAL_HASH_MODULE_ENABLED + #include "stm32f7xx_hal_hash.h" +#endif /* HAL_HASH_MODULE_ENABLED */ + +#ifdef HAL_I2C_MODULE_ENABLED + #include "stm32f7xx_hal_i2c.h" +#endif /* HAL_I2C_MODULE_ENABLED */ + +#ifdef HAL_I2S_MODULE_ENABLED + #include "stm32f7xx_hal_i2s.h" +#endif /* HAL_I2S_MODULE_ENABLED */ + +#ifdef HAL_IWDG_MODULE_ENABLED + #include "stm32f7xx_hal_iwdg.h" +#endif /* HAL_IWDG_MODULE_ENABLED */ + +#ifdef HAL_LPTIM_MODULE_ENABLED + #include "stm32f7xx_hal_lptim.h" +#endif /* HAL_LPTIM_MODULE_ENABLED */ + +#ifdef HAL_LTDC_MODULE_ENABLED + #include "stm32f7xx_hal_ltdc.h" +#endif /* HAL_LTDC_MODULE_ENABLED */ + +#ifdef HAL_PWR_MODULE_ENABLED + #include "stm32f7xx_hal_pwr.h" +#endif /* HAL_PWR_MODULE_ENABLED */ + +#ifdef HAL_QSPI_MODULE_ENABLED + #include "stm32f7xx_hal_qspi.h" +#endif /* HAL_QSPI_MODULE_ENABLED */ + +#ifdef HAL_RNG_MODULE_ENABLED + #include "stm32f7xx_hal_rng.h" +#endif /* HAL_RNG_MODULE_ENABLED */ + +#ifdef HAL_RTC_MODULE_ENABLED + #include "stm32f7xx_hal_rtc.h" +#endif /* HAL_RTC_MODULE_ENABLED */ + +#ifdef HAL_SAI_MODULE_ENABLED + #include "stm32f7xx_hal_sai.h" +#endif /* HAL_SAI_MODULE_ENABLED */ + +#ifdef HAL_SD_MODULE_ENABLED + #include "stm32f7xx_hal_sd.h" +#endif /* HAL_SD_MODULE_ENABLED */ + +#ifdef HAL_SPDIFRX_MODULE_ENABLED + #include "stm32f7xx_hal_spdifrx.h" +#endif /* HAL_SPDIFRX_MODULE_ENABLED */ + +#ifdef HAL_SPI_MODULE_ENABLED + #include "stm32f7xx_hal_spi.h" +#endif /* HAL_SPI_MODULE_ENABLED */ + +#ifdef HAL_TIM_MODULE_ENABLED + #include "stm32f7xx_hal_tim.h" +#endif /* HAL_TIM_MODULE_ENABLED */ + +#ifdef HAL_UART_MODULE_ENABLED + #include "stm32f7xx_hal_uart.h" +#endif /* HAL_UART_MODULE_ENABLED */ + +#ifdef HAL_USART_MODULE_ENABLED + #include "stm32f7xx_hal_usart.h" +#endif /* HAL_USART_MODULE_ENABLED */ + +#ifdef HAL_IRDA_MODULE_ENABLED + #include "stm32f7xx_hal_irda.h" +#endif /* HAL_IRDA_MODULE_ENABLED */ + +#ifdef HAL_SMARTCARD_MODULE_ENABLED + #include "stm32f7xx_hal_smartcard.h" +#endif /* HAL_SMARTCARD_MODULE_ENABLED */ + +#ifdef HAL_WWDG_MODULE_ENABLED + #include "stm32f7xx_hal_wwdg.h" +#endif /* HAL_WWDG_MODULE_ENABLED */ + +#ifdef HAL_PCD_MODULE_ENABLED + #include "stm32f7xx_hal_pcd.h" +#endif /* HAL_PCD_MODULE_ENABLED */ + +#ifdef HAL_HCD_MODULE_ENABLED + #include "stm32f7xx_hal_hcd.h" +#endif /* HAL_HCD_MODULE_ENABLED */ + +/* Exported macro ------------------------------------------------------------*/ +#ifdef USE_FULL_ASSERT +/** + * @brief The assert_param macro is used for function's parameters check. + * @param expr: If expr is false, it calls assert_failed function + * which reports the name of the source file and the source + * line number of the call that failed. + * If expr is true, it returns no value. + * @retval None + */ + #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) +/* Exported functions ------------------------------------------------------- */ + void assert_failed(uint8_t* file, uint32_t line); +#else + #define assert_param(expr) ((void)0) +#endif /* USE_FULL_ASSERT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F7xx_HAL_CONF_H */ + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ From 6906255dcdd8d8ff04efd1651c7a503a230a3523 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Nov 2017 15:25:28 +1100 Subject: [PATCH 034/828] stm32/boards: Remove obsolete and unused board-specific defines. These board-level macros have been completely replaced by feature-level config options. --- ports/stm32/boards/CERB40/mpconfigboard.h | 2 -- ports/stm32/boards/HYDRABUS/mpconfigboard.h | 2 -- ports/stm32/boards/OLIMEX_E407/mpconfigboard.h | 2 -- ports/stm32/boards/STM32F4DISC/mpconfigboard.h | 2 -- ports/stm32/boards/STM32F7DISC/mpconfigboard.h | 2 -- 5 files changed, 10 deletions(-) diff --git a/ports/stm32/boards/CERB40/mpconfigboard.h b/ports/stm32/boards/CERB40/mpconfigboard.h index 284de7a40..e668ba4e1 100644 --- a/ports/stm32/boards/CERB40/mpconfigboard.h +++ b/ports/stm32/boards/CERB40/mpconfigboard.h @@ -1,5 +1,3 @@ -#define CERB40 - #define MICROPY_HW_BOARD_NAME "Cerb40" #define MICROPY_HW_MCU_NAME "STM32F405RG" diff --git a/ports/stm32/boards/HYDRABUS/mpconfigboard.h b/ports/stm32/boards/HYDRABUS/mpconfigboard.h index 38fba9787..8eaa36747 100644 --- a/ports/stm32/boards/HYDRABUS/mpconfigboard.h +++ b/ports/stm32/boards/HYDRABUS/mpconfigboard.h @@ -1,5 +1,3 @@ -#define HYDRABUSV10 - #define MICROPY_HW_BOARD_NAME "HydraBus1.0" #define MICROPY_HW_MCU_NAME "STM32F4" diff --git a/ports/stm32/boards/OLIMEX_E407/mpconfigboard.h b/ports/stm32/boards/OLIMEX_E407/mpconfigboard.h index 5ede68264..86dfd1166 100644 --- a/ports/stm32/boards/OLIMEX_E407/mpconfigboard.h +++ b/ports/stm32/boards/OLIMEX_E407/mpconfigboard.h @@ -1,5 +1,3 @@ -#define STM32E407 - #define MICROPY_HW_BOARD_NAME "OLIMEX STM32-E407" #define MICROPY_HW_MCU_NAME "STM32F407" diff --git a/ports/stm32/boards/STM32F4DISC/mpconfigboard.h b/ports/stm32/boards/STM32F4DISC/mpconfigboard.h index 105875515..688a1ef62 100644 --- a/ports/stm32/boards/STM32F4DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F4DISC/mpconfigboard.h @@ -1,5 +1,3 @@ -#define STM32F4DISC - #define MICROPY_HW_BOARD_NAME "F4DISC" #define MICROPY_HW_MCU_NAME "STM32F407" diff --git a/ports/stm32/boards/STM32F7DISC/mpconfigboard.h b/ports/stm32/boards/STM32F7DISC/mpconfigboard.h index 44a39c0a1..41d49f64b 100644 --- a/ports/stm32/boards/STM32F7DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F7DISC/mpconfigboard.h @@ -1,5 +1,3 @@ -#define STM32F7DISC - #define MICROPY_HW_BOARD_NAME "F7DISC" #define MICROPY_HW_MCU_NAME "STM32F746" From 811ddcc65f9fb666228a9477139f4fceb31240dd Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Nov 2017 15:28:04 +1100 Subject: [PATCH 035/828] stm32/led: Remove unused LED enum constants. --- ports/stm32/led.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ports/stm32/led.h b/ports/stm32/led.h index f1b05d1e2..2c872e9c5 100644 --- a/ports/stm32/led.h +++ b/ports/stm32/led.h @@ -27,21 +27,10 @@ #define MICROPY_INCLUDED_STMHAL_LED_H typedef enum { - // PYBv3 - PYB_LED_R1 = 1, - PYB_LED_R2 = 2, - PYB_LED_G1 = 3, - PYB_LED_G2 = 4, - // PYBv4 PYB_LED_RED = 1, PYB_LED_GREEN = 2, PYB_LED_YELLOW = 3, PYB_LED_BLUE = 4, - //STM32F4DISC - PYB_LED_R = 1, - PYB_LED_G = 2, - PYB_LED_B = 3, - PYB_LED_O = 4, } pyb_led_t; void led_init(void); From da154fdaf933e6546ae4d6888af1a79a76d71b4c Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 1 Apr 2017 23:52:24 +1100 Subject: [PATCH 036/828] py: Add config option to disable multiple inheritance. This patch introduces a new compile-time config option to disable multiple inheritance at the Python level: MICROPY_MULTIPLE_INHERITANCE. It is enabled by default. Disabling multiple inheritance eliminates a lot of recursion in the call graph (which is important for some embedded systems), and can be used to reduce code size for ports that are really constrained (by around 200 bytes for Thumb2 archs). With multiple inheritance disabled all tests in the test-suite pass except those that explicitly test for multiple inheritance. --- py/mpconfig.h | 7 +++++++ py/objtype.c | 14 +++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 4209b32c6..095b7ef7b 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -666,6 +666,13 @@ typedef double mp_float_t; /*****************************************************************************/ /* Fine control over Python builtins, classes, modules, etc */ +// Whether to support multiple inheritance of Python classes. Multiple +// inheritance makes some C functions inherently recursive, and adds a bit of +// code overhead. +#ifndef MICROPY_MULTIPLE_INHERITANCE +#define MICROPY_MULTIPLE_INHERITANCE (1) +#endif + // Whether to implement attributes on functions #ifndef MICROPY_PY_FUNCTION_ATTRS #define MICROPY_PY_FUNCTION_ATTRS (0) diff --git a/py/objtype.c b/py/objtype.c index a8376a306..01d248256 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -67,6 +67,7 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t } else if (type->parent == NULL) { // No parents so end search here. return count; + #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) { // Multiple parents, search through them all recursively. const mp_obj_tuple_t *parent_tuple = type->parent; @@ -78,6 +79,7 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t count += instance_count_native_bases(bt, last_native_base); } return count; + #endif } else { // A single parent, use iteration to continue the search. type = type->parent; @@ -172,6 +174,7 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_ if (type->parent == NULL) { DEBUG_printf("mp_obj_class_lookup: No more parents\n"); return; + #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) { const mp_obj_tuple_t *parent_tuple = type->parent; const mp_obj_t *item = parent_tuple->items; @@ -192,6 +195,7 @@ STATIC void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_ // search last base (simple tail recursion elimination) assert(MP_OBJ_IS_TYPE(*item, &mp_type_type)); type = (mp_obj_type_t*)MP_OBJ_TO_PTR(*item); + #endif } else { type = type->parent; } @@ -247,7 +251,7 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(mp_obj_is_instance_type(self)); - const mp_obj_type_t *native_base; + const mp_obj_type_t *native_base = NULL; size_t num_native_bases = instance_count_native_bases(self, &native_base); assert(num_native_bases < 2); @@ -1022,7 +1026,11 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) o->protocol = ((mp_obj_type_t*)MP_OBJ_TO_PTR(bases_items[0]))->protocol; if (bases_len >= 2) { + #if MICROPY_MULTIPLE_INHERITANCE o->parent = MP_OBJ_TO_PTR(bases_tuple); + #else + mp_raise_NotImplementedError("multiple inheritance not supported"); + #endif } else { o->parent = MP_OBJ_TO_PTR(bases_items[0]); } @@ -1101,6 +1109,7 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (type->parent == NULL) { // no parents, do nothing + #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t*)type->parent)->type == &mp_type_tuple) { const mp_obj_tuple_t *parent_tuple = type->parent; size_t len = parent_tuple->len; @@ -1112,6 +1121,7 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { return; } } + #endif } else { mp_obj_class_lookup(&lookup, type->parent); if (dest[0] != MP_OBJ_NULL) { @@ -1158,6 +1168,7 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) { if (self->parent == NULL) { // type has no parents return false; + #if MICROPY_MULTIPLE_INHERITANCE } else if (((mp_obj_base_t*)self->parent)->type == &mp_type_tuple) { // get the base objects (they should be type objects) const mp_obj_tuple_t *parent_tuple = self->parent; @@ -1173,6 +1184,7 @@ bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo) { // search last base (simple tail recursion elimination) object = *item; + #endif } else { // type has 1 parent object = MP_OBJ_FROM_PTR(self->parent); From 8667a5f0534238e3144adc35d487deb0ac5be5d6 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 28 Oct 2017 18:37:30 +0300 Subject: [PATCH 037/828] py/objnamedtuple: Allow to reuse namedtuple basic functionality. By declaring interface in objnamedtuple.h and introducing a helper allocation function. --- py/objnamedtuple.c | 32 ++++++++++++++------------------ py/objnamedtuple.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 py/objnamedtuple.h diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index 94f02dd69..e7de899cf 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -30,20 +30,11 @@ #include "py/objtuple.h" #include "py/runtime.h" #include "py/objstr.h" +#include "py/objnamedtuple.h" #if MICROPY_PY_COLLECTIONS -typedef struct _mp_obj_namedtuple_type_t { - mp_obj_type_t base; - size_t n_fields; - qstr fields[]; -} mp_obj_namedtuple_type_t; - -typedef struct _mp_obj_namedtuple_t { - mp_obj_tuple_t tuple; -} mp_obj_namedtuple_t; - -STATIC size_t namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name) { +size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name) { for (size_t i = 0; i < type->n_fields; i++) { if (type->fields[i] == name) { return i; @@ -88,7 +79,7 @@ STATIC void namedtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { return; } #endif - size_t id = namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr); + size_t id = mp_obj_namedtuple_find_field((mp_obj_namedtuple_type_t*)self->tuple.base.type, attr); if (id == (size_t)-1) { return; } @@ -128,7 +119,7 @@ STATIC mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, memset(&tuple->items[n_args], 0, sizeof(mp_obj_t) * n_kw); for (size_t i = n_args; i < n_args + 2 * n_kw; i += 2) { qstr kw = mp_obj_str_get_qstr(args[i]); - size_t id = namedtuple_find_field(type, kw); + size_t id = mp_obj_namedtuple_find_field(type, kw); if (id == (size_t)-1) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_arg_error_terse_mismatch(); @@ -151,9 +142,18 @@ STATIC mp_obj_t namedtuple_make_new(const mp_obj_type_t *type_in, size_t n_args, return MP_OBJ_FROM_PTR(tuple); } -STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) { +mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields) { mp_obj_namedtuple_type_t *o = m_new_obj_var(mp_obj_namedtuple_type_t, qstr, n_fields); memset(&o->base, 0, sizeof(o->base)); + o->n_fields = n_fields; + for (size_t i = 0; i < n_fields; i++) { + o->fields[i] = mp_obj_str_get_qstr(fields[i]); + } + return o; +} + +STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t *fields) { + mp_obj_namedtuple_type_t *o = mp_obj_new_namedtuple_base(n_fields, fields); o->base.base.type = &mp_type_type; o->base.name = name; o->base.print = namedtuple_print; @@ -164,10 +164,6 @@ STATIC mp_obj_t mp_obj_new_namedtuple_type(qstr name, size_t n_fields, mp_obj_t o->base.subscr = mp_obj_tuple_subscr; o->base.getiter = mp_obj_tuple_getiter; o->base.parent = &mp_type_tuple; - o->n_fields = n_fields; - for (size_t i = 0; i < n_fields; i++) { - o->fields[i] = mp_obj_str_get_qstr(fields[i]); - } return MP_OBJ_FROM_PTR(o); } diff --git a/py/objnamedtuple.h b/py/objnamedtuple.h new file mode 100644 index 000000000..d32af35af --- /dev/null +++ b/py/objnamedtuple.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Paul Sokolovsky + * + * 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_OBJNAMEDTUPLE_H +#define MICROPY_INCLUDED_PY_OBJNAMEDTUPLE_H + +#include "py/objtuple.h" + +typedef struct _mp_obj_namedtuple_type_t { + mp_obj_type_t base; + size_t n_fields; + qstr fields[]; +} mp_obj_namedtuple_type_t; + +typedef struct _mp_obj_namedtuple_t { + mp_obj_tuple_t tuple; +} mp_obj_namedtuple_t; + +size_t mp_obj_namedtuple_find_field(const mp_obj_namedtuple_type_t *type, qstr name); +mp_obj_namedtuple_type_t *mp_obj_new_namedtuple_base(size_t n_fields, mp_obj_t *fields); + +#endif // MICROPY_INCLUDED_PY_OBJNAMEDTUPLE_H From a07fc5b6403b9a8bf7e7cb64f857272e5346d7e2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 21 Nov 2017 15:01:38 +1100 Subject: [PATCH 038/828] py/objfloat: Allow float() to parse anything with the buffer protocol. This generalises and simplifies the code and follows CPython behaviour. --- py/objfloat.c | 12 ++++++------ tests/float/float1.py | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/py/objfloat.c b/py/objfloat.c index 743287be6..75212a4d2 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -137,12 +137,11 @@ STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size return mp_obj_new_float(0); case 1: - default: - if (MP_OBJ_IS_STR(args[0])) { - // a string, parse it - size_t l; - const char *s = mp_obj_str_get_data(args[0], &l); - return mp_parse_num_decimal(s, l, false, false, NULL); + default: { + mp_buffer_info_t bufinfo; + if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_READ)) { + // a textual representation, parse it + return mp_parse_num_decimal(bufinfo.buf, bufinfo.len, false, false, NULL); } else if (mp_obj_is_float(args[0])) { // a float, just return it return args[0]; @@ -150,6 +149,7 @@ STATIC mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size // something else, try to cast it to a float return mp_obj_new_float(mp_obj_get_float(args[0])); } + } } } diff --git a/tests/float/float1.py b/tests/float/float1.py index c64f965a7..54807e5ac 100644 --- a/tests/float/float1.py +++ b/tests/float/float1.py @@ -36,6 +36,10 @@ try: except ValueError: print("ValueError") +# construct from something with the buffer protocol +print(float(b"1.2")) +print(float(bytearray(b"3.4"))) + # unary operators print(bool(0.0)) print(bool(1.2)) From d5cf5f70fdefa793d3e1fee9a26f03a1dd8c1d1e Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 22 Nov 2017 15:51:51 +1100 Subject: [PATCH 039/828] py/modbuiltins: Slightly simplify code in builtin round(). --- py/modbuiltins.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index ebff5f5c0..0c78832ac 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -457,16 +457,14 @@ STATIC mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) { return o_in; } #if MICROPY_PY_BUILTINS_FLOAT - mp_int_t num_dig = 0; + mp_float_t val = mp_obj_get_float(o_in); if (n_args > 1) { - num_dig = mp_obj_get_int(args[1]); - mp_float_t val = mp_obj_get_float(o_in); + mp_int_t num_dig = mp_obj_get_int(args[1]); mp_float_t mult = MICROPY_FLOAT_C_FUN(pow)(10, num_dig); // TODO may lead to overflow mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val * mult) / mult; return mp_obj_new_float(rounded); } - mp_float_t val = mp_obj_get_float(o_in); mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val); return mp_obj_new_int_from_float(rounded); #else From df078e82136de80a6ff2d30db97a7411c45d4085 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 23 Nov 2017 10:45:12 +1100 Subject: [PATCH 040/828] tests/net_hosted: Add test for socket connect() and poll() behaviour. --- tests/net_hosted/connect_poll.py | 32 ++++++++++++++++++++++++++++ tests/net_hosted/connect_poll.py.exp | 3 +++ 2 files changed, 35 insertions(+) create mode 100644 tests/net_hosted/connect_poll.py create mode 100644 tests/net_hosted/connect_poll.py.exp diff --git a/tests/net_hosted/connect_poll.py b/tests/net_hosted/connect_poll.py new file mode 100644 index 000000000..ece6aa0da --- /dev/null +++ b/tests/net_hosted/connect_poll.py @@ -0,0 +1,32 @@ +# test that socket.connect() has correct polling behaviour before, during and after + +try: + import usocket as socket, uselect as select +except: + import socket, select + + +def test(peer_addr): + s = socket.socket() + poller = select.poll() + poller.register(s) + + # test poll before connect + # note: CPython can return POLLHUP, so use the IN|OUT mask + p = poller.poll(0) + print(len(p), p[0][-1] & (select.POLLIN | select.POLLOUT)) + + s.connect(peer_addr) + + # test poll during connection + print(len(poller.poll(0))) + + # test poll after connection is established + p = poller.poll(1000) + print(len(p), p[0][-1]) + + s.close() + + +if __name__ == "__main__": + test(socket.getaddrinfo('micropython.org', 80)[0][-1]) diff --git a/tests/net_hosted/connect_poll.py.exp b/tests/net_hosted/connect_poll.py.exp new file mode 100644 index 000000000..cdf520e09 --- /dev/null +++ b/tests/net_hosted/connect_poll.py.exp @@ -0,0 +1,3 @@ +1 4 +1 +1 4 From ec1e9a10a73d9c5c3771a5797e9eceb62475b197 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sun, 12 Nov 2017 06:13:45 +0000 Subject: [PATCH 041/828] docs: Add notes on heap allocation caused by bound method refs. --- docs/library/micropython.rst | 11 ++++++++++- docs/reference/isr_rules.rst | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/library/micropython.rst b/docs/library/micropython.rst index c13a7391b..d1f923e31 100644 --- a/docs/library/micropython.rst +++ b/docs/library/micropython.rst @@ -112,5 +112,14 @@ Functions the heap may be locked) and scheduling a function to call later will lift those restrictions. - There is a finite stack to hold the scheduled functions and `schedule` + Note: If `schedule()` is called from a preempting IRQ, when memory + allocation is not allowed and the callback to be passed to `schedule()` is + a bound method, passing this directly will fail. This is because creating a + reference to a bound method causes memory allocation. A solution is to + create a reference to the method in the class constructor and to pass that + reference to `schedule()`. This is discussed in detail here + :ref:`reference documentation ` under "Creation of Python + objects". + + There is a finite stack to hold the scheduled functions and `schedule()` will raise a `RuntimeError` if the stack is full. diff --git a/docs/reference/isr_rules.rst b/docs/reference/isr_rules.rst index 2db261c09..dfdee048c 100644 --- a/docs/reference/isr_rules.rst +++ b/docs/reference/isr_rules.rst @@ -124,6 +124,32 @@ A means of creating an object without employing a class or globals is as follows The compiler instantiates the default ``buf`` argument when the function is loaded for the first time (usually when the module it's in is imported). +An instance of object creation occurs when a reference to a bound method is +created. This means that an ISR cannot pass a bound method to a function. One +solution is to create a reference to the bound method in the class constructor +and to pass that reference in the ISR. For example: + +.. code:: python + + class Foo(): + def __init__(self): + self.bar_ref = self.bar # Allocation occurs here + self.x = 0.1 + tim = pyb.Timer(4) + tim.init(freq=2) + tim.callback(self.cb) + + def bar(self, _): + self.x *= 1.2 + print(self.x) + + def cb(self, t): + # Passing self.bar would cause allocation. + micropython.schedule(self.bar_ref, 0) + +Other techniques are to define and instantiate the method in the constructor +or to pass :meth:`Foo.bar` with the argument *self*. + Use of Python objects ~~~~~~~~~~~~~~~~~~~~~ @@ -179,6 +205,9 @@ interrupt occurs while the previous callback is executing, a further instance of for execution; this will run after the current instance has completed. A sustained high interrupt repetition rate therefore carries a risk of unconstrained queue growth and eventual failure with a ``RuntimeError``. +If the callback to be passed to `schedule()` is a bound method, consider the +note in "Creation of Python objects". + Exceptions ---------- From 067bf849d29ab46319740a10561a112376415f51 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 23 Nov 2017 18:03:32 +0200 Subject: [PATCH 042/828] docs/uselect: poll: Explicitly specify that no-timeout value is -1. --- docs/library/uselect.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/library/uselect.rst b/docs/library/uselect.rst index beffce69a..b9e5da999 100644 --- a/docs/library/uselect.rst +++ b/docs/library/uselect.rst @@ -50,10 +50,11 @@ Methods Modify the *eventmask* for *obj*. -.. method:: poll.poll([timeout]) +.. method:: poll.poll(timeout=-1) - Wait for at least one of the registered objects to become ready. Returns - list of (``obj``, ``event``, ...) tuples, ``event`` element specifies + Wait for at least one of the registered objects to become ready, with optional + timeout in milliseconds (if *timeout* arg is not specified or -1, there is no + timeout). Returns list of (``obj``, ``event``, ...) tuples, ``event`` element specifies which events happened with a stream and is a combination of ``select.POLL*`` constants described above. There may be other elements in tuple, depending on a platform and version, so don't assume that its size is 2. In case of From 9783ac282ebe216d37be39fdf07113e6880c19b7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 24 Nov 2017 12:07:12 +1100 Subject: [PATCH 043/828] py/runtime: Simplify handling of containment binary operator. In mp_binary_op, there is no need to explicitly check for type->getiter being non-null and raising an exception because this is handled exactly by mp_getiter(). So just call the latter unconditionally. --- py/runtime.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 17e5d235c..08a35c2e6 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -536,25 +536,17 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { return res; } } - if (type->getiter != NULL) { - /* second attempt, walk the iterator */ - mp_obj_iter_buf_t iter_buf; - mp_obj_t iter = mp_getiter(rhs, &iter_buf); - mp_obj_t next; - while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { - if (mp_obj_equal(next, lhs)) { - return mp_const_true; - } - } - return mp_const_false; - } - if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { - mp_raise_TypeError("object not iterable"); - } else { - nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, - "'%s' object is not iterable", mp_obj_get_type_str(rhs))); + // final attempt, walk the iterator (will raise if rhs is not iterable) + mp_obj_iter_buf_t iter_buf; + mp_obj_t iter = mp_getiter(rhs, &iter_buf); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + if (mp_obj_equal(next, lhs)) { + return mp_const_true; + } } + return mp_const_false; } // generic binary_op supplied by type From 5b2f62aff312949c9c7edec6cfaaf4f97d93c442 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 24 Nov 2017 12:16:21 +1100 Subject: [PATCH 044/828] py/opmethods: Include the correct header for binary op enums. By directly including runtime0.h the mpconfig.h settings are not included and so the enums in runtime0.h can be incorrect. --- py/opmethods.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/opmethods.c b/py/opmethods.c index 1200ba39e..31901bb52 100644 --- a/py/opmethods.c +++ b/py/opmethods.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include "py/runtime0.h" +#include "py/obj.h" #include "py/builtin.h" STATIC mp_obj_t op_getitem(mp_obj_t self_in, mp_obj_t key_in) { From 5e34a113eaaf736fb4f703a3ee0892e1705d0a63 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 24 Nov 2017 13:04:24 +1100 Subject: [PATCH 045/828] py/runtime: Add MP_BINARY_OP_CONTAINS as reverse of MP_BINARY_OP_IN. Before this patch MP_BINARY_OP_IN had two meanings: coming from bytecode it meant that the args needed to be swapped, but coming from within the runtime meant that the args were already in the correct order. This lead to some confusion in the code and comments stating how args were reversed. It also lead to 2 bugs: 1) containment for a subclass of a native type didn't work; 2) the expression "{True} in True" would illegally succeed and return True. In both of these cases it was because the args to MP_BINARY_OP_IN ended up being reversed twice. To fix these things this patch introduces MP_BINARY_OP_CONTAINS which corresponds exactly to the __contains__ special method, and this is the operator that built-in types should implement. MP_BINARY_OP_IN is now only emitted by the compiler and is converted to MP_BINARY_OP_CONTAINS by swapping the arguments. --- extmod/modbtree.c | 2 +- py/objarray.c | 3 +- py/objdict.c | 4 +-- py/objint_mpz.c | 2 +- py/objset.c | 4 +-- py/objstr.c | 3 +- py/objtype.c | 2 +- py/opmethods.c | 2 +- py/runtime.c | 42 +++++++++++++--------------- py/runtime0.h | 4 +++ tests/micropython/viper_error.py.exp | 2 +- 11 files changed, 34 insertions(+), 36 deletions(-) diff --git a/extmod/modbtree.c b/extmod/modbtree.c index 5c1311532..8b7688580 100644 --- a/extmod/modbtree.c +++ b/extmod/modbtree.c @@ -282,7 +282,7 @@ STATIC mp_obj_t btree_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { STATIC mp_obj_t btree_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_btree_t *self = MP_OBJ_TO_PTR(lhs_in); switch (op) { - case MP_BINARY_OP_IN: { + case MP_BINARY_OP_CONTAINS: { DBT key, val; key.data = (void*)mp_obj_str_get_data(rhs_in, &key.size); int res = __bt_get(self->db, &key, &val, 0); diff --git a/py/objarray.c b/py/objarray.c index 7003ec9e7..a35539484 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -269,8 +269,7 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs return lhs_in; } - case MP_BINARY_OP_IN: { - /* NOTE `a in b` is `b.__contains__(a)` */ + case MP_BINARY_OP_CONTAINS: { mp_buffer_info_t lhs_bufinfo; mp_buffer_info_t rhs_bufinfo; diff --git a/py/objdict.c b/py/objdict.c index 1553a83b4..d0f95e41a 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -115,7 +115,7 @@ STATIC mp_obj_t dict_unary_op(mp_unary_op_t op, mp_obj_t self_in) { STATIC mp_obj_t dict_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_dict_t *o = MP_OBJ_TO_PTR(lhs_in); switch (op) { - case MP_BINARY_OP_IN: { + case MP_BINARY_OP_CONTAINS: { mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP); return mp_obj_new_bool(elem != NULL); } @@ -485,7 +485,7 @@ STATIC mp_obj_t dict_view_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t if (o->kind != MP_DICT_VIEW_KEYS) { return MP_OBJ_NULL; // op not supported } - if (op != MP_BINARY_OP_IN) { + if (op != MP_BINARY_OP_CONTAINS) { return MP_OBJ_NULL; // op not supported } return dict_binary_op(op, o->dict, rhs_in); diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 7b5cb0b9d..17e3ee6d2 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -207,7 +207,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i return mp_obj_new_float(flhs / frhs); #endif - } else if (op >= MP_BINARY_OP_INPLACE_OR) { + } else if (op >= MP_BINARY_OP_INPLACE_OR && op < MP_BINARY_OP_CONTAINS) { mp_obj_int_t *res = mp_obj_int_new_mpz(); switch (op) { diff --git a/py/objset.c b/py/objset.c index 6ed15c791..3e98c30e8 100644 --- a/py/objset.c +++ b/py/objset.c @@ -461,7 +461,7 @@ STATIC mp_obj_t set_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { #else bool update = true; #endif - if (op != MP_BINARY_OP_IN && !is_set_or_frozenset(rhs)) { + if (op != MP_BINARY_OP_CONTAINS && !is_set_or_frozenset(rhs)) { // For all ops except containment the RHS must be a set/frozenset return MP_OBJ_NULL; } @@ -507,7 +507,7 @@ STATIC mp_obj_t set_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { return set_issubset(lhs, rhs); case MP_BINARY_OP_MORE_EQUAL: return set_issuperset(lhs, rhs); - case MP_BINARY_OP_IN: { + case MP_BINARY_OP_CONTAINS: { mp_obj_set_t *o = MP_OBJ_TO_PTR(lhs); mp_obj_t elem = mp_set_lookup(&o->set, rhs, MP_MAP_LOOKUP); return mp_obj_new_bool(elem != MP_OBJ_NULL); diff --git a/py/objstr.c b/py/objstr.c index 1ff5132d2..b4f15b38d 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -384,8 +384,7 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i return mp_obj_new_str_from_vstr(lhs_type, &vstr); } - case MP_BINARY_OP_IN: - /* NOTE `a in b` is `b.__contains__(a)` */ + case MP_BINARY_OP_CONTAINS: return mp_obj_new_bool(find_subbytes(lhs_data, lhs_len, rhs_data, rhs_len, 1) != NULL); //case MP_BINARY_OP_NOT_EQUAL: // This is never passed here diff --git a/py/objtype.c b/py/objtype.c index 01d248256..267cae815 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -424,7 +424,7 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { [MP_BINARY_OP_LESS_EQUAL] = MP_QSTR___le__, [MP_BINARY_OP_MORE_EQUAL] = MP_QSTR___ge__, // MP_BINARY_OP_NOT_EQUAL, // a != b calls a == b and inverts result - [MP_BINARY_OP_IN] = MP_QSTR___contains__, + [MP_BINARY_OP_CONTAINS] = MP_QSTR___contains__, // All inplace methods are optional, and normal methods will be used // as a fallback. diff --git a/py/opmethods.c b/py/opmethods.c index 31901bb52..247fa5bbc 100644 --- a/py/opmethods.c +++ b/py/opmethods.c @@ -47,6 +47,6 @@ MP_DEFINE_CONST_FUN_OBJ_2(mp_op_delitem_obj, op_delitem); STATIC mp_obj_t op_contains(mp_obj_t lhs_in, mp_obj_t rhs_in) { mp_obj_type_t *type = mp_obj_get_type(lhs_in); - return type->binary_op(MP_BINARY_OP_IN, lhs_in, rhs_in); + return type->binary_op(MP_BINARY_OP_CONTAINS, lhs_in, rhs_in); } MP_DEFINE_CONST_FUN_OBJ_2(mp_op_contains_obj, op_contains); diff --git a/py/runtime.c b/py/runtime.c index 08a35c2e6..c7fe39367 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -523,30 +523,12 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { } } - /* deal with `in` - * - * NOTE `a in b` is `b.__contains__(a)`, hence why the generic dispatch - * needs to go below with swapped arguments - */ + // Convert MP_BINARY_OP_IN to MP_BINARY_OP_CONTAINS with swapped args. if (op == MP_BINARY_OP_IN) { - mp_obj_type_t *type = mp_obj_get_type(rhs); - if (type->binary_op != NULL) { - mp_obj_t res = type->binary_op(op, rhs, lhs); - if (res != MP_OBJ_NULL) { - return res; - } - } - - // final attempt, walk the iterator (will raise if rhs is not iterable) - mp_obj_iter_buf_t iter_buf; - mp_obj_t iter = mp_getiter(rhs, &iter_buf); - mp_obj_t next; - while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { - if (mp_obj_equal(next, lhs)) { - return mp_const_true; - } - } - return mp_const_false; + op = MP_BINARY_OP_CONTAINS; + mp_obj_t temp = lhs; + lhs = rhs; + rhs = temp; } // generic binary_op supplied by type @@ -575,6 +557,20 @@ generic_binary_op: } #endif + if (op == MP_BINARY_OP_CONTAINS) { + // If type didn't support containment then explicitly walk the iterator. + // mp_getiter will raise the appropriate exception if lhs is not iterable. + mp_obj_iter_buf_t iter_buf; + mp_obj_t iter = mp_getiter(lhs, &iter_buf); + mp_obj_t next; + while ((next = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) { + if (mp_obj_equal(next, rhs)) { + return mp_const_true; + } + } + return mp_const_false; + } + unsupported_op: if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_TypeError("unsupported type for operator"); diff --git a/py/runtime0.h b/py/runtime0.h index a72b7feb7..960532d17 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -131,6 +131,10 @@ typedef enum { #endif , + // The runtime will convert MP_BINARY_OP_IN to this operator with swapped args. + // A type should implement this containment operator instead of MP_BINARY_OP_IN. + MP_BINARY_OP_CONTAINS, + MP_BINARY_OP_NUM_RUNTIME, // These 2 are not supported by the runtime and must be synthesised by the emitter diff --git a/tests/micropython/viper_error.py.exp b/tests/micropython/viper_error.py.exp index 96be5a590..a44fb3ff0 100644 --- a/tests/micropython/viper_error.py.exp +++ b/tests/micropython/viper_error.py.exp @@ -18,7 +18,7 @@ ViperTypeError('must raise an object',) ViperTypeError('unary op __pos__ not implemented',) ViperTypeError('unary op __neg__ not implemented',) ViperTypeError('unary op __invert__ not implemented',) -ViperTypeError('binary op __contains__ not implemented',) +ViperTypeError('binary op not implemented',) NotImplementedError('native yield',) NotImplementedError('native yield from',) NotImplementedError('conversion to object',) From 505671b698490918fe0ea6c6dfdab8c0b25339be Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 24 Nov 2017 14:48:41 +1100 Subject: [PATCH 046/828] tests/basics: Add test for containment of a subclass of a native type. --- tests/basics/subclass_native_containment.py | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/basics/subclass_native_containment.py diff --git a/tests/basics/subclass_native_containment.py b/tests/basics/subclass_native_containment.py new file mode 100644 index 000000000..7400f7583 --- /dev/null +++ b/tests/basics/subclass_native_containment.py @@ -0,0 +1,22 @@ +# test containment operator on subclass of a native type + +class mylist(list): + pass + +class mydict(dict): + pass + +class mybytes(bytes): + pass + +l = mylist([1, 2, 3]) +print(0 in l) +print(1 in l) + +d = mydict({1:1, 2:2}) +print(0 in l) +print(1 in l) + +b = mybytes(b'1234') +print(0 in b) +print(b'1' in b) From c7a0e1472da8e1b8e359bc26f838ab20bab00a8a Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 24 Nov 2017 15:30:12 +1100 Subject: [PATCH 047/828] tests/basics/builtin_range: Add test for corner case of range slicing. --- tests/basics/builtin_range.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/basics/builtin_range.py b/tests/basics/builtin_range.py index 0e2fabd82..66226bad1 100644 --- a/tests/basics/builtin_range.py +++ b/tests/basics/builtin_range.py @@ -38,6 +38,9 @@ print(range(1, 100, 5)[15:5:-3]) print(range(100, 1, -5)[5:15:3]) print(range(100, 1, -5)[15:5:-3]) +# for this case uPy gives a different stop value but the listed elements are still correct +print(list(range(7, -2, -4)[2:-2:])) + # zero step try: range(1, 2, 0) From e511f24ddd62f8157ac137f8e4b87e15e5012d70 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 30 Oct 2017 12:48:14 +1100 Subject: [PATCH 048/828] extmod/modussl_axtls: Implement key and cert kw args to wrap_socket. The key and cert must both be a str/bytes object in DER format. --- extmod/modussl_axtls.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 4d8440a89..35e3106cd 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -44,6 +44,8 @@ typedef struct _mp_obj_ssl_socket_t { } mp_obj_ssl_socket_t; struct ssl_args { + mp_arg_val_t key; + mp_arg_val_t cert; mp_arg_val_t server_side; mp_arg_val_t server_hostname; }; @@ -62,10 +64,28 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { o->sock = sock; uint32_t options = SSL_SERVER_VERIFY_LATER; + if (args->key.u_obj != mp_const_none) { + options |= SSL_NO_DEFAULT_KEY; + } if ((o->ssl_ctx = ssl_ctx_new(options, SSL_DEFAULT_CLNT_SESS)) == NULL) { mp_raise_OSError(MP_EINVAL); } + if (args->key.u_obj != mp_const_none) { + size_t len; + const byte *data = (const byte*)mp_obj_str_get_data(args->key.u_obj, &len); + int res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_RSA_KEY, data, len, NULL); + if (res != SSL_OK) { + mp_raise_ValueError("invalid key"); + } + + data = (const byte*)mp_obj_str_get_data(args->cert.u_obj, &len); + res = ssl_obj_memory_load(o->ssl_ctx, SSL_OBJ_X509_CERT, data, len, NULL); + if (res != SSL_OK) { + mp_raise_ValueError("invalid cert"); + } + } + if (args->server_side.u_bool) { o->ssl_sock = ssl_server_new(o->ssl_ctx, (long)sock); } else { @@ -211,6 +231,8 @@ STATIC const mp_obj_type_t ussl_socket_type = { STATIC mp_obj_t mod_ssl_wrap_socket(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { // TODO: Implement more args static const mp_arg_t allowed_args[] = { + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_cert, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_server_side, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_server_hostname, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; From 48f6990fbc54ac29b15fa765d431244d1dcb5f21 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 31 Oct 2017 13:50:08 +1100 Subject: [PATCH 049/828] extmod/modlwip: Commit TCP out data to lower layers if buffer gets full. Dramatically improves TCP sending throughput because without an explicit call to tcp_output() the data is only sent to the lower layers via the lwIP slow timer which (by default) ticks every 500ms. --- extmod/modlwip.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index bbb01b5d7..2c194e1bd 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -498,6 +498,11 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui err_t err = tcp_write(socket->pcb.tcp, buf, write_len, TCP_WRITE_FLAG_COPY); + // If the output buffer is getting full then send the data to the lower layers + if (err == ERR_OK && tcp_sndbuf(socket->pcb.tcp) < TCP_SND_BUF / 4) { + err = tcp_output(socket->pcb.tcp); + } + if (err != ERR_OK) { *_errno = error_lookup_table[-err]; return MP_STREAM_ERROR; From c23cc4cc81a3e437528d8c94e55bfe58db87ea86 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 26 Oct 2017 23:09:17 +0300 Subject: [PATCH 050/828] docs/uctypes: Typo/article fixes. --- docs/library/uctypes.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/library/uctypes.rst b/docs/library/uctypes.rst index 2a9c4dd5c..164e0eb9d 100644 --- a/docs/library/uctypes.rst +++ b/docs/library/uctypes.rst @@ -8,7 +8,7 @@ This module implements "foreign data interface" for MicroPython. The idea behind it is similar to CPython's ``ctypes`` modules, but the actual API is different, streamlined and optimized for small size. The basic idea of the module is to define data structure layout with about the same power as the -C language allows, and the access it using familiar dot-syntax to reference +C language allows, and then access it using familiar dot-syntax to reference sub-fields. .. seealso:: @@ -43,7 +43,7 @@ Following are encoding examples for various field types: i.e. value is a 2-tuple, first element of which is offset, and second is a structure descriptor dictionary (note: offsets in recursive descriptors - are relative to a structure it defines). + are relative to the structure it defines). * Arrays of primitive types:: @@ -86,9 +86,9 @@ Following are encoding examples for various field types: BF_POS and BF_LEN positions, respectively. Bitfield position is counted from the least significant bit, and is the number of right-most bit of a field (in other words, it's a number of bits a scalar needs to be shifted - right to extra the bitfield). + right to extract the bitfield). - In the example above, first UINT16 value will be extracted at offset 0 + In the example above, first a UINT16 value will be extracted at offset 0 (this detail may be important when accessing hardware registers, where particular access size and alignment are required), and then bitfield whose rightmost bit is least-significant bit of this UINT16, and length @@ -99,7 +99,7 @@ Following are encoding examples for various field types: in particular, example above will access least-significant byte of UINT16 in both little- and big-endian structures. But it depends on the least significant bit being numbered 0. Some targets may use different - numbering in their native ABI, but ``uctypes`` always uses normalized + numbering in their native ABI, but ``uctypes`` always uses the normalized numbering described above. Module contents From 50cffcfe2c479fac9d815cfb28c19e1854070d56 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 26 Oct 2017 23:11:09 +0300 Subject: [PATCH 051/828] docs/uctypes: Tweak descriptor reference to hopefully be easier to follow. Put offset first in OR expressions, and use "offset" var instead of hardcoded numbers. Hopefully, this will make it more self-describing and show patterns better. --- docs/library/uctypes.rst | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/library/uctypes.rst b/docs/library/uctypes.rst index 164e0eb9d..c938d74a8 100644 --- a/docs/library/uctypes.rst +++ b/docs/library/uctypes.rst @@ -29,16 +29,16 @@ Following are encoding examples for various field types: * Scalar types:: - "field_name": uctypes.UINT32 | 0 + "field_name": offset | uctypes.UINT32 in other words, value is scalar type identifier ORed with field offset (in bytes) from the start of the structure. * Recursive structures:: - "sub": (2, { - "b0": uctypes.UINT8 | 0, - "b1": uctypes.UINT8 | 1, + "sub": (offset, { + "b0": 0 | uctypes.UINT8, + "b1": 1 | uctypes.UINT8, }) i.e. value is a 2-tuple, first element of which is offset, and second is @@ -47,7 +47,7 @@ Following are encoding examples for various field types: * Arrays of primitive types:: - "arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2), + "arr": (offset | uctypes.ARRAY, size | uctypes.UINT8), i.e. value is a 2-tuple, first element of which is ARRAY flag ORed with offset, and second is scalar element type ORed number of elements @@ -55,7 +55,7 @@ Following are encoding examples for various field types: * Arrays of aggregate types:: - "arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}), + "arr2": (offset | uctypes.ARRAY, size, {"b": 0 | uctypes.UINT8}), i.e. value is a 3-tuple, first element of which is ARRAY flag ORed with offset, second is a number of elements in array, and third is @@ -63,21 +63,21 @@ Following are encoding examples for various field types: * Pointer to a primitive type:: - "ptr": (uctypes.PTR | 0, uctypes.UINT8), + "ptr": (offset | uctypes.PTR, uctypes.UINT8), i.e. value is a 2-tuple, first element of which is PTR flag ORed with offset, and second is scalar element type. * Pointer to an aggregate type:: - "ptr2": (uctypes.PTR | 0, {"b": uctypes.UINT8 | 0}), + "ptr2": (offset | uctypes.PTR, {"b": 0 | uctypes.UINT8}), i.e. value is a 2-tuple, first element of which is PTR flag ORed with offset, second is descriptor of type pointed to. * Bitfields:: - "bitf0": uctypes.BFUINT16 | 0 | 0 << uctypes.BF_POS | 8 << uctypes.BF_LEN, + "bitf0": offset | uctypes.BFUINT16 | lsbit << uctypes.BF_POS | bitsize << uctypes.BF_LEN, i.e. value is type of scalar value containing given bitfield (typenames are similar to scalar types, but prefixes with "BF"), ORed with offset for @@ -91,9 +91,10 @@ Following are encoding examples for various field types: In the example above, first a UINT16 value will be extracted at offset 0 (this detail may be important when accessing hardware registers, where particular access size and alignment are required), and then bitfield - whose rightmost bit is least-significant bit of this UINT16, and length - is 8 bits, will be extracted - effectively, this will access - least-significant byte of UINT16. + whose rightmost bit is *lsbit* bit of this UINT16, and length + is *bitsize* bits, will be extracted. For example, if *lsbit* is 0 and + *bitsize* is 8, then effectively it will access least-significant byte + of UINT16. Note that bitfield operations are independent of target byte endianness, in particular, example above will access least-significant byte of UINT16 From f59c6b48aed765fc0eb3785686ffb11f2efc8eae Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 26 Nov 2017 09:55:02 +0200 Subject: [PATCH 052/828] docs/uselect: Describe POLLHUP/POLLERR semantics in more details. Per POSIX, http://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html these flags aren't valid in the input eventmask. Instead, they can be returned in unsolicited manner in the output eventmask at any time. --- docs/library/uselect.rst | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/library/uselect.rst b/docs/library/uselect.rst index b9e5da999..aa70bec69 100644 --- a/docs/library/uselect.rst +++ b/docs/library/uselect.rst @@ -35,12 +35,15 @@ Methods Register *obj* for polling. *eventmask* is logical OR of: - * ``select.POLLIN`` - data available for reading - * ``select.POLLOUT`` - more data can be written - * ``select.POLLERR`` - error occurred - * ``select.POLLHUP`` - end of stream/connection termination detected + * `uselect.POLLIN` - data available for reading + * `uselect.POLLOUT` - more data can be written - *eventmask* defaults to ``select.POLLIN | select.POLLOUT``. + Note that flags like `uselect.POLLHUP` and `uselect.POLLERR` are + *not* valid as input eventmask (these are unsolicited events which + will be returned from `poll()` regardless of whether they are asked + for). This semantics is per POSIX. + + *eventmask* defaults to ``uselect.POLLIN | uselect.POLLOUT``. .. method:: poll.unregister(obj) @@ -52,15 +55,21 @@ Methods .. method:: poll.poll(timeout=-1) - Wait for at least one of the registered objects to become ready, with optional - timeout in milliseconds (if *timeout* arg is not specified or -1, there is no - timeout). Returns list of (``obj``, ``event``, ...) tuples, ``event`` element specifies - which events happened with a stream and is a combination of ``select.POLL*`` - constants described above. There may be other elements in tuple, depending - on a platform and version, so don't assume that its size is 2. In case of - timeout, an empty list is returned. + Wait for at least one of the registered objects to become ready or have an + exceptional condition, with optional timeout in milliseconds (if *timeout* + arg is not specified or -1, there is no timeout). - Timeout is in milliseconds. + Returns list of (``obj``, ``event``, ...) tuples. There may be other elements in + tuple, depending on a platform and version, so don't assume that its size is 2. + The ``event`` element specifies which events happened with a stream and + is a combination of ``uselect.POLL*`` constants described above. Note that + flags `uselect.POLLHUP` and `uselect.POLLERR` can be returned at any time + (even if were not asked for), and must be acted on accordingly (the + corresponding stream unregistered from poll and likely closed), because + otherwise all further invocations of `poll()` may return immediately with + these flags set for this stream again. + + In case of timeout, an empty list is returned. .. admonition:: Difference to CPython :class: attention From 84895f1a210d0037a86887f0f647570bdf40afa2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 27 Nov 2017 12:51:52 +1100 Subject: [PATCH 053/828] py/parsenum: Improve parsing of floating point numbers. This patch improves parsing of floating point numbers by converting all the digits (integer and fractional) together into a number 1 or greater, and then applying the correct power of 10 at the very end. In particular the multiple "multiply by 0.1" operations to build a fraction are now combined together and applied at the same time as the exponent, at the very end. This helps to retain precision during parsing of floats, and also includes a check that the number doesn't overflow during the parsing. One benefit is that a float will have the same value no matter where the decimal point is located, eg 1.23 == 123e-2. --- py/parsenum.c | 27 +++++++++++++++++++++------ tests/float/float_parse.py | 22 ++++++++++++++++++++++ tests/float/float_parse_doubleprec.py | 16 ++++++++++++++++ tests/run-tests | 1 + 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 tests/float/float_parse.py create mode 100644 tests/float/float_parse_doubleprec.py diff --git a/py/parsenum.c b/py/parsenum.c index b62029f7c..98e773685 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -170,6 +170,14 @@ typedef enum { mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex) { #if MICROPY_PY_BUILTINS_FLOAT + +// DEC_VAL_MAX only needs to be rough and is used to retain precision while not overflowing +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define DEC_VAL_MAX 1e20F +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define DEC_VAL_MAX 1e200 +#endif + const char *top = str + len; mp_float_t dec_val = 0; bool dec_neg = false; @@ -214,8 +222,8 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool // string should be a decimal number parse_dec_in_t in = PARSE_DEC_IN_INTG; bool exp_neg = false; - mp_float_t frac_mult = 0.1; mp_int_t exp_val = 0; + mp_int_t exp_extra = 0; while (str < top) { mp_uint_t dig = *str++; if ('0' <= dig && dig <= '9') { @@ -223,11 +231,18 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool if (in == PARSE_DEC_IN_EXP) { exp_val = 10 * exp_val + dig; } else { - if (in == PARSE_DEC_IN_FRAC) { - dec_val += dig * frac_mult; - frac_mult *= MICROPY_FLOAT_CONST(0.1); - } else { + if (dec_val < DEC_VAL_MAX) { + // dec_val won't overflow so keep accumulating dec_val = 10 * dec_val + dig; + if (in == PARSE_DEC_IN_FRAC) { + --exp_extra; + } + } else { + // dec_val might overflow and we anyway can't represent more digits + // of precision, so ignore the digit and just adjust the exponent + if (in == PARSE_DEC_IN_INTG) { + ++exp_extra; + } } } } else if (in == PARSE_DEC_IN_INTG && dig == '.') { @@ -261,7 +276,7 @@ mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool } // apply the exponent - dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val); + dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val + exp_extra); } // negate value if needed diff --git a/tests/float/float_parse.py b/tests/float/float_parse.py new file mode 100644 index 000000000..448eff3bc --- /dev/null +++ b/tests/float/float_parse.py @@ -0,0 +1,22 @@ +# test parsing of floats + +inf = float('inf') + +# it shouldn't matter where the decimal point is if the exponent balances the value +print(float('1234') - float('0.1234e4')) +print(float('1.015625') - float('1015625e-6')) + +# very large integer part with a very negative exponent should cancel out +print(float('9' * 60 + 'e-60')) +print(float('9' * 60 + 'e-40')) +print(float('9' * 60 + 'e-20') == float('1e40')) + +# many fractional digits +print(float('.' + '9' * 70)) +print(float('.' + '9' * 70 + 'e20')) +print(float('.' + '9' * 70 + 'e-50') == float('1e-50')) + +# tiny fraction with large exponent +print(float('.' + '0' * 60 + '1e10') == float('1e-51')) +print(float('.' + '0' * 60 + '9e25')) +print(float('.' + '0' * 60 + '9e40')) diff --git a/tests/float/float_parse_doubleprec.py b/tests/float/float_parse_doubleprec.py new file mode 100644 index 000000000..356601130 --- /dev/null +++ b/tests/float/float_parse_doubleprec.py @@ -0,0 +1,16 @@ +# test parsing of floats, requiring double-precision + +# very large integer part with a very negative exponent should cancel out +print(float('9' * 400 + 'e-100')) +print(float('9' * 400 + 'e-200')) +print(float('9' * 400 + 'e-400')) + +# many fractional digits +print(float('.' + '9' * 400)) +print(float('.' + '9' * 400 + 'e100')) +print(float('.' + '9' * 400 + 'e-100')) + +# tiny fraction with large exponent +print(float('.' + '0' * 400 + '9e100')) +print(float('.' + '0' * 400 + '9e200')) +print(float('.' + '0' * 400 + '9e400')) diff --git a/tests/run-tests b/tests/run-tests index 6280a5182..3c763512c 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -271,6 +271,7 @@ def run_tests(pyb, tests, args, base_path="."): if upy_float_precision < 64: skip_tests.add('float/float_divmod.py') # tested by float/float_divmod_relaxed.py instead skip_tests.add('float/float2int_doubleprec_intbig.py') + skip_tests.add('float/float_parse_doubleprec.py') if not has_complex: skip_tests.add('float/complex1.py') From c3bc8d7b2b372abf154aeddf762f5e2b859a0e5b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 27 Nov 2017 14:14:57 +1100 Subject: [PATCH 054/828] tests/basics/builtin_locals: Add test for using locals() in class body. --- tests/basics/builtin_locals.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/basics/builtin_locals.py b/tests/basics/builtin_locals.py index 3689216ef..e60759a40 100644 --- a/tests/basics/builtin_locals.py +++ b/tests/basics/builtin_locals.py @@ -2,3 +2,12 @@ x = 123 print(locals()['x']) + +class A: + y = 1 + def f(self): + pass + + print('x' in locals()) + print(locals()['y']) + print('f' in locals()) From 2161d6b603e2dfc444a597c4346f7e2301f9c05e Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 27 Nov 2017 23:40:31 +1100 Subject: [PATCH 055/828] py/objdict: Reuse dict-view key iterator for standard dict iterator. It has equivalent behaviour and reusing it saves some code bytes. --- py/objdict.c | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/py/objdict.c b/py/objdict.c index d0f95e41a..f6fb594b3 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -192,37 +192,6 @@ STATIC mp_obj_t dict_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { } } -/******************************************************************************/ -/* dict iterator */ - -typedef struct _mp_obj_dict_it_t { - mp_obj_base_t base; - mp_fun_1_t iternext; - mp_obj_t dict; - size_t cur; -} mp_obj_dict_it_t; - -STATIC mp_obj_t dict_it_iternext(mp_obj_t self_in) { - mp_obj_dict_it_t *self = MP_OBJ_TO_PTR(self_in); - mp_map_elem_t *next = dict_iter_next(MP_OBJ_TO_PTR(self->dict), &self->cur); - - if (next == NULL) { - return MP_OBJ_STOP_ITERATION; - } else { - return next->key; - } -} - -STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { - assert(sizeof(mp_obj_dict_it_t) <= sizeof(mp_obj_iter_buf_t)); - mp_obj_dict_it_t *o = (mp_obj_dict_it_t*)iter_buf; - o->base.type = &mp_type_polymorph_iter; - o->iternext = dict_it_iternext; - o->dict = self_in; - o->cur = 0; - return MP_OBJ_FROM_PTR(o); -} - /******************************************************************************/ /* dict methods */ @@ -527,6 +496,20 @@ STATIC mp_obj_t dict_values(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values); +/******************************************************************************/ +/* dict iterator */ + +STATIC mp_obj_t dict_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_dict_view_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_check_self(MP_OBJ_IS_DICT_TYPE(self_in)); + mp_obj_dict_view_it_t *o = (mp_obj_dict_view_it_t*)iter_buf; + o->base.type = &dict_view_it_type; + o->kind = MP_DICT_VIEW_KEYS; + o->dict = self_in; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} + /******************************************************************************/ /* dict constructors & public C API */ From 7cf446f3daa820b15f0027d3468f1b78be17fec7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 28 Nov 2017 10:50:32 +1100 Subject: [PATCH 056/828] tools/gen-cpydiff.py: Update executable paths to point to new ports dir. --- tools/gen-cpydiff.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/gen-cpydiff.py b/tools/gen-cpydiff.py index aff5b56e7..8aef37514 100644 --- a/tools/gen-cpydiff.py +++ b/tools/gen-cpydiff.py @@ -39,10 +39,10 @@ from collections import namedtuple # to the correct executable. if os.name == 'nt': CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3.exe') - MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../windows/micropython.exe') + MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../ports/windows/micropython.exe') else: CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3') - MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../unix/micropython') + MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../ports/unix/micropython') TESTPATH = '../tests/cpydiff/' DOCPATH = '../docs/genrst/' From 63f47104feec03cd529b80c654a2aa80e3e7d524 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 28 Nov 2017 10:50:53 +1100 Subject: [PATCH 057/828] tests/cpydiff: Add difference-test for second arg of builtin next(). --- tests/cpydiff/builtin_next_arg2.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/cpydiff/builtin_next_arg2.py diff --git a/tests/cpydiff/builtin_next_arg2.py b/tests/cpydiff/builtin_next_arg2.py new file mode 100644 index 000000000..cbde773f9 --- /dev/null +++ b/tests/cpydiff/builtin_next_arg2.py @@ -0,0 +1,12 @@ +""" +categories: Modules,builtins +description: Second argument to next() is not implemented +cause: MicroPython is optimised for code space. +workaround: Instead of `val = next(it, deflt)` use:: + + try: + val = next(it) + except StopIteration: + val = deflt +""" +print(next(iter(range(0)), 42)) From 53e06e05c917ac4a9ee6bff9a7665b2f7439badf Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 28 Nov 2017 13:37:26 +0200 Subject: [PATCH 058/828] zephyr/Makefile: clean: Remove generated prj_*_merged.conf. --- ports/zephyr/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/Makefile b/ports/zephyr/Makefile index 2e6cb41b9..81aaaa6dc 100644 --- a/ports/zephyr/Makefile +++ b/ports/zephyr/Makefile @@ -90,7 +90,7 @@ build/genhdr/qstr.i.last: | $(Z_EXPORTS) LIBMICROPYTHON_EXTRA_CMD = -$(RM) -f outdir/$(OUTDIR_PREFIX)/zephyr.lnk # MicroPython's global clean cleans everything, fast -CLEAN_EXTRA = outdir +CLEAN_EXTRA = outdir prj_*_merged.conf # Clean Zephyr things in Zephyr way z_clean: From a289b24e2524b793b6351ba6feb4e0f5ed2516ec Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 28 Nov 2017 13:59:24 +0200 Subject: [PATCH 059/828] tests/run-tests: "minimal": Skip recently added float/float_parse.py. Fails for Zephyr qemu_x86 with: -9e-36 +9.000001e-36 --- tests/run-tests | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-tests b/tests/run-tests index 3c763512c..a2efede0a 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -312,6 +312,7 @@ def run_tests(pyb, tests, args, base_path="."): skip_tests.add('basics/class_inplace_op.py') # all special methods not supported skip_tests.add('misc/rge_sm.py') # too large skip_tests.add('micropython/opt_level.py') # don't assume line numbers are stored + skip_tests.add('float/float_parse.py') # minor parsing artifacts with 32-bit floats # Some tests are known to fail on 64-bit machines if pyb is None and platform.architecture()[0] == '64bit': From 25b7c7d7c66e46a9a5729b149ac35ebf13343716 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 28 Nov 2017 14:11:46 +0200 Subject: [PATCH 060/828] zephyr/prj_base.conf: Force zephyr.bin build output. As useful for CI systems. 1.10 doesn't build .bin for qemu_* for example. Also, remove deprecated CONFIG_LEGACY_KERNEL option. --- ports/zephyr/prj_base.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/prj_base.conf b/ports/zephyr/prj_base.conf index fbcedf260..58c65f14d 100644 --- a/ports/zephyr/prj_base.conf +++ b/ports/zephyr/prj_base.conf @@ -1,4 +1,4 @@ -CONFIG_LEGACY_KERNEL=n +CONFIG_BUILD_OUTPUT_BIN=y CONFIG_REBOOT=y CONFIG_STDOUT_CONSOLE=y From a036554a77c50fc4aabf056d93f7b3170ee4af21 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 17 Nov 2017 20:23:45 +0200 Subject: [PATCH 061/828] zephyr/Makefile: Convert to new CMake-based Zephyr build system. Zephyr 1.10 switches to CMake-based build system (already in master). --- ports/zephyr/CMakeLists.txt | 21 +++++++++++++++++++++ ports/zephyr/Makefile | 26 +++++++++++++------------- 2 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 ports/zephyr/CMakeLists.txt diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt new file mode 100644 index 000000000..cd7eda227 --- /dev/null +++ b/ports/zephyr/CMakeLists.txt @@ -0,0 +1,21 @@ +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +target_sources(app PRIVATE src/zephyr_start.c src/zephyr_getchar.c) + +add_library(libmicropython STATIC IMPORTED) +set_target_properties(libmicropython PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libmicropython.a) +target_link_libraries(app libmicropython) + +zephyr_get_include_directories_as_string(includes) +zephyr_get_system_include_directories_as_string(system_includes) +zephyr_get_compile_definitions_as_string(definitions) +zephyr_get_compile_options_as_string(options) + +add_custom_target( + outputexports + COMMAND echo CC="${CMAKE_C_COMPILER}" + COMMAND echo Z_CFLAGS=${system_includes}${includes}${definitions}${options} + VERBATIM + USES_TERMINAL +) diff --git a/ports/zephyr/Makefile b/ports/zephyr/Makefile index 81aaaa6dc..ec2bbbcb6 100644 --- a/ports/zephyr/Makefile +++ b/ports/zephyr/Makefile @@ -20,15 +20,15 @@ FROZEN_DIR = scripts # Default target all: +include ../../py/mkenv.mk +include $(TOP)/py/py.mk + # Zephyr (generated) config files - must be defined before include below Z_EXPORTS = outdir/$(OUTDIR_PREFIX)/Makefile.export ifneq ($(MAKECMDGOALS), clean) include $(Z_EXPORTS) endif -include ../../py/mkenv.mk -include $(TOP)/py/py.mk - INC += -I. INC += -I$(TOP) INC += -I$(BUILD) @@ -56,19 +56,11 @@ SRC_QSTR += $(SRC_C) OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -CFLAGS = $(KBUILD_CFLAGS) $(NOSTDINC_FLAGS) $(ZEPHYRINCLUDE) \ +CFLAGS = $(Z_CFLAGS) \ -std=gnu99 -fomit-frame-pointer -DNDEBUG -DMICROPY_HEAP_SIZE=$(MICROPY_HEAP_SIZE) $(CFLAGS_EXTRA) $(INC) include $(TOP)/py/mkrules.mk -# We use single target here ($(Z_EXPORTS)) for simplicity, but actually -# number of things get generated here: 'initconfig' generates C header for -# Kconfig configuration, 'outputexports' generates make environment with CC, -# etc., and 'lib' generates other headers which may be included by zephyr.h, -# e.g. DTS-related. -$(Z_EXPORTS): $(CONF_FILE) - $(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE) initconfig outputexports lib - GENERIC_TARGETS = all zephyr run qemu qemugdb flash debug debugserver KCONFIG_TARGETS = \ initconfig config nconfig menuconfig xconfig gconfig \ @@ -81,7 +73,7 @@ $(GENERIC_TARGETS): $(LIBMICROPYTHON) $(CLEAN_TARGETS): clean $(GENERIC_TARGETS) $(KCONFIG_TARGETS) $(CLEAN_TARGETS): - $(MAKE) -f Makefile.zephyr BOARD=$(BOARD) CONF_FILE=$(CONF_FILE) $@ + $(MAKE) -C outdir/$(BOARD) $@ $(LIBMICROPYTHON): | $(Z_EXPORTS) build/genhdr/qstr.i.last: | $(Z_EXPORTS) @@ -103,3 +95,11 @@ prj_$(BOARD)_merged.conf: prj_base.conf prj_$(BOARD).conf test: cd $(TOP)/tests && ./run-tests --target minimal --device "execpty:make -C ../ports/zephyr run BOARD=$(BOARD) QEMU_PTY=1" + +cmake: outdir/$(BOARD)/Makefile + +outdir/$(BOARD)/Makefile: $(CONF_FILE) + mkdir -p outdir/$(BOARD) && cmake -DBOARD=$(BOARD) -DCONF_FILE=$(CONF_FILE) -Boutdir/$(BOARD) -H. + +$(Z_EXPORTS): outdir/$(BOARD)/Makefile + make --no-print-directory -C outdir/$(BOARD) outputexports CMAKE_COMMAND=: >$@ From b369c1bb9601f83761257d9ba47adef5cbb9ea7b Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 28 Nov 2017 18:19:48 +0200 Subject: [PATCH 062/828] zephyr/Makefile: Make prj_$(BOARD).conf optional, again. This time hopefully should work reliably, using make $(wildcard) function, which in this case either expands to existing prj_$(BOARD).conf file, or to an empty string for non-existing one. --- ports/zephyr/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/zephyr/Makefile b/ports/zephyr/Makefile index ec2bbbcb6..2327cf73c 100644 --- a/ports/zephyr/Makefile +++ b/ports/zephyr/Makefile @@ -89,8 +89,10 @@ z_clean: $(MAKE) -f Makefile.zephyr BOARD=$(BOARD) clean # This rule is for prj_$(BOARD)_merged.conf, not $(CONF_FILE), which -# can be overriden -prj_$(BOARD)_merged.conf: prj_base.conf prj_$(BOARD).conf +# can be overriden. +# prj_$(BOARD).conf is optional, that's why it's resolved with $(wildcard) +# function. +prj_$(BOARD)_merged.conf: prj_base.conf $(wildcard prj_$(BOARD).conf) $(PYTHON) makeprj.py prj_base.conf prj_$(BOARD).conf $@ test: From 3990a52c0f071a7a48cc276f30785930f31eed39 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 29 Nov 2017 15:43:40 +1100 Subject: [PATCH 063/828] py: Annotate func defs with NORETURN when their corresp decls have it. --- py/objstr.c | 2 +- py/stackctrl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/py/objstr.c b/py/objstr.c index b4f15b38d..30153813d 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -2084,7 +2084,7 @@ bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) { } } -STATIC void bad_implicit_conversion(mp_obj_t self_in) { +STATIC NORETURN void bad_implicit_conversion(mp_obj_t self_in) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_TypeError("can't convert to str implicitly"); } else { diff --git a/py/stackctrl.c b/py/stackctrl.c index 7cd35fee2..11165b9a6 100644 --- a/py/stackctrl.c +++ b/py/stackctrl.c @@ -48,7 +48,7 @@ void mp_stack_set_limit(mp_uint_t limit) { MP_STATE_THREAD(stack_limit) = limit; } -void mp_exc_recursion_depth(void) { +NORETURN void mp_exc_recursion_depth(void) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded))); } From 8e323b8fa84277531685985327fb682761abd53d Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 29 Nov 2017 16:58:27 +1100 Subject: [PATCH 064/828] py/qstr: Rewrite find_qstr to make manifest that it returns a valid ptr. So long as the input qstr identifier is valid (below the maximum number of qstrs) the function will always return a valid pointer. This patch eliminates the "return 0" dead-code. --- py/qstr.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/py/qstr.c b/py/qstr.c index a3c9612c6..08c3e2505 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -127,14 +127,12 @@ void qstr_init(void) { STATIC const byte *find_qstr(qstr q) { // search pool for this qstr - for (qstr_pool_t *pool = MP_STATE_VM(last_pool); pool != NULL; pool = pool->prev) { - if (q >= pool->total_prev_len) { - return pool->qstrs[q - pool->total_prev_len]; - } + // total_prev_len==0 in the final pool, so the loop will always terminate + qstr_pool_t *pool = MP_STATE_VM(last_pool); + while (q < pool->total_prev_len) { + pool = pool->prev; } - - // not found - return 0; + return pool->qstrs[q - pool->total_prev_len]; } // qstr_mutex must be taken while in this function From 74fad3536b961e2ffe08e545b1cf787c0c0b4772 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 29 Nov 2017 17:17:08 +1100 Subject: [PATCH 065/828] py/gc: In gc_realloc, convert pointer sanity checks to assertions. These checks are assumed to be true in all cases where gc_realloc is called with a valid pointer, so no need to waste code space and time checking them in a non-debug build. --- py/gc.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/py/gc.c b/py/gc.c index 9752b3532..172a5e8fc 100644 --- a/py/gc.c +++ b/py/gc.c @@ -628,27 +628,18 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { void *ptr = ptr_in; - // sanity check the ptr - if (!VERIFY_PTR(ptr)) { - return NULL; - } - - // get first block - size_t block = BLOCK_FROM_PTR(ptr); - GC_ENTER(); - // sanity check the ptr is pointing to the head of a block - if (ATB_GET_KIND(block) != AT_HEAD) { - GC_EXIT(); - return NULL; - } - if (MP_STATE_MEM(gc_lock_depth) > 0) { GC_EXIT(); return NULL; } + // get the GC block number corresponding to this pointer + assert(VERIFY_PTR(ptr)); + size_t block = BLOCK_FROM_PTR(ptr); + assert(ATB_GET_KIND(block) == AT_HEAD); + // compute number of new blocks that are requested size_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK; From 4e056d82cc7cfa1b40c135e1ac544d728231432e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 30 Nov 2017 10:54:33 +1100 Subject: [PATCH 066/828] esp8266/modules/webrepl_setup: Fix first-time enable of WebREPL. Prior to this fix, enabling WebREPL for the first time via webrepl_setup did not work at all because "boot.py" did not contain any lines with "webrepl" in them that could be uncommented. --- ports/esp8266/modules/webrepl_setup.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/ports/esp8266/modules/webrepl_setup.py b/ports/esp8266/modules/webrepl_setup.py index 5288c49c0..129313a21 100644 --- a/ports/esp8266/modules/webrepl_setup.py +++ b/ports/esp8266/modules/webrepl_setup.py @@ -35,12 +35,6 @@ def exists(fname): except OSError: return False -def copy_stream(s_in, s_out): - buf = bytearray(64) - while 1: - sz = s_in.readinto(buf) - s_out.write(buf, sz) - def get_daemon_status(): with open(RC) as f: @@ -51,22 +45,22 @@ def get_daemon_status(): return True return None -def add_daemon(): - with open(RC) as old_f, open(RC + ".tmp", "w") as new_f: - new_f.write("import webrepl\nwebrepl.start()\n") - copy_stream(old_f, new_f) def change_daemon(action): LINES = ("import webrepl", "webrepl.start()") with open(RC) as old_f, open(RC + ".tmp", "w") as new_f: + found = False for l in old_f: for patt in LINES: if patt in l: + found = True if action and l.startswith("#"): l = l[1:] elif not action and not l.startswith("#"): l = "#" + l new_f.write(l) + if not found: + new_f.write("import webrepl\nwebrepl.start()\n") # FatFs rename() is not POSIX compliant, will raise OSError if # dest file exists. os.remove(RC) From 64f11470bec3c4ce9122593d3dd968e4127eb545 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 30 Nov 2017 12:06:41 +1100 Subject: [PATCH 067/828] py/objgenerator: Remove unreachable code for STOP_ITERATION case. This commit essentially reverts aa9dbb1b033a8163e07fcf5763fc20146354cc48 where this if-condition was added. It seems that even when that commit was made the code was never reached by any tests, nor reachable by analysis (see below). The same is true with the code as it currently stands: no test triggers this if-condition, nor any uasyncio examples. Analysing the flow of the program also shows that it's not reachable: ==START== -> to trigger this if condition mp_execute_bytecode() must return MP_VM_RETURN_YIELD with *sp==MP_OBJ_STOP_ITERATION -> mp_execute_bytecode() can only return MP_VM_RETURN_YIELD from the MP_BC_YIELD_VALUE bytecode, which can happen in 2 ways: -> 1) from a "yield " in bytecode, but must always be a proper object, never MP_OBJ_STOP_ITERATION; ==END1== -> 2) via yield from, via mp_resume() which must return MP_VM_RETURN_YIELD with ret_value==MP_OBJ_STOP_ITERATION, which can happen in 3 ways: -> 1) it delegates to mp_obj_gen_resume(); go back to ==START== -> 2) it returns MP_VM_RETURN_YIELD directly but with a guard that ret_val!=MP_OBJ_STOP_ITERATION; ==END2== -> 3) it returns MP_VM_RETURN_YIELD with ret_val set from mp_call_method_n_kw(), but mp_call_method_n_kw() must return a proper object, never MP_OBJ_STOP_ITERATION; ==END3== The above shows there is no way to trigger the if-condition and it can be removed. --- py/objgenerator.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/py/objgenerator.c b/py/objgenerator.c index bf0bbb0e6..9a294debb 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -125,9 +125,6 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ case MP_VM_RETURN_YIELD: *ret_val = *self->code_state.sp; - if (*ret_val == MP_OBJ_STOP_ITERATION) { - self->code_state.ip = 0; - } break; case MP_VM_RETURN_EXCEPTION: { From 7d25a192206f7effa47c24a52607f00efe86e2ef Mon Sep 17 00:00:00 2001 From: Paul Carver Date: Sun, 26 Nov 2017 11:29:55 -0500 Subject: [PATCH 068/828] docs/library/utime: Fix incorrect example with ticks_diff args order. The parameter order in the example for ticks_diff was incorrect. If it's "too early" that means that scheduled time is greater than current time and if it's "running late" then scheduled time would be less than current time. --- docs/library/utime.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/library/utime.rst b/docs/library/utime.rst index a39f5ee73..7fe83f5ab 100644 --- a/docs/library/utime.rst +++ b/docs/library/utime.rst @@ -187,14 +187,14 @@ Functions # This code snippet is not optimized now = time.ticks_ms() scheduled_time = task.scheduled_time() - if ticks_diff(now, scheduled_time) > 0: + if ticks_diff(scheduled_time, now) > 0: print("Too early, let's nap") - sleep_ms(ticks_diff(now, scheduled_time)) + sleep_ms(ticks_diff(scheduled_time, now)) task.run() - elif ticks_diff(now, scheduled_time) == 0: + elif ticks_diff(scheduled_time, now) == 0: print("Right at time!") task.run() - elif ticks_diff(now, scheduled_time) < 0: + elif ticks_diff(scheduled_time, now) < 0: print("Oops, running late, tell task to run faster!") task.run(run_faster=true) From cb9da2279b021ea7a916cbf16dd0e05c009c22bb Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 30 Nov 2017 20:32:49 +0200 Subject: [PATCH 069/828] docs/uselect: ipoll: Fix grammar/wording of one-shot flag description. --- docs/library/uselect.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/library/uselect.rst b/docs/library/uselect.rst index aa70bec69..211b2a4a8 100644 --- a/docs/library/uselect.rst +++ b/docs/library/uselect.rst @@ -83,10 +83,10 @@ Methods way to poll on streams. If *flags* is 1, one-shot behavior for events is employed: streams for - which events happened, event mask will be automatically reset (equivalent - to ``poll.modify(obj, 0)``), so new events for such a stream won't be - processed until new mask is set with `poll.modify()`. This behavior is - useful for asynchronous I/O schedulers. + which events happened will have their event masks automatically reset + (equivalent to ``poll.modify(obj, 0)``), so new events for such a stream + won't be processed until new mask is set with `poll.modify()`. This + behavior is useful for asynchronous I/O schedulers. .. admonition:: Difference to CPython :class: attention From 8f202319096f3e6a465b0632f5b7783096cc2ee1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 1 Dec 2017 14:48:17 +1100 Subject: [PATCH 070/828] stm32/boards/stm32f767_af.csv: Update AF table based on datasheet. Based on ST datasheet, DocID029041 Rev 3, DM00273119.pdf. --- ports/stm32/boards/stm32f767_af.csv | 160 ++++++++++++++-------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/ports/stm32/boards/stm32f767_af.csv b/ports/stm32/boards/stm32f767_af.csv index 1708dfcca..ab2e248bb 100644 --- a/ports/stm32/boards/stm32f767_af.csv +++ b/ports/stm32/boards/stm32f767_af.csv @@ -1,64 +1,64 @@ Port,,AF0,AF1,AF2,AF3,AF4,AF5,AF6,AF7,AF8,AF9,AF10,AF11,AF12,AF13,AF14,AF15 -,,SYS,TIM1/2,TIM3/4/5,TIM8/9/10/11/LPTIM1/CEC,I2C1/2/3/4/CEC,SPI1/2/3/4/5/6,SPI3/SAI1,SPI2/3/USART1/2/3/UART5/SPDIFRX,SAI2/USART6/UART4/5/7/8/SPDIFRX,CAN1/2/TIM12/13/14/QUADSPI/LCD,SAI2/QUADSPI/SDMMC2/OTG2_HS/OTG1_FS,SDMMC2/ETH,FMC/SDMMC1/OTG2_FS,DCMI,LCD,SYS +,,SYS,I2C4/UART5/TIM1/2,TIM3/4/5,TIM8/9/10/11/LPTIM1/DFSDM1/CEC,I2C1/2/3/4/USART1/CEC,SPI1/I2S1/SPI2/I2S2/SPI3/I2S3/SPI4/5/6,SPI2/I2S2/SPI3/I2S3/SAI1/I2C4/UART4/DFSDM1,SPI2/I2S2/SPI3/I2S3/SPI6/USART1/2/3/UART5/DFSDM1/SPDIF,SPI6/SAI2/USART6/UART4/5/7/8/OTG_FS/SPDIF,CAN1/2/TIM12/13/14/QUADSPI/FMC/LCD,SAI2/QUADSPI/SDMMC2/DFSDM1/OTG2_HS/OTG1_FS/LCD,I2C4/CAN3/SDMMC2/ETH,UART7/FMC/SDMMC1/MDIOS/OTG2_FS,DCMI/LCD/DSI,LCD,SYS PortA,PA0,,TIM2_CH1/TIM2_ETR,TIM5_CH1,TIM8_ETR,,,,USART2_CTS,UART4_TX,,SAI2_SD_B,ETH_MII_CRS,,,,EVENTOUT PortA,PA1,,TIM2_CH2,TIM5_CH2,,,,,USART2_RTS,UART4_RX,QUADSPI_BK1_IO3,SAI2_MCK_B,ETH_MII_RX_CLK/ETH_RMII_REF_CLK,,,LCD_R2,EVENTOUT -PortA,PA2,,TIM2_CH3,TIM5_CH3,TIM9_CH1,,,,USART2_TX,SAI2_SCK_B,,,ETH_MDIO,,,LCD_R1,EVENTOUT -PortA,PA3,,TIM2_CH4,TIM5_CH4,TIM9_CH2,,,,USART2_RX,,,OTG_HS_ULPI_D0,ETH_MII_COL,,,LCD_B5,EVENTOUT -PortA,PA4,,,,,,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,USART2_CK,,,,,OTG_HS_SOF,DCMI_HSYNC,LCD_VSYNC,EVENTOUT -PortA,PA5,,TIM2_CH1/TIM2_ETR,,TIM8_CH1N,,SPI1_SCK/I2S1_CK,,,,,OTG_HS_ULPI_CK,,,,LCD_R4,EVENTOUT -PortA,PA6,,TIM1_BKIN,TIM3_CH1,TIM8_BKIN,,SPI1_MISO,,,,TIM13_CH1,,,,DCMI_PIXCLK,LCD_G2,EVENTOUT -PortA,PA7,,TIM1_CH1N,TIM3_CH2,TIM8_CH1N,,SPI1_MOSI/I2S1_SD,,,,TIM14_CH1,,ETH_MII_RX_DV/ETH_RMII_CRS_DV,FMC_SDNWE,,,EVENTOUT -PortA,PA8,MCO1,TIM1_CH1,,TIM8_BKIN2,I2C3_SCL,,,USART1_CK,,,OTG_FS_SOF,,,,LCD_R6,EVENTOUT -PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,SPI2_SCK/I2S2_CK,,USART1_TX,,,,,,DCMI_D0,,EVENTOUT -PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,,,DCMI_D1,,EVENTOUT -PortA,PA11,,TIM1_CH4,,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,,,LCD_R4,EVENTOUT -PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS,SAI2_FS_B,CAN1_TX,OTG_FS_DP,,,,LCD_R5,EVENTOUT -PortA,PA13,JTMS,SWDIO,,,,,,,,,,,,,,EVENTOUT -PortA,PA14,JTCK,SWCLK,,,,,,,,,,,,,,EVENTOUT -PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,HDMICEC,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,,UART4_RTS,,,,,,,EVENTOUT -PortB,PB0,,TIM1_CH2N,TIM3_CH3T,TIM8_CH2N,,,,,UART4_CTS,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT -PortB,PB1,,TIM1_CH3N,TIM3_CH4T,TIM8_CH3N,,,,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT -PortB,PB2,,,,,,,SAI1_SD_A,SPI3_MOSI/I2S3_SD,,QUADSPI_CLK,,,,,,EVENTOUT -PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK/I2S1_CK,SPI3_SCK/I2S3_CK,,,,SDMMC2_D2,,,,,EVENTOUT -PortB,PB4,NJTRST,,TIM3_CH1,,,SPI1_MISO,SPI3_MISO,SPI2_NSS/I2S2_WS,,,SDMMC2_D3,,,,,EVENTOUT -PortB,PB5,,,TIM3_CH2,,I2C1_SMBA,SPI1_MOSI/I2S1_SD,SPI3_MOSI/I2S3_SD,,,CAN2_RX,OTG_HS_ULPI_D7,ETH_PPS_OUT,FMC_SDCKE1,DCMI_D10,,EVENTOUT -PortB,PB6,,,TIM4_CH1,HDMICEC,I2C1_SCL,,,USART1_TX,,CAN2_TX,QUADSPI_BK1_NCS,,FMC_SDNE1,DCMI_D5,,EVENTOUT -PortB,PB7,,,TIM4_CH2,,I2C1_SDA,,,USART1_RX,,,,,FMC_NL,DCMI_VSYNC,,EVENTOUT -PortB,PB8,,,TIM4_CH3,TIM10_CH1,I2C1_SCL,,,,,CAN1_RX,SDMMC2_D4,ETH_MII_TXD3,SDMMC1_D4,DCMI_D6,LCD_B6,EVENTOUT -PortB,PB9,,,TIM4_CH4,TIM11_CH1,I2C1_SDA,SPI2_NSS/I2S2_WS,,,,CAN1_TX,SDMMC2_D5,,SDMMC1_D5,DCMI_D7,LCD_B7,EVENTOUT -PortB,PB10,,TIM2_CH3,,,I2C2_SCL,SPI2_SCK/I2S2_CK,,USART3_TX,,,OTG_HS_ULPI_D3,ETH_MII_RX_ER,,,LCD_G4,EVENTOUT -PortB,PB11,,TIM2_CH4,,,I2C2_SDA,,,USART3_RX,,,OTG_HS_ULPI_D4,ETH_MII_TX_EN/ETH_RMII_TX_EN,,,LCD_G5,EVENTOUT -PortB,PB12,,TIM1_BKIN,,,I2C2_SMBA,SPI2_NSS/I2S2_WS,,USART3_CK,,CAN2_RX,OTG_HS_ULPI_D5,ETH_MII_TXD0/ETH_RMII_TXD0,OTG_HS_ID,,,EVENTOUT -PortB,PB13,,TIM1_CH1N,,,,SPI2_SCK/I2S2_CK,,USART3_CTS,,CAN2_TX,OTG_HS_ULPI_D6,ETH_MII_TXD1/ETH_RMII_TXD1,,,,EVENTOUT -PortB,PB14,,TIM1_CH2N,,TIM8_CH2N,,SPI2_MISO,,USART3_RTS,,TIM12_CH1,SDMMC2_D0,,OTG_HS_DM,,,EVENTOUT -PortB,PB15,RTC_REFIN,TIM1_CH3N,,TIM8_CH3N,,SPI2_MOSI/I2S2_SD,,,,TIM12_CH2,SDMMC2_D1,,OTG_HS_DP,,,EVENTOUT -PortC,PC0,,,,,,,,,SAI2_FS_B,,OTG_HS_ULPI_STP,,FMC_SDNWE,,LCD_R5,EVENTOUT -PortC,PC1,TRACED0,,,,,SPI2_MOSI/I2S2_SD,SAI1_SD_A,,,,,ETH_MDC,,,,EVENTOUT -PortC,PC2,,,,,,SPI2_MISO,,,,,OTG_HS_ULPI_DIR,ETH_MII_TXD2,FMC_SDNE0,,,EVENTOUT -PortC,PC3,,,,,,SPI2_MOSI/I2S2_SD,,,,,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK,FMC_SDCKE0,,,EVENTOUT -PortC,PC4,,,,,,I2S1_MCK,,,SPDIFRX_IN2,,,ETH_MII_RXD0/ETH_RMII_RXD0,FMC_SDNE0,,,EVENTOUT -PortC,PC5,,,,,,,,,SPDIFRX_IN3,,,ETH_MII_RXD1/ETH_RMII_RXD1,FMC_SDCKE0,,,EVENTOUT -PortC,PC6,,,TIM3_CH1,TIM8_CH1,,I2S2_MCK,,,USART6_TX,,SDMMC2_D6,,SDMMC1_D6,DCMI_D0,LCD_HSYNC,EVENTOUT -PortC,PC7,,,TIM3_CH2,TIM8_CH2,,,I2S3_MCK,,USART6_RX,,SDMMC2_D7,,SDMMC1_D7,DCMI_D1,LCD_G6,EVENTOUT -PortC,PC8,TRACED1,,TIM3_CH3,TIM8_CH3,,,,UART5_RTS,USART6_CK,,,,SDMMC1_D0,DCMI_D2,,EVENTOUT -PortC,PC9,MCO2,,TIM3_CH4,TIM8_CH4,I2C3_SDA,I2S2_CKIN,,UART5_CTS,,QUADSPI_BK1_IO0,,,SDMMC1_D1,DCMI_D3,,EVENTOUT -PortC,PC10,,,,,,,SPI3_SCK/I2S3_CK,USART3_TX,UART4_TX,QUADSPI_BK1_IO1,,,SDMMC1_D2,DCMI_D8,LCD_R2,EVENTOUT -PortC,PC11,,,,,,,SPI3_MISO,USART3_RX,UART4_RX,QUADSPI_BK2_NCS,,,SDMMC1_D3,DCMI_D4,,EVENTOUT +PortA,PA2,,TIM2_CH3,TIM5_CH3,TIM9_CH1,,,,USART2_TX,SAI2_SCK_B,,,ETH_MDIO,MDIOS_MDIO,,LCD_R1,EVENTOUT +PortA,PA3,,TIM2_CH4,TIM5_CH4,TIM9_CH2,,,,USART2_RX,,LCD_B2,OTG_HS_ULPI_D0,ETH_MII_COL,,,LCD_B5,EVENTOUT +PortA,PA4,,,,,,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,USART2_CK,SPI6_NSS,,,,OTG_HS_SOF,DCMI_HSYNC,LCD_VSYNC,EVENTOUT +PortA,PA5,,TIM2_CH1/TIM2_ETR,,TIM8_CH1N,,SPI1_SCK/I2S1_CK,,,SPI6_SCK,,OTG_HS_ULPI_CK,,,,LCD_R4,EVENTOUT +PortA,PA6,,TIM1_BKIN,TIM3_CH1,TIM8_BKIN,,SPI1_MISO,,,SPI6_MISO,TIM13_CH1,,,MDIOS_MDC,DCMI_PIXCLK,LCD_G2,EVENTOUT +PortA,PA7,,TIM1_CH1N,TIM3_CH2,TIM8_CH1N,,SPI1_MOSI/I2S1_SD,,,SPI6_MOSI,TIM14_CH1,,ETH_MII_RX_DV/ETH_RMII_CRS_DV,FMC_SDNWE,,,EVENTOUT +PortA,PA8,MCO1,TIM1_CH1,,TIM8_BKIN2,I2C3_SCL,,,USART1_CK,,,OTG_FS_SOF,CAN3_RX,UART7_RX,LCD_B3,LCD_R6,EVENTOUT +PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,SPI2_SCK/I2S2_CK,,USART1_TX,,,,,,DCMI_D0,LCD_R5,EVENTOUT +PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,LCD_B4,OTG_FS_ID,,MDIOS_MDIO,DCMI_D1,LCD_B1,EVENTOUT +PortA,PA11,,TIM1_CH4,,,,SPI2_NSS/I2S2_WS,UART4_RX,USART1_CTS,,CAN1_RX,OTG_FS_DM,,,,LCD_R4,EVENTOUT +PortA,PA12,,TIM1_ETR,,,,SPI2_SCK/I2S2_CK,UART4_TX,USART1_RTS,SAI2_FS_B,CAN1_TX,OTG_FS_DP,,,,LCD_R5,EVENTOUT +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT +PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,HDMI_CEC,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,SPI6_NSS,UART4_RTS,,,CAN3_TX,UART7_TX,,,EVENTOUT +PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,,,DFSDM1_CKOUT,,UART4_CTS,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,LCD_G1,EVENTOUT +PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,,,DFSDM1_DATAIN1,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,LCD_G0,EVENTOUT +PortB,PB2,,,,,,,SAI1_SD_A,SPI3_MOSI/I2S3_SD,,QUADSPI_CLK,DFSDM1_CKIN1,,,,,EVENTOUT +PortB,PB3,JTDO/TRACESWO,TIM2_CH2,,,,SPI1_SCK/I2S1_CK,SPI3_SCK/I2S3_CK,,SPI6_SCK,,SDMMC2_D2,CAN3_RX,UART7_RX,,,EVENTOUT +PortB,PB4,NJTRST,,TIM3_CH1,,,SPI1_MISO,SPI3_MISO,SPI2_NSS/I2S2_WS,SPI6_MISO,,SDMMC2_D3,CAN3_TX,UART7_TX,,,EVENTOUT +PortB,PB5,,UART5_RX,TIM3_CH2,,I2C1_SMBA,SPI1_MOSI/I2S1_SD,SPI3_MOSI/I2S3_SD,,SPI6_MOSI,CAN2_RX,OTG_HS_ULPI_D7,ETH_PPS_OUT,FMC_SDCKE1,DCMI_D10,LCD_G7,EVENTOUT +PortB,PB6,,UART5_TX,TIM4_CH1,HDMI_CEC,I2C1_SCL,,DFSDM1_DATAIN5,USART1_TX,,CAN2_TX,QUADSPI_BK1_NCS,I2C4_SCL,FMC_SDNE1,DCMI_D5,,EVENTOUT +PortB,PB7,,,TIM4_CH2,,I2C1_SDA,,DFSDM1_CKIN5,USART1_RX,,,,I2C4_SDA,FMC_NL,DCMI_VSYNC,,EVENTOUT +PortB,PB8,,I2C4_SCL,TIM4_CH3,TIM10_CH1,I2C1_SCL,,DFSDM1_CKIN7,UART5_RX,,CAN1_RX,SDMMC2_D4,ETH_MII_TXD3,SDMMC1_D4,DCMI_D6,LCD_B6,EVENTOUT +PortB,PB9,,I2C4_SDA,TIM4_CH4,TIM11_CH1,I2C1_SDA,SPI2_NSS/I2S2_WS,DFSDM1_DATAIN7,UART5_TX,,CAN1_TX,SDMMC2_D5,I2C4_SMBA,SDMMC1_D5,DCMI_D7,LCD_B7,EVENTOUT +PortB,PB10,,TIM2_CH3,,,I2C2_SCL,SPI2_SCK/I2S2_CK,DFSDM1_DATAIN7,USART3_TX,,QUADSPI_BK1_NCS,OTG_HS_ULPI_D3,ETH_MII_RX_ER,,,LCD_G4,EVENTOUT +PortB,PB11,,TIM2_CH4,,,I2C2_SDA,,DFSDM1_CKIN7,USART3_RX,,,OTG_HS_ULPI_D4,ETH_MII_TX_EN/ETH_RMII_TX_EN,,DSI_TE,LCD_G5,EVENTOUT +PortB,PB12,,TIM1_BKIN,,,I2C2_SMBA,SPI2_NSS/I2S2_WS,DFSDM1_DATAIN1,USART3_CK,UART5_RX,CAN2_RX,OTG_HS_ULPI_D5,ETH_MII_TXD0/ETH_RMII_TXD0,OTG_HS_ID,,,EVENTOUT +PortB,PB13,,TIM1_CH1N,,,,SPI2_SCK/I2S2_CK,DFSDM1_CKIN1,USART3_CTS,UART5_TX,CAN2_TX,OTG_HS_ULPI_D6,ETH_MII_TXD1/ETH_RMII_TXD1,,,,EVENTOUT +PortB,PB14,,TIM1_CH2N,,TIM8_CH2N,USART1_TX,SPI2_MISO,DFSDM1_DATAIN2,USART3_RTS,UART4_RTS,TIM12_CH1,SDMMC2_D0,,OTG_HS_DM,,,EVENTOUT +PortB,PB15,RTC_REFIN,TIM1_CH3N,,TIM8_CH3N,USART1_RX,SPI2_MOSI/I2S2_SD,DFSDM1_CKIN2,,UART4_CTS,TIM12_CH2,SDMMC2_D1,,OTG_HS_DP,,,EVENTOUT +PortC,PC0,,,,DFSDM1_CKIN0,,,DFSDM1_DATAIN4,,SAI2_FS_B,,OTG_HS_ULPI_STP,,FMC_SDNWE,,LCD_R5,EVENTOUT +PortC,PC1,TRACED0,,,DFSDM1_DATAIN0,,SPI2_MOSI/I2S2_SD,SAI1_SD_A,,,,DFSDM1_CKIN4,ETH_MDC,MDIOS_MDC,,,EVENTOUT +PortC,PC2,,,,DFSDM1_CKIN1,,SPI2_MISO,DFSDM1_CKOUT,,,,OTG_HS_ULPI_DIR,ETH_MII_TXD2,FMC_SDNE0,,,EVENTOUT +PortC,PC3,,,,DFSDM1_DATAIN1,,SPI2_MOSI/I2S2_SD,,,,,OTG_HS_ULPI_NXT,ETH_MII_TX_CLK,FMC_SDCKE0,,,EVENTOUT +PortC,PC4,,,,DFSDM1_CKIN2,,I2S1_MCK,,,SPDIFRX_IN2,,,ETH_MII_RXD0/ETH_RMII_RXD0,FMC_SDNE0,,,EVENTOUT +PortC,PC5,,,,DFSDM1_DATAIN2,,,,,SPDIFRX_IN3,,,ETH_MII_RXD1/ETH_RMII_RXD1,FMC_SDCKE0,,,EVENTOUT +PortC,PC6,,,TIM3_CH1,TIM8_CH1,,I2S2_MCK,,DFSDM1_CKIN3,USART6_TX,FMC_NWAIT,SDMMC2_D6,,SDMMC1_D6,DCMI_D0,LCD_HSYNC,EVENTOUT +PortC,PC7,,,TIM3_CH2,TIM8_CH2,,,I2S3_MCK,DFSDM1_DATAIN3,USART6_RX,FMC_NE1,SDMMC2_D7,,SDMMC1_D7,DCMI_D1,LCD_G6,EVENTOUT +PortC,PC8,TRACED1,,TIM3_CH3,TIM8_CH3,,,,UART5_RTS,USART6_CK,FMC_NE2/FMC_NCE,,,SDMMC1_D0,DCMI_D2,,EVENTOUT +PortC,PC9,MCO2,,TIM3_CH4,TIM8_CH4,I2C3_SDA,I2S_CKIN,,UART5_CTS,,QUADSPI_BK1_IO0,LCD_G3,,SDMMC1_D1,DCMI_D3,LCD_B2,EVENTOUT +PortC,PC10,,,,DFSDM1_CKIN5,,,SPI3_SCK/I2S3_CK,USART3_TX,UART4_TX,QUADSPI_BK1_IO1,,,SDMMC1_D2,DCMI_D8,LCD_R2,EVENTOUT +PortC,PC11,,,,DFSDM1_DATAIN5,,,SPI3_MISO,USART3_RX,UART4_RX,QUADSPI_BK2_NCS,,,SDMMC1_D3,DCMI_D4,,EVENTOUT PortC,PC12,TRACED3,,,,,,SPI3_MOSI/I2S3_SD,USART3_CK,UART5_TX,,,,SDMMC1_CK,DCMI_D9,,EVENTOUT PortC,PC13,,,,,,,,,,,,,,,,EVENTOUT PortC,PC14,,,,,,,,,,,,,,,,EVENTOUT PortC,PC15,,,,,,,,,,,,,,,,EVENTOUT -PortD,PD0,,,,,,,,,,CAN1_RX,,,FMC_D2,,,EVENTOUT -PortD,PD1,,,,,,,,,,CAN1_TX,,,FMC_D3,,,EVENTOUT +PortD,PD0,,,,DFSDM1_CKIN6,,,DFSDM1_DATAIN7,,UART4_RX,CAN1_RX,,,FMC_D2,,,EVENTOUT +PortD,PD1,,,,DFSDM1_DATAIN6,,,DFSDM1_CKIN7,,UART4_TX,CAN1_TX,,,FMC_D3,,,EVENTOUT PortD,PD2,TRACED2,,TIM3_ETR,,,,,,UART5_RX,,,,SDMMC1_CMD,DCMI_D11,,EVENTOUT -PortD,PD3,,,,,,SPI2_SCK/I2S2_CK,,USART2_CTS,,,,,FMC_CLK,DCMI_D5,LCD_G7,EVENTOUT -PortD,PD4,,,,,,,,USART2_RTS,,,,,FMC_NOE,,,EVENTOUT +PortD,PD3,,,,DFSDM1_CKOUT,,SPI2_SCK/I2S2_CK,DFSDM1_DATAIN0,USART2_CTS,,,,,FMC_CLK,DCMI_D5,LCD_G7,EVENTOUT +PortD,PD4,,,,,,,DFSDM1_CKIN0,USART2_RTS,,,,,FMC_NOE,,,EVENTOUT PortD,PD5,,,,,,,,USART2_TX,,,,,FMC_NWE,,,EVENTOUT -PortD,PD6,,,,,,SPI3_MOSI/I2S3_SD,SAI1_SD_A,USART2_RX,,,,SDMMC2_CK,FMC_NWAIT,DCMI_D10,LCD_B2,EVENTOUT -PortD,PD7,,,,,,,,USART2_CK,SPDIFRX_IN0,,,SDMMC2_CMD,FMC_NE1,,,EVENTOUT -PortD,PD8,,,,,,,,USART3_TX,SPDIFRX_IN1,,,,FMC_D13,,,EVENTOUT -PortD,PD9,,,,,,,,USART3_RX,,,,,FMC_D14,,,EVENTOUT -PortD,PD10,,,,,,,,USART3_CK,,,,,FMC_D15,,LCD_B3,EVENTOUT +PortD,PD6,,,,DFSDM1_CKIN4,,SPI3_MOSI/I2S3_SD,SAI1_SD_A,USART2_RX,,,DFSDM1_DATAIN1,SDMMC2_CK,FMC_NWAIT,DCMI_D10,LCD_B2,EVENTOUT +PortD,PD7,,,,DFSDM1_DATAIN4,,SPI1_MOSI/I2S1_SD,DFSDM1_CKIN1,USART2_CK,SPDIFRX_IN0,,,SDMMC2_CMD,FMC_NE1,,,EVENTOUT +PortD,PD8,,,,DFSDM1_CKIN3,,,,USART3_TX,SPDIFRX_IN1,,,,FMC_D13,,,EVENTOUT +PortD,PD9,,,,DFSDM1_DATAIN3,,,,USART3_RX,,,,,FMC_D14,,,EVENTOUT +PortD,PD10,,,,DFSDM1_CKOUT,,,,USART3_CK,,,,,FMC_D15,,LCD_B3,EVENTOUT PortD,PD11,,,,,I2C4_SMBA,,,USART3_CTS,,QUADSPI_BK1_IO0,SAI2_SD_A,,FMC_A16/FMC_CLE,,,EVENTOUT PortD,PD12,,,TIM4_CH1,LPTIM1_IN1,I2C4_SCL,,,USART3_RTS,,QUADSPI_BK1_IO1,SAI2_FS_A,,FMC_A17/FMC_ALE,,,EVENTOUT PortD,PD13,,,TIM4_CH2,LPTIM1_OUT,I2C4_SDA,,,,,QUADSPI_BK1_IO3,SAI2_SCK_A,,FMC_A18,,,EVENTOUT @@ -68,16 +68,16 @@ PortE,PE0,,,TIM4_ETR,LPTIM1_ETR,,,,,UART8_RX,,SAI2_MCK_A,,FMC_NBL0,DCMI_D2,,EVEN PortE,PE1,,,,LPTIM1_IN2,,,,,UART8_TX,,,,FMC_NBL1,DCMI_D3,,EVENTOUT PortE,PE2,TRACECLK,,,,,SPI4_SCK,SAI1_MCLK_A,,,QUADSPI_BK1_IO2,,ETH_MII_TXD3,FMC_A23,,,EVENTOUT PortE,PE3,TRACED0,,,,,,SAI1_SD_B,,,,,,FMC_A19,,,EVENTOUT -PortE,PE4,TRACED1,,,,,SPI4_NSS,SAI1_FS_A,,,,,,FMC_A20,DCMI_D4,LCD_B0,EVENTOUT -PortE,PE5,TRACED2,,,TIM9_CH1,,SPI4_MISO,SAI1_SCK_A,,,,,,FMC_A21,DCMI_D6,LCD_G0,EVENTOUT +PortE,PE4,TRACED1,,,,,SPI4_NSS,SAI1_FS_A,,,,DFSDM1_DATAIN3,,FMC_A20,DCMI_D4,LCD_B0,EVENTOUT +PortE,PE5,TRACED2,,,TIM9_CH1,,SPI4_MISO,SAI1_SCK_A,,,,DFSDM1_CKIN3,,FMC_A21,DCMI_D6,LCD_G0,EVENTOUT PortE,PE6,TRACED3,TIM1_BKIN2,,TIM9_CH2,,SPI4_MOSI,SAI1_SD_A,,,,SAI2_MCK_B,,FMC_A22,DCMI_D7,LCD_G1,EVENTOUT -PortE,PE7,,TIM1_ETR,,,,,,,UART7_RX,,QUADSPI_BK2_IO0,,FMC_D4,,,EVENTOUT -PortE,PE8,,TIM1_CH1N,,,,,,,UART7_TX,,QUADSPI_BK2_IO1,,FMC_D5,,,EVENTOUT -PortE,PE9,,TIM1_CH1,,,,,,,UART7_RTS,,QUADSPI_BK2_IO2,,FMC_D6,,,EVENTOUT -PortE,PE10,,TIM1_CH2N,,,,,,,UART7_CTS,,QUADSPI_BK2_IO3,,FMC_D7,,,EVENTOUT -PortE,PE11,,TIM1_CH2,,,,SPI4_NSS,,,,,SAI2_SD_B,,FMC_D8,,LCD_G3,EVENTOUT -PortE,PE12,,TIM1_CH3N,,,,SPI4_SCK,,,,,SAI2_SCK_B,,FMC_D9,,LCD_B4,EVENTOUT -PortE,PE13,,TIM1_CH3,,,,SPI4_MISO,,,,,SAI2_FS_B,,FMC_D10,,LCD_DE,EVENTOUT +PortE,PE7,,TIM1_ETR,,,,,DFSDM1_DATAIN2,,UART7_RX,,QUADSPI_BK2_IO0,,FMC_D4,,,EVENTOUT +PortE,PE8,,TIM1_CH1N,,,,,DFSDM1_CKIN2,,UART7_TX,,QUADSPI_BK2_IO1,,FMC_D5,,,EVENTOUT +PortE,PE9,,TIM1_CH1,,,,,DFSDM1_CKOUT,,UART7_RTS,,QUADSPI_BK2_IO2,,FMC_D6,,,EVENTOUT +PortE,PE10,,TIM1_CH2N,,,,,DFSDM1_DATAIN4,,UART7_CTS,,QUADSPI_BK2_IO3,,FMC_D7,,,EVENTOUT +PortE,PE11,,TIM1_CH2,,,,SPI4_NSS,DFSDM1_CKIN4,,,,SAI2_SD_B,,FMC_D8,,LCD_G3,EVENTOUT +PortE,PE12,,TIM1_CH3N,,,,SPI4_SCK,DFSDM1_DATAIN5,,,,SAI2_SCK_B,,FMC_D9,,LCD_B4,EVENTOUT +PortE,PE13,,TIM1_CH3,,,,SPI4_MISO,DFSDM1_CKIN5,,,,SAI2_FS_B,,FMC_D10,,LCD_DE,EVENTOUT PortE,PE14,,TIM1_CH4,,,,SPI4_MOSI,,,,,SAI2_MCK_B,,FMC_D11,,LCD_CLK,EVENTOUT PortE,PE15,,TIM1_BKIN,,,,,,,,,,,FMC_D12,,LCD_R7,EVENTOUT PortF,PF0,,,,,I2C2_SDA,,,,,,,,FMC_A0,,,EVENTOUT @@ -90,11 +90,11 @@ PortF,PF6,,,,TIM10_CH1,,SPI5_NSS,SAI1_SD_B,,UART7_RX,QUADSPI_BK1_IO3,,,,,,EVENTO PortF,PF7,,,,TIM11_CH1,,SPI5_SCK,SAI1_MCLK_B,,UART7_TX,QUADSPI_BK1_IO2,,,,,,EVENTOUT PortF,PF8,,,,,,SPI5_MISO,SAI1_SCK_B,,UART7_RTS,TIM13_CH1,QUADSPI_BK1_IO0,,,,,EVENTOUT PortF,PF9,,,,,,SPI5_MOSI,SAI1_FS_B,,UART7_CTS,TIM14_CH1,QUADSPI_BK1_IO1,,,,,EVENTOUT -PortF,PF10,,,,,,,,,,,,,,DCMI_D11,LCD_DE,EVENTOUT +PortF,PF10,,,,,,,,,,QUADSPI_CLK,,,,DCMI_D11,LCD_DE,EVENTOUT PortF,PF11,,,,,,SPI5_MOSI,,,,,SAI2_SD_B,,FMC_SDNRAS,DCMI_D12,,EVENTOUT PortF,PF12,,,,,,,,,,,,,FMC_A6,,,EVENTOUT -PortF,PF13,,,,,I2C4_SMBA,,,,,,,,FMC_A7,,,EVENTOUT -PortF,PF14,,,,,I2C4_SCL,,,,,,,,FMC_A8,,,EVENTOUT +PortF,PF13,,,,,I2C4_SMBA,,DFSDM1_DATAIN6,,,,,,FMC_A7,,,EVENTOUT +PortF,PF14,,,,,I2C4_SCL,,DFSDM1_CKIN6,,,,,,FMC_A8,,,EVENTOUT PortF,PF15,,,,,I2C4_SDA,,,,,,,,FMC_A9,,,EVENTOUT PortG,PG0,,,,,,,,,,,,,FMC_A10,,,EVENTOUT PortG,PG1,,,,,,,,,,,,,FMC_A11,,,EVENTOUT @@ -102,12 +102,12 @@ PortG,PG2,,,,,,,,,,,,,FMC_A12,,,EVENTOUT PortG,PG3,,,,,,,,,,,,,FMC_A13,,,EVENTOUT PortG,PG4,,,,,,,,,,,,,FMC_A14/FMC_BA0,,,EVENTOUT PortG,PG5,,,,,,,,,,,,,FMC_A15/FMC_BA1,,,EVENTOUT -PortG,PG6,,,,,,,,,,,,,,DCMI_D12,LCD_R7,EVENTOUT -PortG,PG7,,,,,,,,,USART6_CK,,,,FMC_INT,DCMI_D13,LCD_CLK,EVENTOUT -PortG,PG8,,,,,,SPI6_NSS,,SPDIFRX_IN2,USART6_RTS,,,ETH_PPS_OUT,FMC_SDCLK,,,EVENTOUT -PortG,PG9,,,,,,,,SPDIFRX_IN3,USART6_RX,QUADSPI_BK2_IO2,SAI2_FS_B,SDMMC2_D0,FMC_NE2/FMC_NCE,DCMI_VSYNC,,EVENTOUT -PortG,PG10,,,,,,,,,,LCD_G3,SAI2_SD_B,SDMMC2_D1,FMC_NE3,DCMI_D2,LCD_B2,EVENTOUT -PortG,PG11,,,,,,,,SPDIFRX_IN0,,,SDMMC2_D2,ETH_MII_TX_EN/ETH_RMII_TX_EN,,DCMI_D3,LCD_B3,EVENTOUT +PortG,PG6,,,,,,,,,,,,,FMC_NE3,DCMI_D12,LCD_R7,EVENTOUT +PortG,PG7,,,,,,,SAI1_MCLK_A,,USART6_CK,,,,FMC_INT,DCMI_D13,LCD_CLK,EVENTOUT +PortG,PG8,,,,,,SPI6_NSS,,SPDIFRX_IN2,USART6_RTS,,,ETH_PPS_OUT,FMC_SDCLK,,LCD_G7,EVENTOUT +PortG,PG9,,,,,,SPI1_MISO,,SPDIFRX_IN3,USART6_RX,QUADSPI_BK2_IO2,SAI2_FS_B,SDMMC2_D0,FMC_NE2/FMC_NCE,DCMI_VSYNC,,EVENTOUT +PortG,PG10,,,,,,SPI1_NSS/I2S1_WS,,,,LCD_G3,SAI2_SD_B,SDMMC2_D1,FMC_NE3,DCMI_D2,LCD_B2,EVENTOUT +PortG,PG11,,,,,,SPI1_SCK/I2S1_CK,,SPDIFRX_IN0,,,SDMMC2_D2,ETH_MII_TX_EN/ETH_RMII_TX_EN,,DCMI_D3,LCD_B3,EVENTOUT PortG,PG12,,,,LPTIM1_IN1,,SPI6_MISO,,SPDIFRX_IN1,USART6_RTS,LCD_B4,,SDMMC2_D3,FMC_NE4,,LCD_B1,EVENTOUT PortG,PG13,TRACED0,,,LPTIM1_OUT,,SPI6_SCK,,,USART6_CTS,,,ETH_MII_TXD0/ETH_RMII_TXD0,FMC_A24,,LCD_R0,EVENTOUT PortG,PG14,TRACED1,,,LPTIM1_ETR,,SPI6_MOSI,,,USART6_TX,QUADSPI_BK2_IO3,,ETH_MII_TXD1/ETH_RMII_TXD1,FMC_A25,,LCD_B0,EVENTOUT @@ -116,7 +116,7 @@ PortH,PH0,,,,,,,,,,,,,,,,EVENTOUT PortH,PH1,,,,,,,,,,,,,,,,EVENTOUT PortH,PH2,,,,LPTIM1_IN2,,,,,,QUADSPI_BK2_IO0,SAI2_SCK_B,ETH_MII_CRS,FMC_SDCKE0,,LCD_R0,EVENTOUT PortH,PH3,,,,,,,,,,QUADSPI_BK2_IO1,SAI2_MCK_B,ETH_MII_COL,FMC_SDNE0,,LCD_R1,EVENTOUT -PortH,PH4,,,,,I2C2_SCL,,,,,,OTG_HS_ULPI_NXT,,,,,EVENTOUT +PortH,PH4,,,,,I2C2_SCL,,,,,LCD_G5,OTG_HS_ULPI_NXT,,,,LCD_G4,EVENTOUT PortH,PH5,,,,,I2C2_SDA,SPI5_NSS,,,,,,,FMC_SDNWE,,,EVENTOUT PortH,PH6,,,,,I2C2_SMBA,SPI5_SCK,,,,TIM12_CH1,,ETH_MII_RXD2,FMC_SDNE1,DCMI_D8,,EVENTOUT PortH,PH7,,,,,I2C3_SCL,SPI5_MISO,,,,,,ETH_MII_RXD3,FMC_SDCKE1,DCMI_D9,,EVENTOUT @@ -125,8 +125,8 @@ PortH,PH9,,,,,I2C3_SMBA,,,,,TIM12_CH2,,,FMC_D17,DCMI_D0,LCD_R3,EVENTOUT PortH,PH10,,,TIM5_CH1,,I2C4_SMBA,,,,,,,,FMC_D18,DCMI_D1,LCD_R4,EVENTOUT PortH,PH11,,,TIM5_CH2,,I2C4_SCL,,,,,,,,FMC_D19,DCMI_D2,LCD_R5,EVENTOUT PortH,PH12,,,TIM5_CH3,,I2C4_SDA,,,,,,,,FMC_D20,DCMI_D3,LCD_R6,EVENTOUT -PortH,PH13,,,,TIM8_CH1N,,,,,,CAN1_TX,,,FMC_D21,,LCD_G2,EVENTOUT -PortH,PH14,,,,TIM8_CH2N,,,,,,,,,FMC_D22,DCMI_D4,LCD_G3,EVENTOUT +PortH,PH13,,,,TIM8_CH1N,,,,,UART4_TX,CAN1_TX,,,FMC_D21,,LCD_G2,EVENTOUT +PortH,PH14,,,,TIM8_CH2N,,,,,UART4_RX,CAN1_RX,,,FMC_D22,DCMI_D4,LCD_G3,EVENTOUT PortH,PH15,,,,TIM8_CH3N,,,,,,,,,FMC_D23,DCMI_D11,LCD_G4,EVENTOUT PortI,PI0,,,TIM5_CH4,,,SPI2_NSS/I2S2_WS,,,,,,,FMC_D24,DCMI_D13,LCD_G5,EVENTOUT PortI,PI1,,,,TIM8_BKIN2,,SPI2_SCK/I2S2_CK,,,,,,,FMC_D25,DCMI_D8,LCD_G6,EVENTOUT @@ -137,16 +137,16 @@ PortI,PI5,,,,TIM8_CH1,,,,,,,SAI2_SCK_A,,FMC_NBL3,DCMI_VSYNC,LCD_B5,EVENTOUT PortI,PI6,,,,TIM8_CH2,,,,,,,SAI2_SD_A,,FMC_D28,DCMI_D6,LCD_B6,EVENTOUT PortI,PI7,,,,TIM8_CH3,,,,,,,SAI2_FS_A,,FMC_D29,DCMI_D7,LCD_B7,EVENTOUT PortI,PI8,,,,,,,,,,,,,,,,EVENTOUT -PortI,PI9,,,,,,,,,,CAN1_RX,,,FMC_D30,,LCD_VSYNC,EVENTOUT +PortI,PI9,,,,,,,,,UART4_RX,CAN1_RX,,,FMC_D30,,LCD_VSYNC,EVENTOUT PortI,PI10,,,,,,,,,,,,ETH_MII_RX_ER,FMC_D31,,LCD_HSYNC,EVENTOUT -PortI,PI11,,,,,,,,,,,OTG_HS_ULPI_DIR,,,,,EVENTOUT +PortI,PI11,,,,,,,,,,LCD_G6,OTG_HS_ULPI_DIR,,,,,EVENTOUT PortI,PI12,,,,,,,,,,,,,,,LCD_HSYNC,EVENTOUT PortI,PI13,,,,,,,,,,,,,,,LCD_VSYNC,EVENTOUT PortI,PI14,,,,,,,,,,,,,,,LCD_CLK,EVENTOUT -PortI,PI15,,,,,,,,,,,,,,,LCD_R0,EVENTOUT -PortJ,PJ0,,,,,,,,,,,,,,,LCD_R1,EVENTOUT +PortI,PI15,,,,,,,,,,LCD_G2,,,,,LCD_R0,EVENTOUT +PortJ,PJ0,,,,,,,,,,LCD_R7,,,,,LCD_R1,EVENTOUT PortJ,PJ1,,,,,,,,,,,,,,,LCD_R2,EVENTOUT -PortJ,PJ2,,,,,,,,,,,,,,,LCD_R3,EVENTOUT +PortJ,PJ2,,,,,,,,,,,,,,DSI_TE,LCD_R3,EVENTOUT PortJ,PJ3,,,,,,,,,,,,,,,LCD_R4,EVENTOUT PortJ,PJ4,,,,,,,,,,,,,,,LCD_R5,EVENTOUT PortJ,PJ5,,,,,,,,,,,,,,,LCD_R6,EVENTOUT @@ -156,8 +156,8 @@ PortJ,PJ8,,,,,,,,,,,,,,,LCD_G1,EVENTOUT PortJ,PJ9,,,,,,,,,,,,,,,LCD_G2,EVENTOUT PortJ,PJ10,,,,,,,,,,,,,,,LCD_G3,EVENTOUT PortJ,PJ11,,,,,,,,,,,,,,,LCD_G4,EVENTOUT -PortJ,PJ12,,,,,,,,,,,,,,,LCD_B0,EVENTOUT -PortJ,PJ13,,,,,,,,,,,,,,,LCD_B1,EVENTOUT +PortJ,PJ12,,,,,,,,,,LCD_G3,,,,,LCD_B0,EVENTOUT +PortJ,PJ13,,,,,,,,,,LCD_G4,,,,,LCD_B1,EVENTOUT PortJ,PJ14,,,,,,,,,,,,,,,LCD_B2,EVENTOUT PortJ,PJ15,,,,,,,,,,,,,,,LCD_B3,EVENTOUT PortK,PK0,,,,,,,,,,,,,,,LCD_G5,EVENTOUT From 6515acca704a1ac208afb79e407486b58a0781de Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 1 Dec 2017 14:51:36 +1100 Subject: [PATCH 071/828] stm32/boards/*_af.csv: Make consistent use of JTMS, JTCK, SWDIO, SWCLK. 5-pin JTAG and 2-pin SWD are logically separate interfaces so encode them in the AF tables as separate entries (separated by /, not -). --- ports/stm32/boards/stm32f401_af.csv | 4 ++-- ports/stm32/boards/stm32f405_af.csv | 4 ++-- ports/stm32/boards/stm32f411_af.csv | 4 ++-- ports/stm32/boards/stm32f429_af.csv | 4 ++-- ports/stm32/boards/stm32f439_af.csv | 4 ++-- ports/stm32/boards/stm32l476_af.csv | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ports/stm32/boards/stm32f401_af.csv b/ports/stm32/boards/stm32f401_af.csv index 3b29e1349..bdcf9a1fe 100644 --- a/ports/stm32/boards/stm32f401_af.csv +++ b/ports/stm32/boards/stm32f401_af.csv @@ -13,8 +13,8 @@ PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,,,USART1_TX,,,OTG_FS_VBUS,,,,,EVENTOUT, PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,,,,,EVENTOUT, PortA,PA11,,TIM1_CH4,,,,,,USART1_CTS,USART6_TX,,OTG_FS_DM,,,,,EVENTOUT, PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS,USART6_RX,,OTG_FS_DP,,,,,EVENTOUT, -PortA,PA13,JTMS_SWDIO,,,,,,,,,,,,,,,EVENTOUT, -PortA,PA14,JTCK_SWCLK,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT, PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,,SPI1_NSS,SPI3_NSS/I2S3_WS,,,,,,,,,EVENTOUT, PortB,PB0,,TIM1_CH2N,TIM3_CH3,,,,,,,,,,,,,EVENTOUT,ADC1_IN8 PortB,PB1,,TIM1_CH3N,TIM3_CH4,,,,,,,,,,,,,EVENTOUT,ADC1_IN9 diff --git a/ports/stm32/boards/stm32f405_af.csv b/ports/stm32/boards/stm32f405_af.csv index 81f5e80ed..e6d8fcc2b 100644 --- a/ports/stm32/boards/stm32f405_af.csv +++ b/ports/stm32/boards/stm32f405_af.csv @@ -13,8 +13,8 @@ PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,,,USART1_TX,,,,,,DCMI_D0,,EVENTOUT, PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,,,DCMI_D1,,EVENTOUT, PortA,PA11,,TIM1_CH4,,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,,,,EVENTOUT, PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS,,CAN1_TX,OTG_FS_DP,,,,,EVENTOUT, -PortA,PA13,JTMS-SWDIO,,,,,,,,,,,,,,,EVENTOUT, -PortA,PA14,JTCK-SWCLK,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT, PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,,SPI1_NSS,SPI3_NSS/I2S3_WS,,,,,,,,,EVENTOUT, PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,,,,,,,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT,ADC12_IN8 PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,,,,,,,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT,ADC12_IN9 diff --git a/ports/stm32/boards/stm32f411_af.csv b/ports/stm32/boards/stm32f411_af.csv index 29267b1d9..4fe794121 100644 --- a/ports/stm32/boards/stm32f411_af.csv +++ b/ports/stm32/boards/stm32f411_af.csv @@ -13,8 +13,8 @@ PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,,,USART1_TX,,,USB_FS_VBUS,,SDIO_D2,,,EVENTOUT, PortA,PA10,,TIM1_CH3,,,,,SPI5_MOSI/I2S5_SD,USART1_RX,,,USB_FS_ID,,,,,EVENTOUT, PortA,PA11,,TIM1_CH4,,,,,SPI4_MISO,USART1_CTS,USART6_TX,,USB_FS_DM,,,,,EVENTOUT, PortA,PA12,,TIM1_ETR,,,,,SPI5_MISO,USART1_RTS,USART6_RX,,USB_FS_DP,,,,,EVENTOUT, -PortA,PA13,JTMS-SWDIO,,,,,,,,,,,,,,,EVENTOUT, -PortA,PA14,JTCK-SWCLK,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT, PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,,SPI1_NSS/I2S1_WS,SPI3_NSS/I2S3_WS,USART1_TX,,,,,,,,EVENTOUT, PortB,PB0,,TIM1_CH2N,TIM3_CH3,,,,SPI5_SCK/I2S5_CK,,,,,,,,,EVENTOUT,ADC1_IN8 PortB,PB1,,TIM1_CH3N,TIM3_CH4,,,,SPI5_NSS/I2S5_WS,,,,,,,,,EVENTOUT,ADC1_IN9 diff --git a/ports/stm32/boards/stm32f429_af.csv b/ports/stm32/boards/stm32f429_af.csv index 2e501ade0..4ee8edd70 100644 --- a/ports/stm32/boards/stm32f429_af.csv +++ b/ports/stm32/boards/stm32f429_af.csv @@ -13,8 +13,8 @@ PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,,,USART1_TX,,,,,,DCMI_D0,,EVENTOUT, PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,,,DCMI_D1,,EVENTOUT, PortA,PA11,,TIM1_CH4,,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,,,LCD_R4,EVENTOUT, PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS,,CAN1_TX,OTG_FS_DP,,,,LCD_R5,EVENTOUT, -PortA,PA13,JTMS-SWDIO,,,,,,,,,,,,,,,EVENTOUT, -PortA,PA14,JTCK-SWCLK,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT, PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,,SPI1_NSS,SPI3_NSS/I2S3_WS,,,,,,,,,EVENTOUT, PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,,,,,,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT,ADC12_IN8 PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,,,,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT,ADC12_IN9 diff --git a/ports/stm32/boards/stm32f439_af.csv b/ports/stm32/boards/stm32f439_af.csv index 5d5c3e72d..dc3641502 100644 --- a/ports/stm32/boards/stm32f439_af.csv +++ b/ports/stm32/boards/stm32f439_af.csv @@ -13,8 +13,8 @@ PortA,PA9,,TIM1_CH2,,,I2C3_SMBA,,,USART1_TX,,,,,,DCMI_D0,,EVENTOUT, PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,,,DCMI_D1,,EVENTOUT, PortA,PA11,,TIM1_CH4,,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,,,LCD_R4,EVENTOUT, PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS,,CAN1_TX,OTG_FS_DP,,,,LCD_R5,EVENTOUT, -PortA,PA13,JTMS-SWDIO,,,,,,,,,,,,,,,EVENTOUT, -PortA,PA14,JTCK-SWCLK,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA13,JTMS/SWDIO,,,,,,,,,,,,,,,EVENTOUT, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT, PortA,PA15,JTDI,TIM2_CH1/TIM2_ETR,,,,SPI1_NSS,SPI3_NSS/I2S3_WS,,,,,,,,,EVENTOUT, PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,,,,,,LCD_R3,OTG_HS_ULPI_D1,ETH_MII_RXD2,,,,EVENTOUT,ADC12_IN8 PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,,,,,,LCD_R6,OTG_HS_ULPI_D2,ETH_MII_RXD3,,,,EVENTOUT,ADC12_IN9 diff --git a/ports/stm32/boards/stm32l476_af.csv b/ports/stm32/boards/stm32l476_af.csv index d67c33880..a78fc45ab 100644 --- a/ports/stm32/boards/stm32l476_af.csv +++ b/ports/stm32/boards/stm32l476_af.csv @@ -13,8 +13,8 @@ PortA,PA9,,TIM1_CH2,,,,,,USART1_TX,,,,LCD_COM1,,,TIM15_BKIN,EVENTOUT,, PortA,PA10,,TIM1_CH3,,,,,,USART1_RX,,,OTG_FS_ID,LCD_COM2,,,TIM17_BKIN,EVENTOUT,, PortA,PA11,,TIM1_CH4,TIM1_BKIN2,,,,,USART1_CTS,,CAN1_RX,OTG_FS_DM,,TIM1_BKIN2_COMP1,,,EVENTOUT,, PortA,PA12,,TIM1_ETR,,,,,,USART1_RTS_DE,,CAN1_TX,OTG_FS_DP,,,,,EVENTOUT,, -PortA,PA13,JTMS-SWDIO,IR_OUT,,,,,,,,,OTG_FS_NOE,,,,,EVENTOUT,, -PortA,PA14,JTCK-SWCLK,,,,,,,,,,,,,,,EVENTOUT,, +PortA,PA13,JTMS/SWDIO,IR_OUT,,,,,,,,,OTG_FS_NOE,,,,,EVENTOUT,, +PortA,PA14,JTCK/SWCLK,,,,,,,,,,,,,,,EVENTOUT,, PortA,PA15,JTDI,TIM2_CH1,TIM2_ETR,,,SPI1_NSS,SPI3_NSS,,UART4_RTS_DE,TSC_G3_IO1,,LCD_SEG17,,SAI2_FS_B,,EVENTOUT,, PortB,PB0,,TIM1_CH2N,TIM3_CH3,TIM8_CH2N,,,,USART3_CK,,,QUADSPI_BK1_IO1,LCD_SEG5,COMP1_OUT,,,EVENTOUT,ADC12_IN15, PortB,PB1,,TIM1_CH3N,TIM3_CH4,TIM8_CH3N,,,DFSDM_DATIN0,USART3_RTS_DE,,,QUADSPI_BK1_IO0,LCD_SEG6,,,LPTIM2_IN1,EVENTOUT,ADC12_IN16,COMP1_INN From bb047558da6d7e1857e1d4c79547776976353cd3 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 1 Dec 2017 13:45:03 +0200 Subject: [PATCH 072/828] zephyr/Makefile: syscall_macros.h generation was moved from CMake to make. Required for #include to work. --- ports/zephyr/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/zephyr/Makefile b/ports/zephyr/Makefile index 2327cf73c..d6d711d1c 100644 --- a/ports/zephyr/Makefile +++ b/ports/zephyr/Makefile @@ -105,3 +105,4 @@ outdir/$(BOARD)/Makefile: $(CONF_FILE) $(Z_EXPORTS): outdir/$(BOARD)/Makefile make --no-print-directory -C outdir/$(BOARD) outputexports CMAKE_COMMAND=: >$@ + make -C outdir/$(BOARD) syscall_macros_h_target From 4fee35a32c600d603034b6eb077a55899513ba4d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 3 Dec 2017 15:07:46 +0200 Subject: [PATCH 073/828] docs/glossary: Describe the callee-owned tuple concept. --- docs/library/uselect.rst | 4 ++-- docs/reference/glossary.rst | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/library/uselect.rst b/docs/library/uselect.rst index 211b2a4a8..f88ab7d1d 100644 --- a/docs/library/uselect.rst +++ b/docs/library/uselect.rst @@ -78,8 +78,8 @@ Methods .. method:: poll.ipoll(timeout=-1, flags=0) - Like :meth:`poll.poll`, but instead returns an iterator which yields - `callee-owned tuples`. This function provides efficient, allocation-free + Like :meth:`poll.poll`, but instead returns an iterator which yields a + `callee-owned tuple`. This function provides an efficient, allocation-free way to poll on streams. If *flags* is 1, one-shot behavior for events is employed: streams for diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst index 4cd3d84cc..b6153550c 100644 --- a/docs/reference/glossary.rst +++ b/docs/reference/glossary.rst @@ -15,6 +15,29 @@ Glossary may also refer to "boardless" ports like :term:`Unix port `). + callee-owned tuple + A tuple returned by some builtin function/method, containing data + which is valid for a limited time, usually until next call to the + same function (or a group of related functions). After next call, + data in the tuple may be changed. This leads to the following + restriction on the usage of callee-owned tuples - references to + them cannot be stored. The only valid operation is extracting + values from them (including making a copy). Callee-owned tuples + is a MicroPython-specific construct (not available in the general + Python language), introduced for memory allocation optimization. + The idea is that callee-owned tuple is allocated once and stored + on the callee side. Subsequent calls don't require allocation, + allowing to return multiple values when allocation is not possible + (e.g. in interrupt context) or not desirable (because allocation + inherently leads to memory fragmentation). Note that callee-owned + tuples are effectively mutable tuples, making an exception to + Python's rule that tuples are immutable. (It may be interesting + why tuples were used for such a purpose then, instead of mutable + lists - the reason for that is that lists are mutable from user + application side too, so a user could do things to a callee-owned + list which the callee doesn't expect and could lead to problems; + a tuple is protected from this.) + CPython CPython is the reference implementation of Python programming language, and the most well-known one, which most of the people From 3c483842db18038677a97624d9aafc4cc5b291c4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 3 Dec 2017 15:32:09 +0200 Subject: [PATCH 074/828] tests/cpydiff: Fix markup where "`" (xref) was used instead of "``" (code). --- tests/cpydiff/builtin_next_arg2.py | 2 +- tests/cpydiff/types_str_ljust_rjust.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cpydiff/builtin_next_arg2.py b/tests/cpydiff/builtin_next_arg2.py index cbde773f9..5df2d6e70 100644 --- a/tests/cpydiff/builtin_next_arg2.py +++ b/tests/cpydiff/builtin_next_arg2.py @@ -2,7 +2,7 @@ categories: Modules,builtins description: Second argument to next() is not implemented cause: MicroPython is optimised for code space. -workaround: Instead of `val = next(it, deflt)` use:: +workaround: Instead of ``val = next(it, deflt)`` use:: try: val = next(it) diff --git a/tests/cpydiff/types_str_ljust_rjust.py b/tests/cpydiff/types_str_ljust_rjust.py index 498596205..fa3f594c1 100644 --- a/tests/cpydiff/types_str_ljust_rjust.py +++ b/tests/cpydiff/types_str_ljust_rjust.py @@ -2,6 +2,6 @@ categories: Types,str description: str.ljust() and str.rjust() not implemented cause: MicroPython is highly optimized for memory usage. Easy workarounds available. -workaround: Instead of `s.ljust(10)` use `"%-10s" % s`, instead of `s.rjust(10)` use `"% 10s" % s`. Alternatively, `"{:<10}".format(s)` or `"{:>10}".format(s)`. +workaround: Instead of ``s.ljust(10)`` use ``"%-10s" % s``, instead of ``s.rjust(10)`` use ``"% 10s" % s``. Alternatively, ``"{:<10}".format(s)`` or ``"{:>10}".format(s)``. """ print('abc'.ljust(10)) From 140acc9a322c1b575d30115604fb1d3250277c0d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 3 Dec 2017 15:50:37 +0200 Subject: [PATCH 075/828] docs/uerrno: Fix xref-vs-code markup. --- docs/library/uerrno.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/uerrno.rst b/docs/library/uerrno.rst index 0cdcc8448..e336eb5c5 100644 --- a/docs/library/uerrno.rst +++ b/docs/library/uerrno.rst @@ -17,7 +17,7 @@ Constants Error codes, based on ANSI C/POSIX standard. All error codes start with "E". As mentioned above, inventory of the codes depends on `MicroPython port`. Errors are usually accessible as ``exc.args[0]`` - where `exc` is an instance of `OSError`. Usage example:: + where ``exc`` is an instance of `OSError`. Usage example:: try: uos.mkdir("my_dir") From 8175f1608eddc61c495c9163aebcc3c90549b9c4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 3 Dec 2017 18:56:18 +0200 Subject: [PATCH 076/828] docs/glossary: Describe "stream" term. --- docs/reference/glossary.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst index b6153550c..d9650bdd8 100644 --- a/docs/reference/glossary.rst +++ b/docs/reference/glossary.rst @@ -120,6 +120,16 @@ Glossary from context, it's recommended to use full specification like one of the above. + stream + Also known as a "file-like object". An object which provides sequential + read-write access to the underlying data. A stream object implements + a corresponding interface, which consists of methods like ``read()``, + ``write()``, ``readinto()``, ``seek()``, ``flush()``, ``close()``, etc. + A stream is an important concept in MicroPython, many I/O objects + implement the stream interface, and thus can be used consistently and + interchangeably in different contexts. For more information on + streams in MicroPython, see `uio` module. + upip (Literally, "micro pip"). A package manage for MicroPython, inspired by :term:`CPython`'s pip, but much smaller and with reduced functionality. From 3a431fba50c96cc47d8273a6934e200993197b14 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 4 Dec 2017 00:13:10 +0200 Subject: [PATCH 077/828] esp8266/modnetwork: Make sure to intern string passed to .config("param"). This is the proper fix for https://github.com/micropython/micropython/issues/3442. --- ports/esp8266/modnetwork.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index 4066c969c..f7da5b751 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -402,30 +402,30 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs mp_obj_t val; - #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) - switch ((uintptr_t)args[1]) { - case QS(MP_QSTR_mac): { + qstr key = mp_obj_str_get_qstr(args[1]); + switch (key) { + case MP_QSTR_mac: { uint8_t mac[6]; wifi_get_macaddr(self->if_id, mac); return mp_obj_new_bytes(mac, sizeof(mac)); } - case QS(MP_QSTR_essid): + case MP_QSTR_essid: req_if = SOFTAP_IF; val = mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len); break; - case QS(MP_QSTR_hidden): + case MP_QSTR_hidden: req_if = SOFTAP_IF; val = mp_obj_new_bool(cfg.ap.ssid_hidden); break; - case QS(MP_QSTR_authmode): + case MP_QSTR_authmode: req_if = SOFTAP_IF; val = MP_OBJ_NEW_SMALL_INT(cfg.ap.authmode); break; - case QS(MP_QSTR_channel): + case MP_QSTR_channel: req_if = SOFTAP_IF; val = MP_OBJ_NEW_SMALL_INT(cfg.ap.channel); break; - case QS(MP_QSTR_dhcp_hostname): { + case MP_QSTR_dhcp_hostname: { req_if = STATION_IF; char* s = wifi_station_get_hostname(); val = mp_obj_new_str(s, strlen(s)); @@ -434,7 +434,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) { From 155ec21e4933f75ee98bbd3e125e4304fb188170 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 4 Dec 2017 01:01:03 +0200 Subject: [PATCH 078/828] docs/glossary: Describe string interning. --- docs/reference/glossary.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst index d9650bdd8..98a2a50d8 100644 --- a/docs/reference/glossary.rst +++ b/docs/reference/glossary.rst @@ -64,6 +64,22 @@ Glossary properties of these pins (e.g. controllable by the same register). + interned string + A string referenced by its (unique) identity rather than its + address. Interned strings are thus can be quickly compared just + by their identifiers, instead of comparing by content. The + drawbacks of interned strings are that interning operation takes + time (proportional to the number of existing interned strings, + i.e. becoming slower and slower over time) and that the space + used for interned strings is not reclaimable. String interning + is done automatically by MicroPython compiler and runtimer when + it's either required by the implementation (e.g. function keyword + arguments are represented by interned string id's) or deemed + beneficial (e.g. for short enough strings, which have a chance + to be repeated, and thus interning them would save memory on + copies). Most of string and I/O operations don't produce interned + strings due to drawbacks described above. + MCU Microcontroller. Microcontrollers usually have much less resources than a full-fledged computing system, but smaller, cheaper and From 75d3c046dafa84dad51ed757f832c2dad2934bf9 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 4 Dec 2017 11:05:49 +0200 Subject: [PATCH 079/828] py/misc.h: Add m_new_obj_var_with_finaliser(). Similar to existing m_new_obj_with_finaliser(). --- py/misc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/misc.h b/py/misc.h index b9f2dae90..5d557db6f 100644 --- a/py/misc.h +++ b/py/misc.h @@ -63,8 +63,10 @@ typedef unsigned int uint; #define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type*)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num))) #if MICROPY_ENABLE_FINALISER #define m_new_obj_with_finaliser(type) ((type*)(m_malloc_with_finaliser(sizeof(type)))) +#define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type*)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num))) #else #define m_new_obj_with_finaliser(type) m_new_obj(type) +#define m_new_obj_var_with_finaliser(type, var_type, var_num) m_new_obj_var(type, var_type, var_num) #endif #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #define m_renew(type, ptr, old_num, new_num) ((type*)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num)))) From 3ff7040c8ab268777e940a9bed4aa7c24f50ba31 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 4 Dec 2017 18:36:20 +0200 Subject: [PATCH 080/828] docs/library: Add xrefs to "stream" dictionary entry for many modules. --- docs/library/btree.rst | 2 +- docs/library/machine.UART.rst | 2 +- docs/library/pyb.UART.rst | 2 +- docs/library/pyb.USB_VCP.rst | 4 ++-- docs/library/sys.rst | 6 +++--- docs/library/uio.rst | 2 +- docs/library/uos.rst | 2 +- docs/library/uselect.rst | 4 ++-- docs/library/usocket.rst | 4 ++-- docs/library/ussl.rst | 4 ++-- docs/library/uzlib.rst | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/library/btree.rst b/docs/library/btree.rst index 8fac67e8d..3578acd8f 100644 --- a/docs/library/btree.rst +++ b/docs/library/btree.rst @@ -5,7 +5,7 @@ :synopsis: simple BTree database The ``btree`` module implements a simple key-value database using external -storage (disk files, or in general case, a random-access stream). Keys are +storage (disk files, or in general case, a random-access `stream`). Keys are stored sorted in the database, and besides efficient retrieval by a key value, a database also supports efficient ordered range scans (retrieval of values with the keys in a given range). On the application interface diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst index 983ef0a94..1574f17db 100644 --- a/docs/library/machine.UART.rst +++ b/docs/library/machine.UART.rst @@ -24,7 +24,7 @@ are supported. WiPy/CC3200: Bits can be 5, 6, 7, 8. Stop can be 1 or 2. -A UART object acts like a stream object and reading and writing is done +A UART object acts like a `stream` object and reading and writing is done using the standard stream methods:: uart.read(10) # read 10 characters, returns a bytes object diff --git a/docs/library/pyb.UART.rst b/docs/library/pyb.UART.rst index 76f347ffa..c299c838e 100644 --- a/docs/library/pyb.UART.rst +++ b/docs/library/pyb.UART.rst @@ -23,7 +23,7 @@ UART objects can be created and initialised using:: *Note:* with parity=None, only 8 and 9 bits are supported. With parity enabled, only 7 and 8 bits are supported. -A UART object acts like a stream object and reading and writing is done +A UART object acts like a `stream` object and reading and writing is done using the standard stream methods:: uart.read(10) # read 10 characters, returns a bytes object diff --git a/docs/library/pyb.USB_VCP.rst b/docs/library/pyb.USB_VCP.rst index 80cc40cdd..3bc6c749c 100644 --- a/docs/library/pyb.USB_VCP.rst +++ b/docs/library/pyb.USB_VCP.rst @@ -4,7 +4,7 @@ class USB_VCP -- USB virtual comm port ====================================== -The USB_VCP class allows creation of an object representing the USB +The USB_VCP class allows creation of a `stream`-like object representing the USB virtual comm port. It can be used to read and write data over USB to the connected host. @@ -47,7 +47,7 @@ Methods Read at most ``nbytes`` from the serial device and return them as a bytes object. If ``nbytes`` is not specified then the method reads all available bytes from the serial device. - USB_VCP stream implicitly works in non-blocking mode, + USB_VCP `stream` implicitly works in non-blocking mode, so if no pending data available, this method will return immediately with ``None`` value. diff --git a/docs/library/sys.rst b/docs/library/sys.rst index d49577306..f2d96cb8c 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -104,15 +104,15 @@ Constants .. data:: stderr - Standard error stream. + Standard error `stream`. .. data:: stdin - Standard input stream. + Standard input `stream`. .. data:: stdout - Standard output stream. + Standard output `stream`. .. data:: version diff --git a/docs/library/uio.rst b/docs/library/uio.rst index 7042a9e37..7e6c93228 100644 --- a/docs/library/uio.rst +++ b/docs/library/uio.rst @@ -6,7 +6,7 @@ |see_cpython_module| :mod:`python:io`. -This module contains additional types of stream (file-like) objects +This module contains additional types of `stream` (file-like) objects and helper functions. Conceptual hierarchy diff --git a/docs/library/uos.rst b/docs/library/uos.rst index 43bf69cc0..c7fa4b308 100644 --- a/docs/library/uos.rst +++ b/docs/library/uos.rst @@ -91,7 +91,7 @@ Functions .. function:: dupterm(stream_object, index=0) - Duplicate or switch the MicroPython terminal (the REPL) on the given stream-like + Duplicate or switch the MicroPython terminal (the REPL) on the given `stream`-like object. The *stream_object* argument must implement the ``readinto()`` and ``write()`` methods. The stream should be in non-blocking mode and ``readinto()`` should return ``None`` if there is no data available for reading. diff --git a/docs/library/uselect.rst b/docs/library/uselect.rst index f88ab7d1d..fb43f7e63 100644 --- a/docs/library/uselect.rst +++ b/docs/library/uselect.rst @@ -7,7 +7,7 @@ |see_cpython_module| :mod:`python:select`. This module provides functions to efficiently wait for events on multiple -streams (select streams which are ready for operations). +`streams ` (select streams which are ready for operations). Functions --------- @@ -33,7 +33,7 @@ Methods .. method:: poll.register(obj[, eventmask]) - Register *obj* for polling. *eventmask* is logical OR of: + Register `stream` *obj* for polling. *eventmask* is logical OR of: * `uselect.POLLIN` - data available for reading * `uselect.POLLOUT` - more data can be written diff --git a/docs/library/usocket.rst b/docs/library/usocket.rst index fab05b652..de55fc14c 100644 --- a/docs/library/usocket.rst +++ b/docs/library/usocket.rst @@ -12,7 +12,7 @@ This module provides access to the BSD socket interface. .. admonition:: Difference to CPython :class: attention - For efficiency and consistency, socket objects in MicroPython implement a stream + For efficiency and consistency, socket objects in MicroPython implement a `stream` (file-like) interface directly. In CPython, you need to convert a socket to a file-like object using `makefile()` method. This method is still supported by MicroPython (but is a no-op), so where compatibility with CPython matters, @@ -248,7 +248,7 @@ Methods Not every `MicroPython port` supports this method. A more portable and generic solution is to use `uselect.poll` object. This allows to wait on multiple objects at the same time (and not just on sockets, but on generic - stream objects which support polling). Example:: + `stream` objects which support polling). Example:: # Instead of: s.settimeout(1.0) # time in seconds diff --git a/docs/library/ussl.rst b/docs/library/ussl.rst index 3ec609f67..903a351f4 100644 --- a/docs/library/ussl.rst +++ b/docs/library/ussl.rst @@ -15,9 +15,9 @@ Functions .. function:: ussl.wrap_socket(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None) - Takes a stream *sock* (usually usocket.socket instance of ``SOCK_STREAM`` type), + Takes a `stream` *sock* (usually usocket.socket instance of ``SOCK_STREAM`` type), and returns an instance of ssl.SSLSocket, which wraps the underlying stream in - an SSL context. Returned object has the usual stream interface methods like + an SSL context. Returned object has the usual `stream` interface methods like `read()`, `write()`, etc. In MicroPython, the returned object does not expose socket interface and methods like `recv()`, `send()`. In particular, a server-side SSL socket should be created from a normal socket returned from diff --git a/docs/library/uzlib.rst b/docs/library/uzlib.rst index fb1746fe8..0b399f228 100644 --- a/docs/library/uzlib.rst +++ b/docs/library/uzlib.rst @@ -25,7 +25,7 @@ Functions .. class:: DecompIO(stream, wbits=0) - Create a stream wrapper which allows transparent decompression of + Create a `stream` wrapper which allows transparent decompression of compressed data in another *stream*. This allows to process compressed streams with data larger than available heap size. In addition to values described in :func:`decompress`, *wbits* may take values From 62b96147e6126961c5d1d6bb28b7ac7034fa9322 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 5 Dec 2017 00:38:41 +0200 Subject: [PATCH 081/828] py: mp_call_function_*_protected(): Pass-thru return value if possible. Return the result of called function. If exception happened, return MP_OBJ_NULL. Allows to use mp_call_function_*_protected() with callbacks returning values, etc. --- py/runtime.h | 5 +++-- py/runtime_utils.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/py/runtime.h b/py/runtime.h index 9c1921cb5..3f0d1104e 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -106,8 +106,9 @@ mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args); mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args); mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args); // Call function and catch/dump exception - for Python callbacks from C code -void mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg); -void mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2); +// (return MP_OBJ_NULL in case of exception). +mp_obj_t mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg); +mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2); typedef struct _mp_call_args_t { mp_obj_t fun; diff --git a/py/runtime_utils.c b/py/runtime_utils.c index a5c5403ba..b92c6bd76 100644 --- a/py/runtime_utils.c +++ b/py/runtime_utils.c @@ -27,22 +27,26 @@ #include "py/runtime.h" -void mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg) { +mp_obj_t mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_call_function_1(fun, arg); + mp_obj_t ret = mp_call_function_1(fun, arg); nlr_pop(); + return ret; } else { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + return MP_OBJ_NULL; } } -void mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) { +mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_call_function_2(fun, arg1, arg2); + mp_obj_t ret = mp_call_function_2(fun, arg1, arg2); nlr_pop(); + return ret; } else { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + return MP_OBJ_NULL; } } From e104e24e53a4e788785cca85332074b3cb5c6e29 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 5 Dec 2017 01:52:41 +0200 Subject: [PATCH 082/828] tests/run-tests: Wrap long lists to facilitate adding more items. --- tests/run-tests | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index a2efede0a..08a92a14f 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -50,7 +50,10 @@ def convert_regex_escapes(line): def run_micropython(pyb, args, test_file, is_special=False): - special_tests = ('micropython/meminfo.py', 'basics/bytes_compare3.py', 'basics/builtin_help.py', 'thread/thread_exc2.py') + special_tests = ( + 'micropython/meminfo.py', 'basics/bytes_compare3.py', + 'basics/builtin_help.py', 'thread/thread_exc2.py', + ) if pyb is None: # run on PC if test_file.startswith(('cmdline/', 'feature_check/')) or test_file in special_tests: @@ -477,7 +480,10 @@ def main(): test_dirs = ('basics', 'micropython', 'misc', 'extmod', 'wipy') else: # run PC tests - test_dirs = ('basics', 'micropython', 'float', 'import', 'io', 'misc', 'stress', 'unicode', 'extmod', 'unix', 'cmdline') + test_dirs = ( + 'basics', 'micropython', 'float', 'import', 'io', 'misc', + 'stress', 'unicode', 'extmod', 'unix', 'cmdline', + ) else: # run tests from these directories test_dirs = args.test_dirs From 58f00d7c0e116e17ab141ae18275edea14b88431 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 5 Dec 2017 12:14:57 +1100 Subject: [PATCH 083/828] py/modbuiltins: Use standard arg-parsing helper func for builtin print. This allows the function to raise an exception when unknown keyword args are passed in. This patch also reduces code size by (in bytes): bare-arm: -24 minimal x86: -76 unix x64: -56 unix nanbox: -84 stm32: -40 esp8266: -68 cc3200: -48 Furthermore, this patch adds space (" ") to the set of ROM qstrs which means it doesn't need to be put in RAM if it's ever used. --- py/modbuiltins.c | 58 ++++++++++++++++++++++++++---------------------- py/qstrdefs.h | 1 + 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 0c78832ac..c8e3235f6 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -383,46 +383,52 @@ STATIC mp_obj_t mp_builtin_pow(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj, 2, 3, mp_builtin_pow); -STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - mp_map_elem_t *sep_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_sep), MP_MAP_LOOKUP); - mp_map_elem_t *end_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_end), MP_MAP_LOOKUP); - const char *sep_data = " "; - size_t sep_len = 1; - const char *end_data = "\n"; - size_t end_len = 1; - if (sep_elem != NULL && sep_elem->value != mp_const_none) { - sep_data = mp_obj_str_get_data(sep_elem->value, &sep_len); - } - if (end_elem != NULL && end_elem->value != mp_const_none) { - end_data = mp_obj_str_get_data(end_elem->value, &end_len); - } - #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES - void *stream_obj = &mp_sys_stdout_obj; - mp_map_elem_t *file_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_file), MP_MAP_LOOKUP); - if (file_elem != NULL && file_elem->value != mp_const_none) { - stream_obj = MP_OBJ_TO_PTR(file_elem->value); // XXX may not be a concrete object - } +STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sep, ARG_end, ARG_file }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__space_)} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__0x0a_)} }, + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + { MP_QSTR_file, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_sys_stdout_obj)} }, + #endif + }; - mp_print_t print = {stream_obj, mp_stream_write_adaptor}; + // parse args (a union is used to reduce the amount of C stack that is needed) + union { + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + size_t len[2]; + } u; + mp_arg_parse_all(0, NULL, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, u.args); + + #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES + // TODO file may not be a concrete object (eg it could be a small-int) + mp_print_t print = {MP_OBJ_TO_PTR(u.args[ARG_file].u_obj), mp_stream_write_adaptor}; #endif + + // extract the objects first because we are going to use the other part of the union + mp_obj_t sep = u.args[ARG_sep].u_obj; + mp_obj_t end = u.args[ARG_end].u_obj; + const char *sep_data = mp_obj_str_get_data(sep, &u.len[0]); + const char *end_data = mp_obj_str_get_data(end, &u.len[1]); + for (size_t i = 0; i < n_args; i++) { if (i > 0) { #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES - mp_stream_write_adaptor(stream_obj, sep_data, sep_len); + mp_stream_write_adaptor(print.data, sep_data, u.len[0]); #else - mp_print_strn(&mp_plat_print, sep_data, sep_len, 0, 0, 0); + mp_print_strn(&mp_plat_print, sep_data, u.len[0], 0, 0, 0); #endif } #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES - mp_obj_print_helper(&print, args[i], PRINT_STR); + mp_obj_print_helper(&print, pos_args[i], PRINT_STR); #else - mp_obj_print_helper(&mp_plat_print, args[i], PRINT_STR); + mp_obj_print_helper(&mp_plat_print, pos_args[i], PRINT_STR); #endif } #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES - mp_stream_write_adaptor(stream_obj, end_data, end_len); + mp_stream_write_adaptor(print.data, end_data, u.len[1]); #else - mp_print_strn(&mp_plat_print, end_data, end_len, 0, 0, 0); + mp_print_strn(&mp_plat_print, end_data, u.len[1], 0, 0, 0); #endif return mp_const_none; } diff --git a/py/qstrdefs.h b/py/qstrdefs.h index 4ded5be08..1b480c9c7 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -40,6 +40,7 @@ Q(/) Q(%#o) Q(%#x) Q({:#b}) +Q( ) Q(\n) Q(maximum recursion depth exceeded) Q() From ca8034d6b888952dfb4401b867f5682ac683b5ff Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 6 Dec 2017 00:08:24 +0200 Subject: [PATCH 084/828] docs/glossary: Clarify wording for "baremetal". --- docs/reference/glossary.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst index 98a2a50d8..9daf0dc3a 100644 --- a/docs/reference/glossary.rst +++ b/docs/reference/glossary.rst @@ -4,9 +4,10 @@ Glossary .. glossary:: baremetal - A system without (full-fledged) OS, like an :term:`MCU`. When - running on a baremetal system, MicroPython effectively becomes - its user-facing OS with a command interpreter (REPL). + A system without a (full-fledged) OS, for example an + :term:`MCU`-based system. When running on a baremetal system, + MicroPython effectively becomes its user-facing OS with a command + interpreter (REPL). board A PCB board. Oftentimes, the term is used to denote a particular From ccec4ee7ad1efe3cc794945cd172eb820046a543 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 6 Dec 2017 15:31:07 +0200 Subject: [PATCH 085/828] zephyr/CMakeLists.txt: Update for latest Zephyr buildsys changes. --- ports/zephyr/CMakeLists.txt | 8 ++++---- ports/zephyr/Makefile | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index cd7eda227..b360a711d 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -7,10 +7,10 @@ add_library(libmicropython STATIC IMPORTED) set_target_properties(libmicropython PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libmicropython.a) target_link_libraries(app libmicropython) -zephyr_get_include_directories_as_string(includes) -zephyr_get_system_include_directories_as_string(system_includes) -zephyr_get_compile_definitions_as_string(definitions) -zephyr_get_compile_options_as_string(options) +zephyr_get_include_directories_for_lang_as_string(C includes) +zephyr_get_system_include_directories_for_lang_as_string(C system_includes) +zephyr_get_compile_definitions_for_lang_as_string(C definitions) +zephyr_get_compile_options_for_lang_as_string(C options) add_custom_target( outputexports diff --git a/ports/zephyr/Makefile b/ports/zephyr/Makefile index d6d711d1c..2064fcef7 100644 --- a/ports/zephyr/Makefile +++ b/ports/zephyr/Makefile @@ -105,4 +105,4 @@ outdir/$(BOARD)/Makefile: $(CONF_FILE) $(Z_EXPORTS): outdir/$(BOARD)/Makefile make --no-print-directory -C outdir/$(BOARD) outputexports CMAKE_COMMAND=: >$@ - make -C outdir/$(BOARD) syscall_macros_h_target + make -C outdir/$(BOARD) syscall_macros_h_target syscall_list_h_target From ada1dc1c037371cd4680b00acff4bceaea511151 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 6 Dec 2017 16:45:27 +0200 Subject: [PATCH 086/828] zephyr/CMakeLists.txt: Properly separate CFLAGS parts gotten from CMake. Lack of spaces between them led to weird option artifacts like -Ifoo-Dbar. --- ports/zephyr/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index b360a711d..84b0e8190 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -15,7 +15,7 @@ zephyr_get_compile_options_for_lang_as_string(C options) add_custom_target( outputexports COMMAND echo CC="${CMAKE_C_COMPILER}" - COMMAND echo Z_CFLAGS=${system_includes}${includes}${definitions}${options} + COMMAND echo Z_CFLAGS=${system_includes} ${includes} ${definitions} ${options} VERBATIM USES_TERMINAL ) From 5f8ad284f81c3f51be9b3d00ea9fee21ba727a97 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 7 Dec 2017 09:04:54 +0200 Subject: [PATCH 087/828] py/mpprint: Make "%p" format work properly on 64-bit systems. Before, the output was truncated to 32 bits. --- py/mpprint.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/py/mpprint.c b/py/mpprint.c index a569ef793..74912eb5f 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -512,7 +512,8 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { break; case 'p': case 'P': // don't bother to handle upcase for 'P' - chrs += mp_print_int(print, va_arg(args, unsigned int), 0, 16, 'a', flags, fill, width); + // Use unsigned long int to work on both ILP32 and LP64 systems + chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags, fill, width); break; #if MICROPY_PY_BUILTINS_FLOAT case 'e': From 5a10e63543cae424a71f93ea95b79d34df095832 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 7 Dec 2017 10:00:23 +0200 Subject: [PATCH 088/828] py/mpprint: Support "%lx" format on 64-bit systems. Before that, the output was truncated to 32 bits. Only "%x" format is handled, because a typical use is for addresses. This refactor actually decreased x86_64 code size by 30 bytes. --- py/mpprint.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/py/mpprint.c b/py/mpprint.c index 74912eb5f..d6d9cf963 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -446,11 +446,16 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { } } - // parse long specifiers (current not used) - //bool long_arg = false; + // parse long specifiers (only for LP64 model where they make a difference) + #ifndef __LP64__ + const + #endif + bool long_arg = false; if (*fmt == 'l') { ++fmt; - //long_arg = true; + #ifdef __LP64__ + long_arg = true; + #endif } if (*fmt == '\0') { @@ -505,11 +510,17 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { chrs += mp_print_int(print, va_arg(args, int), 1, 10, 'a', flags, fill, width); break; case 'x': - chrs += mp_print_int(print, va_arg(args, unsigned int), 0, 16, 'a', flags, fill, width); - break; - case 'X': - chrs += mp_print_int(print, va_arg(args, unsigned int), 0, 16, 'A', flags, fill, width); + case 'X': { + char fmt_c = 'x' - *fmt + 'A'; + mp_uint_t val; + if (long_arg) { + val = va_arg(args, unsigned long int); + } else { + val = va_arg(args, unsigned int); + } + chrs += mp_print_int(print, val, 0, 16, fmt_c, flags, fill, width); break; + } case 'p': case 'P': // don't bother to handle upcase for 'P' // Use unsigned long int to work on both ILP32 and LP64 systems From f5e097021ce95cf4398f89c4f4268efbc5717b89 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 7 Dec 2017 10:31:14 +0200 Subject: [PATCH 089/828] py/mpprint: Fix "%x" vs "%X" regression introduced in previous commit. --- py/mpprint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpprint.c b/py/mpprint.c index d6d9cf963..c2e65301c 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -511,7 +511,7 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { break; case 'x': case 'X': { - char fmt_c = 'x' - *fmt + 'A'; + char fmt_c = *fmt - 'X' + 'A'; mp_uint_t val; if (long_arg) { val = va_arg(args, unsigned long int); From 88a8043a27c3f75c7e4e52e4e8b0d47005cd6bef Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 7 Dec 2017 10:52:40 +0200 Subject: [PATCH 090/828] py/malloc: MICROPY_MEM_STATS requires MICROPY_MALLOC_USES_ALLOCATED_SIZE. Error out if they're set incompatibly. --- py/malloc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/py/malloc.c b/py/malloc.c index ea1d4c4b9..818a3e57a 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -39,6 +39,9 @@ #endif #if MICROPY_MEM_STATS +#if !MICROPY_MALLOC_USES_ALLOCATED_SIZE +#error MICROPY_MEM_STATS requires MICROPY_MALLOC_USES_ALLOCATED_SIZE +#endif #define UPDATE_PEAK() { if (MP_STATE_MEM(current_bytes_allocated) > MP_STATE_MEM(peak_bytes_allocated)) MP_STATE_MEM(peak_bytes_allocated) = MP_STATE_MEM(current_bytes_allocated); } #endif From 9ebc037eee575fd951dea92c82ed9704d9101924 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 7 Dec 2017 17:57:33 +0200 Subject: [PATCH 091/828] py/malloc: Allow to use debug logging if !MICROPY_MALLOC_USES_ALLOCATED_SIZE. This is mostly a workaround for forceful rebuilding of mpy-cross on every codebase change. If this file has debug logging enabled (by patching), mpy-cross build failed. --- py/malloc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/py/malloc.c b/py/malloc.c index 818a3e57a..6835ed7c9 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -147,7 +147,11 @@ void *m_realloc(void *ptr, size_t new_num_bytes) { MP_STATE_MEM(current_bytes_allocated) += diff; UPDATE_PEAK(); #endif + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr); + #else + DEBUG_printf("realloc %p, %d : %p\n", ptr, new_num_bytes, new_ptr); + #endif return new_ptr; } @@ -171,7 +175,11 @@ void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move) { UPDATE_PEAK(); } #endif + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr); + #else + DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, new_num_bytes, new_ptr); + #endif return new_ptr; } @@ -184,7 +192,11 @@ void m_free(void *ptr) { #if MICROPY_MEM_STATS MP_STATE_MEM(current_bytes_allocated) -= num_bytes; #endif + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE DEBUG_printf("free %p, %d\n", ptr, num_bytes); + #else + DEBUG_printf("free %p\n", ptr); + #endif } #if MICROPY_MEM_STATS From 9ef4be8b41c7d256908ba319918c0f7d54346bf4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 8 Dec 2017 00:10:44 +0200 Subject: [PATCH 092/828] py/gc: Add CLEAR_ON_SWEEP option to debug mis-traced objects. Accessing them will crash immediately instead still working for some time, until overwritten by some other data, leading to much less deterministic crashes. --- py/gc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/py/gc.c b/py/gc.c index 172a5e8fc..9a3c57e61 100644 --- a/py/gc.c +++ b/py/gc.c @@ -44,6 +44,10 @@ // make this 1 to dump the heap each time it changes #define EXTENSIVE_HEAP_PROFILING (0) +// make this 1 to zero out swept memory to more eagerly +// detect untraced object still in use +#define CLEAR_ON_SWEEP (0) + #define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD) #define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK) @@ -286,6 +290,9 @@ STATIC void gc_sweep(void) { case AT_TAIL: if (free_tail) { ATB_ANY_TO_FREE(block); + #if CLEAR_ON_SWEEP + memset((void*)PTR_FROM_BLOCK(block), 0, BYTES_PER_BLOCK); + #endif } break; From f935bce3c52e8eb8f48f7f4947b1210074c359a6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Dec 2017 18:23:23 +1100 Subject: [PATCH 093/828] py/{emitbc,asmbase}: Only clear emit labels to -1 when in debug mode. Clearing the labels to -1 is purely a debugging measure. For release builds there is no need to do it as the label offset table should always have the correct value assigned. --- py/asmbase.c | 4 +++- py/emitbc.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/py/asmbase.c b/py/asmbase.c index c941e917b..83a699cd4 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -47,8 +47,10 @@ void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) { void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { if (pass == MP_ASM_PASS_COMPUTE) { - // reset all labels + #ifndef NDEBUG + // With debugging enabled labels are checked for unique assignment memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t)); + #endif } else if (pass == MP_ASM_PASS_EMIT) { // allocating executable RAM is platform specific MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size); diff --git a/py/emitbc.c b/py/emitbc.c index 3f4dfc178..5e7fa623a 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -313,9 +313,12 @@ void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { emit->scope = scope; emit->last_source_line_offset = 0; emit->last_source_line = 1; + #ifndef NDEBUG + // With debugging enabled labels are checked for unique assignment if (pass < MP_PASS_EMIT) { memset(emit->label_offsets, -1, emit->max_num_labels * sizeof(mp_uint_t)); } + #endif emit->bytecode_offset = 0; emit->code_info_offset = 0; @@ -495,7 +498,6 @@ void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) { emit->label_offsets[l] = emit->bytecode_offset; } else { // ensure label offset has not changed from MP_PASS_CODE_SIZE to MP_PASS_EMIT - //printf("l%d: (at %d vs %d)\n", l, emit->bytecode_offset, emit->label_offsets[l]); assert(emit->label_offsets[l] == emit->bytecode_offset); } } From 53e111800f9e53733042442eefea0ffc293a35df Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Dec 2017 19:07:00 +1100 Subject: [PATCH 094/828] py/asmbase: Revert removal of clearing of label offsets for native emit. The assembler back-end for most architectures needs to know if a jump is backwards in order to emit optimised machine code, and they do this by checking if the destination label has been set or not. So always reset label offsets to -1 (this reverts partially the previous commit, with some minor optimisation for the if-logic with the pass variable). --- py/asmbase.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/py/asmbase.c b/py/asmbase.c index 83a699cd4..4c84c3b25 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -46,12 +46,10 @@ void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) { } void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { - if (pass == MP_ASM_PASS_COMPUTE) { - #ifndef NDEBUG - // With debugging enabled labels are checked for unique assignment + if (pass < MP_ASM_PASS_EMIT) { + // Reset labels so we can detect backwards jumps (and verify unique assignment) memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t)); - #endif - } else if (pass == MP_ASM_PASS_EMIT) { + } else { // allocating executable RAM is platform specific MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size); assert(as->code_base != NULL); From 55d33d5897b27dd4b70041a38674ff1bd55709a4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 8 Dec 2017 12:39:57 +0200 Subject: [PATCH 095/828] zephyr/main: Move var declarations to the top of file. --- ports/zephyr/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 7eb9da3e1..d6ddc65cc 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -4,7 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George - * Copyright (c) 2016 Linaro Limited + * Copyright (c) 2016-2017 Linaro Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,6 +41,9 @@ #include "lib/utils/pyexec.h" #include "lib/mp-readline/readline.h" +static char *stack_top; +static char heap[MICROPY_HEAP_SIZE]; + void do_str(const char *src, mp_parse_input_kind_t input_kind) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { @@ -56,9 +59,6 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { } } -static char *stack_top; -static char heap[MICROPY_HEAP_SIZE]; - void init_zephyr(void) { // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap // network addresses. From 24c641c4e3637a8c73b7546c5500a49f7dd94b2a Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 8 Dec 2017 19:15:45 +0200 Subject: [PATCH 096/828] qemu-arm/test_main: Clean up invocation of tinytest_main(). Command-line argc and argv should be passed, and as we don't have them, placeholders were passed, but incorrectly. As we don't have them, just pass 0/NULL. Looking at the source, this migh lead to problems under Windows, but this test doesn't run under Windows. Also, use "%d" printf format consistently with the rest of the codebase. --- ports/qemu-arm/test_main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index c018ae428..acac66e6a 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -49,12 +49,11 @@ end: #include "genhdr/tests.h" int main() { - const char a[] = {"sim"}; mp_stack_ctrl_init(); mp_stack_set_limit(10240); heap = malloc(HEAP_SIZE); - int r = tinytest_main(1, (const char **) a, groups); - printf( "status: %i\n", r); + int r = tinytest_main(0, NULL, groups); + printf("status: %d\n", r); return r; } From e9d29c9ba997ae0f00f16f3a21dffce7c763a3d4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 8 Dec 2017 19:26:15 +0200 Subject: [PATCH 097/828] lib/tinytest: Move from tools/tinytest. Tinytest library was misplaced under tools/. By convention, any target libraries belong to lib/, while tools/ contains host-side tools. --- {tools => lib}/tinytest/README | 0 {tools => lib}/tinytest/tinytest.c | 0 {tools => lib}/tinytest/tinytest.h | 0 {tools => lib}/tinytest/tinytest_macros.h | 0 ports/qemu-arm/Makefile | 6 ++++-- 5 files changed, 4 insertions(+), 2 deletions(-) rename {tools => lib}/tinytest/README (100%) rename {tools => lib}/tinytest/tinytest.c (100%) rename {tools => lib}/tinytest/tinytest.h (100%) rename {tools => lib}/tinytest/tinytest_macros.h (100%) diff --git a/tools/tinytest/README b/lib/tinytest/README similarity index 100% rename from tools/tinytest/README rename to lib/tinytest/README diff --git a/tools/tinytest/tinytest.c b/lib/tinytest/tinytest.c similarity index 100% rename from tools/tinytest/tinytest.c rename to lib/tinytest/tinytest.c diff --git a/tools/tinytest/tinytest.h b/lib/tinytest/tinytest.h similarity index 100% rename from tools/tinytest/tinytest.h rename to lib/tinytest/tinytest.h diff --git a/tools/tinytest/tinytest_macros.h b/lib/tinytest/tinytest_macros.h similarity index 100% rename from tools/tinytest/tinytest_macros.h rename to lib/tinytest/tinytest_macros.h diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 39e13853f..c0d257a3e 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -9,10 +9,12 @@ include $(TOP)/py/py.mk CROSS_COMPILE = arm-none-eabi- +TINYTEST = $(TOP)/lib/tinytest + INC += -I. INC += -I$(TOP) INC += -I$(BUILD) -INC += -I$(TOP)/tools/tinytest/ +INC += -I$(TINYTEST) CFLAGS_CORTEX_M3 = -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS = $(INC) -Wall -Wpointer-arith -Werror -std=gnu99 $(CFLAGS_CORTEX_M3) $(COPT) \ @@ -101,7 +103,7 @@ $(BUILD)/genhdr/tests.h: $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py) > $@ $(BUILD)/tinytest.o: - $(Q)$(CC) $(CFLAGS) -DNO_FORKING -o $@ -c $(TOP)/tools/tinytest/tinytest.c + $(Q)$(CC) $(CFLAGS) -DNO_FORKING -o $@ -c $(TINYTEST)/tinytest.c ## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here. $(BUILD)/firmware.elf: $(OBJ_COMMON) $(OBJ_RUN) From c0877cbb0d51f66fa828f6df5698751210391296 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 8 Dec 2017 20:40:55 +0200 Subject: [PATCH 098/828] py/objint_longlong: Check for zero division/modulo. --- py/objint_longlong.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 2e567c572..3e5ebadaf 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -151,9 +151,15 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i return mp_obj_new_int_from_ll(lhs_val * rhs_val); case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: + if (rhs_val == 0) { + goto zero_division; + } return mp_obj_new_int_from_ll(lhs_val / rhs_val); case MP_BINARY_OP_MODULO: case MP_BINARY_OP_INPLACE_MODULO: + if (rhs_val == 0) { + goto zero_division; + } return mp_obj_new_int_from_ll(lhs_val % rhs_val); case MP_BINARY_OP_AND: @@ -210,6 +216,9 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i default: return MP_OBJ_NULL; // op not supported } + +zero_division: + mp_raise_msg(&mp_type_ZeroDivisionError, "division by zero"); } mp_obj_t mp_obj_new_int(mp_int_t value) { From 39dd89fe3142478b48d7282b8b1bdff933c25f32 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 9 Dec 2017 01:26:21 +0200 Subject: [PATCH 099/828] py/runtime: When tracing unary/binary ops, output op (method) name. E.g.: >>> 1+1 binary 26 __add__ 3 3 Output is similar to bytecode dump (numeric code, then op name). --- py/runtime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index c7fe39367..457266c67 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -214,7 +214,7 @@ void mp_delete_global(qstr qst) { } mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { - DEBUG_OP_printf("unary " UINT_FMT " %p\n", op, arg); + DEBUG_OP_printf("unary " UINT_FMT " %q %p\n", op, mp_unary_op_method_name[op], arg); if (op == MP_UNARY_OP_NOT) { // "not x" is the negative of whether "x" is true per Python semantics @@ -275,7 +275,7 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) { } mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { - DEBUG_OP_printf("binary " UINT_FMT " %p %p\n", op, lhs, rhs); + DEBUG_OP_printf("binary " UINT_FMT " %q %p %p\n", op, mp_binary_op_method_name[op], lhs, rhs); // TODO correctly distinguish inplace operators for mutable objects // lookup logic that CPython uses for +=: From 5453d88d5db94e686cf26930e88a5e20fd21d8f8 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 9 Dec 2017 01:48:26 +0200 Subject: [PATCH 100/828] py/gc: Factor out a macro to trace GC mark operations. To allow easier override it for custom tracing. --- py/gc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/py/gc.c b/py/gc.c index 9a3c57e61..1b99ab4cf 100644 --- a/py/gc.c +++ b/py/gc.c @@ -195,6 +195,14 @@ bool gc_is_locked(void) { && ptr < (void*)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \ ) +#ifndef TRACE_MARK +#if DEBUG_PRINT +#define TRACE_MARK(block, ptr) DEBUG_printf("gc_mark(%p)\n", ptr) +#else +#define TRACE_MARK(block, ptr) +#endif +#endif + // ptr should be of type void* #define VERIFY_MARK_AND_PUSH(ptr) \ do { \ @@ -202,7 +210,7 @@ bool gc_is_locked(void) { size_t _block = BLOCK_FROM_PTR(ptr); \ if (ATB_GET_KIND(_block) == AT_HEAD) { \ /* an unmarked head, mark it, and push it on gc stack */ \ - DEBUG_printf("gc_mark(%p)\n", ptr); \ + TRACE_MARK(_block, ptr); \ ATB_HEAD_TO_MARK(_block); \ if (MP_STATE_MEM(gc_sp) < &MP_STATE_MEM(gc_stack)[MICROPY_ALLOC_GC_STACK_SIZE]) { \ *MP_STATE_MEM(gc_sp)++ = _block; \ From dea3fb93c74ae61dc5168b62a780dc6ce7865e09 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 9 Dec 2017 01:54:01 +0200 Subject: [PATCH 101/828] py/gc: In sweep debug output, print pointer as a pointer. Or it will be truncated on a 64-bit platform. --- py/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/gc.c b/py/gc.c index 1b99ab4cf..734f5c364 100644 --- a/py/gc.c +++ b/py/gc.c @@ -289,7 +289,7 @@ STATIC void gc_sweep(void) { } #endif free_tail = 1; - DEBUG_printf("gc_sweep(%x)\n", PTR_FROM_BLOCK(block)); + DEBUG_printf("gc_sweep(%p)\n", PTR_FROM_BLOCK(block)); #if MICROPY_PY_GC_COLLECT_RETVAL MP_STATE_MEM(gc_collected)++; #endif From fca1d1aa62306fc523d192c1e2dd2d20dccbe94f Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 9 Dec 2017 09:19:34 +0200 Subject: [PATCH 102/828] py/objfun: Factor out macro for decoding codestate size. fun_bc_call() starts with almost the same code as mp_obj_fun_bc_prepare_codestate(), the only difference is a way to allocate the codestate object (heap vs stack with heap fallback). Still, would be nice to avoid code duplication to make further refactoring easier. So, this commit factors out the common code before the allocation - decoding and calculating codestate size. It produces two values, so structured as a macro which writes to 2 variables passed as arguments. --- py/objfun.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/py/objfun.c b/py/objfun.c index 030b3f7cb..445f25d46 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -195,20 +195,30 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { // than this will try to use the heap, with fallback to stack allocation. #define VM_MAX_STATE_ON_STACK (11 * sizeof(mp_uint_t)) -// Set this to enable a simple stack overflow check. +// Set this to 1 to enable a simple stack overflow check. #define VM_DETECT_STACK_OVERFLOW (0) +#define DECODE_CODESTATE_SIZE(bytecode, n_state_out_var, state_size_out_var) \ + { \ + /* bytecode prelude: state size and exception stack size */ \ + n_state_out_var = mp_decode_uint_value(bytecode); \ + size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(bytecode)); \ + \ + n_state += VM_DETECT_STACK_OVERFLOW; \ + \ + /* state size in bytes */ \ + state_size_out_var = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); \ + } + #if MICROPY_STACKLESS mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); - // bytecode prelude: state size and exception stack size - size_t n_state = mp_decode_uint_value(self->bytecode); - size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(self->bytecode)); + size_t n_state, state_size; + DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size); // allocate state for locals and stack - size_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); mp_code_state_t *code_state; code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); if (!code_state) { @@ -238,16 +248,10 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); DEBUG_printf("Func n_def_args: %d\n", self->n_def_args); - // bytecode prelude: state size and exception stack size - size_t n_state = mp_decode_uint_value(self->bytecode); - size_t n_exc_stack = mp_decode_uint_value(mp_decode_uint_skip(self->bytecode)); - -#if VM_DETECT_STACK_OVERFLOW - n_state += 1; -#endif + size_t n_state, state_size; + DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size); // allocate state for locals and stack - size_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); mp_code_state_t *code_state = NULL; if (state_size > VM_MAX_STATE_ON_STACK) { code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); From d72370def72c74ca98c1ec4eb7b58ba0fbcc9629 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 9 Dec 2017 10:57:40 +0200 Subject: [PATCH 103/828] py/objfun, vm: Add comments on codestate allocation in stackless mode. --- py/objfun.c | 6 +++++- py/vm.c | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/py/objfun.c b/py/objfun.c index 445f25d46..e27413e40 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -218,8 +218,12 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args size_t n_state, state_size; DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size); - // allocate state for locals and stack mp_code_state_t *code_state; + // If we use m_new_obj_var(), then on no memory, MemoryError will be + // raised. But this is not correct exception for a function call, + // RuntimeError should be raised instead. So, we use m_new_obj_var_maybe(), + // return NULL, then vm.c takes the needed action (either raise + // RuntimeError or fallback to stack allocation). code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); if (!code_state) { return NULL; diff --git a/py/vm.c b/py/vm.c index 564200037..e6679729b 100644 --- a/py/vm.c +++ b/py/vm.c @@ -937,6 +937,9 @@ unwind_jump:; deep_recursion_error: mp_exc_recursion_depth(); } + #else + // If we couldn't allocate codestate on heap, in + // non non-strict case fall thru to stack allocation. #endif } #endif @@ -974,6 +977,9 @@ unwind_jump:; else { goto deep_recursion_error; } + #else + // If we couldn't allocate codestate on heap, in + // non non-strict case fall thru to stack allocation. #endif } #endif @@ -1008,6 +1014,9 @@ unwind_jump:; else { goto deep_recursion_error; } + #else + // If we couldn't allocate codestate on heap, in + // non non-strict case fall thru to stack allocation. #endif } #endif @@ -1045,6 +1054,9 @@ unwind_jump:; else { goto deep_recursion_error; } + #else + // If we couldn't allocate codestate on heap, in + // non non-strict case fall thru to stack allocation. #endif } #endif From 2b00181592b23e5ca97464791c1ffa56e9495489 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 9 Dec 2017 12:45:28 +0200 Subject: [PATCH 104/828] py/objfun: Factor out macro for initializing codestate. This is second part of fun_bc_call() vs mp_obj_fun_bc_prepare_codestate() common code refactor. This factors out code to initialize codestate object. After this patch, mp_obj_fun_bc_prepare_codestate() is effectively DECODE_CODESTATE_SIZE() followed by allocation followed by INIT_CODESTATE(), and fun_bc_call() starts with that too. --- py/objfun.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/py/objfun.c b/py/objfun.c index e27413e40..8fb3ec6fa 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -210,6 +210,12 @@ STATIC void dump_args(const mp_obj_t *a, size_t sz) { state_size_out_var = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); \ } +#define INIT_CODESTATE(code_state, _fun_bc, n_args, n_kw, args) \ + code_state->fun_bc = _fun_bc; \ + code_state->ip = 0; \ + mp_setup_code_state(code_state, n_args, n_kw, args); \ + code_state->old_globals = mp_globals_get(); + #if MICROPY_STACKLESS mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); @@ -229,12 +235,9 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args return NULL; } - code_state->fun_bc = self; - code_state->ip = 0; - mp_setup_code_state(code_state, n_args, n_kw, args); + INIT_CODESTATE(code_state, self, n_args, n_kw, args); // execute the byte code with the correct globals context - code_state->old_globals = mp_globals_get(); mp_globals_set(self->globals); return code_state; @@ -265,12 +268,9 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const state_size = 0; // indicate that we allocated using alloca } - code_state->fun_bc = self; - code_state->ip = 0; - mp_setup_code_state(code_state, n_args, n_kw, args); + INIT_CODESTATE(code_state, self, n_args, n_kw, args); // execute the byte code with the correct globals context - code_state->old_globals = mp_globals_get(); mp_globals_set(self->globals); mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL); mp_globals_set(code_state->old_globals); From a35d923cdf5a94ef9a29645f9dd461479dc819ce Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 9 Dec 2017 17:30:55 +0200 Subject: [PATCH 105/828] py/map: Allow to trace rehashing operations. --- py/map.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/py/map.c b/py/map.c index 4f76b9b16..696c7a2ea 100644 --- a/py/map.c +++ b/py/map.c @@ -33,6 +33,13 @@ #include "py/misc.h" #include "py/runtime.h" +#if MICROPY_DEBUG_VERBOSE // print debugging info +#define DEBUG_PRINT (1) +#else // don't print debugging info +#define DEBUG_PRINT (0) +#define DEBUG_printf(...) (void)0 +#endif + // Fixed empty map. Useful when need to call kw-receiving functions // without any keywords from C, etc. const mp_map_t mp_const_empty_map = { @@ -114,6 +121,7 @@ void mp_map_clear(mp_map_t *map) { STATIC void mp_map_rehash(mp_map_t *map) { size_t old_alloc = map->alloc; size_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1); + DEBUG_printf("mp_map_rehash(%p): " UINT_FMT " -> " UINT_FMT "\n", map, old_alloc, new_alloc); mp_map_elem_t *old_table = map->table; mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc); // If we reach this point, table resizing succeeded, now we can edit the old map. From d21d029d55ddc026fcaf6673f2b712a645286dce Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 10 Dec 2017 01:05:29 +0200 Subject: [PATCH 106/828] py/mkrules.mk: Add "clean-frozen" target to clean frozen script/modules dir. This target removes any stray files (i.e. something not committed to git) from scripts/ and modules/ dirs (or whatever FROZEN_DIR and FROZEN_MPY_DIR is set to). The expected workflow is: 1. make clean-frozen 2. micropython -m upip -p modules 3. make As it can be expected that people may drop random thing in those dirs which they can miss later, the content is actually backed up before cleaning. --- py/mkrules.mk | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/py/mkrules.mk b/py/mkrules.mk index 72a5c3de0..96f6e35a6 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -160,6 +160,27 @@ clean: $(RM) -rf $(BUILD) $(CLEAN_EXTRA) .PHONY: clean +# Clean every non-git file from FROZEN_DIR/FROZEN_MPY_DIR, but making a backup. +# We run rmdir below to avoid empty backup dir (it will silently fail if backup +# is non-empty). +clean-frozen: + if [ -n "$(FROZEN_MPY_DIR)" ]; then \ + backup_dir=$(FROZEN_MPY_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \ + cd $(FROZEN_MPY_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \ + | xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \ + rmdir ../$$backup_dir 2>/dev/null || true; \ + git clean -d -f .; \ + fi + + if [ -n "$(FROZEN_DIR)" ]; then \ + backup_dir=$(FROZEN_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \ + cd $(FROZEN_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \ + | xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \ + rmdir ../$$backup_dir 2>/dev/null || true; \ + git clean -d -f .; \ + fi +.PHONY: clean-frozen + print-cfg: $(ECHO) "PY_SRC = $(PY_SRC)" $(ECHO) "BUILD = $(BUILD)" From e7fc765880ba6025ac5677db696669f43975483e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 10 Dec 2017 02:15:43 +0200 Subject: [PATCH 107/828] unix/mpconfigport: Disable uio.resource_stream(). This function was implemented as an experiment, and was enabled only in unix port. To remind, it allows to access arbitrary files frozen as source modules (vs bytecode). However, further experimentation showed that the same functionality can be implemented with frozen bytecode. The process requires more steps, but with suitable toolset it doesn't matter patch. This process is: 1. Convert binary files into "Python resource module" with tools/mpy_bin2res.py. 2. Freeze as the bytecode. 3. Use micropython-lib's pkg_resources.resource_stream() to access it. In other words, the extra step is using tools/mpy_bin2res.py (because there would be wrapper for uio.resource_stream() anyway). Going frozen bytecode route allows more flexibility, and same/additional efficiency: 1. Frozen source support can be disabled altogether for additional code savings. 2. Resources could be also accessed as a buffer, not just as a stream. There're few caveats too: 1. It wasn't actually profiled the overhead of storing a resource in "Python resource module" vs storing it directly, but it's assumed that overhead is small. 2. The "efficiency" claim above applies to the case when resource file is frozen as the bytecode. If it's not, it actually will take a lot of RAM on loading. But in this case, the resource file should not be used (i.e. generated) in the first place, and micropython-lib's pkg_resources.resource_stream() implementation has the appropriate fallback to read the raw files instead. This still poses some distribution issues, e.g. to deployable to baremetal ports (which almost certainly would require freezeing as the bytecode), a distribution package should include the resource module. But for non-freezing deployment, presense of resource module will lead to memory inefficiency. All the discussion above reminds why uio.resource_stream() was implemented in the first place - to address some of the issues above. However, since then, frozen bytecode approach seems to prevail, so, while there're still some issues to address with it, this change is being made. This change saves 488 bytes for the unix x86_64 port. --- ports/unix/mpconfigport.h | 1 - py/mpconfig.h | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index a3d2bb7db..06ae1e234 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -103,7 +103,6 @@ #endif #define MICROPY_PY_CMATH (1) #define MICROPY_PY_IO_FILEIO (1) -#define MICROPY_PY_IO_RESOURCE_STREAM (1) #define MICROPY_PY_GC_COLLECT_RETVAL (1) #define MICROPY_MODULE_FROZEN_STR (1) diff --git a/py/mpconfig.h b/py/mpconfig.h index 095b7ef7b..778436708 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -938,7 +938,11 @@ typedef double mp_float_t; // Whether to provide "uio.resource_stream()" function with // the semantics of CPython's pkg_resources.resource_stream() -// (allows to access resources in frozen packages). +// (allows to access binary resources in frozen source packages). +// Note that the same functionality can be achieved in "pure +// Python" by prepocessing binary resources into Python source +// and bytecode-freezing it (with a simple helper module available +// e.g. in micropython-lib). #ifndef MICROPY_PY_IO_RESOURCE_STREAM #define MICROPY_PY_IO_RESOURCE_STREAM (0) #endif From c60fc670ea9c2f525e16bb5a175db077b71b93e6 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 00:06:38 +0200 Subject: [PATCH 108/828] docs/reference/packages: Add chapter on distribution packages and deployment. A long overdue overview of preparing packages, installing them with upip, freezing, dealing with resources. Initial version, more iterations required. --- docs/reference/index.rst | 1 + docs/reference/packages.rst | 274 ++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 docs/reference/packages.rst diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 4d822d6fa..9c5c164f3 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -24,6 +24,7 @@ implementation and the best practices to use them. isr_rules.rst speed_python.rst constrained.rst + packages.rst .. only:: port_pyboard diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst new file mode 100644 index 000000000..28f5f9f48 --- /dev/null +++ b/docs/reference/packages.rst @@ -0,0 +1,274 @@ +Distribution packages, package management, and deploying applications +===================================================================== + +Just as the "big" Python, MicroPython supports creation of "third party" +packages, distributing them, and easily installing them in each user's +environment. This chapter discusses how these actions are achieved. +Some familiarity with Python packaging is recommended. + +Overview +-------- + +Steps below represent a high-level workflow when creating and consuming +packages: + +1. Python modules and packages are turned into distribution package + archives, and published at the Python Package Index (PyPI). +2. `upip` package manager can be used to install a distribution package + on a `MicroPython port` with networking capabilities (for example, + on the Unix port). +3. For ports without networking capabilities, an "installation image" + can be prepared on the Unix port, and transferred to a device by + suitable means. +4. For low-memory ports, the installation image can be frozen as the + bytecode into MicroPython executable, thus minimizing the memory + storage overheads. + +The sections below describe this process in details. + +Distribution packages +--------------------- + +Python modules and packages can be packaged into archives suitable for +transfer between systems, storing at the well-known location (PyPI), +and downloading on demand for deployment. These archives are known as +*distribution packages* (to differentiate them from Python packages +(means to organize Python source code)). + +The MicroPython distribution package format is a well-known tar.gz +format, with some adaptations however. The Gzip compressor, used as +an external wrapper for TAR archives, by default uses 32KB dictionary +size, which means that to uncompress a compressed stream, 32KB of +contguous memory needs to be allocated. This requirement may be not +satisfiable on low-memory devices, which may have total memory available +less than that amount, and even if not, a contiguous block like that +may be hard to allocate due to `memory fragmentation`. To accommodate +these constraints, MicroPython distribution packages use Gzip compression +with the dictionary size of 4K, which should be a suitable compromise +with still achieving some compression while being able to uncompressed +even by the smallest devices. + +Besides the small compression dictionary size, MicroPython distribution +packages also have other optimizations, like removing any files from +the archive which aren't used by the installation process. In particular, +`upip` package manager doesn't execute ``setup.py`` during installation +(see below), and thus that file is not included in the archive. + +At the same time, these optimizations make MicroPython distribution +packages not compatible with `CPython`'s package manager, ``pip``. +This isn't considered a big problem, because: + +1. Packages can be installed with `upip`, and then can be used with + CPython (if they are compatible with it). +2. In the other direction, majority of CPython packages would be + incompatible with MicroPython by various reasons, first of all, + the reliance on features not implemented by MicroPython. + +Summing up, the MicroPython distribution package archives are highly +optimized for MicroPython's target environments, which are highly +resource constrained devices. + + +``upip`` package manager +------------------------ + +MicroPython distribution packages are intended to be installed using +the `upip` package manager. `upip` is a Python application which is +usually distributed (as frozen bytecode) with network-enabled +`MicroPython ports `. At the very least, +`upip` is available in the `MicroPython Unix port`. + +On any `MicroPython port` providing `upip`, it can be accessed as +following:: + + import upip + upip.help() + upip.install(package_or_package_list, [path]) + +Where *package_or_package_list* is the name of a distribution +package to install, or a list of such names to install multiple +packages. Optional *path* parameter specifies filesystem +location to install under and defaults to the standard library +location (see below). + +An example of installing a specific package and then using it:: + + >>> import upip + >>> upip.install("micropython-pystone_lowmem") + [...] + >>> import pystone_lowmem + >>> pystone_lowmem.main() + +Note that the name of Python package and the name of distribution +package for it in general don't have to match, and oftentimes they +don't. This is because PyPI provides a central package repository +for all different Python implementations and versions, and thus +distribution package names may need to be namespaced for a particular +implementation. For example, all packages from `micropython-lib` +follow this naming convention: for a Python module or package named +``foo``, the distribution package name is ``micropython-foo``. + +For the ports which run MicroPython executable from the OS command +prompts (like the Unix port), `upip` can be (and indeed, usually is) +run from the command line instead of MicroPython's own REPL. The +commands which corresponds to the example above are:: + + micropython -m upip -h + micropython -m upip install [-p ] ... + micropython -m upip install micropython-pystone_lowmem + +[TODO: Describe installation path.] + + +Cross-installing packages +------------------------- + +For `MicroPython ports ` without native networking +capabilities, the recommend process is "cross-installing" them into a +"directory image" using the `MicroPython Unix port`, and then +transferring this image to a device by suitable means. + +Installing to a directory image involves using ``-p`` switch to `upip`:: + + micropython -m upip install -p install_image micropython-pystone_lowmem + +After this command, the package content (and contents of every depenency +packages) will be available in the ``install_image/`` subdirectory. You +would need to transfer contents of this directory (without the +``install_image/`` prefix) to the device, at the suitable location, where +it can be found by the Python ``import`` statement (see discussion of +the `upip` installation path above). + + +Cross-installing packages with freezing +--------------------------------------- + +For the low-memory `MicroPython ports `, the process +described in the previous section does not provide the most efficient +resource usage,because the packages are installed in the source form, +so need to be compiled to the bytecome on each import. This compilation +requires RAM, and the resulting bytecode is also stored in RAM, reducing +its amount available for storing application data. Moreover, the process +above requires presence of the filesystem on a device, and the most +resource-constrained devices may not even have it. + +The bytecode freezing is a process which resolves all the issues +mentioned above: + +* The source code is pre-compiled into bytecode and store as such. +* The bytecode is stored in ROM, not RAM. +* Filesystem is not required for frozen packages. + +Using frozen bytecode requires building the executable (firmware) +for a given `MicroPython port` from the C source code. Consequently, +the process is: + +1. Follow the instructions for a particular port on setting up a + toolchain and building the port. For example, for ESP8266 port, + study instructions in ``ports/esp8266/README.md`` and follow them. + Make sure you can build the port and deploy the resulting + executable/firmware successfully before proceeding to the next steps. +2. Build `MicroPython Unix port` and make sure it is in your PATH and + you can execute ``micropython``. +3. Change to port's directory (e.g. ``ports/esp8266/`` for ESP8266). +4. Run ``make clean-frozen``. This step cleans up any previous + modules which were installed for freezing (consequently, you need + to skip this step to add additional modules, instead of starting + from scratch). +5. Run ``micropython -m upip install -p modules ...`` to + install packages you want to freeze. +6. Run ``make clean``. +7. Run ``make``. + +After this, you should have the executable/firmware with modules as +the bytecode inside, which you can deploy the usual way. + +Few notes: + +1. Step 5 in the sequence above assumes that the distribution package + is available from PyPI. If that is not the case, you would need + to copy Python source files manually to ``modules/`` subdirectory + of the port port directory. (Note that upip does not support + installing from e.g. version control repositories). +2. The firmware for baremetal devices usually has size restrictions, + so adding too many frozen modules may overflow it. Usually, you + would get a linking error if this happens. However, in some cases, + an image may be produced, which is not runnable on a device. Such + cases are in general bugs, and should be reported and further + investigated. If you face such a situation, as an initial step, + you may want to decrease the amount of frozen modules included. + + +Application resources +--------------------- + +A complete application, besides the source code, oftentimes also consists +of data files, e.g. web page templates, game images, etc. It's clear how +to deal with those when application is installed manually - you just put +those data files in the filesystem at some location and use the normal +file access functions. + +The situation is different when deploying applications from packages - this +is more advanced, streamlined and flexible way, but also requires more +advanced approach to accessing data files. This approach is treating +the data files as "resources", and abstracting away access to them. + +Python supports resource access using its "setuptools" library, using +``pkg_resources`` module. MicroPython, following its usual approach, +implements subset of the functionality of that module, specifically +`pkg_resources.resource_stream(package, resource)` function. +The idea is that an application calls this function, passing a +resource identifier, which is a relative path to data file within +the specified package (usually top-level application package). It +returns a stream object which can be used to access resource contents. +Thus, the ``resource_stream()`` emulates interface of the standard +`open()` function. + +Implementation-wise, ``resource_stream()`` uses file operations +underlyingly, if distribution package is install in the filesystem. +However, it also supports functioning without the underlying filesystem, +e.g. if the package is frozen as the bytecode. This however requires +an extra intermediate step when packaging application - creation of +"Python resource module". + +The idea of this module is to convert binary data to a Python bytes +object, and put it into the dictionary, indexed by the resource name. +This conversion is done using ``tools/mpy_bin2res.py`` script from +the MicroPython distribution. + +Let's trace the complete process using the following example. Suppose +your application has the following structure:: + + my_app/ + __main__.py + utils.py + data/ + page.html + image.png + +``__main__.py`` and ``utils.py`` should access resources using the +following calls:: + + import pkg_resources + + pkg_resources.resource_stream(__name__, "data/page.html") + pkg_resources.resource_stream(__name__, "data/image.png") + +You can develop and debug using the `MicroPython Unix port` as usual. +When times come to make a distribution package out of it, you would +need to run following command, with ``my_app/`` being the current +directory (and assuming ``mpy_bin2res.py`` is in your path):: + + mpy_bin2res.py data/page.html data/image.png + +This will produce a Python resource module named ``R.py``. Afterwards, +you package the project for distribution as usual (using ``setup.py sdist``). +Prepared like this, your application will work both when deployed to +filesystem and as frozen bytecode. + +References +---------- + +* Python Packaging User Guide: https://packaging.python.org/ +* Setuptools documentation: https://setuptools.readthedocs.io/ +* Distutils documentation: https://docs.python.org/3/library/distutils.html From 5b8998da6dfed6c8f54b8b34228f25d93dbb9d29 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 20 Nov 2017 17:29:58 +1100 Subject: [PATCH 109/828] py/runtime: Move mp_exc_recursion_depth to runtime and rename to raise. For consistency this helper function is renamed to match the other exception helpers, and moved to their location in runtime.c. --- py/runtime.c | 7 +++++++ py/runtime.h | 2 +- py/stackctrl.c | 7 +------ py/vm.c | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 457266c67..5fd053e1a 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1456,3 +1456,10 @@ NORETURN void mp_raise_OSError(int errno_) { NORETURN void mp_raise_NotImplementedError(const char *msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } + +#if MICROPY_STACK_CHECK +NORETURN void mp_raise_recursion_depth(void) { + nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, + MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded))); +} +#endif diff --git a/py/runtime.h b/py/runtime.h index 3f0d1104e..a19f64c06 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -151,7 +151,7 @@ NORETURN void mp_raise_ValueError(const char *msg); NORETURN void mp_raise_TypeError(const char *msg); NORETURN void mp_raise_NotImplementedError(const char *msg); NORETURN void mp_raise_OSError(int errno_); -NORETURN void mp_exc_recursion_depth(void); +NORETURN void mp_raise_recursion_depth(void); #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG #undef mp_check_self diff --git a/py/stackctrl.c b/py/stackctrl.c index 11165b9a6..5c07796bd 100644 --- a/py/stackctrl.c +++ b/py/stackctrl.c @@ -48,14 +48,9 @@ void mp_stack_set_limit(mp_uint_t limit) { MP_STATE_THREAD(stack_limit) = limit; } -NORETURN void mp_exc_recursion_depth(void) { - nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, - MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded))); -} - void mp_stack_check(void) { if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { - mp_exc_recursion_depth(); + mp_raise_recursion_depth(); } } diff --git a/py/vm.c b/py/vm.c index e6679729b..5011af5c3 100644 --- a/py/vm.c +++ b/py/vm.c @@ -935,7 +935,7 @@ unwind_jump:; #if MICROPY_STACKLESS_STRICT else { deep_recursion_error: - mp_exc_recursion_depth(); + mp_raise_recursion_depth(); } #else // If we couldn't allocate codestate on heap, in From 02d830c035aca166d70551e485ccd2d1658189c9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Nov 2017 23:28:40 +1100 Subject: [PATCH 110/828] py: Introduce a Python stack for scoped allocation. This patch introduces the MICROPY_ENABLE_PYSTACK option (disabled by default) which enables a "Python stack" that allows to allocate and free memory in a scoped, or Last-In-First-Out (LIFO) way, similar to alloca(). A new memory allocation API is introduced along with this Py-stack. It includes both "local" and "nonlocal" LIFO allocation. Local allocation is intended to be equivalent to using alloca(), whereby the same function must free the memory. Nonlocal allocation is where another function may free the memory, so long as it's still LIFO. Follow-up patches will convert all uses of alloca() and VLA to the new scoped allocation API. The old behaviour (using alloca()) will still be available, but when MICROPY_ENABLE_PYSTACK is enabled then alloca() is no longer required or used. The benefits of enabling this option are (or will be once subsequent patches are made to convert alloca()/VLA): - Toolchains without alloca() can use this feature to obtain correct and efficient scoped memory allocation (compared to using the heap instead of alloca(), which is slower). - Even if alloca() is available, enabling the Py-stack gives slightly more efficient use of stack space when calling nested Python functions, due to the way that compilers implement alloca(). - Enabling the Py-stack with the stackless mode allows for even more efficient stack usage, as well as retaining high performance (because the heap is no longer used to build and destroy stackless code states). - With Py-stack and stackless enabled, Python-calling-Python is no longer recursive in the C mp_execute_bytecode function. The micropython.pystack_use() function is included to measure usage of the Python stack. --- py/gc.c | 7 +++ py/modmicropython.c | 10 ++++ py/modthread.c | 6 +++ py/mpconfig.h | 11 ++++ py/mpstate.h | 6 +++ py/nlr.h | 19 ++++++- py/nlrsetjmp.c | 1 + py/nlrthumb.c | 2 + py/nlrx64.c | 2 + py/nlrx86.c | 2 + py/nlrxtensa.c | 2 + py/py.mk | 1 + py/pystack.c | 56 ++++++++++++++++++++ py/pystack.h | 123 ++++++++++++++++++++++++++++++++++++++++++++ py/runtime.c | 2 +- py/runtime.h | 1 + 16 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 py/pystack.c create mode 100644 py/pystack.h diff --git a/py/gc.c b/py/gc.c index 734f5c364..5196954e2 100644 --- a/py/gc.c +++ b/py/gc.c @@ -320,11 +320,18 @@ void gc_collect_start(void) { #endif MP_STATE_MEM(gc_stack_overflow) = 0; MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack); + // Trace root pointers. This relies on the root pointers being organised // correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals, // dict_globals, then the root pointer section of mp_state_vm. void **ptrs = (void**)(void*)&mp_state_ctx; gc_collect_root(ptrs, offsetof(mp_state_ctx_t, vm.qstr_last_chunk) / sizeof(void*)); + + #if MICROPY_ENABLE_PYSTACK + // Trace root pointers from the Python stack. + ptrs = (void**)(void*)MP_STATE_THREAD(pystack_start); + gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void*)); + #endif } void gc_collect_root(void **ptrs, size_t len) { diff --git a/py/modmicropython.c b/py/modmicropython.c index 2aac53adc..c14a0177d 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -112,6 +112,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_st #endif // MICROPY_PY_MICROPYTHON_MEM_INFO +#if MICROPY_ENABLE_PYSTACK +STATIC mp_obj_t mp_micropython_pystack_use(void) { + return MP_OBJ_NEW_SMALL_INT(mp_pystack_usage()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_pystack_use_obj, mp_micropython_pystack_use); +#endif + #if MICROPY_ENABLE_GC STATIC mp_obj_t mp_micropython_heap_lock(void) { gc_lock(); @@ -167,6 +174,9 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = { #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0) { MP_ROM_QSTR(MP_QSTR_alloc_emergency_exception_buf), MP_ROM_PTR(&mp_alloc_emergency_exception_buf_obj) }, #endif + #if MICROPY_ENABLE_PYSTACK + { MP_ROM_QSTR(MP_QSTR_pystack_use), MP_ROM_PTR(&mp_micropython_pystack_use_obj) }, + #endif #if MICROPY_ENABLE_GC { MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) }, { MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) }, diff --git a/py/modthread.c b/py/modthread.c index cb071d0f8..61ada5035 100644 --- a/py/modthread.c +++ b/py/modthread.c @@ -165,6 +165,12 @@ STATIC void *thread_entry(void *args_in) { mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan mp_stack_set_limit(args->stack_size); + #if MICROPY_ENABLE_PYSTACK + // TODO threading and pystack is not fully supported, for now just make a small stack + mp_obj_t mini_pystack[128]; + mp_pystack_init(mini_pystack, &mini_pystack[128]); + #endif + // set locals and globals from the calling context mp_locals_set(args->dict_locals); mp_globals_set(args->dict_globals); diff --git a/py/mpconfig.h b/py/mpconfig.h index 778436708..96cd8c651 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -441,6 +441,17 @@ #define MICROPY_ENABLE_FINALISER (0) #endif +// Whether to enable a separate allocator for the Python stack. +// If enabled then the code must call mp_pystack_init before mp_init. +#ifndef MICROPY_ENABLE_PYSTACK +#define MICROPY_ENABLE_PYSTACK (0) +#endif + +// Number of bytes that memory returned by mp_pystack_alloc will be aligned by. +#ifndef MICROPY_PYSTACK_ALIGN +#define MICROPY_PYSTACK_ALIGN (8) +#endif + // Whether to check C stack usage. C stack used for calling Python functions, // etc. Not checking means segfault on overflow. #ifndef MICROPY_STACK_CHECK diff --git a/py/mpstate.h b/py/mpstate.h index 6a39ebdea..d3b53f915 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -224,6 +224,12 @@ typedef struct _mp_state_thread_t { #if MICROPY_STACK_CHECK size_t stack_limit; #endif + + #if MICROPY_ENABLE_PYSTACK + uint8_t *pystack_start; + uint8_t *pystack_end; + uint8_t *pystack_cur; + #endif } mp_state_thread_t; // This structure combines the above 3 structures. diff --git a/py/nlr.h b/py/nlr.h index 63fe392d9..1235f1460 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -62,15 +62,32 @@ struct _nlr_buf_t { #if MICROPY_NLR_SETJMP jmp_buf jmpbuf; #endif + + #if MICROPY_ENABLE_PYSTACK + void *pystack; + #endif }; +// Helper macros to save/restore the pystack state +#if MICROPY_ENABLE_PYSTACK +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur) +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack +#else +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf +#endif + #if MICROPY_NLR_SETJMP #include "py/mpstate.h" NORETURN void nlr_setjmp_jump(void *val); // nlr_push() must be defined as a macro, because "The stack context will be // invalidated if the function which called setjmp() returns." -#define nlr_push(buf) ((buf)->prev = MP_STATE_THREAD(nlr_top), MP_STATE_THREAD(nlr_top) = (buf), setjmp((buf)->jmpbuf)) +#define nlr_push(buf) ( \ + (buf)->prev = MP_STATE_THREAD(nlr_top), \ + MP_NLR_SAVE_PYSTACK(buf), \ + MP_STATE_THREAD(nlr_top) = (buf), \ + setjmp((buf)->jmpbuf)) #define nlr_pop() { MP_STATE_THREAD(nlr_top) = MP_STATE_THREAD(nlr_top)->prev; } #define nlr_jump(val) nlr_setjmp_jump(val) #else diff --git a/py/nlrsetjmp.c b/py/nlrsetjmp.c index 1fb459440..63376a553 100644 --- a/py/nlrsetjmp.c +++ b/py/nlrsetjmp.c @@ -35,6 +35,7 @@ void nlr_setjmp_jump(void *val) { nlr_jump_fail(val); } top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); *top_ptr = top->prev; longjmp(top->jmpbuf, 1); } diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 6e7d71766..eab5759f2 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -82,6 +82,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); *top = nlr; return 0; // normal return } @@ -99,6 +100,7 @@ NORETURN __attribute__((naked)) void nlr_jump(void *val) { } top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); *top_ptr = top->prev; __asm volatile ( diff --git a/py/nlrx64.c b/py/nlrx64.c index 847d10398..ddcd76166 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -91,6 +91,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); *top = nlr; return 0; // normal return } @@ -108,6 +109,7 @@ NORETURN void nlr_jump(void *val) { } top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); *top_ptr = top->prev; __asm volatile ( diff --git a/py/nlrx86.c b/py/nlrx86.c index 094dea3cc..3a27460eb 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -73,6 +73,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); *top = nlr; return 0; // normal return } @@ -90,6 +91,7 @@ NORETURN void nlr_jump(void *val) { } top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); *top_ptr = top->prev; __asm volatile ( diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index 4520e7e7a..5a969fc87 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -58,6 +58,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); *top = nlr; return 0; // normal return } @@ -75,6 +76,7 @@ NORETURN void nlr_jump(void *val) { } top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); *top_ptr = top->prev; __asm volatile ( diff --git a/py/py.mk b/py/py.mk index f5faad182..0b5d5f8c4 100644 --- a/py/py.mk +++ b/py/py.mk @@ -110,6 +110,7 @@ PY_O_BASENAME = \ nlrsetjmp.o \ malloc.o \ gc.o \ + pystack.o \ qstr.o \ vstr.o \ mpprint.o \ diff --git a/py/pystack.c b/py/pystack.c new file mode 100644 index 000000000..767c307e9 --- /dev/null +++ b/py/pystack.c @@ -0,0 +1,56 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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" + +#if MICROPY_ENABLE_PYSTACK + +void mp_pystack_init(void *start, void *end) { + MP_STATE_THREAD(pystack_start) = start; + MP_STATE_THREAD(pystack_end) = end; + MP_STATE_THREAD(pystack_cur) = start; +} + +void *mp_pystack_alloc(size_t n_bytes) { + n_bytes = (n_bytes + (MICROPY_PYSTACK_ALIGN - 1)) & ~(MICROPY_PYSTACK_ALIGN - 1); + #if MP_PYSTACK_DEBUG + n_bytes += MICROPY_PYSTACK_ALIGN; + #endif + if (MP_STATE_THREAD(pystack_cur) + n_bytes > MP_STATE_THREAD(pystack_end)) { + // out of memory in the pystack + mp_raise_recursion_depth(); + } + void *ptr = MP_STATE_THREAD(pystack_cur); + MP_STATE_THREAD(pystack_cur) += n_bytes; + #if MP_PYSTACK_DEBUG + *(size_t*)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN) = n_bytes; + #endif + return ptr; +} + +#endif diff --git a/py/pystack.h b/py/pystack.h new file mode 100644 index 000000000..82ac3743d --- /dev/null +++ b/py/pystack.h @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 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. + */ +#ifndef MICROPY_INCLUDED_PY_PYSTACK_H +#define MICROPY_INCLUDED_PY_PYSTACK_H + +#include "py/mpstate.h" + +// Enable this debugging option to check that the amount of memory freed is +// consistent with amounts that were previously allocated. +#define MP_PYSTACK_DEBUG (0) + +#if MICROPY_ENABLE_PYSTACK + +void mp_pystack_init(void *start, void *end); +void *mp_pystack_alloc(size_t n_bytes); + +// This function can free multiple continuous blocks at once: just pass the +// pointer to the block that was allocated first and it and all subsequently +// allocated blocks will be freed. +static inline void mp_pystack_free(void *ptr) { + assert((uint8_t*)ptr >= MP_STATE_THREAD(pystack_start)); + assert((uint8_t*)ptr <= MP_STATE_THREAD(pystack_cur)); + #if MP_PYSTACK_DEBUG + size_t n_bytes_to_free = MP_STATE_THREAD(pystack_cur) - (uint8_t*)ptr; + size_t n_bytes = *(size_t*)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN); + while (n_bytes < n_bytes_to_free) { + n_bytes += *(size_t*)(MP_STATE_THREAD(pystack_cur) - n_bytes - MICROPY_PYSTACK_ALIGN); + } + if (n_bytes != n_bytes_to_free) { + mp_printf(&mp_plat_print, "mp_pystack_free() failed: %u != %u\n", (uint)n_bytes_to_free, + (uint)*(size_t*)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN)); + assert(0); + } + #endif + MP_STATE_THREAD(pystack_cur) = (uint8_t*)ptr; +} + +static inline void mp_pystack_realloc(void *ptr, size_t n_bytes) { + mp_pystack_free(ptr); + mp_pystack_alloc(n_bytes); +} + +static inline size_t mp_pystack_usage(void) { + return MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start); +} + +static inline size_t mp_pystack_limit(void) { + return MP_STATE_THREAD(pystack_end) - MP_STATE_THREAD(pystack_start); +} + +#endif + +#if !MICROPY_ENABLE_PYSTACK + +#define mp_local_alloc(n_bytes) alloca(n_bytes) + +static inline void mp_local_free(void *ptr) { + (void)ptr; +} + +static inline void *mp_nonlocal_alloc(size_t n_bytes) { + return m_new(uint8_t, n_bytes); +} + +static inline void *mp_nonlocal_realloc(void *ptr, size_t old_n_bytes, size_t new_n_bytes) { + return m_renew(uint8_t, ptr, old_n_bytes, new_n_bytes); +} + +static inline void mp_nonlocal_free(void *ptr, size_t n_bytes) { + m_del(uint8_t, ptr, n_bytes); +} + +#else + +static inline void *mp_local_alloc(size_t n_bytes) { + return mp_pystack_alloc(n_bytes); +} + +static inline void mp_local_free(void *ptr) { + mp_pystack_free(ptr); +} + +static inline void *mp_nonlocal_alloc(size_t n_bytes) { + return mp_pystack_alloc(n_bytes); +} + +static inline void *mp_nonlocal_realloc(void *ptr, size_t old_n_bytes, size_t new_n_bytes) { + (void)old_n_bytes; + mp_pystack_realloc(ptr, new_n_bytes); + return ptr; +} + +static inline void mp_nonlocal_free(void *ptr, size_t n_bytes) { + (void)n_bytes; + mp_pystack_free(ptr); +} + +#endif + +#endif // MICROPY_INCLUDED_PY_PYSTACK_H diff --git a/py/runtime.c b/py/runtime.c index 5fd053e1a..3a4a8a93b 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1457,7 +1457,7 @@ NORETURN void mp_raise_NotImplementedError(const char *msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } -#if MICROPY_STACK_CHECK +#if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK NORETURN void mp_raise_recursion_depth(void) { nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded))); diff --git a/py/runtime.h b/py/runtime.h index a19f64c06..6288e8836 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_RUNTIME_H #include "py/mpstate.h" +#include "py/pystack.h" typedef enum { MP_VM_RETURN_NORMAL, From 1e5a33df413bbd8a8aa5bd880be445c684fc5506 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Nov 2017 23:37:19 +1100 Subject: [PATCH 111/828] py: Convert all uses of alloca() to use new scoped allocation API. --- py/builtinimport.c | 5 +++-- py/compile.c | 3 ++- py/objboundmeth.c | 8 ++++++++ py/objfun.c | 12 ++++++++++++ py/runtime.c | 3 ++- py/vm.c | 16 ++++++++++++++-- 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/py/builtinimport.c b/py/builtinimport.c index 9235e946c..2157902c9 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -318,7 +318,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { } uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len); - char *new_mod = alloca(new_mod_l); + char *new_mod = mp_local_alloc(new_mod_l); memcpy(new_mod, this_name, p - this_name); if (mod_len != 0) { new_mod[p - this_name] = '.'; @@ -326,9 +326,10 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { } qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l); + mp_local_free(new_mod); DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q)); module_name = MP_OBJ_NEW_QSTR(new_mod_q); - mod_str = new_mod; + mod_str = qstr_str(new_mod_q); mod_len = new_mod_l; } diff --git a/py/compile.c b/py/compile.c index ee017498a..52d10ee5e 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1050,7 +1050,7 @@ STATIC void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) { for (int i = 0; i < n; i++) { len += qstr_len(MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])); } - char *q_ptr = alloca(len); + char *q_ptr = mp_local_alloc(len); char *str_dest = q_ptr; for (int i = 0; i < n; i++) { if (i > 0) { @@ -1062,6 +1062,7 @@ STATIC void do_import_name(compiler_t *comp, mp_parse_node_t pn, qstr *q_base) { str_dest += str_src_len; } qstr q_full = qstr_from_strn(q_ptr, len); + mp_local_free(q_ptr); EMIT_ARG(import_name, q_full); if (is_as) { for (int i = 1; i < n; i++) { diff --git a/py/objboundmeth.c b/py/objboundmeth.c index 890f8b15b..b0df6a68a 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -51,6 +51,9 @@ mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, s // need to insert self before all other args and then call meth size_t n_total = n_args + 2 * n_kw; mp_obj_t *args2 = NULL; + #if MICROPY_ENABLE_PYSTACK + args2 = mp_pystack_alloc(sizeof(mp_obj_t) * (1 + n_total)); + #else mp_obj_t *free_args2 = NULL; if (n_total > 4) { // try to use heap to allocate temporary args array @@ -61,12 +64,17 @@ mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, s // (fallback to) use stack to allocate temporary args array args2 = alloca(sizeof(mp_obj_t) * (1 + n_total)); } + #endif args2[0] = self; memcpy(args2 + 1, args, n_total * sizeof(mp_obj_t)); mp_obj_t res = mp_call_function_n_kw(meth, n_args + 1, n_kw, args2); + #if MICROPY_ENABLE_PYSTACK + mp_pystack_free(args2); + #else if (free_args2 != NULL) { m_del(mp_obj_t, free_args2, 1 + n_total); } + #endif return res; } diff --git a/py/objfun.c b/py/objfun.c index 8fb3ec6fa..e6d33d287 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -225,6 +225,9 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args DECODE_CODESTATE_SIZE(self->bytecode, n_state, state_size); mp_code_state_t *code_state; + #if MICROPY_ENABLE_PYSTACK + code_state = mp_pystack_alloc(sizeof(mp_code_state_t) + state_size); + #else // If we use m_new_obj_var(), then on no memory, MemoryError will be // raised. But this is not correct exception for a function call, // RuntimeError should be raised instead. So, we use m_new_obj_var_maybe(), @@ -234,6 +237,7 @@ mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, size_t n_args if (!code_state) { return NULL; } + #endif INIT_CODESTATE(code_state, self, n_args, n_kw, args); @@ -260,6 +264,9 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const // allocate state for locals and stack mp_code_state_t *code_state = NULL; + #if MICROPY_ENABLE_PYSTACK + code_state = mp_pystack_alloc(sizeof(mp_code_state_t) + state_size); + #else if (state_size > VM_MAX_STATE_ON_STACK) { code_state = m_new_obj_var_maybe(mp_code_state_t, byte, state_size); } @@ -267,6 +274,7 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const code_state = alloca(sizeof(mp_code_state_t) + state_size); state_size = 0; // indicate that we allocated using alloca } + #endif INIT_CODESTATE(code_state, self, n_args, n_kw, args); @@ -312,10 +320,14 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const result = code_state->state[n_state - 1]; } + #if MICROPY_ENABLE_PYSTACK + mp_pystack_free(code_state); + #else // free the state if it was allocated on the heap if (state_size != 0) { m_del_var(mp_code_state_t, byte, state_size, code_state); } + #endif if (vm_return_kind == MP_VM_RETURN_NORMAL) { return result; diff --git a/py/runtime.c b/py/runtime.c index 3a4a8a93b..8df0c0a08 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1348,11 +1348,12 @@ import_error: const char *pkg_name = mp_obj_str_get_data(dest[0], &pkg_name_len); const uint dot_name_len = pkg_name_len + 1 + qstr_len(name); - char *dot_name = alloca(dot_name_len); + char *dot_name = mp_local_alloc(dot_name_len); memcpy(dot_name, pkg_name, pkg_name_len); dot_name[pkg_name_len] = '.'; memcpy(dot_name + pkg_name_len + 1, qstr_str(name), qstr_len(name)); qstr dot_name_q = qstr_from_strn(dot_name, dot_name_len); + mp_local_free(dot_name); mp_obj_t args[5]; args[0] = MP_OBJ_NEW_QSTR(dot_name_q); diff --git a/py/vm.c b/py/vm.c index 5011af5c3..a8a73f323 100644 --- a/py/vm.c +++ b/py/vm.c @@ -1108,7 +1108,13 @@ unwind_return: if (code_state->prev != NULL) { mp_obj_t res = *sp; mp_globals_set(code_state->old_globals); - code_state = code_state->prev; + mp_code_state_t *new_code_state = code_state->prev; + #if MICROPY_ENABLE_PYSTACK + // The sizeof in the following statement does not include the size of the variable + // part of the struct. This arg is anyway not used if pystack is enabled. + mp_nonlocal_free(code_state, sizeof(mp_code_state_t)); + #endif + code_state = new_code_state; *code_state->sp = res; goto run_code_state; } @@ -1450,7 +1456,13 @@ unwind_loop: #if MICROPY_STACKLESS } else if (code_state->prev != NULL) { mp_globals_set(code_state->old_globals); - code_state = code_state->prev; + mp_code_state_t *new_code_state = code_state->prev; + #if MICROPY_ENABLE_PYSTACK + // The sizeof in the following statement does not include the size of the variable + // part of the struct. This arg is anyway not used if pystack is enabled. + mp_nonlocal_free(code_state, sizeof(mp_code_state_t)); + #endif + code_state = new_code_state; size_t n_state = mp_decode_uint_value(code_state->fun_bc->bytecode); fastn = &code_state->state[n_state - 1]; exc_stack = (mp_exc_stack_t*)(code_state->state + n_state); From 6df7b2f2fee71e231927511d4baf58ff86eb7983 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Nov 2017 23:38:50 +1100 Subject: [PATCH 112/828] extmod/machine_signal: Change VLA to use new scoped allocation API. --- extmod/machine_signal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extmod/machine_signal.c b/extmod/machine_signal.c index 78d0c3fee..3f9f5af94 100644 --- a/extmod/machine_signal.c +++ b/extmod/machine_signal.c @@ -58,7 +58,7 @@ STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t // If first argument isn't a Pin-like object, we filter out "invert" // from keyword arguments and pass them all to the exported Pin // constructor to create one. - mp_obj_t pin_args[n_args + n_kw * 2]; + mp_obj_t *pin_args = mp_local_alloc((n_args + n_kw * 2) * sizeof(mp_obj_t)); memcpy(pin_args, args, n_args * sizeof(mp_obj_t)); const mp_obj_t *src = args + n_args; mp_obj_t *dst = pin_args + n_args; @@ -88,6 +88,8 @@ STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t // will just ignore it as set a concrete type. If not, we'd need // to expose port's "default" pin type too. pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args); + + mp_local_free(pin_args); } else #endif From ab750ee2fb6329eb7b0b06ff44a0d0d152ba32b7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Nov 2017 23:39:25 +1100 Subject: [PATCH 113/828] extmod/modure: Convert alloca() to use new scoped allocation API. --- extmod/modure.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extmod/modure.c b/extmod/modure.c index 78de4706d..d0180881d 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -144,7 +144,7 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) { } mp_obj_t retval = mp_obj_new_list(0, NULL); - const char **caps = alloca(caps_num * sizeof(char*)); + const char **caps = mp_local_alloc(caps_num * sizeof(char*)); while (true) { // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char memset((char**)caps, 0, caps_num * sizeof(char*)); @@ -165,6 +165,7 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) { break; } } + mp_local_free(caps); mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, subj.end - subj.begin); mp_obj_list_append(retval, s); From 357486d9b455a8ac0c7066a997ebd4ee8e22b38f Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Nov 2017 23:40:30 +1100 Subject: [PATCH 114/828] unix: Add support for using the Python stack. --- ports/unix/main.c | 5 +++++ ports/unix/mpthreadport.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/ports/unix/main.c b/ports/unix/main.c index 25f3e04fb..03f2f357e 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -440,6 +440,11 @@ MP_NOINLINE int main_(int argc, char **argv) { gc_init(heap, heap + heap_size); #endif + #if MICROPY_ENABLE_PYSTACK + static mp_obj_t pystack[1024]; + mp_pystack_init(pystack, &pystack[MP_ARRAY_SIZE(pystack)]); + #endif + mp_init(); char *home = getenv("HOME"); diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 8c636a445..baca0a2b1 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -66,6 +66,10 @@ STATIC void mp_thread_gc(int signo, siginfo_t *info, void *context) { // that we don't need the extra information, enough is captured by the // gc_collect_regs_and_stack function above //gc_collect_root((void**)context, sizeof(ucontext_t) / sizeof(uintptr_t)); + #if MICROPY_ENABLE_PYSTACK + void **ptrs = (void**)(void*)MP_STATE_THREAD(pystack_start); + gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void*)); + #endif thread_signal_done = 1; } } From 971699abe7c9ac07c3059cbbc452043e35eb6200 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Nov 2017 23:40:42 +1100 Subject: [PATCH 115/828] stm32: Add support for using the Python stack. --- ports/stm32/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 16279d073..9a83f9f36 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -511,6 +511,11 @@ soft_reset: // GC init gc_init(&_heap_start, &_heap_end); + #if MICROPY_ENABLE_PYSTACK + static mp_obj_t pystack[384]; + mp_pystack_init(pystack, &pystack[384]); + #endif + // MicroPython init mp_init(); mp_obj_list_init(mp_sys_path, 0); From 30fd8484ebe41faad467fbc8dd4a6f72250f203c Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 26 Nov 2017 23:48:23 +1100 Subject: [PATCH 116/828] py/runtime: Use the Python stack when building *arg and **kwarg state. With MICROPY_ENABLE_PYSTACK enabled the following language constructs no longer allocate on the heap: f(*arg), f(**kwarg). --- py/runtime.c | 12 ++++++------ py/vm.c | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 8df0c0a08..41f2f6976 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -669,7 +669,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // allocate memory for the new array of args args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len); - args2 = m_new(mp_obj_t, args2_alloc); + args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); // copy the self if (self != MP_OBJ_NULL) { @@ -690,7 +690,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // allocate memory for the new array of args args2_alloc = 1 + n_args + len + 2 * (n_kw + kw_dict_len); - args2 = m_new(mp_obj_t, args2_alloc); + args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); // copy the self if (self != MP_OBJ_NULL) { @@ -706,7 +706,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ // allocate memory for the new array of args args2_alloc = 1 + n_args + 2 * (n_kw + kw_dict_len) + 3; - args2 = m_new(mp_obj_t, args2_alloc); + args2 = mp_nonlocal_alloc(args2_alloc * sizeof(mp_obj_t)); // copy the self if (self != MP_OBJ_NULL) { @@ -723,7 +723,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ mp_obj_t item; while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { if (args2_len >= args2_alloc) { - args2 = m_renew(mp_obj_t, args2, args2_alloc, args2_alloc * 2); + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), args2_alloc * 2 * sizeof(mp_obj_t)); args2_alloc *= 2; } args2[args2_len++] = item; @@ -774,7 +774,7 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ if (new_alloc < 4) { new_alloc = 4; } - args2 = m_renew(mp_obj_t, args2, args2_alloc, new_alloc); + args2 = mp_nonlocal_realloc(args2, args2_alloc * sizeof(mp_obj_t), new_alloc * sizeof(mp_obj_t)); args2_alloc = new_alloc; } @@ -806,7 +806,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ob mp_call_prepare_args_n_kw_var(have_self, n_args_n_kw, args, &out_args); mp_obj_t res = mp_call_function_n_kw(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args); - m_del(mp_obj_t, out_args.args, out_args.n_alloc); + mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); return res; } diff --git a/py/vm.c b/py/vm.c index a8a73f323..b18a2b5e3 100644 --- a/py/vm.c +++ b/py/vm.c @@ -966,7 +966,11 @@ unwind_jump:; mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args); - m_del(mp_obj_t, out_args.args, out_args.n_alloc); + #if !MICROPY_ENABLE_PYSTACK + // Freeing args at this point does not follow a LIFO order so only do it if + // pystack is not enabled. For pystack, they are freed when code_state is. + mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); + #endif if (new_state) { new_state->prev = code_state; code_state = new_state; @@ -1043,7 +1047,11 @@ unwind_jump:; mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args); - m_del(mp_obj_t, out_args.args, out_args.n_alloc); + #if !MICROPY_ENABLE_PYSTACK + // Freeing args at this point does not follow a LIFO order so only do it if + // pystack is not enabled. For pystack, they are freed when code_state is. + mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); + #endif if (new_state) { new_state->prev = code_state; code_state = new_state; @@ -1110,6 +1118,8 @@ unwind_return: mp_globals_set(code_state->old_globals); mp_code_state_t *new_code_state = code_state->prev; #if MICROPY_ENABLE_PYSTACK + // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var + // (The latter is implicitly freed when using pystack due to its LIFO nature.) // The sizeof in the following statement does not include the size of the variable // part of the struct. This arg is anyway not used if pystack is enabled. mp_nonlocal_free(code_state, sizeof(mp_code_state_t)); @@ -1458,6 +1468,8 @@ unwind_loop: mp_globals_set(code_state->old_globals); mp_code_state_t *new_code_state = code_state->prev; #if MICROPY_ENABLE_PYSTACK + // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var + // (The latter is implicitly freed when using pystack due to its LIFO nature.) // The sizeof in the following statement does not include the size of the variable // part of the struct. This arg is anyway not used if pystack is enabled. mp_nonlocal_free(code_state, sizeof(mp_code_state_t)); From e02cb9ec318580497dbb17f369a61d9e932094e5 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 11:56:20 +0200 Subject: [PATCH 117/828] tests/heapalloc_*: Refactor some tests to work in strict stackless mode. In strict stackless mode, it's not possible to make a function call with heap locked (because function activation record aka frame is allocated on heap). So, if the only purpose of function is to introduce local variable scope, move heap lock/unlock calls inside the function. --- tests/micropython/heapalloc_exc_raise.py | 4 ++-- tests/micropython/heapalloc_iter.py | 4 ++-- tests/micropython/heapalloc_traceback.py | 4 ++-- tests/micropython/heapalloc_traceback.py.exp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/micropython/heapalloc_exc_raise.py b/tests/micropython/heapalloc_exc_raise.py index d60e14ce5..fb63a84bf 100644 --- a/tests/micropython/heapalloc_exc_raise.py +++ b/tests/micropython/heapalloc_exc_raise.py @@ -5,6 +5,7 @@ import micropython e = ValueError("error") def func(): + micropython.heap_lock() try: # This works as is because traceback is not allocated # if not possible (heap is locked, no memory). If heap @@ -16,8 +17,7 @@ def func(): raise e except Exception as e2: print(e2) + micropython.heap_unlock() -micropython.heap_lock() func() print("ok") -micropython.heap_unlock() diff --git a/tests/micropython/heapalloc_iter.py b/tests/micropython/heapalloc_iter.py index 79461f999..30ac82e14 100644 --- a/tests/micropython/heapalloc_iter.py +++ b/tests/micropython/heapalloc_iter.py @@ -11,8 +11,10 @@ except (ImportError, AttributeError): heap_lock = heap_unlock = lambda:0 def do_iter(l): + heap_lock() for i in l: print(i) + heap_unlock() def gen_func(): yield 1 @@ -55,7 +57,6 @@ print(sum(t)) heap_unlock() # test iterating over collections with the heap locked -heap_lock() do_iter(b'123') do_iter(ba) do_iter(ar) @@ -66,4 +67,3 @@ do_iter(s) do_iter(fs) do_iter(g1) do_iter(g2) -heap_unlock() diff --git a/tests/micropython/heapalloc_traceback.py b/tests/micropython/heapalloc_traceback.py index f4212b6ce..813dea4b2 100644 --- a/tests/micropython/heapalloc_traceback.py +++ b/tests/micropython/heapalloc_traceback.py @@ -16,17 +16,17 @@ except: pass def test(): + micropython.heap_lock() global global_exc global_exc.__traceback__ = None try: raise global_exc except StopIteration: print('StopIteration') + micropython.heap_unlock() # call test() with heap allocation disabled -micropython.heap_lock() test() -micropython.heap_unlock() # print the exception that was raised buf = uio.StringIO() diff --git a/tests/micropython/heapalloc_traceback.py.exp b/tests/micropython/heapalloc_traceback.py.exp index 291bbd697..facd0af13 100644 --- a/tests/micropython/heapalloc_traceback.py.exp +++ b/tests/micropython/heapalloc_traceback.py.exp @@ -1,5 +1,5 @@ StopIteration Traceback (most recent call last): - File , line 22, in test + File , line 23, in test StopIteration: From 016f83053669ee56561bebd8e9b65dac77b670ac Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 11:58:44 +0200 Subject: [PATCH 118/828] tests/heapalloc, heapalloc_super: Skip in strict stackless mode. These tests involves testing allocation-free function calling, and in strict stackless mode, it's not possible to make a function call with heap locked (because function activation record aka frame is allocated on the heap). --- tests/micropython/heapalloc.py | 9 +++++++++ tests/micropython/heapalloc_super.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/tests/micropython/heapalloc.py b/tests/micropython/heapalloc.py index 62f26df6a..f74bb92c8 100644 --- a/tests/micropython/heapalloc.py +++ b/tests/micropython/heapalloc.py @@ -2,6 +2,15 @@ import micropython +# Check for stackless build, which can't call functions without +# allocating a frame on heap. +try: + def stackless(): pass + micropython.heap_lock(); stackless(); micropython.heap_unlock() +except RuntimeError: + print("SKIP") + raise SystemExit + def f1(a): print(a) diff --git a/tests/micropython/heapalloc_super.py b/tests/micropython/heapalloc_super.py index 1cf5293d2..a8c23393e 100644 --- a/tests/micropython/heapalloc_super.py +++ b/tests/micropython/heapalloc_super.py @@ -1,6 +1,15 @@ # test super() operations which don't require allocation import micropython +# Check for stackless build, which can't call functions without +# allocating a frame on heap. +try: + def stackless(): pass + micropython.heap_lock(); stackless(); micropython.heap_unlock() +except RuntimeError: + print("SKIP") + raise SystemExit + class A: def foo(self): print('A foo') From 9c027073568d9be43bd1aabcac60a6ff208c4299 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Dec 2017 22:38:30 +1100 Subject: [PATCH 119/828] py/objexcept: Use INT_FMT when printing errno value. --- py/objexcept.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/objexcept.c b/py/objexcept.c index b87609a6b..380bc3534 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -118,7 +118,7 @@ STATIC void mp_obj_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_pr if (o->base.type == &mp_type_OSError && MP_OBJ_IS_SMALL_INT(o->args->items[0])) { qstr qst = mp_errno_to_str(o->args->items[0]); if (qst != MP_QSTR_NULL) { - mp_printf(print, "[Errno %d] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst); + mp_printf(print, "[Errno " INT_FMT "] %q", MP_OBJ_SMALL_INT_VALUE(o->args->items[0]), qst); return; } } From 2759bec8587b0c0b7da1fa3a013e4f1b1530ad27 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Dec 2017 22:39:12 +1100 Subject: [PATCH 120/828] py: Extend nan-boxing config to have 47-bit small integers. The nan-boxing representation has an extra 16-bits of space to store small-int values, and making use of it allows to create and manipulate full 32-bit positive integers (ie up to 0xffffffff) without using the heap. --- py/mpconfig.h | 2 +- py/obj.h | 4 ++-- py/parse.c | 18 +++++++++++++++--- py/smallint.h | 6 +++--- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 96cd8c651..19bae4f88 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -79,7 +79,7 @@ // - seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 64-bit fp, e != 0x7ff // - s1111111 11110000 00000000 00000000 00000000 00000000 00000000 00000000 +/- inf // - 01111111 11111000 00000000 00000000 00000000 00000000 00000000 00000000 normalised nan -// - 01111111 11111101 00000000 00000000 iiiiiiii iiiiiiii iiiiiiii iiiiiii1 small int +// - 01111111 11111101 iiiiiiii iiiiiiii iiiiiiii iiiiiiii iiiiiiii iiiiiii1 small int // - 01111111 11111110 00000000 00000000 qqqqqqqq qqqqqqqq qqqqqqqq qqqqqqq1 str // - 01111111 11111100 00000000 00000000 pppppppp pppppppp pppppppp pppppp00 ptr (4 byte alignment) // Stored as O = R + 0x8004000000000000, retrieved as R = O - 0x8004000000000000. diff --git a/py/obj.h b/py/obj.h index 31c3ce95c..10cb48961 100644 --- a/py/obj.h +++ b/py/obj.h @@ -170,8 +170,8 @@ static inline bool MP_OBJ_IS_OBJ(mp_const_obj_t o) static inline bool MP_OBJ_IS_SMALL_INT(mp_const_obj_t o) { return ((((mp_int_t)(o)) & 0xffff000000000000) == 0x0001000000000000); } -#define MP_OBJ_SMALL_INT_VALUE(o) (((intptr_t)(o)) >> 1) -#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)(((uintptr_t)(small_int)) << 1) | 0x0001000000000001) +#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)((o) << 16)) >> 17) +#define MP_OBJ_NEW_SMALL_INT(small_int) (((((uint64_t)(small_int)) & 0x7fffffffffff) << 1) | 0x0001000000000001) static inline bool MP_OBJ_IS_QSTR(mp_const_obj_t o) { return ((((mp_int_t)(o)) & 0xffff000000000000) == 0x0002000000000000); } diff --git a/py/parse.c b/py/parse.c index f7fe30418..b80cc8fb1 100644 --- a/py/parse.c +++ b/py/parse.c @@ -369,6 +369,18 @@ STATIC mp_parse_node_t make_node_const_object(parser_t *parser, size_t src_line, return (mp_parse_node_t)pn; } +STATIC mp_parse_node_t mp_parse_node_new_small_int_checked(parser_t *parser, mp_obj_t o_val) { + (void)parser; + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(o_val); + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + // A parse node is only 32-bits and the small-int value must fit in 31-bits + if (((val ^ (val << 1)) & 0xffffffff80000000) != 0) { + return make_node_const_object(parser, 0, o_val); + } + #endif + return mp_parse_node_new_small_int(val); +} + STATIC void push_result_token(parser_t *parser, const rule_t *rule) { mp_parse_node_t pn; mp_lexer_t *lex = parser->lexer; @@ -380,7 +392,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) { if (rule->rule_id == RULE_atom && (elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP)) != NULL) { if (MP_OBJ_IS_SMALL_INT(elem->value)) { - pn = mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(elem->value)); + pn = mp_parse_node_new_small_int_checked(parser, elem->value); } else { pn = make_node_const_object(parser, lex->tok_line, elem->value); } @@ -394,7 +406,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) { } else if (lex->tok_kind == MP_TOKEN_INTEGER) { mp_obj_t o = mp_parse_num_integer(lex->vstr.buf, lex->vstr.len, 0, lex); if (MP_OBJ_IS_SMALL_INT(o)) { - pn = mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(o)); + pn = mp_parse_node_new_small_int_checked(parser, o); } else { pn = make_node_const_object(parser, lex->tok_line, o); } @@ -686,7 +698,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args pop_result(parser); } if (MP_OBJ_IS_SMALL_INT(arg0)) { - push_result_node(parser, mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(arg0))); + push_result_node(parser, mp_parse_node_new_small_int_checked(parser, arg0)); } else { // TODO reuse memory for parse node struct? push_result_node(parser, make_node_const_object(parser, 0, arg0)); diff --git a/py/smallint.h b/py/smallint.h index 42679a78f..6a3c75236 100644 --- a/py/smallint.h +++ b/py/smallint.h @@ -50,10 +50,10 @@ #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D -#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)0xffffffff80000000) >> 1)) -#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & 0xffffffff80000000) == 0) +#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)0xffff800000000000) >> 1)) +#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & 0xffff800000000000) == 0) // Mask to truncate mp_int_t to positive value -#define MP_SMALL_INT_POSITIVE_MASK ~(0xffffffff80000000 | (0xffffffff80000000 >> 1)) +#define MP_SMALL_INT_POSITIVE_MASK ~(0xffff800000000000 | (0xffff800000000000 >> 1)) #endif From d3f82bc42576ccd80206a17a4941fd5c28c56530 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Dec 2017 22:51:52 +1100 Subject: [PATCH 121/828] py/mpstate.h: Remove obsolete comment about nlr_top being coded in asm. --- py/mpstate.h | 1 - 1 file changed, 1 deletion(-) diff --git a/py/mpstate.h b/py/mpstate.h index d3b53f915..45e89590f 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -215,7 +215,6 @@ typedef struct _mp_state_thread_t { mp_obj_dict_t *dict_locals; mp_obj_dict_t *dict_globals; - // Note: nlr asm code has the offset of this hard-coded nlr_buf_t *nlr_top; // ROOT POINTER // Stack top at the start of program From d32d22dfd79439e07739166eaa3f7e41466c7ec8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 30 Nov 2017 10:31:42 +1100 Subject: [PATCH 122/828] py/objtype: Implement better support for overriding native's __init__. This patch cleans up and generalises part of the code which handles overriding and calling a native base-class's __init__ method. It defers the call to the native make_new() function until after the user (Python) __init__() method has run. That user method now has the chance to call the native __init__/make_new and pass it different arguments. If the user doesn't call the super().__init__ method then it will be called automatically after the user code finishes, to finalise construction of the instance. --- py/objexcept.c | 15 ---------- py/objtype.c | 78 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/py/objexcept.c b/py/objexcept.c index 380bc3534..524f105ce 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -212,27 +212,12 @@ STATIC void exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } } -STATIC mp_obj_t exc___init__(size_t n_args, const mp_obj_t *args) { - mp_obj_exception_t *self = MP_OBJ_TO_PTR(args[0]); - mp_obj_t argst = mp_obj_new_tuple(n_args - 1, args + 1); - self->args = MP_OBJ_TO_PTR(argst); - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(exc___init___obj, 1, MP_OBJ_FUN_ARGS_MAX, exc___init__); - -STATIC const mp_rom_map_elem_t exc_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&exc___init___obj) }, -}; - -STATIC MP_DEFINE_CONST_DICT(exc_locals_dict, exc_locals_dict_table); - const mp_obj_type_t mp_type_BaseException = { { &mp_type_type }, .name = MP_QSTR_BaseException, .print = mp_obj_exception_print, .make_new = mp_obj_exception_make_new, .attr = exception_attr, - .locals_dict = (mp_obj_dict_t*)&exc_locals_dict, }; #define MP_DEFINE_EXCEPTION(exc_name, base_name) \ diff --git a/py/objtype.c b/py/objtype.c index 267cae815..22c8f01bc 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -46,14 +46,6 @@ STATIC mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_ /******************************************************************************/ // instance object -STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs) { - mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs); - o->base.type = class; - mp_map_init(&o->members, 0); - mp_seq_clear(o->subobj, 0, subobjs, sizeof(*o->subobj)); - return MP_OBJ_FROM_PTR(o); -} - STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_type_t **last_native_base) { int count = 0; for (;;) { @@ -87,6 +79,30 @@ STATIC int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t } } +// This wrapper function is allows a subclass of a native type to call the +// __init__() method (corresponding to type->make_new) of the native type. +STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) { + mp_obj_instance_t *self = MP_OBJ_TO_PTR(args[0]); + const mp_obj_type_t *native_base = NULL; + instance_count_native_bases(self->base.type, &native_base); + self->subobj[0] = native_base->make_new(native_base, n_args - 1, 0, args + 1); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper); + +STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs) { + mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs); + o->base.type = class; + mp_map_init(&o->members, 0); + // Initialise the native base-class slot (should be 1 at most) with a valid + // object. It doesn't matter which object, so long as it can be uniquely + // distinguished from a native class that is initialised. + if (subobjs != 0) { + o->subobj[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj); + } + return MP_OBJ_FROM_PTR(o); +} + // TODO // This implements depth-first left-to-right MRO, which is not compliant with Python3 MRO // http://python-history.blogspot.com/2010/06/method-resolution-order.html @@ -280,7 +296,13 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size if (init_fn[0] == MP_OBJ_SENTINEL) { // Native type's constructor is what wins - it gets all our arguments, // and none Python classes are initialized at all. - o->subobj[0] = native_base->make_new(native_base, n_args, n_kw, args); + + // Since type->make_new() implements both __new__() and __init__() in + // one go, of which the latter may be overridden by the Python subclass, + // we defer (see the end of this function) the call of the native + // constructor to give a chance for the Python __init__() method to call + // said native constructor. + } else if (init_fn[0] != MP_OBJ_NULL) { // now call Python class __new__ function with all args if (n_args == 0 && n_kw == 0) { @@ -305,6 +327,8 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size o = MP_OBJ_TO_PTR(new_ret); // now call Python class __init__ function with all args + // This method has a chance to call super().__init__() to construct a + // possible native base class. init_fn[0] = init_fn[1] = MP_OBJ_NULL; lookup.obj = o; lookup.attr = MP_QSTR___init__; @@ -333,6 +357,12 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size } + // If the type had a native base that was not explicitly initialised + // (constructed) by the Python __init__() method then construct it now. + if (native_base != NULL && o->subobj[0] == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) { + o->subobj[0] = native_base->make_new(native_base, n_args, n_kw, args); + } + return MP_OBJ_FROM_PTR(o); } @@ -1107,6 +1137,11 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { .is_type = false, }; + // Allow a call super().__init__() to reach any native base classes + if (attr == MP_QSTR___init__) { + lookup.meth_offset = offsetof(mp_obj_type_t, make_new); + } + if (type->parent == NULL) { // no parents, do nothing #if MICROPY_MULTIPLE_INHERITANCE @@ -1116,19 +1151,34 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { const mp_obj_t *items = parent_tuple->items; for (size_t i = 0; i < len; i++) { assert(MP_OBJ_IS_TYPE(items[i], &mp_type_type)); + if (MP_OBJ_TO_PTR(items[i]) == &mp_type_object) { + // The "object" type will be searched at the end of this function, + // and we don't want to lookup native methods in object. + continue; + } mp_obj_class_lookup(&lookup, (mp_obj_type_t*)MP_OBJ_TO_PTR(items[i])); if (dest[0] != MP_OBJ_NULL) { - return; + break; } } #endif - } else { + } else if (type->parent != &mp_type_object) { mp_obj_class_lookup(&lookup, type->parent); - if (dest[0] != MP_OBJ_NULL) { - return; - } } + if (dest[0] != MP_OBJ_NULL) { + if (dest[0] == MP_OBJ_SENTINEL) { + // Looked up native __init__ so defer to it + dest[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj); + dest[1] = self->obj; + } + return; + } + + // Reset meth_offset so we don't look up any native methods in object, + // because object never takes up the native base-class slot. + lookup.meth_offset = 0; + mp_obj_class_lookup(&lookup, &mp_type_object); } From fd0b0db8738efcee4b0b4d5c337aa2ad9c1ae3b0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 30 Nov 2017 10:32:35 +1100 Subject: [PATCH 123/828] tests/basics: Add test for overriding a native base-class's init method. --- tests/basics/subclass_native_init.py | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/basics/subclass_native_init.py diff --git a/tests/basics/subclass_native_init.py b/tests/basics/subclass_native_init.py new file mode 100644 index 000000000..38d2f23ac --- /dev/null +++ b/tests/basics/subclass_native_init.py @@ -0,0 +1,44 @@ +# test subclassing a native type and overriding __init__ + +# overriding list.__init__() +class L(list): + def __init__(self, a, b): + super().__init__([a, b]) +print(L(2, 3)) + +# inherits implicitly from object +class A: + def __init__(self): + print("A.__init__") + super().__init__() +A() + +# inherits explicitly from object +class B(object): + def __init__(self): + print("B.__init__") + super().__init__() +B() + +# multiple inheritance with object explicitly mentioned +class C: + pass +class D(C, object): + def __init__(self): + print('D.__init__') + super().__init__() + def reinit(self): + print('D.foo') + super().__init__() +a = D() +a.__init__() +a.reinit() + +# call __init__() after object is already init'd +class L(list): + def reinit(self): + super().__init__(range(2)) +a = L(range(5)) +print(a) +a.reinit() +print(a) From 3c28df16586157b6b80e7437559c36f05a309e24 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2017 14:00:16 +1100 Subject: [PATCH 124/828] tests/extmod: Add test which subclasses framebuf.FrameBuffer. --- tests/extmod/framebuf_subclass.py | 20 ++++++++++++++++++++ tests/extmod/framebuf_subclass.py.exp | 1 + 2 files changed, 21 insertions(+) create mode 100644 tests/extmod/framebuf_subclass.py create mode 100644 tests/extmod/framebuf_subclass.py.exp diff --git a/tests/extmod/framebuf_subclass.py b/tests/extmod/framebuf_subclass.py new file mode 100644 index 000000000..6363c224f --- /dev/null +++ b/tests/extmod/framebuf_subclass.py @@ -0,0 +1,20 @@ +# test subclassing framebuf.FrameBuffer + +try: + import framebuf +except ImportError: + print('SKIP') + raise SystemExit + +class FB(framebuf.FrameBuffer): + def __init__(self, n): + self.n = n + super().__init__(bytearray(2 * n * n), n, n, framebuf.RGB565) + + def foo(self): + self.hline(0, 2, self.n, 0x0304) + +fb = FB(n=3) +fb.pixel(0, 0, 0x0102) +fb.foo() +print(bytes(fb)) diff --git a/tests/extmod/framebuf_subclass.py.exp b/tests/extmod/framebuf_subclass.py.exp new file mode 100644 index 000000000..23d53ccc6 --- /dev/null +++ b/tests/extmod/framebuf_subclass.py.exp @@ -0,0 +1 @@ +b'\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x03\x04\x03\x04\x03' From c78ef92d787d7bab8acbec69e978037ec2b20d21 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2017 15:22:03 +1100 Subject: [PATCH 125/828] py/objtype: Refactor object's handling of __new__ to not create 2 objs. Before this patch, if a user defined the __new__() function for a class then two instances of that class would be created: once before __new__ is called and once during the __new__ call (assuming the user creates some instance, eg using super().__new__, which is most of the time). The first one was then discarded. This refactor makes it so that a new instance is only created if the user __new__ function doesn't exist. --- py/objobject.c | 9 +++++--- py/objtype.c | 57 +++++++++++++++++++++++--------------------------- py/objtype.h | 5 +++++ 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/py/objobject.c b/py/objobject.c index 49d2ec62e..265fcfbf2 100644 --- a/py/objobject.c +++ b/py/objobject.c @@ -52,9 +52,12 @@ STATIC mp_obj_t object___new__(mp_obj_t cls) { if (!MP_OBJ_IS_TYPE(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) { mp_raise_TypeError("__new__ arg must be a user-type"); } - mp_obj_t o = MP_OBJ_SENTINEL; - mp_obj_t res = mp_obj_instance_make_new(MP_OBJ_TO_PTR(cls), 1, 0, &o); - return res; + // This executes only "__new__" part of instance creation. + // TODO: This won't work well for classes with native bases. + // TODO: This is a hack, should be resolved along the lines of + // https://github.com/micropython/micropython/issues/606#issuecomment-43685883 + const mp_obj_type_t *native_base; + return MP_OBJ_FROM_PTR(mp_obj_new_instance(MP_OBJ_TO_PTR(cls), &native_base)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__); STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj)); diff --git a/py/objtype.c b/py/objtype.c index 22c8f01bc..33757287a 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -90,17 +90,22 @@ STATIC mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(native_base_init_wrapper_obj, 1, MP_OBJ_FUN_ARGS_MAX, native_base_init_wrapper); -STATIC mp_obj_t mp_obj_new_instance(const mp_obj_type_t *class, size_t subobjs) { - mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, subobjs); +#if !MICROPY_CPYTHON_COMPAT +STATIC +#endif +mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *class, const mp_obj_type_t **native_base) { + size_t num_native_bases = instance_count_native_bases(class, native_base); + assert(num_native_bases < 2); + mp_obj_instance_t *o = m_new_obj_var(mp_obj_instance_t, mp_obj_t, num_native_bases); o->base.type = class; mp_map_init(&o->members, 0); // Initialise the native base-class slot (should be 1 at most) with a valid // object. It doesn't matter which object, so long as it can be uniquely // distinguished from a native class that is initialised. - if (subobjs != 0) { + if (num_native_bases != 0) { o->subobj[0] = MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj); } - return MP_OBJ_FROM_PTR(o); + return o; } // TODO @@ -267,20 +272,6 @@ STATIC void instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size_t n_kw, const mp_obj_t *args) { assert(mp_obj_is_instance_type(self)); - const mp_obj_type_t *native_base = NULL; - size_t num_native_bases = instance_count_native_bases(self, &native_base); - assert(num_native_bases < 2); - - mp_obj_instance_t *o = MP_OBJ_TO_PTR(mp_obj_new_instance(self, num_native_bases)); - - // This executes only "__new__" part of instance creation. - // TODO: This won't work well for classes with native bases. - // TODO: This is a hack, should be resolved along the lines of - // https://github.com/micropython/micropython/issues/606#issuecomment-43685883 - if (n_args == 1 && *args == MP_OBJ_SENTINEL) { - return MP_OBJ_FROM_PTR(o); - } - // look for __new__ function mp_obj_t init_fn[2] = {MP_OBJ_NULL}; struct class_lookup_data lookup = { @@ -292,10 +283,12 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size }; mp_obj_class_lookup(&lookup, self); - mp_obj_t new_ret = MP_OBJ_FROM_PTR(o); - if (init_fn[0] == MP_OBJ_SENTINEL) { - // Native type's constructor is what wins - it gets all our arguments, - // and none Python classes are initialized at all. + const mp_obj_type_t *native_base = NULL; + mp_obj_instance_t *o; + if (init_fn[0] == MP_OBJ_NULL || init_fn[0] == MP_OBJ_SENTINEL) { + // Either there is no __new__() method defined or there is a native + // constructor. In both cases create a blank instance. + o = mp_obj_new_instance(self, &native_base); // Since type->make_new() implements both __new__() and __init__() in // one go, of which the latter may be overridden by the Python subclass, @@ -303,8 +296,9 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size // constructor to give a chance for the Python __init__() method to call // said native constructor. - } else if (init_fn[0] != MP_OBJ_NULL) { - // now call Python class __new__ function with all args + } else { + // Call Python class __new__ function with all args to create an instance + mp_obj_t new_ret; if (n_args == 0 && n_kw == 0) { mp_obj_t args2[1] = {MP_OBJ_FROM_PTR(self)}; new_ret = mp_call_function_n_kw(init_fn[0], 1, 0, args2); @@ -316,16 +310,17 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size m_del(mp_obj_t, args2, 1 + n_args + 2 * n_kw); } - } + // https://docs.python.org/3.4/reference/datamodel.html#object.__new__ + // "If __new__() does not return an instance of cls, then the new + // instance's __init__() method will not be invoked." + if (mp_obj_get_type(new_ret) != self) { + return new_ret; + } - // https://docs.python.org/3.4/reference/datamodel.html#object.__new__ - // "If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked." - if (mp_obj_get_type(new_ret) != self) { - return new_ret; + // The instance returned by __new__() becomes the new object + o = MP_OBJ_TO_PTR(new_ret); } - o = MP_OBJ_TO_PTR(new_ret); - // now call Python class __init__ function with all args // This method has a chance to call super().__init__() to construct a // possible native base class. diff --git a/py/objtype.h b/py/objtype.h index 52419f3cd..1f4313084 100644 --- a/py/objtype.h +++ b/py/objtype.h @@ -37,6 +37,11 @@ typedef struct _mp_obj_instance_t { // TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them } mp_obj_instance_t; +#if MICROPY_CPYTHON_COMPAT +// this is needed for object.__new__ +mp_obj_instance_t *mp_obj_new_instance(const mp_obj_type_t *cls, const mp_obj_type_t **native_base); +#endif + // this needs to be exposed for MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE to work void mp_obj_instance_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest); From e4e3f0d7270e93e6123dbf05e1f51993e38d970c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 12 Dec 2017 17:13:39 +1100 Subject: [PATCH 126/828] tests/cpydiff: Update subclassing Exception case and give work-around. --- tests/cpydiff/types_exception_subclassinit.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/cpydiff/types_exception_subclassinit.py b/tests/cpydiff/types_exception_subclassinit.py index 177094646..56bab51d1 100644 --- a/tests/cpydiff/types_exception_subclassinit.py +++ b/tests/cpydiff/types_exception_subclassinit.py @@ -1,8 +1,12 @@ """ categories: Types,Exception -description: Exception.__init__ raises TypeError if overridden and called by subclass -cause: Unknown -workaround: Unknown +description: Exception.__init__ method does not exist. +cause: Subclassing native classes is not fully supported in MicroPython. +workaround: Call using ``super()`` instead:: + +class A(Exception): + def __init__(self): + super().__init__() """ class A(Exception): def __init__(self): From da34b6ef452514170e8ce1d1819070a920c2568e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 12 Dec 2017 01:20:11 +0200 Subject: [PATCH 127/828] tests: Fix few test for proper "skipped" detection with qemu-arm's tinytest. "Builtin" tinytest-based testsuite as employed by qemu-arm (and now generalized by me to be reusable for other targets) performs simplified detection of skipped tests, it treats as such tests which raised SystemExit (instead of checking got "SKIP" output). Consequently, each "SKIP" must be accompanied by SystemExit (and conversely, SystemExit should not be used if test is not skipped, which so far seems to be true). --- tests/basics/builtin_compile.py | 1 + tests/basics/class_descriptor.py | 8 ++++---- tests/basics/fun_name.py | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/basics/builtin_compile.py b/tests/basics/builtin_compile.py index 32c6667d8..bf272aca1 100644 --- a/tests/basics/builtin_compile.py +++ b/tests/basics/builtin_compile.py @@ -46,3 +46,4 @@ if have_compile(): test() else: print("SKIP") + raise SystemExit diff --git a/tests/basics/class_descriptor.py b/tests/basics/class_descriptor.py index eb88ba7b9..54f386230 100644 --- a/tests/basics/class_descriptor.py +++ b/tests/basics/class_descriptor.py @@ -27,8 +27,8 @@ except AttributeError: r = m.Forward if 'Descriptor' in repr(r.__class__): print('SKIP') -else: - print(r) - m.Forward = 'a' - del m.Forward + raise SystemExit +print(r) +m.Forward = 'a' +del m.Forward diff --git a/tests/basics/fun_name.py b/tests/basics/fun_name.py index 7a84fc339..a724f4111 100644 --- a/tests/basics/fun_name.py +++ b/tests/basics/fun_name.py @@ -14,3 +14,4 @@ try: print(A().Fun.__name__) except AttributeError: print('SKIP') + raise SystemExit From 54cd6e3e4bb45f5ff649e3d31521f9a78015fb6b Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 13 Dec 2017 00:12:37 +0200 Subject: [PATCH 128/828] docs/packages: Add quick "Creating distribution packages" section. Needs more details. --- docs/reference/packages.rst | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index 28f5f9f48..d8d198e62 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -199,6 +199,32 @@ Few notes: you may want to decrease the amount of frozen modules included. +Creating distribution packages +------------------------------ + +Distribution packages for MicroPython are created in the same manner +as for CPython or any other Python implementation, see references at +the end of chapter. "Source distribution" (sdist) format is used for +packaging. The post-processing discussed above, (and pre-processing +discussed in the following section) is achieved by using custom +"sdist" command for distutils/setuptools. Thus, packaging steps +remain the same as for standard distutils/setuptools, the user just +need to override "sdist" command implementation by passing the +appropriate argument to ``setup()`` call:: + + from setuptools import setup + import sdist_upip + + setup( + ..., + cmdclass={'sdist': sdist_upip.sdist} + ) + +The sdist_upip.py module as referenced above can be found in +`micropython-lib`: +https://github.com/micropython/micropython-lib/blob/master/sdist_upip.py + + Application resources --------------------- From 479392a56e6edd8449e594c0e5c1e8f5176411ab Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Tue, 12 Dec 2017 08:13:48 +0000 Subject: [PATCH 129/828] drivers/display/ssd1306: Make SSD1306 class inherit from FrameBuffer. --- drivers/display/ssd1306.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/display/ssd1306.py b/drivers/display/ssd1306.py index cd358d00e..cebe10e67 100644 --- a/drivers/display/ssd1306.py +++ b/drivers/display/ssd1306.py @@ -23,29 +23,16 @@ SET_PRECHARGE = const(0xd9) SET_VCOM_DESEL = const(0xdb) SET_CHARGE_PUMP = const(0x8d) - -class SSD1306: +# Subclassing FrameBuffer provides support for graphics primitives +# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html +class SSD1306(framebuf.FrameBuffer): def __init__(self, width, height, external_vcc): self.width = width self.height = height self.external_vcc = external_vcc self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) - fb = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MONO_VLSB) - self.framebuf = fb - # Provide methods for accessing FrameBuffer graphics primitives. This is a - # workround because inheritance from a native class is currently unsupported. - # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html - self.fill = fb.fill - self.pixel = fb.pixel - self.hline = fb.hline - self.vline = fb.vline - self.line = fb.line - self.rect = fb.rect - self.fill_rect = fb.fill_rect - self.text = fb.text - self.scroll = fb.scroll - self.blit = fb.blit + super.__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) self.init_display() def init_display(self): From f1c9e7760d91cb141b0f03ee348b3fe36d2cf450 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 6 Mar 2017 16:46:34 +1100 Subject: [PATCH 130/828] py/builtinimport: Call __init__ for modules imported via a weak link. This is a bit of a clumsy way of doing it but solves the issue of __init__ not running when a module is imported via its weak-link name. Ideally a better solution would be found. --- py/builtinimport.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/py/builtinimport.c b/py/builtinimport.c index 2157902c9..97c1789f8 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -389,6 +389,19 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { } // found weak linked module module_obj = el->value; + if (MICROPY_MODULE_BUILTIN_INIT) { + // look for __init__ and call it if it exists + // Note: this code doesn't work fully correctly because it allows the + // __init__ function to be called twice if the module is imported by its + // non-weak-link name. Also, this code is duplicated in objmodule.c. + mp_obj_t dest[2]; + mp_load_method_maybe(el->value, MP_QSTR___init__, dest); + if (dest[0] != MP_OBJ_NULL) { + mp_call_method_n_kw(0, 0, dest); + // register module so __init__ is not called again + mp_module_register(mod_name, el->value); + } + } } else { no_exist: #else From bc08c884a2e7d1d3a4476c224f230b90ee2bf1e3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 6 Dec 2016 12:20:10 +1100 Subject: [PATCH 131/828] esp32: Add new port to Espressif ESP32 SoC. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is a combination of 216 commits from the initial stages of development of this port, up to and including the point where the code was moved to the ports/esp32 directory. These commits were mostly concerned with setting up the build system and getting a reliable port working with basic features. The following is a digest of the original commits in their original order (most recent listed first), grouped where possible per author. The list is here to give credit for the work and provide some level of traceability and accountability. For the full history of development please consult the original repository. All code is MIT licensed and the relevant copyright holders are listed in the comment-header of each file. Damien George esp32: Update to latest ESP IDF. esp32: Update module symlinks now that code lives under the ports dir. esp32: Update to compile with new ports/esp32 directory structure. esp32: Move it to the ports/ directory. esp32/machine_uart: Don't save baudrate but compute it instead. esp32/modsocket: Add socket.readinto() method. esp32/modesp: Add esp.gpio_matrix_in and esp.gpio_matrix_out functions. esp32/machine_uart: Wait for all data to be tx'd before changing config. NyxCode esp32: Add note to README.md about updating the submodules of ESP IDF. Anthony Briggs esp32: Update README.md installation and flashing instructions. Javier Candeira esp32: Raise error when setting input-only pin to output. With help from Adrian Smith (fon@thefon.net) Javier Candeira esp32: Replace exception raising with corresponding mp_raise_XXX funcs. Tisham Dhar esp32: Add some specific notes about building on Windows using WSL. Ben Gamari esp32: Provide machine.Signal class. Damien George esp32/modnetwork: Implement AP version of network.isconnected(). Eric Poulsen esp32/README.md: Add note about btree submodule initialization. Damien George esp32: Make firmware.bin start at 0x1000 to allow flash size autodetect. esp32: Changes to follow latest version of upstream uPy. esp32/Makefile: Separate ESP-specific inc dirs to avoid header clashes. esp32: Enable "btree" database module. esp32: Update to latest ESP IDF. Roosted7 esp32: Update to latest ESP-IDF. Alex King esp32/machine_timer: Add support for esp32 hardware timer. Code lineage: Timer() is based loosely on the version in esp8266, although the implementation is differs significantly because of the change in the underlying platform. Damien George esp32/machine_uart: Increase UART TX buffer size to 64. esp32/modules: Update dht symlink. esp32/mpconfigport.h: Enable utimeq module, needed for uasyncio. esp32: Changes to follow latest version of upstream uPy. esp32: Update to latest ESP-IDF. esp32/machine_uart: Add uart.any() method. esp32/machine_uart: Uninstall the UART driver before installing it. Thomas Roos esp32: Update to latest ESP-IDF. Eric Poulsen esp32/modsocket: Make read/write return None when in non-blocking mode. esp32/modsocket.c: Fix send/sendto/write for non-blocking sockets. Odd Stråbø esp32: Initial working implementation of machine.UART. Code lineage (as seen by previous commits): I copied the ESP8266 code, renamed pyb -> machine, and used esp-idf as a reference while implementing minimal functionality. I provide all of my changes under the MIT license. Odd Stråbø esp32/machine_uart: Rename pyb to machine. esp32: Copy machine_uart.c from esp8266 port. Damien George esp32/moduos: Add uos.ilistdir() function. esp32: Mount filesystem at the root of the VFS, following esp8266. Andy Valencia esp32: Add hardware SHA1/SHA256 support via mbedtls API. Code lineage: a copy of extmod/moduhashlib with the API invocation details edited. Total derivative work. Andy Valencia esp32: Add PWM support via machine.PWM class. Code lineage: I started by copying the esp8266 machine_pwm.c. I used information from the ESP32 Technical Reference Manual, the esp-idf documentation, and the SDK's sample ledc example code (but I did not copy that code, just studied it to understand the SDK's API for PWM). So aside from the code copied from the esp8266 PWM support, everything else you see is just new code I wrote. I wasn't an employee of anybody when I wrote it, and I wrote it with the understanding and intention that it's simply a derivative work of the existing micropython code. I freely and willingly contribute it to the project and intend that it not change the legal status of the micropython code base in any way, even if it is included in that base in whole or part. Damien George esp32/modules: Add symlinks for upysh and upip. Eric Poulsen esp32/modmachine: Add unique_id() function to machine module. Damien George esp32: Change dac_out_voltage to dac_output_voltage for new IDF API. esp32: Update esp32.custom_common.ld to align with changes in ESP IDF. Eric Poulsen esp32: Update to latest ESP IDF. Damien George esp32/modsocket: When resolving IP addr handle the case of host=''. esp32: Update to latest ESP IDF. Eric Poulsen esp32/Makefile: Change default FLASH_MODE to dio for WROOM-32 module. esp32: Move FAT FS to start at 0x200000 and increase size to 2MiB. Damien George esp32: Remove enable_irq/disable_irq and use ATOMIC_SECTION instead. esp32/mpconfigport.h: Provide ATOMIC_SECTION macros. esp32/main: Restart the MCU if there is a failed NLR jump. Daniel Campora esp32: Enable threading; be sure to exit GIL when a thread will block. esp32: Trace the registers when doing a gc collect. Also make it thread ready. esp32: Add threading implementation, disabled for the time being. Damien George esp32: Update to latest ESP IDF. esp32/uart: Use high-level function to install UART0 RX ISR handler. esp32/Makefile: Make FreeRTOS private include dir really private. Eric Poulsen esp32: Add support for hardware SPI peripheral (block 1 and 2). Sergio Conde Gómez esp32/modules/inisetup.py: Mount filesystem at /flash like ESP8266 Damien George esp32: Convert to use core-provided KeyboardInterrupt exception. esp32: Pump the event loop while waiting for rx-chr or delay_ms. esp32: Implement Pin.irq() using "soft" scheduled interrupts. esp32: Update to latest ESP IDF version. Eric Poulsen esp32/README: Add troubleshooting section to the end. tyggerjai esp32: Add support for WS2812 and APA106 RGB LEDs. Damien George esp32: Add makeimg.py script to build full firmware; use it in Makefile. esp32/modsocket: Make socket.read return when socket closes. esp32/modsocket: Initialise the timeout on an accepted socket. esp32/mphalport: Provide proper implementations of disable_/enable_irq. esp32/modmachine: Add disable_irq/enable_irq functions. Nick Moore esp32/modsocket.c: add comment explaining timeout behaviour esp32/modsocket.c: clean up send methods for retries too esp32/modsocket.c: sockets always nonblocking, accept timeout in modsocket esp32: Update to latest ESP IDF version. esp32/modsocket.c: remove MSG_PEEK workaround on select ioctl. esp32/modsocket.c: Initialize tcp when modsocket loads. Damien George esp32/main: Bump heap size from 64k to 96k. esp32/modutime: Add time.time() function. esp32/modsocket: Convert lwip errnos to uPy ones. esp32/modules: Provide symlink to ds18x20 module. esp32: Add support for onewire protocol via OneWire module. esp32: Add support for DHT11 and DHT22 sensors. esp32/mphalport: Improve delay and ticks functions. esp32: Update to latest ESP IDF. esp32/modules: Provide symlink to urequests from micropython-lib. esp32: Populate sys.path. Nick Moore esp32/machine_dac.c: implement DAC pins as well esp32/machine_adc.c: also machine.ADC esp32/machine_touchpad.c: add support for touchpad Damien George esp32/README: Add hint about using GNUmakefile on case-insensitive FS. esp32/mpconfigport.h: Enable maximum speed software SPI. esp32: Provide improved version of mp_hal_delay_us_fast. esp32/mpconfigport.h: Enable MICROPY_PY_BUILTINS_POW3 option. esp32: Update to latest ESP IDF. esp32: Convert to use new oofatfs library and generic VFS sub-system. esp32: Enable help('modules') to list builtin modules. esp32: Convert to use new builtin help function. Aaron Kelly esp32/README: Add comment about ESP-IDF version Damien George esp32: Consistently use size_t instead of mp_uint_t. esp32: Change "Micro Python" to "MicroPython" in license comments. esp32/Makefile: Use -C argument to git instead of cd'ing. esp32/help: Add section to help about using the network module. esp32/README: Add section about configuring and using an ESP32 board. esp32/README: Remove paragraph about buggy toolchain, it's now fixed. esp32/modnetwork: Change network init logging from info to debug. esp32/modnetwork: Don't start AP automatically when init'ing wifi. esp32/modsocket: Implement socket.setsockopt, to support SO_REUSEADDR. esp32: Update to latest ESP IDF. esp32/sdkconfig.h: Remove unused CONFIG_ESPTOOLPY_xxx config settings. esp32/modsocket: Add support for DGRAM and RAW, and sendto/recvfrom. esp32/modsocket: Fix return value of "port" in socket.accept. esp32/modsocket: Make socket.recv take exactly 2 args. esp32: Enable ussl module, using mbedtls component from ESP IDF. esp32/modsocket: Rename "socket" module to "usocket". esp32/sdkconfig: Increase max number of open sockets from 4 to 8. esp32/modsocket: Add error checking for creating and closing sockets. esp32/modsocket: Use _r (re-entrant) versions of LWIP socket API funcs. esp32/modsocket: Raise an exception if socket.connect did not succeed. esp32/modsocket: Make socket.accept return a tuple: (client, addr). esp32/modsocket: Use m_new_obj_with_finaliser instead of calloc. esp32/Makefile: Add check for IDF version, and warn if not supported. esp32/esp32.custom_common.ld: Update to follow changes in IDF. esp32: Update to latest ESP IDF. nubcore esp32: add #define CONFIG_ESP32_WIFI_RX_BUFFER_NUM 25 Nick Moore esp32/modsocket.c: add in sendall and makefile methods #10 esp32/modsocket.c: fixups for #10 esp32/modsocket.c: fix copyright, socket_recv gets param and exception esp32/modnetwork.c: fix copyright, network.active param to bool Damien George esp32/modnetwork: Implement wlan.isconnected() method. esp32/modnetwork: Add initial implementation of wlan.config(). esp32/modnetwork: Simplify event_handler messages. Nick Moore esp32/modsocket.c: support for ioctl, settimeout, setblocking, getaddrinfo Damien George esp32/README: Add comment about FLASH_MODE being dio. esp32: Update to latest ESP IDF. esp32/modnetwork: Remove unnecessary indirection variable for scan list. esp32/modnetwork: Check that STA is active before trying to scan. esp32/mphalport: Replace portTICK_RATE_MS with portTICK_PERIOD_MS. esp32/README: Add comment about using $(HOME) in makefile. esp32/modnetwork: Use memset instead of bzero, the latter is deprecated. esp32/modnetwork: Improve error handling when STA is connecting to AP. esp32/Makefile: Use tab instead of spaces, and use shorter variable. Nick Moore esp32/modsocket.c: AF_*, SOCK_* and IPPROTO_* constants esp32/modsocket.c: socket.settimeout implementation Damien George esp32/Makefile: Update to latest ESP IDF. Nick Moore esp32/modsocket.c: use mp streams for sockets esp32: network.WLAN.ifconfig based on esp8266 version esp32: Fix up exception handling esp32: sketchy modsocket ... revisit this once modnetwork is sorted esp32: First cut at modnetwork, manually rebased from prev. version Damien George esp32/help: Update help text. esp32: Add info about Microbric Pty Ltd being the sponsor of this port. esp32: Add README.md file. esp32/mpconfigport.h: Add weak links to many of the builtin modules. esp32: Enable soft implementation of machine.SPI class. esp32/Makefile: Simplify APP_LD_ARGS by using OBJ variable. esp32/Makefile: Reorganise Makefile and add some comments. esp32/Makefile: Clean up CFLAGS for ESP IDF components. esp32/Makefile: Tidy up names of ESP IDF components, to match dir name. esp32/Makefile: Define and use ESPCOMP variable. esp32: Update to latest ESP IDF. esp32/main: Enable filesystem support. esp32: Use custom ld script to ensure correct code get placed in iram. esp32: Update to latest ESP IDF. esp32/main: Pin the uPy task to core 0. esp32: Update to use latest ESP IDF. esp32: Disable boot-up scripts, spi_flash_erase_sector no longer works. esp32: Add scripts to init and mount filesystem. esp32: Enable frozen bytecode, with scripts stored in "modules/" dir. esp32/modesp: Increase flash_user_start position to 1Mbyte. esp32/Makefile: Add "erase" target for convenient erasure. esp32/sdkconfig: Reorder config settings to put common things together. esp32/sdkconfig: Change to use single core only. esp32: Add esp module. esp32/uart.c: Make sure uart ISR handler is all in iram. esp32/main.c: Use ESP_TASK_PRIO_MIN + 1 for mp_task's priority. esp32/Makefile: Use only bare-minimum flags when compiling .S files. esp32/Makefile: Rename "firmware" to "application". esp32: Update ESP IDF version. esp32/Makefile: Add declarations to build bootloader and partitions. esp32/Makefile: When deploying, write the application last. esp32/Makefile: Use $(INC) variable instead of listing include dirs. esp32/Makefile: Use locally built versions of freertos and newlib libs. esp32: Add low-level uart handler with ISR and ringbuf for stdin. esp32: Add machine.idle() function. esp32: Add machine.I2C class. esp32: Enable machine.time_pulse_us. esp32: Add initial implementation of machine.Pin class. esp32: Prepare main.c for using xTaskCreateStatic. esp32: Clean up mphalport.h. esp32: Add initial uos module. esp32: Clean up mpconfigport.h, enable more features. esp32: Use new reset function. esp32: Update to latest ESP IDF. esp32: Add idf-version target to Makefile, to track IDF commit. esp32: Initial port to ESP32. --- ports/esp32/Makefile | 719 +++++++++++++++++++++++++++ ports/esp32/README.md | 199 ++++++++ ports/esp32/esp32.custom_common.ld | 215 ++++++++ ports/esp32/espneopixel.c | 53 ++ ports/esp32/esponewire.c | 80 +++ ports/esp32/fatfs_port.c | 46 ++ ports/esp32/gccollect.c | 66 +++ ports/esp32/gccollect.h | 42 ++ ports/esp32/help.c | 65 +++ ports/esp32/machine_adc.c | 132 +++++ ports/esp32/machine_dac.c | 97 ++++ ports/esp32/machine_hw_spi.c | 370 ++++++++++++++ ports/esp32/machine_pin.c | 375 ++++++++++++++ ports/esp32/machine_pwm.c | 277 +++++++++++ ports/esp32/machine_timer.c | 192 +++++++ ports/esp32/machine_touchpad.c | 110 ++++ ports/esp32/machine_uart.c | 359 +++++++++++++ ports/esp32/main.c | 131 +++++ ports/esp32/makeimg.py | 25 + ports/esp32/memory.h | 2 + ports/esp32/modesp.c | 129 +++++ ports/esp32/modesp.h | 1 + ports/esp32/modmachine.c | 135 +++++ ports/esp32/modmachine.h | 18 + ports/esp32/modnetwork.c | 561 +++++++++++++++++++++ ports/esp32/modsocket.c | 570 +++++++++++++++++++++ ports/esp32/moduhashlib.c | 142 ++++++ ports/esp32/modules/_boot.py | 12 + ports/esp32/modules/apa106.py | 8 + ports/esp32/modules/dht.py | 1 + ports/esp32/modules/ds18x20.py | 1 + ports/esp32/modules/flashbdev.py | 34 ++ ports/esp32/modules/inisetup.py | 38 ++ ports/esp32/modules/neopixel.py | 33 ++ ports/esp32/modules/onewire.py | 1 + ports/esp32/modules/upip.py | 1 + ports/esp32/modules/upip_utarfile.py | 1 + ports/esp32/modules/upysh.py | 1 + ports/esp32/modules/urequests.py | 1 + ports/esp32/moduos.c | 128 +++++ ports/esp32/modutime.c | 61 +++ ports/esp32/mpconfigport.h | 248 +++++++++ ports/esp32/mphalport.c | 139 ++++++ ports/esp32/mphalport.h | 83 ++++ ports/esp32/mpthreadport.c | 226 +++++++++ ports/esp32/mpthreadport.h | 45 ++ ports/esp32/qstrdefsport.h | 30 ++ ports/esp32/sdkconfig.h | 162 ++++++ ports/esp32/uart.c | 65 +++ ports/esp32/uart.h | 33 ++ 50 files changed, 6463 insertions(+) create mode 100644 ports/esp32/Makefile create mode 100644 ports/esp32/README.md create mode 100644 ports/esp32/esp32.custom_common.ld create mode 100644 ports/esp32/espneopixel.c create mode 100644 ports/esp32/esponewire.c create mode 100644 ports/esp32/fatfs_port.c create mode 100644 ports/esp32/gccollect.c create mode 100644 ports/esp32/gccollect.h create mode 100644 ports/esp32/help.c create mode 100644 ports/esp32/machine_adc.c create mode 100644 ports/esp32/machine_dac.c create mode 100644 ports/esp32/machine_hw_spi.c create mode 100644 ports/esp32/machine_pin.c create mode 100644 ports/esp32/machine_pwm.c create mode 100644 ports/esp32/machine_timer.c create mode 100644 ports/esp32/machine_touchpad.c create mode 100644 ports/esp32/machine_uart.c create mode 100644 ports/esp32/main.c create mode 100644 ports/esp32/makeimg.py create mode 100644 ports/esp32/memory.h create mode 100644 ports/esp32/modesp.c create mode 100644 ports/esp32/modesp.h create mode 100644 ports/esp32/modmachine.c create mode 100644 ports/esp32/modmachine.h create mode 100644 ports/esp32/modnetwork.c create mode 100644 ports/esp32/modsocket.c create mode 100644 ports/esp32/moduhashlib.c create mode 100644 ports/esp32/modules/_boot.py create mode 100644 ports/esp32/modules/apa106.py create mode 120000 ports/esp32/modules/dht.py create mode 120000 ports/esp32/modules/ds18x20.py create mode 100644 ports/esp32/modules/flashbdev.py create mode 100644 ports/esp32/modules/inisetup.py create mode 100644 ports/esp32/modules/neopixel.py create mode 120000 ports/esp32/modules/onewire.py create mode 120000 ports/esp32/modules/upip.py create mode 120000 ports/esp32/modules/upip_utarfile.py create mode 120000 ports/esp32/modules/upysh.py create mode 120000 ports/esp32/modules/urequests.py create mode 100644 ports/esp32/moduos.c create mode 100644 ports/esp32/modutime.c create mode 100644 ports/esp32/mpconfigport.h create mode 100644 ports/esp32/mphalport.c create mode 100644 ports/esp32/mphalport.h create mode 100644 ports/esp32/mpthreadport.c create mode 100644 ports/esp32/mpthreadport.h create mode 100644 ports/esp32/qstrdefsport.h create mode 100644 ports/esp32/sdkconfig.h create mode 100644 ports/esp32/uart.c create mode 100644 ports/esp32/uart.h diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile new file mode 100644 index 000000000..2eae802be --- /dev/null +++ b/ports/esp32/Makefile @@ -0,0 +1,719 @@ +include ../../py/mkenv.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +MICROPY_PY_USSL = 0 +MICROPY_SSL_AXTLS = 0 +MICROPY_FATFS = 1 +MICROPY_PY_BTREE = 1 + +#FROZEN_DIR = scripts +FROZEN_MPY_DIR = modules + +# include py core make definitions +include $(TOP)/py/py.mk + +PORT ?= /dev/ttyUSB0 +BAUD ?= 460800 +FLASH_MODE ?= dio +FLASH_FREQ ?= 40m +FLASH_SIZE ?= 4MB +CROSS_COMPILE ?= xtensa-esp32-elf- + +# paths to ESP IDF and its components +ifeq ($(ESPIDF),) +$(error Please configure the ESPIDF variable) +endif +ESPCOMP = $(ESPIDF)/components +ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py + +# verify the ESP IDF version +ESPIDF_SUPHASH := 9a26296a0e88a4c3ae27e9c848be970946fff87e +ESPIDF_CURHASH := $(shell git -C $(ESPIDF) show -s --pretty=format:'%H') +ifneq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH)) +$(info ** WARNING **) +$(info The git hash of ESP IDF does not match the supported version) +$(info The build may complete and the firmware may work but it is not guaranteed) +$(info ESP IDF path: $(ESPIDF)) +$(info Current git hash: $(ESPIDF_CURHASH)) +$(info Supported git hash: $(ESPIDF_SUPHASH)) +endif + +# pretty format of ESP IDF version, used internally by the IDF +IDF_VER := $(shell git -C $(ESPIDF) describe) + +INC += -I. +INC += -I$(TOP) +INC += -I$(TOP)/lib/mp-readline +INC += -I$(TOP)/lib/netutils +INC += -I$(TOP)/lib/timeutils +INC += -I$(BUILD) + +INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include +INC_ESPCOMP += -I$(ESPCOMP)/driver/include +INC_ESPCOMP += -I$(ESPCOMP)/driver/include/driver +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/port/include +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/nghttp2/lib/includes +INC_ESPCOMP += -I$(ESPCOMP)/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/ethernet/include +INC_ESPCOMP += -I$(ESPCOMP)/expat/include/expat +INC_ESPCOMP += -I$(ESPCOMP)/expat/port/include +INC_ESPCOMP += -I$(ESPCOMP)/heap/include +INC_ESPCOMP += -I$(ESPCOMP)/json/include +INC_ESPCOMP += -I$(ESPCOMP)/json/port/include +INC_ESPCOMP += -I$(ESPCOMP)/log/include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/include +INC_ESPCOMP += -I$(ESPCOMP)/nvs_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/freertos/include +INC_ESPCOMP += -I$(ESPCOMP)/tcpip_adapter/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/lwip +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/lwip/port +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/lwip/posix +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/include +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/port/include +INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/vfs/include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/platform_include +INC_ESPCOMP += -I$(ESPCOMP)/xtensa-debug-module/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/port/include +INC_ESPCOMP += -I$(ESPCOMP)/ethernet/include +INC_ESPCOMP += -I$(ESPCOMP)/app_trace/include + +CFLAGS_BASE = -std=gnu99 -Os -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wall -Werror -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -DMBEDTLS_CONFIG_FILE='"mbedtls/esp_config.h"' -DHAVE_CONFIG_H -DESP_PLATFORM +CFLAGS = $(CFLAGS_BASE) $(INC) $(INC_ESPCOMP) +CFLAGS += -DIDF_VER=\"$(IDF_VER)\" +CFLAGS += $(CFLAGS_MOD) + +# this is what ESPIDF uses for c++ compilation +CXXFLAGS = -std=gnu++11 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wall -Werror -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -DESP_PLATFORM $(INC) $(INC_ESPCOMP) + +LDFLAGS = -nostdlib -Map=$(@:.elf=.map) --cref +LDFLAGS += --gc-sections -static -EL +LDFLAGS += -u call_user_start_cpu0 -u uxTopUsedPriority -u ld_include_panic_highint_hdl +LDFLAGS += -u __cxa_guard_dummy # so that implementation of static guards is taken from cxx_guards.o instead of libstdc++.a +LDFLAGS += -L$(ESPCOMP)/esp32/ld +LDFLAGS += -T $(BUILD)/esp32_out.ld +LDFLAGS += -T ./esp32.custom_common.ld +LDFLAGS += -T esp32.rom.ld +LDFLAGS += -T esp32.rom.spiram_incompatible_fns.ld +LDFLAGS += -T esp32.peripherals.ld + +LIBGCC_FILE_NAME = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) +LIBSTDCXX_FILE_NAME = $(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a) + +# Debugging/Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -g +COPT = -O0 +else +#CFLAGS += -fdata-sections -ffunction-sections +COPT += -Os -DNDEBUG +#LDFLAGS += --gc-sections +endif + +################################################################################ +# List of MicroPython source and object files + +SRC_C = \ + main.c \ + uart.c \ + gccollect.c \ + mphalport.c \ + fatfs_port.c \ + help.c \ + modutime.c \ + moduos.c \ + machine_timer.c \ + machine_pin.c \ + machine_touchpad.c \ + machine_adc.c \ + machine_dac.c \ + machine_pwm.c \ + machine_uart.c \ + modmachine.c \ + modnetwork.c \ + modsocket.c \ + modesp.c \ + moduhashlib.c \ + espneopixel.c \ + machine_hw_spi.c \ + mpthreadport.c \ + $(SRC_MOD) + +EXTMOD_SRC_C = $(addprefix extmod/,\ + modonewire.c \ + ) + +LIB_SRC_C = $(addprefix lib/,\ + libm/math.c \ + libm/fmodf.c \ + libm/roundf.c \ + libm/ef_sqrt.c \ + libm/kf_rem_pio2.c \ + libm/kf_sin.c \ + libm/kf_cos.c \ + libm/kf_tan.c \ + libm/ef_rem_pio2.c \ + libm/sf_sin.c \ + libm/sf_cos.c \ + libm/sf_tan.c \ + libm/sf_frexp.c \ + libm/sf_modf.c \ + libm/sf_ldexp.c \ + libm/asinfacosf.c \ + libm/atanf.c \ + libm/atan2f.c \ + mp-readline/readline.c \ + netutils/netutils.c \ + timeutils/timeutils.c \ + utils/pyexec.c \ + utils/interrupt_char.c \ + utils/sys_stdio_mphal.c \ + ) + +ifeq ($(MICROPY_FATFS), 1) +LIB_SRC_C += \ + lib/oofatfs/ff.c \ + lib/oofatfs/option/unicode.c +endif + +DRIVERS_SRC_C = $(addprefix drivers/,\ + dht/dht.c \ + ) + +OBJ_MP = +OBJ_MP += $(PY_O) +OBJ_MP += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ_MP += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) +OBJ_MP += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) +OBJ_MP += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_C) $(EXTMOD_SRC_C) $(LIB_SRC_C) $(DRIVERS_SRC_C) +# Append any auto-generated sources that are needed by sources listed in SRC_QSTR +SRC_QSTR_AUTO_DEPS += + +################################################################################ +# List of object files from the ESP32 IDF components + +ESPIDF_DRIVER_O = $(addprefix $(ESPCOMP)/driver/,\ + uart.o \ + periph_ctrl.o \ + ledc.o \ + gpio.o \ + timer.o \ + spi_master.o \ + spi_common.o \ + rtc_module.o \ + ) + +$(BUILD)/$(ESPCOMP)/esp32/dport_access.o: CFLAGS += -Wno-array-bounds +ESPIDF_ESP32_O = $(addprefix $(ESPCOMP)/esp32/,\ + panic.o \ + esp_timer.o \ + esp_timer_esp32.o \ + ets_timer_legacy.o \ + event_default_handlers.o \ + fast_crypto_ops.o \ + task_wdt.o \ + cache_err_int.o \ + clk.o \ + core_dump.o \ + cpu_start.o \ + gdbstub.o \ + crosscore_int.o \ + ipc.o \ + int_wdt.o \ + event_loop.o \ + hwcrypto/sha.o \ + hwcrypto/aes.o \ + lib_printf.o \ + freertos_hooks.o \ + system_api.o \ + hw_random.o \ + phy_init.o \ + intr_alloc.o \ + dport_access.o \ + wifi_init.o \ + ) + +ESPIDF_HEAP_O = $(addprefix $(ESPCOMP)/heap/,\ + heap_caps.o \ + heap_caps_init.o \ + multi_heap.o \ + ) + +ESPIDF_SOC_O = $(addprefix $(ESPCOMP)/soc/,\ + esp32/cpu_util.o \ + esp32/rtc_clk.o \ + esp32/rtc_init.o \ + esp32/rtc_pm.o \ + esp32/rtc_sleep.o \ + esp32/rtc_time.o \ + esp32/soc_memory_layout.o \ + ) + +ESPIDF_CXX_O = $(addprefix $(ESPCOMP)/cxx/,\ + cxx_guards.o \ + ) + +ESPIDF_ETHERNET_O = $(addprefix $(ESPCOMP)/ethernet/,\ + emac_dev.o \ + emac_main.o \ + ) + +$(BUILD)/$(ESPCOMP)/expat/%.o: CFLAGS += -Wno-unused-function +ESPIDF_EXPAT_O = $(addprefix $(ESPCOMP)/expat/,\ + library/xmltok_ns.o \ + library/xmltok.o \ + library/xmlparse.o \ + library/xmlrole.o \ + library/xmltok_impl.o \ + port/minicheck.o \ + port/expat_element.o \ + port/chardata.o \ + ) + +ESPIDF_PTHREAD_O = $(addprefix $(ESPCOMP)/pthread/,\ + pthread.o \ + ) + +# Assembler .S files need only basic flags, and in particular should not have +# -Os because that generates subtly different code. +# We also need custom CFLAGS for .c files because FreeRTOS has headers with +# generic names (eg queue.h) which can clash with other files in the port. +CFLAGS_ASM = -I$(ESPCOMP)/esp32/include -I$(ESPCOMP)/soc/esp32/include -I$(ESPCOMP)/freertos/include/freertos -I. +$(BUILD)/$(ESPCOMP)/freertos/portasm.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/xtensa_context.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/xtensa_intr_asm.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/xtensa_vectors.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/%.o: CFLAGS = $(CFLAGS_BASE) -I. $(INC_ESPCOMP) -I$(ESPCOMP)/freertos/include/freertos +ESPIDF_FREERTOS_O = $(addprefix $(ESPCOMP)/freertos/,\ + croutine.o \ + event_groups.o \ + FreeRTOS-openocd.o \ + list.o \ + portasm.o \ + port.o \ + queue.o \ + ringbuf.o \ + tasks.o \ + timers.o \ + xtensa_context.o \ + xtensa_init.o \ + xtensa_intr_asm.o \ + xtensa_intr.o \ + xtensa_overlay_os_hook.o \ + xtensa_vector_defaults.o \ + xtensa_vectors.o \ + ) + +ESPIDF_VFS_O = $(addprefix $(ESPCOMP)/vfs/,\ + vfs_uart.o \ + vfs.o \ + ) + +ESPIDF_JSON_O = $(addprefix $(ESPCOMP)/json/,\ + library/cJSON.o \ + port/cJSON_Utils.o \ + ) + +ESPIDF_LOG_O = $(addprefix $(ESPCOMP)/log/,\ + log.o \ + ) + +ESPIDF_XTENSA_DEBUG_MODULE_O = $(addprefix $(ESPCOMP)/xtensa-debug-module/,\ + eri.o \ + trax.o \ + ) + +ESPIDF_TCPIP_ADAPTER_O = $(addprefix $(ESPCOMP)/tcpip_adapter/,\ + tcpip_adapter_lwip.o \ + ) + +ESPIDF_APP_TRACE_O = $(addprefix $(ESPCOMP)/app_trace/,\ + app_trace.o \ + ) + +ESPIDF_NEWLIB_O = $(addprefix $(ESPCOMP)/newlib/,\ + time.o \ + syscalls.o \ + syscall_table.o \ + reent_init.o \ + locks.o \ + ) + +ESPIDF_NGHTTP_O = $(addprefix $(ESPCOMP)/nghttp/,\ + nghttp2/lib/nghttp2_http.o \ + nghttp2/lib/nghttp2_version.o \ + nghttp2/lib/nghttp2_mem.o \ + nghttp2/lib/nghttp2_hd_huffman.o \ + nghttp2/lib/nghttp2_rcbuf.o \ + nghttp2/lib/nghttp2_callbacks.o \ + nghttp2/lib/nghttp2_session.o \ + nghttp2/lib/nghttp2_stream.o \ + nghttp2/lib/nghttp2_hd.o \ + nghttp2/lib/nghttp2_priority_spec.o \ + nghttp2/lib/nghttp2_buf.o \ + nghttp2/lib/nghttp2_option.o \ + nghttp2/lib/nghttp2_npn.o \ + nghttp2/lib/nghttp2_helper.o \ + nghttp2/lib/nghttp2_frame.o \ + nghttp2/lib/nghttp2_outbound_item.o \ + nghttp2/lib/nghttp2_hd_huffman_data.o \ + nghttp2/lib/nghttp2_pq.o \ + nghttp2/lib/nghttp2_queue.o \ + nghttp2/lib/nghttp2_submit.o \ + nghttp2/lib/nghttp2_map.o \ + port/http_parser.o \ + ) + +ESPIDF_NVS_FLASH_O = $(addprefix $(ESPCOMP)/nvs_flash/,\ + src/nvs_types.o \ + src/nvs_page.o \ + src/nvs_item_hash_list.o \ + src/nvs_pagemanager.o \ + src/nvs_storage.o \ + src/nvs_api.o \ + ) + +ESPIDF_OPENSSL_O = $(addprefix $(ESPCOMP)/openssl/,\ + ) + +ESPIDF_SPI_FLASH_O = $(addprefix $(ESPCOMP)/spi_flash/,\ + flash_mmap.o \ + partition.o \ + spi_flash_rom_patch.o \ + cache_utils.o \ + flash_ops.o \ + ) + +$(BUILD)/$(ESPCOMP)/lwip/%.o: CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable +ESPIDF_LWIP_O = $(addprefix $(ESPCOMP)/lwip/,\ + api/pppapi.o \ + api/netbuf.o \ + api/api_lib.o \ + api/netifapi.o \ + api/tcpip.o \ + api/netdb.o \ + api/err.o \ + api/api_msg.o \ + api/sockets.o \ + apps/sntp/sntp.o \ + apps/dhcpserver.o \ + core/ipv4/ip_frag.o \ + core/ipv4/dhcp.o \ + core/ipv4/ip4_addr.o \ + core/ipv4/igmp.o \ + core/ipv4/ip4.o \ + core/ipv4/autoip.o \ + core/ipv4/icmp.o \ + core/ipv6/ip6_frag.o \ + core/ipv6/dhcp6.o \ + core/ipv6/inet6.o \ + core/ipv6/ip6_addr.o \ + core/ipv6/ip6.o \ + core/ipv6/nd6.o \ + core/ipv6/mld6.o \ + core/ipv6/ethip6.o \ + core/ipv6/icmp6.o \ + core/mem.o \ + core/init.o \ + core/memp.o \ + core/sys.o \ + core/tcp_in.o \ + core/dns.o \ + core/ip.o \ + core/pbuf.o \ + core/raw.o \ + core/tcp.o \ + core/def.o \ + core/netif.o \ + core/stats.o \ + core/timers.o \ + core/inet_chksum.o \ + core/udp.o \ + core/tcp_out.o \ + netif/slipif.o \ + netif/etharp.o \ + netif/ethernet.o \ + netif/lowpan6.o \ + netif/ethernetif.o \ + port/freertos/sys_arch.o \ + port/netif/wlanif.o \ + port/netif/ethernetif.o \ + ) + +ESPIDF_MBEDTLS_O = $(addprefix $(ESPCOMP)/mbedtls/,\ + library/entropy.o \ + library/pkcs12.o \ + library/ccm.o \ + library/pk.o \ + library/sha1.o \ + library/x509_csr.o \ + library/ssl_cli.o \ + library/ecp.o \ + library/blowfish.o \ + library/x509.o \ + library/ecp_curves.o \ + library/error.o \ + library/ssl_ticket.o \ + library/entropy_poll.o \ + library/cipher.o \ + library/version_features.o \ + library/ripemd160.o \ + library/rsa.o \ + library/md.o \ + library/md_wrap.o \ + library/sha256.o \ + library/dhm.o \ + library/ssl_cache.o \ + library/pkwrite.o \ + library/base64.o \ + library/asn1parse.o \ + library/ssl_tls.o \ + library/hmac_drbg.o \ + library/pem.o \ + library/version.o \ + library/gcm.o \ + library/memory_buffer_alloc.o \ + library/md2.o \ + library/ecdsa.o \ + library/ssl_srv.o \ + library/x509_crt.o \ + library/ecdh.o \ + library/asn1write.o \ + library/md4.o \ + library/debug.o \ + library/x509_create.o \ + library/ecjpake.o \ + library/oid.o \ + library/md5.o \ + library/ssl_ciphersuites.o \ + library/sha512.o \ + library/xtea.o \ + library/aes.o \ + library/cipher_wrap.o \ + library/arc4.o \ + library/bignum.o \ + library/pkparse.o \ + library/padlock.o \ + library/threading.o \ + library/x509_crl.o \ + library/pkcs11.o \ + library/aesni.o \ + library/timing.o \ + library/certs.o \ + library/pkcs5.o \ + library/ssl_cookie.o \ + library/camellia.o \ + library/havege.o \ + library/des.o \ + library/x509write_csr.o \ + library/platform.o \ + library/ctr_drbg.o \ + library/x509write_crt.o \ + library/pk_wrap.o \ + port/esp_bignum.o \ + port/esp_hardware.o \ + port/esp_sha1.o \ + port/esp_sha256.o \ + port/esp_sha512.o \ + ) + +$(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing +ESPIDF_WPA_SUPPLICANT_O = $(addprefix $(ESPCOMP)/wpa_supplicant/,\ + src/crypto/aes-internal-enc.o \ + src/crypto/sha256-internal.o \ + src/crypto/md5-internal.o \ + src/crypto/aes-internal.o \ + src/crypto/sha1.o \ + src/crypto/aes-internal-dec.o \ + src/crypto/aes-unwrap.o \ + src/crypto/crypto_internal-rsa.o \ + src/crypto/dh_groups.o \ + src/crypto/crypto_internal.o \ + src/crypto/aes-wrap.o \ + src/crypto/sha1-internal.o \ + src/crypto/dh_group5.o \ + src/crypto/sha256.o \ + src/crypto/rc4.o \ + src/crypto/md5.o \ + src/crypto/aes-cbc.o \ + src/crypto/sha1-pbkdf2.o \ + src/crypto/bignum.o \ + src/crypto/crypto_internal-modexp.o \ + src/crypto/crypto_internal-cipher.o \ + src/fast_crypto/fast_aes-unwrap.o \ + src/fast_crypto/fast_aes-wrap.o \ + src/fast_crypto/fast_sha256.o \ + src/fast_crypto/fast_sha256-internal.o \ + port/os_xtensa.o \ + ) + +OBJ_ESPIDF = +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NEWLIB_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_DRIVER_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_ESP32_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_HEAP_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_SOC_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_CXX_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_ETHERNET_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_EXPAT_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_PTHREAD_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_FREERTOS_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_VFS_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_JSON_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_LOG_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_LWIP_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_MBEDTLS_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_XTENSA_DEBUG_MODULE_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_TCPIP_ADAPTER_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_APP_TRACE_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NGHTTP_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NVS_FLASH_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_OPENSSL_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_SPI_FLASH_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_WPA_SUPPLICANT_O)) +################################################################################ +# Main targets + +all: $(BUILD)/firmware.bin + +.PHONY: idf-version deploy erase + +idf-version: + $(ECHO) "ESP IDF supported hash: $(ESPIDF_SUPHASH)" + +$(BUILD)/firmware.bin: $(BUILD)/bootloader.bin $(BUILD)/partitions.bin $(BUILD)/application.bin + $(ECHO) "Create $@" + $(Q)$(PYTHON) makeimg.py $^ $@ + +deploy: $(BUILD)/firmware.bin + $(ECHO) "Writing $^ to the board" + $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) write_flash -z --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) 0x1000 $^ + +erase: + $(ECHO) "Erasing flash" + $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) erase_flash + +################################################################################ +# Declarations to build the application + +OBJ = $(OBJ_MP) $(OBJ_ESPIDF) + +APP_LD_ARGS = +APP_LD_ARGS += $(LDFLAGS_MOD) +APP_LD_ARGS += --start-group +APP_LD_ARGS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc +APP_LD_ARGS += -L$(dir $(LIBSTDCXX_FILE_NAME)) -lstdc++ +APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libc.a +APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libm.a +APP_LD_ARGS += $(ESPCOMP)/esp32/libhal.a +APP_LD_ARGS += -L$(ESPCOMP)/esp32/lib -lcore -lnet80211 -lphy -lrtc -lpp -lwpa -lsmartconfig -lcoexist +APP_LD_ARGS += $(OBJ) +APP_LD_ARGS += --end-group + +$(BUILD)/esp32_out.ld: sdkconfig.h + $(Q)$(CC) -I. -C -P -x c -E $(ESPCOMP)/esp32/ld/esp32.ld -o $@ + +$(BUILD)/application.bin: $(BUILD)/application.elf + $(ECHO) "Create $@" + $(Q)$(ESPTOOL) --chip esp32 elf2image --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) $< + +$(BUILD)/application.elf: $(OBJ) $(BUILD)/esp32_out.ld + $(ECHO) "LINK $@" + $(Q)$(LD) $(LDFLAGS) -o $@ $(APP_LD_ARGS) + $(Q)$(SIZE) $@ + +define compile_cxx +$(ECHO) "CXX $<" +$(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< +@# The following fixes the dependency file. +@# See http://make.paulandlesley.org/autodep.html for details. +@# Regex adjusted from the above to play better with Windows paths, etc. +@$(CP) $(@:.o=.d) $(@:.o=.P); \ + $(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \ + $(RM) -f $(@:.o=.d) +endef + +vpath %.cpp . $(TOP) +$(BUILD)/%.o: %.cpp + $(call compile_cxx) + +################################################################################ +# Declarations to build the bootloader + +$(BUILD)/bootloader/$(ESPCOMP)/%.o: CFLAGS += -DBOOTLOADER_BUILD=1 -I$(ESPCOMP)/bootloader_support/include_priv -I$(ESPCOMP)/bootloader_support/include -I$(ESPCOMP)/micro-ecc/micro-ecc -I$(ESPCOMP)/esp32 -Wno-error=format +BOOTLOADER_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ + bootloader_support/src/bootloader_flash.o \ + bootloader_support/src/bootloader_random.o \ + bootloader_support/src/bootloader_sha.o \ + bootloader_support/src/secure_boot_signatures.o \ + bootloader_support/src/secure_boot.o \ + bootloader_support/src/esp_image_format.o \ + bootloader_support/src/flash_encrypt.o \ + bootloader_support/src/flash_partitions.o \ + log/log.o \ + spi_flash/spi_flash_rom_patch.o \ + soc/esp32/rtc_clk.o \ + soc/esp32/rtc_time.o \ + micro-ecc/micro-ecc/uECC.o \ + bootloader/subproject/main/bootloader_start.o \ + ) + +BOOTLOADER_LIBS = +BOOTLOADER_LIBS += -Wl,--start-group +BOOTLOADER_LIBS += $(BOOTLOADER_OBJ) +BOOTLOADER_LIBS += -L$(ESPCOMP)/esp32/lib -lrtc +BOOTLOADER_LIBS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc +BOOTLOADER_LIBS += -Wl,--end-group + +BOOTLOADER_LDFLAGS = +BOOTLOADER_LDFLAGS += -nostdlib +BOOTLOADER_LDFLAGS += -L$(ESPIDF)/lib +BOOTLOADER_LDFLAGS += -L$(ESPIDF)/ld +BOOTLOADER_LDFLAGS += -u call_user_start_cpu0 +BOOTLOADER_LDFLAGS += -Wl,--gc-sections +BOOTLOADER_LDFLAGS += -static +BOOTLOADER_LDFLAGS += -Wl,-EL +BOOTLOADER_LDFLAGS += -Wl,-Map=$(@:.elf=.map) -Wl,--cref +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/bootloader/subproject/main/esp32.bootloader.ld +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/bootloader/subproject/main/esp32.bootloader.rom.ld +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp32/ld/esp32.rom.ld +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp32/ld/esp32.rom.spiram_incompatible_fns.ld + +BOOTLOADER_OBJ_DIRS = $(sort $(dir $(BOOTLOADER_OBJ))) +$(BOOTLOADER_OBJ): | $(BOOTLOADER_OBJ_DIRS) +$(BOOTLOADER_OBJ_DIRS): + $(MKDIR) -p $@ + +$(BUILD)/bootloader/%.o: %.c + $(call compile_c) + +$(BUILD)/bootloader.bin: $(BUILD)/bootloader.elf + $(ECHO) "Create $@" + $(Q)$(ESPTOOL) --chip esp32 elf2image --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) $< + +$(BUILD)/bootloader.elf: $(BOOTLOADER_OBJ) + $(ECHO) "LINK $@" + $(Q)$(CC) $(BOOTLOADER_LDFLAGS) -o $@ $(BOOTLOADER_LIBS) + +################################################################################ +# Declarations to build the partitions + +PYTHON2 ?= python2 +PART_SRC = $(ESPCOMP)/partition_table/partitions_singleapp.csv + +$(BUILD)/partitions.bin: $(PART_SRC) + $(ECHO) "Create $@" + $(Q)$(PYTHON2) $(ESPCOMP)/partition_table/gen_esp32part.py -q $< $@ + +################################################################################ + +include $(TOP)/py/mkrules.mk diff --git a/ports/esp32/README.md b/ports/esp32/README.md new file mode 100644 index 000000000..7591f040a --- /dev/null +++ b/ports/esp32/README.md @@ -0,0 +1,199 @@ +MicroPython port to the ESP32 +============================= + +This is an experimental port of MicroPython to the Espressif ESP32 +microcontroller. It uses the ESP-IDF framework and MicroPython runs as +a task under FreeRTOS. + +Supported features include: +- REPL (Python prompt) over UART0. +- 16k stack for the MicroPython task and 64k Python heap. +- Many of MicroPython's features are enabled: unicode, arbitrary-precision + integers, single-precision floats, complex numbers, frozen bytecode, as + well as many of the internal modules. +- Internal filesystem using the flash (currently 256k in size). +- The machine module with basic GPIO and bit-banging I2C, SPI support. + +Development of this ESP32 port was sponsored in part by Microbric Pty Ltd. + +Setting up the toolchain and ESP-IDF +------------------------------------ + +There are two main components that are needed to build the firmware: +- the Xtensa cross-compiler that targets the CPU in the ESP32 (this is + different to the compiler used by the ESP8266) +- the Espressif IDF (IoT development framework, aka SDK) + +Instructions for setting up both of these components are provided by the +ESP-IDF itself, which is found at https://github.com/espressif/esp-idf . +Follow the guide "Setting Up ESP-IDF", for Windows, Mac or Linux. You +only need to perform up to "Step 2" of the guide, by which stage you +should have installed the cross-compile and cloned the ESP-IDF repository. + +If you are on a Windows machine then the +[Windows Subsystem for Linux](https://msdn.microsoft.com/en-au/commandline/wsl/install_guide) +is the most efficient way to install the ESP32 toolchain and build the project. +If you use WSL then follow the +[Linux guidelines](http://esp-idf.readthedocs.io/en/latest/get-started/linux-setup.html) +for the ESP-IDF instead of the Windows ones. + +Be advised that the ESP-IDF is still undergoing changes and only some +versions are supported. To find which build is compatible refer to the line +in the makefile containing the following: +``` +ESPIDF_SUPHASH := +``` +After finishing "Step 2" you can roll back your current build of +the ESP-IDF (and update the submodules accordingly) using: +``` +$ git checkout +$ git submodule update --recursive +``` +Note that you will get a warning when building the code if the ESP-IDF +version is incorrect. + +The Espressif ESP-IDF instructions above only install pyserial for Python 2, +so if you're running Python 3 or a non-system Python you'll also need to +install `pyserial` (or `esptool`) so that the Makefile can flash the board +and set parameters: +```bash +$ pip install pyserial +``` + +Once everything is set up you should have a functioning toolchain with +prefix xtensa-esp32-elf- (or otherwise if you configured it differently) +as well as a copy of the ESP-IDF repository. + +You then need to set the `ESPIDF` environment/makefile variable to point to +the root of the ESP-IDF repository. You can set the variable in your PATH, +or at the command line when calling make, or in your own custom `makefile`. +The last option is recommended as it allows you to easily configure other +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 = +#PORT = /dev/ttyUSB0 +#FLASH_MODE = qio +#FLASH_SIZE = 4MB +#CROSS_COMPILE = xtensa-esp32-elf- + +include Makefile +``` +Be sure to enter the correct path to your local copy of the IDF repository +(and use `$(HOME)`, not tilde, to reference your home directory). +If your filesystem is case-insensitive then you'll need to use `GNUmakefile` +instead of `makefile`. +If the Xtensa cross-compiler is not in your path you can use the +`CROSS_COMPILE` variable to set its location. Other options of interest +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. + +Building the firmware +--------------------- + +The MicroPython cross-compiler must be built to pre-compile some of the +built-in scripts to bytecode. This can be done by (from the root of +this repository): +```bash +$ make -C mpy-cross +``` + +The ESP32 port has a dependency on Berkeley DB, which is an external +dependency (git submodule). You'll need to have git initialize that +module using the commands: +```bash +$ git submodule init lib/berkeley-db-1.xx +$ git submodule update +``` + +Then to build MicroPython for the ESP32 run: +```bash +$ cd ports/esp32 +$ make +``` +This will produce binary firmware images in the `build/` subdirectory +(three of them: bootloader.bin, partitions.bin and application.bin). + +To flash the firmware you must have your ESP32 module in the bootloader +mode and connected to a serial port on your PC. Refer to the documentation +for your particular ESP32 module for how to do this. The serial port and +flash settings are set in the `Makefile`, and can be overridden in your +local `makefile`; see above for more details. + +You will also need to have user permissions to access the /dev/ttyUSB0 device. +On Linux, you can enable this by adding your user to the `dialout` group, +and rebooting or logging out and in again. +```bash +$ sudo adduser dialout +``` + +If you are installing MicroPython to your module for the first time, or +after installing any other firmware, you should first erase the flash +completely: +```bash +$ make erase +``` + +To flash the MicroPython firmware to your ESP32 use: +```bash +$ make deploy +``` +This will use the `esptool.py` script (provided by ESP-IDF) to download the +binary images. + +Getting a Python prompt +----------------------- + +You can get a prompt via the serial port, via UART0, which is the same UART +that is used for programming the firmware. The baudrate for the REPL is +115200 and you can use a command such as: +```bash +$ picocom -b 115200 /dev/ttyUSB0 +``` + +Configuring the WiFi and using the board +---------------------------------------- + +The ESP32 port is designed to be (almost) equivalent to the ESP8266 in +terms of the modules and user-facing API. There are some small differences, +notably that the ESP32 does not automatically connect to the last access +point when booting up. But for the most part the documentation and tutorials +for the ESP8266 should apply to the ESP32 (at least for the components that +are implemented). + +See http://docs.micropython.org/en/latest/esp8266/esp8266/quickref.html for +a quick reference, and http://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html +for a tutorial. + +The following function can be used to connect to a WiFi access point (you can +either pass in your own SSID and password, or change the defaults so you can +quickly call `wlan_connect()` and it just works): +```python +def wlan_connect(ssid='MYSSID', password='MYPASS'): + import network + wlan = network.WLAN(network.STA_IF) + if not wlan.active() or not wlan.isconnected(): + wlan.active(True) + print('connecting to:', ssid) + wlan.connect(ssid, password) + while not wlan.isconnected(): + pass + print('network config:', wlan.ifconfig()) +``` + +Note that some boards require you to configure the WiFi antenna before using +the WiFi. On Pycom boards like the LoPy and WiPy 2.0 you need to execute the +following code to select the internal antenna (best to put this line in your +boot.py file): +```python +import machine +antenna = machine.Pin(16, machine.Pin.OUT, value=0) +``` + +Troubleshooting +--------------- + +* Continuous reboots after programming: Ensure FLASH_MODE is correct for your + board (e.g. ESP-WROOM-32 should be DIO). Then perform a `make clean`, rebuild, + redeploy. diff --git a/ports/esp32/esp32.custom_common.ld b/ports/esp32/esp32.custom_common.ld new file mode 100644 index 000000000..45242f9af --- /dev/null +++ b/ports/esp32/esp32.custom_common.ld @@ -0,0 +1,215 @@ +/* Default entry point: */ +ENTRY(call_start_cpu0); + +SECTIONS +{ + /* RTC fast memory holds RTC wake stub code, + including from any source file named rtc_wake_stub*.c + */ + .rtc.text : + { + . = ALIGN(4); + *(.rtc.literal .rtc.text) + *rtc_wake_stub*.o(.literal .text .literal.* .text.*) + } >rtc_iram_seg + + /* RTC slow memory holds RTC wake stub + data/rodata, including from any source file + named rtc_wake_stub*.c + */ + .rtc.data : + { + _rtc_data_start = ABSOLUTE(.); + *(.rtc.data) + *(.rtc.rodata) + *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* RTC bss, from any source file named rtc_wake_stub*.c */ + .rtc.bss (NOLOAD) : + { + _rtc_bss_start = ABSOLUTE(.); + *rtc_wake_stub*.o(.bss .bss.*) + *rtc_wake_stub*.o(COMMON) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* Send .iram0 code to iram */ + .iram0.vectors : + { + /* Vectors go to IRAM */ + _init_start = ABSOLUTE(.); + /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */ + . = 0x0; + KEEP(*(.WindowVectors.text)); + . = 0x180; + KEEP(*(.Level2InterruptVector.text)); + . = 0x1c0; + KEEP(*(.Level3InterruptVector.text)); + . = 0x200; + KEEP(*(.Level4InterruptVector.text)); + . = 0x240; + KEEP(*(.Level5InterruptVector.text)); + . = 0x280; + KEEP(*(.DebugExceptionVector.text)); + . = 0x2c0; + KEEP(*(.NMIExceptionVector.text)); + . = 0x300; + KEEP(*(.KernelExceptionVector.text)); + . = 0x340; + KEEP(*(.UserExceptionVector.text)); + . = 0x3C0; + KEEP(*(.DoubleExceptionVector.text)); + . = 0x400; + *(.*Vector.literal) + + *(.UserEnter.literal); + *(.UserEnter.text); + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + _init_end = ABSOLUTE(.); + + /* This goes here, not at top of linker script, so addr2line finds it last, + and uses it in preference to the first symbol in IRAM */ + _iram_start = ABSOLUTE(0); + } > iram0_0_seg + + .iram0.text : + { + /* Code marked as runnning out of IRAM */ + _iram_text_start = ABSOLUTE(.); + *(.iram1 .iram1.*) + *freertos/*(.literal .text .literal.* .text.*) + *heap/multi_heap.o(.literal .text .literal.* .text.*) + *heap/multi_heap_poisoning.o(.literal .text .literal.* .text.*) + *esp32/panic.o(.literal .text .literal.* .text.*) + *esp32/core_dump.o(.literal .text .literal.* .text.*) + *app_trace/*(.literal .text .literal.* .text.*) + *xtensa-debug-module/eri.o(.literal .text .literal.* .text.*) + *esp32/app_trace.o(.literal .text .literal.* .text.*) + *libphy.a:(.literal .text .literal.* .text.*) + *librtc.a:(.literal .text .literal.* .text.*) + *libsoc.a:(.literal .text .literal.* .text.*) + *libhal.a:(.literal .text .literal.* .text.*) + *libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*) + *spi_flash/spi_flash_rom_patch.o(.literal .text .literal.* .text.*) + *py/scheduler.o*(.literal .text .literal.* .text.*) + _iram_text_end = ABSOLUTE(.); + } > iram0_0_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + *(.dram1 .dram1.*) + *esp32/panic.o(.rodata .rodata.*) + *libphy.a:(.rodata .rodata.*) + *app_trace/app_trace.o:(.rodata .rodata.*) + *heap/multi_heap.o(.rodata .rodata.*) + *heap/multi_heap_poisoning.o(.rodata .rodata.*) + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } >dram0_0_seg + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); + } >dram0_0_seg + + .flash.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table .gcc_except_table.*) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + . = (. + 3) & ~ 3; + __eh_frame = ABSOLUTE(.); + KEEP(*(.eh_frame)) + . = (. + 7) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + } >drom0_0_seg + + .flash.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + + /* Similar to _iram_start, this symbol goes here so it is + resolved by addr2line in preference to the first symbol in + the flash.text segment. + */ + _flash_cache_start = ABSOLUTE(0); + } >iram0_2_seg +} diff --git a/ports/esp32/espneopixel.c b/ports/esp32/espneopixel.c new file mode 100644 index 000000000..829c8b1c8 --- /dev/null +++ b/ports/esp32/espneopixel.c @@ -0,0 +1,53 @@ +// Original version from https://github.com/adafruit/Adafruit_NeoPixel +// Modifications by dpgeorge to support auto-CPU-frequency detection + +// This is a mash-up of the Due show() code + insights from Michael Miller's +// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus +// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution. + +#include "py/mpconfig.h" +#include "py/mphal.h" +#include "modesp.h" + +void IRAM_ATTR esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t timing) { + uint8_t *p, *end, pix, mask; + uint32_t t, time0, time1, period, c, startTime, pinMask; + + pinMask = 1 << pin; + p = pixels; + end = p + numBytes; + pix = *p++; + mask = 0x80; + startTime = 0; + + uint32_t fcpu = ets_get_cpu_frequency() * 1000000; + + if (timing == 1) { + // 800 KHz + time0 = (fcpu * 0.35) / 1000000; // 0.35us + time1 = (fcpu * 0.8) / 1000000; // 0.8us + period = (fcpu * 1.25) / 1000000; // 1.25us per bit + } else { + // 400 KHz + time0 = (fcpu * 0.5) / 1000000; // 0.35us + time1 = (fcpu * 1.2) / 1000000; // 0.8us + period = (fcpu * 2.5) / 1000000; // 1.25us per bit + } + + uint32_t irq_state = mp_hal_quiet_timing_enter(); + for (t = time0;; t = time0) { + if (pix & mask) t = time1; // Bit high duration + while (((c = mp_hal_ticks_cpu()) - startTime) < period); // Wait for bit start + GPIO_REG_WRITE(GPIO_OUT_W1TS_REG, pinMask); // Set high + startTime = c; // Save start time + while (((c = mp_hal_ticks_cpu()) - startTime) < t); // Wait high duration + GPIO_REG_WRITE(GPIO_OUT_W1TC_REG, pinMask); // Set low + if (!(mask >>= 1)) { // Next bit/byte + if(p >= end) break; + pix = *p++; + mask = 0x80; + } + } + while ((mp_hal_ticks_cpu() - startTime) < period); // Wait for last bit + mp_hal_quiet_timing_exit(irq_state); +} diff --git a/ports/esp32/esponewire.c b/ports/esp32/esponewire.c new file mode 100644 index 000000000..781616cbe --- /dev/null +++ b/ports/esp32/esponewire.c @@ -0,0 +1,80 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2017 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/mphal.h" +#include "esp8266/esponewire.h" + +#define TIMING_RESET1 (0) +#define TIMING_RESET2 (1) +#define TIMING_RESET3 (2) +#define TIMING_READ1 (3) +#define TIMING_READ2 (4) +#define TIMING_READ3 (5) +#define TIMING_WRITE1 (6) +#define TIMING_WRITE2 (7) +#define TIMING_WRITE3 (8) + +uint16_t esp_onewire_timings[9] = {480, 40, 420, 5, 5, 40, 10, 50, 10}; + +#define DELAY_US mp_hal_delay_us_fast + +int esp_onewire_reset(mp_hal_pin_obj_t pin) { + mp_hal_pin_write(pin, 0); + DELAY_US(esp_onewire_timings[TIMING_RESET1]); + uint32_t i = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_hal_pin_write(pin, 1); + DELAY_US(esp_onewire_timings[TIMING_RESET2]); + int status = !mp_hal_pin_read(pin); + MICROPY_END_ATOMIC_SECTION(i); + DELAY_US(esp_onewire_timings[TIMING_RESET3]); + return status; +} + +int esp_onewire_readbit(mp_hal_pin_obj_t pin) { + mp_hal_pin_write(pin, 1); + uint32_t i = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_hal_pin_write(pin, 0); + DELAY_US(esp_onewire_timings[TIMING_READ1]); + mp_hal_pin_write(pin, 1); + DELAY_US(esp_onewire_timings[TIMING_READ2]); + int value = mp_hal_pin_read(pin); + MICROPY_END_ATOMIC_SECTION(i); + DELAY_US(esp_onewire_timings[TIMING_READ3]); + return value; +} + +void esp_onewire_writebit(mp_hal_pin_obj_t pin, int value) { + uint32_t i = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_hal_pin_write(pin, 0); + DELAY_US(esp_onewire_timings[TIMING_WRITE1]); + if (value) { + mp_hal_pin_write(pin, 1); + } + DELAY_US(esp_onewire_timings[TIMING_WRITE2]); + mp_hal_pin_write(pin, 1); + DELAY_US(esp_onewire_timings[TIMING_WRITE3]); + MICROPY_END_ATOMIC_SECTION(i); +} diff --git a/ports/esp32/fatfs_port.c b/ports/esp32/fatfs_port.c new file mode 100644 index 000000000..b3690a01f --- /dev/null +++ b/ports/esp32/fatfs_port.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014, 2016 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/obj.h" +#include "lib/oofatfs/ff.h" +#include "timeutils.h" +//#include "modmachine.h" + +DWORD get_fattime(void) { + + // TODO: Optimize division (there's no HW division support on ESP8266, + // so it's expensive). + //uint32_t secs = (uint32_t)(pyb_rtc_get_us_since_2000() / 1000000); + uint32_t secs = 0; + + timeutils_struct_time_t tm; + timeutils_seconds_since_2000_to_struct_time(secs, &tm); + + return (((DWORD)(tm.tm_year - 1980) << 25) | ((DWORD)tm.tm_mon << 21) | ((DWORD)tm.tm_mday << 16) | + ((DWORD)tm.tm_hour << 11) | ((DWORD)tm.tm_min << 5) | ((DWORD)tm.tm_sec >> 1)); +} diff --git a/ports/esp32/gccollect.c b/ports/esp32/gccollect.c new file mode 100644 index 000000000..9843cef00 --- /dev/null +++ b/ports/esp32/gccollect.c @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * Copyright (c) 2017 Pycom Limited + * + * 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/mpconfig.h" +#include "py/mpstate.h" +#include "py/gc.h" +#include "py/mpthread.h" +#include "gccollect.h" +#include "soc/cpu.h" +#include "xtensa/hal.h" + + +static void gc_collect_inner(int level) { + if (level < XCHAL_NUM_AREGS / 8) { + gc_collect_inner(level + 1); + if (level != 0) { + return; + } + } + + if (level == XCHAL_NUM_AREGS / 8) { + // get the sp + volatile uint32_t sp = (uint32_t)get_sp(); + gc_collect_root((void**)sp, ((mp_uint_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + return; + } + + // trace root pointers from any threads + #if MICROPY_PY_THREAD + mp_thread_gc_others(); + #endif +} + +void gc_collect(void) { + gc_collect_start(); + gc_collect_inner(0); + gc_collect_end(); +} diff --git a/ports/esp32/gccollect.h b/ports/esp32/gccollect.h new file mode 100644 index 000000000..fe02cc62b --- /dev/null +++ b/ports/esp32/gccollect.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 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. + */ + +extern uint32_t _text_start; +extern uint32_t _text_end; +extern uint32_t _irom0_text_start; +extern uint32_t _irom0_text_end; +extern uint32_t _data_start; +extern uint32_t _data_end; +extern uint32_t _rodata_start; +extern uint32_t _rodata_end; +extern uint32_t _bss_start; +extern uint32_t _bss_end; +extern uint32_t _heap_start; +extern uint32_t _heap_end; + +void gc_collect(void); diff --git a/ports/esp32/help.c b/ports/esp32/help.c new file mode 100644 index 000000000..95d115c56 --- /dev/null +++ b/ports/esp32/help.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 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/builtin.h" + +const char esp32_help_text[] = +"Welcome to MicroPython on the ESP32!\n" +"\n" +"For generic online docs please visit http://docs.micropython.org/\n" +"\n" +"For access to the hardware use the 'machine' module:\n" +"\n" +"import machine\n" +"pin12 = machine.Pin(12, machine.Pin.OUT)\n" +"pin12.value(1)\n" +"pin13 = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)\n" +"print(pin13.value())\n" +"i2c = machine.I2C(scl=machine.Pin(21), sda=machine.Pin(22))\n" +"i2c.scan()\n" +"i2c.writeto(addr, b'1234')\n" +"i2c.readfrom(addr, 4)\n" +"\n" +"Basic WiFi configuration:\n" +"\n" +"import network\n" +"sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" +"sta_if.scan() # Scan for available access points\n" +"sta_if.connect(\"\", \"\") # Connect to an AP\n" +"sta_if.isconnected() # Check for successful connection\n" +"\n" +"Control commands:\n" +" CTRL-A -- on a blank line, enter raw REPL mode\n" +" CTRL-B -- on a blank line, enter normal REPL mode\n" +" CTRL-C -- interrupt a running program\n" +" CTRL-D -- on a blank line, do a soft reset of the board\n" +" CTRL-E -- on a blank line, enter paste mode\n" +"\n" +"For further help on a specific object, type help(obj)\n" +"For a list of available modules, type help('modules')\n" +; diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c new file mode 100644 index 000000000..d62f362e9 --- /dev/null +++ b/ports/esp32/machine_adc.c @@ -0,0 +1,132 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Nick Moore + * + * 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 "esp_log.h" + +#include "driver/gpio.h" +#include "driver/adc.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" + +typedef struct _madc_obj_t { + mp_obj_base_t base; + gpio_num_t gpio_id; + adc1_channel_t adc1_id; +} madc_obj_t; + +STATIC const madc_obj_t madc_obj[] = { + {{&machine_adc_type}, GPIO_NUM_36, ADC1_CHANNEL_0}, + {{&machine_adc_type}, GPIO_NUM_37, ADC1_CHANNEL_1}, + {{&machine_adc_type}, GPIO_NUM_38, ADC1_CHANNEL_2}, + {{&machine_adc_type}, GPIO_NUM_39, ADC1_CHANNEL_3}, + {{&machine_adc_type}, GPIO_NUM_32, ADC1_CHANNEL_4}, + {{&machine_adc_type}, GPIO_NUM_33, ADC1_CHANNEL_5}, + {{&machine_adc_type}, GPIO_NUM_34, ADC1_CHANNEL_6}, + {{&machine_adc_type}, GPIO_NUM_35, ADC1_CHANNEL_7}, +}; + +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); + initialized = 1; + } + + mp_arg_check_num(n_args, n_kw, 1, 1, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + const madc_obj_t *self = NULL; + for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { + if (pin_id == madc_obj[i].gpio_id) { self = &madc_obj[i]; break; } + } + if (!self) mp_raise_ValueError("invalid Pin for ADC"); + esp_err_t err = adc1_config_channel_atten(self->adc1_id, ADC_ATTEN_0db); + if (err == ESP_OK) return MP_OBJ_FROM_PTR(self); + mp_raise_ValueError("Parameter Error"); +} + +STATIC void madc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + madc_obj_t *self = self_in; + mp_printf(print, "ADC(Pin(%u))", self->gpio_id); +} + +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); + if (val == -1) mp_raise_ValueError("Parameter Error"); + return MP_OBJ_NEW_SMALL_INT(val); +} +MP_DEFINE_CONST_FUN_OBJ_1(madc_read_obj, madc_read); + +STATIC mp_obj_t madc_atten(mp_obj_t self_in, mp_obj_t atten_in) { + madc_obj_t *self = self_in; + adc_atten_t atten = mp_obj_get_int(atten_in); + esp_err_t err = adc1_config_channel_atten(self->adc1_id, atten); + if (err == ESP_OK) return mp_const_none; + mp_raise_ValueError("Parameter Error"); +} +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"); +} +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), 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) }, + + { MP_ROM_QSTR(MP_QSTR_ATTN_0DB), MP_ROM_INT(ADC_ATTEN_0db) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_2_5DB), MP_ROM_INT(ADC_ATTEN_2_5db) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_6DB), MP_ROM_INT(ADC_ATTEN_6db) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_11DB), MP_ROM_INT(ADC_ATTEN_11db) }, + + { MP_ROM_QSTR(MP_QSTR_WIDTH_9BIT), MP_ROM_INT(ADC_WIDTH_9Bit) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_10BIT), MP_ROM_INT(ADC_WIDTH_10Bit) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_11BIT), MP_ROM_INT(ADC_WIDTH_11Bit) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_12BIT), MP_ROM_INT(ADC_WIDTH_12Bit) }, +}; + +STATIC MP_DEFINE_CONST_DICT(madc_locals_dict, madc_locals_dict_table); + +const mp_obj_type_t machine_adc_type = { + { &mp_type_type }, + .name = MP_QSTR_ADC, + .print = madc_print, + .make_new = madc_make_new, + .locals_dict = (mp_obj_t)&madc_locals_dict, +}; diff --git a/ports/esp32/machine_dac.c b/ports/esp32/machine_dac.c new file mode 100644 index 000000000..bd0804ec4 --- /dev/null +++ b/ports/esp32/machine_dac.c @@ -0,0 +1,97 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Nick Moore + * + * 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 "esp_log.h" + +#include "driver/gpio.h" +#include "driver/dac.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" + +typedef struct _mdac_obj_t { + mp_obj_base_t base; + gpio_num_t gpio_id; + dac_channel_t dac_id; +} mdac_obj_t; + +STATIC const mdac_obj_t mdac_obj[] = { + {{&machine_dac_type}, GPIO_NUM_25, DAC_CHANNEL_1}, + {{&machine_dac_type}, GPIO_NUM_26, DAC_CHANNEL_2}, +}; + +STATIC mp_obj_t mdac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, + const mp_obj_t *args) { + + mp_arg_check_num(n_args, n_kw, 1, 1, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + const mdac_obj_t *self = NULL; + for (int i = 0; i < MP_ARRAY_SIZE(mdac_obj); i++) { + if (pin_id == mdac_obj[i].gpio_id) { self = &mdac_obj[i]; break; } + } + if (!self) mp_raise_ValueError("invalid Pin for DAC"); + + esp_err_t err = dac_output_enable(self->dac_id); + if (err == ESP_OK) { + err = dac_output_voltage(self->dac_id, 0); + } + if (err == ESP_OK) return MP_OBJ_FROM_PTR(self); + mp_raise_ValueError("Parameter Error"); +} + +STATIC void mdac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mdac_obj_t *self = self_in; + mp_printf(print, "DAC(Pin(%u))", self->gpio_id); +} + +STATIC mp_obj_t mdac_write(mp_obj_t self_in, mp_obj_t value_in) { + mdac_obj_t *self = self_in; + int value = mp_obj_get_int(value_in); + if (value < 0 || value > 255) mp_raise_ValueError("Value out of range"); + + esp_err_t err = dac_output_voltage(self->dac_id, value); + if (err == ESP_OK) return mp_const_none; + mp_raise_ValueError("Parameter Error"); +} +MP_DEFINE_CONST_FUN_OBJ_2(mdac_write_obj, mdac_write); + +STATIC const mp_rom_map_elem_t mdac_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mdac_write_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mdac_locals_dict, mdac_locals_dict_table); + +const mp_obj_type_t machine_dac_type = { + { &mp_type_type }, + .name = MP_QSTR_DAC, + .print = mdac_print, + .make_new = mdac_make_new, + .locals_dict = (mp_obj_t)&mdac_locals_dict, +}; diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c new file mode 100644 index 000000000..437b620f5 --- /dev/null +++ b/ports/esp32/machine_hw_spi.c @@ -0,0 +1,370 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 "Eric Poulsen" + * + * 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 +#include + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mphal.h" +#include "extmod/machine_spi.h" +#include "modmachine.h" + +#include "driver/spi_master.h" + +typedef struct _machine_hw_spi_obj_t { + mp_obj_base_t base; + spi_host_device_t host; + uint32_t baudrate; + uint8_t polarity; + uint8_t phase; + uint8_t bits; + uint8_t firstbit; + int8_t sck; + int8_t mosi; + int8_t miso; + spi_device_handle_t spi; + enum { + MACHINE_HW_SPI_STATE_NONE, + MACHINE_HW_SPI_STATE_INIT, + MACHINE_HW_SPI_STATE_DEINIT + } state; +} machine_hw_spi_obj_t; + +STATIC void machine_hw_spi_deinit_internal(machine_hw_spi_obj_t *self) { + switch (spi_bus_remove_device(self->spi)) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + return; + + case ESP_ERR_INVALID_STATE: + mp_raise_msg(&mp_type_OSError, "SPI device already freed"); + return; + } + + switch (spi_bus_free(self->host)) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + return; + + case ESP_ERR_INVALID_STATE: + mp_raise_msg(&mp_type_OSError, "SPI bus already freed"); + return; + } + + int8_t pins[3] = {self->miso, self->mosi, self->sck}; + + for (int i = 0; i < 3; i++) { + if (pins[i] != -1) { + gpio_pad_select_gpio(pins[i]); + gpio_matrix_out(pins[i], SIG_GPIO_OUT_IDX, false, false); + gpio_set_direction(pins[i], GPIO_MODE_INPUT); + } + } +} + +STATIC void machine_hw_spi_init_internal( + machine_hw_spi_obj_t *self, + int8_t host, + int32_t baudrate, + int8_t polarity, + int8_t phase, + int8_t bits, + int8_t firstbit, + int8_t sck, + int8_t mosi, + int8_t miso) { + + // if we're not initialized, then we're + // implicitly 'changed', since this is the init routine + bool changed = self->state != MACHINE_HW_SPI_STATE_INIT; + + esp_err_t ret; + + machine_hw_spi_obj_t old_self = *self; + + if (host != -1 && host != self->host) { + self->host = host; + changed = true; + } + + if (baudrate != -1 && baudrate != self->baudrate) { + self->baudrate = baudrate; + changed = true; + } + + if (polarity != -1 && polarity != self->polarity) { + self->polarity = polarity; + changed = true; + } + + if (phase != -1 && phase != self->phase) { + self->phase = phase; + changed = true; + } + + if (bits != -1 && bits != self->bits) { + self->bits = bits; + changed = true; + } + + if (firstbit != -1 && firstbit != self->firstbit) { + self->firstbit = firstbit; + changed = true; + } + + if (sck != -2 && sck != self->sck) { + self->sck = sck; + changed = true; + } + + if (mosi != -2 && mosi != self->mosi) { + self->mosi = mosi; + changed = true; + } + + if (miso != -2 && miso != self->miso) { + self->miso = miso; + changed = true; + } + + if (self->host != HSPI_HOST && self->host != VSPI_HOST) { + mp_raise_ValueError("SPI ID must be either HSPI(1) or VSPI(2)"); + } + + if (changed) { + if (self->state == MACHINE_HW_SPI_STATE_INIT) { + self->state = MACHINE_HW_SPI_STATE_DEINIT; + machine_hw_spi_deinit_internal(&old_self); + } + } else { + return; // no changes + } + + spi_bus_config_t buscfg = { + .miso_io_num = self->miso, + .mosi_io_num = self->mosi, + .sclk_io_num = self->sck, + .quadwp_io_num = -1, + .quadhd_io_num = -1 + }; + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = self->baudrate, + .mode = self->phase | (self->polarity << 1), + .spics_io_num = -1, // No CS pin + .queue_size = 1, + .flags = self->firstbit == MICROPY_PY_MACHINE_SPI_LSB ? SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST : 0, + .pre_cb = NULL + }; + + //Initialize the SPI bus + // FIXME: Does the DMA matter? There are two + + ret = spi_bus_initialize(self->host, &buscfg, 1); + switch (ret) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + return; + + case ESP_ERR_INVALID_STATE: + mp_raise_msg(&mp_type_OSError, "SPI device already in use"); + return; + } + + ret = spi_bus_add_device(self->host, &devcfg, &self->spi); + switch (ret) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + spi_bus_free(self->host); + return; + + case ESP_ERR_NO_MEM: + mp_raise_msg(&mp_type_OSError, "out of memory"); + spi_bus_free(self->host); + return; + + case ESP_ERR_NOT_FOUND: + mp_raise_msg(&mp_type_OSError, "no free slots"); + spi_bus_free(self->host); + return; + } + self->state = MACHINE_HW_SPI_STATE_INIT; +} + +STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) { + machine_hw_spi_obj_t *self = (machine_hw_spi_obj_t *) self_in; + if (self->state == MACHINE_HW_SPI_STATE_INIT) { + self->state = MACHINE_HW_SPI_STATE_DEINIT; + machine_hw_spi_deinit_internal(self); + } +} + +STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int bits_to_send = len * self->bits; + bool shortMsg = len <= 4; + + if (self->state == MACHINE_HW_SPI_STATE_DEINIT) { + mp_raise_msg(&mp_type_OSError, "transfer on deinitialized SPI"); + return; + } + + struct spi_transaction_t transaction = { + .flags = 0, + .length = bits_to_send, + .tx_buffer = NULL, + .rx_buffer = NULL, + }; + + if (shortMsg) { + if (src != NULL) { + memcpy(&transaction.tx_data, src, len); + } + transaction.flags |= (SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA); + } else { + transaction.tx_buffer = src; + transaction.rx_buffer = dest; + } + + spi_device_transmit(self->spi, &transaction); + + if (shortMsg && dest != NULL) { + memcpy(dest, &transaction.rx_data, len); + } +} + +/******************************************************************************/ +// MicroPython bindings for hw_spi + +STATIC void machine_hw_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "SPI(id=%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, firstbit=%u, sck=%d, mosi=%d, miso=%d)", + self->host, self->baudrate, self->polarity, + self->phase, self->bits, self->firstbit, + self->sck, self->mosi, self->miso); +} + +STATIC void machine_hw_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + machine_hw_spi_obj_t *self = (machine_hw_spi_obj_t *) self_in; + + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT , {.u_int = -1} }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + 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); + int8_t sck, mosi, miso; + + if (args[ARG_sck].u_obj == MP_OBJ_NULL) { + sck = -2; + } else if (args[ARG_sck].u_obj == mp_const_none) { + sck = -1; + } else { + sck = machine_pin_get_id(args[ARG_sck].u_obj); + } + + if (args[ARG_miso].u_obj == MP_OBJ_NULL) { + miso = -2; + } else if (args[ARG_miso].u_obj == mp_const_none) { + miso = -1; + } else { + miso = machine_pin_get_id(args[ARG_miso].u_obj); + } + + if (args[ARG_mosi].u_obj == MP_OBJ_NULL) { + mosi = -2; + } else if (args[ARG_mosi].u_obj == mp_const_none) { + mosi = -1; + } else { + mosi = machine_pin_get_id(args[ARG_mosi].u_obj); + } + + machine_hw_spi_init_internal(self, args[ARG_id].u_int, args[ARG_baudrate].u_int, + args[ARG_polarity].u_int, args[ARG_phase].u_int, args[ARG_bits].u_int, + args[ARG_firstbit].u_int, sck, mosi, miso); +} + +mp_obj_t machine_hw_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT , {.u_int = -1} }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + 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_hw_spi_obj_t *self = m_new_obj(machine_hw_spi_obj_t); + self->base.type = &machine_hw_spi_type; + + machine_hw_spi_init_internal( + self, + args[ARG_id].u_int, + args[ARG_baudrate].u_int, + args[ARG_polarity].u_int, + args[ARG_phase].u_int, + args[ARG_bits].u_int, + args[ARG_firstbit].u_int, + args[ARG_sck].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_sck].u_obj), + args[ARG_mosi].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_mosi].u_obj), + args[ARG_miso].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_miso].u_obj)); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC const mp_machine_spi_p_t machine_hw_spi_p = { + .init = machine_hw_spi_init, + .deinit = machine_hw_spi_deinit, + .transfer = machine_hw_spi_transfer, +}; + +const mp_obj_type_t machine_hw_spi_type = { + { &mp_type_type }, + .name = MP_QSTR_SPI, + .print = machine_hw_spi_print, + .make_new = machine_hw_spi_make_new, + .protocol = &machine_hw_spi_p, + .locals_dict = (mp_obj_dict_t *) &mp_machine_spi_locals_dict, +}; diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c new file mode 100644 index 000000000..493dff3f5 --- /dev/null +++ b/ports/esp32/machine_pin.c @@ -0,0 +1,375 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 + +#include "driver/gpio.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" +#include "extmod/virtpin.h" + +typedef struct _machine_pin_obj_t { + mp_obj_base_t base; + gpio_num_t id; +} machine_pin_obj_t; + +typedef struct _machine_pin_irq_obj_t { + mp_obj_base_t base; + gpio_num_t id; +} machine_pin_irq_obj_t; + +STATIC const machine_pin_obj_t machine_pin_obj[] = { + {{&machine_pin_type}, GPIO_NUM_0}, + {{&machine_pin_type}, GPIO_NUM_1}, + {{&machine_pin_type}, GPIO_NUM_2}, + {{&machine_pin_type}, GPIO_NUM_3}, + {{&machine_pin_type}, GPIO_NUM_4}, + {{&machine_pin_type}, GPIO_NUM_5}, + {{&machine_pin_type}, GPIO_NUM_6}, + {{&machine_pin_type}, GPIO_NUM_7}, + {{&machine_pin_type}, GPIO_NUM_8}, + {{&machine_pin_type}, GPIO_NUM_9}, + {{&machine_pin_type}, GPIO_NUM_10}, + {{&machine_pin_type}, GPIO_NUM_11}, + {{&machine_pin_type}, GPIO_NUM_12}, + {{&machine_pin_type}, GPIO_NUM_13}, + {{&machine_pin_type}, GPIO_NUM_14}, + {{&machine_pin_type}, GPIO_NUM_15}, + {{&machine_pin_type}, GPIO_NUM_16}, + {{&machine_pin_type}, GPIO_NUM_17}, + {{&machine_pin_type}, GPIO_NUM_18}, + {{&machine_pin_type}, GPIO_NUM_19}, + {{NULL}, -1}, + {{&machine_pin_type}, GPIO_NUM_21}, + {{&machine_pin_type}, GPIO_NUM_22}, + {{&machine_pin_type}, GPIO_NUM_23}, + {{NULL}, -1}, + {{&machine_pin_type}, GPIO_NUM_25}, + {{&machine_pin_type}, GPIO_NUM_26}, + {{&machine_pin_type}, GPIO_NUM_27}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{&machine_pin_type}, GPIO_NUM_32}, + {{&machine_pin_type}, GPIO_NUM_33}, + {{&machine_pin_type}, GPIO_NUM_34}, + {{&machine_pin_type}, GPIO_NUM_35}, + {{&machine_pin_type}, GPIO_NUM_36}, + {{&machine_pin_type}, GPIO_NUM_37}, + {{&machine_pin_type}, GPIO_NUM_38}, + {{&machine_pin_type}, GPIO_NUM_39}, +}; + +// forward declaration +STATIC const machine_pin_irq_obj_t machine_pin_irq_object[]; + +void machine_pins_init(void) { + static bool did_install = false; + if (!did_install) { + gpio_install_isr_service(0); + did_install = true; + } + memset(&MP_STATE_PORT(machine_pin_irq_handler[0]), 0, sizeof(MP_STATE_PORT(machine_pin_irq_handler))); +} + +void machine_pins_deinit(void) { + for (int i = 0; i < MP_ARRAY_SIZE(machine_pin_obj); ++i) { + if (machine_pin_obj[i].id != (gpio_num_t)-1) { + gpio_isr_handler_remove(machine_pin_obj[i].id); + } + } +} + +STATIC void IRAM_ATTR machine_pin_isr_handler(void *arg) { + machine_pin_obj_t *self = arg; + mp_obj_t handler = MP_STATE_PORT(machine_pin_irq_handler)[self->id]; + mp_sched_schedule(handler, MP_OBJ_FROM_PTR(self)); +} + +gpio_num_t machine_pin_get_id(mp_obj_t pin_in) { + if (mp_obj_get_type(pin_in) != &machine_pin_type) { + mp_raise_ValueError("expecting a pin"); + } + machine_pin_obj_t *self = pin_in; + return self->id; +} + +STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_pin_obj_t *self = self_in; + mp_printf(print, "Pin(%u)", self->id); +} + +// pin.init(mode, pull=None, *, value) +STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_mode, ARG_pull, ARG_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = mp_const_none}}, + { MP_QSTR_pull, MP_ARG_OBJ, {.u_obj = mp_const_none}}, + { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + }; + + // parse args + 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); + + // configure the pin for gpio + gpio_pad_select_gpio(self->id); + + // set initial value (do this before configuring mode/pull) + if (args[ARG_value].u_obj != MP_OBJ_NULL) { + gpio_set_level(self->id, mp_obj_is_true(args[ARG_value].u_obj)); + } + + // configure mode + if (args[ARG_mode].u_obj != mp_const_none) { + mp_int_t pin_io_mode = mp_obj_get_int(args[ARG_mode].u_obj); + if (self->id >= 34 && (pin_io_mode & GPIO_MODE_DEF_OUTPUT)) { + mp_raise_ValueError("pin can only be input"); + } else { + gpio_set_direction(self->id, pin_io_mode); + } + } + + // configure pull + if (args[ARG_pull].u_obj != mp_const_none) { + gpio_set_pull_mode(self->id, mp_obj_get_int(args[ARG_pull].u_obj)); + } + + return mp_const_none; +} + +// constructor(id, ...) +mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // get the wanted pin object + int wanted_pin = mp_obj_get_int(args[0]); + const machine_pin_obj_t *self = NULL; + if (0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj)) { + self = (machine_pin_obj_t*)&machine_pin_obj[wanted_pin]; + } + if (self == NULL || self->base.type == NULL) { + mp_raise_ValueError("invalid pin"); + } + + if (n_args > 1 || n_kw > 0) { + // pin mode given, so configure this GPIO + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); + } + + return MP_OBJ_FROM_PTR(self); +} + +// fast method for getting/setting pin value +STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + machine_pin_obj_t *self = self_in; + if (n_args == 0) { + // get pin + return MP_OBJ_NEW_SMALL_INT(gpio_get_level(self->id)); + } else { + // set pin + gpio_set_level(self->id, mp_obj_is_true(args[0])); + return mp_const_none; + } +} + +// pin.init(mode, pull) +STATIC mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); + +// pin.value([value]) +STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { + return machine_pin_call(args[0], n_args - 1, 0, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value); + +// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING) +STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = GPIO_PIN_INTR_POSEDGE | GPIO_PIN_INTR_NEGEDGE} }, + }; + machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (n_args > 1 || kw_args->used != 0) { + // configure irq + mp_obj_t handler = args[ARG_handler].u_obj; + uint32_t trigger = args[ARG_trigger].u_int; + if (handler == mp_const_none) { + handler = MP_OBJ_NULL; + trigger = 0; + } + gpio_isr_handler_remove(self->id); + MP_STATE_PORT(machine_pin_irq_handler)[self->id] = handler; + gpio_set_intr_type(self->id, trigger); + gpio_isr_handler_add(self->id, machine_pin_isr_handler, (void*)self); + } + + // return the irq object + return MP_OBJ_FROM_PTR(&machine_pin_irq_object[self->id]); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq); + +STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_INPUT) }, + { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_MODE_INPUT_OUTPUT) }, + { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_MODE_INPUT_OUTPUT_OD) }, + { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULLUP_ONLY) }, + { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULLDOWN_ONLY) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_PIN_INTR_POSEDGE) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_PIN_INTR_NEGEDGE) }, +}; + +STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + (void)errcode; + machine_pin_obj_t *self = self_in; + + switch (request) { + case MP_PIN_READ: { + return gpio_get_level(self->id); + } + case MP_PIN_WRITE: { + gpio_set_level(self->id, arg); + return 0; + } + } + return -1; +} + +STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); + +STATIC const mp_pin_p_t pin_pin_p = { + .ioctl = pin_ioctl, +}; + +const mp_obj_type_t machine_pin_type = { + { &mp_type_type }, + .name = MP_QSTR_Pin, + .print = machine_pin_print, + .make_new = mp_pin_make_new, + .call = machine_pin_call, + .protocol = &pin_pin_p, + .locals_dict = (mp_obj_t)&machine_pin_locals_dict, +}; + +/******************************************************************************/ +// Pin IRQ object + +STATIC const mp_obj_type_t machine_pin_irq_type; + +STATIC const machine_pin_irq_obj_t machine_pin_irq_object[] = { + {{&machine_pin_irq_type}, GPIO_NUM_0}, + {{&machine_pin_irq_type}, GPIO_NUM_1}, + {{&machine_pin_irq_type}, GPIO_NUM_2}, + {{&machine_pin_irq_type}, GPIO_NUM_3}, + {{&machine_pin_irq_type}, GPIO_NUM_4}, + {{&machine_pin_irq_type}, GPIO_NUM_5}, + {{&machine_pin_irq_type}, GPIO_NUM_6}, + {{&machine_pin_irq_type}, GPIO_NUM_7}, + {{&machine_pin_irq_type}, GPIO_NUM_8}, + {{&machine_pin_irq_type}, GPIO_NUM_9}, + {{&machine_pin_irq_type}, GPIO_NUM_10}, + {{&machine_pin_irq_type}, GPIO_NUM_11}, + {{&machine_pin_irq_type}, GPIO_NUM_12}, + {{&machine_pin_irq_type}, GPIO_NUM_13}, + {{&machine_pin_irq_type}, GPIO_NUM_14}, + {{&machine_pin_irq_type}, GPIO_NUM_15}, + {{&machine_pin_irq_type}, GPIO_NUM_16}, + {{&machine_pin_irq_type}, GPIO_NUM_17}, + {{&machine_pin_irq_type}, GPIO_NUM_18}, + {{&machine_pin_irq_type}, GPIO_NUM_19}, + {{NULL}, -1}, + {{&machine_pin_irq_type}, GPIO_NUM_21}, + {{&machine_pin_irq_type}, GPIO_NUM_22}, + {{&machine_pin_irq_type}, GPIO_NUM_23}, + {{NULL}, -1}, + {{&machine_pin_irq_type}, GPIO_NUM_25}, + {{&machine_pin_irq_type}, GPIO_NUM_26}, + {{&machine_pin_irq_type}, GPIO_NUM_27}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{&machine_pin_irq_type}, GPIO_NUM_32}, + {{&machine_pin_irq_type}, GPIO_NUM_33}, + {{&machine_pin_irq_type}, GPIO_NUM_34}, + {{&machine_pin_irq_type}, GPIO_NUM_35}, + {{&machine_pin_irq_type}, GPIO_NUM_36}, + {{&machine_pin_irq_type}, GPIO_NUM_37}, + {{&machine_pin_irq_type}, GPIO_NUM_38}, + {{&machine_pin_irq_type}, GPIO_NUM_39}, +}; + +STATIC mp_obj_t machine_pin_irq_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + machine_pin_irq_obj_t *self = self_in; + mp_arg_check_num(n_args, n_kw, 0, 0, false); + machine_pin_isr_handler((void*)&machine_pin_obj[self->id]); + return mp_const_none; +} + +STATIC mp_obj_t machine_pin_irq_trigger(size_t n_args, const mp_obj_t *args) { + machine_pin_irq_obj_t *self = args[0]; + uint32_t orig_trig = GPIO.pin[self->id].int_type; + if (n_args == 2) { + // set trigger + gpio_set_intr_type(self->id, mp_obj_get_int(args[1])); + } + // return original trigger value + return MP_OBJ_NEW_SMALL_INT(orig_trig); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_irq_trigger_obj, 1, 2, machine_pin_irq_trigger); + +STATIC const mp_rom_map_elem_t machine_pin_irq_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_trigger), MP_ROM_PTR(&machine_pin_irq_trigger_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_pin_irq_locals_dict, machine_pin_irq_locals_dict_table); + +STATIC const mp_obj_type_t machine_pin_irq_type = { + { &mp_type_type }, + .name = MP_QSTR_IRQ, + .call = machine_pin_irq_call, + .locals_dict = (mp_obj_dict_t*)&machine_pin_irq_locals_dict, +}; diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c new file mode 100644 index 000000000..489833e7c --- /dev/null +++ b/ports/esp32/machine_pwm.c @@ -0,0 +1,277 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 "driver/ledc.h" +#include "esp_err.h" + +#include "py/nlr.h" +#include "py/runtime.h" +#include "modmachine.h" +#include "mphalport.h" + +// Forward dec'l +extern const mp_obj_type_t machine_pwm_type; + +typedef struct _esp32_pwm_obj_t { + mp_obj_base_t base; + gpio_num_t pin; + uint8_t active; + uint8_t channel; +} esp32_pwm_obj_t; + +// Which channel has which GPIO pin assigned? +// (-1 if not assigned) +STATIC int chan_gpio[LEDC_CHANNEL_MAX]; + +// Params for PW operation +// 5khz +#define PWFREQ (5000) +// High speed mode +#define PWMODE (LEDC_HIGH_SPEED_MODE) +// 10-bit resolution (compatible with esp8266 PWM) +#define PWRES (LEDC_TIMER_10_BIT) +// Timer 1 +#define PWTIMER (LEDC_TIMER_1) + +// Config of timer upon which we run all PWM'ed GPIO pins +STATIC bool pwm_inited = false; +STATIC ledc_timer_config_t timer_cfg = { + .bit_num = PWRES, + .freq_hz = PWFREQ, + .speed_mode = PWMODE, + .timer_num = PWTIMER +}; + +STATIC void pwm_init(void) { + + // Initial condition: no channels assigned + for (int x = 0; x < LEDC_CHANNEL_MAX; ++x) { + chan_gpio[x] = -1; + } + + // Init with default timer params + ledc_timer_config(&timer_cfg); +} + +STATIC int set_freq(int newval) { + int oval = timer_cfg.freq_hz; + + timer_cfg.freq_hz = newval; + if (ledc_timer_config(&timer_cfg) != ESP_OK) { + timer_cfg.freq_hz = oval; + return 0; + } + return 1; +} + +/******************************************************************************/ + +// MicroPython bindings for PWM + +STATIC void esp32_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "PWM(%u", self->pin); + if (self->active) { + mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz, + ledc_get_duty(PWMODE, self->channel)); + } + mp_printf(print, ")"); +} + +STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self, + size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_freq, ARG_duty }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_duty, MP_ARG_INT, {.u_int = -1} }, + }; + 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); + + int channel; + int avail = -1; + + // Find a free PWM channel, also spot if our pin is + // already mentioned. + for (channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if (chan_gpio[channel] == self->pin) { + break; + } + if ((avail == -1) && (chan_gpio[channel] == -1)) { + avail = channel; + } + } + if (channel >= LEDC_CHANNEL_MAX) { + if (avail == -1) { + mp_raise_ValueError("out of PWM channels"); + } + channel = avail; + } + + // New PWM assignment + self->active = 1; + if (chan_gpio[channel] == -1) { + ledc_channel_config_t cfg = { + .channel = channel, + .duty = (1 << PWRES) / 2, + .gpio_num = self->pin, + .intr_type = LEDC_INTR_DISABLE, + .speed_mode = PWMODE, + .timer_sel = PWTIMER, + }; + if (ledc_channel_config(&cfg) != ESP_OK) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "PWM not supported on pin %d", self->pin)); + } + chan_gpio[channel] = self->pin; + self->channel = channel; + } + + // Maybe change PWM timer + int tval = args[ARG_freq].u_int; + if (tval != -1) { + if (tval != timer_cfg.freq_hz) { + if (!set_freq(tval)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Bad frequency %d", tval)); + } + } + } + + // Set duty cycle? + int dval = args[ARG_duty].u_int; + if (dval != -1) { + dval &= ((1 << PWRES)-1); + ledc_set_duty(PWMODE, channel, dval); + ledc_update_duty(PWMODE, channel); + } +} + +STATIC mp_obj_t esp32_pwm_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + + // create PWM object from the given pin + esp32_pwm_obj_t *self = m_new_obj(esp32_pwm_obj_t); + self->base.type = &machine_pwm_type; + self->pin = pin_id; + self->active = 0; + self->channel = -1; + + // start the PWM subsystem if it's not already running + if (!pwm_inited) { + pwm_init(); + pwm_inited = true; + } + + // start the PWM running for this channel + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + esp32_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t esp32_pwm_init(size_t n_args, + const mp_obj_t *args, mp_map_t *kw_args) { + esp32_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(esp32_pwm_init_obj, 1, esp32_pwm_init); + +STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) { + esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + int chan = self->channel; + + // Valid channel? + if ((chan >= 0) && (chan < LEDC_CHANNEL_MAX)) { + // Mark it unused, and tell the hardware to stop routing + chan_gpio[chan] = -1; + ledc_stop(PWMODE, chan, 0); + self->active = 0; + self->channel = -1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_pwm_deinit_obj, esp32_pwm_deinit); + +STATIC mp_obj_t esp32_pwm_freq(size_t n_args, const mp_obj_t *args) { + if (n_args == 1) { + // get + return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz); + } + + // set + int tval = mp_obj_get_int(args[1]); + if (!set_freq(tval)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Bad frequency %d", tval)); + } + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_pwm_freq_obj, 1, 2, esp32_pwm_freq); + +STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) { + esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + int duty; + + if (n_args == 1) { + // get + duty = ledc_get_duty(PWMODE, self->channel); + return MP_OBJ_NEW_SMALL_INT(duty); + } + + // set + duty = mp_obj_get_int(args[1]); + duty &= ((1 << PWRES)-1); + ledc_set_duty(PWMODE, self->channel, duty); + ledc_update_duty(PWMODE, self->channel); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_pwm_duty_obj, + 1, 2, esp32_pwm_duty); + +STATIC const mp_rom_map_elem_t esp32_pwm_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&esp32_pwm_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_pwm_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&esp32_pwm_freq_obj) }, + { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&esp32_pwm_duty_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(esp32_pwm_locals_dict, + esp32_pwm_locals_dict_table); + +const mp_obj_type_t machine_pwm_type = { + { &mp_type_type }, + .name = MP_QSTR_PWM, + .print = esp32_pwm_print, + .make_new = esp32_pwm_make_new, + .locals_dict = (mp_obj_dict_t*)&esp32_pwm_locals_dict, +}; diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c new file mode 100644 index 000000000..d75efb8fc --- /dev/null +++ b/ports/esp32/machine_timer.c @@ -0,0 +1,192 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * 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 + +#include "driver/timer.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "modmachine.h" + +#define TIMER_INTR_SEL TIMER_INTR_LEVEL +#define TIMER_DIVIDER 40000 +#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) + +#define TIMER_FLAGS 0 + +typedef struct _machine_timer_obj_t { + mp_obj_base_t base; + mp_uint_t group; + mp_uint_t index; + + mp_uint_t repeat; + mp_uint_t period; + + mp_obj_t callback; + + intr_handle_t handle; +} machine_timer_obj_t; + +const mp_obj_type_t machine_timer_type; + +STATIC esp_err_t check_esp_err(esp_err_t code) { + if (code) { + mp_raise_OSError(code); + } + + return code; +} + +STATIC void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_timer_obj_t *self = self_in; + + timer_config_t config; + mp_printf(print, "Timer(%p; ", self); + + timer_get_config(self->group, self->index, &config); + + mp_printf(print, "alarm_en=%d, ", config.alarm_en); + mp_printf(print, "auto_reload=%d, ", config.auto_reload); + mp_printf(print, "counter_en=%d)", config.counter_en); +} + +STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + machine_timer_obj_t *self = m_new_obj(machine_timer_obj_t); + self->base.type = &machine_timer_type; + + self->group = (mp_obj_get_int(args[0]) >> 1) & 1; + self->index = mp_obj_get_int(args[0]) & 1; + + return self; +} + +STATIC void machine_timer_disable(machine_timer_obj_t *self) { + if (self->handle) { + timer_pause(self->group, self->index); + esp_intr_free(self->handle); + self->handle = NULL; + } +} + +STATIC void machine_timer_isr(void *self_in) { + machine_timer_obj_t *self = self_in; + timg_dev_t *device = self->group ? &(TIMERG1) : &(TIMERG0); + + device->hw_timer[self->index].update = 1; + if (self->index) { + device->int_clr_timers.t1 = 1; + } else { + device->int_clr_timers.t0 = 1; + } + device->hw_timer[self->index].config.alarm_en = self->repeat; + + mp_sched_schedule(self->callback, self); +} + +STATIC void machine_timer_enable(machine_timer_obj_t *self) { + timer_config_t config; + config.alarm_en = TIMER_ALARM_EN; + config.auto_reload = self->repeat; + config.counter_dir = TIMER_COUNT_UP; + config.divider = TIMER_DIVIDER; + config.intr_type = TIMER_INTR_LEVEL; + config.counter_en = TIMER_PAUSE; + + check_esp_err(timer_init(self->group, self->index, &config)); + check_esp_err(timer_set_counter_value(self->group, self->index, 0x00000000)); + check_esp_err(timer_set_alarm_value(self->group, self->index, self->period)); + check_esp_err(timer_enable_intr(self->group, self->index)); + check_esp_err(timer_isr_register(self->group, self->index, machine_timer_isr, (void*)self, TIMER_FLAGS, &self->handle)); + check_esp_err(timer_start(self->group, self->index)); +} + +STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + machine_timer_disable(self); + + 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); + + // Timer uses an 80MHz base clock, which is divided by the divider/scalar, we then convert to ms. + self->period = (args[0].u_int * TIMER_BASE_CLK) / (1000 * TIMER_DIVIDER); + self->repeat = args[1].u_int; + self->callback = args[2].u_obj; + self->handle = NULL; + + machine_timer_enable(self); + + return mp_const_none; +} + +STATIC mp_obj_t machine_timer_deinit(mp_obj_t self_in) { + machine_timer_disable(self_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); + +STATIC mp_obj_t machine_timer_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); + +STATIC mp_obj_t machine_timer_value(mp_obj_t self_in) { + machine_timer_obj_t *self = self_in; + double result; + + timer_get_counter_time_sec(self->group, self->index, &result); + + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result * 1000)); // value in ms +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_value_obj, machine_timer_value); + +STATIC const mp_map_elem_t machine_timer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), (mp_obj_t)&machine_timer_deinit_obj }, + { MP_ROM_QSTR(MP_QSTR_deinit), (mp_obj_t)&machine_timer_deinit_obj }, + { MP_ROM_QSTR(MP_QSTR_init), (mp_obj_t)&machine_timer_init_obj }, + { MP_ROM_QSTR(MP_QSTR_value), (mp_obj_t)&machine_timer_value_obj }, + { MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(false) }, + { MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(true) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table); + +const mp_obj_type_t machine_timer_type = { + { &mp_type_type }, + .name = MP_QSTR_Timer, + .print = machine_timer_print, + .make_new = machine_timer_make_new, + .locals_dict = (mp_obj_t)&machine_timer_locals_dict, +}; diff --git a/ports/esp32/machine_touchpad.c b/ports/esp32/machine_touchpad.c new file mode 100644 index 000000000..96de1a2a1 --- /dev/null +++ b/ports/esp32/machine_touchpad.c @@ -0,0 +1,110 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Nick Moore + * + * 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 "esp_log.h" + +#include "driver/gpio.h" +#include "driver/touch_pad.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" + +typedef struct _mtp_obj_t { + mp_obj_base_t base; + gpio_num_t gpio_id; + touch_pad_t touchpad_id; +} mtp_obj_t; + +STATIC const mtp_obj_t touchpad_obj[] = { + {{&machine_touchpad_type}, GPIO_NUM_4, TOUCH_PAD_NUM0}, + {{&machine_touchpad_type}, GPIO_NUM_0, TOUCH_PAD_NUM1}, + {{&machine_touchpad_type}, GPIO_NUM_2, TOUCH_PAD_NUM2}, + {{&machine_touchpad_type}, GPIO_NUM_15, TOUCH_PAD_NUM3}, + {{&machine_touchpad_type}, GPIO_NUM_13, TOUCH_PAD_NUM4}, + {{&machine_touchpad_type}, GPIO_NUM_12, TOUCH_PAD_NUM5}, + {{&machine_touchpad_type}, GPIO_NUM_14, TOUCH_PAD_NUM6}, + {{&machine_touchpad_type}, GPIO_NUM_27, TOUCH_PAD_NUM7}, + {{&machine_touchpad_type}, GPIO_NUM_33, TOUCH_PAD_NUM8}, + {{&machine_touchpad_type}, GPIO_NUM_32, TOUCH_PAD_NUM9}, +}; + +STATIC mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, + const mp_obj_t *args) { + + mp_arg_check_num(n_args, n_kw, 1, 1, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + const mtp_obj_t *self = NULL; + for (int i = 0; i < MP_ARRAY_SIZE(touchpad_obj); i++) { + if (pin_id == touchpad_obj[i].gpio_id) { self = &touchpad_obj[i]; break; } + } + if (!self) mp_raise_ValueError("invalid pin for touchpad"); + + static int initialized = 0; + if (!initialized) { + touch_pad_init(); + initialized = 1; + } + esp_err_t err = touch_pad_config(self->touchpad_id, 0); + if (err == ESP_OK) return MP_OBJ_FROM_PTR(self); + mp_raise_ValueError("Touch pad error"); +} + +STATIC mp_obj_t mtp_config(mp_obj_t self_in, mp_obj_t value_in) { + mtp_obj_t *self = self_in; + uint16_t value = mp_obj_get_int(value_in); + esp_err_t err = touch_pad_config(self->touchpad_id, value); + if (err == ESP_OK) return mp_const_none; + mp_raise_ValueError("Touch pad error"); +} +MP_DEFINE_CONST_FUN_OBJ_2(mtp_config_obj, mtp_config); + +STATIC mp_obj_t mtp_read(mp_obj_t self_in) { + mtp_obj_t *self = self_in; + uint16_t value; + esp_err_t err = touch_pad_read(self->touchpad_id, &value); + if (err == ESP_OK) return MP_OBJ_NEW_SMALL_INT(value); + mp_raise_ValueError("Touch pad error"); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtp_read_obj, mtp_read); + +STATIC const mp_rom_map_elem_t mtp_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&mtp_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mtp_read_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mtp_locals_dict, mtp_locals_dict_table); + +const mp_obj_type_t machine_touchpad_type = { + { &mp_type_type }, + .name = MP_QSTR_TouchPad, + .make_new = mtp_make_new, + .locals_dict = (mp_obj_t)&mtp_locals_dict, +}; diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c new file mode 100644 index 000000000..0b303d424 --- /dev/null +++ b/ports/esp32/machine_uart.c @@ -0,0 +1,359 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 +#include + +#include "driver/uart.h" +#include "freertos/FreeRTOS.h" + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "modmachine.h" + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + uart_port_t uart_num; + uint8_t bits; + uint8_t parity; + uint8_t stop; + int8_t tx; + int8_t rx; + int8_t rts; + int8_t cts; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) +} machine_uart_obj_t; + +STATIC const char *_parity_name[] = {"None", "1", "0"}; + +QueueHandle_t UART_QUEUE[UART_NUM_MAX] = {}; + +/******************************************************************************/ +// MicroPython bindings for UART + +STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t baudrate; + uart_get_baudrate(self->uart_num, &baudrate); + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d, rts=%d, cts=%d, timeout=%u, timeout_char=%u)", + self->uart_num, baudrate, self->bits, _parity_name[self->parity], + self->stop, self->tx, self->rx, self->rts, self->cts, self->timeout, self->timeout_char); +} + +STATIC void machine_uart_init_helper(machine_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_tx, ARG_rx, ARG_rts, ARG_cts, 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_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { 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} }, + }; + 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); + + // wait for all data to be transmitted before changing settings + uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(1000)); + + // set baudrate + uint32_t baudrate = 115200; + if (args[ARG_baudrate].u_int > 0) { + uart_set_baudrate(self->uart_num, args[ARG_baudrate].u_int); + uart_get_baudrate(self->uart_num, &baudrate); + } + + uart_set_pin(self->uart_num, args[ARG_tx].u_int, args[ARG_rx].u_int, args[ARG_rts].u_int, args[ARG_cts].u_int); + if (args[ARG_tx].u_int != UART_PIN_NO_CHANGE) { + self->tx = args[ARG_tx].u_int; + } + + if (args[ARG_rx].u_int != UART_PIN_NO_CHANGE) { + self->rx = args[ARG_rx].u_int; + } + + if (args[ARG_rts].u_int != UART_PIN_NO_CHANGE) { + self->rts = args[ARG_rts].u_int; + } + + if (args[ARG_cts].u_int != UART_PIN_NO_CHANGE) { + self->cts = args[ARG_cts].u_int; + } + + // set data bits + switch (args[ARG_bits].u_int) { + case 0: + break; + case 5: + uart_set_word_length(self->uart_num, UART_DATA_5_BITS); + self->bits = 5; + break; + case 6: + uart_set_word_length(self->uart_num, UART_DATA_6_BITS); + self->bits = 6; + break; + case 7: + uart_set_word_length(self->uart_num, UART_DATA_7_BITS); + self->bits = 7; + break; + case 8: + uart_set_word_length(self->uart_num, UART_DATA_8_BITS); + self->bits = 8; + break; + default: + mp_raise_ValueError("invalid data bits"); + break; + } + + // set parity + if (args[ARG_parity].u_obj != MP_OBJ_NULL) { + if (args[ARG_parity].u_obj == mp_const_none) { + uart_set_parity(self->uart_num, UART_PARITY_DISABLE); + self->parity = 0; + } else { + mp_int_t parity = mp_obj_get_int(args[ARG_parity].u_obj); + if (parity & 1) { + uart_set_parity(self->uart_num, UART_PARITY_ODD); + self->parity = 1; + } else { + uart_set_parity(self->uart_num, UART_PARITY_EVEN); + self->parity = 2; + } + } + } + + // set stop bits + switch (args[ARG_stop].u_int) { + // FIXME: ESP32 also supports 1.5 stop bits + case 0: + break; + case 1: + uart_set_stop_bits(self->uart_num, UART_STOP_BITS_1); + self->stop = 1; + break; + case 2: + uart_set_stop_bits(self->uart_num, UART_STOP_BITS_2); + self->stop = 2; + break; + default: + mp_raise_ValueError("invalid stop bits"); + break; + } + + // set timeout + self->timeout = args[ARG_timeout].u_int; + + // set timeout_char + // make sure it is at least as long as a whole character (13 bits to be safe) + self->timeout_char = args[ARG_timeout_char].u_int; + uint32_t min_timeout_char = 13000 / baudrate + 1; + if (self->timeout_char < min_timeout_char) { + self->timeout_char = min_timeout_char; + } +} + +STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // get uart id + mp_int_t uart_num = mp_obj_get_int(args[0]); + if (uart_num < 0 || uart_num > UART_NUM_MAX) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) does not exist", uart_num)); + } + + // Attempts to use UART0 from Python has resulted in all sorts of fun errors. + // FIXME: UART0 is disabled for now. + if (uart_num == UART_NUM_0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) is disabled (dedicated to REPL)", uart_num)); + } + + // Defaults + uart_config_t uartcfg = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0 + }; + + // create instance + machine_uart_obj_t *self = m_new_obj(machine_uart_obj_t); + self->base.type = &machine_uart_type; + self->uart_num = uart_num; + self->bits = 8; + self->parity = 0; + self->stop = 1; + self->rts = UART_PIN_NO_CHANGE; + self->cts = UART_PIN_NO_CHANGE; + self->timeout = 0; + self->timeout_char = 0; + + switch (uart_num) { + case UART_NUM_0: + self->rx = UART_PIN_NO_CHANGE; // GPIO 3 + self->tx = UART_PIN_NO_CHANGE; // GPIO 1 + break; + case UART_NUM_1: + self->rx = 9; + self->tx = 10; + break; + case UART_NUM_2: + self->rx = 16; + self->tx = 17; + break; + } + + // Remove any existing configuration + uart_driver_delete(self->uart_num); + + // init the peripheral + // Setup + uart_param_config(self->uart_num, &uartcfg); + + // RX and TX buffers are currently hardcoded at 256 bytes each (IDF minimum). + uart_driver_install(uart_num, 256, 256, 10, &UART_QUEUE[self->uart_num], 0); + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args); + + // Make sure pins are connected. + uart_set_pin(self->uart_num, self->tx, self->rx, self->rts, self->cts); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + machine_uart_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init); + +STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t rxbufsize; + uart_get_buffered_data_len(self->uart_num, &rxbufsize); + return MP_OBJ_NEW_SMALL_INT(rxbufsize); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any); + +STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_uart_init_obj) }, + + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); + +STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + TickType_t time_to_wait; + if (self->timeout == 0) { + time_to_wait = 0; + } else { + time_to_wait = pdMS_TO_TICKS(self->timeout); + } + + int bytes_read = uart_read_bytes(self->uart_num, buf_in, size, time_to_wait); + + if (bytes_read < 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + return bytes_read; +} + +STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int bytes_written = uart_write_bytes(self->uart_num, buf_in, size); + + if (bytes_written < 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + // return number of bytes written + return bytes_written; +} + +STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + machine_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + mp_uint_t flags = arg; + ret = 0; + size_t rxbufsize; + uart_get_buffered_data_len(self->uart_num, &rxbufsize); + if ((flags & MP_STREAM_POLL_RD) && rxbufsize > 0) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && 1) { // FIXME: uart_tx_any_room(self->uart_num) + ret |= MP_STREAM_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_stream_p_t uart_stream_p = { + .read = machine_uart_read, + .write = machine_uart_write, + .ioctl = machine_uart_ioctl, + .is_text = false, +}; + +const mp_obj_type_t machine_uart_type = { + { &mp_type_type }, + .name = MP_QSTR_UART, + .print = machine_uart_print, + .make_new = machine_uart_make_new, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &uart_stream_p, + .locals_dict = (mp_obj_dict_t*)&machine_uart_locals_dict, +}; diff --git a/ports/esp32/main.c b/ports/esp32/main.c new file mode 100644 index 000000000..091cbddc9 --- /dev/null +++ b/ports/esp32/main.c @@ -0,0 +1,131 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_task.h" +#include "soc/cpu.h" + +#include "py/stackctrl.h" +#include "py/nlr.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "lib/mp-readline/readline.h" +#include "lib/utils/pyexec.h" +#include "uart.h" +#include "modmachine.h" +#include "mpthreadport.h" + +// MicroPython runs as a task under FreeRTOS +#define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) +#define MP_TASK_STACK_SIZE (16 * 1024) +#define MP_TASK_STACK_LEN (MP_TASK_STACK_SIZE / sizeof(StackType_t)) +#define MP_TASK_HEAP_SIZE (96 * 1024) + +STATIC StaticTask_t mp_task_tcb; +STATIC StackType_t mp_task_stack[MP_TASK_STACK_LEN] __attribute__((aligned (8))); +STATIC uint8_t mp_task_heap[MP_TASK_HEAP_SIZE]; + +void mp_task(void *pvParameter) { + volatile uint32_t sp = (uint32_t)get_sp(); + #if MICROPY_PY_THREAD + mp_thread_init(&mp_task_stack[0], MP_TASK_STACK_LEN); + #endif + uart_init(); + +soft_reset: + // initialise the stack pointer for the main thread + mp_stack_set_top((void *)sp); + mp_stack_set_limit(MP_TASK_STACK_SIZE - 1024); + gc_init(mp_task_heap, mp_task_heap + sizeof(mp_task_heap)); + mp_init(); + mp_obj_list_init(mp_sys_path, 0); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); + mp_obj_list_init(mp_sys_argv, 0); + readline_init0(); + + // initialise peripherals + machine_pins_init(); + + // run boot-up scripts + pyexec_frozen_module("_boot.py"); + pyexec_file("boot.py"); + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { + pyexec_file("main.py"); + } + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + + #if MICROPY_PY_THREAD + mp_thread_deinit(); + #endif + + mp_hal_stdout_tx_str("PYB: soft reboot\r\n"); + + // deinitialise peripherals + machine_pins_deinit(); + + mp_deinit(); + fflush(stdout); + goto soft_reset; +} + +void app_main(void) { + nvs_flash_init(); + xTaskCreateStaticPinnedToCore(mp_task, "mp_task", MP_TASK_STACK_LEN, NULL, MP_TASK_PRIORITY, + &mp_task_stack[0], &mp_task_tcb, 0); +} + +void nlr_jump_fail(void *val) { + printf("NLR jump failed, val=%p\n", val); + esp_restart(); +} + +// modussl_mbedtls uses this function but it's not enabled in ESP IDF +void mbedtls_debug_set_threshold(int threshold) { + (void)threshold; +} diff --git a/ports/esp32/makeimg.py b/ports/esp32/makeimg.py new file mode 100644 index 000000000..aeedbff7e --- /dev/null +++ b/ports/esp32/makeimg.py @@ -0,0 +1,25 @@ +import sys + +OFFSET_BOOTLOADER = 0x1000 +OFFSET_PARTITIONS = 0x8000 +OFFSET_APPLICATION = 0x10000 + +files_in = [ + ('bootloader', OFFSET_BOOTLOADER, sys.argv[1]), + ('partitions', OFFSET_PARTITIONS, sys.argv[2]), + ('application', OFFSET_APPLICATION, sys.argv[3]), +] +file_out = sys.argv[4] + +cur_offset = OFFSET_BOOTLOADER +with open(file_out, 'wb') as fout: + for name, offset, file_in in files_in: + assert offset >= cur_offset + fout.write(b'\xff' * (offset - cur_offset)) + cur_offset = offset + with open(file_in, 'rb') as fin: + data = fin.read() + fout.write(data) + cur_offset += len(data) + print('%-12s% 8d' % (name, len(data))) + print('%-12s% 8d' % ('total', cur_offset)) diff --git a/ports/esp32/memory.h b/ports/esp32/memory.h new file mode 100644 index 000000000..f3777b0e3 --- /dev/null +++ b/ports/esp32/memory.h @@ -0,0 +1,2 @@ +// this is needed for extmod/crypto-algorithms/sha256.c +#include diff --git a/ports/esp32/modesp.c b/ports/esp32/modesp.c new file mode 100644 index 000000000..caa055164 --- /dev/null +++ b/ports/esp32/modesp.c @@ -0,0 +1,129 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paul Sokolovsky + * Copyright (c) 2016 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 "rom/gpio.h" +#include "esp_spi_flash.h" + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "drivers/dht/dht.h" +#include "modesp.h" + +STATIC mp_obj_t esp_flash_read(mp_obj_t offset_in, mp_obj_t buf_in) { + mp_int_t offset = mp_obj_get_int(offset_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + esp_err_t res = spi_flash_read(offset, bufinfo.buf, bufinfo.len); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_flash_read_obj, esp_flash_read); + +STATIC mp_obj_t esp_flash_write(mp_obj_t offset_in, mp_obj_t buf_in) { + mp_int_t offset = mp_obj_get_int(offset_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + esp_err_t res = spi_flash_write(offset, bufinfo.buf, bufinfo.len); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_flash_write_obj, esp_flash_write); + +STATIC mp_obj_t esp_flash_erase(mp_obj_t sector_in) { + mp_int_t sector = mp_obj_get_int(sector_in); + esp_err_t res = spi_flash_erase_sector(sector); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_flash_erase_obj, esp_flash_erase); + +STATIC mp_obj_t esp_flash_size(void) { + return mp_obj_new_int_from_uint(spi_flash_get_chip_size()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size); + +STATIC mp_obj_t esp_flash_user_start(void) { + return MP_OBJ_NEW_SMALL_INT(0x200000); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start); + +STATIC mp_obj_t esp_gpio_matrix_in(mp_obj_t pin, mp_obj_t sig, mp_obj_t inv) { + gpio_matrix_in(mp_obj_get_int(pin), mp_obj_get_int(sig), mp_obj_get_int(inv)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_gpio_matrix_in_obj, esp_gpio_matrix_in); + +STATIC mp_obj_t esp_gpio_matrix_out(size_t n_args, const mp_obj_t *args) { + (void)n_args; + gpio_matrix_out(mp_obj_get_int(args[0]), mp_obj_get_int(args[1]), mp_obj_get_int(args[2]), mp_obj_get_int(args[3])); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_gpio_matrix_out_obj, 4, 4, esp_gpio_matrix_out); + +STATIC mp_obj_t esp_neopixel_write_(mp_obj_t pin, mp_obj_t buf, mp_obj_t timing) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); + esp_neopixel_write(mp_hal_get_pin_obj(pin), + (uint8_t*)bufinfo.buf, bufinfo.len, mp_obj_get_int(timing)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_neopixel_write_obj, esp_neopixel_write_); + +STATIC const mp_rom_map_elem_t esp_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp) }, + + { MP_ROM_QSTR(MP_QSTR_flash_read), MP_ROM_PTR(&esp_flash_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_write), MP_ROM_PTR(&esp_flash_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_erase), MP_ROM_PTR(&esp_flash_erase_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_size), MP_ROM_PTR(&esp_flash_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_user_start), MP_ROM_PTR(&esp_flash_user_start_obj) }, + + { MP_ROM_QSTR(MP_QSTR_gpio_matrix_in), MP_ROM_PTR(&esp_gpio_matrix_in_obj) }, + { MP_ROM_QSTR(MP_QSTR_gpio_matrix_out), MP_ROM_PTR(&esp_gpio_matrix_out_obj) }, + + { MP_ROM_QSTR(MP_QSTR_neopixel_write), MP_ROM_PTR(&esp_neopixel_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_dht_readinto), MP_ROM_PTR(&dht_readinto_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(esp_module_globals, esp_module_globals_table); + +const mp_obj_module_t esp_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&esp_module_globals, +}; + diff --git a/ports/esp32/modesp.h b/ports/esp32/modesp.h new file mode 100644 index 000000000..a822c02ec --- /dev/null +++ b/ports/esp32/modesp.h @@ -0,0 +1 @@ +void esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t timing); diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c new file mode 100644 index 000000000..51038f314 --- /dev/null +++ b/ports/esp32/modmachine.c @@ -0,0 +1,135 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * 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 + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "esp_system.h" + +#include "py/obj.h" +#include "py/runtime.h" +#include "extmod/machine_mem.h" +#include "extmod/machine_signal.h" +#include "extmod/machine_pulse.h" +#include "extmod/machine_i2c.h" +#include "extmod/machine_spi.h" +#include "modmachine.h" + +#if MICROPY_PY_MACHINE + +STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + // get + return mp_obj_new_int(ets_get_cpu_frequency() * 1000000); + } else { + // set + mp_int_t freq = mp_obj_get_int(args[0]) / 1000000; + if (freq != 80 && freq != 160 && freq != 240) { + mp_raise_ValueError("frequency can only be either 80Mhz, 160MHz or 240MHz"); + } + /* + system_update_cpu_freq(freq); + */ + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); + +STATIC mp_obj_t machine_reset(void) { + esp_restart(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); + +STATIC mp_obj_t machine_unique_id(void) { + uint8_t chipid[6]; + esp_efuse_mac_get_default(chipid); + return mp_obj_new_bytes(chipid, 6); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); + +STATIC mp_obj_t machine_idle(void) { + taskYIELD(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + +STATIC mp_obj_t machine_disable_irq(void) { + uint32_t state = MICROPY_BEGIN_ATOMIC_SECTION(); + return mp_obj_new_int(state); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); + +STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) { + uint32_t state = mp_obj_get_int(state_in); + MICROPY_END_ATOMIC_SECTION(state); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq); + +STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, + + { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, + + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, + + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, + + { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, + + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, + { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, + { MP_ROM_QSTR(MP_QSTR_TouchPad), MP_ROM_PTR(&machine_touchpad_type) }, + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); + +const mp_obj_module_t mp_module_machine = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&machine_module_globals, +}; + +#endif // MICROPY_PY_MACHINE diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h new file mode 100644 index 000000000..4909235b4 --- /dev/null +++ b/ports/esp32/modmachine.h @@ -0,0 +1,18 @@ +#ifndef MICROPY_INCLUDED_ESP32_MODMACHINE_H +#define MICROPY_INCLUDED_ESP32_MODMACHINE_H + +#include "py/obj.h" + +extern const mp_obj_type_t machine_timer_type; +extern const mp_obj_type_t machine_pin_type; +extern const mp_obj_type_t machine_touchpad_type; +extern const mp_obj_type_t machine_adc_type; +extern const mp_obj_type_t machine_dac_type; +extern const mp_obj_type_t machine_pwm_type; +extern const mp_obj_type_t machine_hw_spi_type; +extern const mp_obj_type_t machine_uart_type; + +void machine_pins_init(void); +void machine_pins_deinit(void); + +#endif // MICROPY_INCLUDED_ESP32_MODMACHINE_H diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c new file mode 100644 index 000000000..73a471861 --- /dev/null +++ b/ports/esp32/modnetwork.c @@ -0,0 +1,561 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * and Mnemote Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Nick Moore @mnemote + * + * Based on esp8266/modnetwork.c which is Copyright (c) 2015 Paul Sokolovsky + * And the ESP IDF example code which is Public Domain / CC0 + * + * 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 +#include + +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "netutils.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "esp_log.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "lwip/dns.h" +#include "tcpip_adapter.h" + +#define MODNETWORK_INCLUDE_CONSTANTS (1) + +NORETURN void _esp_exceptions(esp_err_t e) { + switch (e) { + case ESP_ERR_WIFI_NOT_INIT: + mp_raise_msg(&mp_type_OSError, "Wifi Not Initialized"); + case ESP_ERR_WIFI_NOT_STARTED: + mp_raise_msg(&mp_type_OSError, "Wifi Not Started"); + case ESP_ERR_WIFI_CONN: + mp_raise_msg(&mp_type_OSError, "Wifi Internal Error"); + case ESP_ERR_WIFI_SSID: + mp_raise_msg(&mp_type_OSError, "Wifi SSID Invalid"); + case ESP_ERR_WIFI_FAIL: + mp_raise_msg(&mp_type_OSError, "Wifi Internal Failure"); + case ESP_ERR_WIFI_IF: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Interface"); + case ESP_ERR_WIFI_MAC: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid MAC Address"); + case ESP_ERR_WIFI_ARG: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Argument"); + case ESP_ERR_WIFI_MODE: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Mode"); + case ESP_ERR_WIFI_PASSWORD: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Password"); + case ESP_ERR_WIFI_NVS: + mp_raise_msg(&mp_type_OSError, "Wifi Internal NVS Error"); + case ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS: + mp_raise_msg(&mp_type_OSError, "TCP/IP Invalid Parameters"); + case ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY: + mp_raise_msg(&mp_type_OSError, "TCP/IP IF Not Ready"); + case ESP_ERR_TCPIP_ADAPTER_DHCPC_START_FAILED: + mp_raise_msg(&mp_type_OSError, "TCP/IP DHCP Client Start Failed"); + case ESP_ERR_WIFI_TIMEOUT: + mp_raise_OSError(MP_ETIMEDOUT); + case ESP_ERR_TCPIP_ADAPTER_NO_MEM: + case ESP_ERR_WIFI_NO_MEM: + mp_raise_OSError(MP_ENOMEM); + default: + nlr_raise(mp_obj_new_exception_msg_varg( + &mp_type_RuntimeError, "Wifi Unknown Error 0x%04x", e + )); + } +} + +static inline void esp_exceptions(esp_err_t e) { + if (e != ESP_OK) _esp_exceptions(e); +} + +#define ESP_EXCEPTIONS(x) do { esp_exceptions(x); } while (0); + +typedef struct _wlan_if_obj_t { + mp_obj_base_t base; + int if_id; +} wlan_if_obj_t; + +const mp_obj_type_t wlan_if_type; +STATIC const wlan_if_obj_t wlan_sta_obj = {{&wlan_if_type}, WIFI_IF_STA}; +STATIC const wlan_if_obj_t wlan_ap_obj = {{&wlan_if_type}, WIFI_IF_AP}; + +//static wifi_config_t wifi_ap_config = {{{0}}}; +static wifi_config_t wifi_sta_config = {{{0}}}; + +// Set to "true" if the STA interface is requested to be connected by the +// user, used for automatic reassociation. +static bool wifi_sta_connected = false; + +// 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) { + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + ESP_LOGI("wifi", "STA_START"); + break; + case SYSTEM_EVENT_STA_GOT_IP: + ESP_LOGI("wifi", "GOT_IP"); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: { + // This is a workaround as ESP32 WiFi libs don't currently + // auto-reassociate. + system_event_sta_disconnected_t *disconn = &event->event_info.disconnected; + ESP_LOGI("wifi", "STA_DISCONNECTED, reason:%d", disconn->reason); + switch (disconn->reason) { + case WIFI_REASON_AUTH_FAIL: + mp_printf(MP_PYTHON_PRINTER, "authentication failed"); + wifi_sta_connected = false; + break; + default: + // Let other errors through and try to reconnect. + break; + } + if (wifi_sta_connected) { + wifi_mode_t mode; + if (esp_wifi_get_mode(&mode) == ESP_OK) { + if (mode & WIFI_MODE_STA) { + // STA is active so attempt to reconnect. + esp_err_t e = esp_wifi_connect(); + if (e != ESP_OK) { + mp_printf(MP_PYTHON_PRINTER, "error attempting to reconnect: 0x%04x", e); + } + } + } + } + break; + } + default: + ESP_LOGI("wifi", "event %d", event->event_id); + break; + } + return ESP_OK; +} + +/*void error_check(bool status, const char *msg) { + if (!status) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, msg)); + } +} +*/ + +STATIC void require_if(mp_obj_t wlan_if, int if_no) { + wlan_if_obj_t *self = MP_OBJ_TO_PTR(wlan_if); + if (self->if_id != if_no) { + mp_raise_msg(&mp_type_OSError, if_no == WIFI_IF_STA ? "STA required" : "AP required"); + } +} + +STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) { + int idx = (n_args > 0) ? mp_obj_get_int(args[0]) : WIFI_IF_STA; + if (idx == WIFI_IF_STA) { + return MP_OBJ_FROM_PTR(&wlan_sta_obj); + } else if (idx == WIFI_IF_AP) { + return MP_OBJ_FROM_PTR(&wlan_ap_obj); + } else { + mp_raise_ValueError("invalid WLAN interface identifier"); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_wlan_obj, 0, 1, get_wlan); + +STATIC mp_obj_t esp_initialize() { + static int initialized = 0; + if (!initialized) { + ESP_LOGD("modnetwork", "Initializing TCP/IP"); + tcpip_adapter_init(); + ESP_LOGD("modnetwork", "Initializing Event Loop"); + ESP_EXCEPTIONS( esp_event_loop_init(event_handler, NULL) ); + ESP_LOGD("modnetwork", "esp_event_loop_init done"); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_LOGD("modnetwork", "Initializing WiFi"); + ESP_EXCEPTIONS( esp_wifi_init(&cfg) ); + ESP_EXCEPTIONS( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_LOGD("modnetwork", "Initialized"); + ESP_EXCEPTIONS( esp_wifi_set_mode(0) ); + ESP_EXCEPTIONS( esp_wifi_start() ); + ESP_LOGD("modnetwork", "Started"); + initialized = 1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_initialize_obj, esp_initialize); + +#if (WIFI_MODE_STA & WIFI_MODE_AP != WIFI_MODE_NULL || WIFI_MODE_STA | WIFI_MODE_AP != WIFI_MODE_APSTA) +#error WIFI_MODE_STA and WIFI_MODE_AP are supposed to be bitfields! +#endif + +STATIC mp_obj_t esp_active(size_t n_args, const mp_obj_t *args) { + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + wifi_mode_t mode; + ESP_EXCEPTIONS( esp_wifi_get_mode(&mode) ); + int bit = (self->if_id == WIFI_IF_STA) ? WIFI_MODE_STA : WIFI_MODE_AP; + + if (n_args > 1) { + bool active = mp_obj_is_true(args[1]); + mode = active ? (mode | bit) : (mode & ~bit); + ESP_EXCEPTIONS( esp_wifi_set_mode(mode) ); + } + + return (mode & bit) ? mp_const_true : mp_const_false; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_active_obj, 1, 2, esp_active); + +STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *args) { + + mp_uint_t len; + const char *p; + if (n_args > 1) { + memset(&wifi_sta_config, 0, sizeof(wifi_sta_config)); + p = mp_obj_str_get_data(args[1], &len); + memcpy(wifi_sta_config.sta.ssid, p, MIN(len, sizeof(wifi_sta_config.sta.ssid))); + p = (n_args > 2) ? mp_obj_str_get_data(args[2], &len) : ""; + memcpy(wifi_sta_config.sta.password, p, MIN(len, sizeof(wifi_sta_config.sta.password))); + ESP_EXCEPTIONS( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config) ); + } + MP_THREAD_GIL_EXIT(); + ESP_EXCEPTIONS( esp_wifi_connect() ); + MP_THREAD_GIL_ENTER(); + wifi_sta_connected = true; + + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_connect_obj, 1, 7, esp_connect); + +STATIC mp_obj_t esp_disconnect(mp_obj_t self_in) { + wifi_sta_connected = false; + ESP_EXCEPTIONS( esp_wifi_disconnect() ); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_disconnect_obj, esp_disconnect); + +STATIC mp_obj_t esp_status(mp_obj_t self_in) { + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_status_obj, esp_status); + +STATIC mp_obj_t esp_scan(mp_obj_t self_in) { + // check that STA mode is active + wifi_mode_t mode; + ESP_EXCEPTIONS(esp_wifi_get_mode(&mode)); + if ((mode & WIFI_MODE_STA) == 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "STA must be active")); + } + + mp_obj_t list = mp_obj_new_list(0, NULL); + wifi_scan_config_t config = { 0 }; + // XXX how do we scan hidden APs (and if we can scan them, are they really hidden?) + MP_THREAD_GIL_EXIT(); + esp_err_t status = esp_wifi_scan_start(&config, 1); + MP_THREAD_GIL_ENTER(); + if (status == 0) { + uint16_t count = 0; + ESP_EXCEPTIONS( esp_wifi_scan_get_ap_num(&count) ); + wifi_ap_record_t *wifi_ap_records = calloc(count, sizeof(wifi_ap_record_t)); + ESP_EXCEPTIONS( esp_wifi_scan_get_ap_records(&count, wifi_ap_records) ); + for (uint16_t i = 0; i < count; i++) { + mp_obj_tuple_t *t = mp_obj_new_tuple(6, NULL); + uint8_t *x = memchr(wifi_ap_records[i].ssid, 0, sizeof(wifi_ap_records[i].ssid)); + int ssid_len = x ? x - wifi_ap_records[i].ssid : sizeof(wifi_ap_records[i].ssid); + t->items[0] = mp_obj_new_bytes(wifi_ap_records[i].ssid, ssid_len); + t->items[1] = mp_obj_new_bytes(wifi_ap_records[i].bssid, sizeof(wifi_ap_records[i].bssid)); + t->items[2] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].primary); + t->items[3] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].rssi); + t->items[4] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].authmode); + t->items[5] = mp_const_false; // XXX hidden? + mp_obj_list_append(list, MP_OBJ_FROM_PTR(t)); + } + free(wifi_ap_records); + } + return list; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_scan_obj, esp_scan); + +STATIC mp_obj_t esp_isconnected(mp_obj_t self_in) { + wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->if_id == WIFI_IF_STA) { + tcpip_adapter_ip_info_t info; + tcpip_adapter_get_ip_info(WIFI_IF_STA, &info); + return mp_obj_new_bool(info.ip.addr != 0); + } else { + wifi_sta_list_t sta; + esp_wifi_ap_get_sta_list(&sta); + return mp_obj_new_bool(sta.num != 0); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_isconnected_obj, esp_isconnected); + +STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + tcpip_adapter_ip_info_t info; + ip_addr_t dns_addr; + tcpip_adapter_get_ip_info(self->if_id, &info); + if (n_args == 1) { + // get + dns_addr = dns_getserver(0); + mp_obj_t tuple[4] = { + netutils_format_ipv4_addr((uint8_t*)&info.ip, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&info.netmask, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&info.gw, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&dns_addr, NETUTILS_BIG), + }; + return mp_obj_new_tuple(4, tuple); + } else { + // set + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 4, &items); + netutils_parse_ipv4_addr(items[0], (void*)&info.ip, NETUTILS_BIG); + if (mp_obj_is_integer(items[1])) { + // allow numeric netmask, i.e.: + // 24 -> 255.255.255.0 + // 16 -> 255.255.0.0 + // etc... + uint32_t* m = (uint32_t*)&info.netmask; + *m = htonl(0xffffffff << (32 - mp_obj_get_int(items[1]))); + } else { + netutils_parse_ipv4_addr(items[1], (void*)&info.netmask, NETUTILS_BIG); + } + netutils_parse_ipv4_addr(items[2], (void*)&info.gw, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[3], (void*)&dns_addr, NETUTILS_BIG); + // To set a static IP we have to disable DHCP first + if (self->if_id == WIFI_IF_STA) { + esp_err_t e = tcpip_adapter_dhcpc_stop(WIFI_IF_STA); + if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); + ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_STA, &info)); + } else if (self->if_id == WIFI_IF_AP) { + esp_err_t e = tcpip_adapter_dhcps_stop(WIFI_IF_AP); + if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); + ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_AP, &info)); + ESP_EXCEPTIONS(tcpip_adapter_dhcps_start(WIFI_IF_AP)); + } + return mp_const_none; + } +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj, 1, 2, esp_ifconfig); + +STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (n_args != 1 && kwargs->used != 0) { + mp_raise_TypeError("either pos or kw args are allowed"); + } + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + // get the config for the interface + wifi_config_t cfg; + ESP_EXCEPTIONS(esp_wifi_get_config(self->if_id, &cfg)); + + if (kwargs->used != 0) { + + 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; + mp_get_buffer_raise(kwargs->table[i].value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != 6) { + mp_raise_ValueError("invalid buffer length"); + } + ESP_EXCEPTIONS(esp_wifi_set_mac(self->if_id, bufinfo.buf)); + break; + } + case QS(MP_QSTR_essid): { + req_if = WIFI_IF_AP; + mp_uint_t len; + const char *s = mp_obj_str_get_data(kwargs->table[i].value, &len); + len = MIN(len, sizeof(cfg.ap.ssid)); + memcpy(cfg.ap.ssid, s, len); + cfg.ap.ssid_len = len; + break; + } + case QS(MP_QSTR_hidden): { + req_if = WIFI_IF_AP; + cfg.ap.ssid_hidden = mp_obj_is_true(kwargs->table[i].value); + break; + } + case QS(MP_QSTR_authmode): { + req_if = WIFI_IF_AP; + cfg.ap.authmode = mp_obj_get_int(kwargs->table[i].value); + break; + } + case QS(MP_QSTR_password): { + req_if = WIFI_IF_AP; + mp_uint_t len; + const char *s = mp_obj_str_get_data(kwargs->table[i].value, &len); + len = MIN(len, sizeof(cfg.ap.password) - 1); + memcpy(cfg.ap.password, s, len); + cfg.ap.password[len] = 0; + break; + } + case QS(MP_QSTR_channel): { + req_if = WIFI_IF_AP; + cfg.ap.channel = mp_obj_get_int(kwargs->table[i].value); + break; + } + default: + goto unknown; + } + #undef QS + + // We post-check interface requirements to save on code size + if (req_if >= 0) { + require_if(args[0], req_if); + } + } + } + + ESP_EXCEPTIONS(esp_wifi_set_config(self->if_id, &cfg)); + + return mp_const_none; + } + + // Get config + + if (n_args != 2) { + mp_raise_TypeError("can query only one param"); + } + + int req_if = -1; + mp_obj_t val; + + #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)); + } + case QS(MP_QSTR_essid): + req_if = WIFI_IF_AP; + val = mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len); + break; + case QS(MP_QSTR_hidden): + req_if = WIFI_IF_AP; + val = mp_obj_new_bool(cfg.ap.ssid_hidden); + break; + case QS(MP_QSTR_authmode): + req_if = WIFI_IF_AP; + val = MP_OBJ_NEW_SMALL_INT(cfg.ap.authmode); + break; + case QS(MP_QSTR_channel): + req_if = WIFI_IF_AP; + val = MP_OBJ_NEW_SMALL_INT(cfg.ap.channel); + break; + default: + goto unknown; + } + #undef QS + + // We post-check interface requirements to save on code size + if (req_if >= 0) { + require_if(args[0], req_if); + } + + return val; + +unknown: + mp_raise_ValueError("unknown config param"); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_config_obj, 1, esp_config); + +STATIC const mp_map_elem_t wlan_if_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_active), (mp_obj_t)&esp_active_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&esp_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&esp_disconnect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_status), (mp_obj_t)&esp_status_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_scan), (mp_obj_t)&esp_scan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&esp_isconnected_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_config), (mp_obj_t)&esp_config_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&esp_ifconfig_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); + +const mp_obj_type_t wlan_if_type = { + { &mp_type_type }, + .name = MP_QSTR_WLAN, + .locals_dict = (mp_obj_t)&wlan_if_locals_dict, +}; + +STATIC mp_obj_t esp_phy_mode(size_t n_args, const mp_obj_t *args) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_phy_mode_obj, 0, 1, esp_phy_mode); + + +STATIC const mp_map_elem_t mp_module_network_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_network) }, + { MP_OBJ_NEW_QSTR(MP_QSTR___init__), (mp_obj_t)&esp_initialize_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WLAN), (mp_obj_t)&get_wlan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_phy_mode), (mp_obj_t)&esp_phy_mode_obj }, + +#if MODNETWORK_INCLUDE_CONSTANTS + { MP_OBJ_NEW_QSTR(MP_QSTR_STA_IF), + MP_OBJ_NEW_SMALL_INT(WIFI_IF_STA)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_AP_IF), + MP_OBJ_NEW_SMALL_INT(WIFI_IF_AP)}, + + { MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11B), + MP_OBJ_NEW_SMALL_INT(WIFI_PROTOCOL_11B) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11G), + MP_OBJ_NEW_SMALL_INT(WIFI_PROTOCOL_11G) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11N), + MP_OBJ_NEW_SMALL_INT(WIFI_PROTOCOL_11N) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_OPEN), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_OPEN) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WEP), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WEP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WPA_PSK), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WPA_PSK) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WPA2_PSK), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WPA2_PSK) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WPA_WPA2_PSK), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WPA_WPA2_PSK) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_MAX), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_MAX) }, +#endif +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_network_globals, mp_module_network_globals_table); + +const mp_obj_module_t mp_module_network = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_network_globals, +}; diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c new file mode 100644 index 000000000..6ba61e874 --- /dev/null +++ b/ports/esp32/modsocket.c @@ -0,0 +1,570 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * and Mnemote Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Nick Moore @mnemote + * + * Based on extmod/modlwip.c + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Galen Hazelwood + * + * 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 +#include +#include + +#include "py/runtime0.h" +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "lib/netutils/netutils.h" +#include "tcpip_adapter.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#include "lwip/ip4.h" +#include "esp_log.h" + +#define SOCKET_POLL_US (100000) + +typedef struct _socket_obj_t { + mp_obj_base_t base; + int fd; + uint8_t domain; + uint8_t type; + uint8_t proto; + unsigned int retries; +} socket_obj_t; + +void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms); + +NORETURN static void exception_from_errno(int _errno) { + // Here we need to convert from lwip errno values to MicroPython's standard ones + if (_errno == EINPROGRESS) { + _errno = MP_EINPROGRESS; + } + mp_raise_OSError(_errno); +} + +void check_for_exceptions() { + mp_obj_t exc = MP_STATE_VM(mp_pending_exception); + if (exc != MP_OBJ_NULL) { + MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + nlr_raise(exc); + } +} + +STATIC mp_obj_t socket_close(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (self->fd >= 0) { + int ret = lwip_close_r(self->fd); + if (ret != 0) { + exception_from_errno(errno); + } + self->fd = -1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); + +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, + .ai_socktype = SOCK_STREAM, + }; + + mp_obj_t port = portx; + if (MP_OBJ_IS_SMALL_INT(port)) { + // This is perverse, because lwip_getaddrinfo promptly converts it back to an int, but + // that's the API we have to work with ... + port = mp_obj_str_binary_op(MP_BINARY_OP_MODULO, mp_obj_new_str_via_qstr("%s", 2), port); + } + + const char *host_str = mp_obj_str_get_str(host); + const char *port_str = mp_obj_str_get_str(port); + + if (host_str[0] == '\0') { + // a host of "" is equivalent to the default/all-local IP address + host_str = "0.0.0.0"; + } + + MP_THREAD_GIL_EXIT(); + int res = lwip_getaddrinfo(host_str, port_str, &hints, resp); + MP_THREAD_GIL_ENTER(); + + return res; +} + +int _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) { + mp_uint_t len = 0; + mp_obj_t *elem; + mp_obj_get_array(addrtuple, &len, &elem); + if (len != 2) return -1; + return _socket_getaddrinfo2(elem[0], elem[1], resp); +} + +STATIC mp_obj_t socket_bind(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + struct addrinfo *res; + _socket_getaddrinfo(arg1, &res); + int r = lwip_bind_r(self->fd, res->ai_addr, res->ai_addrlen); + lwip_freeaddrinfo(res); + if (r < 0) exception_from_errno(errno); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); + +STATIC mp_obj_t socket_listen(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + int backlog = mp_obj_get_int(arg1); + int r = lwip_listen_r(self->fd, backlog); + if (r < 0) exception_from_errno(errno); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen); + +STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + + struct sockaddr addr; + socklen_t addr_len = sizeof(addr); + + int new_fd = -1; + for (int i=0; i<=self->retries; i++) { + MP_THREAD_GIL_EXIT(); + new_fd = lwip_accept_r(self->fd, &addr, &addr_len); + MP_THREAD_GIL_ENTER(); + if (new_fd >= 0) break; + if (errno != EAGAIN) exception_from_errno(errno); + check_for_exceptions(); + } + if (new_fd < 0) mp_raise_OSError(MP_ETIMEDOUT); + + // create new socket object + socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); + sock->base.type = self->base.type; + sock->fd = new_fd; + sock->domain = self->domain; + sock->type = self->type; + sock->proto = self->proto; + _socket_settimeout(sock, UINT64_MAX); + + // make the return value + uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&addr)->sin_addr; + mp_uint_t port = lwip_ntohs(((struct sockaddr_in*)&addr)->sin_port); + mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL); + client->items[0] = sock; + client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + + return client; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); + +STATIC mp_obj_t socket_connect(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + struct addrinfo *res; + _socket_getaddrinfo(arg1, &res); + MP_THREAD_GIL_EXIT(); + int r = lwip_connect_r(self->fd, res->ai_addr, res->ai_addrlen); + MP_THREAD_GIL_ENTER(); + lwip_freeaddrinfo(res); + if (r != 0) { + exception_from_errno(errno); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); + +STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { + (void)n_args; // always 4 + socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + int level = mp_obj_get_int(args[1]); + if (level != SOL_SOCKET) { + mp_raise_ValueError("unsupported level"); + } + + // only "int" arguments are supported at the moment + int opt = mp_obj_get_int(args[2]); + int val = mp_obj_get_int(args[3]); + int ret = lwip_setsockopt_r(self->fd, SOL_SOCKET, opt, &val, sizeof(int)); + + if (ret != 0) { + exception_from_errno(errno); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt); + +void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms) { + // Rather than waiting for the entire timeout specified, we wait sock->retries times + // for SOCKET_POLL_US each, checking for a MicroPython interrupt between timeouts. + // with SOCKET_POLL_MS == 100ms, sock->retries allows for timeouts up to 13 years. + // if timeout_ms == UINT64_MAX, wait forever. + sock->retries = (timeout_ms == UINT64_MAX) ? UINT_MAX : timeout_ms * 1000 / SOCKET_POLL_US; + + struct timeval timeout = { + .tv_sec = 0, + .tv_usec = timeout_ms ? SOCKET_POLL_US : 0 + }; + lwip_setsockopt_r(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&timeout, sizeof(timeout)); + lwip_setsockopt_r(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout)); + lwip_fcntl_r(sock->fd, F_SETFL, timeout_ms ? 0 : O_NONBLOCK); +} + +STATIC mp_obj_t socket_settimeout(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (arg1 == mp_const_none) _socket_settimeout(self, UINT64_MAX); + else _socket_settimeout(self, mp_obj_get_float(arg1) * 1000L); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout); + +STATIC mp_obj_t socket_setblocking(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (mp_obj_is_true(arg1)) _socket_settimeout(self, UINT64_MAX); + else _socket_settimeout(self, 0); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); + +mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, + struct sockaddr *from, socklen_t *from_len) { + socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); + size_t len = mp_obj_get_int(len_in); + vstr_t vstr; + vstr_init_len(&vstr, len); + + // XXX Would be nicer to use RTC to handle timeouts + for (int i=0; i<=sock->retries; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_recvfrom_r(sock->fd, vstr.buf, len, 0, from, from_len); + MP_THREAD_GIL_ENTER(); + if (r >= 0) { vstr.len = r; return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } + if (errno != EWOULDBLOCK) exception_from_errno(errno); + check_for_exceptions(); + } + mp_raise_OSError(MP_ETIMEDOUT); +} + +STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { + return _socket_recvfrom(self_in, len_in, NULL, NULL); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); + +STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { + struct sockaddr from; + socklen_t fromlen = sizeof(from); + + mp_obj_t tuple[2]; + tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); + + uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&from)->sin_addr; + mp_uint_t port = lwip_ntohs(((struct sockaddr_in*)&from)->sin_port); + tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); + +int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { + int sentlen = 0; + for (int i=0; i<=sock->retries && sentlen < datalen; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_write_r(sock->fd, data+sentlen, datalen-sentlen); + MP_THREAD_GIL_ENTER(); + if (r < 0 && errno != EWOULDBLOCK) exception_from_errno(errno); + if (r > 0) sentlen += r; + check_for_exceptions(); + } + if (sentlen == 0) mp_raise_OSError(MP_ETIMEDOUT); + return sentlen; +} + +STATIC mp_obj_t socket_send(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); + mp_uint_t datalen; + const char *data = mp_obj_str_get_data(arg1, &datalen); + int r = _socket_send(sock, data, datalen); + return mp_obj_new_int(r); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send); + +STATIC mp_obj_t socket_sendall(const mp_obj_t arg0, const mp_obj_t arg1) { + // XXX behaviour when nonblocking (see extmod/modlwip.c) + // XXX also timeout behaviour. + socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg1, &bufinfo, MP_BUFFER_READ); + int r = _socket_send(sock, bufinfo.buf, bufinfo.len); + if (r < bufinfo.len) mp_raise_OSError(MP_ETIMEDOUT); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_sendall_obj, socket_sendall); + +STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { + socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // get the buffer to send + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); + + // create the destination address + struct sockaddr_in to; + to.sin_len = sizeof(to); + to.sin_family = AF_INET; + to.sin_port = lwip_htons(netutils_parse_inet_addr(addr_in, (uint8_t*)&to.sin_addr, NETUTILS_BIG)); + + // send the data + for (int i=0; i<=self->retries; i++) { + MP_THREAD_GIL_EXIT(); + int ret = lwip_sendto_r(self->fd, bufinfo.buf, bufinfo.len, 0, (struct sockaddr*)&to, sizeof(to)); + MP_THREAD_GIL_ENTER(); + if (ret > 0) return mp_obj_new_int_from_uint(ret); + if (ret == -1 && errno != EWOULDBLOCK) { + exception_from_errno(errno); + } + check_for_exceptions(); + } + mp_raise_OSError(MP_ETIMEDOUT); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(socket_sendto_obj, socket_sendto); + +STATIC mp_obj_t socket_fileno(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + return mp_obj_new_int(self->fd); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_fileno_obj, socket_fileno); + +STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { + (void)n_args; + return args[0]; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); + + +// XXX this can end up waiting a very long time if the content is dribbled in one character +// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not +// good behaviour. + +STATIC mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { + socket_obj_t *sock = self_in; + + // XXX Would be nicer to use RTC to handle timeouts + for (int i=0; i<=sock->retries; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_recvfrom_r(sock->fd, buf, size, 0, NULL, NULL); + MP_THREAD_GIL_ENTER(); + if (r >= 0) return r; + if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } + check_for_exceptions(); + } + *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; + return MP_STREAM_ERROR; +} + +STATIC mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { + socket_obj_t *sock = self_in; + for (int i=0; i<=sock->retries; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_write_r(sock->fd, buf, size); + MP_THREAD_GIL_ENTER(); + if (r > 0) return r; + if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } + check_for_exceptions(); + } + *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; + return MP_STREAM_ERROR; +} + +STATIC mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + socket_obj_t * socket = self_in; + if (request == MP_STREAM_POLL) { + + fd_set rfds; FD_ZERO(&rfds); + fd_set wfds; FD_ZERO(&wfds); + fd_set efds; FD_ZERO(&efds); + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; + if (arg & MP_STREAM_POLL_RD) FD_SET(socket->fd, &rfds); + if (arg & MP_STREAM_POLL_WR) FD_SET(socket->fd, &wfds); + if (arg & MP_STREAM_POLL_HUP) FD_SET(socket->fd, &efds); + + int r = select((socket->fd)+1, &rfds, &wfds, &efds, &timeout); + if (r < 0) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + + mp_uint_t ret = 0; + if (FD_ISSET(socket->fd, &rfds)) ret |= MP_STREAM_POLL_RD; + if (FD_ISSET(socket->fd, &wfds)) ret |= MP_STREAM_POLL_WR; + if (FD_ISSET(socket->fd, &efds)) ret |= MP_STREAM_POLL_HUP; + return ret; + } + + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; +} + +STATIC const mp_map_elem_t socket_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&socket_bind_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&socket_listen_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&socket_accept_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&socket_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&socket_send_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sendall), (mp_obj_t)&socket_sendall_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&socket_sendto_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&socket_recv_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&socket_recvfrom_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&socket_setsockopt_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_settimeout), (mp_obj_t)&socket_settimeout_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setblocking), (mp_obj_t)&socket_setblocking_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_makefile), (mp_obj_t)&socket_makefile_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_fileno), (mp_obj_t)&socket_fileno_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); + +STATIC const mp_stream_p_t socket_stream_p = { + .read = socket_stream_read, + .write = socket_stream_write, + .ioctl = socket_stream_ioctl +}; + +STATIC const mp_obj_type_t socket_type = { + { &mp_type_type }, + .name = MP_QSTR_socket, + .protocol = &socket_stream_p, + .locals_dict = (mp_obj_t)&socket_locals_dict, +}; + +STATIC mp_obj_t get_socket(size_t n_args, const mp_obj_t *args) { + socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); + sock->base.type = &socket_type; + sock->domain = AF_INET; + sock->type = SOCK_STREAM; + sock->proto = 0; + if (n_args > 0) { + sock->domain = mp_obj_get_int(args[0]); + if (n_args > 1) { + sock->type = mp_obj_get_int(args[1]); + if (n_args > 2) { + sock->proto = mp_obj_get_int(args[2]); + } + } + } + + sock->fd = lwip_socket(sock->domain, sock->type, sock->proto); + if (sock->fd < 0) { + exception_from_errno(errno); + } + _socket_settimeout(sock, UINT64_MAX); + + return MP_OBJ_FROM_PTR(sock); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_socket_obj, 0, 3, get_socket); + +STATIC mp_obj_t esp_socket_getaddrinfo(const mp_obj_t host, const mp_obj_t port) { + struct addrinfo *res = NULL; + _socket_getaddrinfo2(host, port, &res); + mp_obj_t ret_list = mp_obj_new_list(0, NULL); + + for (struct addrinfo *resi = res; resi; resi = resi->ai_next) { + mp_obj_t addrinfo_objs[5] = { + mp_obj_new_int(resi->ai_family), + mp_obj_new_int(resi->ai_socktype), + mp_obj_new_int(resi->ai_protocol), + mp_obj_new_str(resi->ai_canonname, strlen(resi->ai_canonname)), + mp_const_none + }; + + if (resi->ai_family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *)resi->ai_addr; + // This looks odd, but it's really just a u32_t + ip4_addr_t ip4_addr = { .addr = addr->sin_addr.s_addr }; + char buf[16]; + ip4addr_ntoa_r(&ip4_addr, buf, sizeof(buf)); + mp_obj_t inaddr_objs[2] = { + mp_obj_new_str(buf, strlen(buf)), + mp_obj_new_int(ntohs(addr->sin_port)) + }; + addrinfo_objs[4] = mp_obj_new_tuple(2, inaddr_objs); + } + mp_obj_list_append(ret_list, mp_obj_new_tuple(5, addrinfo_objs)); + } + + if (res) lwip_freeaddrinfo(res); + return ret_list; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_getaddrinfo_obj, esp_socket_getaddrinfo); + +STATIC mp_obj_t esp_socket_initialize() { + static int initialized = 0; + if (!initialized) { + ESP_LOGI("modsocket", "Initializing"); + tcpip_adapter_init(); + initialized = 1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_socket_initialize_obj, esp_socket_initialize); + +STATIC const mp_map_elem_t mp_module_socket_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usocket) }, + { MP_OBJ_NEW_QSTR(MP_QSTR___init__), (mp_obj_t)&esp_socket_initialize_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&get_socket_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&esp_socket_getaddrinfo_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET), MP_OBJ_NEW_SMALL_INT(AF_INET) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET6), MP_OBJ_NEW_SMALL_INT(AF_INET6) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_STREAM), MP_OBJ_NEW_SMALL_INT(SOCK_STREAM) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_DGRAM), MP_OBJ_NEW_SMALL_INT(SOCK_DGRAM) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_RAW), MP_OBJ_NEW_SMALL_INT(SOCK_RAW) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_TCP), MP_OBJ_NEW_SMALL_INT(IPPROTO_TCP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_UDP), MP_OBJ_NEW_SMALL_INT(IPPROTO_UDP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOL_SOCKET), MP_OBJ_NEW_SMALL_INT(SOL_SOCKET) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SO_REUSEADDR), MP_OBJ_NEW_SMALL_INT(SO_REUSEADDR) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); + +const mp_obj_module_t mp_module_usocket = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_socket_globals, +}; diff --git a/ports/esp32/moduhashlib.c b/ports/esp32/moduhashlib.c new file mode 100644 index 000000000..6f67aa7d9 --- /dev/null +++ b/ports/esp32/moduhashlib.c @@ -0,0 +1,142 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Paul Sokolovsky + * + * 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 "mbedtls/sha256.h" +#include "mbedtls/sha1.h" + +union sha_ctxs { + mbedtls_sha256_context sha256; + mbedtls_sha1_context sha1; +}; +typedef struct _mp_obj_hash_t { + mp_obj_base_t base; + union sha_ctxs state; +} mp_obj_hash_t; + +STATIC mp_obj_t sha256_update(mp_obj_t self_in, mp_obj_t arg); +STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg); + +STATIC mp_obj_t sha256_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(union sha_ctxs)); + o->base.type = type; + mbedtls_sha256_init(&o->state.sha256); + if (n_args == 1) { + sha256_update(MP_OBJ_FROM_PTR(o), args[0]); + } + return MP_OBJ_FROM_PTR(o); +} + +STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(union sha_ctxs)); + o->base.type = type; + mbedtls_sha1_init(&o->state.sha1); + if (n_args == 1) { + sha1_update(MP_OBJ_FROM_PTR(o), args[0]); + } + return MP_OBJ_FROM_PTR(o); +} + +STATIC mp_obj_t sha256_update(mp_obj_t self_in, mp_obj_t arg) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); + mbedtls_sha256_update(&self->state.sha256, bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(sha256_update_obj, sha256_update); + +STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); + mbedtls_sha1_update(&self->state.sha1, bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(sha1_update_obj, sha1_update); + +STATIC mp_obj_t sha256_digest(mp_obj_t self_in) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + vstr_t vstr; + vstr_init_len(&vstr, 32); + mbedtls_sha256_finish(&self->state.sha256, (unsigned char *)vstr.buf); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_1(sha256_digest_obj, sha256_digest); + +STATIC mp_obj_t sha1_digest(mp_obj_t self_in) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + vstr_t vstr; + vstr_init_len(&vstr, 20); + mbedtls_sha1_finish(&self->state.sha1, (unsigned char *)vstr.buf); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_1(sha1_digest_obj, sha1_digest); + +STATIC const mp_rom_map_elem_t sha256_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&sha256_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&sha256_digest_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(sha256_locals_dict, sha256_locals_dict_table); + +STATIC const mp_obj_type_t sha256_type = { + { &mp_type_type }, + .name = MP_QSTR_sha256, + .make_new = sha256_make_new, + .locals_dict = (void*)&sha256_locals_dict, +}; + +STATIC const mp_rom_map_elem_t sha1_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&sha1_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&sha1_digest_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(sha1_locals_dict, sha1_locals_dict_table); + +STATIC const mp_obj_type_t sha1_type = { + { &mp_type_type }, + .name = MP_QSTR_sha1, + .make_new = sha1_make_new, + .locals_dict = (void*)&sha1_locals_dict, +}; + +STATIC const mp_rom_map_elem_t mp_module_hashlib_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uhashlib) }, + { MP_ROM_QSTR(MP_QSTR_sha256), MP_ROM_PTR(&sha256_type) }, + { MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&sha1_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_hashlib_globals, + mp_module_hashlib_globals_table); + +const mp_obj_module_t mp_module_uhashlib = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_hashlib_globals, +}; diff --git a/ports/esp32/modules/_boot.py b/ports/esp32/modules/_boot.py new file mode 100644 index 000000000..bf40ea3a9 --- /dev/null +++ b/ports/esp32/modules/_boot.py @@ -0,0 +1,12 @@ +import gc +import uos +from flashbdev import bdev + +try: + if bdev: + uos.mount(bdev, '/') +except OSError: + import inisetup + vfs = inisetup.setup() + +gc.collect() diff --git a/ports/esp32/modules/apa106.py b/ports/esp32/modules/apa106.py new file mode 100644 index 000000000..ef971d78b --- /dev/null +++ b/ports/esp32/modules/apa106.py @@ -0,0 +1,8 @@ +# APA106driver for MicroPython on ESP32 +# MIT license; Copyright (c) 2016 Damien P. George + +from neopixel import NeoPixel + + +class APA106(NeoPixel): + ORDER = (0, 1, 2, 3) diff --git a/ports/esp32/modules/dht.py b/ports/esp32/modules/dht.py new file mode 120000 index 000000000..b6acf2853 --- /dev/null +++ b/ports/esp32/modules/dht.py @@ -0,0 +1 @@ +../../esp8266/modules/dht.py \ No newline at end of file diff --git a/ports/esp32/modules/ds18x20.py b/ports/esp32/modules/ds18x20.py new file mode 120000 index 000000000..9721929a3 --- /dev/null +++ b/ports/esp32/modules/ds18x20.py @@ -0,0 +1 @@ +../../esp8266/modules/ds18x20.py \ No newline at end of file diff --git a/ports/esp32/modules/flashbdev.py b/ports/esp32/modules/flashbdev.py new file mode 100644 index 000000000..935f5342f --- /dev/null +++ b/ports/esp32/modules/flashbdev.py @@ -0,0 +1,34 @@ +import esp + +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) diff --git a/ports/esp32/modules/inisetup.py b/ports/esp32/modules/inisetup.py new file mode 100644 index 000000000..45bce82cc --- /dev/null +++ b/ports/esp32/modules/inisetup.py @@ -0,0 +1,38 @@ +import uos +from flashbdev import bdev + +def check_bootsec(): + buf = bytearray(bdev.SEC_SIZE) + bdev.readblocks(0, buf) + empty = True + for b in buf: + if b != 0xff: + empty = False + break + if empty: + return True + fs_corrupted() + +def fs_corrupted(): + import time + while 1: + print("""\ +FAT filesystem appears to be corrupted. If you had important data there, you +may want to make a flash snapshot to try to recover it. Otherwise, perform +factory reprogramming of MicroPython firmware (completely erase flash, followed +by firmware programming). +""") + time.sleep(3) + +def setup(): + check_bootsec() + print("Performing initial setup") + uos.VfsFat.mkfs(bdev) + vfs = uos.VfsFat(bdev) + uos.mount(vfs, '/flash') + uos.chdir('/flash') + with open("boot.py", "w") as f: + f.write("""\ +# This file is executed on every boot (including wake-boot from deepsleep) +""") + return vfs diff --git a/ports/esp32/modules/neopixel.py b/ports/esp32/modules/neopixel.py new file mode 100644 index 000000000..86c1586cd --- /dev/null +++ b/ports/esp32/modules/neopixel.py @@ -0,0 +1,33 @@ +# NeoPixel driver for MicroPython on ESP32 +# MIT license; Copyright (c) 2016 Damien P. George + +from esp import neopixel_write + + +class NeoPixel: + ORDER = (1, 0, 2, 3) + + def __init__(self, pin, n, bpp=3, timing=0): + self.pin = pin + self.n = n + self.bpp = bpp + self.buf = bytearray(n * bpp) + self.pin.init(pin.OUT) + self.timing = timing + + def __setitem__(self, index, val): + offset = index * self.bpp + for i in range(self.bpp): + self.buf[offset + self.ORDER[i]] = val[i] + + def __getitem__(self, index): + offset = index * self.bpp + return tuple(self.buf[offset + self.ORDER[i]] + for i in range(self.bpp)) + + def fill(self, color): + for i in range(self.n): + self[i] = color + + def write(self): + neopixel_write(self.pin, self.buf, self.timing) diff --git a/ports/esp32/modules/onewire.py b/ports/esp32/modules/onewire.py new file mode 120000 index 000000000..091629488 --- /dev/null +++ b/ports/esp32/modules/onewire.py @@ -0,0 +1 @@ +../../esp8266/modules/onewire.py \ No newline at end of file diff --git a/ports/esp32/modules/upip.py b/ports/esp32/modules/upip.py new file mode 120000 index 000000000..130eb6901 --- /dev/null +++ b/ports/esp32/modules/upip.py @@ -0,0 +1 @@ +../../../tools/upip.py \ No newline at end of file diff --git a/ports/esp32/modules/upip_utarfile.py b/ports/esp32/modules/upip_utarfile.py new file mode 120000 index 000000000..d9653d6a6 --- /dev/null +++ b/ports/esp32/modules/upip_utarfile.py @@ -0,0 +1 @@ +../../../tools/upip_utarfile.py \ No newline at end of file diff --git a/ports/esp32/modules/upysh.py b/ports/esp32/modules/upysh.py new file mode 120000 index 000000000..12d100c29 --- /dev/null +++ b/ports/esp32/modules/upysh.py @@ -0,0 +1 @@ +../../../../micropython-lib/upysh/upysh.py \ No newline at end of file diff --git a/ports/esp32/modules/urequests.py b/ports/esp32/modules/urequests.py new file mode 120000 index 000000000..76661112e --- /dev/null +++ b/ports/esp32/modules/urequests.py @@ -0,0 +1 @@ +../../../../micropython-lib/urequests/urequests.py \ No newline at end of file diff --git a/ports/esp32/moduos.c b/ports/esp32/moduos.c new file mode 100644 index 000000000..9f0e291a6 --- /dev/null +++ b/ports/esp32/moduos.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Josef Gajdusek + * Copyright (c) 2016 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 "esp_system.h" + +#include "py/mpconfig.h" +#include "py/obj.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "genhdr/mpversion.h" + +extern const mp_obj_type_t mp_fat_vfs_type; + +STATIC const qstr os_uname_info_fields[] = { + MP_QSTR_sysname, MP_QSTR_nodename, + MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine +}; +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); + +STATIC MP_DEFINE_ATTRTUPLE( + os_uname_info_obj, + os_uname_info_fields, + 5, + (mp_obj_t)&os_uname_info_sysname_obj, + (mp_obj_t)&os_uname_info_nodename_obj, + (mp_obj_t)&os_uname_info_release_obj, + (mp_obj_t)&os_uname_info_version_obj, + (mp_obj_t)&os_uname_info_machine_obj +); + +STATIC mp_obj_t os_uname(void) { + return (mp_obj_t)&os_uname_info_obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); + +STATIC mp_obj_t os_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + uint32_t r = 0; + for (int i = 0; i < n; i++) { + if ((i & 3) == 0) { + r = esp_random(); // returns 32-bit hardware random number + } + vstr.buf[i] = r; + r >>= 8; + } + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); + +#if MICROPY_PY_OS_DUPTERM +STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { + (void)obj_in; + mp_hal_signal_dupterm_input(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_dupterm_notify_obj, os_dupterm_notify); +#endif + +STATIC const mp_rom_map_elem_t os_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, + { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, + #if MICROPY_PY_OS_DUPTERM + { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&os_dupterm_notify_obj) }, + #endif + #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, + #if MICROPY_VFS_FAT + { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, + #endif + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t uos_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&os_module_globals, +}; diff --git a/ports/esp32/modutime.c b/ports/esp32/modutime.c new file mode 100644 index 000000000..f037faad9 --- /dev/null +++ b/ports/esp32/modutime.c @@ -0,0 +1,61 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 +#include + +#include "extmod/utime_mphal.h" + +STATIC mp_obj_t time_time(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return mp_obj_new_int(tv.tv_sec); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); + +STATIC const mp_rom_map_elem_t time_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, + + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t utime_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&time_module_globals, +}; diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h new file mode 100644 index 000000000..2c3259324 --- /dev/null +++ b/ports/esp32/mpconfigport.h @@ -0,0 +1,248 @@ +// Options to control how MicroPython is built for this port, +// overriding defaults in py/mpconfig.h. + +#include +#include +#include "rom/ets_sys.h" + +// object representation and NLR handling +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) +#define MICROPY_NLR_SETJMP (1) + +// memory allocation policies +#define MICROPY_ALLOC_PATH_MAX (128) + +// emitters +#define MICROPY_PERSISTENT_CODE_LOAD (1) + +// compiler configuration +#define MICROPY_COMP_MODULE_CONST (1) +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) + +// optimisations +#define MICROPY_OPT_COMPUTED_GOTO (1) +#define MICROPY_OPT_MPZ_BITWISE (1) + +// Python internal features +#define MICROPY_READER_VFS (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_STACK_CHECK (1) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_REPL_EMACS_KEYS (1) +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) +#define MICROPY_WARNINGS (1) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_PY_BUILTINS_COMPLEX (1) +#define MICROPY_CPYTHON_COMPAT (1) +#define MICROPY_STREAMS_NON_BLOCK (1) +#define MICROPY_STREAMS_POSIX_API (1) +#define MICROPY_MODULE_BUILTIN_INIT (1) +#define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_MODULE_FROZEN_STR (0) +#define MICROPY_MODULE_FROZEN_MPY (1) +#define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool +#define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_SCHEDULER_DEPTH (8) +#define MICROPY_VFS (1) +#define MICROPY_VFS_FAT (1) + +// control over Python builtins +#define MICROPY_PY_FUNCTION_ATTRS (1) +#define MICROPY_PY_STR_BYTES_CMP_WARN (1) +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) +#define MICROPY_PY_BUILTINS_STR_CENTER (1) +#define MICROPY_PY_BUILTINS_STR_PARTITION (1) +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_RANGE_ATTRS (1) +#define MICROPY_PY_BUILTINS_TIMEOUTERROR (1) +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_BUILTINS_COMPILE (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_EXECFILE (1) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY_BUILTINS_POW3 (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_TEXT esp32_help_text +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY___FILE__ (1) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_ARRAY (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#define MICROPY_PY_ATTRTUPLE (1) +#define MICROPY_PY_COLLECTIONS (1) +#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) +#define MICROPY_PY_MATH (1) +#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_GC (1) +#define MICROPY_PY_IO (1) +#define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_IO_BYTESIO (1) +#define MICROPY_PY_IO_BUFFEREDWRITER (1) +#define MICROPY_PY_STRUCT (1) +#define MICROPY_PY_SYS (1) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_PY_SYS_MODULES (1) +#define MICROPY_PY_SYS_EXIT (1) +#define MICROPY_PY_SYS_STDFILES (1) +#define MICROPY_PY_SYS_STDIO_BUFFER (1) +#define MICROPY_PY_UERRNO (1) +#define MICROPY_PY_USELECT (1) +#define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_THREAD (1) +#define MICROPY_PY_THREAD_GIL (1) +#define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32) + +// extended modules +#define MICROPY_PY_UCTYPES (1) +#define MICROPY_PY_UZLIB (1) +#define MICROPY_PY_UJSON (1) +#define MICROPY_PY_URE (1) +#define MICROPY_PY_UHEAPQ (1) +#define MICROPY_PY_UTIMEQ (1) +#define MICROPY_PY_UHASHLIB (0) // We use the ESP32 version +#define MICROPY_PY_UHASHLIB_SHA1 (MICROPY_PY_USSL && MICROPY_SSL_AXTLS) +#define MICROPY_PY_UBINASCII (1) +#define MICROPY_PY_UBINASCII_CRC32 (1) +#define MICROPY_PY_URANDOM (1) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SPI (1) +#define MICROPY_PY_MACHINE_SPI_MSB (0) +#define MICROPY_PY_MACHINE_SPI_LSB (1) +#define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hw_spi_make_new +#define MICROPY_PY_MACHINE_SPI_MIN_DELAY (0) +#define MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE (ets_get_cpu_frequency() * 1000000 / 200) // roughly +#define MICROPY_PY_USSL (1) +#define MICROPY_SSL_MBEDTLS (1) +#define MICROPY_PY_WEBSOCKET (0) +#define MICROPY_PY_FRAMEBUF (1) + +// fatfs configuration +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_RPATH (2) +#define MICROPY_FATFS_MAX_SS (4096) +#define MICROPY_FATFS_LFN_CODE_PAGE (437) /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +#define mp_type_fileio fatfs_type_fileio +#define mp_type_textio fatfs_type_textio + +// use vfs's functions for import stat and builtin open +#define mp_import_stat mp_vfs_import_stat +#define mp_builtin_open mp_vfs_open +#define mp_builtin_open_obj mp_vfs_open_obj + +// extra built in names to add to the global namespace +#define MICROPY_PORT_BUILTINS \ + { MP_OBJ_NEW_QSTR(MP_QSTR_input), (mp_obj_t)&mp_builtin_input_obj }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj }, + +// extra built in modules to add to the list of known ones +extern const struct _mp_obj_module_t esp_module; +extern const struct _mp_obj_module_t utime_module; +extern const struct _mp_obj_module_t uos_module; +extern const struct _mp_obj_module_t mp_module_usocket; +extern const struct _mp_obj_module_t mp_module_machine; +extern const struct _mp_obj_module_t mp_module_network; +extern const struct _mp_obj_module_t mp_module_onewire; + +#define MICROPY_PORT_BUILTIN_MODULES \ + { MP_OBJ_NEW_QSTR(MP_QSTR_esp), (mp_obj_t)&esp_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ + +#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ + { MP_OBJ_NEW_QSTR(MP_QSTR_binascii), (mp_obj_t)&mp_module_ubinascii }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_collections), (mp_obj_t)&mp_module_collections }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_errno), (mp_obj_t)&mp_module_uerrno }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_hashlib), (mp_obj_t)&mp_module_uhashlib }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_heapq), (mp_obj_t)&mp_module_uheapq }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_io), (mp_obj_t)&mp_module_io }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_json), (mp_obj_t)&mp_module_ujson }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&uos_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&mp_module_urandom }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_re), (mp_obj_t)&mp_module_ure }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_module_uselect }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&mp_module_usocket }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_ssl), (mp_obj_t)&mp_module_ussl }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&mp_module_ustruct }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&utime_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_zlib), (mp_obj_t)&mp_module_uzlib }, \ + +#define MP_STATE_PORT MP_STATE_VM + +#define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; \ + mp_obj_t machine_pin_irq_handler[40]; \ + +// type definitions for the specific machine + +#define BYTES_PER_WORD (4) +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p))) +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) +#define MP_SSIZE_MAX (0x7fffffff) + +// Note: these "critical nested" macros do not ensure cross-CPU exclusion, +// the only disable interrupts on the current CPU. To full manage exclusion +// one should use portENTER_CRITICAL/portEXIT_CRITICAL instead. +#include "freertos/FreeRTOS.h" +#define MICROPY_BEGIN_ATOMIC_SECTION() portENTER_CRITICAL_NESTED() +#define MICROPY_END_ATOMIC_SECTION(state) portEXIT_CRITICAL_NESTED(state) + +#if MICROPY_PY_THREAD +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(void); \ + mp_handle_pending(); \ + MP_THREAD_GIL_EXIT(); \ + MP_THREAD_GIL_ENTER(); \ + } while (0); +#else +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(void); \ + mp_handle_pending(); \ + asm("waiti 0"); \ + } while (0); +#endif + +#define UINT_FMT "%u" +#define INT_FMT "%d" + +typedef int32_t mp_int_t; // must be pointer size +typedef uint32_t mp_uint_t; // must be pointer size +typedef long mp_off_t; +// ssize_t, off_t as required by POSIX-signatured functions in stream.h +#include + +// board specifics + +#define MICROPY_HW_BOARD_NAME "ESP32 module" +#define MICROPY_HW_MCU_NAME "ESP32" +#define MICROPY_PY_SYS_PLATFORM "esp32" diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c new file mode 100644 index 000000000..e588fc65a --- /dev/null +++ b/ports/esp32/mphalport.c @@ -0,0 +1,139 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 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 + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/uart.h" + +#include "py/obj.h" +#include "py/mpstate.h" +#include "py/mphal.h" +#include "extmod/misc.h" +#include "lib/utils/pyexec.h" + +STATIC uint8_t stdin_ringbuf_array[256]; +ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array)}; + +int mp_hal_stdin_rx_chr(void) { + for (;;) { + int c = ringbuf_get(&stdin_ringbuf); + if (c != -1) { + return c; + } + MICROPY_EVENT_POLL_HOOK + vTaskDelay(1); + } +} + +void mp_hal_stdout_tx_char(char c) { + uart_tx_one_char(c); + //mp_uos_dupterm_tx_strn(&c, 1); +} + +void mp_hal_stdout_tx_str(const char *str) { + MP_THREAD_GIL_EXIT(); + while (*str) { + mp_hal_stdout_tx_char(*str++); + } + MP_THREAD_GIL_ENTER(); +} + +void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { + MP_THREAD_GIL_EXIT(); + while (len--) { + mp_hal_stdout_tx_char(*str++); + } + MP_THREAD_GIL_ENTER(); +} + +void mp_hal_stdout_tx_strn_cooked(const char *str, uint32_t len) { + MP_THREAD_GIL_EXIT(); + while (len--) { + if (*str == '\n') { + mp_hal_stdout_tx_char('\r'); + } + mp_hal_stdout_tx_char(*str++); + } + MP_THREAD_GIL_ENTER(); +} + +uint32_t mp_hal_ticks_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +uint32_t mp_hal_ticks_us(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +void mp_hal_delay_ms(uint32_t ms) { + struct timeval tv_start; + struct timeval tv_end; + uint64_t dt; + gettimeofday(&tv_start, NULL); + for (;;) { + gettimeofday(&tv_end, NULL); + dt = (tv_end.tv_sec - tv_start.tv_sec) * 1000 + (tv_end.tv_usec - tv_start.tv_usec) / 1000; + if (dt + portTICK_PERIOD_MS >= ms) { + // doing a vTaskDelay would take us beyound requested delay time + break; + } + MICROPY_EVENT_POLL_HOOK + vTaskDelay(1); + } + if (dt < ms) { + // do the remaining delay accurately + ets_delay_us((ms - dt) * 1000); + } +} + +void mp_hal_delay_us(uint32_t us) { + ets_delay_us(us); +} + +// this function could do with improvements (eg use ets_delay_us) +void mp_hal_delay_us_fast(uint32_t us) { + uint32_t delay = ets_get_cpu_frequency() / 19; + while (--us) { + for (volatile uint32_t i = delay; i; --i) { + } + } +} + +/* +extern int mp_stream_errno; +int *__errno() { + return &mp_stream_errno; +} +*/ diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h new file mode 100644 index 000000000..8a0f1dc8b --- /dev/null +++ b/ports/esp32/mphalport.h @@ -0,0 +1,83 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 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. + */ + +#ifndef INCLUDED_MPHALPORT_H +#define INCLUDED_MPHALPORT_H + +#include "py/ringbuf.h" +#include "lib/utils/interrupt_char.h" + +extern ringbuf_t stdin_ringbuf; + +uint32_t mp_hal_ticks_us(void); +__attribute__((always_inline)) static inline uint32_t mp_hal_ticks_cpu(void) { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + +void mp_hal_delay_us(uint32_t); +void mp_hal_delay_us_fast(uint32_t); +void mp_hal_set_interrupt_char(int c); +uint32_t mp_hal_get_cpu_freq(void); + +#define mp_hal_quiet_timing_enter() MICROPY_BEGIN_ATOMIC_SECTION() +#define mp_hal_quiet_timing_exit(irq_state) MICROPY_END_ATOMIC_SECTION(irq_state) + +// C-level pin HAL +#include "py/obj.h" +#include "driver/gpio.h" +#define MP_HAL_PIN_FMT "%u" +#define mp_hal_pin_obj_t gpio_num_t +mp_hal_pin_obj_t machine_pin_get_id(mp_obj_t pin_in); +#define mp_hal_get_pin_obj(o) machine_pin_get_id(o) +#define mp_obj_get_pin(o) machine_pin_get_id(o) // legacy name; only to support esp8266/modonewire +#define mp_hal_pin_name(p) (p) +static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { + gpio_set_direction(pin, GPIO_MODE_INPUT); +} +static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { + gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT); +} +static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { + gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT_OD); +} +static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) { + gpio_set_level(pin, 0); +} +static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { + gpio_set_level(pin, 1); +} +static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) { + return gpio_get_level(pin); +} +static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) { + gpio_set_level(pin, v); +} + +#endif // INCLUDED_MPHALPORT_H diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c new file mode 100644 index 000000000..76d9431c0 --- /dev/null +++ b/ports/esp32/mpthreadport.c @@ -0,0 +1,226 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * Copyright (c) 2017 Pycom Limited + * + * 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 "stdio.h" + +#include "py/mpconfig.h" +#include "py/mpstate.h" +#include "py/gc.h" +#include "py/mpthread.h" +#include "mpthreadport.h" + +#include "esp_task.h" + +#if MICROPY_PY_THREAD + +#define MP_THREAD_MIN_STACK_SIZE (4 * 1024) +#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024) +#define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1) + +// this structure forms a linked list, one node per active thread +typedef struct _thread_t { + TaskHandle_t id; // system id of thread + int ready; // whether the thread is ready and running + void *arg; // thread Python args, a GC root pointer + void *stack; // pointer to the stack + StaticTask_t *tcb; // pointer to the Task Control Block + size_t stack_len; // number of words in the stack + struct _thread_t *next; +} thread_t; + +// the mutex controls access to the linked list +STATIC mp_thread_mutex_t thread_mutex; +STATIC thread_t thread_entry0; +STATIC thread_t *thread; // root pointer, handled by mp_thread_gc_others + +void mp_thread_init(void *stack, uint32_t stack_len) { + mp_thread_set_state(&mp_state_ctx.thread); + // create the first entry in the linked list of all threads + thread = &thread_entry0; + thread->id = xTaskGetCurrentTaskHandle(); + thread->ready = 1; + thread->arg = NULL; + thread->stack = stack; + thread->stack_len = stack_len; + thread->next = NULL; + mp_thread_mutex_init(&thread_mutex); +} + +void mp_thread_gc_others(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + gc_collect_root((void**)&th, 1); + gc_collect_root(&th->arg, 1); // probably not needed + if (th->id == xTaskGetCurrentTaskHandle()) { + continue; + } + if (!th->ready) { + continue; + } + gc_collect_root(th->stack, th->stack_len); // probably not needed + } + mp_thread_mutex_unlock(&thread_mutex); +} + +mp_state_thread_t *mp_thread_get_state(void) { + return pvTaskGetThreadLocalStoragePointer(NULL, 1); +} + +void mp_thread_set_state(void *state) { + vTaskSetThreadLocalStoragePointer(NULL, 1, state); +} + +void mp_thread_start(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == xTaskGetCurrentTaskHandle()) { + th->ready = 1; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +STATIC void *(*ext_thread_entry)(void*) = NULL; + +STATIC void freertos_entry(void *arg) { + if (ext_thread_entry) { + ext_thread_entry(arg); + } + vTaskDelete(NULL); + for (;;); +} + +void mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack_size, int priority, char *name) { + // store thread entry function into a global variable so we can access it + ext_thread_entry = entry; + + if (*stack_size == 0) { + *stack_size = MP_THREAD_DEFAULT_STACK_SIZE; // default stack size + } else if (*stack_size < MP_THREAD_MIN_STACK_SIZE) { + *stack_size = MP_THREAD_MIN_STACK_SIZE; // minimum stack size + } + + // allocate TCB, stack and linked-list node (must be outside thread_mutex lock) + StaticTask_t *tcb = m_new(StaticTask_t, 1); + StackType_t *stack = m_new(StackType_t, *stack_size / sizeof(StackType_t)); + thread_t *th = m_new_obj(thread_t); + + mp_thread_mutex_lock(&thread_mutex, 1); + + // create thread + TaskHandle_t id = xTaskCreateStaticPinnedToCore(freertos_entry, name, *stack_size / sizeof(StackType_t), arg, priority, stack, tcb, 0); + if (id == NULL) { + mp_thread_mutex_unlock(&thread_mutex); + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread")); + } + + // adjust the stack_size to provide room to recover from hitting the limit + *stack_size -= 1024; + + // add thread to linked list of all threads + th->id = id; + th->ready = 0; + th->arg = arg; + th->stack = stack; + th->tcb = tcb; + th->stack_len = *stack_size / sizeof(StackType_t); + th->next = thread; + thread = th; + + mp_thread_mutex_unlock(&thread_mutex); +} + +void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) { + mp_thread_create_ex(entry, arg, stack_size, MP_THREAD_PRIORITY, "mp_thread"); +} + +void mp_thread_finish(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == xTaskGetCurrentTaskHandle()) { + th->ready = 0; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +void vPortCleanUpTCB(void *tcb) { + thread_t *prev = NULL; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; prev = th, th = th->next) { + // unlink the node from the list + if (th->tcb == tcb) { + if (prev != NULL) { + prev->next = th->next; + } else { + // move the start pointer + thread = th->next; + } + // explicitly release all its memory + m_del(StaticTask_t, th->tcb, 1); + m_del(StackType_t, th->stack, th->stack_len); + m_del(thread_t, th, 1); + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +void mp_thread_mutex_init(mp_thread_mutex_t *mutex) { + mutex->handle = xSemaphoreCreateMutexStatic(&mutex->buffer); +} + +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { + return (pdTRUE == xSemaphoreTake(mutex->handle, wait ? portMAX_DELAY : 0)); +} + +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { + xSemaphoreGive(mutex->handle); +} + +void mp_thread_deinit(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + // don't delete the current task + if (th->id == xTaskGetCurrentTaskHandle()) { + continue; + } + vTaskDelete(th->id); + } + mp_thread_mutex_unlock(&thread_mutex); + // allow FreeRTOS to clean-up the threads + vTaskDelay(2); +} + +#else + +void vPortCleanUpTCB(void *tcb) { +} + +#endif // MICROPY_PY_THREAD diff --git a/ports/esp32/mpthreadport.h b/ports/esp32/mpthreadport.h new file mode 100644 index 000000000..54e35d61c --- /dev/null +++ b/ports/esp32/mpthreadport.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * Copyright (c) 2017 Pycom Limited + * + * 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_ESP32_MPTHREADPORT_H +#define MICROPY_INCLUDED_ESP32_MPTHREADPORT_H + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + +typedef struct _mp_thread_mutex_t { + SemaphoreHandle_t handle; + StaticSemaphore_t buffer; +} mp_thread_mutex_t; + +void mp_thread_init(void *stack, uint32_t stack_len); +void mp_thread_gc_others(void); +void mp_thread_deinit(void); + +#endif // MICROPY_INCLUDED_ESP32_MPTHREADPORT_H diff --git a/ports/esp32/qstrdefsport.h b/ports/esp32/qstrdefsport.h new file mode 100644 index 000000000..ff6c2cc42 --- /dev/null +++ b/ports/esp32/qstrdefsport.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 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. + */ + +// qstrs specific to this port, only needed if they aren't auto-generated + +// Entries for sys.path +Q(/lib) diff --git a/ports/esp32/sdkconfig.h b/ports/esp32/sdkconfig.h new file mode 100644 index 000000000..ce254c0e6 --- /dev/null +++ b/ports/esp32/sdkconfig.h @@ -0,0 +1,162 @@ +#define CONFIG_TRACEMEM_RESERVE_DRAM 0x0 +#define CONFIG_BT_RESERVE_DRAM 0x0 +#define CONFIG_ULP_COPROC_RESERVE_MEM 0 +#define CONFIG_PHY_DATA_OFFSET 0xf000 +#define CONFIG_APP_OFFSET 0x10000 + +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1 +#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4 + +#define CONFIG_TCPIP_TASK_STACK_SIZE 2560 + +#define CONFIG_ESP32_APPTRACE_DEST_NONE 1 +#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 +#define CONFIG_ESP32_PANIC_PRINT_REBOOT 1 +#define CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC 1 +#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1 +#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 240 +#define CONFIG_ESP32_DEFAULT_CPU_FREQ_240 1 +#define CONFIG_ESP32_DEBUG_OCDAWARE 1 +#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 0 +#define CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE 1 +#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 +#define CONFIG_ESP32_WIFI_AMPDU_ENABLED 1 +#define CONFIG_ESP32_WIFI_NVS_ENABLED 1 +#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10 +#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 0 +#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1 +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1 +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32 +#define CONFIG_ESP32_WIFI_RX_BA_WIN 6 +#define CONFIG_ESP32_WIFI_TX_BA_WIN 6 +#define CONFIG_ESP32_XTAL_FREQ_AUTO 1 +#define CONFIG_ESP32_XTAL_FREQ 0 +#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024 +#define CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT 5 +#define CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT 2048 + +#define CONFIG_FOUR_MAC_ADDRESS_FROM_EFUSE 1 +#define CONFIG_DMA_RX_BUF_NUM 10 +#define CONFIG_DMA_TX_BUF_NUM 10 +#define CONFIG_EMAC_TASK_PRIORITY 20 + +#define CONFIG_INT_WDT 1 +#define CONFIG_INT_WDT_TIMEOUT_MS 300 +#define CONFIG_INT_WDT_CHECK_CPU1 0 +#define CONFIG_TASK_WDT 1 +#define CONFIG_TASK_WDT_TIMEOUT_S 5 +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK 0 +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 0 + +#define CONFIG_FREERTOS_UNICORE 1 +#define CONFIG_FREERTOS_CORETIMER_0 1 +#define CONFIG_FREERTOS_HZ 100 +#define CONFIG_FREERTOS_ASSERT_FAIL_ABORT 1 +#define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1 +#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE 1 +#define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 2 +#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1024 +#define CONFIG_FREERTOS_ISR_STACKSIZE 1536 +#define CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG 1 +#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16 +#define CONFIG_SUPPORT_STATIC_ALLOCATION 1 +#define CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK 1 + +#define CONFIG_MAIN_TASK_STACK_SIZE 4096 +#define CONFIG_IPC_TASK_STACK_SIZE 1024 +#define CONFIG_BTC_TASK_STACK_SIZE 3072 +#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 4096 +#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32 +#define CONFIG_TIMER_TASK_STACK_SIZE 4096 +#define CONFIG_TIMER_TASK_PRIORITY 1 +#define CONFIG_TIMER_TASK_STACK_DEPTH 2048 +#define CONFIG_TIMER_QUEUE_LENGTH 10 + +#define CONFIG_NEWLIB_STDIN_LINE_ENDING_CR 1 +#define CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF 1 +#define CONFIG_PHY_ENABLED 1 +#define CONFIG_WIFI_ENABLED 1 +#define CONFIG_OPTIMIZATION_LEVEL_DEBUG 1 +#define CONFIG_MEMMAP_SMP 1 + +#define CONFIG_PARTITION_TABLE_SINGLE_APP 1 +#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" +#define CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET 0x10000 +#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv" + +#define CONFIG_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_CONSOLE_UART_NUM 0 +#define CONFIG_CONSOLE_UART_DEFAULT 1 + +#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1 +#define CONFIG_LOG_BOOTLOADER_LEVEL_WARN 1 +#define CONFIG_LOG_DEFAULT_LEVEL 3 +#define CONFIG_LOG_COLORS 1 +#define CONFIG_LOG_BOOTLOADER_LEVEL 2 + +#define CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX 0 +#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1 +#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 +#define CONFIG_LWIP_MAX_SOCKETS 8 +#define CONFIG_LWIP_SO_REUSE 1 +#define CONFIG_IP_LOST_TIMER_INTERVAL 120 +#define CONFIG_UDP_RECVMBOX_SIZE 6 +#define CONFIG_TCP_MAXRTX 12 +#define CONFIG_TCP_SYNMAXRTX 6 +#define CONFIG_TCP_MSL 60000 +#define CONFIG_TCP_MSS 1436 +#define CONFIG_TCP_SND_BUF_DEFAULT 5744 +#define CONFIG_TCP_WND_DEFAULT 5744 +#define CONFIG_TCP_QUEUE_OOSEQ 1 +#define CONFIG_TCP_OVERSIZE_MSS 1 +#define CONFIG_TCP_RECVMBOX_SIZE 6 + +#define CONFIG_MBEDTLS_AES_C 1 +#define CONFIG_MBEDTLS_CCM_C 1 +#define CONFIG_MBEDTLS_ECDH_C 1 +#define CONFIG_MBEDTLS_ECDSA_C 1 +#define CONFIG_MBEDTLS_ECP_C 1 +#define CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_NIST_OPTIM 1 +#define CONFIG_MBEDTLS_GCM_C 1 +#define CONFIG_MBEDTLS_HARDWARE_AES 1 +#define CONFIG_MBEDTLS_HAVE_TIME 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_RSA 1 +#define CONFIG_MBEDTLS_PEM_PARSE_C 1 +#define CONFIG_MBEDTLS_PEM_WRITE_C 1 +#define CONFIG_MBEDTLS_RC4_DISABLED 1 +#define CONFIG_MBEDTLS_SSL_ALPN 1 +#define CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1 1 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_1 1 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_2 1 +#define CONFIG_MBEDTLS_SSL_RENEGOTIATION 1 +#define CONFIG_MBEDTLS_SSL_SESSION_TICKETS 1 +#define CONFIG_MBEDTLS_TLS_CLIENT 1 +#define CONFIG_MBEDTLS_TLS_ENABLED 1 +#define CONFIG_MBEDTLS_TLS_SERVER 1 +#define CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT 1 +#define CONFIG_MBEDTLS_X509_CRL_PARSE_C 1 +#define CONFIG_MBEDTLS_X509_CSR_PARSE_C 1 + +#define CONFIG_MAKE_WARN_UNDEFINED_VARIABLES 1 +#define CONFIG_TOOLPREFIX "xtensa-esp32-elf-" +#define CONFIG_PYTHON "python2" diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c new file mode 100644 index 000000000..10a4ba462 --- /dev/null +++ b/ports/esp32/uart.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 "driver/uart.h" + +#include "py/mpstate.h" +#include "py/mphal.h" + +STATIC void uart_irq_handler(void *arg); + +void uart_init(void) { + uart_isr_handle_t handle; + uart_isr_register(UART_NUM_0, uart_irq_handler, NULL, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, &handle); + uart_enable_rx_intr(UART_NUM_0); +} + +// all code executed in ISR must be in IRAM, and any const data must be in DRAM +STATIC void IRAM_ATTR uart_irq_handler(void *arg) { + volatile uart_dev_t *uart = &UART0; + uart->int_clr.rxfifo_full = 1; + uart->int_clr.frm_err = 1; + uart->int_clr.rxfifo_tout = 1; + while (uart->status.rxfifo_cnt) { + uint8_t c = uart->fifo.rw_byte; + if (c == mp_interrupt_char) { + // inline version of mp_keyboard_interrupt(); + MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); + #if MICROPY_ENABLE_SCHEDULER + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } + #endif + } else { + // this is an inline function so will be in IRAM + ringbuf_put(&stdin_ringbuf, c); + } + } +} diff --git a/ports/esp32/uart.h b/ports/esp32/uart.h new file mode 100644 index 000000000..264c8b894 --- /dev/null +++ b/ports/esp32/uart.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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. + */ +#ifndef MICROPY_INCLUDED_ESP32_UART_H +#define MICROPY_INCLUDED_ESP32_UART_H + +void uart_init(void); + +#endif // MICROPY_INCLUDED_ESP32_UART_H From bfc9845d005b361cc1ba58939c6d3317aeb37a07 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Sep 2017 13:29:36 +1000 Subject: [PATCH 132/828] esp32/modnetwork: Give better error msgs for AP timeout and not-found. --- ports/esp32/modnetwork.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 73a471861..af1c9301c 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -130,8 +130,16 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) { system_event_sta_disconnected_t *disconn = &event->event_info.disconnected; ESP_LOGI("wifi", "STA_DISCONNECTED, reason:%d", disconn->reason); switch (disconn->reason) { + case WIFI_REASON_BEACON_TIMEOUT: + mp_printf(MP_PYTHON_PRINTER, "beacon timeout\n"); + // AP has dropped out; try to reconnect. + break; + case WIFI_REASON_NO_AP_FOUND: + mp_printf(MP_PYTHON_PRINTER, "no AP found\n"); + // AP may not exist, or it may have momentarily dropped out; try to reconnect. + break; case WIFI_REASON_AUTH_FAIL: - mp_printf(MP_PYTHON_PRINTER, "authentication failed"); + mp_printf(MP_PYTHON_PRINTER, "authentication failed\n"); wifi_sta_connected = false; break; default: From 6cc716c4aa86f35f14930abbb02c6d63f9329aa1 Mon Sep 17 00:00:00 2001 From: Timmenem Date: Fri, 22 Sep 2017 12:31:10 +0200 Subject: [PATCH 133/828] esp32/modsocket: Implement setsockopt(IP_ADD_MEMBERSHIP). Allows to join multicast groups. --- ports/esp32/modsocket.c | 42 +++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 6ba61e874..cba34d76c 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -51,6 +51,7 @@ #include "lwip/sockets.h" #include "lwip/netdb.h" #include "lwip/ip4.h" +#include "lwip/igmp.h" #include "esp_log.h" #define SOCKET_POLL_US (100000) @@ -208,18 +209,37 @@ STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { (void)n_args; // always 4 socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); - int level = mp_obj_get_int(args[1]); - if (level != SOL_SOCKET) { - mp_raise_ValueError("unsupported level"); - } - - // only "int" arguments are supported at the moment int opt = mp_obj_get_int(args[2]); - int val = mp_obj_get_int(args[3]); - int ret = lwip_setsockopt_r(self->fd, SOL_SOCKET, opt, &val, sizeof(int)); - if (ret != 0) { - exception_from_errno(errno); + switch (opt) { + // level: SOL_SOCKET + case SO_REUSEADDR: { + int val = mp_obj_get_int(args[3]); + int ret = lwip_setsockopt_r(self->fd, SOL_SOCKET, opt, &val, sizeof(int)); + if (ret != 0) { + exception_from_errno(errno); + } + break; + } + + // level: IPPROTO_IP + case IP_ADD_MEMBERSHIP: { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != sizeof(ip4_addr_t) * 2) { + mp_raise_ValueError(NULL); + } + + // POSIX setsockopt has order: group addr, if addr, lwIP has it vice-versa + err_t err = igmp_joingroup((const ip4_addr_t*)bufinfo.buf + 1, bufinfo.buf); + if (err != ERR_OK) { + mp_raise_OSError(-err); + } + break; + } + + default: + mp_printf(&mp_plat_print, "Warning: lwip.setsockopt() option not implemented\n"); } return mp_const_none; @@ -558,8 +578,10 @@ STATIC const mp_map_elem_t mp_module_socket_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_RAW), MP_OBJ_NEW_SMALL_INT(SOCK_RAW) }, { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_TCP), MP_OBJ_NEW_SMALL_INT(IPPROTO_TCP) }, { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_UDP), MP_OBJ_NEW_SMALL_INT(IPPROTO_UDP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_IP), MP_OBJ_NEW_SMALL_INT(IPPROTO_IP) }, { MP_OBJ_NEW_QSTR(MP_QSTR_SOL_SOCKET), MP_OBJ_NEW_SMALL_INT(SOL_SOCKET) }, { MP_OBJ_NEW_QSTR(MP_QSTR_SO_REUSEADDR), MP_OBJ_NEW_SMALL_INT(SO_REUSEADDR) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_OBJ_NEW_SMALL_INT(IP_ADD_MEMBERSHIP) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); From b74809c70a78cc7fd7796f46776b17f8cf6ad3a4 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Tue, 12 Sep 2017 10:12:22 +1000 Subject: [PATCH 134/828] esp32/mpconfigport.h: Add missing uhashlib. --- ports/esp32/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 2c3259324..3c4b77d3d 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -176,6 +176,7 @@ extern const struct _mp_obj_module_t mp_module_onewire; { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \ { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_uhashlib), (mp_obj_t)&mp_module_uhashlib }, \ #define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ { MP_OBJ_NEW_QSTR(MP_QSTR_binascii), (mp_obj_t)&mp_module_ubinascii }, \ From 5adc133f051172ebbc5b6fb3d8913599f5df3c06 Mon Sep 17 00:00:00 2001 From: Eric Poulsen Date: Fri, 29 Sep 2017 09:00:55 -0700 Subject: [PATCH 135/828] esp32/mphalport.h: Make mp_hal_pin_ select gpio on the pad. Otherwise interfaces like software I2C and SPI don't initialise correctly. --- ports/esp32/mphalport.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h index 8a0f1dc8b..3215bc062 100644 --- a/ports/esp32/mphalport.h +++ b/ports/esp32/mphalport.h @@ -59,12 +59,15 @@ mp_hal_pin_obj_t machine_pin_get_id(mp_obj_t pin_in); #define mp_obj_get_pin(o) machine_pin_get_id(o) // legacy name; only to support esp8266/modonewire #define mp_hal_pin_name(p) (p) static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { + gpio_pad_select_gpio(pin); gpio_set_direction(pin, GPIO_MODE_INPUT); } static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { + gpio_pad_select_gpio(pin); gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT); } static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { + gpio_pad_select_gpio(pin); gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT_OD); } static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) { From 5f8ad6072f16414e6fc31125a38f21c366e0cb34 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 5 Oct 2017 22:06:26 +1100 Subject: [PATCH 136/828] esp32: Call initialization function on sha1 and sha256. Add in calls to mbedtls_sha1_starts() and mbedtls_sha256_starts(). --- ports/esp32/moduhashlib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/esp32/moduhashlib.c b/ports/esp32/moduhashlib.c index 6f67aa7d9..e8bc5e83c 100644 --- a/ports/esp32/moduhashlib.c +++ b/ports/esp32/moduhashlib.c @@ -46,6 +46,7 @@ STATIC mp_obj_t sha256_make_new(const mp_obj_type_t *type, mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(union sha_ctxs)); o->base.type = type; mbedtls_sha256_init(&o->state.sha256); + mbedtls_sha256_starts(&o->state.sha256, 0); if (n_args == 1) { sha256_update(MP_OBJ_FROM_PTR(o), args[0]); } @@ -58,6 +59,7 @@ STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(union sha_ctxs)); o->base.type = type; mbedtls_sha1_init(&o->state.sha1); + mbedtls_sha1_starts(&o->state.sha1); if (n_args == 1) { sha1_update(MP_OBJ_FROM_PTR(o), args[0]); } From 31747fe2660c5c48dd8546ce3665e798d36698aa Mon Sep 17 00:00:00 2001 From: Eric Poulsen Date: Tue, 23 May 2017 09:24:51 -0700 Subject: [PATCH 137/828] esp32: Implement machine.WDT() class. --- ports/esp32/Makefile | 1 + ports/esp32/machine_wdt.c | 78 +++++++++++++++++++++++++++++++++++++++ ports/esp32/modmachine.c | 1 + ports/esp32/modmachine.h | 1 + ports/esp32/sdkconfig.h | 1 + 5 files changed, 82 insertions(+) create mode 100644 ports/esp32/machine_wdt.c diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 2eae802be..a461f50ff 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -141,6 +141,7 @@ SRC_C = \ moduhashlib.c \ espneopixel.c \ machine_hw_spi.c \ + machine_wdt.c \ mpthreadport.c \ $(SRC_MOD) diff --git a/ports/esp32/machine_wdt.c b/ports/esp32/machine_wdt.c new file mode 100644 index 000000000..0389e8689 --- /dev/null +++ b/ports/esp32/machine_wdt.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * Copyright (c) 2017 Eric Poulsen + * + * 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/nlr.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "esp_task_wdt.h" + +const mp_obj_type_t machine_wdt_type; + +typedef struct _machine_wdt_obj_t { + mp_obj_base_t base; +} machine_wdt_obj_t; + +STATIC machine_wdt_obj_t wdt_default = {{&machine_wdt_type}}; + +STATIC mp_obj_t machine_wdt_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, 0, 1, false); + + mp_int_t id = 0; + if (n_args > 0) { + id = mp_obj_get_int(args[0]); + } + + switch (id) { + case 0: + esp_task_wdt_feed(); + return &wdt_default; + default: + mp_raise_ValueError(NULL); + } +} + +STATIC mp_obj_t machine_wdt_feed(mp_obj_t self_in) { + (void)self_in; + esp_task_wdt_feed(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_wdt_feed_obj, machine_wdt_feed); + +STATIC const mp_map_elem_t machine_wdt_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_feed), (mp_obj_t)&machine_wdt_feed_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_wdt_locals_dict, machine_wdt_locals_dict_table); + +const mp_obj_type_t machine_wdt_type = { + { &mp_type_type }, + .name = MP_QSTR_WDT, + .make_new = machine_wdt_make_new, + .locals_dict = (mp_obj_t)&machine_wdt_locals_dict, +}; diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 51038f314..32c9c5ad5 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -114,6 +114,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, + { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, { MP_ROM_QSTR(MP_QSTR_TouchPad), MP_ROM_PTR(&machine_touchpad_type) }, diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index 4909235b4..58229007d 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -4,6 +4,7 @@ #include "py/obj.h" extern const mp_obj_type_t machine_timer_type; +extern const mp_obj_type_t machine_wdt_type; extern const mp_obj_type_t machine_pin_type; extern const mp_obj_type_t machine_touchpad_type; extern const mp_obj_type_t machine_adc_type; diff --git a/ports/esp32/sdkconfig.h b/ports/esp32/sdkconfig.h index ce254c0e6..38050fab2 100644 --- a/ports/esp32/sdkconfig.h +++ b/ports/esp32/sdkconfig.h @@ -45,6 +45,7 @@ #define CONFIG_INT_WDT_TIMEOUT_MS 300 #define CONFIG_INT_WDT_CHECK_CPU1 0 #define CONFIG_TASK_WDT 1 +#define CONFIG_TASK_WDT_PANIC 1 #define CONFIG_TASK_WDT_TIMEOUT_S 5 #define CONFIG_TASK_WDT_CHECK_IDLE_TASK 0 #define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 0 From ba2d960276083c12b2ccf6f431064ab4a073a73d Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 15 Oct 2017 18:14:37 +1100 Subject: [PATCH 138/828] esp32/README: Update general description of port to add avail features. --- ports/esp32/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 7591f040a..5c0eb813d 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -7,12 +7,14 @@ a task under FreeRTOS. Supported features include: - REPL (Python prompt) over UART0. -- 16k stack for the MicroPython task and 64k Python heap. +- 16k stack for the MicroPython task and 96k Python heap. - Many of MicroPython's features are enabled: unicode, arbitrary-precision integers, single-precision floats, complex numbers, frozen bytecode, as well as many of the internal modules. -- Internal filesystem using the flash (currently 256k in size). -- The machine module with basic GPIO and bit-banging I2C, SPI support. +- Internal filesystem using the flash (currently 2M in size). +- The machine module with GPIO, UART, SPI, software I2C, ADC, DAC, PWM, + TouchPad, WDT and Timer. +- The network module with WLAN (WiFi) support. Development of this ESP32 port was sponsored in part by Microbric Pty Ltd. From f0628f5499774817ccbf34e66b22c56ec8e68a90 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Thu, 19 Oct 2017 11:01:53 +1100 Subject: [PATCH 139/828] esp32/modutime.c: Add localtime and mktime functions. --- ports/esp32/modutime.c | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ports/esp32/modutime.c b/ports/esp32/modutime.c index f037faad9..002854298 100644 --- a/ports/esp32/modutime.c +++ b/ports/esp32/modutime.c @@ -30,8 +30,51 @@ #include #include +#include "py/runtime.h" +#include "lib/timeutils/timeutils.h" #include "extmod/utime_mphal.h" +STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + timeutils_struct_time_t tm; + mp_int_t seconds; + if (n_args == 0 || args[0] == mp_const_none) { + struct timeval tv; + gettimeofday(&tv, NULL); + seconds = tv.tv_sec; + } else { + seconds = mp_obj_get_int(args[0]); + } + timeutils_seconds_since_2000_to_struct_time(seconds, &tm); + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime); + +STATIC mp_obj_t time_mktime(mp_obj_t tuple) { + size_t len; + mp_obj_t *elem; + mp_obj_get_array(tuple, &len, &elem); + + // localtime generates a tuple of len 8. CPython uses 9, so we accept both. + if (len < 8 || len > 9) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "mktime needs a tuple of length 8 or 9 (%d given)", len)); + } + + return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]), + mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_obj_get_int(elem[3]), + mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5]))); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); + STATIC mp_obj_t time_time(void) { struct timeval tv; gettimeofday(&tv, NULL); @@ -42,6 +85,8 @@ MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); STATIC const mp_rom_map_elem_t time_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, + { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&time_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&time_mktime_obj) }, { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, From a5808e2fca043dcacc2de8563533f71d1a12fb39 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 26 Oct 2017 17:17:08 +1100 Subject: [PATCH 140/828] esp32/machine_pwm: Always set the channel in the PWM object. --- ports/esp32/machine_pwm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 489833e7c..4d6c59f0f 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -131,6 +131,7 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self, } channel = avail; } + self->channel = channel; // New PWM assignment self->active = 1; @@ -148,7 +149,6 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self, "PWM not supported on pin %d", self->pin)); } chan_gpio[channel] = self->pin; - self->channel = channel; } // Maybe change PWM timer From 48613b6011fc84136125b7595e1e2cca673e7ed5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 26 Oct 2017 18:24:35 +1100 Subject: [PATCH 141/828] esp32: Update to latest ESP IDF. This update requires the xtensa-esp32-elf to be upgraded to the latest version, 1.22.0-73-ge28a011-5.2.0. --- ports/esp32/Makefile | 8 ++++++-- ports/esp32/esp32.custom_common.ld | 8 ++++++-- ports/esp32/sdkconfig.h | 6 ++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index a461f50ff..a533fd31d 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -29,7 +29,7 @@ ESPCOMP = $(ESPIDF)/components ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py # verify the ESP IDF version -ESPIDF_SUPHASH := 9a26296a0e88a4c3ae27e9c848be970946fff87e +ESPIDF_SUPHASH := 2c95a77cf93781f296883d5dbafcdc18e4389656 ESPIDF_CURHASH := $(shell git -C $(ESPIDF) show -s --pretty=format:'%H') ifneq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH)) $(info ** WARNING **) @@ -214,6 +214,7 @@ ESPIDF_DRIVER_O = $(addprefix $(ESPCOMP)/driver/,\ $(BUILD)/$(ESPCOMP)/esp32/dport_access.o: CFLAGS += -Wno-array-bounds ESPIDF_ESP32_O = $(addprefix $(ESPCOMP)/esp32/,\ + brownout.o \ panic.o \ esp_timer.o \ esp_timer_esp32.o \ @@ -240,6 +241,7 @@ ESPIDF_ESP32_O = $(addprefix $(ESPCOMP)/esp32/,\ intr_alloc.o \ dport_access.o \ wifi_init.o \ + wifi_internal.o \ ) ESPIDF_HEAP_O = $(addprefix $(ESPCOMP)/heap/,\ @@ -281,6 +283,7 @@ ESPIDF_EXPAT_O = $(addprefix $(ESPCOMP)/expat/,\ ESPIDF_PTHREAD_O = $(addprefix $(ESPCOMP)/pthread/,\ pthread.o \ + pthread_local_storage.o \ ) # Assembler .S files need only basic flags, and in particular should not have @@ -447,6 +450,7 @@ ESPIDF_LWIP_O = $(addprefix $(ESPCOMP)/lwip/,\ port/freertos/sys_arch.o \ port/netif/wlanif.o \ port/netif/ethernetif.o \ + port/vfs_lwip.o \ ) ESPIDF_MBEDTLS_O = $(addprefix $(ESPCOMP)/mbedtls/,\ @@ -615,7 +619,7 @@ APP_LD_ARGS += -L$(dir $(LIBSTDCXX_FILE_NAME)) -lstdc++ APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libc.a APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libm.a APP_LD_ARGS += $(ESPCOMP)/esp32/libhal.a -APP_LD_ARGS += -L$(ESPCOMP)/esp32/lib -lcore -lnet80211 -lphy -lrtc -lpp -lwpa -lsmartconfig -lcoexist +APP_LD_ARGS += -L$(ESPCOMP)/esp32/lib -lcore -lnet80211 -lphy -lrtc -lpp -lwpa -lsmartconfig -lcoexist -lwps -lwpa2 APP_LD_ARGS += $(OBJ) APP_LD_ARGS += --end-group diff --git a/ports/esp32/esp32.custom_common.ld b/ports/esp32/esp32.custom_common.ld index 45242f9af..eb1dee3c0 100644 --- a/ports/esp32/esp32.custom_common.ld +++ b/ports/esp32/esp32.custom_common.ld @@ -89,13 +89,14 @@ SECTIONS *esp32/core_dump.o(.literal .text .literal.* .text.*) *app_trace/*(.literal .text .literal.* .text.*) *xtensa-debug-module/eri.o(.literal .text .literal.* .text.*) - *esp32/app_trace.o(.literal .text .literal.* .text.*) *libphy.a:(.literal .text .literal.* .text.*) *librtc.a:(.literal .text .literal.* .text.*) *libsoc.a:(.literal .text .literal.* .text.*) *libhal.a:(.literal .text .literal.* .text.*) *libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*) *spi_flash/spi_flash_rom_patch.o(.literal .text .literal.* .text.*) + *libgcov.a:(.literal .text .literal.* .text.*) + INCLUDE esp32.spiram.rom-functions-iram.ld *py/scheduler.o*(.literal .text .literal.* .text.*) _iram_text_end = ABSOLUTE(.); } > iram0_0_seg @@ -117,9 +118,12 @@ SECTIONS *(.dram1 .dram1.*) *esp32/panic.o(.rodata .rodata.*) *libphy.a:(.rodata .rodata.*) - *app_trace/app_trace.o:(.rodata .rodata.*) + *soc/esp32/rtc_clk.o(.rodata .rodata.*) + *app_trace/app_trace.o(.rodata .rodata.*) + *libgcov.a:(.rodata .rodata.*) *heap/multi_heap.o(.rodata .rodata.*) *heap/multi_heap_poisoning.o(.rodata .rodata.*) + INCLUDE esp32.spiram.rom-functions-dram.ld _data_end = ABSOLUTE(.); . = ALIGN(4); } >dram0_0_seg diff --git a/ports/esp32/sdkconfig.h b/ports/esp32/sdkconfig.h index 38050fab2..7fcbb7c01 100644 --- a/ports/esp32/sdkconfig.h +++ b/ports/esp32/sdkconfig.h @@ -8,7 +8,12 @@ #define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1 #define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4 +#define CONFIG_BROWNOUT_DET 1 +#define CONFIG_BROWNOUT_DET_LVL 0 +#define CONFIG_BROWNOUT_DET_LVL_SEL_0 1 + #define CONFIG_TCPIP_TASK_STACK_SIZE 2560 +#define CONFIG_TCPIP_RECVMBOX_SIZE 32 #define CONFIG_ESP32_APPTRACE_DEST_NONE 1 #define CONFIG_ESP32_PHY_MAX_TX_POWER 20 @@ -101,6 +106,7 @@ #define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 #define CONFIG_LWIP_MAX_SOCKETS 8 #define CONFIG_LWIP_SO_REUSE 1 +#define CONFIG_LWIP_ETHARP_TRUST_IP_MAC 1 #define CONFIG_IP_LOST_TIMER_INTERVAL 120 #define CONFIG_UDP_RECVMBOX_SIZE 6 #define CONFIG_TCP_MAXRTX 12 From 934abc9b9d3ddcb194f4687144b6deb3e2430cd9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 26 Oct 2017 18:25:41 +1100 Subject: [PATCH 142/828] esp32/modules: Symlink in ntptime.py from esp8266/modules. --- ports/esp32/modules/ntptime.py | 1 + 1 file changed, 1 insertion(+) create mode 120000 ports/esp32/modules/ntptime.py diff --git a/ports/esp32/modules/ntptime.py b/ports/esp32/modules/ntptime.py new file mode 120000 index 000000000..e90900d5a --- /dev/null +++ b/ports/esp32/modules/ntptime.py @@ -0,0 +1 @@ +../../esp8266/modules/ntptime.py \ No newline at end of file From b0853b5a39eb46523a137ec50e777678e85d2cbf Mon Sep 17 00:00:00 2001 From: Eric Poulsen Date: Sun, 29 Oct 2017 17:10:00 -0700 Subject: [PATCH 143/828] esp32/modnetwork.c: Fix for setting DNS with network.WLAN.ifconfig(). When configuring a static set of values with ifconfig() the DNS was not being set. This patch fixes that, and additionally uses the tcpip_adapter API to ensure it is thread safe. Further discussion is here: https://github.com/micropython/micropython-esp32/issues/210/ --- ports/esp32/modnetwork.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index af1c9301c..83f321dc2 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -328,16 +328,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_isconnected_obj, esp_isconnected); STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); tcpip_adapter_ip_info_t info; - ip_addr_t dns_addr; + tcpip_adapter_dns_info_t dns_info; tcpip_adapter_get_ip_info(self->if_id, &info); + tcpip_adapter_get_dns_info(self->if_id, TCPIP_ADAPTER_DNS_MAIN, &dns_info); if (n_args == 1) { // get - dns_addr = dns_getserver(0); mp_obj_t tuple[4] = { netutils_format_ipv4_addr((uint8_t*)&info.ip, NETUTILS_BIG), netutils_format_ipv4_addr((uint8_t*)&info.netmask, NETUTILS_BIG), netutils_format_ipv4_addr((uint8_t*)&info.gw, NETUTILS_BIG), - netutils_format_ipv4_addr((uint8_t*)&dns_addr, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&dns_info.ip, NETUTILS_BIG), }; return mp_obj_new_tuple(4, tuple); } else { @@ -356,16 +356,18 @@ STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { netutils_parse_ipv4_addr(items[1], (void*)&info.netmask, NETUTILS_BIG); } netutils_parse_ipv4_addr(items[2], (void*)&info.gw, NETUTILS_BIG); - netutils_parse_ipv4_addr(items[3], (void*)&dns_addr, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[3], (void*)&dns_info.ip, NETUTILS_BIG); // To set a static IP we have to disable DHCP first if (self->if_id == WIFI_IF_STA) { esp_err_t e = tcpip_adapter_dhcpc_stop(WIFI_IF_STA); if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_STA, &info)); + ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(WIFI_IF_STA, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); } else if (self->if_id == WIFI_IF_AP) { esp_err_t e = tcpip_adapter_dhcps_stop(WIFI_IF_AP); if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_AP, &info)); + ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(WIFI_IF_AP, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); ESP_EXCEPTIONS(tcpip_adapter_dhcps_start(WIFI_IF_AP)); } return mp_const_none; From 1c52d3e8c64f7d4369bf43fc0eb0cbeb76e32c03 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 30 Oct 2017 16:19:52 +1100 Subject: [PATCH 144/828] esp32/mpconfigport.h: Enable ussl finaliser. --- ports/esp32/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 3c4b77d3d..1eb4c408d 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -138,6 +138,7 @@ #define MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE (ets_get_cpu_frequency() * 1000000 / 200) // roughly #define MICROPY_PY_USSL (1) #define MICROPY_SSL_MBEDTLS (1) +#define MICROPY_PY_USSL_FINALISER (1) #define MICROPY_PY_WEBSOCKET (0) #define MICROPY_PY_FRAMEBUF (1) From 7df2ebbfeaf390cc257c56916c5af94c7785469f Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 31 Oct 2017 15:54:15 +1100 Subject: [PATCH 145/828] extmod/modussl_mbedtls: Clean up mbedtls state when error during setup. Without this patch, if the SSL handshake fails (eg the connection was lost) then the mbedtls state (memory) will never be freed. --- extmod/modussl_mbedtls.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index 69a64d2f7..197a0651c 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -141,8 +141,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { const byte seed[] = "upy"; ret = mbedtls_ctr_drbg_seed(&o->ctr_drbg, null_entropy_func/*mbedtls_entropy_func*/, &o->entropy, seed, sizeof(seed)); if (ret != 0) { - printf("ret=%d\n", ret); - assert(0); + goto cleanup; } ret = mbedtls_ssl_config_defaults(&o->conf, @@ -150,7 +149,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret != 0) { - assert(0); + goto cleanup; } mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE); @@ -161,14 +160,14 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { ret = mbedtls_ssl_setup(&o->ssl, &o->conf); if (ret != 0) { - assert(0); + goto cleanup; } if (args->server_hostname.u_obj != mp_const_none) { const char *sni = mp_obj_str_get_str(args->server_hostname.u_obj); ret = mbedtls_ssl_set_hostname(&o->ssl, sni); if (ret != 0) { - assert(0); + goto cleanup; } } @@ -194,13 +193,27 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { while ((ret = mbedtls_ssl_handshake(&o->ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - //assert(0); printf("mbedtls_ssl_handshake error: -%x\n", -ret); - mp_raise_OSError(MP_EIO); + goto cleanup; } } return o; + +cleanup: + mbedtls_pk_free(&o->pkey); + mbedtls_x509_crt_free(&o->cert); + mbedtls_x509_crt_free(&o->cacert); + mbedtls_ssl_free(&o->ssl); + mbedtls_ssl_config_free(&o->conf); + mbedtls_ctr_drbg_free(&o->ctr_drbg); + mbedtls_entropy_free(&o->entropy); + + if (ret == MBEDTLS_ERR_SSL_ALLOC_FAILED) { + mp_raise_OSError(MP_ENOMEM); + } else { + mp_raise_OSError(MP_EIO); + } } STATIC mp_obj_t mod_ssl_getpeercert(mp_obj_t o_in, mp_obj_t binary_form) { From 29dd6a7678dfbc63ef20a273014ed762af6c738f Mon Sep 17 00:00:00 2001 From: Eric Poulsen Date: Wed, 27 Sep 2017 14:59:49 -0700 Subject: [PATCH 146/828] esp32: Implement wired Ethernet via network.LAN(). Updates to Makefile, modnetwork.c, and addition of network_lan.c to implement `network.LAN()` object for wired PHY objects. --- ports/esp32/Makefile | 4 + ports/esp32/modnetwork.c | 44 ++++++--- ports/esp32/modnetwork.h | 34 +++++++ ports/esp32/network_lan.c | 203 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 270 insertions(+), 15 deletions(-) create mode 100644 ports/esp32/modnetwork.h create mode 100644 ports/esp32/network_lan.c diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index a533fd31d..48d4b9d5c 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -136,6 +136,7 @@ SRC_C = \ machine_uart.c \ modmachine.c \ modnetwork.c \ + network_lan.c \ modsocket.c \ modesp.c \ moduhashlib.c \ @@ -267,6 +268,9 @@ ESPIDF_CXX_O = $(addprefix $(ESPCOMP)/cxx/,\ ESPIDF_ETHERNET_O = $(addprefix $(ESPCOMP)/ethernet/,\ emac_dev.o \ emac_main.o \ + eth_phy/phy_tlk110.o \ + eth_phy/phy_lan8720.o \ + eth_phy/phy_common.o \ ) $(BUILD)/$(ESPCOMP)/expat/%.o: CFLAGS += -Wno-unused-function diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 83f321dc2..3c348fc15 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -7,6 +7,7 @@ * The MIT License (MIT) * * Copyright (c) 2016, 2017 Nick Moore @mnemote + * Copyright (c) 2017 "Eric Poulsen" * * Based on esp8266/modnetwork.c which is Copyright (c) 2015 Paul Sokolovsky * And the ESP IDF example code which is Public Domain / CC0 @@ -48,6 +49,8 @@ #include "lwip/dns.h" #include "tcpip_adapter.h" +#include "modnetwork.h" + #define MODNETWORK_INCLUDE_CONSTANTS (1) NORETURN void _esp_exceptions(esp_err_t e) { @@ -122,7 +125,7 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) { ESP_LOGI("wifi", "STA_START"); break; case SYSTEM_EVENT_STA_GOT_IP: - ESP_LOGI("wifi", "GOT_IP"); + ESP_LOGI("network", "GOT_IP"); break; case SYSTEM_EVENT_STA_DISCONNECTED: { // This is a workaround as ESP32 WiFi libs don't currently @@ -161,7 +164,7 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) { break; } default: - ESP_LOGI("wifi", "event %d", event->event_id); + ESP_LOGI("network", "event %d", event->event_id); break; } return ESP_OK; @@ -182,6 +185,19 @@ STATIC void require_if(mp_obj_t wlan_if, int if_no) { } STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) { + static int initialized = 0; + if (!initialized) { + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_LOGD("modnetwork", "Initializing WiFi"); + ESP_EXCEPTIONS( esp_wifi_init(&cfg) ); + ESP_EXCEPTIONS( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_LOGD("modnetwork", "Initialized"); + ESP_EXCEPTIONS( esp_wifi_set_mode(0) ); + ESP_EXCEPTIONS( esp_wifi_start() ); + ESP_LOGD("modnetwork", "Started"); + initialized = 1; + } + int idx = (n_args > 0) ? mp_obj_get_int(args[0]) : WIFI_IF_STA; if (idx == WIFI_IF_STA) { return MP_OBJ_FROM_PTR(&wlan_sta_obj); @@ -201,14 +217,6 @@ STATIC mp_obj_t esp_initialize() { ESP_LOGD("modnetwork", "Initializing Event Loop"); ESP_EXCEPTIONS( esp_event_loop_init(event_handler, NULL) ); ESP_LOGD("modnetwork", "esp_event_loop_init done"); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_LOGD("modnetwork", "Initializing WiFi"); - ESP_EXCEPTIONS( esp_wifi_init(&cfg) ); - ESP_EXCEPTIONS( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - ESP_LOGD("modnetwork", "Initialized"); - ESP_EXCEPTIONS( esp_wifi_set_mode(0) ); - ESP_EXCEPTIONS( esp_wifi_start() ); - ESP_LOGD("modnetwork", "Started"); initialized = 1; } return mp_const_none; @@ -358,11 +366,11 @@ STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { netutils_parse_ipv4_addr(items[2], (void*)&info.gw, NETUTILS_BIG); netutils_parse_ipv4_addr(items[3], (void*)&dns_info.ip, NETUTILS_BIG); // To set a static IP we have to disable DHCP first - if (self->if_id == WIFI_IF_STA) { - esp_err_t e = tcpip_adapter_dhcpc_stop(WIFI_IF_STA); + if (self->if_id == WIFI_IF_STA || self->if_id == ESP_IF_ETH) { + esp_err_t e = tcpip_adapter_dhcpc_stop(self->if_id); if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); - ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_STA, &info)); - ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(WIFI_IF_STA, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); + ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(self->if_id, &info)); + ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(self->if_id, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); } else if (self->if_id == WIFI_IF_AP) { esp_err_t e = tcpip_adapter_dhcps_stop(WIFI_IF_AP); if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); @@ -374,7 +382,7 @@ STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { } } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj, 1, 2, esp_ifconfig); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj, 1, 2, esp_ifconfig); STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { if (n_args != 1 && kwargs->used != 0) { @@ -533,6 +541,7 @@ STATIC const mp_map_elem_t mp_module_network_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_network) }, { MP_OBJ_NEW_QSTR(MP_QSTR___init__), (mp_obj_t)&esp_initialize_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_WLAN), (mp_obj_t)&get_wlan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LAN), (mp_obj_t)&get_lan_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_phy_mode), (mp_obj_t)&esp_phy_mode_obj }, #if MODNETWORK_INCLUDE_CONSTANTS @@ -560,6 +569,11 @@ STATIC const mp_map_elem_t mp_module_network_globals_table[] = { MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WPA_WPA2_PSK) }, { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_MAX), MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_MAX) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_PHY_LAN8720), + MP_OBJ_NEW_SMALL_INT(PHY_LAN8720) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PHY_TLK110), + MP_OBJ_NEW_SMALL_INT(PHY_TLK110) }, #endif }; diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h new file mode 100644 index 000000000..d9f56591e --- /dev/null +++ b/ports/esp32/modnetwork.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 "Eric Poulsen" + * + * 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_ESP32_MODNETWORK_H +#define MICROPY_INCLUDED_ESP32_MODNETWORK_H + +enum { PHY_LAN8720, PHY_TLK110 }; + +MP_DECLARE_CONST_FUN_OBJ_KW(get_lan_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj); + +#endif diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c new file mode 100644 index 000000000..fba4de73a --- /dev/null +++ b/ports/esp32/network_lan.c @@ -0,0 +1,203 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 "Eric Poulsen" + * + * Based on the ESP IDF example code which is Public Domain / CC0 + * + * 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" + +#include "eth_phy/phy.h" +#include "eth_phy/phy_tlk110.h" +#include "eth_phy/phy_lan8720.h" +#include "tcpip_adapter.h" + +#include "modnetwork.h" + +typedef struct _lan_if_obj_t { + mp_obj_base_t base; + int if_id; // MUST BE FIRST to match wlan_if_obj_t + bool initialized; + bool active; + uint8_t mdc_pin; + uint8_t mdio_pin; + int8_t phy_power_pin; + uint8_t phy_addr; + uint8_t phy_type; + eth_phy_check_link_func link_func; + eth_phy_power_enable_func power_func; +} lan_if_obj_t; + +const mp_obj_type_t lan_if_type; +STATIC lan_if_obj_t lan_obj = {{&lan_if_type}, ESP_IF_ETH, false, false}; + +STATIC void phy_power_enable(bool enable) { + lan_if_obj_t* self = &lan_obj; + + if (self->phy_power_pin != -1) { + + if (!enable) { + // Do the PHY-specific power_enable(false) function before powering down + self->power_func(false); + } + + gpio_pad_select_gpio(self->phy_power_pin); + gpio_set_direction(self->phy_power_pin, GPIO_MODE_OUTPUT); + if (enable) { + gpio_set_level(self->phy_power_pin, 1); + } else { + gpio_set_level(self->phy_power_pin, 0); + } + + // Allow the power up/down to take effect, min 300us + vTaskDelay(1); + + if (enable) { + // Run the PHY-specific power on operations now the PHY has power + self->power_func(true); + } + } +} + +STATIC void init_lan_rmii() { + lan_if_obj_t* self = &lan_obj; + phy_rmii_configure_data_interface_pins(); + phy_rmii_smi_configure_pins(self->mdc_pin, self->mdio_pin); +} + +STATIC mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + lan_if_obj_t* self = &lan_obj; + + if (self->initialized) { + return MP_OBJ_FROM_PTR(&lan_obj); + } + + enum { ARG_id, ARG_mdc, ARG_mdio, ARG_power, ARG_phy_addr, ARG_phy_type }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_mdc, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_mdio, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_power, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_phy_addr, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_phy_type, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + }; + + 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); + + if (args[ARG_id].u_obj != mp_const_none) { + if (mp_obj_get_int(args[ARG_id].u_obj) != 0) { + mp_raise_ValueError("invalid LAN interface identifier"); + } + } + + self->mdc_pin = machine_pin_get_id(args[ARG_mdc].u_obj); + self->mdio_pin = machine_pin_get_id(args[ARG_mdio].u_obj); + self->phy_power_pin = args[ARG_power].u_obj == mp_const_none ? -1 : machine_pin_get_id(args[ARG_power].u_obj); + + if (args[ARG_phy_addr].u_int < 0x00 || args[ARG_phy_addr].u_int > 0x1f) { + mp_raise_ValueError("invalid phy address"); + } + + if (args[ARG_phy_type].u_int != PHY_LAN8720 && args[ARG_phy_type].u_int != PHY_TLK110) { + mp_raise_ValueError("invalid phy type"); + } + + eth_config_t config; + + switch (args[ARG_phy_type].u_int) { + case PHY_TLK110: + config = phy_tlk110_default_ethernet_config; + break; + case PHY_LAN8720: + config = phy_lan8720_default_ethernet_config; + break; + } + + self->link_func = config.phy_check_link; + + // Replace default power func with our own + self->power_func = config.phy_power_enable; + config.phy_power_enable = phy_power_enable; + + config.phy_addr = args[ARG_phy_addr].u_int; + config.gpio_config = init_lan_rmii; + config.tcpip_input = tcpip_adapter_eth_input; + + if (esp_eth_init(&config) == ESP_OK) { + self->active = false; + self->initialized = true; + } else { + mp_raise_msg(&mp_type_OSError, "esp_eth_init() failed"); + } + return MP_OBJ_FROM_PTR(&lan_obj); +} +MP_DEFINE_CONST_FUN_OBJ_KW(get_lan_obj, 0, get_lan); + +STATIC mp_obj_t lan_active(size_t n_args, const mp_obj_t *args) { + lan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (n_args > 1) { + if (mp_obj_is_true(args[1])) { + self->active = (esp_eth_enable() == ESP_OK); + if (!self->active) { + mp_raise_msg(&mp_type_OSError, "ethernet enable failed"); + } + } else { + self->active = !(esp_eth_disable() == ESP_OK); + if (self->active) { + mp_raise_msg(&mp_type_OSError, "ethernet disable failed"); + } + } + } + return mp_obj_new_bool(self->active); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lan_active_obj, 1, 2, lan_active); + +STATIC mp_obj_t lan_status(mp_obj_t self_in) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(lan_status_obj, lan_status); + +STATIC mp_obj_t lan_isconnected(mp_obj_t self_in) { + lan_if_obj_t *self = MP_OBJ_TO_PTR(self_in); + return self->active ? mp_obj_new_bool(self->link_func()) : mp_const_false; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(lan_isconnected_obj, lan_isconnected); + +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_ifconfig), MP_ROM_PTR(&esp_ifconfig_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(lan_if_locals_dict, lan_if_locals_dict_table); + +const mp_obj_type_t lan_if_type = { + { &mp_type_type }, + .name = MP_QSTR_LAN, + .locals_dict = (mp_obj_dict_t*)&lan_if_locals_dict, +}; From 9acd5906754dd794e964a915bbb88fce25cb3bc5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 28 Nov 2017 12:13:47 +1100 Subject: [PATCH 147/828] esp32/mpconfigport.h: Enable websocket module. --- ports/esp32/mpconfigport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index 1eb4c408d..69d17fd81 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -139,7 +139,7 @@ #define MICROPY_PY_USSL (1) #define MICROPY_SSL_MBEDTLS (1) #define MICROPY_PY_USSL_FINALISER (1) -#define MICROPY_PY_WEBSOCKET (0) +#define MICROPY_PY_WEBSOCKET (1) #define MICROPY_PY_FRAMEBUF (1) // fatfs configuration From 78302f7bb2b32ce8ad91d9d3a4ef55f5c7f8840e Mon Sep 17 00:00:00 2001 From: Alex King Date: Wed, 14 Jun 2017 21:03:42 -0500 Subject: [PATCH 148/828] esp32/modesp: Add osdebug() function to disable or change IDF logging. Code lineage: osdebug() is based loosely on the version in esp8266, but there didn't seem to be an obvious way of choosing a particular UART. The basic behavior is the same, though: provide None, and logging is disabled; provide an integer and logging is restored to the default level. To build on that, and because the IDF provides more functionality, a second parameter has now been implemented which allows the active log level to be set: esp.osdebug(uart[, level]) The module has a corresponding set of LOG_ values to set this accordingly. --- ports/esp32/modesp.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ports/esp32/modesp.c b/ports/esp32/modesp.c index caa055164..e614f77a6 100644 --- a/ports/esp32/modesp.c +++ b/ports/esp32/modesp.c @@ -30,6 +30,7 @@ #include #include "rom/gpio.h" +#include "esp_log.h" #include "esp_spi_flash.h" #include "py/runtime.h" @@ -38,6 +39,23 @@ #include "drivers/dht/dht.h" #include "modesp.h" +STATIC mp_obj_t esp_osdebug(size_t n_args, const mp_obj_t *args) { + esp_log_level_t level = LOG_LOCAL_LEVEL; + if (n_args == 2) { + level = mp_obj_get_int(args[1]); + } + if (args[0] == mp_const_none) { + // Disable logging + esp_log_level_set("*", ESP_LOG_ERROR); + } else { + // Enable logging at the given level + // TODO args[0] should set the UART to which debug is sent + esp_log_level_set("*", level); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_osdebug_obj, 1, 2, esp_osdebug); + STATIC mp_obj_t esp_flash_read(mp_obj_t offset_in, mp_obj_t buf_in) { mp_int_t offset = mp_obj_get_int(offset_in); mp_buffer_info_t bufinfo; @@ -107,6 +125,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_neopixel_write_obj, esp_neopixel_write_); STATIC const mp_rom_map_elem_t esp_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp) }, + { MP_ROM_QSTR(MP_QSTR_osdebug), MP_ROM_PTR(&esp_osdebug_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_read), MP_ROM_PTR(&esp_flash_read_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_write), MP_ROM_PTR(&esp_flash_write_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_erase), MP_ROM_PTR(&esp_flash_erase_obj) }, @@ -118,6 +138,14 @@ STATIC const mp_rom_map_elem_t esp_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_neopixel_write), MP_ROM_PTR(&esp_neopixel_write_obj) }, { MP_ROM_QSTR(MP_QSTR_dht_readinto), MP_ROM_PTR(&dht_readinto_obj) }, + + // Constants for second arg of osdebug() + { MP_ROM_QSTR(MP_QSTR_LOG_NONE), MP_ROM_INT((mp_uint_t)ESP_LOG_NONE)}, + { MP_ROM_QSTR(MP_QSTR_LOG_ERROR), MP_ROM_INT((mp_uint_t)ESP_LOG_ERROR)}, + { MP_ROM_QSTR(MP_QSTR_LOG_WARNING), MP_ROM_INT((mp_uint_t)ESP_LOG_WARN)}, + { MP_ROM_QSTR(MP_QSTR_LOG_INFO), MP_ROM_INT((mp_uint_t)ESP_LOG_INFO)}, + { MP_ROM_QSTR(MP_QSTR_LOG_DEBUG), MP_ROM_INT((mp_uint_t)ESP_LOG_DEBUG)}, + { MP_ROM_QSTR(MP_QSTR_LOG_VERBOSE), MP_ROM_INT((mp_uint_t)ESP_LOG_VERBOSE)}, }; STATIC MP_DEFINE_CONST_DICT(esp_module_globals, esp_module_globals_table); From 0593d6f5627a75d6d3cc894c4cf421158dc6cedf Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Dec 2017 14:56:28 +1100 Subject: [PATCH 149/828] esp32/Makefile: Support using IDF_PATH as the env var to the IDF source. --- ports/esp32/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 48d4b9d5c..3f0e47c17 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -23,8 +23,12 @@ CROSS_COMPILE ?= xtensa-esp32-elf- # paths to ESP IDF and its components ifeq ($(ESPIDF),) +ifneq ($(IDF_PATH),) +ESPIDF = $(IDF_PATH) +else $(error Please configure the ESPIDF variable) endif +endif ESPCOMP = $(ESPIDF)/components ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py From 19c7098e183a8148c541fe34c3fc1252dbbb90db Mon Sep 17 00:00:00 2001 From: Eric Poulsen Date: Sat, 9 Dec 2017 23:45:50 -0800 Subject: [PATCH 150/828] esp32: Add custom partitions.csv file with uPy specific size. --- ports/esp32/Makefile | 2 +- ports/esp32/partitions.csv | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 ports/esp32/partitions.csv diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 3f0e47c17..2fa8c1c50 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -721,7 +721,7 @@ $(BUILD)/bootloader.elf: $(BOOTLOADER_OBJ) # Declarations to build the partitions PYTHON2 ?= python2 -PART_SRC = $(ESPCOMP)/partition_table/partitions_singleapp.csv +PART_SRC = partitions.csv $(BUILD)/partitions.bin: $(PART_SRC) $(ECHO) "Create $@" diff --git a/ports/esp32/partitions.csv b/ports/esp32/partitions.csv new file mode 100644 index 000000000..98adcd20a --- /dev/null +++ b/ports/esp32/partitions.csv @@ -0,0 +1,5 @@ +# 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, 0x180000, From 1e2b78111b196207fa632c3091c75b91d0ccd75e Mon Sep 17 00:00:00 2001 From: Ryan Finnie Date: Sat, 9 Dec 2017 22:52:27 -0800 Subject: [PATCH 151/828] docs/esp8266/tutorial: Fix typo, change -> changed. --- docs/esp8266/tutorial/powerctrl.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/esp8266/tutorial/powerctrl.rst b/docs/esp8266/tutorial/powerctrl.rst index 3502624ab..289b4c42f 100644 --- a/docs/esp8266/tutorial/powerctrl.rst +++ b/docs/esp8266/tutorial/powerctrl.rst @@ -14,7 +14,7 @@ current frequency use:: >>> machine.freq() 80000000 -By default the CPU runs at 80MHz. It can be change to 160MHz if you need more +By default the CPU runs at 80MHz. It can be changed to 160MHz if you need more processing power, at the expense of current consumption:: >>> machine.freq(160000000) From 140bbced6f270d9baf3df3b725932ecbbd5a4577 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 19:45:57 +0200 Subject: [PATCH 152/828] lib/upytesthelper: MicroPython test helper layer on top of tinytest. Tinytest is classical assert-style framework, but MicroPython tests work in different way - they produce content, and that content should be matched against expected one to see if test passes. upytesthelper exactly adds helper functions to make that possible. --- lib/upytesthelper/upytesthelper.c | 126 ++++++++++++++++++++++++++++++ lib/upytesthelper/upytesthelper.h | 37 +++++++++ 2 files changed, 163 insertions(+) create mode 100644 lib/upytesthelper/upytesthelper.c create mode 100644 lib/upytesthelper/upytesthelper.h diff --git a/lib/upytesthelper/upytesthelper.c b/lib/upytesthelper/upytesthelper.c new file mode 100644 index 000000000..d28c5b9cc --- /dev/null +++ b/lib/upytesthelper/upytesthelper.c @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Linaro Limited + * + * 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/mphal.h" +#include "py/gc.h" +#include "py/runtime.h" +#include "py/compile.h" +#include "upytesthelper.h" + +static const char *test_exp_output; +static int test_exp_output_len, test_rem_output_len; +static int test_failed; +static void *heap_start, *heap_end; + +void upytest_set_heap(void *start, void *end) { + heap_start = start; + heap_end = end; +} + +void upytest_set_expected_output(const char *output, unsigned len) { + test_exp_output = output; + test_exp_output_len = test_rem_output_len = len; + test_failed = false; +} + +bool upytest_is_failed(void) { + if (test_failed) { + return true; + } + #if 0 + if (test_rem_output_len != 0) { + printf("remaining len: %d\n", test_rem_output_len); + } + #endif + return test_rem_output_len != 0; +} + +// MP_PLAT_PRINT_STRN() should be redirected to this function. +// It will pass-thru any content to mp_hal_stdout_tx_strn_cooked() +// (the dfault value of MP_PLAT_PRINT_STRN), but will also match +// it to the expected output as set by upytest_set_expected_output(). +// If mismatch happens, upytest_is_failed() returns true. +void upytest_output(const char *str, mp_uint_t len) { + if (!test_failed) { + if (len > test_rem_output_len) { + test_failed = true; + } else { + test_failed = memcmp(test_exp_output, str, len); + #if 0 + if (test_failed) { + printf("failed after char %u, within %d chars, res: %d\n", + test_exp_output_len - test_rem_output_len, (int)len, test_failed); + for (int i = 0; i < len; i++) { + if (str[i] != test_exp_output[i]) { + printf("%d %02x %02x\n", i, str[i], test_exp_output[i]); + } + } + } + #endif + test_exp_output += len; + test_rem_output_len -= len; + } + } + mp_hal_stdout_tx_strn_cooked(str, len); +} + +void upytest_execute_test(const char *src) { + // To provide clean room for each test, interpreter and heap are + // reinitialized before running each. + gc_init(heap_start, heap_end); + mp_init(); + mp_obj_list_init(mp_sys_path, 0); + mp_obj_list_init(mp_sys_argv, 0); + + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + 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_call_function_0(module_fun); + nlr_pop(); + } else { + mp_obj_t exc = (mp_obj_t)nlr.ret_val; + if (mp_obj_is_subclass_fast(mp_obj_get_type(exc), &mp_type_SystemExit)) { + // Assume that sys.exit() is called to skip the test. + // TODO: That can be always true, we should set up convention to + // use specific exit code as skip indicator. + tinytest_set_test_skipped_(); + goto end; + } + mp_obj_print_exception(&mp_plat_print, exc); + tt_abort_msg("Uncaught exception\n"); + } + + if (upytest_is_failed()) { + tinytest_set_test_failed_(); + } + +end: + mp_deinit(); +} diff --git a/lib/upytesthelper/upytesthelper.h b/lib/upytesthelper/upytesthelper.h new file mode 100644 index 000000000..3a292befd --- /dev/null +++ b/lib/upytesthelper/upytesthelper.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Linaro Limited + * + * 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 +#include "py/mpconfig.h" +#include "lib/tinytest/tinytest.h" +#include "lib/tinytest/tinytest_macros.h" + +void upytest_set_heap(void *start, void *end); +void upytest_set_expected_output(const char *output, unsigned len); +void upytest_execute_test(const char *src); +void upytest_output(const char *str, mp_uint_t len); +bool upytest_is_failed(void); From 48e931e1d33d008c2622d3fbc5ab7e4a49296bf2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 19:52:06 +0200 Subject: [PATCH 153/828] tools/tinytest-codegen.py: Generate code for upytesthelper. The way tinytest was used in qemu-arm test target is that it didn't test much. MicroPython tests are based on matching the test output against reference output, but qemu-arm's implementation didn't do that, it effectively tested just that there was no exception during test execution. "upytesthelper" wrapper was introduce to fix it, and so test generator is now switched to generate test code for it. Also, fix PEP8 and other codestyle issues. --- tools/tinytest-codegen.py | 48 ++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index dadfea1cc..062a00935 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -1,32 +1,38 @@ -#! /usr/bin/env python3 +#!/usr/bin/env python3 import os, sys from glob import glob from re import sub def escape(s): - lookup = { - '\0': '\\0', - '\t': '\\t', - '\n': '\\n\"\n\"', - '\r': '\\r', - '\\': '\\\\', - '\"': '\\\"', - } - return "\"\"\n\"{}\"".format(''.join([lookup[x] if x in lookup else x for x in s])) + s = s.decode() + lookup = { + '\0': '\\0', + '\t': '\\t', + '\n': '\\n\"\n\"', + '\r': '\\r', + '\\': '\\\\', + '\"': '\\\"', + } + return "\"\"\n\"{}\"".format(''.join([lookup[x] if x in lookup else x for x in s])) def chew_filename(t): - return { 'func': "test_{}_fn".format(sub(r'/|\.|-', '_', t)), 'desc': t.split('/')[1] } + return { 'func': "test_{}_fn".format(sub(r'/|\.|-', '_', t)), 'desc': t.split('/')[1] } -def script_to_map(t): - r = { 'name': chew_filename(t)['func'] } - with open(t) as f: r['script'] = escape(''.join(f.readlines())) - return r +def script_to_map(test_file): + r = {"name": chew_filename(test_file)["func"]} + with open(test_file, "rb") as f: + r["script"] = escape(f.read()) + with open(test_file + ".exp", "rb") as f: + r["output"] = escape(f.read()) + return r test_function = ( "void {name}(void* data) {{\n" - " const char * pystr = {script};\n" - " do_str(pystr);\n" + " static const char pystr[] = {script};\n" + " static const char exp[] = {output};\n" + " upytest_set_expected_output(exp, sizeof(exp) - 1);\n" + " upytest_execute_test(pystr);\n" "}}" ) @@ -57,10 +63,10 @@ exclude_tests = ( output = [] for group in test_dirs: - tests = [test for test in glob('{}/*.py'.format(group)) if test not in exclude_tests] - output.extend([test_function.format(**script_to_map(test)) for test in tests]) - testcase_members = [testcase_member.format(**chew_filename(test)) for test in tests] - output.append(testcase_struct.format(name=group, body='\n'.join(testcase_members))) + tests = [test for test in glob('{}/*.py'.format(group)) if test not in exclude_tests] + output.extend([test_function.format(**script_to_map(test)) for test in tests]) + testcase_members = [testcase_member.format(**chew_filename(test)) for test in tests] + output.append(testcase_struct.format(name=group, body='\n'.join(testcase_members))) testgroup_members = [testgroup_member.format(name=group) for group in test_dirs] From 4db6a7adbe25ba2a096cd70ec4072c168fdb4bd4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 19:59:11 +0200 Subject: [PATCH 154/828] tools/tinytest-codegen: Wrap lines of exclude_tests. So it was manageable and extensible. --- tools/tinytest-codegen.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index 062a00935..6744446ea 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -54,10 +54,16 @@ testgroup_member = ( # currently these tests are selected because they pass on qemu-arm test_dirs = ('basics', 'micropython', 'float', 'extmod', 'inlineasm') # 'import', 'io', 'misc') exclude_tests = ( - 'float/float2int_doubleprec_intbig.py', # requires double precision floating point to work - 'inlineasm/asmfpaddsub.py', 'inlineasm/asmfpcmp.py', 'inlineasm/asmfpldrstr.py', 'inlineasm/asmfpmuldiv.py', 'inlineasm/asmfpsqrt.py', - 'extmod/ticks_diff.py', 'extmod/time_ms_us.py', 'extmod/uheapq_timeq.py', - 'extmod/vfs_fat_ramdisk.py', 'extmod/vfs_fat_fileio.py', 'extmod/vfs_fat_fsusermount.py', 'extmod/vfs_fat_oldproto.py', + 'extmod/ticks_diff.py', + 'extmod/time_ms_us.py', + 'extmod/uheapq_timeq.py', + 'extmod/vfs_fat_ramdisk.py', 'extmod/vfs_fat_fileio.py', + 'extmod/vfs_fat_fsusermount.py', 'extmod/vfs_fat_oldproto.py', + # requires double precision floating point to work + 'float/float2int_doubleprec_intbig.py', + # inline asm tests + 'inlineasm/asmfpaddsub.py', 'inlineasm/asmfpcmp.py', 'inlineasm/asmfpldrstr.py', + 'inlineasm/asmfpmuldiv.py','inlineasm/asmfpsqrt.py', ) output = [] From e6f0d547ab16096d7a2c24824c6e398c082c0d50 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 20:00:25 +0200 Subject: [PATCH 155/828] tools/tinytest-codegen: More excludes after enabling expected output match. --- tools/tinytest-codegen.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index 6744446ea..d7761cd5a 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -54,16 +54,31 @@ testgroup_member = ( # currently these tests are selected because they pass on qemu-arm test_dirs = ('basics', 'micropython', 'float', 'extmod', 'inlineasm') # 'import', 'io', 'misc') exclude_tests = ( + # pattern matching in .exp + 'basics/bytes_compare3.py', 'extmod/ticks_diff.py', 'extmod/time_ms_us.py', 'extmod/uheapq_timeq.py', + # unicode char issue + 'extmod/ujson_loads.py', + # doesn't output to python stdout + 'extmod/ure_debug.py', + 'extmod/vfs_basic.py', 'extmod/vfs_fat_ramdisk.py', 'extmod/vfs_fat_fileio.py', 'extmod/vfs_fat_fsusermount.py', 'extmod/vfs_fat_oldproto.py', + # rounding issues + 'float/float_divmod.py', # requires double precision floating point to work 'float/float2int_doubleprec_intbig.py', - # inline asm tests + 'float/float_parse_doubleprec.py', + # inline asm FP tests (require Cortex-M4) 'inlineasm/asmfpaddsub.py', 'inlineasm/asmfpcmp.py', 'inlineasm/asmfpldrstr.py', 'inlineasm/asmfpmuldiv.py','inlineasm/asmfpsqrt.py', + # different filename in output + 'micropython/emg_exc.py', + 'micropython/heapalloc_traceback.py', + # pattern matching in .exp + 'micropython/meminfo.py', ) output = [] From ea6bddbf8125e09d5d779d7203d402dbc093379d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 20:04:27 +0200 Subject: [PATCH 156/828] ports/qemu-arm: Rework "test" target using upytesthelper. The way tinytest was used in qemu-arm test target is that it didn't test much. MicroPython tests are based on matching the test output against reference output, but qemu-arm's implementation didn't do that, it effectively tested just that there was no exception during test execution. "upytesthelper" wrapper was introduce to fix it, so switch test implementation to use it. This requires passing different CFLAGS when building the firmware, so split out test-related parts to Makefile.test. --- ports/qemu-arm/Makefile | 20 +------------------- ports/qemu-arm/Makefile.test | 24 ++++++++++++++++++++++++ ports/qemu-arm/README.md | 4 ++++ ports/qemu-arm/mpconfigport.h | 7 +++++++ ports/qemu-arm/test_main.c | 30 +----------------------------- 5 files changed, 37 insertions(+), 48 deletions(-) create mode 100644 ports/qemu-arm/Makefile.test diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index c0d257a3e..95f349beb 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -46,7 +46,7 @@ SRC_RUN_C = \ SRC_TEST_C = \ test_main.c \ -LIB_SRC_C = $(addprefix lib/,\ +LIB_SRC_C += $(addprefix lib/,\ libm/math.c \ libm/fmodf.c \ libm/nearbyintf.c \ @@ -91,27 +91,9 @@ all: run run: $(BUILD)/firmware.elf qemu-system-arm -machine integratorcp -cpu cortex-m3 -nographic -monitor null -serial null -semihosting -kernel $(BUILD)/firmware.elf -test: $(BUILD)/firmware-test.elf - qemu-system-arm -machine integratorcp -cpu cortex-m3 -nographic -monitor null -serial null -semihosting -kernel $(BUILD)/firmware-test.elf > $(BUILD)/console.out - $(Q)tail -n2 $(BUILD)/console.out - $(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0" - -.PHONY: $(BUILD)/genhdr/tests.h - -$(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h -$(BUILD)/genhdr/tests.h: - $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py) > $@ - -$(BUILD)/tinytest.o: - $(Q)$(CC) $(CFLAGS) -DNO_FORKING -o $@ -c $(TINYTEST)/tinytest.c - ## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here. $(BUILD)/firmware.elf: $(OBJ_COMMON) $(OBJ_RUN) $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(Q)$(SIZE) $@ -$(BUILD)/firmware-test.elf: $(OBJ_COMMON) $(OBJ_TEST) - $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) - $(Q)$(SIZE) $@ - include $(TOP)/py/mkrules.mk diff --git a/ports/qemu-arm/Makefile.test b/ports/qemu-arm/Makefile.test new file mode 100644 index 000000000..a9aace6d0 --- /dev/null +++ b/ports/qemu-arm/Makefile.test @@ -0,0 +1,24 @@ +LIB_SRC_C = lib/upytesthelper/upytesthelper.c + +include Makefile + +CFLAGS += -DTEST + +.PHONY: $(BUILD)/genhdr/tests.h + +$(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h +$(BUILD)/genhdr/tests.h: + (cd $(TOP)/tests; ./run-tests --write-exp) + $(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py) > $@ + +$(BUILD)/tinytest.o: + $(Q)$(CC) $(CFLAGS) -DNO_FORKING -o $@ -c $(TINYTEST)/tinytest.c + +$(BUILD)/firmware-test.elf: $(OBJ_COMMON) $(OBJ_TEST) + $(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) + $(Q)$(SIZE) $@ + +test: $(BUILD)/firmware-test.elf + qemu-system-arm -machine integratorcp -cpu cortex-m3 -nographic -monitor null -serial null -semihosting -kernel $(BUILD)/firmware-test.elf > $(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/README.md b/ports/qemu-arm/README.md index 0cf93c7d5..4f1e79b10 100644 --- a/ports/qemu-arm/README.md +++ b/ports/qemu-arm/README.md @@ -21,3 +21,7 @@ toolchain and not with CodeSourcery toolchain. You will need to modify The difference is that CodeSourcery needs `-T generic-m-hosted.ld` while ARM's version requires `--specs=nano.specs --specs=rdimon.specs` to be passed to the linker. + +To build and run image with builtin testsuite: + + make -f Makefile.test test diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index 51706b927..5d8619198 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -17,6 +17,7 @@ #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_WARNINGS (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (1) #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) @@ -69,3 +70,9 @@ extern const struct _mp_obj_module_t mp_module_uos; // We need to provide a declaration/definition of alloca() #include + +#ifdef TEST +#include "lib/upytesthelper/upytesthelper.h" +#undef MP_PLAT_PRINT_STRN +#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) +#endif diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index acac66e6a..926478ea1 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -17,41 +17,13 @@ #define HEAP_SIZE (128 * 1024) STATIC void *heap; -void do_str(const char *src); -inline void do_str(const char *src) { - gc_init(heap, (char*)heap + HEAP_SIZE); - mp_init(); - - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - 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_call_function_0(module_fun); - nlr_pop(); - } else { - mp_obj_t exc = (mp_obj_t)nlr.ret_val; - if (mp_obj_is_subclass_fast(mp_obj_get_type(exc), &mp_type_SystemExit)) { - // Assume that sys.exit() is called to skip the test. - // TODO: That can be always true, we should set up convention to - // use specific exit code as skip indicator. - tinytest_set_test_skipped_(); - goto end; - } - mp_obj_print_exception(&mp_plat_print, exc); - tt_abort_msg("Uncaught exception"); - } -end: - mp_deinit(); -} - #include "genhdr/tests.h" int main() { mp_stack_ctrl_init(); mp_stack_set_limit(10240); heap = malloc(HEAP_SIZE); + upytest_set_heap(heap, (char*)heap + HEAP_SIZE); int r = tinytest_main(0, NULL, groups); printf("status: %d\n", r); return r; From 8462f167dcc7accb0b4329ce2ea57d4c400b29ee Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 11 Dec 2017 20:22:31 +0200 Subject: [PATCH 157/828] .travis.yml: Update for qemu-arm's testing moved to Makefile.test. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fd23a3707..4a9d238ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,7 +37,7 @@ script: - make -C ports/unix - make -C ports/unix nanbox - make -C ports/bare-arm - - make -C ports/qemu-arm test + - make -C ports/qemu-arm -f Makefile.test test - make -C ports/stm32 - make -C ports/stm32 BOARD=PYBV11 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 - make -C ports/stm32 BOARD=STM32F769DISC From 3f6d3ccc11866ea6f84fa43fb50ac9fe5a171fe8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Dec 2017 18:33:39 +1100 Subject: [PATCH 158/828] stm32/usbdev: Pass thru correct val for SCSI PreventAllowMediumRemoval. This value is "1" when the medium should not be removed, "0" otherwise. --- ports/stm32/usbdev/class/src/usbd_msc_scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/usbdev/class/src/usbd_msc_scsi.c b/ports/stm32/usbdev/class/src/usbd_msc_scsi.c index 50cd5b971..b5363e2d4 100644 --- a/ports/stm32/usbdev/class/src/usbd_msc_scsi.c +++ b/ports/stm32/usbdev/class/src/usbd_msc_scsi.c @@ -481,7 +481,7 @@ static int8_t SCSI_AllowMediumRemoval(USBD_HandleTypeDef *pdev, uint8_t lun, ui { USBD_MSC_BOT_HandleTypeDef *hmsc = &((usbd_cdc_msc_hid_state_t*)pdev->pClassData)->MSC_BOT_ClassData; hmsc->bot_data_length = 0; - hmsc->bdev_ops->PreventAllowMediumRemoval(lun, params[0]); + hmsc->bdev_ops->PreventAllowMediumRemoval(lun, params[4]); return 0; } From 1b223a42bf79640d37567c4f427767ee97d45897 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 13 Dec 2017 22:22:57 +1100 Subject: [PATCH 159/828] extmod/modure: Add cast to workaround bug in MSVC. --- extmod/modure.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extmod/modure.c b/extmod/modure.c index d0180881d..31c2b9864 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -165,7 +165,8 @@ STATIC mp_obj_t re_split(size_t n_args, const mp_obj_t *args) { break; } } - mp_local_free(caps); + // cast is a workaround for a bug in msvc (see above) + mp_local_free((char**)caps); mp_obj_t s = mp_obj_new_str_of_type(str_type, (const byte*)subj.begin, subj.end - subj.begin); mp_obj_list_append(retval, s); From 334934ee972537d9628b9cf4f53bec952c29dcdb Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 13 Dec 2017 18:01:35 +0200 Subject: [PATCH 160/828] tests/run-tests: Add --list-tests switch. Lists tests to be executed, subject to all other filters requested. This options would be useful e.g. for scripts like tools/tinytest-codegen.py, which currently contains hardcoded filters for particular a particular target and can't work for multiple targets. --- tests/run-tests | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index 08a92a14f..45d988b25 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -380,6 +380,10 @@ def run_tests(pyb, tests, args, base_path="."): skipped_tests.append(test_name) continue + if args.list_tests: + print(test_file) + continue + # get expected output test_file_expected = test_file + '.exp' if os.path.isfile(test_file_expected): @@ -430,6 +434,9 @@ def run_tests(pyb, tests, args, base_path="."): test_count += 1 + if args.list_tests: + return True + print("{} tests performed ({} individual testcases)".format(test_count, testcase_count)) print("{} tests passed".format(passed_count)) @@ -451,6 +458,7 @@ def main(): cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') cmd_parser.add_argument('-d', '--test-dirs', nargs='*', help='input test directories (if no files given)') cmd_parser.add_argument('--write-exp', action='store_true', help='save .exp files to run tests w/o CPython') + cmd_parser.add_argument('--list-tests', action='store_true', help='list tests instead of running them') cmd_parser.add_argument('--emit', default='bytecode', help='MicroPython emitter to use (bytecode or native)') cmd_parser.add_argument('--heapsize', help='heapsize to use (use default if not specified)') cmd_parser.add_argument('--via-mpy', action='store_true', help='compile .py files to .mpy first') @@ -459,12 +467,12 @@ def main(): args = cmd_parser.parse_args() EXTERNAL_TARGETS = ('pyboard', 'wipy', 'esp8266', 'minimal') - if args.target in EXTERNAL_TARGETS: + if args.target == 'unix' or args.list_tests: + pyb = None + elif args.target in EXTERNAL_TARGETS: import pyboard pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password) pyb.enter_raw_repl() - elif args.target == 'unix': - pyb = None else: raise ValueError('target must be either %s or unix' % ", ".join(EXTERNAL_TARGETS)) From 43141ddb55c715e90b60d2fa2d9d5e01cc66b511 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 13 Dec 2017 18:32:25 +0200 Subject: [PATCH 161/828] tools/tinytest-codegen: Take --target= option for test set selection. Gets passed to run-tests --list-tests to get actual list of tests to use. If --target= is not given, legacy set hardcoded in tinytest-codegen itself is used. Also, get rid of tinytest test groups - they aren't really used for anything, and only complicate processing. Besides, one of the next step is to limit number of tests per a generated file to control the binary size, which also will require "flat" list of tests. --- tools/tinytest-codegen.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index d7761cd5a..c6ba1867a 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -3,6 +3,8 @@ import os, sys from glob import glob from re import sub +import argparse + def escape(s): s = s.decode() @@ -17,7 +19,7 @@ def escape(s): return "\"\"\n\"{}\"".format(''.join([lookup[x] if x in lookup else x for x in s])) def chew_filename(t): - return { 'func': "test_{}_fn".format(sub(r'/|\.|-', '_', t)), 'desc': t.split('/')[1] } + return { 'func': "test_{}_fn".format(sub(r'/|\.|-', '_', t)), 'desc': t } def script_to_map(test_file): r = {"name": chew_filename(test_file)["func"]} @@ -47,7 +49,7 @@ testgroup_struct = ( "struct testgroup_t groups[] = {{\n{body}\n END_OF_GROUPS\n}};" ) testgroup_member = ( - " {{ \"{name}/\", {name}_tests }}," + " {{ \"{name}\", {name}_tests }}," ) ## XXX: may be we could have `--without ` argument... @@ -82,14 +84,24 @@ exclude_tests = ( ) output = [] +tests = [] -for group in test_dirs: - tests = [test for test in glob('{}/*.py'.format(group)) if test not in exclude_tests] - output.extend([test_function.format(**script_to_map(test)) for test in tests]) - testcase_members = [testcase_member.format(**chew_filename(test)) for test in tests] - output.append(testcase_struct.format(name=group, body='\n'.join(testcase_members))) +argparser = argparse.ArgumentParser(description='Convert native MicroPython tests to tinytest/upytesthelper C code') +argparser.add_argument('--target', help='the target platform') +args = argparser.parse_args() -testgroup_members = [testgroup_member.format(name=group) for group in test_dirs] +if not args.target: + for group in test_dirs: + tests += [test for test in glob('{}/*.py'.format(group)) if test not in exclude_tests] +else: + for l in os.popen("./run-tests --list-tests --target=%s" % args.target, "r"): + tests.append(l.rstrip()) + +output.extend([test_function.format(**script_to_map(test)) for test in tests]) +testcase_members = [testcase_member.format(**chew_filename(test)) for test in tests] +output.append(testcase_struct.format(name="", body='\n'.join(testcase_members))) + +testgroup_members = [testgroup_member.format(name=group) for group in [""]] output.append(testgroup_struct.format(body='\n'.join(testgroup_members))) From bb516af1eb97d0c24fa12b00bfde196b3db94c66 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 14 Dec 2017 10:08:37 +1100 Subject: [PATCH 162/828] tools/pydfu.py: Call set_configuration() on fresh USB device object. This call is required before using the device (some operating systems don't need it but others do). Fixes issue #3476. --- tools/pydfu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/pydfu.py b/tools/pydfu.py index 8c0220de8..4296f07bf 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -81,6 +81,7 @@ def init(): if len(devices) > 1: raise ValueError("Multiple DFU devices found") __dev = devices[0] + __dev.set_configuration() # Claim DFU interface usb.util.claim_interface(__dev, __DFU_INTERFACE) From badaf3ecfe5d0c8158debf4bd0cb1458016c77f4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 14 Dec 2017 10:40:35 +1100 Subject: [PATCH 163/828] esp8266/machine_hspi: After an SPI write wait for last byte to transfer. Because otherwise the function can return with data still waiting to be clocked out, and CS might then be disabled before the SPI transaction is complete. Fixes issue #3487. --- ports/esp8266/machine_hspi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/esp8266/machine_hspi.c b/ports/esp8266/machine_hspi.c index 9fd0f4868..ff7728291 100644 --- a/ports/esp8266/machine_hspi.c +++ b/ports/esp8266/machine_hspi.c @@ -65,6 +65,9 @@ STATIC void machine_hspi_transfer(mp_obj_base_t *self_in, size_t len, const uint spi_tx8fast(HSPI, src[i]); ++i; } + // wait for SPI transaction to complete + while (spi_busy(HSPI)) { + } } else { // we need to read and write data From 36f79523abe8d79fec1cc7af41e8e96e8ceb2cc4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 14 Dec 2017 12:25:30 +1100 Subject: [PATCH 164/828] tests: Add tests to improve coverage of py/objtype.c. --- tests/basics/class_inherit_mul.py | 5 +++++ tests/basics/class_super_multinherit.py | 16 ++++++++++++++++ tests/basics/sys_getsizeof.py | 15 +++++++++++++++ tests/misc/non_compliant.py | 15 +++++++++++++++ tests/misc/non_compliant.py.exp | 2 ++ 5 files changed, 53 insertions(+) create mode 100644 tests/basics/class_super_multinherit.py create mode 100644 tests/basics/sys_getsizeof.py diff --git a/tests/basics/class_inherit_mul.py b/tests/basics/class_inherit_mul.py index 23476132b..4a43a7f41 100644 --- a/tests/basics/class_inherit_mul.py +++ b/tests/basics/class_inherit_mul.py @@ -1,3 +1,5 @@ +# test multiple inheritance of user classes + class A: def __init__(self, x): print('A init', x) @@ -30,6 +32,9 @@ class Sub(A, B): def g(self): print(self.x) +print(issubclass(Sub, A)) +print(issubclass(Sub, B)) + o = Sub() print(o.x) o.f() diff --git a/tests/basics/class_super_multinherit.py b/tests/basics/class_super_multinherit.py new file mode 100644 index 000000000..642a73ce1 --- /dev/null +++ b/tests/basics/class_super_multinherit.py @@ -0,0 +1,16 @@ +# test super with multiple inheritance + +class A: + def foo(self): + print('A.foo') + +class B: + def foo(self): + print('B.foo') + +class C(A, B): + def foo(self): + print('C.foo') + super().foo() + +C().foo() diff --git a/tests/basics/sys_getsizeof.py b/tests/basics/sys_getsizeof.py new file mode 100644 index 000000000..d16eb1561 --- /dev/null +++ b/tests/basics/sys_getsizeof.py @@ -0,0 +1,15 @@ +# test sys.getsizeof() function + +import sys +try: + sys.getsizeof +except AttributeError: + print('SKIP') + raise SystemExit + +print(sys.getsizeof([1, 2]) >= 2) +print(sys.getsizeof({1: 2}) >= 2) + +class A: + pass +print(sys.getsizeof(A()) > 0) diff --git a/tests/misc/non_compliant.py b/tests/misc/non_compliant.py index 152633c3b..31129f075 100644 --- a/tests/misc/non_compliant.py +++ b/tests/misc/non_compliant.py @@ -124,3 +124,18 @@ try: f.x = 1 except AttributeError: print('AttributeError') + +# can't call a function type (ie make new instances of a function) +try: + type(f)() +except TypeError: + print('TypeError') + +# test when object explicitly listed at not-last position in parent tuple +# this is not compliant with CPython because of illegal MRO +class A: + def foo(self): + print('A.foo') +class B(object, A): + pass +B().foo() diff --git a/tests/misc/non_compliant.py.exp b/tests/misc/non_compliant.py.exp index 9c157fd5b..061e3fcc8 100644 --- a/tests/misc/non_compliant.py.exp +++ b/tests/misc/non_compliant.py.exp @@ -18,3 +18,5 @@ b'\x01\x02' b'\x01\x00' NotImplementedError AttributeError +TypeError +A.foo From 34247465c34113bb26f6c9582ea31f31b0ea0d1b Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Sat, 15 Jul 2017 15:53:47 +0200 Subject: [PATCH 165/828] extmod/modframebuf: Add 2-bit color format (GS2_HMSB). This format is used in 2-color LED matrices and in e-ink displays like SSD1606. --- docs/library/framebuf.rst | 4 +++ extmod/modframebuf.c | 30 +++++++++++++++++ tests/extmod/framebuf2.py | 62 +++++++++++++++++++++++++++++++++++ tests/extmod/framebuf2.py.exp | 57 ++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 tests/extmod/framebuf2.py create mode 100644 tests/extmod/framebuf2.py.exp diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index 74c9f8564..4f0026e38 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -148,6 +148,10 @@ Constants Red Green Blue (16-bit, 5+6+5) color format +.. data:: framebuf.GS2_HMSB + + Grayscale (2-bit) color format + .. data:: framebuf.GS4_HMSB Grayscale (4-bit) color format diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 20e40d579..f2dc4e858 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -54,6 +54,7 @@ typedef struct _mp_framebuf_p_t { // constants for formats #define FRAMEBUF_MVLSB (0) #define FRAMEBUF_RGB565 (1) +#define FRAMEBUF_GS2_HMSB (5) #define FRAMEBUF_GS4_HMSB (2) #define FRAMEBUF_MHLSB (3) #define FRAMEBUF_MHMSB (4) @@ -130,6 +131,30 @@ STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, i } } +// Functions for GS2_HMSB format + +STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { + uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2]; + uint8_t shift = (x & 0x3) << 1; + uint8_t mask = 0x3 << shift; + uint8_t color = (col & 0x3) << shift; + *pixel = color | (*pixel & (~mask)); +} + +STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + uint8_t pixel = ((uint8_t*)fb->buf)[(x + y * fb->stride) >> 2]; + uint8_t shift = (x & 0x3) << 1; + return (pixel >> shift) & 0x3; +} + +STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { + for (int xx=x; xx < x+w; xx++) { + for (int yy=y; yy < y+h; yy++) { + gs2_hmsb_setpixel(fb, xx, yy, col); + } + } +} + // Functions for GS4_HMSB format STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { @@ -184,6 +209,7 @@ STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, STATIC mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, + [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, @@ -240,6 +266,9 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size case FRAMEBUF_MHMSB: o->stride = (o->stride + 7) & ~7; break; + case FRAMEBUF_GS2_HMSB: + o->stride = (o->stride + 3) & ~3; + break; case FRAMEBUF_GS4_HMSB: o->stride = (o->stride + 1) & ~1; break; @@ -579,6 +608,7 @@ STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MVLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_VLSB), MP_ROM_INT(FRAMEBUF_MVLSB) }, { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, + { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) }, diff --git a/tests/extmod/framebuf2.py b/tests/extmod/framebuf2.py new file mode 100644 index 000000000..a313170eb --- /dev/null +++ b/tests/extmod/framebuf2.py @@ -0,0 +1,62 @@ +try: + import framebuf +except ImportError: + print("SKIP") + raise SystemExit + +def printbuf(): + print("--8<--") + for y in range(h): + for x in range(w): + print('%u' % ((buf[(x + y * w) // 4] >> ((x & 3) << 1)) & 3), end='') + print() + print("-->8--") + +w = 8 +h = 5 +buf = bytearray(w * h // 4) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS2_HMSB) + +# fill +fbuf.fill(3) +printbuf() +fbuf.fill(0) +printbuf() + +# put pixel +fbuf.pixel(0, 0, 1) +fbuf.pixel(3, 0, 2) +fbuf.pixel(0, 4, 3) +fbuf.pixel(3, 4, 2) +printbuf() + +# get pixel +print(fbuf.pixel(0, 4), fbuf.pixel(1, 1)) + +# scroll +fbuf.fill(0) +fbuf.pixel(2, 2, 3) +printbuf() +fbuf.scroll(0, 1) +printbuf() +fbuf.scroll(1, 0) +printbuf() +fbuf.scroll(-1, -2) +printbuf() + +w2 = 2 +h2 = 3 +buf2 = bytearray(w2 * h2 // 4) +fbuf2 = framebuf.FrameBuffer(buf2, w2, h2, framebuf.GS2_HMSB) + +# blit +fbuf2.fill(0) +fbuf2.pixel(0, 0, 1) +fbuf2.pixel(0, 2, 2) +fbuf2.pixel(1, 0, 1) +fbuf2.pixel(1, 2, 2) +fbuf.fill(3) +fbuf.blit(fbuf2, 3, 3, 0) +fbuf.blit(fbuf2, -1, -1, 0) +fbuf.blit(fbuf2, 16, 16, 0) +printbuf() diff --git a/tests/extmod/framebuf2.py.exp b/tests/extmod/framebuf2.py.exp new file mode 100644 index 000000000..c53e518a6 --- /dev/null +++ b/tests/extmod/framebuf2.py.exp @@ -0,0 +1,57 @@ +--8<-- +33333333 +33333333 +33333333 +33333333 +33333333 +-->8-- +--8<-- +00000000 +00000000 +00000000 +00000000 +00000000 +-->8-- +--8<-- +10020000 +00000000 +00000000 +00000000 +30020000 +-->8-- +3 0 +--8<-- +00000000 +00000000 +00300000 +00000000 +00000000 +-->8-- +--8<-- +00000000 +00000000 +00000000 +00300000 +00000000 +-->8-- +--8<-- +00000000 +00000000 +00000000 +00030000 +00000000 +-->8-- +--8<-- +00000000 +00300000 +00000000 +00030000 +00000000 +-->8-- +--8<-- +33333333 +23333333 +33333333 +33311333 +33333333 +-->8-- From 46b35356e1727365dd5a33fcf3a722fda82c8b08 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 14 Dec 2017 17:36:13 +1100 Subject: [PATCH 166/828] extmod/modframebuf: Add 8-bit greyscale format (GS8). --- docs/library/framebuf.rst | 4 ++++ extmod/modframebuf.c | 24 ++++++++++++++++++++++++ tests/extmod/framebuf8.py | 32 ++++++++++++++++++++++++++++++++ tests/extmod/framebuf8.py.exp | 15 +++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 tests/extmod/framebuf8.py create mode 100644 tests/extmod/framebuf8.py.exp diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst index 4f0026e38..ed4b78ab1 100644 --- a/docs/library/framebuf.rst +++ b/docs/library/framebuf.rst @@ -155,3 +155,7 @@ Constants .. data:: framebuf.GS4_HMSB Grayscale (4-bit) color format + +.. data:: framebuf.GS8 + + Grayscale (8-bit) color format diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index f2dc4e858..a7f6ba905 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -56,6 +56,7 @@ typedef struct _mp_framebuf_p_t { #define FRAMEBUF_RGB565 (1) #define FRAMEBUF_GS2_HMSB (5) #define FRAMEBUF_GS4_HMSB (2) +#define FRAMEBUF_GS8 (6) #define FRAMEBUF_MHLSB (3) #define FRAMEBUF_MHMSB (4) @@ -206,11 +207,31 @@ STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, } } +// Functions for GS8 format + +STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { + uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)]; + *pixel = col & 0xff; +} + +STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { + return ((uint8_t*)fb->buf)[(x + y * fb->stride)]; +} + +STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { + uint8_t *pixel = &((uint8_t*)fb->buf)[(x + y * fb->stride)]; + while (h--) { + memset(pixel, col, w); + pixel += fb->stride; + } +} + STATIC mp_framebuf_p_t formats[] = { [FRAMEBUF_MVLSB] = {mvlsb_setpixel, mvlsb_getpixel, mvlsb_fill_rect}, [FRAMEBUF_RGB565] = {rgb565_setpixel, rgb565_getpixel, rgb565_fill_rect}, [FRAMEBUF_GS2_HMSB] = {gs2_hmsb_setpixel, gs2_hmsb_getpixel, gs2_hmsb_fill_rect}, [FRAMEBUF_GS4_HMSB] = {gs4_hmsb_setpixel, gs4_hmsb_getpixel, gs4_hmsb_fill_rect}, + [FRAMEBUF_GS8] = {gs8_setpixel, gs8_getpixel, gs8_fill_rect}, [FRAMEBUF_MHLSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, }; @@ -272,6 +293,8 @@ STATIC mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size case FRAMEBUF_GS4_HMSB: o->stride = (o->stride + 1) & ~1; break; + case FRAMEBUF_GS8: + break; default: mp_raise_ValueError("invalid format"); } @@ -610,6 +633,7 @@ STATIC const mp_rom_map_elem_t framebuf_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_RGB565), MP_ROM_INT(FRAMEBUF_RGB565) }, { MP_ROM_QSTR(MP_QSTR_GS2_HMSB), MP_ROM_INT(FRAMEBUF_GS2_HMSB) }, { MP_ROM_QSTR(MP_QSTR_GS4_HMSB), MP_ROM_INT(FRAMEBUF_GS4_HMSB) }, + { MP_ROM_QSTR(MP_QSTR_GS8), MP_ROM_INT(FRAMEBUF_GS8) }, { MP_ROM_QSTR(MP_QSTR_MONO_HLSB), MP_ROM_INT(FRAMEBUF_MHLSB) }, { MP_ROM_QSTR(MP_QSTR_MONO_HMSB), MP_ROM_INT(FRAMEBUF_MHMSB) }, }; diff --git a/tests/extmod/framebuf8.py b/tests/extmod/framebuf8.py new file mode 100644 index 000000000..b6899aae9 --- /dev/null +++ b/tests/extmod/framebuf8.py @@ -0,0 +1,32 @@ +try: + import framebuf +except ImportError: + print("SKIP") + raise SystemExit + +def printbuf(): + print("--8<--") + for y in range(h): + for x in range(w): + print('%02x' % buf[(x + y * w)], end='') + print() + print("-->8--") + +w = 8 +h = 5 +buf = bytearray(w * h) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) + +# fill +fbuf.fill(0x55) +printbuf() + +# put pixel +fbuf.pixel(0, 0, 0x11) +fbuf.pixel(w - 1, 0, 0x22) +fbuf.pixel(0, h - 1, 0x33) +fbuf.pixel(w - 1, h - 1, 0xff) +printbuf() + +# get pixel +print(hex(fbuf.pixel(0, h - 1)), hex(fbuf.pixel(1, 1))) diff --git a/tests/extmod/framebuf8.py.exp b/tests/extmod/framebuf8.py.exp new file mode 100644 index 000000000..01d8976fe --- /dev/null +++ b/tests/extmod/framebuf8.py.exp @@ -0,0 +1,15 @@ +--8<-- +5555555555555555 +5555555555555555 +5555555555555555 +5555555555555555 +5555555555555555 +-->8-- +--8<-- +1155555555555522 +5555555555555555 +5555555555555555 +5555555555555555 +33555555555555ff +-->8-- +0x33 0x55 From 8d11fc0bc4c0b9d569a343a785bf079d6441eb16 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 14 Dec 2017 10:32:34 +0200 Subject: [PATCH 167/828] tests/run-tests: minimal: Exclude recently added subclass_native_init.py. It relies on MICROPY_CPYTHON_COMPAT being defined. --- tests/run-tests | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run-tests b/tests/run-tests index 45d988b25..35b609612 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -313,6 +313,7 @@ def run_tests(pyb, tests, args, base_path="."): skip_tests.add('misc/rge_sm.py') # too large elif args.target == 'minimal': skip_tests.add('basics/class_inplace_op.py') # all special methods not supported + skip_tests.add('basics/subclass_native_init.py')# native subclassing corner cases not support skip_tests.add('misc/rge_sm.py') # too large skip_tests.add('micropython/opt_level.py') # don't assume line numbers are stored skip_tests.add('float/float_parse.py') # minor parsing artifacts with 32-bit floats From aaeb70b7b7a670e3beb51fc2efa956d4de51a05a Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 14 Dec 2017 12:11:17 +0200 Subject: [PATCH 168/828] tests/run-tests: Fix handling of --list-tests wrt skipped tests. "skip " message could leak before. --- tests/run-tests | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index 35b609612..2330b7df0 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -376,15 +376,16 @@ def run_tests(pyb, tests, args, base_path="."): skip_it |= skip_const and is_const skip_it |= skip_revops and test_name.startswith("class_reverse_op") + if args.list_tests: + if not skip_it: + print(test_file) + continue + if skip_it: print("skip ", test_file) skipped_tests.append(test_name) continue - if args.list_tests: - print(test_file) - continue - # get expected output test_file_expected = test_file + '.exp' if os.path.isfile(test_file_expected): From 64bb32d87f35531fc003493af9b4f526b19e68f3 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 14 Dec 2017 12:26:10 +0200 Subject: [PATCH 169/828] tests/run-tests: Add composable --include and --exclude options. The idea that --list-tests would be enough to produce list of tests for tinytest-codegen didn't work, because normal run-tests processing heavily relies on dynamic target capabilities discovery, and test filtering happens as the result of that. So, approach the issue from different end - allow to specify arbitrary filtering criteria as run-tests arguments. This way, specific filters will be still hardcoded, but at least on a particular target's side, instead of constant patching tinytest-codegen and/or run-tests. --- tests/run-tests | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/run-tests b/tests/run-tests index 2330b7df0..b5f19be47 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -358,6 +358,16 @@ def run_tests(pyb, tests, args, base_path="."): for test_file in tests: test_file = test_file.replace('\\', '/') + + if args.filters: + # Default verdict is the opposit of the first action + verdict = "include" if args.filters[0][0] == "exclude" else "exclude" + for action, pat in args.filters: + if pat.search(test_file): + verdict = action + if verdict == "exclude": + continue + test_basename = os.path.basename(test_file) test_name = os.path.splitext(test_basename)[0] is_native = test_name.startswith("native_") or test_name.startswith("viper_") @@ -451,14 +461,42 @@ def run_tests(pyb, tests, args, base_path="."): # all tests succeeded return True + +class append_filter(argparse.Action): + + def __init__(self, option_strings, dest, **kwargs): + super().__init__(option_strings, dest, default=[], **kwargs) + + def __call__(self, parser, args, value, option): + if not hasattr(args, self.dest): + args.filters = [] + if option.startswith(("-e", "--e")): + option = "exclude" + else: + option = "include" + args.filters.append((option, re.compile(value))) + + def main(): - cmd_parser = argparse.ArgumentParser(description='Run tests for MicroPython.') + cmd_parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description='Run and manage tests for MicroPython.', + epilog='''\ +Options -i and -e can be multiple and processed in the order given. Regex +"search" (vs "match") operation is used. An action (include/exclude) of +the last matching regex is used: + run-tests -i async - exclude all, then include tests containg "async" anywhere + run-tests -e '/big.+int' - include all, then exclude by regex + run-tests -e async -i async_foo - include all, exclude async, yet still include async_foo +''') cmd_parser.add_argument('--target', default='unix', help='the target platform') cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device or the IP address of the pyboard') cmd_parser.add_argument('-b', '--baudrate', default=115200, help='the baud rate of the serial device') cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username') cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') cmd_parser.add_argument('-d', '--test-dirs', nargs='*', help='input test directories (if no files given)') + cmd_parser.add_argument('-e', '--exclude', action=append_filter, metavar='REGEX', dest='filters', help='exclude test by regex on path/name.py') + cmd_parser.add_argument('-i', '--include', action=append_filter, metavar='REGEX', dest='filters', help='include test by regex on path/name.py') cmd_parser.add_argument('--write-exp', action='store_true', help='save .exp files to run tests w/o CPython') cmd_parser.add_argument('--list-tests', action='store_true', help='list tests instead of running them') cmd_parser.add_argument('--emit', default='bytecode', help='MicroPython emitter to use (bytecode or native)') From 325d0fc74ba9634f89f6535210944ccdd635a995 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 14 Dec 2017 12:26:59 +0200 Subject: [PATCH 170/828] tools/tinytest-codegen: Add --stdin switch instead of recently added --target. Instead of passing thru more and more options from tinytest-codegen to run-tests --list-tests, pipe output of run-tests --list-tests into tinytest-codegen. --- tools/tinytest-codegen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index c6ba1867a..5339972cd 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -87,14 +87,14 @@ output = [] tests = [] argparser = argparse.ArgumentParser(description='Convert native MicroPython tests to tinytest/upytesthelper C code') -argparser.add_argument('--target', help='the target platform') +argparser.add_argument('--stdin', action="store_true", help='read list of tests from stdin') args = argparser.parse_args() -if not args.target: +if not args.stdin: for group in test_dirs: tests += [test for test in glob('{}/*.py'.format(group)) if test not in exclude_tests] else: - for l in os.popen("./run-tests --list-tests --target=%s" % args.target, "r"): + for l in sys.stdin: tests.append(l.rstrip()) output.extend([test_function.format(**script_to_map(test)) for test in tests]) From 3233537a1505780ac189031857f0b95d43fb9adb Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 14 Dec 2017 13:36:06 +0200 Subject: [PATCH 171/828] tests/run-tests: Don't test for --target=unix with "pyb is None". If we test for unix target, do that explicitly. pyb var will be None for commands like --list-tests too. --- tests/run-tests | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index b5f19be47..79b3e2112 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -287,14 +287,14 @@ def run_tests(pyb, tests, args, base_path="."): skip_tests.add('cmdline/cmd_parsetree.py') # Some tests shouldn't be run on a PC - if pyb is None: + if args.target == 'unix': # unix build does not have the GIL so can't run thread mutation tests for t in tests: if t.startswith('thread/mutate_'): skip_tests.add(t) # Some tests shouldn't be run on pyboard - if pyb is not None: + if args.target != 'unix': skip_tests.add('basics/exception_chain.py') # warning is not printed skip_tests.add('micropython/meminfo.py') # output is very different to PC output skip_tests.add('extmod/machine_mem.py') # raw memory access not supported From bf73ee114f4085e06181037305599fdfeba59523 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 14 Dec 2017 18:28:10 +0200 Subject: [PATCH 172/828] docs/packages: mpy_bin2res no longer required to create resources. Everything happens automagically with overridden "sdist" from sdist_upip.py. --- docs/reference/packages.rst | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index d8d198e62..3d05f0b27 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -259,8 +259,8 @@ an extra intermediate step when packaging application - creation of The idea of this module is to convert binary data to a Python bytes object, and put it into the dictionary, indexed by the resource name. -This conversion is done using ``tools/mpy_bin2res.py`` script from -the MicroPython distribution. +This conversion is done automatically using overridden ``sdist`` command +described in the previous section. Let's trace the complete process using the following example. Suppose your application has the following structure:: @@ -281,17 +281,28 @@ following calls:: pkg_resources.resource_stream(__name__, "data/image.png") You can develop and debug using the `MicroPython Unix port` as usual. -When times come to make a distribution package out of it, you would -need to run following command, with ``my_app/`` being the current -directory (and assuming ``mpy_bin2res.py`` is in your path):: +When time comes to make a distribution package out of it, just use +overridden "sdist" command from sdist_upip.py module as described in +the previous section. - mpy_bin2res.py data/page.html data/image.png +This will create a Python resource module named ``R.py``, based on the +files declared in ``MANIFEST`` or ``MANIFEST.in`` files (any non-``.py`` +file will be considered a resource and added to ``R.py``) - before +proceeding with the normal packaging steps. -This will produce a Python resource module named ``R.py``. Afterwards, -you package the project for distribution as usual (using ``setup.py sdist``). Prepared like this, your application will work both when deployed to filesystem and as frozen bytecode. +If you would like to debug ``R.py`` creation, you can run:: + + python3 setup.py sdist --manifest-only + +Alternatively, you can use tools/mpy_bin2res.py script from the +MicroPython distribution, in which can you will need to pass paths +to all resource files:: + + mpy_bin2res.py data/page.html data/image.png + References ---------- From 448d93a04aff95dd300cf3485eb7e7a74cfdfcbf Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 15 Dec 2017 00:11:02 +0200 Subject: [PATCH 173/828] docs/glossary: micropython-lib: Clarify wording. --- docs/reference/glossary.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst index 9daf0dc3a..a6abc8b9d 100644 --- a/docs/reference/glossary.rst +++ b/docs/reference/glossary.rst @@ -95,8 +95,8 @@ Glossary `micropython-lib `_ which provides implementations for many modules from CPython's standard library. However, large subset of these modules require - POSIX-like environment (Linux, MacOS, Windows may be partially - supported), and thus would work or make sense only with + POSIX-like environment (Linux, FreeBSD, MacOS, etc.; Windows may be + partially supported), and thus would work or make sense only with `MicroPython Unix port`. Some subset of modules is however usable for `baremetal` ports too. From cf8e8c29e72ef4871b9d5ab3de32bdaf429c5dbb Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 15 Dec 2017 10:21:10 +1100 Subject: [PATCH 174/828] py/emitglue: Change type of bit-field to explicitly unsigned mp_uint_t. Some compilers can treat enum types as signed, in which case 3 bits is not enough to encode all mp_raw_code_kind_t values. So change the type to mp_uint_t. --- py/emitglue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/emitglue.h b/py/emitglue.h index 43930333d..f2a48c5e5 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -40,7 +40,7 @@ typedef enum { } mp_raw_code_kind_t; typedef struct _mp_raw_code_t { - mp_raw_code_kind_t kind : 3; + mp_uint_t kind : 3; // of type mp_raw_code_kind_t mp_uint_t scope_flags : 7; mp_uint_t n_pos_args : 11; union { From 4475f324202e15b98f2f365e33078aebcf0bc5a5 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 15 Dec 2017 11:37:32 +0200 Subject: [PATCH 175/828] tools/tinytest-codegen: Ignore system locale, write output in UTF-8. Way to reproduce a UnicodeEncodeError without this patch: LC_ALL=C tinytest-codegen.py ... --- tools/tinytest-codegen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py index 5339972cd..ad3b3bbec 100755 --- a/tools/tinytest-codegen.py +++ b/tools/tinytest-codegen.py @@ -106,4 +106,5 @@ testgroup_members = [testgroup_member.format(name=group) for group in [""]] output.append(testgroup_struct.format(body='\n'.join(testgroup_members))) ## XXX: may be we could have `--output ` argument... -print('\n\n'.join(output)) +# Don't depend on what system locale is set, use utf8 encoding. +sys.stdout.buffer.write('\n\n'.join(output).encode('utf8')) From 103eeffcd925891867916c6f7d23dcbb098c59fe Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 15 Dec 2017 12:07:09 +0200 Subject: [PATCH 176/828] tests/run-tests: Skip running feature checks for --list-tests/--write-exp. The whole idea of --list-tests is that we prepare a list of tests to run later, and currently don't have a connection to target board. Similarly for --write-exp - only "python3" binary would be required for this operation, not "micropython". --- tests/run-tests | 76 +++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index 79b3e2112..45cb1a746 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -211,48 +211,56 @@ def run_tests(pyb, tests, args, base_path="."): skip_async = False skip_const = False skip_revops = False + skip_endian = False + has_complex = True + has_coverage = False - # Check if micropython.native is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'native_check.py') - if output == b'CRASH': - skip_native = True + upy_float_precision = 32 - # Check if arbitrary-precision integers are supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'int_big.py') - if output != b'1000000000000000000000000000000000000000000000\n': - skip_int_big = True + # If we're asked to --list-tests, we can't assume that there's a + # connection to target, so we can't run feature checks usefully. + if not (args.list_tests or args.write_exp): + # Check if micropython.native is supported, and skip such tests if it's not + output = run_feature_check(pyb, args, base_path, 'native_check.py') + if output == b'CRASH': + skip_native = True - # Check if set type (and set literals) is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'set_check.py') - if output == b'CRASH': - skip_set_type = True + # Check if arbitrary-precision integers are supported, and skip such tests if it's not + output = run_feature_check(pyb, args, base_path, 'int_big.py') + if output != b'1000000000000000000000000000000000000000000000\n': + skip_int_big = True - # Check if async/await keywords are supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'async_check.py') - if output == b'CRASH': - skip_async = True + # Check if set type (and set literals) is supported, and skip such tests if it's not + output = run_feature_check(pyb, args, base_path, 'set_check.py') + if output == b'CRASH': + skip_set_type = True - # Check if const keyword (MicroPython extension) is supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'const.py') - if output == b'CRASH': - skip_const = True + # Check if async/await keywords are supported, and skip such tests if it's not + output = run_feature_check(pyb, args, base_path, 'async_check.py') + if output == b'CRASH': + skip_async = True - # Check if __rOP__ special methods are supported, and skip such tests if it's not - output = run_feature_check(pyb, args, base_path, 'reverse_ops.py') - if output == b'TypeError\n': - skip_revops = True + # Check if const keyword (MicroPython extension) is supported, and skip such tests if it's not + output = run_feature_check(pyb, args, base_path, 'const.py') + if output == b'CRASH': + skip_const = True - # Check if emacs repl is supported, and skip such tests if it's not - t = run_feature_check(pyb, args, base_path, 'repl_emacs_check.py') - if not 'True' in str(t, 'ascii'): - skip_tests.add('cmdline/repl_emacs_keys.py') + # Check if __rOP__ special methods are supported, and skip such tests if it's not + output = run_feature_check(pyb, args, base_path, 'reverse_ops.py') + if output == b'TypeError\n': + skip_revops = True - upy_byteorder = run_feature_check(pyb, args, base_path, 'byteorder.py') - upy_float_precision = int(run_feature_check(pyb, args, base_path, 'float.py')) - has_complex = run_feature_check(pyb, args, base_path, 'complex.py') == b'complex\n' - has_coverage = run_feature_check(pyb, args, base_path, 'coverage.py') == b'coverage\n' - cpy_byteorder = subprocess.check_output([CPYTHON3, base_path + '/feature_check/byteorder.py']) - skip_endian = (upy_byteorder != cpy_byteorder) + # Check if emacs repl is supported, and skip such tests if it's not + t = run_feature_check(pyb, args, base_path, 'repl_emacs_check.py') + if not 'True' in str(t, 'ascii'): + skip_tests.add('cmdline/repl_emacs_keys.py') + + upy_byteorder = run_feature_check(pyb, args, base_path, 'byteorder.py') + upy_float_precision = int(run_feature_check(pyb, args, base_path, 'float.py')) + has_complex = run_feature_check(pyb, args, base_path, 'complex.py') == b'complex\n' + has_coverage = run_feature_check(pyb, args, base_path, 'coverage.py') == b'coverage\n' + cpy_byteorder = subprocess.check_output([CPYTHON3, base_path + '/feature_check/byteorder.py']) + skip_endian = (upy_byteorder != cpy_byteorder) # Some tests shouldn't be run under Travis CI if os.getenv('TRAVIS') == 'true': From 6b19520a743ddafcdd1881582d4c90a9ea068610 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 15 Dec 2017 12:10:39 +0200 Subject: [PATCH 177/828] zephyr: Add support for binary with builtin testsuite. If TEST is defined, file it refers to will be used as the testsuite source (should be generated with tools/tinytest-codegen.py). "make-bin-testsuite" script is introduce to build such a binary. --- ports/zephyr/main.c | 15 +++++++++++ ports/zephyr/make-bin-testsuite | 20 +++++++++++++++ ports/zephyr/mpconfigport_bin_testsuite.h | 31 +++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100755 ports/zephyr/make-bin-testsuite create mode 100644 ports/zephyr/mpconfigport_bin_testsuite.h diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index d6ddc65cc..8c64fdcee 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -41,6 +41,13 @@ #include "lib/utils/pyexec.h" #include "lib/mp-readline/readline.h" +#ifdef TEST +#include "lib/upytesthelper/upytesthelper.h" +#include "lib/tinytest/tinytest.c" +#include "lib/upytesthelper/upytesthelper.c" +#include TEST +#endif + static char *stack_top; static char heap[MICROPY_HEAP_SIZE]; @@ -95,6 +102,14 @@ int real_main(void) { init_zephyr(); + #ifdef TEST + static const char *argv[] = {"test"}; + upytest_set_heap(heap, heap + sizeof(heap)); + int r = tinytest_main(1, argv, groups); + printf("status: %d\n", r); + return 0; + #endif + soft_reset: #if MICROPY_ENABLE_GC gc_init(heap, heap + sizeof(heap)); diff --git a/ports/zephyr/make-bin-testsuite b/ports/zephyr/make-bin-testsuite new file mode 100755 index 000000000..f6aeb83f8 --- /dev/null +++ b/ports/zephyr/make-bin-testsuite @@ -0,0 +1,20 @@ +#!/bin/sh +# +# This is a wrapper for make to build a binary with builtin testsuite. +# It should be run just like make (i.e. extra vars can be passed on the +# command line, etc.), e.g.: +# +# ./make-bin-testsuite BOARD=qemu_cortex_m3 +# ./make-bin-testsuite BOARD=qemu_cortex_m3 run +# + +(cd ../../tests; ./run-tests --write-exp) +(cd ../../tests; ./run-tests --list-tests --target=minimal \ + -e async -e intbig -e int_big -e builtin_help -e memstats -e bytes_compare3 -e class_reverse_op \ + -e /set -e frozenset -e complex -e const -e native -e viper \ + -e 'float_divmod\.' -e float_parse_doubleprec -e float/true_value -e float/types \ + | ../tools/tinytest-codegen.py --stdin) > bin-testsuite.c + +make \ + CFLAGS_EXTRA='-DMP_CONFIGFILE="" -DTEST=\"bin-testsuite.c\" -DNO_FORKING' \ + "$@" diff --git a/ports/zephyr/mpconfigport_bin_testsuite.h b/ports/zephyr/mpconfigport_bin_testsuite.h new file mode 100644 index 000000000..684b4f41c --- /dev/null +++ b/ports/zephyr/mpconfigport_bin_testsuite.h @@ -0,0 +1,31 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Linaro Limited + * + * 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 "mpconfigport.h" + +#ifdef TEST +#include "lib/upytesthelper/upytesthelper.h" +#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len) +#endif From dd35fe7ca0e18af0286f89a20e5f743015d3dac5 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 15 Dec 2017 18:17:00 +0200 Subject: [PATCH 178/828] zephyr/prj_base.conf: Bump MAIN_STACK_SIZE to let builtin testsuite run. --- ports/zephyr/prj_base.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/prj_base.conf b/ports/zephyr/prj_base.conf index 58c65f14d..3858c0a02 100644 --- a/ports/zephyr/prj_base.conf +++ b/ports/zephyr/prj_base.conf @@ -12,7 +12,7 @@ CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128 CONFIG_NEWLIB_LIBC=y CONFIG_FLOAT=y -CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_MAIN_STACK_SIZE=4736 # Networking config CONFIG_NETWORKING=y From f4ed2dfa942339dc1f174e8a83ff0d41073f1972 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 15 Dec 2017 19:41:08 +0200 Subject: [PATCH 179/828] lib/tinytest: Clean up test reporting in the presence of stdout output. tinytest is written with the idea that tests won't write to stdout, so it prints test name witjout newline, then executes test, then writes status. But MicroPython tests write to stdout, so the test output becomes a mess. So, instead print it like: # starting basics/andor.py ... test output ... basics/andor.py: OK --- lib/tinytest/tinytest.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/tinytest/tinytest.c b/lib/tinytest/tinytest.c index 1ef957d31..01772f3f8 100644 --- a/lib/tinytest/tinytest.c +++ b/lib/tinytest/tinytest.c @@ -234,8 +234,9 @@ testcase_run_one(const struct testgroup_t *group, return SKIP; } + printf("# starting %s%s\n", group->prefix, testcase->name); if (opt_verbosity>0 && !opt_forked) { - printf("%s%s: ", group->prefix, testcase->name); + //printf("%s%s: ", group->prefix, testcase->name); } else { if (opt_verbosity==0) printf("."); cur_test_prefix = group->prefix; @@ -252,6 +253,7 @@ testcase_run_one(const struct testgroup_t *group, outcome = testcase_run_bare_(testcase); } + printf("%s%s: ", group->prefix, testcase->name); if (outcome == OK) { ++n_ok; if (opt_verbosity>0 && !opt_forked) @@ -263,7 +265,8 @@ testcase_run_one(const struct testgroup_t *group, } else { ++n_bad; if (!opt_forked) - printf("\n [%s FAILED]\n", testcase->name); + //printf("\n [%s FAILED]\n", testcase->name); + puts("FAILED"); } if (opt_forked) { From 63644016669c173190c71d39fb33ad9502fe2b0f Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 21 Oct 2017 12:13:44 +0300 Subject: [PATCH 180/828] py/objgenerator: Allow to pend an exception for next execution. This implements .pend_throw(exc) method, which sets up an exception to be triggered on the next call to generator's .__next__() or .send() method. This is unlike .throw(), which immediately starts to execute the generator to process the exception. This effectively adds Future-like capabilities to generator protocol (exception will be raised in the future). The need for such a method arised to implement uasyncio wait_for() function efficiently (its behavior is clearly "Future" like, and normally would require to introduce an expensive Future wrapper around all native couroutines, like upstream asyncio does). py/objgenerator: pend_throw: Return previous pended value. This effectively allows to store an additional value (not necessary an exception) in a coroutine while it's not being executed. uasyncio has exactly this usecase: to mark a coro waiting in I/O queue (and thus not executed in the normal scheduling queue), for the purpose of implementing wait_for() function (cancellation of such waiting coro by a timeout). --- py/mpconfig.h | 9 +++++++ py/objgenerator.c | 30 ++++++++++++++++++++++-- tests/basics/generator_pend_throw.py | 26 ++++++++++++++++++++ tests/basics/generator_pend_throw.py.exp | 4 ++++ tests/run-tests | 2 +- 5 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 tests/basics/generator_pend_throw.py create mode 100644 tests/basics/generator_pend_throw.py.exp diff --git a/py/mpconfig.h b/py/mpconfig.h index 19bae4f88..763bb378e 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -706,6 +706,15 @@ typedef double mp_float_t; #define MICROPY_PY_ASYNC_AWAIT (1) #endif +// Non-standard .pend_throw() method for generators, allowing for +// Future-like behavior with respect to exception handling: an +// exception set with .pend_throw() will activate on the next call +// to generator's .send() or .__next__(). (This is useful to implement +// async schedulers.) +#ifndef MICROPY_PY_GENERATOR_PEND_THROW +#define MICROPY_PY_GENERATOR_PEND_THROW (1) +#endif + // Issue a warning when comparing str and bytes objects #ifndef MICROPY_PY_STR_BYTES_CMP_WARN #define MICROPY_PY_STR_BYTES_CMP_WARN (0) diff --git a/py/objgenerator.c b/py/objgenerator.c index 9a294debb..8c1260b60 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -4,7 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George - * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2014-2017 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -104,7 +104,16 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ mp_raise_TypeError("can't send non-None value to a just-started generator"); } } else { - *self->code_state.sp = send_value; + #if MICROPY_PY_GENERATOR_PEND_THROW + // If exception is pending (set using .pend_throw()), process it now. + if (*self->code_state.sp != mp_const_none) { + throw_value = *self->code_state.sp; + *self->code_state.sp = MP_OBJ_NULL; + } else + #endif + { + *self->code_state.sp = send_value; + } } mp_obj_dict_t *old_globals = mp_globals_get(); mp_globals_set(self->globals); @@ -125,6 +134,9 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_ case MP_VM_RETURN_YIELD: *ret_val = *self->code_state.sp; + #if MICROPY_PY_GENERATOR_PEND_THROW + *self->code_state.sp = mp_const_none; + #endif break; case MP_VM_RETURN_EXCEPTION: { @@ -219,10 +231,24 @@ STATIC mp_obj_t gen_instance_close(mp_obj_t self_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_close_obj, gen_instance_close); +STATIC mp_obj_t gen_instance_pend_throw(mp_obj_t self_in, mp_obj_t exc_in) { + mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); + if (self->code_state.sp == self->code_state.state - 1) { + mp_raise_TypeError("can't pend throw to just-started generator"); + } + mp_obj_t prev = *self->code_state.sp; + *self->code_state.sp = exc_in; + return prev; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_pend_throw_obj, gen_instance_pend_throw); + STATIC const mp_rom_map_elem_t gen_instance_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&gen_instance_close_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&gen_instance_send_obj) }, { MP_ROM_QSTR(MP_QSTR_throw), MP_ROM_PTR(&gen_instance_throw_obj) }, + #if MICROPY_PY_GENERATOR_PEND_THROW + { MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(gen_instance_locals_dict, gen_instance_locals_dict_table); diff --git a/tests/basics/generator_pend_throw.py b/tests/basics/generator_pend_throw.py new file mode 100644 index 000000000..949655612 --- /dev/null +++ b/tests/basics/generator_pend_throw.py @@ -0,0 +1,26 @@ +def gen(): + i = 0 + while 1: + yield i + i += 1 + +g = gen() + +try: + g.pend_throw +except AttributeError: + print("SKIP") + raise SystemExit + + +print(next(g)) +print(next(g)) +g.pend_throw(ValueError()) + +v = None +try: + v = next(g) +except Exception as e: + print("raised", repr(e)) + +print("ret was:", v) diff --git a/tests/basics/generator_pend_throw.py.exp b/tests/basics/generator_pend_throw.py.exp new file mode 100644 index 000000000..f9894a089 --- /dev/null +++ b/tests/basics/generator_pend_throw.py.exp @@ -0,0 +1,4 @@ +0 +1 +raised ValueError() +ret was: None diff --git a/tests/run-tests b/tests/run-tests index 45cb1a746..8719befbd 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -337,7 +337,7 @@ def run_tests(pyb, tests, args, base_path="."): # Some tests are known to fail with native emitter # Remove them from the below when they work if args.emit == 'native': - skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_stopped gen_yield_from_throw gen_yield_from_throw2 gen_yield_from_throw3 generator1 generator2 generator_args generator_close generator_closure generator_exc generator_return generator_send'.split()}) # require yield + skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_stopped gen_yield_from_throw gen_yield_from_throw2 gen_yield_from_throw3 generator1 generator2 generator_args generator_close generator_closure generator_exc generator_pend_throw generator_return generator_send'.split()}) # require yield skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class globals_del string_join'.split()}) # require yield skip_tests.update({'basics/async_%s.py' % t for t in 'def await await2 for for2 with with2'.split()}) # require yield skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs From 02d2a0fb3a004238b5c15e02b0699824b385ee96 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 7 Nov 2017 20:04:18 +0200 Subject: [PATCH 181/828] docs/conf: Reference CPython 3.5 docs. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPython 3.6 contains some backward incompatible changes, and further version(s) are expected to have more. As we anyway implemente 3.4 with some features of 3.5, refer to 3.5 docs to avoid confusion. Examples of 3.6 backward incompatibilities: https://docs.python.org/3.6/library/json.html#json.dump https://docs.python.org/3.6/library/json.html#json.load > Changed in version 3.6: All optional parameters are now keyword-only. https://docs.python.org/3.6/library/functions.html#type > Changed in version 3.6: Subclasses of type which don’t override > type.__new__ may no longer use the one-argument form to get the > type of an object. https://docs.python.org/3.6/library/collections.html#collections.namedtuple > Changed in version 3.6: The verbose and rename parameters became > keyword-only arguments. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 5352d16e4..f9c3ecdb7 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -322,7 +322,7 @@ texinfo_documents = [ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'python': ('http://docs.python.org/3', None)} +intersphinx_mapping = {'python': ('http://docs.python.org/3.5', None)} # Append the other ports' specific folders/files to the exclude pattern exclude_patterns.extend([port + '*' for port in ports if port != micropy_port]) From 9251f1395efff46bb63f5010534452f4c437206f Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 16 Dec 2017 10:37:36 +0200 Subject: [PATCH 182/828] docs/packages: Use "install_dir/" in examples. --- docs/reference/packages.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index 3d05f0b27..efe119f71 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -130,12 +130,12 @@ transferring this image to a device by suitable means. Installing to a directory image involves using ``-p`` switch to `upip`:: - micropython -m upip install -p install_image micropython-pystone_lowmem + micropython -m upip install -p install_dir micropython-pystone_lowmem After this command, the package content (and contents of every depenency -packages) will be available in the ``install_image/`` subdirectory. You +packages) will be available in the ``install_dir/`` subdirectory. You would need to transfer contents of this directory (without the -``install_image/`` prefix) to the device, at the suitable location, where +``install_dir/`` prefix) to the device, at the suitable location, where it can be found by the Python ``import`` statement (see discussion of the `upip` installation path above). From e37ccfe59b88cbc0c1617ee1d0ec418d52ba6f76 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 16 Dec 2017 10:41:45 +0200 Subject: [PATCH 183/828] docs/packages: Explicitly recommend usage of setuptools instead of distutils. --- docs/reference/packages.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index efe119f71..e1609985a 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -204,12 +204,13 @@ Creating distribution packages Distribution packages for MicroPython are created in the same manner as for CPython or any other Python implementation, see references at -the end of chapter. "Source distribution" (sdist) format is used for -packaging. The post-processing discussed above, (and pre-processing -discussed in the following section) is achieved by using custom -"sdist" command for distutils/setuptools. Thus, packaging steps -remain the same as for standard distutils/setuptools, the user just -need to override "sdist" command implementation by passing the +the end of chapter. Setuptools (instead of distutils) should be used, +because distutils do not support dependencies and other features. "Source +distribution" (``sdist``) format is used for packaging. The post-processing +discussed above, (and pre-processing discussed in the following section) +is achieved by using custom ``sdist`` command for setuptools. Thus, packaging +steps remain the same as for the standard setuptools, the user just +needs to override ``sdist`` command implementation by passing the appropriate argument to ``setup()`` call:: from setuptools import setup From 7f9a62408de96f8d78926690968ac1f9ead19f3c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 16 Dec 2017 20:21:09 +0200 Subject: [PATCH 184/828] unix/Makefile: coverage: Allow user to pass CFLAGS_EXTRA. This build sets CFLAGS_EXTRA itself, but preserve user's value as passed on make command line/etc. --- ports/unix/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index b96391f69..b5f9f8e7d 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -243,7 +243,7 @@ freedos: # build an interpreter for coverage testing and do the testing coverage: $(MAKE) \ - COPT="-O0" CFLAGS_EXTRA='-DMP_CONFIGFILE="" \ + COPT="-O0" CFLAGS_EXTRA='$(CFLAGS_EXTRA) -DMP_CONFIGFILE="" \ -fprofile-arcs -ftest-coverage \ -Wdouble-promotion -Wformat -Wmissing-declarations -Wmissing-prototypes -Wsign-compare \ -Wold-style-definition -Wpointer-arith -Wshadow -Wuninitialized -Wunused-parameter \ From ea742085ed0ca3ce3b8b14b69442fd13ccc2a754 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 16 Dec 2017 20:43:04 +0200 Subject: [PATCH 185/828] unix/mpconfigport.h: Allow to override stackless options from commandline. --- ports/unix/mpconfigport.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 06ae1e234..353bfa3e4 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -106,8 +106,10 @@ #define MICROPY_PY_GC_COLLECT_RETVAL (1) #define MICROPY_MODULE_FROZEN_STR (1) +#ifndef MICROPY_STACKLESS #define MICROPY_STACKLESS (0) #define MICROPY_STACKLESS_STRICT (0) +#endif #define MICROPY_PY_OS_STATVFS (1) #define MICROPY_PY_UTIME (1) From 5455bf79c568fa56b2ad05d28d10575d987030b4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 16 Dec 2017 20:43:30 +0200 Subject: [PATCH 186/828] .travis.yml: Build and test strict stackless build. Previously, testing of stackless build happened (manually) in travis-stackless branch. However, stackless offers important featureset, so it's worth to test it as a part of the main CI. Strict stackless is used because it's the "real" stackless build, which avoids using C stack as much as possible (non-strict just prefers heap over C stack, but may end up using the latter). --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4a9d238ab..b98ee9971 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,6 +61,11 @@ script: # run coveralls coverage analysis (try to, even if some builds/tests failed) - (cd ports/unix && coveralls --root ../.. --build-root . --gcov $(which gcov) --gcov-options '\-o build-coverage/' --include py --include extmod) + # run tests on stackless build + - rm -rf ports/unix/build-coverage + - make -C ports/unix coverage CFLAGS_EXTRA="-DMICROPY_STACKLESS=1 -DMICROPY_STACKLESS_STRICT=1" + - (cd tests && MICROPY_CPYTHON3=python3.4 MICROPY_MICROPYTHON=../ports/unix/micropython_coverage ./run-tests) + after_failure: - (cd tests && for exp in *.exp; do testbase=$(basename $exp .exp); echo -e "\nFAILURE $testbase"; diff -u $testbase.exp $testbase.out; done) - (grep "FAIL" ports/qemu-arm/build/console.out) From 9123c8d9460017b8c8dc4a38d9a26968eacaa4cb Mon Sep 17 00:00:00 2001 From: Eric Poulsen Date: Tue, 28 Nov 2017 18:48:30 -0800 Subject: [PATCH 187/828] esp32/machine_hw_spi: Fix large HW SPI transfers by splitting them up. Breaks up HW SPI transfers into maximum chunks of 32736 bits (4092 bytes), because this is the maximum that the underlying ESP IDF will accept. --- ports/esp32/machine_hw_spi.c | 54 ++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 437b620f5..54790bf8d 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -36,6 +36,9 @@ #include "driver/spi_master.h" +#define MP_HW_SPI_MAX_XFER_BYTES (4092) +#define MP_HW_SPI_MAX_XFER_BITS (MP_HW_SPI_MAX_XFER_BYTES * 8) // Has to be an even multiple of 8 + typedef struct _machine_hw_spi_obj_t { mp_obj_base_t base; spi_host_device_t host; @@ -227,35 +230,52 @@ STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) { STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); - int bits_to_send = len * self->bits; - bool shortMsg = len <= 4; - if (self->state == MACHINE_HW_SPI_STATE_DEINIT) { mp_raise_msg(&mp_type_OSError, "transfer on deinitialized SPI"); return; } - struct spi_transaction_t transaction = { - .flags = 0, - .length = bits_to_send, - .tx_buffer = NULL, - .rx_buffer = NULL, - }; + struct spi_transaction_t transaction = { 0 }; - if (shortMsg) { + // Round to nearest whole set of bits + int bits_to_send = len * 8 / self->bits * self->bits; + + + if (len <= 4) { if (src != NULL) { memcpy(&transaction.tx_data, src, len); } - transaction.flags |= (SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA); + + transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA; + transaction.length = bits_to_send; + spi_device_transmit(self->spi, &transaction); + + if (dest != NULL) { + memcpy(dest, &transaction.rx_data, len); + } } else { - transaction.tx_buffer = src; - transaction.rx_buffer = dest; - } + int offset = 0; + int bits_remaining = bits_to_send; - spi_device_transmit(self->spi, &transaction); + while (bits_remaining) { + memset(&transaction, 0, sizeof(transaction)); - if (shortMsg && dest != NULL) { - memcpy(dest, &transaction.rx_data, len); + transaction.length = + bits_remaining > MP_HW_SPI_MAX_XFER_BITS ? MP_HW_SPI_MAX_XFER_BITS : bits_remaining; + + if (src != NULL) { + transaction.tx_buffer = src + offset; + } + if (dest != NULL) { + transaction.rx_buffer = dest + offset; + } + + spi_device_transmit(self->spi, &transaction); + bits_remaining -= transaction.length; + + // doesn't need ceil(); loop ends when bits_remaining is 0 + offset += transaction.length / 8; + } } } From d1fd889ad072afb1e7ffe4c0e5f4ef258226d70f Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 18 Dec 2017 15:46:08 +1100 Subject: [PATCH 188/828] esp32/machine_hw_spi: Remove unnecessary white space for consistency. --- ports/esp32/machine_hw_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 54790bf8d..d011ce53e 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -295,7 +295,7 @@ STATIC void machine_hw_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_ enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_INT , {.u_int = -1} }, + { MP_QSTR_id, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, @@ -343,7 +343,7 @@ STATIC void machine_hw_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_ mp_obj_t machine_hw_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT , {.u_int = -1} }, + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, From f5fb68e94fe72a7d8c158a47c2374ee0cea0227c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 13:13:21 +1100 Subject: [PATCH 189/828] py/runtime: Remove unnecessary break statements from switch. --- py/runtime.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/py/runtime.c b/py/runtime.c index 41f2f6976..9dff9847a 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -413,7 +413,6 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { // use standard precision return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val); } - break; } case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: @@ -488,10 +487,10 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { return MP_OBJ_FROM_PTR(tuple); } - case MP_BINARY_OP_LESS: return mp_obj_new_bool(lhs_val < rhs_val); break; - case MP_BINARY_OP_MORE: return mp_obj_new_bool(lhs_val > rhs_val); break; - case MP_BINARY_OP_LESS_EQUAL: return mp_obj_new_bool(lhs_val <= rhs_val); break; - case MP_BINARY_OP_MORE_EQUAL: return mp_obj_new_bool(lhs_val >= rhs_val); break; + case MP_BINARY_OP_LESS: return mp_obj_new_bool(lhs_val < rhs_val); + case MP_BINARY_OP_MORE: return mp_obj_new_bool(lhs_val > rhs_val); + case MP_BINARY_OP_LESS_EQUAL: return mp_obj_new_bool(lhs_val <= rhs_val); + case MP_BINARY_OP_MORE_EQUAL: return mp_obj_new_bool(lhs_val >= rhs_val); default: goto unsupported_op; From 136cb7f27c27ee128d3adab9850b74f6129c8732 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 13:37:15 +1100 Subject: [PATCH 190/828] py/map: Don't include ordered-dict mutating code when not needed. --- py/map.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/py/map.c b/py/map.c index 696c7a2ea..6abf4853f 100644 --- a/py/map.c +++ b/py/map.c @@ -170,6 +170,7 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t if (map->is_ordered) { for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) { if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) { + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) { // remove the found element by moving the rest of the array down mp_obj_t value = elem->value; @@ -180,9 +181,11 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t elem->key = MP_OBJ_NULL; elem->value = value; } + #endif return elem; } } + #if MICROPY_PY_COLLECTIONS_ORDEREDDICT if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) { return NULL; } @@ -198,6 +201,9 @@ mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t map->all_keys_are_qstrs = 0; } return elem; + #else + return NULL; + #endif } // map is a hash table (not an ordered array), so do a hash lookup From 7208cad97afb4f65902f7943ace425f652eeb669 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 13:59:54 +1100 Subject: [PATCH 191/828] tests/basics: Add more set tests to improve coverage of py/objset.c. --- tests/basics/set_binop.py | 6 ++++++ tests/basics/set_isfooset.py | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/basics/set_binop.py b/tests/basics/set_binop.py index bc76533b1..bf55f87db 100644 --- a/tests/basics/set_binop.py +++ b/tests/basics/set_binop.py @@ -60,6 +60,12 @@ except TypeError: print('TypeError') # unsupported operator +try: + set('abc') * set('abc') +except TypeError: + print('TypeError') + +# unsupported operator with RHS not a set try: set('abc') * 2 except TypeError: diff --git a/tests/basics/set_isfooset.py b/tests/basics/set_isfooset.py index ce7952cd2..27dedea00 100644 --- a/tests/basics/set_isfooset.py +++ b/tests/basics/set_isfooset.py @@ -1,5 +1,6 @@ sets = [set(), {1}, {1, 2, 3}, {3, 4, 5}, {5, 6, 7}] +args = sets + [[1], [1, 2], [1, 2 ,3]] for i in sets: - for j in sets: + for j in args: print(i.issubset(j)) print(i.issuperset(j)) From 7db79d8b031e69392432ace687c9fcfdd2c56d4c Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 14:01:19 +1100 Subject: [PATCH 192/828] py/objset: Remove unneeded check from set_equal. set_equal is called only from set_binary_op, and this guarantees that the second arg to set_equal is always a set or frozenset. So there is no need to do a further check. --- py/objset.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/py/objset.c b/py/objset.c index 3e98c30e8..799ba9df0 100644 --- a/py/objset.c +++ b/py/objset.c @@ -351,11 +351,9 @@ STATIC mp_obj_t set_issuperset_proper(mp_obj_t self_in, mp_obj_t other_in) { } STATIC mp_obj_t set_equal(mp_obj_t self_in, mp_obj_t other_in) { + assert(is_set_or_frozenset(other_in)); check_set_or_frozenset(self_in); mp_obj_set_t *self = MP_OBJ_TO_PTR(self_in); - if (!is_set_or_frozenset(other_in)) { - return mp_const_false; - } mp_obj_set_t *other = MP_OBJ_TO_PTR(other_in); if (self->set.used != other->set.used) { return mp_const_false; From 251b00457c02718373a03cd5c1aa04a67ba30711 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 14:45:53 +1100 Subject: [PATCH 193/828] tests/extmod/uhashlib_sha256: Add test for hashing 56 bytes of data. --- tests/extmod/uhashlib_sha256.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/extmod/uhashlib_sha256.py b/tests/extmod/uhashlib_sha256.py index 3200e8f5c..676d47979 100644 --- a/tests/extmod/uhashlib_sha256.py +++ b/tests/extmod/uhashlib_sha256.py @@ -23,6 +23,9 @@ print(h.digest()) print(hashlib.sha256(b"\xff" * 64).digest()) +# 56 bytes is a boundary case in the algorithm +print(hashlib.sha256(b"\xff" * 56).digest()) + # TODO: running .digest() several times in row is not supported() #h = hashlib.sha256(b'123') #print(h.digest()) From 7cae17fac774bec7529581f074cfa95716b984b1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 14:50:33 +1100 Subject: [PATCH 194/828] tests/float/builtin_float_hash: Add test to improve objfloat.c coverage. --- tests/float/builtin_float_hash.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/float/builtin_float_hash.py b/tests/float/builtin_float_hash.py index ba6b63907..dd184595f 100644 --- a/tests/float/builtin_float_hash.py +++ b/tests/float/builtin_float_hash.py @@ -15,6 +15,7 @@ for val in ( '0.1', '-0.1', '10.3', + '1e16', 'inf', '-inf', 'nan', From e800e4463db90b3fd971a051c12153f4d61dd10e Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 15:01:17 +1100 Subject: [PATCH 195/828] tests/unix: Add test for printf with %lx format. --- ports/unix/coverage.c | 1 + tests/unix/extra_coverage.py.exp | 1 + 2 files changed, 2 insertions(+) diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index e2896c2dd..5118f9052 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -145,6 +145,7 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%d %+d % d\n", -123, 123, 123); // sign mp_printf(&mp_plat_print, "%05d\n", -123); // negative number with zero padding mp_printf(&mp_plat_print, "%ld\n", 123); // long + mp_printf(&mp_plat_print, "%lx\n", 0x123); // long hex mp_printf(&mp_plat_print, "%X\n", 0x1abcdef); // capital hex mp_printf(&mp_plat_print, "%.2s %.3s\n", "abc", "abc"); // fixed string precision mp_printf(&mp_plat_print, "%.*s\n", -1, "abc"); // negative string precision diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 1db46ab8f..bbac5f3d7 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -2,6 +2,7 @@ -123 +123 123 -0123 123 +123 1ABCDEF ab abc From 8e6113a18808792c5d1eb30e54bb0a1694bb4042 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 15:01:49 +1100 Subject: [PATCH 196/828] tests/basics/generator_pend_throw: Add test for just-started generator. --- tests/basics/generator_pend_throw.py | 5 +++++ tests/basics/generator_pend_throw.py.exp | 1 + 2 files changed, 6 insertions(+) diff --git a/tests/basics/generator_pend_throw.py b/tests/basics/generator_pend_throw.py index 949655612..f00ff793b 100644 --- a/tests/basics/generator_pend_throw.py +++ b/tests/basics/generator_pend_throw.py @@ -24,3 +24,8 @@ except Exception as e: print("raised", repr(e)) print("ret was:", v) + +try: + gen().pend_throw(ValueError()) +except TypeError: + print("TypeError") diff --git a/tests/basics/generator_pend_throw.py.exp b/tests/basics/generator_pend_throw.py.exp index f9894a089..ed4d88295 100644 --- a/tests/basics/generator_pend_throw.py.exp +++ b/tests/basics/generator_pend_throw.py.exp @@ -2,3 +2,4 @@ 1 raised ValueError() ret was: None +TypeError From 374eaf5271c9bfc63b5aa1139de55753ad67cc9a Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 15:42:58 +1100 Subject: [PATCH 197/828] py/mpz: Fix pow3 function so it handles the case when 3rd arg is 1. In this case the result should always be 0, even if 2nd arg is 0. --- py/mpz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/mpz.c b/py/mpz.c index d300a8e5d..16112c201 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1390,7 +1390,7 @@ void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) { can have dest, lhs, rhs the same; mod can't be the same as dest */ void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t *mod) { - if (lhs->len == 0 || rhs->neg != 0) { + if (lhs->len == 0 || rhs->neg != 0 || (mod->len == 1 && mod->dig[0] == 1)) { mpz_set_from_int(dest, 0); return; } From 2bfa531798de1061e1417785acc42b892f96f532 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 15:44:10 +1100 Subject: [PATCH 198/828] tests/basics/builtin_pow3: Add tests for edge cases of pow3. --- tests/basics/builtin_pow3.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/basics/builtin_pow3.py b/tests/basics/builtin_pow3.py index 69b57e548..94e657bc4 100644 --- a/tests/basics/builtin_pow3.py +++ b/tests/basics/builtin_pow3.py @@ -7,6 +7,12 @@ except NotImplementedError: print("SKIP") raise SystemExit +# test some edge cases +print(pow(1, 1, 1)) +print(pow(0, 1, 1)) +print(pow(1, 0, 1)) +print(pow(1, 0, 2)) + # 3 arg pow is defined to only work on integers try: print(pow("x", 5, 6)) From ae1be76d4063997b2703ba1ac9d009b31bc06eca Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 15:45:56 +1100 Subject: [PATCH 199/828] py/mpz: Apply a small code-size optimisation. --- py/mpz.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/py/mpz.c b/py/mpz.c index 16112c201..018a5454f 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1395,8 +1395,9 @@ void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t return; } + mpz_set_from_int(dest, 1); + if (rhs->len == 0) { - mpz_set_from_int(dest, 1); return; } @@ -1404,8 +1405,6 @@ void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t mpz_t *n = mpz_clone(rhs); mpz_t quo; mpz_init_zero(&quo); - mpz_set_from_int(dest, 1); - while (n->len > 0) { if ((n->dig[0] & 1) != 0) { mpz_mul_inpl(dest, dest, x); From 35a759dc1dae33d62d005fe44f3cda4599a3c297 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 16:13:00 +1100 Subject: [PATCH 200/828] tests: Add some more tests to improve coverage of py/parse.c. --- tests/basics/errno1.py | 4 ++++ tests/basics/errno1.py.exp | 1 + tests/basics/int_constfolding.py | 6 ++++++ tests/basics/syntaxerror.py | 4 ++++ tests/cmdline/cmd_parsetree.py | 1 + tests/cmdline/cmd_parsetree.py.exp | 12 +++++++++--- 6 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/basics/errno1.py b/tests/basics/errno1.py index 63930b767..d7a5ccd54 100644 --- a/tests/basics/errno1.py +++ b/tests/basics/errno1.py @@ -15,3 +15,7 @@ print(msg[:7], msg[-5:]) # check that unknown errno is still rendered print(str(OSError(9999))) + +# this tests a failed constant lookup in errno +errno = uerrno +print(errno.__name__) diff --git a/tests/basics/errno1.py.exp b/tests/basics/errno1.py.exp index c3703df4a..7dd22757d 100644 --- a/tests/basics/errno1.py.exp +++ b/tests/basics/errno1.py.exp @@ -1,3 +1,4 @@ [Errno ] EIO 9999 +uerrno diff --git a/tests/basics/int_constfolding.py b/tests/basics/int_constfolding.py index 7bb538378..158897f55 100644 --- a/tests/basics/int_constfolding.py +++ b/tests/basics/int_constfolding.py @@ -29,3 +29,9 @@ print(123 // 7, 123 % 7) print(-123 // 7, -123 % 7) print(123 // -7, 123 % -7) print(-123 // -7, -123 % -7) + +# won't fold so an exception can be raised at runtime +try: + 1 << -1 +except ValueError: + print('ValueError') diff --git a/tests/basics/syntaxerror.py b/tests/basics/syntaxerror.py index 4161de017..843459f0b 100644 --- a/tests/basics/syntaxerror.py +++ b/tests/basics/syntaxerror.py @@ -29,6 +29,10 @@ test_syntax(" a\n") # malformed integer literal (parser error) test_syntax("123z") +# input doesn't match the grammar (parser error) +test_syntax('1 or 2 or') +test_syntax('{1:') + # can't assign to literals test_syntax("1 = 2") test_syntax("'' = 1") diff --git a/tests/cmdline/cmd_parsetree.py b/tests/cmdline/cmd_parsetree.py index 5f698eeae..da36c8070 100644 --- a/tests/cmdline/cmd_parsetree.py +++ b/tests/cmdline/cmd_parsetree.py @@ -9,3 +9,4 @@ c = 'a very long str that will not be interned' d = b'bytes' e = b'a very long bytes that will not be interned' f = 123456789012345678901234567890 +g = 123 diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index d9f81d8d4..12a1bfbe9 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -1,5 +1,5 @@ ---------------- -[ 4] rule(1) (n=8) +[ 4] rule(1) (n=9) tok(4) [ 4] rule(22) (n=4) id(i) @@ -25,6 +25,9 @@ [ 11] rule(5) (n=2) id(f) [ 11] literal \.\+ +[ 12] rule(5) (n=2) + id(g) + int(123) ---------------- File cmdline/cmd_parsetree.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): @@ -42,6 +45,7 @@ arg names: bc=27 line=9 bc=32 line=10 bc=37 line=11 + bc=42 line=12 00 BUILD_TUPLE 0 02 GET_ITER_STACK 03 FOR_ITER 12 @@ -59,8 +63,10 @@ arg names: 34 STORE_NAME e 37 LOAD_CONST_OBJ \.\+ 39 STORE_NAME f -42 LOAD_CONST_NONE -43 RETURN_VALUE +42 LOAD_CONST_SMALL_INT 123 +45 STORE_NAME g +48 LOAD_CONST_NONE +49 RETURN_VALUE mem: total=\\d\+, current=\\d\+, peak=\\d\+ stack: \\d\+ out of \\d\+ GC: total: \\d\+, used: \\d\+, free: \\d\+ From d35c6ffc84e94dca370479a057eed60b9b347c1b Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 16:48:41 +1100 Subject: [PATCH 201/828] tests/extmod: Add some uctypes tests to improve coverage of that module. --- tests/extmod/uctypes_bytearray.py | 3 ++ tests/extmod/uctypes_bytearray.py.exp | 1 + tests/extmod/uctypes_byteat.py | 10 +++++++ tests/extmod/uctypes_byteat.py.exp | 2 ++ tests/extmod/uctypes_error.py | 37 ++++++++++++++++++++++++ tests/extmod/uctypes_error.py.exp | 4 +++ tests/extmod/uctypes_sizeof.py | 5 ++++ tests/extmod/uctypes_sizeof.py.exp | 1 + tests/extmod/uctypes_sizeof_float.py | 8 +++++ tests/extmod/uctypes_sizeof_float.py.exp | 2 ++ 10 files changed, 73 insertions(+) create mode 100644 tests/extmod/uctypes_byteat.py create mode 100644 tests/extmod/uctypes_byteat.py.exp create mode 100644 tests/extmod/uctypes_error.py create mode 100644 tests/extmod/uctypes_error.py.exp create mode 100644 tests/extmod/uctypes_sizeof_float.py create mode 100644 tests/extmod/uctypes_sizeof_float.py.exp diff --git a/tests/extmod/uctypes_bytearray.py b/tests/extmod/uctypes_bytearray.py index 61c7da271..77c93c376 100644 --- a/tests/extmod/uctypes_bytearray.py +++ b/tests/extmod/uctypes_bytearray.py @@ -17,3 +17,6 @@ S = uctypes.struct(uctypes.addressof(data), desc, uctypes.LITTLE_ENDIAN) print(S.arr) # But not INT8, because value range is different print(type(S.arr2)) + +# convert to buffer +print(bytearray(S)) diff --git a/tests/extmod/uctypes_bytearray.py.exp b/tests/extmod/uctypes_bytearray.py.exp index 294f8a5fa..7c84edbb6 100644 --- a/tests/extmod/uctypes_bytearray.py.exp +++ b/tests/extmod/uctypes_bytearray.py.exp @@ -1,2 +1,3 @@ bytearray(b'01') +bytearray(b'0123') diff --git a/tests/extmod/uctypes_byteat.py b/tests/extmod/uctypes_byteat.py new file mode 100644 index 000000000..ab2535db8 --- /dev/null +++ b/tests/extmod/uctypes_byteat.py @@ -0,0 +1,10 @@ +try: + import uctypes +except ImportError: + print("SKIP") + raise SystemExit + +data = bytearray(b'01234567') + +print(uctypes.bytes_at(uctypes.addressof(data), 4)) +print(uctypes.bytearray_at(uctypes.addressof(data), 4)) diff --git a/tests/extmod/uctypes_byteat.py.exp b/tests/extmod/uctypes_byteat.py.exp new file mode 100644 index 000000000..e1ae4d053 --- /dev/null +++ b/tests/extmod/uctypes_byteat.py.exp @@ -0,0 +1,2 @@ +b'0123' +bytearray(b'0123') diff --git a/tests/extmod/uctypes_error.py b/tests/extmod/uctypes_error.py new file mode 100644 index 000000000..95ba0fad4 --- /dev/null +++ b/tests/extmod/uctypes_error.py @@ -0,0 +1,37 @@ +# test general errors with uctypes + +try: + import uctypes +except ImportError: + print("SKIP") + raise SystemExit + +data = bytearray(b"01234567") + +# del subscr not supported +S = uctypes.struct(uctypes.addressof(data), {}) +try: + del S[0] +except TypeError: + print('TypeError') + +# list is an invalid descriptor +S = uctypes.struct(uctypes.addressof(data), []) +try: + S.x +except TypeError: + print('TypeError') + +# can't access attribute with invalid descriptor +S = uctypes.struct(uctypes.addressof(data), {'x':[]}) +try: + S.x +except TypeError: + print('TypeError') + +# can't assign to aggregate +S = uctypes.struct(uctypes.addressof(data), {'x':(uctypes.ARRAY | 0, uctypes.INT8 | 2)}) +try: + S.x = 1 +except TypeError: + print('TypeError') diff --git a/tests/extmod/uctypes_error.py.exp b/tests/extmod/uctypes_error.py.exp new file mode 100644 index 000000000..802c260d2 --- /dev/null +++ b/tests/extmod/uctypes_error.py.exp @@ -0,0 +1,4 @@ +TypeError +TypeError +TypeError +TypeError diff --git a/tests/extmod/uctypes_sizeof.py b/tests/extmod/uctypes_sizeof.py index 5a6adb437..e42e06c92 100644 --- a/tests/extmod/uctypes_sizeof.py +++ b/tests/extmod/uctypes_sizeof.py @@ -40,3 +40,8 @@ assert uctypes.sizeof(S.arr4) == 6 print(uctypes.sizeof(S.sub)) assert uctypes.sizeof(S.sub) == 1 +# invalid descriptor +try: + print(uctypes.sizeof([])) +except TypeError: + print("TypeError") diff --git a/tests/extmod/uctypes_sizeof.py.exp b/tests/extmod/uctypes_sizeof.py.exp index fb74def60..b35b11aa0 100644 --- a/tests/extmod/uctypes_sizeof.py.exp +++ b/tests/extmod/uctypes_sizeof.py.exp @@ -4,3 +4,4 @@ TypeError 6 1 +TypeError diff --git a/tests/extmod/uctypes_sizeof_float.py b/tests/extmod/uctypes_sizeof_float.py new file mode 100644 index 000000000..1ba8871bd --- /dev/null +++ b/tests/extmod/uctypes_sizeof_float.py @@ -0,0 +1,8 @@ +try: + import uctypes +except ImportError: + print("SKIP") + raise SystemExit + +print(uctypes.sizeof({'f':uctypes.FLOAT32})) +print(uctypes.sizeof({'f':uctypes.FLOAT64})) diff --git a/tests/extmod/uctypes_sizeof_float.py.exp b/tests/extmod/uctypes_sizeof_float.py.exp new file mode 100644 index 000000000..de7818072 --- /dev/null +++ b/tests/extmod/uctypes_sizeof_float.py.exp @@ -0,0 +1,2 @@ +4 +8 From 304a3bcc1cf4f272b62a6cd8f14583db84d84189 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 16:59:08 +1100 Subject: [PATCH 202/828] py/modio: Use correct config macro to enable resource_stream function. --- py/modio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/modio.c b/py/modio.c index 828bcec46..3a5c69c4c 100644 --- a/py/modio.c +++ b/py/modio.c @@ -131,7 +131,7 @@ STATIC const mp_obj_type_t bufwriter_type = { }; #endif // MICROPY_PY_IO_BUFFEREDWRITER -#if MICROPY_MODULE_FROZEN_STR +#if MICROPY_PY_IO_RESOURCE_STREAM STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) { VSTR_FIXED(path_buf, MICROPY_ALLOC_PATH_MAX); size_t len; @@ -179,7 +179,7 @@ STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) { mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len); return mp_builtin_open(1, &path_out, (mp_map_t*)&mp_const_empty_map); } -MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream); #endif STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = { From d8d633f15658911369d7b1b912777fa74efd3ea6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 17:04:55 +1100 Subject: [PATCH 203/828] unix/mpconfigport_coverage.h: Enable MICROPY_PY_IO_RESOURCE_STREAM. Where possible it's important to test all code in the code base. --- ports/unix/mpconfigport_coverage.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/unix/mpconfigport_coverage.h b/ports/unix/mpconfigport_coverage.h index 0dcfdd5fd..cf976bf7a 100644 --- a/ports/unix/mpconfigport_coverage.h +++ b/ports/unix/mpconfigport_coverage.h @@ -41,6 +41,7 @@ #define MICROPY_PY_SYS_GETSIZEOF (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #define MICROPY_PY_IO_BUFFEREDWRITER (1) +#define MICROPY_PY_IO_RESOURCE_STREAM (1) #undef MICROPY_VFS_FAT #define MICROPY_VFS_FAT (1) #define MICROPY_PY_FRAMEBUF (1) From 6a3a742a6c9caaa2be0fd0aac7a5df4ac816081c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 18 Dec 2017 18:57:15 +1100 Subject: [PATCH 204/828] py/nlr: Factor out common NLR code to generic functions. Each NLR implementation (Thumb, x86, x64, xtensa, setjmp) duplicates a lot of the NLR code, specifically that dealing with pushing and popping the NLR pointer to maintain the linked-list of NLR buffers. This patch factors all of that code out of the specific implementations into generic functions in nlr.c. This eliminates duplicated code. The factoring also allows to make the machine-specific NLR code pure assembler code, thus allowing nlrthumb.c to use naked function attributes in the correct way (naked functions can only have basic inline assembler code in them). There is a small overhead introduced (typically 1 machine instruction) because now the generic nlr_jump() must call nlr_jump_tail() rather than them being one combined function. --- py/nlr.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ py/nlr.h | 71 +++++++++++++++++++++++------------------------- py/nlrsetjmp.c | 10 +------ py/nlrthumb.c | 38 ++------------------------ py/nlrx64.c | 70 ++++++++++++++++++----------------------------- py/nlrx86.c | 63 +++++++++--------------------------------- py/nlrxtensa.c | 34 ++++------------------- py/py.mk | 1 + 8 files changed, 157 insertions(+), 204 deletions(-) create mode 100644 py/nlr.c diff --git a/py/nlr.c b/py/nlr.c new file mode 100644 index 000000000..e4d6d10c0 --- /dev/null +++ b/py/nlr.c @@ -0,0 +1,74 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 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/mpstate.h" + +// Helper macros to save/restore the pystack state +#if MICROPY_ENABLE_PYSTACK +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur) +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack +#else +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf +#endif + +#if !MICROPY_NLR_SETJMP +// When not using setjmp, nlr_push_tail is called from inline asm so needs special care +#if MICROPY_NLR_X86 && (defined(_WIN32) || defined(__CYGWIN__)) +// On these 32-bit platforms make sure nlr_push_tail doesn't have a leading underscore +unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); +#else +// LTO can't see inside inline asm functions so explicitly mark nlr_push_tail as used +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); +#endif +#endif + +unsigned int nlr_push_tail(nlr_buf_t *nlr) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); + *top = nlr; + return 0; // normal return +} + +void nlr_pop(void) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + *top = (*top)->prev; +} + +NORETURN void nlr_jump(void *val) { + nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); + nlr_buf_t *top = *top_ptr; + if (top == NULL) { + nlr_jump_fail(val); + } + + top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); + *top_ptr = top->prev; + + nlr_jump_tail(top); +} diff --git a/py/nlr.h b/py/nlr.h index 1235f1460..012a73c31 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -30,29 +30,29 @@ // exception handling, basically a stack of setjmp/longjmp buffers #include -#include #include #include "py/mpconfig.h" -typedef struct _nlr_buf_t nlr_buf_t; -struct _nlr_buf_t { - // the entries here must all be machine word size - nlr_buf_t *prev; - void *ret_val; // always a concrete object (an exception instance) -#if !defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP +// If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch +// Allow a port to set MICROPY_NLR_NUM_REGS to define their own implementation +#if !MICROPY_NLR_SETJMP && !defined(MICROPY_NLR_NUM_REGS) #if defined(__i386__) - void *regs[6]; + #define MICROPY_NLR_X86 (1) + #define MICROPY_NLR_NUM_REGS (6) #elif defined(__x86_64__) - #if defined(__CYGWIN__) - void *regs[12]; - #else - void *regs[8]; - #endif + #define MICROPY_NLR_X64 (1) + #if defined(__CYGWIN__) + #define MICROPY_NLR_NUM_REGS (12) + #else + #define MICROPY_NLR_NUM_REGS (8) + #endif #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) - void *regs[10]; + #define MICROPY_NLR_THUMB (1) + #define MICROPY_NLR_NUM_REGS (10) #elif defined(__xtensa__) - void *regs[10]; + #define MICROPY_NLR_XTENSA (1) + #define MICROPY_NLR_NUM_REGS (10) #else #define MICROPY_NLR_SETJMP (1) //#warning "No native NLR support for this arch, using setjmp implementation" @@ -60,41 +60,39 @@ struct _nlr_buf_t { #endif #if MICROPY_NLR_SETJMP - jmp_buf jmpbuf; +#include #endif +typedef struct _nlr_buf_t nlr_buf_t; +struct _nlr_buf_t { + // the entries here must all be machine word size + nlr_buf_t *prev; + void *ret_val; // always a concrete object (an exception instance) + + #if MICROPY_NLR_SETJMP + jmp_buf jmpbuf; + #else + void *regs[MICROPY_NLR_NUM_REGS]; + #endif + #if MICROPY_ENABLE_PYSTACK void *pystack; #endif }; -// Helper macros to save/restore the pystack state -#if MICROPY_ENABLE_PYSTACK -#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur) -#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack -#else -#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf -#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf -#endif - #if MICROPY_NLR_SETJMP -#include "py/mpstate.h" - -NORETURN void nlr_setjmp_jump(void *val); // nlr_push() must be defined as a macro, because "The stack context will be // invalidated if the function which called setjmp() returns." -#define nlr_push(buf) ( \ - (buf)->prev = MP_STATE_THREAD(nlr_top), \ - MP_NLR_SAVE_PYSTACK(buf), \ - MP_STATE_THREAD(nlr_top) = (buf), \ - setjmp((buf)->jmpbuf)) -#define nlr_pop() { MP_STATE_THREAD(nlr_top) = MP_STATE_THREAD(nlr_top)->prev; } -#define nlr_jump(val) nlr_setjmp_jump(val) +// For this case it is safe to call nlr_push_tail() first. +#define nlr_push(buf) (nlr_push_tail(buf), setjmp((buf)->jmpbuf)) #else unsigned int nlr_push(nlr_buf_t *); +#endif + +unsigned int nlr_push_tail(nlr_buf_t *top); void nlr_pop(void); NORETURN void nlr_jump(void *val); -#endif +NORETURN void nlr_jump_tail(nlr_buf_t *top); // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather @@ -123,7 +121,6 @@ NORETURN void nlr_jump_fail(void *val); /* #define nlr_push(val) \ printf("nlr_push: before: nlr_top=%p, val=%p\n", MP_STATE_THREAD(nlr_top), val),assert(MP_STATE_THREAD(nlr_top) != val),nlr_push(val) -#endif */ #endif diff --git a/py/nlrsetjmp.c b/py/nlrsetjmp.c index 63376a553..6ddad3793 100644 --- a/py/nlrsetjmp.c +++ b/py/nlrsetjmp.c @@ -28,15 +28,7 @@ #if MICROPY_NLR_SETJMP -void nlr_setjmp_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; +NORETURN void nlr_jump_tail(nlr_buf_t *top) { longjmp(top->jmpbuf, 1); } diff --git a/py/nlrthumb.c b/py/nlrthumb.c index eab5759f2..4edd1456d 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) +#if MICROPY_NLR_THUMB #undef nlr_push @@ -37,7 +37,6 @@ // r4-r11, r13=sp __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { - __asm volatile ( "str r4, [r0, #12] \n" // store r4 into nlr_buf "str r5, [r0, #16] \n" // store r5 into nlr_buf @@ -75,36 +74,10 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { "b nlr_push_tail \n" // do the rest in C #endif ); - - return 0; // needed to silence compiler warning } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - -NORETURN __attribute__((naked)) void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; - +NORETURN __attribute__((naked)) void nlr_jump_tail(nlr_buf_t *top) { __asm volatile ( - "mov r0, %0 \n" // r0 points to nlr_buf "ldr r4, [r0, #12] \n" // load r4 from nlr_buf "ldr r5, [r0, #16] \n" // load r5 from nlr_buf "ldr r6, [r0, #20] \n" // load r6 from nlr_buf @@ -133,12 +106,7 @@ NORETURN __attribute__((naked)) void nlr_jump(void *val) { #endif "movs r0, #1 \n" // return 1, non-local return "bx lr \n" // return - : // output operands - : "r"(top) // input operands - : // clobbered registers ); - - for (;;); // needed to silence compiler warning } -#endif // (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) +#endif // MICROPY_NLR_THUMB diff --git a/py/nlrx64.c b/py/nlrx64.c index ddcd76166..e7e2e1474 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if !MICROPY_NLR_SETJMP && defined(__x86_64__) +#if MICROPY_NLR_X64 #undef nlr_push @@ -39,8 +39,6 @@ #define NLR_OS_WINDOWS 0 #endif -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); - unsigned int nlr_push(nlr_buf_t *nlr) { (void)nlr; @@ -88,54 +86,38 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - -NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; +NORETURN void nlr_jump_tail(nlr_buf_t *top) { + (void)top; __asm volatile ( - "movq %0, %%rcx \n" // %rcx points to nlr_buf #if NLR_OS_WINDOWS - "movq 88(%%rcx), %%rsi \n" // load saved %rsi - "movq 80(%%rcx), %%rdi \n" // load saved %rdr + "movq 88(%rcx), %rsi \n" // load saved %rsi + "movq 80(%rcx), %rdi \n" // load saved %rdr + "movq 72(%rcx), %r15 \n" // load saved %r15 + "movq 64(%rcx), %r14 \n" // load saved %r14 + "movq 56(%rcx), %r13 \n" // load saved %r13 + "movq 48(%rcx), %r12 \n" // load saved %r12 + "movq 40(%rcx), %rbx \n" // load saved %rbx + "movq 32(%rcx), %rsp \n" // load saved %rsp + "movq 24(%rcx), %rbp \n" // load saved %rbp + "movq 16(%rcx), %rax \n" // load saved %rip + #else + "movq 72(%rdi), %r15 \n" // load saved %r15 + "movq 64(%rdi), %r14 \n" // load saved %r14 + "movq 56(%rdi), %r13 \n" // load saved %r13 + "movq 48(%rdi), %r12 \n" // load saved %r12 + "movq 40(%rdi), %rbx \n" // load saved %rbx + "movq 32(%rdi), %rsp \n" // load saved %rsp + "movq 24(%rdi), %rbp \n" // load saved %rbp + "movq 16(%rdi), %rax \n" // load saved %rip #endif - "movq 72(%%rcx), %%r15 \n" // load saved %r15 - "movq 64(%%rcx), %%r14 \n" // load saved %r14 - "movq 56(%%rcx), %%r13 \n" // load saved %r13 - "movq 48(%%rcx), %%r12 \n" // load saved %r12 - "movq 40(%%rcx), %%rbx \n" // load saved %rbx - "movq 32(%%rcx), %%rsp \n" // load saved %rsp - "movq 24(%%rcx), %%rbp \n" // load saved %rbp - "movq 16(%%rcx), %%rax \n" // load saved %rip - "movq %%rax, (%%rsp) \n" // store saved %rip to stack - "xorq %%rax, %%rax \n" // clear return register - "inc %%al \n" // increase to make 1, non-local return + "movq %rax, (%rsp) \n" // store saved %rip to stack + "xorq %rax, %rax \n" // clear return register + "inc %al \n" // increase to make 1, non-local return "ret \n" // return - : // output operands - : "r"(top) // input operands - : // clobbered registers ); for (;;); // needed to silence compiler warning } -#endif // !MICROPY_NLR_SETJMP && defined(__x86_64__) +#endif // MICROPY_NLR_X64 diff --git a/py/nlrx86.c b/py/nlrx86.c index 3a27460eb..cc37f72af 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -26,25 +26,13 @@ #include "py/mpstate.h" -#if !MICROPY_NLR_SETJMP && defined(__i386__) +#if MICROPY_NLR_X86 #undef nlr_push // For reference, x86 callee save regs are: // ebx, esi, edi, ebp, esp, eip -#if defined(_WIN32) || defined(__CYGWIN__) -#define NLR_OS_WINDOWS 1 -#else -#define NLR_OS_WINDOWS 0 -#endif - -#if NLR_OS_WINDOWS -unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); -#else -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); -#endif - unsigned int nlr_push(nlr_buf_t *nlr) { (void)nlr; @@ -70,48 +58,23 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - -NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; +NORETURN void nlr_jump_tail(nlr_buf_t *top) { + (void)top; __asm volatile ( - "mov %0, %%edx \n" // %edx points to nlr_buf - "mov 28(%%edx), %%esi \n" // load saved %esi - "mov 24(%%edx), %%edi \n" // load saved %edi - "mov 20(%%edx), %%ebx \n" // load saved %ebx - "mov 16(%%edx), %%esp \n" // load saved %esp - "mov 12(%%edx), %%ebp \n" // load saved %ebp - "mov 8(%%edx), %%eax \n" // load saved %eip - "mov %%eax, (%%esp) \n" // store saved %eip to stack - "xor %%eax, %%eax \n" // clear return register - "inc %%al \n" // increase to make 1, non-local return + "mov 28(%edx), %esi \n" // load saved %esi + "mov 24(%edx), %edi \n" // load saved %edi + "mov 20(%edx), %ebx \n" // load saved %ebx + "mov 16(%edx), %esp \n" // load saved %esp + "mov 12(%edx), %ebp \n" // load saved %ebp + "mov 8(%edx), %eax \n" // load saved %eip + "mov %eax, (%esp) \n" // store saved %eip to stack + "xor %eax, %eax \n" // clear return register + "inc %al \n" // increase to make 1, non-local return "ret \n" // return - : // output operands - : "r"(top) // input operands - : // clobbered registers ); for (;;); // needed to silence compiler warning } -#endif // !MICROPY_NLR_SETJMP && defined(__i386__) +#endif // MICROPY_NLR_X86 diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index 5a969fc87..6b9918227 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if !MICROPY_NLR_SETJMP && defined(__xtensa__) +#if MICROPY_NLR_XTENSA #undef nlr_push @@ -37,6 +37,7 @@ // a3-a7 = rest of args unsigned int nlr_push(nlr_buf_t *nlr) { + (void)nlr; __asm volatile ( "s32i.n a0, a2, 8 \n" // save regs... @@ -55,32 +56,10 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - -NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; +NORETURN void nlr_jump_tail(nlr_buf_t *top) { + (void)top; __asm volatile ( - "mov.n a2, %0 \n" // a2 points to nlr_buf "l32i.n a0, a2, 8 \n" // restore regs... "l32i.n a1, a2, 12 \n" "l32i.n a8, a2, 16 \n" @@ -93,12 +72,9 @@ NORETURN void nlr_jump(void *val) { "l32i.n a15, a2, 44 \n" "movi.n a2, 1 \n" // return 1, non-local return "ret.n \n" // return - : // output operands - : "r"(top) // input operands - : // clobbered registers ); for (;;); // needed to silence compiler warning } -#endif // !MICROPY_NLR_SETJMP && defined(__xtensa__) +#endif // MICROPY_NLR_XTENSA diff --git a/py/py.mk b/py/py.mk index 0b5d5f8c4..de82a971b 100644 --- a/py/py.mk +++ b/py/py.mk @@ -103,6 +103,7 @@ endif # py object files PY_O_BASENAME = \ mpstate.o \ + nlr.o \ nlrx86.o \ nlrx64.o \ nlrthumb.o \ From d7a52e15398e1746d0b2fd651d11d092fa59c580 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 19 Dec 2017 11:15:41 +1100 Subject: [PATCH 205/828] qemu-arm/test_main: Include setjmp.h because it's used by gc_collect. And it's no longer unconditionally included by nlr.h, only if NLR_SETJMP is defined. --- ports/qemu-arm/test_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index 926478ea1..adbdf04e1 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "py/obj.h" #include "py/compile.h" From 26d4a6fa45526fa7c267cd9228259642701708f6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 20 Dec 2017 16:54:31 +1100 Subject: [PATCH 206/828] py/malloc: Remove unneeded code checking m_malloc return value. m_malloc already checks for a failed allocation so there's no need to check for it in m_malloc0. --- py/malloc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/py/malloc.c b/py/malloc.c index 6835ed7c9..ba5c952f3 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -117,9 +117,6 @@ void *m_malloc_with_finaliser(size_t num_bytes) { void *m_malloc0(size_t num_bytes) { void *ptr = m_malloc(num_bytes); - if (ptr == NULL && num_bytes != 0) { - m_malloc_fail(num_bytes); - } // If this config is set then the GC clears all memory, so we don't need to. #if !MICROPY_GC_CONSERVATIVE_CLEAR memset(ptr, 0, num_bytes); From a1d85d6199c03871ccf16700743160fad64d057b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 20 Dec 2017 16:58:27 +1100 Subject: [PATCH 207/828] tests/basics/memoryerror: Add test for out-of-memory using realloc. --- tests/basics/memoryerror.py | 7 +++++++ tests/basics/memoryerror.py.exp | 1 + 2 files changed, 8 insertions(+) diff --git a/tests/basics/memoryerror.py b/tests/basics/memoryerror.py index ba145b7d4..18053f097 100644 --- a/tests/basics/memoryerror.py +++ b/tests/basics/memoryerror.py @@ -1,6 +1,13 @@ +# test out-of-memory with malloc l = list(range(1000)) try: 1000000000 * l except MemoryError: print('MemoryError') print(len(l), l[0], l[-1]) + +# test out-of-memory with realloc +try: + [].extend(range(1000000000)) +except MemoryError: + print('MemoryError') diff --git a/tests/basics/memoryerror.py.exp b/tests/basics/memoryerror.py.exp index c41e8c1a6..3d6c7f000 100644 --- a/tests/basics/memoryerror.py.exp +++ b/tests/basics/memoryerror.py.exp @@ -1,2 +1,3 @@ MemoryError 1000 0 999 +MemoryError From 82dc5c1d8c90c45ce93bbb41292df5e420642448 Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Wed, 20 Dec 2017 10:45:22 -0500 Subject: [PATCH 208/828] stm32: Use corrected capitalization of HAL_SD_CardStateTypedef. It was originally TypeDef. STM32L4 only supports Typedef and F4/F7 have legacy macros in stm32_hal_legacy.h to support both. --- ports/stm32/sdcard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index 484426b84..a54e05011 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -251,7 +251,7 @@ STATIC HAL_StatusTypeDef sdcard_wait_finished(SD_HandleTypeDef *sd, uint32_t tim } // Wait for SD card to complete the operation for (;;) { - HAL_SD_CardStateTypeDef state = HAL_SD_GetCardState(sd); + HAL_SD_CardStateTypedef state = HAL_SD_GetCardState(sd); if (state == HAL_SD_CARD_TRANSFER) { return HAL_OK; } From c73360bfdbecb0e2143147a7e7223c8161938456 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Dec 2017 15:20:42 +1100 Subject: [PATCH 209/828] stm32: Allow to build a board without any hardware I2C ports defined. This patch adds in internal config value MICROPY_HW_ENABLE_HW_I2C that is automatically configured, and enabled only if one or more hardware I2C ports are defined in the mpconfigboard.h file. If none are defined then the pyb.I2C class is excluded from the build, along with all supporting code. The machine.I2C class will still be available for software I2C. Disabling all hardware I2C on an F4 board saves around 10,000 bytes of code and 200 bytes of RAM. --- ports/stm32/i2c.c | 4 ++++ ports/stm32/machine_i2c.c | 4 ++++ ports/stm32/main.c | 3 +++ ports/stm32/modpyb.c | 2 ++ ports/stm32/mpconfigport.h | 12 +++++++++++- 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/ports/stm32/i2c.c b/ports/stm32/i2c.c index b22787cab..e255cbc6b 100644 --- a/ports/stm32/i2c.c +++ b/ports/stm32/i2c.c @@ -36,6 +36,8 @@ #include "dma.h" #include "i2c.h" +#if MICROPY_HW_ENABLE_HW_I2C + /// \moduleref pyb /// \class I2C - a two-wire serial protocol /// @@ -1033,3 +1035,5 @@ const mp_obj_type_t pyb_i2c_type = { .make_new = pyb_i2c_make_new, .locals_dict = (mp_obj_dict_t*)&pyb_i2c_locals_dict, }; + +#endif // MICROPY_HW_ENABLE_HW_I2C diff --git a/ports/stm32/machine_i2c.c b/ports/stm32/machine_i2c.c index 1be2151e3..1018a9b1a 100644 --- a/ports/stm32/machine_i2c.c +++ b/ports/stm32/machine_i2c.c @@ -34,6 +34,8 @@ #include "genhdr/pins.h" #include "i2c.h" +#if MICROPY_HW_ENABLE_HW_I2C + STATIC const mp_obj_type_t machine_hard_i2c_type; #if defined(MCU_SERIES_F4) @@ -548,3 +550,5 @@ STATIC const mp_obj_type_t machine_hard_i2c_type = { .protocol = &machine_hard_i2c_p, .locals_dict = (mp_obj_dict_t*)&mp_machine_soft_i2c_locals_dict, }; + +#endif // MICROPY_HW_ENABLE_HW_I2C diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 9a83f9f36..352c09bcd 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -556,7 +556,10 @@ soft_reset: rng_init0(); #endif + #if MICROPY_HW_ENABLE_HW_I2C i2c_init0(); + #endif + spi_init0(); pyb_usb_init0(); diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index 81cbdcc19..4d186e278 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -203,7 +203,9 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { #if defined(MICROPY_HW_LED1) { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pyb_led_type) }, #endif + #if MICROPY_HW_ENABLE_HW_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&pyb_i2c_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&pyb_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, #if MICROPY_HW_ENABLE_CAN diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 51d442561..6fa286b26 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -131,7 +131,6 @@ #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_I2C (1) -#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (SPI_FIRSTBIT_MSB) #define MICROPY_PY_MACHINE_SPI_LSB (SPI_FIRSTBIT_LSB) @@ -245,6 +244,17 @@ extern const struct _mp_obj_module_t mp_module_onewire; #define MICROPY_HW_MAX_UART (6) #endif +// enable hardware I2C if there are any peripherals defined +#define MICROPY_HW_ENABLE_HW_I2C ( \ + defined(MICROPY_HW_I2C1_SCL) \ + || defined(MICROPY_HW_I2C2_SCL) \ + || defined(MICROPY_HW_I2C3_SCL) \ + || defined(MICROPY_HW_I2C4_SCL) \ +) +#if MICROPY_HW_ENABLE_HW_I2C +#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new +#endif + #define MP_STATE_PORT MP_STATE_VM #define MICROPY_PORT_ROOT_POINTERS \ From 7a46d9ae7313d4f92e9c32bf4dcc1e161a56d7ff Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Wed, 20 Dec 2017 10:31:05 -0500 Subject: [PATCH 210/828] stm32/uart: Add support for 7-bit modes: 7N1 and 7N2. --- ports/stm32/uart.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 0b46d4f04..b2962984f 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -500,7 +500,14 @@ STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k if (!self->is_enabled) { mp_printf(print, "UART(%u)", self->uart_id); } else { - mp_int_t bits = (self->uart.Init.WordLength == UART_WORDLENGTH_8B ? 8 : 9); + mp_int_t bits; + switch (self->uart.Init.WordLength) { + #ifdef UART_WORDLENGTH_7B + case UART_WORDLENGTH_7B: bits = 7; break; + #endif + case UART_WORDLENGTH_8B: bits = 8; break; + case UART_WORDLENGTH_9B: default: bits = 9; break; + } if (self->uart.Init.Parity != UART_PARITY_NONE) { bits -= 1; } @@ -580,6 +587,10 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const init->WordLength = UART_WORDLENGTH_8B; } else if (bits == 9) { init->WordLength = UART_WORDLENGTH_9B; + #ifdef UART_WORDLENGTH_7B + } else if (bits == 7) { + init->WordLength = UART_WORDLENGTH_7B; + #endif } else { mp_raise_ValueError("unsupported combination of bits and parity"); } From f16c775a0782961a841f77cc48a789b3246d7ef7 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 10 Dec 2017 18:25:33 +0100 Subject: [PATCH 211/828] esp32/README: Update toolchain setup. --- ports/esp32/Makefile | 8 ++++++-- ports/esp32/README.md | 44 +++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 2fa8c1c50..6bf212964 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -21,19 +21,23 @@ FLASH_FREQ ?= 40m FLASH_SIZE ?= 4MB CROSS_COMPILE ?= xtensa-esp32-elf- +ESPIDF_SUPHASH := 2c95a77cf93781f296883d5dbafcdc18e4389656 + # paths to ESP IDF and its components ifeq ($(ESPIDF),) ifneq ($(IDF_PATH),) ESPIDF = $(IDF_PATH) else -$(error Please configure the ESPIDF variable) +$(info The ESPIDF variable has not been set, please set it to the root of the esp-idf repository.) +$(info See README.md for installation instructions.) +$(info Supported git hash: $(ESPIDF_SUPHASH)) +$(error ESPIDF not set) endif endif ESPCOMP = $(ESPIDF)/components ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py # verify the ESP IDF version -ESPIDF_SUPHASH := 2c95a77cf93781f296883d5dbafcdc18e4389656 ESPIDF_CURHASH := $(shell git -C $(ESPIDF) show -s --pretty=format:'%H') ifneq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH)) $(info ** WARNING **) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 5c0eb813d..ffd651c45 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -26,34 +26,28 @@ There are two main components that are needed to build the firmware: different to the compiler used by the ESP8266) - the Espressif IDF (IoT development framework, aka SDK) -Instructions for setting up both of these components are provided by the -ESP-IDF itself, which is found at https://github.com/espressif/esp-idf . -Follow the guide "Setting Up ESP-IDF", for Windows, Mac or Linux. You -only need to perform up to "Step 2" of the guide, by which stage you -should have installed the cross-compile and cloned the ESP-IDF repository. +The ESP-IDF changes quickly and MicroPython only supports a certain version. The +git hash of this version can be found by running `make` without a configured +`ESPIDF`. Then you can fetch only the given esp-idf using the following command: + + $ git clone https://github.com/espressif/esp-idf.git + $ git checkout + $ git submodule update --recursive + +The binary toolchain (binutils, gcc, etc.) can be installed using the following +guides: + + * [Linux installation](https://esp-idf.readthedocs.io/en/latest/get-started/linux-setup.html) + * [MacOS installation](https://esp-idf.readthedocs.io/en/latest/get-started/macos-setup.html) + * [Windows installation](https://esp-idf.readthedocs.io/en/latest/get-started/windows-setup.html) If you are on a Windows machine then the [Windows Subsystem for Linux](https://msdn.microsoft.com/en-au/commandline/wsl/install_guide) is the most efficient way to install the ESP32 toolchain and build the project. If you use WSL then follow the -[Linux guidelines](http://esp-idf.readthedocs.io/en/latest/get-started/linux-setup.html) +[Linux guidelines](https://esp-idf.readthedocs.io/en/latest/get-started/linux-setup.html) for the ESP-IDF instead of the Windows ones. -Be advised that the ESP-IDF is still undergoing changes and only some -versions are supported. To find which build is compatible refer to the line -in the makefile containing the following: -``` -ESPIDF_SUPHASH := -``` -After finishing "Step 2" you can roll back your current build of -the ESP-IDF (and update the submodules accordingly) using: -``` -$ git checkout -$ git submodule update --recursive -``` -Note that you will get a warning when building the code if the ESP-IDF -version is incorrect. - The Espressif ESP-IDF instructions above only install pyserial for Python 2, so if you're running Python 3 or a non-system Python you'll also need to install `pyserial` (or `esptool`) so that the Makefile can flash the board @@ -64,7 +58,13 @@ $ pip install pyserial Once everything is set up you should have a functioning toolchain with prefix xtensa-esp32-elf- (or otherwise if you configured it differently) -as well as a copy of the ESP-IDF repository. +as well as a copy of the ESP-IDF repository. You will need to update your `PATH` +environment variable to include the ESP32 toolchain. For example, you can issue +the following commands on (at least) Linux: + + $ export PATH=$PATH:$HOME/esp/crosstool-NG/builds/xtensa-esp32-elf/bin + +You cam put this command in your `.profile` or `.bash_login`. You then need to set the `ESPIDF` environment/makefile variable to point to the root of the ESP-IDF repository. You can set the variable in your PATH, From b90f51f86a0824d646131538320d0405ab3d0187 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 20 Dec 2017 01:15:26 +0100 Subject: [PATCH 212/828] drivers/sdcard: Support old SD cards (<=2GB). --- drivers/sdcard/sdcard.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/sdcard/sdcard.py b/drivers/sdcard/sdcard.py index 75a0c501e..fedb76f02 100644 --- a/drivers/sdcard/sdcard.py +++ b/drivers/sdcard/sdcard.py @@ -96,9 +96,14 @@ class SDCard: raise OSError("no response from SD card") csd = bytearray(16) self.readinto(csd) - if csd[0] & 0xc0 != 0x40: + if csd[0] & 0xc0 == 0x40: # CSD version 2.0 + self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 2014 + elif csd[0] & 0xc0 == 0x00: # CSD version 1.0 (old, <=2GB) + c_size = csd[6] & 0b11 | csd[7] << 2 | (csd[8] & 0b11000000) << 4 + c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7 + self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2)) + else: raise OSError("SD card CSD format not supported") - self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 2014 #print('sectors', self.sectors) # CMD16: set block length to 512 bytes From 9bcdb0acd1ea059533ba01d8c8ccfd12adeb0eee Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Dec 2017 17:16:42 +1100 Subject: [PATCH 213/828] esp8266/Makefile: Remove commented-out unused lines. These were copied from the stm32 port (then stmhal) at the very beginning of this port, with the anticipation that the esp8266 port would have board definition files with a list of valid pins and their names. But that has not been implemented and likely won't be, so remove the corresponding lines from the Makefile. --- ports/esp8266/Makefile | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/ports/esp8266/Makefile b/ports/esp8266/Makefile index 95236a8d9..9d6e502c7 100644 --- a/ports/esp8266/Makefile +++ b/ports/esp8266/Makefile @@ -143,7 +143,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) -#OBJ += $(BUILD)/pins_$(BOARD).o # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(EXTMOD_SRC_C) $(LIB_SRC_C) $(DRIVERS_SRC_C) @@ -195,32 +194,6 @@ ota: rm -f $(BUILD)/firmware.elf $(BUILD)/firmware.elf*.bin $(MAKE) LDSCRIPT=esp8266_ota.ld FWBIN=$(BUILD)/firmware-ota.bin -#MAKE_PINS = boards/make-pins.py -#BOARD_PINS = boards/$(BOARD)/pins.csv -#AF_FILE = boards/stm32f4xx_af.csv -#PREFIX_FILE = boards/stm32f4xx_prefix.c -#GEN_PINS_SRC = $(BUILD)/pins_$(BOARD).c -#GEN_PINS_HDR = $(HEADER_BUILD)/pins.h -#GEN_PINS_QSTR = $(BUILD)/pins_qstr.h -#GEN_PINS_AF_CONST = $(HEADER_BUILD)/pins_af_const.h -#GEN_PINS_AF_PY = $(BUILD)/pins_af.py - -# Making OBJ use an order-only depenedency on the generated pins.h file -# has the side effect of making the pins.h file before we actually compile -# any of the objects. The normal dependency generation will deal with the -# case when pins.h is modified. But when it doesn't exist, we don't know -# which source files might need it. -#$(OBJ): | $(HEADER_BUILD)/pins.h - -# Use a pattern rule here so that make will only call make-pins.py once to make -# both pins_$(BOARD).c and pins.h -#$(BUILD)/%_$(BOARD).c $(HEADER_BUILD)/%.h $(HEADER_BUILD)/%_af_const.h $(BUILD)/%_qstr.h: boards/$(BOARD)/%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE) | $(HEADER_BUILD) -# $(ECHO) "Create $@" -# $(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) --qstr $(GEN_PINS_QSTR) --af-const $(GEN_PINS_AF_CONST) --af-py $(GEN_PINS_AF_PY) > $(GEN_PINS_SRC) -# -#$(BUILD)/pins_$(BOARD).o: $(BUILD)/pins_$(BOARD).c -# $(call compile_c) - include $(TOP)/py/mkrules.mk axtls: $(BUILD)/libaxtls.a From d32417c0969a9cea5edec0858e147102a5720ed2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 23 Dec 2017 19:01:23 +1100 Subject: [PATCH 214/828] stm32/uart: Support board configs with CTS/RTS on UART6. --- ports/stm32/uart.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index b2962984f..659aa9943 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -241,6 +241,16 @@ STATIC bool uart_init2(pyb_uart_obj_t *uart_obj) { irqn = USART6_IRQn; pins[0] = &MICROPY_HW_UART6_TX; pins[1] = &MICROPY_HW_UART6_RX; + #if defined(MICROPY_HW_UART6_RTS) + if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_RTS) { + pins[2] = &MICROPY_HW_UART6_RTS; + } + #endif + #if defined(MICROPY_HW_UART6_CTS) + if (uart_obj->uart.Init.HwFlowCtl & UART_HWCONTROL_CTS) { + pins[3] = &MICROPY_HW_UART6_CTS; + } + #endif __USART6_CLK_ENABLE(); break; #endif From 008e1788e8a08261bb7c15976d18c946dd3b2259 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 23 Dec 2017 19:22:52 +1100 Subject: [PATCH 215/828] stm32/i2c: Fix bug with I2C4 initialisation. --- ports/stm32/i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/i2c.c b/ports/stm32/i2c.c index e255cbc6b..5bbc889c1 100644 --- a/ports/stm32/i2c.c +++ b/ports/stm32/i2c.c @@ -222,7 +222,7 @@ void i2c_init0(void) { #endif #if defined(MICROPY_HW_I2C4_SCL) memset(&I2CHandle4, 0, sizeof(I2C_HandleTypeDef)); - I2CHandle3.Instance = I2C4; + I2CHandle4.Instance = I2C4; #endif } @@ -258,7 +258,7 @@ void i2c_init(I2C_HandleTypeDef *i2c) { i2c_unit = 4; scl_pin = &MICROPY_HW_I2C4_SCL; sda_pin = &MICROPY_HW_I2C4_SDA; - __I2C3_CLK_ENABLE(); + __I2C4_CLK_ENABLE(); #endif } else { // I2C does not exist for this board (shouldn't get here, should be checked by caller) From b806889512a8c7f817ff23c7bf572c971acc9763 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 23 Dec 2017 19:23:35 +1100 Subject: [PATCH 216/828] stm32/i2c: Support more I2C baudrates for F746, and more F7 MCUs. --- ports/stm32/i2c.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ports/stm32/i2c.c b/ports/stm32/i2c.c index 5bbc889c1..5a6edc329 100644 --- a/ports/stm32/i2c.c +++ b/ports/stm32/i2c.c @@ -136,11 +136,17 @@ const pyb_i2c_obj_t pyb_i2c_obj[] = { // The value 0x40912732 was obtained from the DISCOVERY_I2Cx_TIMING constant // defined in the STM32F7Cube file Drivers/BSP/STM32F746G-Discovery/stm32f7456g_discovery.h -#define MICROPY_HW_I2C_BAUDRATE_TIMING {{100000, 0x40912732}} -#define MICROPY_HW_I2C_BAUDRATE_DEFAULT (100000) -#define MICROPY_HW_I2C_BAUDRATE_MAX (100000) +#define MICROPY_HW_I2C_BAUDRATE_TIMING { \ + {100000, 0x40912732}, \ + {400000, 0x10911823}, \ + {1000000, 0x00611116}, \ + } +#define MICROPY_HW_I2C_BAUDRATE_DEFAULT (400000) +#define MICROPY_HW_I2C_BAUDRATE_MAX (1000000) -#elif defined(STM32F767xx) || defined(STM32F769xx) +#elif defined(STM32F722xx) || defined(STM32F723xx) \ + || defined(STM32F732xx) || defined(STM32F733xx) \ + || defined(STM32F767xx) || defined(STM32F769xx) // These timing values are for f_I2CCLK=54MHz and are only approximate #define MICROPY_HW_I2C_BAUDRATE_TIMING { \ From 5de064fbd056944e393be9355855eee6562fecdb Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 23 Dec 2017 21:21:08 +0200 Subject: [PATCH 217/828] docs/library/index: Elaborate uPy libraries intro. --- docs/library/index.rst | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/library/index.rst b/docs/library/index.rst index b141abf1e..af1a0ff69 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -9,34 +9,36 @@ MicroPython libraries * MicroPython implements a subset of Python functionality for each module. * To ease extensibility, MicroPython versions of standard Python modules - usually have ``u`` (micro) prefix. + usually have ``u`` ("micro") prefix. * Any particular MicroPython variant or port may miss any feature/function - described in this general documentation, due to resource constraints. + described in this general documentation (due to resource constraints or + other limitations). This chapter describes modules (function and class libraries) which are built -into MicroPython. There are a few categories of modules: +into MicroPython. There are a few categories of such modules: * Modules which implement a subset of standard Python functionality and are not intended to be extended by the user. * Modules which implement a subset of Python functionality, with a provision for extension by the user (via Python code). * Modules which implement MicroPython extensions to the Python standard libraries. -* Modules specific to a particular port and thus not portable. +* Modules specific to a particular `MicroPython port` and thus not portable. -Note about the availability of modules and their contents: This documentation +Note about the availability of the modules and their contents: This documentation in general aspires to describe all modules and functions/classes which are -implemented in MicroPython. However, MicroPython is highly configurable, and +implemented in MicroPython project. However, MicroPython is highly configurable, and each port to a particular board/embedded system makes available only a subset of MicroPython libraries. For officially supported ports, there is an effort to either filter out non-applicable items, or mark individual descriptions with "Availability:" clauses describing which ports provide a given feature. + With that in mind, please still be warned that some functions/classes -in a module (or even the entire module) described in this documentation may be -unavailable in a particular build of MicroPython on a particular board. The +in a module (or even the entire module) described in this documentation **may be +unavailable** in a particular build of MicroPython on a particular system. The best place to find general information of the availability/non-availability of a particular feature is the "General Information" section which contains -information pertaining to a specific port. +information pertaining to a specific `MicroPython port`. Beyond the built-in libraries described in this documentation, many more modules from the Python standard library, as well as further MicroPython @@ -58,9 +60,9 @@ what done by the `micropython-lib` project mentioned above). On some embedded platforms, where it may be cumbersome to add Python-level wrapper modules to achieve naming compatibility with CPython, micro-modules are available both by their u-name, and also by their non-u-name. The -non-u-name can be overridden by a file of that name in your package path. -For example, ``import json`` will first search for a file ``json.py`` or -directory ``json`` and load that package if it is found. If nothing is found, +non-u-name can be overridden by a file of that name in your library path (``sys.path``). +For example, ``import json`` will first search for a file ``json.py`` (or package +directory ``json``) and load that module if it is found. If nothing is found, it will fallback to loading the built-in ``ujson`` module. .. only:: port_unix From d9977a8ad9c257005455ca8cbc7913584f2394a6 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 26 Dec 2017 14:46:16 +0200 Subject: [PATCH 218/828] zephyr/Makefile: clean: Clean libmicropython.a too. --- ports/zephyr/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr/Makefile b/ports/zephyr/Makefile index 2064fcef7..49a6f6718 100644 --- a/ports/zephyr/Makefile +++ b/ports/zephyr/Makefile @@ -82,7 +82,7 @@ build/genhdr/qstr.i.last: | $(Z_EXPORTS) LIBMICROPYTHON_EXTRA_CMD = -$(RM) -f outdir/$(OUTDIR_PREFIX)/zephyr.lnk # MicroPython's global clean cleans everything, fast -CLEAN_EXTRA = outdir prj_*_merged.conf +CLEAN_EXTRA = outdir libmicropython.a prj_*_merged.conf # Clean Zephyr things in Zephyr way z_clean: From 096e967aad6df760c16de4878b8b2eea02330011 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 26 Dec 2017 18:39:51 +0200 Subject: [PATCH 219/828] Revert "py/nlr: Factor out common NLR code to generic functions." This reverts commit 6a3a742a6c9caaa2be0fd0aac7a5df4ac816081c. The above commit has number of faults starting from the motivation down to the actual implementation. 1. Faulty implementation. The original code contained functions like: NORETURN void nlr_jump(void *val) { nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); nlr_buf_t *top = *top_ptr; ... __asm volatile ( "mov %0, %%edx \n" // %edx points to nlr_buf "mov 28(%%edx), %%esi \n" // load saved %esi "mov 24(%%edx), %%edi \n" // load saved %edi "mov 20(%%edx), %%ebx \n" // load saved %ebx "mov 16(%%edx), %%esp \n" // load saved %esp "mov 12(%%edx), %%ebp \n" // load saved %ebp "mov 8(%%edx), %%eax \n" // load saved %eip "mov %%eax, (%%esp) \n" // store saved %eip to stack "xor %%eax, %%eax \n" // clear return register "inc %%al \n" // increase to make 1, non-local return "ret \n" // return : // output operands : "r"(top) // input operands : // clobbered registers ); } Which clearly stated that C-level variable should be a parameter of the assembly, whcih then moved it into correct register. Whereas now it's: NORETURN void nlr_jump_tail(nlr_buf_t *top) { (void)top; __asm volatile ( "mov 28(%edx), %esi \n" // load saved %esi "mov 24(%edx), %edi \n" // load saved %edi "mov 20(%edx), %ebx \n" // load saved %ebx "mov 16(%edx), %esp \n" // load saved %esp "mov 12(%edx), %ebp \n" // load saved %ebp "mov 8(%edx), %eax \n" // load saved %eip "mov %eax, (%esp) \n" // store saved %eip to stack "xor %eax, %eax \n" // clear return register "inc %al \n" // increase to make 1, non-local return "ret \n" // return ); for (;;); // needed to silence compiler warning } Which just tries to perform operations on a completely random register (edx in this case). The outcome is the expected: saving the pure random luck of the compiler putting the right value in the random register above, there's a crash. 2. Non-critical assessment. The original commit message says "There is a small overhead introduced (typically 1 machine instruction)". That machine instruction is a call if a compiler doesn't perform tail optimization (happens regularly), and it's 1 instruction only with the broken code shown above, fixing it requires adding more. With inefficiencies already presented in the NLR code, the overhead becomes "considerable" (several times more than 1%), not "small". The commit message also says "This eliminates duplicated code.". An obvious way to eliminate duplication would be to factor out common code to macros, not introduce overhead and breakage like above. 3. Faulty motivation. All this started with a report of warnings/errors happening for a niche compiler. It could have been solved in one the direct ways: a) fixing it just for affected compiler(s); b) rewriting it in proper assembly (like it was before BTW); c) by not doing anything at all, MICROPY_NLR_SETJMP exists exactly to address minor-impact cases like thar (where a) or b) are not applicable). Instead, a backwards "solution" was put forward, leading to all the issues above. The best action thus appears to be revert and rework, not trying to work around what went haywire in the first place. --- py/nlr.c | 74 ------------------------------------------------- py/nlr.h | 75 ++++++++++++++++++++++++++------------------------ py/nlrsetjmp.c | 10 ++++++- py/nlrthumb.c | 38 +++++++++++++++++++++++-- py/nlrx64.c | 70 +++++++++++++++++++++++++++++----------------- py/nlrx86.c | 63 +++++++++++++++++++++++++++++++++--------- py/nlrxtensa.c | 34 +++++++++++++++++++---- py/py.mk | 1 - 8 files changed, 206 insertions(+), 159 deletions(-) delete mode 100644 py/nlr.c diff --git a/py/nlr.c b/py/nlr.c deleted file mode 100644 index e4d6d10c0..000000000 --- a/py/nlr.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013-2017 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/mpstate.h" - -// Helper macros to save/restore the pystack state -#if MICROPY_ENABLE_PYSTACK -#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur) -#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack -#else -#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf -#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf -#endif - -#if !MICROPY_NLR_SETJMP -// When not using setjmp, nlr_push_tail is called from inline asm so needs special care -#if MICROPY_NLR_X86 && (defined(_WIN32) || defined(__CYGWIN__)) -// On these 32-bit platforms make sure nlr_push_tail doesn't have a leading underscore -unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); -#else -// LTO can't see inside inline asm functions so explicitly mark nlr_push_tail as used -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); -#endif -#endif - -unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - -NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; - - nlr_jump_tail(top); -} diff --git a/py/nlr.h b/py/nlr.h index 012a73c31..1235f1460 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -30,29 +30,29 @@ // exception handling, basically a stack of setjmp/longjmp buffers #include +#include #include #include "py/mpconfig.h" -// If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch -// Allow a port to set MICROPY_NLR_NUM_REGS to define their own implementation -#if !MICROPY_NLR_SETJMP && !defined(MICROPY_NLR_NUM_REGS) +typedef struct _nlr_buf_t nlr_buf_t; +struct _nlr_buf_t { + // the entries here must all be machine word size + nlr_buf_t *prev; + void *ret_val; // always a concrete object (an exception instance) +#if !defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP #if defined(__i386__) - #define MICROPY_NLR_X86 (1) - #define MICROPY_NLR_NUM_REGS (6) + void *regs[6]; #elif defined(__x86_64__) - #define MICROPY_NLR_X64 (1) - #if defined(__CYGWIN__) - #define MICROPY_NLR_NUM_REGS (12) - #else - #define MICROPY_NLR_NUM_REGS (8) - #endif + #if defined(__CYGWIN__) + void *regs[12]; + #else + void *regs[8]; + #endif #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) - #define MICROPY_NLR_THUMB (1) - #define MICROPY_NLR_NUM_REGS (10) + void *regs[10]; #elif defined(__xtensa__) - #define MICROPY_NLR_XTENSA (1) - #define MICROPY_NLR_NUM_REGS (10) + void *regs[10]; #else #define MICROPY_NLR_SETJMP (1) //#warning "No native NLR support for this arch, using setjmp implementation" @@ -60,39 +60,41 @@ #endif #if MICROPY_NLR_SETJMP -#include -#endif - -typedef struct _nlr_buf_t nlr_buf_t; -struct _nlr_buf_t { - // the entries here must all be machine word size - nlr_buf_t *prev; - void *ret_val; // always a concrete object (an exception instance) - - #if MICROPY_NLR_SETJMP jmp_buf jmpbuf; - #else - void *regs[MICROPY_NLR_NUM_REGS]; - #endif +#endif #if MICROPY_ENABLE_PYSTACK void *pystack; #endif }; -#if MICROPY_NLR_SETJMP -// nlr_push() must be defined as a macro, because "The stack context will be -// invalidated if the function which called setjmp() returns." -// For this case it is safe to call nlr_push_tail() first. -#define nlr_push(buf) (nlr_push_tail(buf), setjmp((buf)->jmpbuf)) +// Helper macros to save/restore the pystack state +#if MICROPY_ENABLE_PYSTACK +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur) +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack #else -unsigned int nlr_push(nlr_buf_t *); +#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf +#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf #endif -unsigned int nlr_push_tail(nlr_buf_t *top); +#if MICROPY_NLR_SETJMP +#include "py/mpstate.h" + +NORETURN void nlr_setjmp_jump(void *val); +// nlr_push() must be defined as a macro, because "The stack context will be +// invalidated if the function which called setjmp() returns." +#define nlr_push(buf) ( \ + (buf)->prev = MP_STATE_THREAD(nlr_top), \ + MP_NLR_SAVE_PYSTACK(buf), \ + MP_STATE_THREAD(nlr_top) = (buf), \ + setjmp((buf)->jmpbuf)) +#define nlr_pop() { MP_STATE_THREAD(nlr_top) = MP_STATE_THREAD(nlr_top)->prev; } +#define nlr_jump(val) nlr_setjmp_jump(val) +#else +unsigned int nlr_push(nlr_buf_t *); void nlr_pop(void); NORETURN void nlr_jump(void *val); -NORETURN void nlr_jump_tail(nlr_buf_t *top); +#endif // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather @@ -121,6 +123,7 @@ NORETURN void nlr_jump_fail(void *val); /* #define nlr_push(val) \ printf("nlr_push: before: nlr_top=%p, val=%p\n", MP_STATE_THREAD(nlr_top), val),assert(MP_STATE_THREAD(nlr_top) != val),nlr_push(val) +#endif */ #endif diff --git a/py/nlrsetjmp.c b/py/nlrsetjmp.c index 6ddad3793..63376a553 100644 --- a/py/nlrsetjmp.c +++ b/py/nlrsetjmp.c @@ -28,7 +28,15 @@ #if MICROPY_NLR_SETJMP -NORETURN void nlr_jump_tail(nlr_buf_t *top) { +void nlr_setjmp_jump(void *val) { + nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); + nlr_buf_t *top = *top_ptr; + if (top == NULL) { + nlr_jump_fail(val); + } + top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); + *top_ptr = top->prev; longjmp(top->jmpbuf, 1); } diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 4edd1456d..eab5759f2 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if MICROPY_NLR_THUMB +#if (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) #undef nlr_push @@ -37,6 +37,7 @@ // r4-r11, r13=sp __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { + __asm volatile ( "str r4, [r0, #12] \n" // store r4 into nlr_buf "str r5, [r0, #16] \n" // store r5 into nlr_buf @@ -74,10 +75,36 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { "b nlr_push_tail \n" // do the rest in C #endif ); + + return 0; // needed to silence compiler warning } -NORETURN __attribute__((naked)) void nlr_jump_tail(nlr_buf_t *top) { +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); + *top = nlr; + return 0; // normal return +} + +void nlr_pop(void) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + *top = (*top)->prev; +} + +NORETURN __attribute__((naked)) void nlr_jump(void *val) { + nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); + nlr_buf_t *top = *top_ptr; + if (top == NULL) { + nlr_jump_fail(val); + } + + top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); + *top_ptr = top->prev; + __asm volatile ( + "mov r0, %0 \n" // r0 points to nlr_buf "ldr r4, [r0, #12] \n" // load r4 from nlr_buf "ldr r5, [r0, #16] \n" // load r5 from nlr_buf "ldr r6, [r0, #20] \n" // load r6 from nlr_buf @@ -106,7 +133,12 @@ NORETURN __attribute__((naked)) void nlr_jump_tail(nlr_buf_t *top) { #endif "movs r0, #1 \n" // return 1, non-local return "bx lr \n" // return + : // output operands + : "r"(top) // input operands + : // clobbered registers ); + + for (;;); // needed to silence compiler warning } -#endif // MICROPY_NLR_THUMB +#endif // (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) diff --git a/py/nlrx64.c b/py/nlrx64.c index e7e2e1474..ddcd76166 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if MICROPY_NLR_X64 +#if !MICROPY_NLR_SETJMP && defined(__x86_64__) #undef nlr_push @@ -39,6 +39,8 @@ #define NLR_OS_WINDOWS 0 #endif +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); + unsigned int nlr_push(nlr_buf_t *nlr) { (void)nlr; @@ -86,38 +88,54 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -NORETURN void nlr_jump_tail(nlr_buf_t *top) { - (void)top; +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); + *top = nlr; + return 0; // normal return +} + +void nlr_pop(void) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + *top = (*top)->prev; +} + +NORETURN void nlr_jump(void *val) { + nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); + nlr_buf_t *top = *top_ptr; + if (top == NULL) { + nlr_jump_fail(val); + } + + top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); + *top_ptr = top->prev; __asm volatile ( + "movq %0, %%rcx \n" // %rcx points to nlr_buf #if NLR_OS_WINDOWS - "movq 88(%rcx), %rsi \n" // load saved %rsi - "movq 80(%rcx), %rdi \n" // load saved %rdr - "movq 72(%rcx), %r15 \n" // load saved %r15 - "movq 64(%rcx), %r14 \n" // load saved %r14 - "movq 56(%rcx), %r13 \n" // load saved %r13 - "movq 48(%rcx), %r12 \n" // load saved %r12 - "movq 40(%rcx), %rbx \n" // load saved %rbx - "movq 32(%rcx), %rsp \n" // load saved %rsp - "movq 24(%rcx), %rbp \n" // load saved %rbp - "movq 16(%rcx), %rax \n" // load saved %rip - #else - "movq 72(%rdi), %r15 \n" // load saved %r15 - "movq 64(%rdi), %r14 \n" // load saved %r14 - "movq 56(%rdi), %r13 \n" // load saved %r13 - "movq 48(%rdi), %r12 \n" // load saved %r12 - "movq 40(%rdi), %rbx \n" // load saved %rbx - "movq 32(%rdi), %rsp \n" // load saved %rsp - "movq 24(%rdi), %rbp \n" // load saved %rbp - "movq 16(%rdi), %rax \n" // load saved %rip + "movq 88(%%rcx), %%rsi \n" // load saved %rsi + "movq 80(%%rcx), %%rdi \n" // load saved %rdr #endif - "movq %rax, (%rsp) \n" // store saved %rip to stack - "xorq %rax, %rax \n" // clear return register - "inc %al \n" // increase to make 1, non-local return + "movq 72(%%rcx), %%r15 \n" // load saved %r15 + "movq 64(%%rcx), %%r14 \n" // load saved %r14 + "movq 56(%%rcx), %%r13 \n" // load saved %r13 + "movq 48(%%rcx), %%r12 \n" // load saved %r12 + "movq 40(%%rcx), %%rbx \n" // load saved %rbx + "movq 32(%%rcx), %%rsp \n" // load saved %rsp + "movq 24(%%rcx), %%rbp \n" // load saved %rbp + "movq 16(%%rcx), %%rax \n" // load saved %rip + "movq %%rax, (%%rsp) \n" // store saved %rip to stack + "xorq %%rax, %%rax \n" // clear return register + "inc %%al \n" // increase to make 1, non-local return "ret \n" // return + : // output operands + : "r"(top) // input operands + : // clobbered registers ); for (;;); // needed to silence compiler warning } -#endif // MICROPY_NLR_X64 +#endif // !MICROPY_NLR_SETJMP && defined(__x86_64__) diff --git a/py/nlrx86.c b/py/nlrx86.c index cc37f72af..3a27460eb 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -26,13 +26,25 @@ #include "py/mpstate.h" -#if MICROPY_NLR_X86 +#if !MICROPY_NLR_SETJMP && defined(__i386__) #undef nlr_push // For reference, x86 callee save regs are: // ebx, esi, edi, ebp, esp, eip +#if defined(_WIN32) || defined(__CYGWIN__) +#define NLR_OS_WINDOWS 1 +#else +#define NLR_OS_WINDOWS 0 +#endif + +#if NLR_OS_WINDOWS +unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); +#else +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); +#endif + unsigned int nlr_push(nlr_buf_t *nlr) { (void)nlr; @@ -58,23 +70,48 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -NORETURN void nlr_jump_tail(nlr_buf_t *top) { - (void)top; +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); + *top = nlr; + return 0; // normal return +} + +void nlr_pop(void) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + *top = (*top)->prev; +} + +NORETURN void nlr_jump(void *val) { + nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); + nlr_buf_t *top = *top_ptr; + if (top == NULL) { + nlr_jump_fail(val); + } + + top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); + *top_ptr = top->prev; __asm volatile ( - "mov 28(%edx), %esi \n" // load saved %esi - "mov 24(%edx), %edi \n" // load saved %edi - "mov 20(%edx), %ebx \n" // load saved %ebx - "mov 16(%edx), %esp \n" // load saved %esp - "mov 12(%edx), %ebp \n" // load saved %ebp - "mov 8(%edx), %eax \n" // load saved %eip - "mov %eax, (%esp) \n" // store saved %eip to stack - "xor %eax, %eax \n" // clear return register - "inc %al \n" // increase to make 1, non-local return + "mov %0, %%edx \n" // %edx points to nlr_buf + "mov 28(%%edx), %%esi \n" // load saved %esi + "mov 24(%%edx), %%edi \n" // load saved %edi + "mov 20(%%edx), %%ebx \n" // load saved %ebx + "mov 16(%%edx), %%esp \n" // load saved %esp + "mov 12(%%edx), %%ebp \n" // load saved %ebp + "mov 8(%%edx), %%eax \n" // load saved %eip + "mov %%eax, (%%esp) \n" // store saved %eip to stack + "xor %%eax, %%eax \n" // clear return register + "inc %%al \n" // increase to make 1, non-local return "ret \n" // return + : // output operands + : "r"(top) // input operands + : // clobbered registers ); for (;;); // needed to silence compiler warning } -#endif // MICROPY_NLR_X86 +#endif // !MICROPY_NLR_SETJMP && defined(__i386__) diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index 6b9918227..5a969fc87 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if MICROPY_NLR_XTENSA +#if !MICROPY_NLR_SETJMP && defined(__xtensa__) #undef nlr_push @@ -37,7 +37,6 @@ // a3-a7 = rest of args unsigned int nlr_push(nlr_buf_t *nlr) { - (void)nlr; __asm volatile ( "s32i.n a0, a2, 8 \n" // save regs... @@ -56,10 +55,32 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -NORETURN void nlr_jump_tail(nlr_buf_t *top) { - (void)top; +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); + *top = nlr; + return 0; // normal return +} + +void nlr_pop(void) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + *top = (*top)->prev; +} + +NORETURN void nlr_jump(void *val) { + nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); + nlr_buf_t *top = *top_ptr; + if (top == NULL) { + nlr_jump_fail(val); + } + + top->ret_val = val; + MP_NLR_RESTORE_PYSTACK(top); + *top_ptr = top->prev; __asm volatile ( + "mov.n a2, %0 \n" // a2 points to nlr_buf "l32i.n a0, a2, 8 \n" // restore regs... "l32i.n a1, a2, 12 \n" "l32i.n a8, a2, 16 \n" @@ -72,9 +93,12 @@ NORETURN void nlr_jump_tail(nlr_buf_t *top) { "l32i.n a15, a2, 44 \n" "movi.n a2, 1 \n" // return 1, non-local return "ret.n \n" // return + : // output operands + : "r"(top) // input operands + : // clobbered registers ); for (;;); // needed to silence compiler warning } -#endif // MICROPY_NLR_XTENSA +#endif // !MICROPY_NLR_SETJMP && defined(__xtensa__) diff --git a/py/py.mk b/py/py.mk index de82a971b..0b5d5f8c4 100644 --- a/py/py.mk +++ b/py/py.mk @@ -103,7 +103,6 @@ endif # py object files PY_O_BASENAME = \ mpstate.o \ - nlr.o \ nlrx86.o \ nlrx64.o \ nlrthumb.o \ From 7a9a73ee8466111f73a7de7d27568c1260d802ef Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 26 Dec 2017 20:16:08 +0200 Subject: [PATCH 220/828] zephyr/main: Remove unused do_str() function. The artifact of initial porting effort. --- ports/zephyr/main.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 8c64fdcee..58d127ec6 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -51,21 +51,6 @@ static char *stack_top; static char heap[MICROPY_HEAP_SIZE]; -void do_str(const char *src, mp_parse_input_kind_t input_kind) { - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - 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_call_function_0(module_fun); - nlr_pop(); - } else { - // uncaught exception - mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); - } -} - void init_zephyr(void) { // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap // network addresses. From 97cc48553828ed091325d0d3922eb4cd6c377221 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 Dec 2017 15:59:09 +1100 Subject: [PATCH 221/828] py/nlrthumb: Fix use of naked funcs, must only contain basic asm code. A function with a naked attribute must only contain basic inline asm statements and no C code. For nlr_push this means removing the "return 0" statement. But for some gcc versions this induces a compiler warning so the __builtin_unreachable() line needs to be added. For nlr_jump, this function contains a combination of C code and inline asm so cannot be naked. --- py/nlrthumb.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/py/nlrthumb.c b/py/nlrthumb.c index eab5759f2..18d31eb70 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -76,7 +76,10 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { #endif ); - return 0; // needed to silence compiler warning + #if defined(__GNUC__) + // Older versions of gcc give an error when naked functions don't return a value + __builtin_unreachable(); + #endif } __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { @@ -92,7 +95,7 @@ void nlr_pop(void) { *top = (*top)->prev; } -NORETURN __attribute__((naked)) void nlr_jump(void *val) { +NORETURN void nlr_jump(void *val) { nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); nlr_buf_t *top = *top_ptr; if (top == NULL) { @@ -138,7 +141,11 @@ NORETURN __attribute__((naked)) void nlr_jump(void *val) { : // clobbered registers ); + #if defined(__GNUC__) + __builtin_unreachable(); + #else for (;;); // needed to silence compiler warning + #endif } #endif // (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) From 5bf8e85fc828974199d469db711aa2f9649c467b Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 Dec 2017 16:18:39 +1100 Subject: [PATCH 222/828] py/nlr: Clean up selection and config of NLR implementation. If MICROPY_NLR_SETJMP is not enabled and the machine is auto-detected then nlr.h now defines some convenience macros for the individual NLR implementations to use (eg MICROPY_NLR_THUMB). This keeps nlr.h and the implementation in sync, and also makes the nlr_buf_t struct easier to read. --- py/nlr.h | 44 +++++++++++++++++++++++++++----------------- py/nlrthumb.c | 4 ++-- py/nlrx64.c | 4 ++-- py/nlrx86.c | 4 ++-- py/nlrxtensa.c | 4 ++-- 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/py/nlr.h b/py/nlr.h index 1235f1460..bd9fcc884 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -30,29 +30,28 @@ // exception handling, basically a stack of setjmp/longjmp buffers #include -#include #include #include "py/mpconfig.h" -typedef struct _nlr_buf_t nlr_buf_t; -struct _nlr_buf_t { - // the entries here must all be machine word size - nlr_buf_t *prev; - void *ret_val; // always a concrete object (an exception instance) -#if !defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP +// If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch +#if !MICROPY_NLR_SETJMP #if defined(__i386__) - void *regs[6]; + #define MICROPY_NLR_X86 (1) + #define MICROPY_NLR_NUM_REGS (6) #elif defined(__x86_64__) - #if defined(__CYGWIN__) - void *regs[12]; - #else - void *regs[8]; - #endif + #define MICROPY_NLR_X64 (1) + #if defined(__CYGWIN__) + #define MICROPY_NLR_NUM_REGS (12) + #else + #define MICROPY_NLR_NUM_REGS (8) + #endif #elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) - void *regs[10]; + #define MICROPY_NLR_THUMB (1) + #define MICROPY_NLR_NUM_REGS (10) #elif defined(__xtensa__) - void *regs[10]; + #define MICROPY_NLR_XTENSA (1) + #define MICROPY_NLR_NUM_REGS (10) #else #define MICROPY_NLR_SETJMP (1) //#warning "No native NLR support for this arch, using setjmp implementation" @@ -60,9 +59,21 @@ struct _nlr_buf_t { #endif #if MICROPY_NLR_SETJMP - jmp_buf jmpbuf; +#include #endif +typedef struct _nlr_buf_t nlr_buf_t; +struct _nlr_buf_t { + // the entries here must all be machine word size + nlr_buf_t *prev; + void *ret_val; // always a concrete object (an exception instance) + + #if MICROPY_NLR_SETJMP + jmp_buf jmpbuf; + #else + void *regs[MICROPY_NLR_NUM_REGS]; + #endif + #if MICROPY_ENABLE_PYSTACK void *pystack; #endif @@ -123,7 +134,6 @@ NORETURN void nlr_jump_fail(void *val); /* #define nlr_push(val) \ printf("nlr_push: before: nlr_top=%p, val=%p\n", MP_STATE_THREAD(nlr_top), val),assert(MP_STATE_THREAD(nlr_top) != val),nlr_push(val) -#endif */ #endif diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 18d31eb70..cc081e3ff 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) +#if MICROPY_NLR_THUMB #undef nlr_push @@ -148,4 +148,4 @@ NORETURN void nlr_jump(void *val) { #endif } -#endif // (!defined(MICROPY_NLR_SETJMP) || !MICROPY_NLR_SETJMP) && (defined(__thumb2__) || defined(__thumb__) || defined(__arm__)) +#endif // MICROPY_NLR_THUMB diff --git a/py/nlrx64.c b/py/nlrx64.c index ddcd76166..927b21591 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if !MICROPY_NLR_SETJMP && defined(__x86_64__) +#if MICROPY_NLR_X64 #undef nlr_push @@ -138,4 +138,4 @@ NORETURN void nlr_jump(void *val) { for (;;); // needed to silence compiler warning } -#endif // !MICROPY_NLR_SETJMP && defined(__x86_64__) +#endif // MICROPY_NLR_X64 diff --git a/py/nlrx86.c b/py/nlrx86.c index 3a27460eb..0e03eef6f 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if !MICROPY_NLR_SETJMP && defined(__i386__) +#if MICROPY_NLR_X86 #undef nlr_push @@ -114,4 +114,4 @@ NORETURN void nlr_jump(void *val) { for (;;); // needed to silence compiler warning } -#endif // !MICROPY_NLR_SETJMP && defined(__i386__) +#endif // MICROPY_NLR_X86 diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index 5a969fc87..73f14385d 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -26,7 +26,7 @@ #include "py/mpstate.h" -#if !MICROPY_NLR_SETJMP && defined(__xtensa__) +#if MICROPY_NLR_XTENSA #undef nlr_push @@ -101,4 +101,4 @@ NORETURN void nlr_jump(void *val) { for (;;); // needed to silence compiler warning } -#endif // !MICROPY_NLR_SETJMP && defined(__xtensa__) +#endif // MICROPY_NLR_XTENSA From b25f92160b318a096c516c430afde5472a944c19 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 Dec 2017 16:46:30 +1100 Subject: [PATCH 223/828] py/nlr: Factor out common NLR code to macro and generic funcs in nlr.c. Each NLR implementation (Thumb, x86, x64, xtensa, setjmp) duplicates a lot of the NLR code, specifically that dealing with pushing and popping the NLR pointer to maintain the linked-list of NLR buffers. This patch factors all of that code out of the specific implementations into generic functions in nlr.c, along with a helper macro in nlr.h. This eliminates duplicated code. --- py/nlr.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ py/nlr.h | 27 +++++++++++++++----------- py/nlrsetjmp.c | 6 +++--- py/nlrthumb.c | 25 ++----------------------- py/nlrx64.c | 23 +---------------------- py/nlrx86.c | 23 +---------------------- py/nlrxtensa.c | 23 +---------------------- py/py.mk | 1 + 8 files changed, 76 insertions(+), 103 deletions(-) create mode 100644 py/nlr.c diff --git a/py/nlr.c b/py/nlr.c new file mode 100644 index 000000000..52d56afb8 --- /dev/null +++ b/py/nlr.c @@ -0,0 +1,51 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2017 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/mpstate.h" + +#if !MICROPY_NLR_SETJMP +// When not using setjmp, nlr_push_tail is called from inline asm so needs special c +#if MICROPY_NLR_X86 && (defined(_WIN32) || defined(__CYGWIN__)) +// On these 32-bit platforms make sure nlr_push_tail doesn't have a leading undersco +unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); +#else +// LTO can't see inside inline asm functions so explicitly mark nlr_push_tail as use +__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); +#endif +#endif + +unsigned int nlr_push_tail(nlr_buf_t *nlr) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + nlr->prev = *top; + MP_NLR_SAVE_PYSTACK(nlr); + *top = nlr; + return 0; // normal return +} + +void nlr_pop(void) { + nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); + *top = (*top)->prev; +} diff --git a/py/nlr.h b/py/nlr.h index bd9fcc884..e4dfa6896 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -88,24 +88,29 @@ struct _nlr_buf_t { #define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf #endif -#if MICROPY_NLR_SETJMP -#include "py/mpstate.h" +// Helper macro to use at the start of a specific nlr_jump implementation +#define MP_NLR_JUMP_HEAD(val, top) \ + nlr_buf_t **_top_ptr = &MP_STATE_THREAD(nlr_top); \ + nlr_buf_t *top = *_top_ptr; \ + if (top == NULL) { \ + nlr_jump_fail(val); \ + } \ + top->ret_val = val; \ + MP_NLR_RESTORE_PYSTACK(top); \ + *_top_ptr = top->prev; \ -NORETURN void nlr_setjmp_jump(void *val); +#if MICROPY_NLR_SETJMP // nlr_push() must be defined as a macro, because "The stack context will be // invalidated if the function which called setjmp() returns." -#define nlr_push(buf) ( \ - (buf)->prev = MP_STATE_THREAD(nlr_top), \ - MP_NLR_SAVE_PYSTACK(buf), \ - MP_STATE_THREAD(nlr_top) = (buf), \ - setjmp((buf)->jmpbuf)) -#define nlr_pop() { MP_STATE_THREAD(nlr_top) = MP_STATE_THREAD(nlr_top)->prev; } -#define nlr_jump(val) nlr_setjmp_jump(val) +// For this case it is safe to call nlr_push_tail() first. +#define nlr_push(buf) (nlr_push_tail(buf), setjmp((buf)->jmpbuf)) #else unsigned int nlr_push(nlr_buf_t *); +#endif + +unsigned int nlr_push_tail(nlr_buf_t *top); void nlr_pop(void); NORETURN void nlr_jump(void *val); -#endif // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather diff --git a/py/nlrsetjmp.c b/py/nlrsetjmp.c index 63376a553..960dd86f5 100644 --- a/py/nlrsetjmp.c +++ b/py/nlrsetjmp.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2013-2017 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 @@ -24,11 +24,11 @@ * THE SOFTWARE. */ -#include "py/nlr.h" +#include "py/mpstate.h" #if MICROPY_NLR_SETJMP -void nlr_setjmp_jump(void *val) { +void nlr_jump(void *val) { nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); nlr_buf_t *top = *top_ptr; if (top == NULL) { diff --git a/py/nlrthumb.c b/py/nlrthumb.c index cc081e3ff..fb0a92236 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013-2016 Damien P. George + * Copyright (c) 2013-2017 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 @@ -82,29 +82,8 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { #endif } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; + MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "mov r0, %0 \n" // r0 points to nlr_buf diff --git a/py/nlrx64.c b/py/nlrx64.c index 927b21591..663a457b7 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -88,29 +88,8 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; + MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "movq %0, %%rcx \n" // %rcx points to nlr_buf diff --git a/py/nlrx86.c b/py/nlrx86.c index 0e03eef6f..9490c4f42 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -70,29 +70,8 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; + MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "mov %0, %%edx \n" // %edx points to nlr_buf diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index 73f14385d..cd3dee364 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -55,29 +55,8 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - nlr->prev = *top; - MP_NLR_SAVE_PYSTACK(nlr); - *top = nlr; - return 0; // normal return -} - -void nlr_pop(void) { - nlr_buf_t **top = &MP_STATE_THREAD(nlr_top); - *top = (*top)->prev; -} - NORETURN void nlr_jump(void *val) { - nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top); - nlr_buf_t *top = *top_ptr; - if (top == NULL) { - nlr_jump_fail(val); - } - - top->ret_val = val; - MP_NLR_RESTORE_PYSTACK(top); - *top_ptr = top->prev; + MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "mov.n a2, %0 \n" // a2 points to nlr_buf diff --git a/py/py.mk b/py/py.mk index 0b5d5f8c4..de82a971b 100644 --- a/py/py.mk +++ b/py/py.mk @@ -103,6 +103,7 @@ endif # py object files PY_O_BASENAME = \ mpstate.o \ + nlr.o \ nlrx86.o \ nlrx64.o \ nlrthumb.o \ From dfe8980acfc678bfa3270bd17b7b3e45958a0a3a Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Wed, 20 Dec 2017 10:39:30 -0500 Subject: [PATCH 224/828] stm32/spi: If MICROPY_HW_SPIn_MISO undefined, do not claim pin on init. This permits output-only SPI use. --- ports/stm32/spi.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index cfd9c2667..3cd470ccb 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -243,8 +243,7 @@ STATIC void spi_set_params(SPI_HandleTypeDef *spi, uint32_t prescale, int32_t ba // TODO allow to take a list of pins to use void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { const pyb_spi_obj_t *self; - const pin_obj_t *pins[4]; - pins[0] = NULL; + const pin_obj_t *pins[4] = { NULL, NULL, NULL, NULL }; if (0) { #if defined(MICROPY_HW_SPI1_SCK) @@ -254,7 +253,9 @@ void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { pins[0] = &MICROPY_HW_SPI1_NSS; #endif pins[1] = &MICROPY_HW_SPI1_SCK; + #if defined(MICROPY_HW_SPI1_MISO) pins[2] = &MICROPY_HW_SPI1_MISO; + #endif pins[3] = &MICROPY_HW_SPI1_MOSI; // enable the SPI clock __SPI1_CLK_ENABLE(); @@ -266,7 +267,9 @@ void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { pins[0] = &MICROPY_HW_SPI2_NSS; #endif pins[1] = &MICROPY_HW_SPI2_SCK; + #if defined(MICROPY_HW_SPI2_MISO) pins[2] = &MICROPY_HW_SPI2_MISO; + #endif pins[3] = &MICROPY_HW_SPI2_MOSI; // enable the SPI clock __SPI2_CLK_ENABLE(); @@ -278,7 +281,9 @@ void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { pins[0] = &MICROPY_HW_SPI3_NSS; #endif pins[1] = &MICROPY_HW_SPI3_SCK; + #if defined(MICROPY_HW_SPI3_MISO) pins[2] = &MICROPY_HW_SPI3_MISO; + #endif pins[3] = &MICROPY_HW_SPI3_MOSI; // enable the SPI clock __SPI3_CLK_ENABLE(); @@ -290,7 +295,9 @@ void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { pins[0] = &MICROPY_HW_SPI4_NSS; #endif pins[1] = &MICROPY_HW_SPI4_SCK; + #if defined(MICROPY_HW_SPI4_MISO) pins[2] = &MICROPY_HW_SPI4_MISO; + #endif pins[3] = &MICROPY_HW_SPI4_MOSI; // enable the SPI clock __SPI4_CLK_ENABLE(); @@ -302,7 +309,9 @@ void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { pins[0] = &MICROPY_HW_SPI5_NSS; #endif pins[1] = &MICROPY_HW_SPI5_SCK; + #if defined(MICROPY_HW_SPI5_MISO) pins[2] = &MICROPY_HW_SPI5_MISO; + #endif pins[3] = &MICROPY_HW_SPI5_MOSI; // enable the SPI clock __SPI5_CLK_ENABLE(); @@ -314,7 +323,9 @@ void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { pins[0] = &MICROPY_HW_SPI6_NSS; #endif pins[1] = &MICROPY_HW_SPI6_SCK; + #if defined(MICROPY_HW_SPI6_MISO) pins[2] = &MICROPY_HW_SPI6_MISO; + #endif pins[3] = &MICROPY_HW_SPI6_MOSI; // enable the SPI clock __SPI6_CLK_ENABLE(); @@ -327,7 +338,10 @@ void spi_init(SPI_HandleTypeDef *spi, bool enable_nss_pin) { // init the GPIO lines uint32_t mode = MP_HAL_PIN_MODE_ALT; uint32_t pull = spi->Init.CLKPolarity == SPI_POLARITY_LOW ? MP_HAL_PIN_PULL_DOWN : MP_HAL_PIN_PULL_UP; - for (uint i = (enable_nss_pin && pins[0] ? 0 : 1); i < 4; i++) { + for (uint i = (enable_nss_pin ? 0 : 1); i < 4; i++) { + if (pins[i] == NULL) { + continue; + } mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_SPI, (self - &pyb_spi_obj[0]) + 1); } From 1039c5e6998561c452cfc23a836eec374bef39bd Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 23 Dec 2017 23:04:02 +1100 Subject: [PATCH 225/828] py/parse: Split out rule name from rule struct into separate array. The rule name is only used for debugging, and this patch makes things a bit cleaner by completely separating out the rule name from the rest of the rule data. --- py/parse.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/py/parse.c b/py/parse.c index b80cc8fb1..7fcdf2c43 100644 --- a/py/parse.c +++ b/py/parse.c @@ -61,9 +61,6 @@ typedef struct _rule_t { byte rule_id; byte act; -#ifdef USE_RULE_NAME - const char *rule_name; -#endif uint16_t arg[]; } rule_t; @@ -94,13 +91,8 @@ enum { #define tok(t) (RULE_ARG_TOK | MP_TOKEN_##t) #define rule(r) (RULE_ARG_RULE | RULE_##r) #define opt_rule(r) (RULE_ARG_OPT_RULE | RULE_##r) -#ifdef USE_RULE_NAME -#define DEF_RULE(rule, comp, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, #rule, { __VA_ARGS__ } }; -#define DEF_RULE_NC(rule, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, #rule, { __VA_ARGS__ } }; -#else #define DEF_RULE(rule, comp, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, { __VA_ARGS__ } }; #define DEF_RULE_NC(rule, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, { __VA_ARGS__ } }; -#endif #include "py/grammar.h" #undef or #undef and @@ -130,6 +122,23 @@ STATIC const rule_t *const rules[] = { #undef DEF_RULE_NC }; +#if USE_RULE_NAME +// Define an array of rule names corresponding to each rule +STATIC const char *const rule_name_table[] = { +#define DEF_RULE(rule, comp, kind, ...) #rule, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + "", // RULE_const_object +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) #rule, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +}; +#endif + typedef struct _rule_stack_t { size_t src_line : 8 * sizeof(size_t) - 8; // maximum bits storing source line number size_t rule_id : 8; // this must be large enough to fit largest rule number @@ -313,11 +322,11 @@ void mp_parse_node_print(mp_parse_node_t pn, size_t indent) { #endif } else { size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); -#ifdef USE_RULE_NAME - printf("%s(%u) (n=%u)\n", rules[MP_PARSE_NODE_STRUCT_KIND(pns)]->rule_name, (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); -#else + #if USE_RULE_NAME + printf("%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); + #else printf("rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); -#endif + #endif for (size_t i = 0; i < n; i++) { mp_parse_node_print(pns->nodes[i], indent + 2); } @@ -797,7 +806,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { for (int j = 0; j < parser.rule_stack_top; ++j) { printf(" "); } - printf("%s n=%d i=%d bt=%d\n", rule->rule_name, n, i, backtrack); + printf("%s n=%d i=%d bt=%d\n", rule_name_table[rule->rule_id], n, i, backtrack); */ switch (rule->act & RULE_ACT_KIND_MASK) { From 845511af257cd33f1397c5dd05b1c9198d2be96a Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 23 Dec 2017 23:36:08 +1100 Subject: [PATCH 226/828] py/parse: Break rule data into separate act and arg arrays. Instead of each rule being stored in ROM as a struct with rule_id, act and arg, the act and arg parts are now in separate arrays and the rule_id part is removed because it's not needed. This reduces code size, by roughly one byte per grammar rule, around 150 bytes. --- py/parse.c | 94 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/py/parse.c b/py/parse.c index 7fcdf2c43..bc2afb494 100644 --- a/py/parse.c +++ b/py/parse.c @@ -61,7 +61,7 @@ typedef struct _rule_t { byte rule_id; byte act; - uint16_t arg[]; + const uint16_t *arg; } rule_t; enum { @@ -81,6 +81,8 @@ enum { #undef DEF_RULE_NC }; +// Define an array of actions corresponding to each rule +STATIC const uint8_t rule_act_table[] = { #define or(n) (RULE_ACT_OR | n) #define and(n) (RULE_ACT_AND | n) #define and_ident(n) (RULE_ACT_AND | n | RULE_ACT_ALLOW_IDENT) @@ -88,35 +90,55 @@ enum { #define one_or_more (RULE_ACT_LIST | 2) #define list (RULE_ACT_LIST | 1) #define list_with_end (RULE_ACT_LIST | 3) -#define tok(t) (RULE_ARG_TOK | MP_TOKEN_##t) -#define rule(r) (RULE_ARG_RULE | RULE_##r) -#define opt_rule(r) (RULE_ARG_OPT_RULE | RULE_##r) -#define DEF_RULE(rule, comp, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, { __VA_ARGS__ } }; -#define DEF_RULE_NC(rule, kind, ...) static const rule_t rule_##rule = { RULE_##rule, kind, { __VA_ARGS__ } }; + +#define DEF_RULE(rule, comp, kind, ...) kind, +#define DEF_RULE_NC(rule, kind, ...) #include "py/grammar.h" -#undef or -#undef and -#undef list -#undef list_with_end -#undef tok -#undef rule -#undef opt_rule -#undef one_or_more #undef DEF_RULE #undef DEF_RULE_NC -STATIC const rule_t *const rules[] = { -// define rules with a compile function -#define DEF_RULE(rule, comp, kind, ...) &rule_##rule, + 0, // RULE_const_object + +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) kind, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + +#undef or +#undef and +#undef and_ident +#undef and_blank +#undef one_or_more +#undef list +#undef list_with_end +}; + +// Define the argument data for each rule +#define tok(t) (RULE_ARG_TOK | MP_TOKEN_##t) +#define rule(r) (RULE_ARG_RULE | RULE_##r) +#define opt_rule(r) (RULE_ARG_OPT_RULE | RULE_##r) + +#define DEF_RULE(rule, comp, kind, ...) static const uint16_t const rule_arg_##rule[] = { __VA_ARGS__ }; +#define DEF_RULE_NC(rule, kind, ...) static const uint16_t const rule_arg_##rule[] = { __VA_ARGS__ }; +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + +#undef tok +#undef rule +#undef opt_rule + +// Define an array of pointers to corresponding rule data +STATIC const uint16_t *const rule_arg_table[] = { +#define DEF_RULE(rule, comp, kind, ...) &rule_arg_##rule[0], #define DEF_RULE_NC(rule, kind, ...) #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC NULL, // RULE_const_object - -// define rules without a compile function #define DEF_RULE(rule, comp, kind, ...) -#define DEF_RULE_NC(rule, kind, ...) &rule_##rule, +#define DEF_RULE_NC(rule, kind, ...) &rule_arg_##rule[0], #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC @@ -139,6 +161,10 @@ STATIC const char *const rule_name_table[] = { }; #endif +#if (MICROPY_COMP_CONST_FOLDING && MICROPY_COMP_CONST) || !MICROPY_ENABLE_DOC_STRING +static const rule_t rule_pass_stmt = {RULE_pass_stmt, (RULE_ACT_AND | 1), &rule_arg_pass_stmt[0]}; +#endif + typedef struct _rule_stack_t { size_t src_line : 8 * sizeof(size_t) - 8; // maximum bits storing source line number size_t rule_id : 8; // this must be large enough to fit largest rule number @@ -214,7 +240,7 @@ STATIC void *parser_alloc(parser_t *parser, size_t num_bytes) { return ret; } -STATIC void push_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t arg_i) { +STATIC void push_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t arg_i) { if (parser->rule_stack_top >= parser->rule_stack_alloc) { rule_stack_t *rs = m_renew(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc + MICROPY_ALLOC_PARSE_RULE_INC); parser->rule_stack = rs; @@ -222,19 +248,21 @@ STATIC void push_rule(parser_t *parser, size_t src_line, const rule_t *rule, siz } rule_stack_t *rs = &parser->rule_stack[parser->rule_stack_top++]; rs->src_line = src_line; - rs->rule_id = rule->rule_id; + rs->rule_id = rule_id; rs->arg_i = arg_i; } STATIC void push_rule_from_arg(parser_t *parser, size_t arg) { assert((arg & RULE_ARG_KIND_MASK) == RULE_ARG_RULE || (arg & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE); size_t rule_id = arg & RULE_ARG_ARG_MASK; - push_rule(parser, parser->lexer->tok_line, rules[rule_id], 0); + push_rule(parser, parser->lexer->tok_line, rule_id, 0); } -STATIC void pop_rule(parser_t *parser, const rule_t **rule, size_t *arg_i, size_t *src_line) { +STATIC void pop_rule(parser_t *parser, rule_t *rule, size_t *arg_i, size_t *src_line) { parser->rule_stack_top -= 1; - *rule = rules[parser->rule_stack[parser->rule_stack_top].rule_id]; + rule->rule_id = parser->rule_stack[parser->rule_stack_top].rule_id; + rule->act = rule_act_table[rule->rule_id]; + rule->arg = rule_arg_table[rule->rule_id]; *arg_i = parser->rule_stack[parser->rule_stack_top].arg_i; *src_line = parser->rule_stack[parser->rule_stack_top].src_line; } @@ -656,7 +684,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args if (qstr_str(id)[0] == '_') { pop_result(parser); // pop const(value) pop_result(parser); // pop id - push_result_rule(parser, 0, rules[RULE_pass_stmt], 0); // replace with "pass" + push_result_rule(parser, 0, &rule_pass_stmt, 0); // replace with "pass" return true; } @@ -782,7 +810,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { case MP_PARSE_EVAL_INPUT: top_level_rule = RULE_eval_input; break; default: top_level_rule = RULE_file_input; } - push_rule(&parser, lex->tok_line, rules[top_level_rule], 0); + push_rule(&parser, lex->tok_line, top_level_rule, 0); // parse! @@ -797,7 +825,9 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { break; } - pop_rule(&parser, &rule, &i, &rule_src_line); + rule_t rule_data; + pop_rule(&parser, &rule_data, &i, &rule_src_line); + rule = &rule_data; n = rule->act & RULE_ACT_ARG_MASK; /* @@ -827,7 +857,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } else { assert(kind == RULE_ARG_RULE); if (i + 1 < n) { - push_rule(&parser, rule_src_line, rule, i + 1); // save this or-rule + push_rule(&parser, rule_src_line, rule->rule_id, i + 1); // save this or-rule } push_rule_from_arg(&parser, rule->arg[i]); // push child of or-rule goto next_rule; @@ -879,7 +909,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } } } else { - push_rule(&parser, rule_src_line, rule, i + 1); // save this and-rule + push_rule(&parser, rule_src_line, rule->rule_id, i + 1); // save this and-rule push_rule_from_arg(&parser, rule->arg[i]); // push child of and-rule goto next_rule; } @@ -900,7 +930,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // Pushing the "pass" rule here will overwrite any RULE_const_object // entry that was on the result stack, allowing the GC to reclaim // the memory from the const object when needed. - push_result_rule(&parser, rule_src_line, rules[RULE_pass_stmt], 0); + push_result_rule(&parser, rule_src_line, &rule_pass_stmt, 0); break; } } @@ -1009,7 +1039,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } } else { assert((arg & RULE_ARG_KIND_MASK) == RULE_ARG_RULE); - push_rule(&parser, rule_src_line, rule, i + 1); // save this list-rule + push_rule(&parser, rule_src_line, rule->rule_id, i + 1); // save this list-rule push_rule_from_arg(&parser, arg); // push child of list-rule goto next_rule; } From 815a8cd1ae3a0a451b0b0b687277093f3392cdab Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 23 Dec 2017 23:49:48 +1100 Subject: [PATCH 227/828] py/parse: Pass rule_id to push_result_rule, instead of passing rule_t*. Reduces code size by eliminating quite a few pointer dereferences. --- py/parse.c | 60 +++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/py/parse.c b/py/parse.c index bc2afb494..fffa98953 100644 --- a/py/parse.c +++ b/py/parse.c @@ -161,10 +161,6 @@ STATIC const char *const rule_name_table[] = { }; #endif -#if (MICROPY_COMP_CONST_FOLDING && MICROPY_COMP_CONST) || !MICROPY_ENABLE_DOC_STRING -static const rule_t rule_pass_stmt = {RULE_pass_stmt, (RULE_ACT_AND | 1), &rule_arg_pass_stmt[0]}; -#endif - typedef struct _rule_stack_t { size_t src_line : 8 * sizeof(size_t) - 8; // maximum bits storing source line number size_t rule_id : 8; // this must be large enough to fit largest rule number @@ -491,12 +487,12 @@ STATIC const mp_rom_map_elem_t mp_constants_table[] = { STATIC MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); #endif -STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t num_args); +STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args); #if MICROPY_COMP_CONST_FOLDING -STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t *num_args) { - if (rule->rule_id == RULE_or_test - || rule->rule_id == RULE_and_test) { +STATIC bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *num_args) { + if (rule_id == RULE_or_test + || rule_id == RULE_and_test) { // folding for binary logical ops: or and size_t copy_to = *num_args; for (size_t i = copy_to; i > 0;) { @@ -506,7 +502,7 @@ STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t // always need to keep the last value break; } - if (rule->rule_id == RULE_or_test) { + if (rule_id == RULE_or_test) { if (mp_parse_node_is_const_true(pn)) { // break; @@ -533,7 +529,7 @@ STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t // we did a complete folding if there's only 1 arg left return *num_args == 1; - } else if (rule->rule_id == RULE_not_test_2) { + } else if (rule_id == RULE_not_test_2) { // folding for unary logical op: not mp_parse_node_t pn = peek_result(parser, 0); if (mp_parse_node_is_const_false(pn)) { @@ -551,23 +547,23 @@ STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t return false; } -STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args) { +STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4 // it does not do partial folding, eg 1 + 2 + x -> 3 + x mp_obj_t arg0; - if (rule->rule_id == RULE_expr - || rule->rule_id == RULE_xor_expr - || rule->rule_id == RULE_and_expr) { + if (rule_id == RULE_expr + || rule_id == RULE_xor_expr + || rule_id == RULE_and_expr) { // folding for binary ops: | ^ & mp_parse_node_t pn = peek_result(parser, num_args - 1); if (!mp_parse_node_get_int_maybe(pn, &arg0)) { return false; } mp_binary_op_t op; - if (rule->rule_id == RULE_expr) { + if (rule_id == RULE_expr) { op = MP_BINARY_OP_OR; - } else if (rule->rule_id == RULE_xor_expr) { + } else if (rule_id == RULE_xor_expr) { op = MP_BINARY_OP_XOR; } else { op = MP_BINARY_OP_AND; @@ -580,9 +576,9 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args } arg0 = mp_binary_op(op, arg0, arg1); } - } else if (rule->rule_id == RULE_shift_expr - || rule->rule_id == RULE_arith_expr - || rule->rule_id == RULE_term) { + } else if (rule_id == RULE_shift_expr + || rule_id == RULE_arith_expr + || rule_id == RULE_term) { // folding for binary ops: << >> + - * / % // mp_parse_node_t pn = peek_result(parser, num_args - 1); if (!mp_parse_node_get_int_maybe(pn, &arg0)) { @@ -626,7 +622,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args } arg0 = mp_binary_op(op, arg0, arg1); } - } else if (rule->rule_id == RULE_factor_2) { + } else if (rule_id == RULE_factor_2) { // folding for unary ops: + - ~ mp_parse_node_t pn = peek_result(parser, 0); if (!mp_parse_node_get_int_maybe(pn, &arg0)) { @@ -645,7 +641,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args arg0 = mp_unary_op(op, arg0); #if MICROPY_COMP_CONST - } else if (rule->rule_id == RULE_expr_stmt) { + } else if (rule_id == RULE_expr_stmt) { mp_parse_node_t pn1 = peek_result(parser, 0); if (!MP_PARSE_NODE_IS_NULL(pn1) && !(MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_augassign) @@ -684,7 +680,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args if (qstr_str(id)[0] == '_') { pop_result(parser); // pop const(value) pop_result(parser); // pop id - push_result_rule(parser, 0, &rule_pass_stmt, 0); // replace with "pass" + push_result_rule(parser, 0, RULE_pass_stmt, 0); // replace with "pass" return true; } @@ -700,7 +696,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args #endif #if MICROPY_COMP_MODULE_CONST - } else if (rule->rule_id == RULE_atom_expr_normal) { + } else if (rule_id == RULE_atom_expr_normal) { mp_parse_node_t pn0 = peek_result(parser, 1); mp_parse_node_t pn1 = peek_result(parser, 0); if (!(MP_PARSE_NODE_IS_ID(pn0) @@ -745,9 +741,9 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args } #endif -STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t num_args) { +STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args) { // optimise away parenthesis around an expression if possible - if (rule->rule_id == RULE_atom_paren) { + if (rule_id == RULE_atom_paren) { // there should be just 1 arg for this rule mp_parse_node_t pn = peek_result(parser, 0); if (MP_PARSE_NODE_IS_NULL(pn)) { @@ -761,11 +757,11 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *ru } #if MICROPY_COMP_CONST_FOLDING - if (fold_logical_constants(parser, rule, &num_args)) { + if (fold_logical_constants(parser, rule_id, &num_args)) { // we folded this rule so return straight away return; } - if (fold_constants(parser, rule, num_args)) { + if (fold_constants(parser, rule_id, num_args)) { // we folded this rule so return straight away return; } @@ -773,7 +769,7 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *ru mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * num_args); pn->source_line = src_line; - pn->kind_num_nodes = (rule->rule_id & 0xff) | (num_args << 8); + pn->kind_num_nodes = (rule_id & 0xff) | (num_args << 8); for (size_t i = num_args; i > 0; i--) { pn->nodes[i - 1] = pop_result(parser); } @@ -930,7 +926,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // Pushing the "pass" rule here will overwrite any RULE_const_object // entry that was on the result stack, allowing the GC to reclaim // the memory from the const object when needed. - push_result_rule(&parser, rule_src_line, &rule_pass_stmt, 0); + push_result_rule(&parser, rule_src_line, RULE_pass_stmt, 0); break; } } @@ -976,7 +972,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { i += 1; } - push_result_rule(&parser, rule_src_line, rule, i); + push_result_rule(&parser, rule_src_line, rule->rule_id, i); } break; } @@ -1058,12 +1054,12 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // list matched single item if (had_trailing_sep) { // if there was a trailing separator, make a list of a single item - push_result_rule(&parser, rule_src_line, rule, i); + push_result_rule(&parser, rule_src_line, rule->rule_id, i); } else { // just leave single item on stack (ie don't wrap in a list) } } else { - push_result_rule(&parser, rule_src_line, rule, i); + push_result_rule(&parser, rule_src_line, rule->rule_id, i); } break; } From 66d8885d85bb591810b25e763c0921372ab74ef4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 23 Dec 2017 23:53:01 +1100 Subject: [PATCH 228/828] py/parse: Pass rule_id to push_result_token, instead of passing rule_t*. --- py/parse.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/py/parse.c b/py/parse.c index fffa98953..6244af432 100644 --- a/py/parse.c +++ b/py/parse.c @@ -414,7 +414,7 @@ STATIC mp_parse_node_t mp_parse_node_new_small_int_checked(parser_t *parser, mp_ return mp_parse_node_new_small_int(val); } -STATIC void push_result_token(parser_t *parser, const rule_t *rule) { +STATIC void push_result_token(parser_t *parser, uint8_t rule_id) { mp_parse_node_t pn; mp_lexer_t *lex = parser->lexer; if (lex->tok_kind == MP_TOKEN_NAME) { @@ -422,7 +422,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) { #if MICROPY_COMP_CONST // if name is a standalone identifier, look it up in the table of dynamic constants mp_map_elem_t *elem; - if (rule->rule_id == RULE_atom + if (rule_id == RULE_atom && (elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP)) != NULL) { if (MP_OBJ_IS_SMALL_INT(elem->value)) { pn = mp_parse_node_new_small_int_checked(parser, elem->value); @@ -433,7 +433,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) { pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id); } #else - (void)rule; + (void)rule_id; pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id); #endif } else if (lex->tok_kind == MP_TOKEN_INTEGER) { @@ -846,7 +846,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { uint16_t kind = rule->arg[i] & RULE_ARG_KIND_MASK; if (kind == RULE_ARG_TOK) { if (lex->tok_kind == (rule->arg[i] & RULE_ARG_ARG_MASK)) { - push_result_token(&parser, rule); + push_result_token(&parser, rule->rule_id); mp_lexer_to_next(lex); goto next_rule; } @@ -890,7 +890,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { if (lex->tok_kind == tok_kind) { // matched token if (tok_kind == MP_TOKEN_NAME) { - push_result_token(&parser, rule); + push_result_token(&parser, rule->rule_id); } mp_lexer_to_next(lex); } else { @@ -1022,7 +1022,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { if (i & 1 & n) { // separators which are tokens are not pushed to result stack } else { - push_result_token(&parser, rule); + push_result_token(&parser, rule->rule_id); } mp_lexer_to_next(lex); // got element of list, so continue parsing list From c2c92ceefc86d7e5cdc358c4ea3226844391ae44 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 24 Dec 2017 00:01:02 +1100 Subject: [PATCH 229/828] py/parse: Remove rule_t struct because it's no longer needed. --- py/parse.c | 79 +++++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/py/parse.c b/py/parse.c index 6244af432..1af0e9c5f 100644 --- a/py/parse.c +++ b/py/parse.c @@ -58,12 +58,6 @@ // (un)comment to use rule names; for debugging //#define USE_RULE_NAME (1) -typedef struct _rule_t { - byte rule_id; - byte act; - const uint16_t *arg; -} rule_t; - enum { // define rules with a compile function #define DEF_RULE(rule, comp, kind, ...) RULE_##rule, @@ -254,13 +248,12 @@ STATIC void push_rule_from_arg(parser_t *parser, size_t arg) { push_rule(parser, parser->lexer->tok_line, rule_id, 0); } -STATIC void pop_rule(parser_t *parser, rule_t *rule, size_t *arg_i, size_t *src_line) { +STATIC uint8_t pop_rule(parser_t *parser, size_t *arg_i, size_t *src_line) { parser->rule_stack_top -= 1; - rule->rule_id = parser->rule_stack[parser->rule_stack_top].rule_id; - rule->act = rule_act_table[rule->rule_id]; - rule->arg = rule_arg_table[rule->rule_id]; + uint8_t rule_id = parser->rule_stack[parser->rule_stack_top].rule_id; *arg_i = parser->rule_stack[parser->rule_stack_top].arg_i; *src_line = parser->rule_stack[parser->rule_stack_top].src_line; + return rule_id; } bool mp_parse_node_is_const_false(mp_parse_node_t pn) { @@ -810,10 +803,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // parse! - size_t n, i; // state for the current rule - size_t rule_src_line; // source line for the first token matched by the current rule bool backtrack = false; - const rule_t *rule = NULL; for (;;) { next_rule: @@ -821,10 +811,13 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { break; } - rule_t rule_data; - pop_rule(&parser, &rule_data, &i, &rule_src_line); - rule = &rule_data; - n = rule->act & RULE_ACT_ARG_MASK; + // Pop the next rule to process it + size_t i; // state for the current rule + size_t rule_src_line; // source line for the first token matched by the current rule + uint8_t rule_id = pop_rule(&parser, &i, &rule_src_line); + uint8_t rule_act = rule_act_table[rule_id]; + const uint16_t *rule_arg = rule_arg_table[rule_id]; + size_t n = rule_act & RULE_ACT_ARG_MASK; /* // debugging @@ -832,10 +825,10 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { for (int j = 0; j < parser.rule_stack_top; ++j) { printf(" "); } - printf("%s n=%d i=%d bt=%d\n", rule_name_table[rule->rule_id], n, i, backtrack); + printf("%s n=%d i=%d bt=%d\n", rule_name_table[rule_id], n, i, backtrack); */ - switch (rule->act & RULE_ACT_KIND_MASK) { + switch (rule_act & RULE_ACT_KIND_MASK) { case RULE_ACT_OR: if (i > 0 && !backtrack) { goto next_rule; @@ -843,19 +836,19 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { backtrack = false; } for (; i < n; ++i) { - uint16_t kind = rule->arg[i] & RULE_ARG_KIND_MASK; + uint16_t kind = rule_arg[i] & RULE_ARG_KIND_MASK; if (kind == RULE_ARG_TOK) { - if (lex->tok_kind == (rule->arg[i] & RULE_ARG_ARG_MASK)) { - push_result_token(&parser, rule->rule_id); + if (lex->tok_kind == (rule_arg[i] & RULE_ARG_ARG_MASK)) { + push_result_token(&parser, rule_id); mp_lexer_to_next(lex); goto next_rule; } } else { assert(kind == RULE_ARG_RULE); if (i + 1 < n) { - push_rule(&parser, rule_src_line, rule->rule_id, i + 1); // save this or-rule + push_rule(&parser, rule_src_line, rule_id, i + 1); // save this or-rule } - push_rule_from_arg(&parser, rule->arg[i]); // push child of or-rule + push_rule_from_arg(&parser, rule_arg[i]); // push child of or-rule goto next_rule; } } @@ -867,7 +860,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // failed, backtrack if we can, else syntax error if (backtrack) { assert(i > 0); - if ((rule->arg[i - 1] & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE) { + if ((rule_arg[i - 1] & RULE_ARG_KIND_MASK) == RULE_ARG_OPT_RULE) { // an optional rule that failed, so continue with next arg push_result_node(&parser, MP_PARSE_NODE_NULL); backtrack = false; @@ -884,13 +877,13 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // progress through the rule for (; i < n; ++i) { - if ((rule->arg[i] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { + if ((rule_arg[i] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { // need to match a token - mp_token_kind_t tok_kind = rule->arg[i] & RULE_ARG_ARG_MASK; + mp_token_kind_t tok_kind = rule_arg[i] & RULE_ARG_ARG_MASK; if (lex->tok_kind == tok_kind) { // matched token if (tok_kind == MP_TOKEN_NAME) { - push_result_token(&parser, rule->rule_id); + push_result_token(&parser, rule_id); } mp_lexer_to_next(lex); } else { @@ -905,8 +898,8 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } } } else { - push_rule(&parser, rule_src_line, rule->rule_id, i + 1); // save this and-rule - push_rule_from_arg(&parser, rule->arg[i]); // push child of and-rule + push_rule(&parser, rule_src_line, rule_id, i + 1); // save this and-rule + push_rule_from_arg(&parser, rule_arg[i]); // push child of and-rule goto next_rule; } } @@ -917,7 +910,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { #if !MICROPY_ENABLE_DOC_STRING // this code discards lonely statements, such as doc strings - if (input_kind != MP_PARSE_SINGLE_INPUT && rule->rule_id == RULE_expr_stmt && peek_result(&parser, 0) == MP_PARSE_NODE_NULL) { + if (input_kind != MP_PARSE_SINGLE_INPUT && rule_id == RULE_expr_stmt && peek_result(&parser, 0) == MP_PARSE_NODE_NULL) { mp_parse_node_t p = peek_result(&parser, 1); if ((MP_PARSE_NODE_IS_LEAF(p) && !MP_PARSE_NODE_IS_ID(p)) || MP_PARSE_NODE_IS_STRUCT_KIND(p, RULE_const_object)) { @@ -937,8 +930,8 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { size_t num_not_nil = 0; for (size_t x = n; x > 0;) { --x; - if ((rule->arg[x] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { - mp_token_kind_t tok_kind = rule->arg[x] & RULE_ARG_ARG_MASK; + if ((rule_arg[x] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { + mp_token_kind_t tok_kind = rule_arg[x] & RULE_ARG_ARG_MASK; if (tok_kind == MP_TOKEN_NAME) { // only tokens which were names are pushed to stack i += 1; @@ -953,7 +946,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } } - if (num_not_nil == 1 && (rule->act & RULE_ACT_ALLOW_IDENT)) { + if (num_not_nil == 1 && (rule_act & RULE_ACT_ALLOW_IDENT)) { // this rule has only 1 argument and should not be emitted mp_parse_node_t pn = MP_PARSE_NODE_NULL; for (size_t x = 0; x < i; ++x) { @@ -966,19 +959,19 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } else { // this rule must be emitted - if (rule->act & RULE_ACT_ADD_BLANK) { + if (rule_act & RULE_ACT_ADD_BLANK) { // and add an extra blank node at the end (used by the compiler to store data) push_result_node(&parser, MP_PARSE_NODE_NULL); i += 1; } - push_result_rule(&parser, rule_src_line, rule->rule_id, i); + push_result_rule(&parser, rule_src_line, rule_id, i); } break; } default: { - assert((rule->act & RULE_ACT_KIND_MASK) == RULE_ACT_LIST); + assert((rule_act & RULE_ACT_KIND_MASK) == RULE_ACT_LIST); // n=2 is: item item* // n=1 is: item (sep item)* @@ -1016,13 +1009,13 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } } else { for (;;) { - size_t arg = rule->arg[i & 1 & n]; + size_t arg = rule_arg[i & 1 & n]; if ((arg & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { if (lex->tok_kind == (arg & RULE_ARG_ARG_MASK)) { if (i & 1 & n) { // separators which are tokens are not pushed to result stack } else { - push_result_token(&parser, rule->rule_id); + push_result_token(&parser, rule_id); } mp_lexer_to_next(lex); // got element of list, so continue parsing list @@ -1035,7 +1028,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { } } else { assert((arg & RULE_ARG_KIND_MASK) == RULE_ARG_RULE); - push_rule(&parser, rule_src_line, rule->rule_id, i + 1); // save this list-rule + push_rule(&parser, rule_src_line, rule_id, i + 1); // save this list-rule push_rule_from_arg(&parser, arg); // push child of list-rule goto next_rule; } @@ -1045,7 +1038,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // compute number of elements in list, result in i i -= 1; - if ((n & 1) && (rule->arg[1] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { + if ((n & 1) && (rule_arg[1] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { // don't count separators when they are tokens i = (i + 1) / 2; } @@ -1054,12 +1047,12 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { // list matched single item if (had_trailing_sep) { // if there was a trailing separator, make a list of a single item - push_result_rule(&parser, rule_src_line, rule->rule_id, i); + push_result_rule(&parser, rule_src_line, rule_id, i); } else { // just leave single item on stack (ie don't wrap in a list) } } else { - push_result_rule(&parser, rule_src_line, rule->rule_id, i); + push_result_rule(&parser, rule_src_line, rule_id, i); } break; } From 0016a45368d8e7272d1c1f891994d6de4effcb40 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 26 Dec 2017 13:39:02 +1100 Subject: [PATCH 230/828] py/parse: Compress rule pointer table to table of offsets. This is the sixth and final patch in a series of patches to the parser that aims to reduce code size by compressing the data corresponding to the rules of the grammar. Prior to this set of patches the rules were stored as rule_t structs with rule_id, act and arg members. And then there was a big table of pointers which allowed to lookup the address of a rule_t struct given the id of that rule. The changes that have been made are: - Breaking up of the rule_t struct into individual components, with each component in a separate array. - Removal of the rule_id part of the struct because it's not needed. - Put all the rule arg data in a big array. - Change the table of pointers to rules to a table of offsets within the array of rule arg data. The last point is what is done in this patch here and brings about the biggest decreases in code size, because an array of pointers is now an array of bytes. Code size changes for the six patches combined is: bare-arm: -644 minimal x86: -1856 unix x64: -5408 unix nanbox: -2080 stm32: -720 esp8266: -812 cc3200: -712 For the change in parser performance: it was measured on pyboard that these six patches combined gave an increase in script parse time of about 0.4%. This is due to the slightly more complicated way of looking up the data for a rule (since the 9th bit of the offset into the rule arg data table is calculated with an if statement). This is an acceptable increase in parse time considering that parsing is only done once per script (if compiled on the target). --- py/parse.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/py/parse.c b/py/parse.c index 1af0e9c5f..8e0793185 100644 --- a/py/parse.c +++ b/py/parse.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2013-2017 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 @@ -108,13 +108,20 @@ STATIC const uint8_t rule_act_table[] = { #undef list_with_end }; -// Define the argument data for each rule +// Define the argument data for each rule, as a combined array +STATIC const uint16_t rule_arg_combined_table[] = { #define tok(t) (RULE_ARG_TOK | MP_TOKEN_##t) #define rule(r) (RULE_ARG_RULE | RULE_##r) #define opt_rule(r) (RULE_ARG_OPT_RULE | RULE_##r) -#define DEF_RULE(rule, comp, kind, ...) static const uint16_t const rule_arg_##rule[] = { __VA_ARGS__ }; -#define DEF_RULE_NC(rule, kind, ...) static const uint16_t const rule_arg_##rule[] = { __VA_ARGS__ }; +#define DEF_RULE(rule, comp, kind, ...) __VA_ARGS__, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) __VA_ARGS__, #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC @@ -122,22 +129,60 @@ STATIC const uint8_t rule_act_table[] = { #undef tok #undef rule #undef opt_rule +}; -// Define an array of pointers to corresponding rule data -STATIC const uint16_t *const rule_arg_table[] = { -#define DEF_RULE(rule, comp, kind, ...) &rule_arg_##rule[0], +// Macro to create a list of N-1 identifiers where N is the number of variable arguments to the macro +#define RULE_PADDING(rule, ...) RULE_PADDING2(rule, __VA_ARGS__, RULE_PADDING_IDS(rule)) +#define RULE_PADDING2(rule, ...) RULE_PADDING3(rule, __VA_ARGS__) +#define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) __VA_ARGS__ +#define RULE_PADDING_IDS(r) PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, + +// Use an enum to create constants that specify where in rule_arg_combined_table a given rule starts +enum { +#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET_##rule, RULE_PADDING(rule, __VA_ARGS__) #define DEF_RULE_NC(rule, kind, ...) #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC - NULL, // RULE_const_object #define DEF_RULE(rule, comp, kind, ...) -#define DEF_RULE_NC(rule, kind, ...) &rule_arg_##rule[0], +#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET_##rule, RULE_PADDING(rule, __VA_ARGS__) #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC }; +// Use the above enum values to create a table of offsets for each rule's arg +// data, which indexes rule_arg_combined_table. The offsets require 9 bits of +// storage but only the lower 8 bits are stored here. The 9th bit is computed +// in get_rule_arg using the FIRST_RULE_WITH_OFFSET_ABOVE_255 constant. +STATIC const uint8_t rule_arg_offset_table[] = { +#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET_##rule & 0xff, +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC + 0, // RULE_const_object +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET_##rule & 0xff, +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +}; + +// Define a constant that's used to determine the 9th bit of the values in rule_arg_offset_table +static const size_t FIRST_RULE_WITH_OFFSET_ABOVE_255 = +#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET_##rule >= 0x100 ? RULE_##rule : +#define DEF_RULE_NC(rule, kind, ...) +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +#define DEF_RULE(rule, comp, kind, ...) +#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET_##rule >= 0x100 ? RULE_##rule : +#include "py/grammar.h" +#undef DEF_RULE +#undef DEF_RULE_NC +0; + #if USE_RULE_NAME // Define an array of rule names corresponding to each rule STATIC const char *const rule_name_table[] = { @@ -189,6 +234,14 @@ typedef struct _parser_t { #endif } parser_t; +STATIC const uint16_t *get_rule_arg(uint8_t r_id) { + size_t off = rule_arg_offset_table[r_id]; + if (r_id >= FIRST_RULE_WITH_OFFSET_ABOVE_255) { + off |= 0x100; + } + return &rule_arg_combined_table[off]; +} + STATIC void *parser_alloc(parser_t *parser, size_t num_bytes) { // use a custom memory allocator to store parse nodes sequentially in large chunks @@ -816,7 +869,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { size_t rule_src_line; // source line for the first token matched by the current rule uint8_t rule_id = pop_rule(&parser, &i, &rule_src_line); uint8_t rule_act = rule_act_table[rule_id]; - const uint16_t *rule_arg = rule_arg_table[rule_id]; + const uint16_t *rule_arg = get_rule_arg(rule_id); size_t n = rule_act & RULE_ACT_ARG_MASK; /* From d3fbfa491f46ec7e3f69d12e037cb3da7b3ae984 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 26 Dec 2017 13:39:26 +1100 Subject: [PATCH 231/828] py/parse: Update debugging code to compile on 64-bit arch. --- py/parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/py/parse.c b/py/parse.c index 8e0793185..238c94695 100644 --- a/py/parse.c +++ b/py/parse.c @@ -872,14 +872,14 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { const uint16_t *rule_arg = get_rule_arg(rule_id); size_t n = rule_act & RULE_ACT_ARG_MASK; - /* + #if 0 // debugging - printf("depth=%d ", parser.rule_stack_top); + printf("depth=" UINT_FMT " ", parser.rule_stack_top); for (int j = 0; j < parser.rule_stack_top; ++j) { printf(" "); } - printf("%s n=%d i=%d bt=%d\n", rule_name_table[rule_id], n, i, backtrack); - */ + printf("%s n=" UINT_FMT " i=" UINT_FMT " bt=%d\n", rule_name_table[rule_id], n, i, backtrack); + #endif switch (rule_act & RULE_ACT_KIND_MASK) { case RULE_ACT_OR: From c7cb1dfcb9f89eb0a2d2a90a5e496e919c8a0b94 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 29 Dec 2017 00:53:57 +1100 Subject: [PATCH 232/828] py/parse: Fix macro evaluation by avoiding empty __VA_ARGS__. Empty __VA_ARGS__ are not allowed in the C preprocessor so adjust the rule arg offset calculation to not use them. Also, some compilers (eg MSVC) require an extra layer of macro expansion. --- py/parse.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/py/parse.c b/py/parse.c index 238c94695..8c1286492 100644 --- a/py/parse.c +++ b/py/parse.c @@ -131,39 +131,46 @@ STATIC const uint16_t rule_arg_combined_table[] = { #undef opt_rule }; -// Macro to create a list of N-1 identifiers where N is the number of variable arguments to the macro +// Macro to create a list of N identifiers where N is the number of variable arguments to the macro +#define RULE_EXPAND(x) x #define RULE_PADDING(rule, ...) RULE_PADDING2(rule, __VA_ARGS__, RULE_PADDING_IDS(rule)) -#define RULE_PADDING2(rule, ...) RULE_PADDING3(rule, __VA_ARGS__) -#define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) __VA_ARGS__ +#define RULE_PADDING2(rule, ...) RULE_EXPAND(RULE_PADDING3(rule, __VA_ARGS__)) +#define RULE_PADDING3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) __VA_ARGS__ #define RULE_PADDING_IDS(r) PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, -// Use an enum to create constants that specify where in rule_arg_combined_table a given rule starts +// Use an enum to create constants specifying how much room a rule takes in rule_arg_combined_table enum { -#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET_##rule, RULE_PADDING(rule, __VA_ARGS__) +#define DEF_RULE(rule, comp, kind, ...) RULE_PADDING(rule, __VA_ARGS__) #define DEF_RULE_NC(rule, kind, ...) #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC #define DEF_RULE(rule, comp, kind, ...) -#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET_##rule, RULE_PADDING(rule, __VA_ARGS__) +#define DEF_RULE_NC(rule, kind, ...) RULE_PADDING(rule, __VA_ARGS__) #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC }; +// Macro to compute the start of a rule in rule_arg_combined_table +#define RULE_ARG_OFFSET(rule, ...) RULE_ARG_OFFSET2(rule, __VA_ARGS__, RULE_ARG_OFFSET_IDS(rule)) +#define RULE_ARG_OFFSET2(rule, ...) RULE_EXPAND(RULE_ARG_OFFSET3(rule, __VA_ARGS__)) +#define RULE_ARG_OFFSET3(rule, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) _13 +#define RULE_ARG_OFFSET_IDS(r) PAD12_##r, PAD11_##r, PAD10_##r, PAD9_##r, PAD8_##r, PAD7_##r, PAD6_##r, PAD5_##r, PAD4_##r, PAD3_##r, PAD2_##r, PAD1_##r, PAD0_##r, + // Use the above enum values to create a table of offsets for each rule's arg // data, which indexes rule_arg_combined_table. The offsets require 9 bits of // storage but only the lower 8 bits are stored here. The 9th bit is computed // in get_rule_arg using the FIRST_RULE_WITH_OFFSET_ABOVE_255 constant. STATIC const uint8_t rule_arg_offset_table[] = { -#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET_##rule & 0xff, +#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) & 0xff, #define DEF_RULE_NC(rule, kind, ...) #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC 0, // RULE_const_object #define DEF_RULE(rule, comp, kind, ...) -#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET_##rule & 0xff, +#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) & 0xff, #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC @@ -171,13 +178,13 @@ STATIC const uint8_t rule_arg_offset_table[] = { // Define a constant that's used to determine the 9th bit of the values in rule_arg_offset_table static const size_t FIRST_RULE_WITH_OFFSET_ABOVE_255 = -#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET_##rule >= 0x100 ? RULE_##rule : +#define DEF_RULE(rule, comp, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) >= 0x100 ? RULE_##rule : #define DEF_RULE_NC(rule, kind, ...) #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC #define DEF_RULE(rule, comp, kind, ...) -#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET_##rule >= 0x100 ? RULE_##rule : +#define DEF_RULE_NC(rule, kind, ...) RULE_ARG_OFFSET(rule, __VA_ARGS__) >= 0x100 ? RULE_##rule : #include "py/grammar.h" #undef DEF_RULE #undef DEF_RULE_NC From 9766fddcdc29cb2b5a8657389b870703580a6e57 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 Dec 2017 14:02:36 +1100 Subject: [PATCH 233/828] py/mpz: Simplify handling of borrow and quo adjustment in mpn_div. The motivation behind this patch is to remove unreachable code in mpn_div. This unreachable code was added some time ago in 9a21d2e070c9ee0ef2c003f3a668e635c6ae4401, when a loop in mpn_div was copied and adjusted to work when mpz_dig_t was exactly half of the size of mpz_dbl_dig_t (a common case). The loop was copied correctly but it wasn't noticed at the time that the final part of the calculation of num-quo*den could be optimised, and hence unreachable code was left for a case that never occurred. The observation for the optimisation is that the initial value of quo in mpn_div is either exact or too large (never too small), and therefore the subtraction of quo*den from num may subtract exactly enough or too much (but never too little). Using this observation the part of the algorithm that handles the borrow value can be simplified, and most importantly this eliminates the unreachable code. The new code has been tested with DIG_SIZE=3 and DIG_SIZE=4 by dividing all possible combinations of non-negative integers with between 0 and 3 (inclusive) mpz digits. --- py/mpz.c | 114 +++++++++++++++++++++---------------------------------- py/mpz.h | 4 ++ 2 files changed, 48 insertions(+), 70 deletions(-) diff --git a/py/mpz.c b/py/mpz.c index 018a5454f..78dee3132 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -537,83 +537,57 @@ STATIC void mpn_div(mpz_dig_t *num_dig, size_t *num_len, const mpz_dig_t *den_di // not to overflow the borrow variable. And the shifting of // borrow needs some special logic (it's a shift right with // round up). - - if (DIG_SIZE < 8 * sizeof(mpz_dbl_dig_t) / 2) { - const mpz_dig_t *d = den_dig; - mpz_dbl_dig_t d_norm = 0; - mpz_dbl_dig_signed_t borrow = 0; - - for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { - d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); - borrow += (mpz_dbl_dig_t)*n - (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK); // will overflow if DIG_SIZE >= 8*sizeof(mpz_dbl_dig_t)/2 - *n = borrow & DIG_MASK; - borrow >>= DIG_SIZE; - } - borrow += *num_dig; // will overflow if DIG_SIZE >= 8*sizeof(mpz_dbl_dig_t)/2 - *num_dig = borrow & DIG_MASK; - borrow >>= DIG_SIZE; - - // adjust quotient if it is too big - for (; borrow != 0; --quo) { - d = den_dig; - d_norm = 0; - mpz_dbl_dig_t carry = 0; - for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { - d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); - carry += (mpz_dbl_dig_t)*n + (d_norm & DIG_MASK); - *n = carry & DIG_MASK; - carry >>= DIG_SIZE; - } - carry += *num_dig; - *num_dig = carry & DIG_MASK; - carry >>= DIG_SIZE; - - borrow += carry; - } - } else { // DIG_SIZE == 8 * sizeof(mpz_dbl_dig_t) / 2 - const mpz_dig_t *d = den_dig; - mpz_dbl_dig_t d_norm = 0; - mpz_dbl_dig_t borrow = 0; - - for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { - d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); - mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK); - if (x >= *n || *n - x <= borrow) { - borrow += (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)*n; - *n = (-borrow) & DIG_MASK; - borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up - } else { - *n = ((mpz_dbl_dig_t)*n - (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)borrow) & DIG_MASK; - borrow = 0; - } - } - if (borrow >= *num_dig) { - borrow -= (mpz_dbl_dig_t)*num_dig; - *num_dig = (-borrow) & DIG_MASK; + // + const mpz_dig_t *d = den_dig; + mpz_dbl_dig_t d_norm = 0; + mpz_dbl_dig_t borrow = 0; + for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { + d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); + mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK); + #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2 + borrow += (mpz_dbl_dig_t)*n - x; // will overflow if DIG_SIZE >= MPZ_DBL_DIG_SIZE/2 + *n = borrow & DIG_MASK; + borrow = (mpz_dbl_dig_signed_t)borrow >> DIG_SIZE; + #else // DIG_SIZE == MPZ_DBL_DIG_SIZE / 2 + if (x >= *n || *n - x <= borrow) { + borrow += x - (mpz_dbl_dig_t)*n; + *n = (-borrow) & DIG_MASK; borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up } else { - *num_dig = (*num_dig - borrow) & DIG_MASK; + *n = ((mpz_dbl_dig_t)*n - x - borrow) & DIG_MASK; borrow = 0; } + #endif + } - // adjust quotient if it is too big - for (; borrow != 0; --quo) { - d = den_dig; - d_norm = 0; - mpz_dbl_dig_t carry = 0; - for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { - d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); - carry += (mpz_dbl_dig_t)*n + (d_norm & DIG_MASK); - *n = carry & DIG_MASK; - carry >>= DIG_SIZE; - } - carry += (mpz_dbl_dig_t)*num_dig; - *num_dig = carry & DIG_MASK; + #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2 + // Borrow was negative in the above for-loop, make it positive for next if-block. + borrow = -borrow; + #endif + + // At this point we have either: + // + // 1. quo was the correct value and the most-sig-digit of num is exactly + // cancelled by borrow (borrow == *num_dig). In this case there is + // nothing more to do. + // + // 2. quo was too large, we subtracted too many den from num, and the + // most-sig-digit of num is 1 less than borrow (borrow == *num_dig + 1). + // In this case we must reduce quo and add back den to num until the + // carry from this operation cancels out the borrow. + // + borrow -= *num_dig; + for (; borrow != 0; --quo) { + d = den_dig; + d_norm = 0; + mpz_dbl_dig_t carry = 0; + for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { + d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); + carry += (mpz_dbl_dig_t)*n + (d_norm & DIG_MASK); + *n = carry & DIG_MASK; carry >>= DIG_SIZE; - - //assert(borrow >= carry); // enable this to check the logic - borrow -= carry; } + borrow -= carry; } // store this digit of the quotient diff --git a/py/mpz.h b/py/mpz.h index e2d0c30aa..3c36cac66 100644 --- a/py/mpz.h +++ b/py/mpz.h @@ -55,18 +55,22 @@ #endif #if MPZ_DIG_SIZE > 16 +#define MPZ_DBL_DIG_SIZE (64) typedef uint32_t mpz_dig_t; typedef uint64_t mpz_dbl_dig_t; typedef int64_t mpz_dbl_dig_signed_t; #elif MPZ_DIG_SIZE > 8 +#define MPZ_DBL_DIG_SIZE (32) typedef uint16_t mpz_dig_t; typedef uint32_t mpz_dbl_dig_t; typedef int32_t mpz_dbl_dig_signed_t; #elif MPZ_DIG_SIZE > 4 +#define MPZ_DBL_DIG_SIZE (16) typedef uint8_t mpz_dig_t; typedef uint16_t mpz_dbl_dig_t; typedef int16_t mpz_dbl_dig_signed_t; #else +#define MPZ_DBL_DIG_SIZE (8) typedef uint8_t mpz_dig_t; typedef uint8_t mpz_dbl_dig_t; typedef int8_t mpz_dbl_dig_signed_t; From e78427443094163d1839387a2470fd22612ca8d1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 28 Dec 2017 14:14:06 +1100 Subject: [PATCH 234/828] py/mpz: In mpz_as_str_inpl, convert always-false checks to assertions. There are two checks that are always false so can be converted to (negated) assertions to save code space and execution time. They are: 1. The check of the str parameter, which is required to be non-NULL as per the original comment that it has enough space in it as calculated by mp_int_format_size. And for all uses of this function str is indeed non-NULL. 2. The check of the base parameter, which is already required to be between 2 and 16 (inclusive) via the assertion in mp_int_format_size. --- py/mpz.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/py/mpz.c b/py/mpz.c index 78dee3132..380e8968e 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1647,16 +1647,12 @@ char *mpz_as_str(const mpz_t *i, unsigned int base) { } #endif -// assumes enough space as calculated by mp_int_format_size +// assumes enough space in str as calculated by mp_int_format_size +// base must be between 2 and 32 inclusive // returns length of string, not including null byte size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, char base_char, char comma, char *str) { - if (str == NULL) { - return 0; - } - if (base < 2 || base > 32) { - str[0] = 0; - return 0; - } + assert(str != NULL); + assert(2 <= base && base <= 32); size_t ilen = i->len; From 6fc58db5d8ea52e10b54b08e82fc2351d9f5caf0 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 14 Dec 2017 10:58:49 +0100 Subject: [PATCH 235/828] windows/mpconfigport: Provide off_t definition for MSVC port For MSVC off_t is defined in sys/types.h but according to the comment earlier in mpconfigport.h this cannot be included directly. So just make off_t the same as mp_off_t. This fixes the build for MSVC with MICROPY_STREAMS_POSIX_API enabled because stream.h uses off_t. --- ports/windows/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index abad35282..e41f2ebbe 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -232,6 +232,7 @@ typedef __int64 ssize_t; #define SSIZE_MAX _I32_MAX typedef int ssize_t; #endif +typedef mp_off_t off_t; // Put static/global variables in sections with a known name From 8041de59fed09e3185aaf01bb2faf1f733747dc8 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 14 Dec 2017 11:09:59 +0100 Subject: [PATCH 236/828] windows/mpconfigport: Enable some features, including the Python stack Add some features which are already enabled in the unix port and default to using the Python stack for scoped allocations: this can be more performant in cases the heap is heavily used because for example the memory needed for storing *args and **kwargs doesn't require scanning the heap to find a free block. --- ports/windows/mpconfigport.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index e41f2ebbe..1ae20fb04 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -41,6 +41,7 @@ #define MICROPY_COMP_RETURN_IF_EXPR (1) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_ENABLE_PYSTACK (1) #define MICROPY_STACK_CHECK (1) #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) #define MICROPY_MEM_STATS (1) @@ -56,6 +57,7 @@ #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_STREAMS_NON_BLOCK (1) +#define MICROPY_STREAMS_POSIX_API (1) #define MICROPY_OPT_COMPUTED_GOTO (0) #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (1) #define MICROPY_CAN_OVERRIDE_BUILTINS (1) @@ -73,6 +75,7 @@ #define MICROPY_PY_BUILTINS_POW3 (1) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) #define MICROPY_PY_SYS_EXIT (1) @@ -109,9 +112,12 @@ #define MICROPY_MACHINE_MEM_GET_WRITE_ADDR mod_machine_mem_get_addr #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) +#define MICROPY_ERROR_PRINTER (&mp_stderr_print) #define MICROPY_WARNINGS (1) #define MICROPY_PY_STR_BYTES_CMP_WARN (1) +extern const struct _mp_print_t mp_stderr_print; + #ifdef _MSC_VER #define MICROPY_GCREGS_SETJMP (1) #endif From b184b6ae5334b87472897d0034a7388acafe41cc Mon Sep 17 00:00:00 2001 From: stijn Date: Tue, 26 Dec 2017 10:52:09 +0100 Subject: [PATCH 237/828] py/nlr: Fix nlr functions for 64bit ports built with gcc on Windows The number of registers used should be 10, not 12, to match the assembly code in nlrx64.c. With this change the 64bit mingw builds don't need to use the setjmp implementation, and this fixes miscellaneous crashes and assertion failures as reported in #1751 for instance. To avoid mistakes in the future where something gcc-related for Windows only gets fixed for one particular compiler/environment combination, make use of a MICROPY_NLR_OS_WINDOWS macro. To make sure everything nlr-related is now ok when built with gcc this has been verified with: - unix port built with gcc on Cygwin (i686-pc-cygwin-gcc and x86_64-pc-cygwin-gcc, version 6.4.0) - windows port built with mingw-w64's gcc from Cygwin (i686-w64-mingw32-gcc and x86_64-w64-mingw32-gcc, version 6.4.0) and MSYS2 (like the ones on Cygwin but version 7.2.0) --- ports/windows/Makefile | 4 ---- py/nlr.c | 2 +- py/nlr.h | 10 ++++++++-- py/nlrx64.c | 10 ++-------- py/nlrx86.c | 8 +------- 5 files changed, 12 insertions(+), 22 deletions(-) diff --git a/ports/windows/Makefile b/ports/windows/Makefile index b6433300f..725cb686e 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -50,10 +50,6 @@ CFLAGS_MOD += -DMICROPY_USE_READLINE=2 LDFLAGS_MOD += -lreadline endif -ifeq ($(CROSS_COMPILE),x86_64-w64-mingw32-) -CFLAGS_MOD += -DMICROPY_NLR_SETJMP=1 -endif - LIB += -lws2_32 # List of sources for qstr extraction diff --git a/py/nlr.c b/py/nlr.c index 52d56afb8..7114d4997 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -28,7 +28,7 @@ #if !MICROPY_NLR_SETJMP // When not using setjmp, nlr_push_tail is called from inline asm so needs special c -#if MICROPY_NLR_X86 && (defined(_WIN32) || defined(__CYGWIN__)) +#if MICROPY_NLR_X86 && MICROPY_NLR_OS_WINDOWS // On these 32-bit platforms make sure nlr_push_tail doesn't have a leading undersco unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); #else diff --git a/py/nlr.h b/py/nlr.h index e4dfa6896..90595a12d 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -36,13 +36,19 @@ // If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch #if !MICROPY_NLR_SETJMP +// A lot of nlr-related things need different treatment on Windows +#if defined(_WIN32) || defined(__CYGWIN__) +#define MICROPY_NLR_OS_WINDOWS 1 +#else +#define MICROPY_NLR_OS_WINDOWS 0 +#endif #if defined(__i386__) #define MICROPY_NLR_X86 (1) #define MICROPY_NLR_NUM_REGS (6) #elif defined(__x86_64__) #define MICROPY_NLR_X64 (1) - #if defined(__CYGWIN__) - #define MICROPY_NLR_NUM_REGS (12) + #if MICROPY_NLR_OS_WINDOWS + #define MICROPY_NLR_NUM_REGS (10) #else #define MICROPY_NLR_NUM_REGS (8) #endif diff --git a/py/nlrx64.c b/py/nlrx64.c index 663a457b7..a3a1cf341 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -33,18 +33,12 @@ // x86-64 callee-save registers are: // rbx, rbp, rsp, r12, r13, r14, r15 -#if defined(_WIN32) || defined(__CYGWIN__) -#define NLR_OS_WINDOWS 1 -#else -#define NLR_OS_WINDOWS 0 -#endif - __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); unsigned int nlr_push(nlr_buf_t *nlr) { (void)nlr; - #if NLR_OS_WINDOWS + #if MICROPY_NLR_OS_WINDOWS __asm volatile ( "movq (%rsp), %rax \n" // load return %rip @@ -93,7 +87,7 @@ NORETURN void nlr_jump(void *val) { __asm volatile ( "movq %0, %%rcx \n" // %rcx points to nlr_buf - #if NLR_OS_WINDOWS + #if MICROPY_NLR_OS_WINDOWS "movq 88(%%rcx), %%rsi \n" // load saved %rsi "movq 80(%%rcx), %%rdi \n" // load saved %rdr #endif diff --git a/py/nlrx86.c b/py/nlrx86.c index 9490c4f42..23882cc30 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -33,13 +33,7 @@ // For reference, x86 callee save regs are: // ebx, esi, edi, ebp, esp, eip -#if defined(_WIN32) || defined(__CYGWIN__) -#define NLR_OS_WINDOWS 1 -#else -#define NLR_OS_WINDOWS 0 -#endif - -#if NLR_OS_WINDOWS +#if MICROPY_NLR_OS_WINDOWS unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); #else __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); From 42c4dd09a19ac864825786ece39d759bca34abd2 Mon Sep 17 00:00:00 2001 From: stijn Date: Thu, 28 Dec 2017 11:02:29 +0100 Subject: [PATCH 238/828] py/nlr: Fix missing trailing characters in comments in nlr.c --- py/nlr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/py/nlr.c b/py/nlr.c index 7114d4997..03d01577e 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -27,12 +27,12 @@ #include "py/mpstate.h" #if !MICROPY_NLR_SETJMP -// When not using setjmp, nlr_push_tail is called from inline asm so needs special c +// When not using setjmp, nlr_push_tail is called from inline asm so needs special care #if MICROPY_NLR_X86 && MICROPY_NLR_OS_WINDOWS -// On these 32-bit platforms make sure nlr_push_tail doesn't have a leading undersco +// On these 32-bit platforms make sure nlr_push_tail doesn't have a leading underscore unsigned int nlr_push_tail(nlr_buf_t *nlr) asm("nlr_push_tail"); #else -// LTO can't see inside inline asm functions so explicitly mark nlr_push_tail as use +// LTO can't see inside inline asm functions so explicitly mark nlr_push_tail as used __attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr); #endif #endif From bb3412291a0f88cb958852b268d5dc43db23d4fb Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Tue, 2 Jan 2018 21:36:46 +1100 Subject: [PATCH 239/828] drivers/display/ssd1306: Fix super() call in SSD1306 driver. --- drivers/display/ssd1306.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/display/ssd1306.py b/drivers/display/ssd1306.py index cebe10e67..178b4911d 100644 --- a/drivers/display/ssd1306.py +++ b/drivers/display/ssd1306.py @@ -32,7 +32,7 @@ class SSD1306(framebuf.FrameBuffer): self.external_vcc = external_vcc self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) - super.__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) + super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) self.init_display() def init_display(self): From a275cb0f487cd6517760271dc01d369c32600c63 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 19 Dec 2017 23:54:24 +0100 Subject: [PATCH 240/828] drivers/sdcard: Avoid allocation on the heap. This commit fixes two things: 1. Do not allocate on the heap in readblocks() - unless the block size is bigger than 512 bytes. 2. Raise an error instead of returning 1 to indicate an error: the FAT block device layer does not check the return value. And other backends (e.g. esp32 blockdev) also raise an error instead of returning non-zero. --- drivers/sdcard/sdcard.py | 48 ++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/drivers/sdcard/sdcard.py b/drivers/sdcard/sdcard.py index fedb76f02..815631cae 100644 --- a/drivers/sdcard/sdcard.py +++ b/drivers/sdcard/sdcard.py @@ -46,6 +46,7 @@ class SDCard: self.cmdbuf = bytearray(6) self.dummybuf = bytearray(512) + self.tokenbuf = bytearray(1) for i in range(512): self.dummybuf[i] = 0xff self.dummybuf_memoryview = memoryview(self.dummybuf) @@ -134,7 +135,7 @@ class SDCard: return raise OSError("timeout waiting for v2 card") - def cmd(self, cmd, arg, crc, final=0, release=True): + def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): self.cs(0) # create and send the command @@ -147,9 +148,13 @@ class SDCard: buf[5] = crc self.spi.write(buf) + if skip1: + self.spi.readinto(self.tokenbuf, 0xff) + # wait for the response (response[7] == 0) for i in range(_CMD_TIMEOUT): - response = self.spi.read(1, 0xff)[0] + self.spi.readinto(self.tokenbuf, 0xff) + response = self.tokenbuf[0] if not (response & 0x80): # this could be a big-endian integer that we are getting here for j in range(final): @@ -164,27 +169,19 @@ class SDCard: self.spi.write(b'\xff') return -1 - def cmd_nodata(self, cmd): - self.spi.write(cmd) - self.spi.read(1, 0xff) # ignore stuff byte - for _ in range(_CMD_TIMEOUT): - if self.spi.read(1, 0xff)[0] == 0xff: - self.cs(1) - self.spi.write(b'\xff') - return 0 # OK - self.cs(1) - self.spi.write(b'\xff') - return 1 # timeout - def readinto(self, buf): self.cs(0) # read until start byte (0xff) - while self.spi.read(1, 0xff)[0] != 0xfe: - pass + while True: + self.spi.readinto(self.tokenbuf, 0xff) + if self.tokenbuf[0] == 0xfe: + break # read data - mv = self.dummybuf_memoryview[:len(buf)] + mv = self.dummybuf_memoryview + if len(buf) != len(mv): + mv = mv[:len(buf)] self.spi.write_readinto(mv, buf) # read checksum @@ -231,26 +228,26 @@ class SDCard: return self.sectors def readblocks(self, block_num, buf): - nblocks, err = divmod(len(buf), 512) - assert nblocks and not err, 'Buffer length is invalid' + nblocks = len(buf) // 512 + assert nblocks and not len(buf) % 512, 'Buffer length is invalid' if nblocks == 1: # CMD17: set read address for single block if self.cmd(17, block_num * self.cdv, 0) != 0: - return 1 + raise OSError(5) # EIO # receive the data self.readinto(buf) else: # CMD18: set read address for multiple blocks if self.cmd(18, block_num * self.cdv, 0) != 0: - return 1 + raise OSError(5) # EIO offset = 0 mv = memoryview(buf) while nblocks: self.readinto(mv[offset : offset + 512]) offset += 512 nblocks -= 1 - return self.cmd_nodata(b'\x0c') # cmd 12 - return 0 + if self.cmd(12, 0, 0xff, skip1=True): + raise OSError(5) # EIO def writeblocks(self, block_num, buf): nblocks, err = divmod(len(buf), 512) @@ -258,14 +255,14 @@ class SDCard: if nblocks == 1: # CMD24: set write address for single block if self.cmd(24, block_num * self.cdv, 0) != 0: - return 1 + raise OSError(5) # EIO # send the data self.write(_TOKEN_DATA, buf) else: # CMD25: set write address for first block if self.cmd(25, block_num * self.cdv, 0) != 0: - return 1 + raise OSError(5) # EIO # send the data offset = 0 mv = memoryview(buf) @@ -274,4 +271,3 @@ class SDCard: offset += 512 nblocks -= 1 self.write_token(_TOKEN_STOP_TRAN) - return 0 From 1ed2c23efb3dc4d12f07e10e20f6194da4babf16 Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Thu, 4 Jan 2018 11:45:36 -0500 Subject: [PATCH 241/828] stm32/modmachine: Handle case of no MICROPY_PY_MACHINE_I2C. --- ports/stm32/modmachine.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 8c59758fa..d9eb6383e 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -558,7 +558,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&pyb_rtc_type) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pyb_adc_type) }, #endif +#if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, +#endif { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&pyb_wdt_type) }, From df952633ef806d848ea75148a7be52e6a4e7f9fe Mon Sep 17 00:00:00 2001 From: stijn Date: Wed, 3 Jan 2018 21:10:03 +0100 Subject: [PATCH 242/828] windows: Add Appveyor CI builds for windows mingw port Build and test 32bit and 64bit versions of the windows port using gcc from mingw-w64. Note a bunch of tests which rely on floating point math/printing have been disabled for now since they fail. --- ports/windows/.appveyor.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ports/windows/.appveyor.yml b/ports/windows/.appveyor.yml index a82cf5adc..795330eff 100644 --- a/ports/windows/.appveyor.yml +++ b/ports/windows/.appveyor.yml @@ -24,6 +24,26 @@ test_script: %MICROPY_CPYTHON3% run-tests +# After the build/test phase for the MSVC build completes, +# build and test with mingw-w64, release versions only. +after_test: +- ps: | + if ($env:configuration -eq 'Debug') { + return + } + $env:MSYSTEM = if ($platform -eq 'x86') {'MINGW32'} else {'MINGW64'} + $env:CHERE_INVOKING = 'enabled_from_arguments' + cd (Join-Path $env:APPVEYOR_BUILD_FOLDER 'ports/windows') + C:\msys64\usr\bin\bash.exe -l -c "make -B -j4 V=1" + if ($LASTEXITCODE -ne 0) { + throw "$env:MSYSTEM build exited with code $LASTEXITCODE" + } + cd (Join-Path $env:APPVEYOR_BUILD_FOLDER 'tests') + & $env:MICROPY_CPYTHON3 run-tests -e math_fun -e float2int_double -e float_parse -e math_domain_special + if ($LASTEXITCODE -ne 0) { + throw "$env:MSYSTEM tests exited with code $LASTEXITCODE" + } + skip_tags: true deploy: off From 7642785881550bd71815623c4f93e0516217429c Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 19 Dec 2017 23:47:07 +0100 Subject: [PATCH 243/828] extmod/vfs_fat_file: Implement SEEK_CUR for non-zero offset. CPython doesn't allow SEEK_CUR with non-zero offset for files in text mode, and uPy inherited this behaviour for both text and binary files. It makes sense to provide full support for SEEK_CUR of binary-mode files in uPy, and to do this in a minimal way means also allowing to use SEEK_CUR with non-zero offsets on text-mode files. That seems to be a fair compromise. --- extmod/vfs_fat_file.c | 6 +----- tests/extmod/vfs_fat_fileio1.py | 6 ++---- tests/extmod/vfs_fat_fileio1.py.exp | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index 1fcbb253d..6154c8483 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -134,11 +134,7 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, break; case 1: // SEEK_CUR - if (s->offset != 0) { - *errcode = MP_EOPNOTSUPP; - return MP_STREAM_ERROR; - } - // no-operation + f_lseek(&self->fp, f_tell(&self->fp) + s->offset); break; case 2: // SEEK_END diff --git a/tests/extmod/vfs_fat_fileio1.py b/tests/extmod/vfs_fat_fileio1.py index d19df120b..8b9ff92eb 100644 --- a/tests/extmod/vfs_fat_fileio1.py +++ b/tests/extmod/vfs_fat_fileio1.py @@ -91,10 +91,8 @@ with open("foo_file.txt") as f2: f2.seek(0, 1) # SEEK_CUR print(f2.read(1)) - try: - f2.seek(1, 1) # SEEK_END - except OSError as e: - print(e.args[0] == uerrno.EOPNOTSUPP) + f2.seek(2, 1) # SEEK_CUR + print(f2.read(1)) f2.seek(-2, 2) # SEEK_END print(f2.read(1)) diff --git a/tests/extmod/vfs_fat_fileio1.py.exp b/tests/extmod/vfs_fat_fileio1.py.exp index d777585cf..a66f07605 100644 --- a/tests/extmod/vfs_fat_fileio1.py.exp +++ b/tests/extmod/vfs_fat_fileio1.py.exp @@ -7,7 +7,7 @@ hello!world! 12 h e -True +o d True [('foo_dir', 16384, 0)] From a40ce1d829eca6fe0fad0b7f4478a6651903dd2a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Jan 2018 18:11:06 +1100 Subject: [PATCH 244/828] esp8266/modules: Move dht.py driver to drivers/dht directory. --- drivers/dht/dht.py | 32 ++++++++++++++++++++++++++++++++ ports/esp32/modules/dht.py | 2 +- ports/esp8266/modules/dht.py | 33 +-------------------------------- 3 files changed, 34 insertions(+), 33 deletions(-) create mode 100644 drivers/dht/dht.py mode change 100644 => 120000 ports/esp8266/modules/dht.py diff --git a/drivers/dht/dht.py b/drivers/dht/dht.py new file mode 100644 index 000000000..9a69e7e07 --- /dev/null +++ b/drivers/dht/dht.py @@ -0,0 +1,32 @@ +# DHT11/DHT22 driver for MicroPython on ESP8266 +# MIT license; Copyright (c) 2016 Damien P. George + +import esp + +class DHTBase: + def __init__(self, pin): + self.pin = pin + self.buf = bytearray(5) + + def measure(self): + buf = self.buf + esp.dht_readinto(self.pin, buf) + if (buf[0] + buf[1] + buf[2] + buf[3]) & 0xff != buf[4]: + raise Exception("checksum error") + +class DHT11(DHTBase): + def humidity(self): + return self.buf[0] + + def temperature(self): + return self.buf[2] + +class DHT22(DHTBase): + def humidity(self): + return (self.buf[0] << 8 | self.buf[1]) * 0.1 + + def temperature(self): + t = ((self.buf[2] & 0x7f) << 8 | self.buf[3]) * 0.1 + if self.buf[2] & 0x80: + t = -t + return t diff --git a/ports/esp32/modules/dht.py b/ports/esp32/modules/dht.py index b6acf2853..2aa2f5cbf 120000 --- a/ports/esp32/modules/dht.py +++ b/ports/esp32/modules/dht.py @@ -1 +1 @@ -../../esp8266/modules/dht.py \ No newline at end of file +../../../drivers/dht/dht.py \ No newline at end of file diff --git a/ports/esp8266/modules/dht.py b/ports/esp8266/modules/dht.py deleted file mode 100644 index 9a69e7e07..000000000 --- a/ports/esp8266/modules/dht.py +++ /dev/null @@ -1,32 +0,0 @@ -# DHT11/DHT22 driver for MicroPython on ESP8266 -# MIT license; Copyright (c) 2016 Damien P. George - -import esp - -class DHTBase: - def __init__(self, pin): - self.pin = pin - self.buf = bytearray(5) - - def measure(self): - buf = self.buf - esp.dht_readinto(self.pin, buf) - if (buf[0] + buf[1] + buf[2] + buf[3]) & 0xff != buf[4]: - raise Exception("checksum error") - -class DHT11(DHTBase): - def humidity(self): - return self.buf[0] - - def temperature(self): - return self.buf[2] - -class DHT22(DHTBase): - def humidity(self): - return (self.buf[0] << 8 | self.buf[1]) * 0.1 - - def temperature(self): - t = ((self.buf[2] & 0x7f) << 8 | self.buf[3]) * 0.1 - if self.buf[2] & 0x80: - t = -t - return t diff --git a/ports/esp8266/modules/dht.py b/ports/esp8266/modules/dht.py new file mode 120000 index 000000000..2aa2f5cbf --- /dev/null +++ b/ports/esp8266/modules/dht.py @@ -0,0 +1 @@ +../../../drivers/dht/dht.py \ No newline at end of file From efdda2c62deba4f4f24672e441cffe496158bb35 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Jan 2018 18:12:53 +1100 Subject: [PATCH 245/828] stm32: Add support for DHT11/DHT22 sensors. --- drivers/dht/dht.py | 7 +++++-- ports/stm32/Makefile | 1 + ports/stm32/modpyb.c | 4 ++++ ports/stm32/modules/dht.py | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) create mode 120000 ports/stm32/modules/dht.py diff --git a/drivers/dht/dht.py b/drivers/dht/dht.py index 9a69e7e07..eed61df7c 100644 --- a/drivers/dht/dht.py +++ b/drivers/dht/dht.py @@ -1,7 +1,10 @@ # DHT11/DHT22 driver for MicroPython on ESP8266 # MIT license; Copyright (c) 2016 Damien P. George -import esp +try: + from esp import dht_readinto +except: + from pyb import dht_readinto class DHTBase: def __init__(self, pin): @@ -10,7 +13,7 @@ class DHTBase: def measure(self): buf = self.buf - esp.dht_readinto(self.pin, buf) + dht_readinto(self.pin, buf) if (buf[0] + buf[1] + buf[2] + buf[3]) & 0xff != buf[4]: raise Exception("checksum error") diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 68b007471..65962bea6 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -186,6 +186,7 @@ EXTMOD_SRC_C = $(addprefix extmod/,\ DRIVERS_SRC_C = $(addprefix drivers/,\ memory/spiflash.c \ + dht/dht.c \ ) SRC_C = \ diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index 4d186e278..970b5b954 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -34,6 +34,7 @@ #include "lib/utils/pyexec.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" +#include "drivers/dht/dht.h" #include "gccollect.h" #include "stm32_it.h" #include "irq.h" @@ -168,6 +169,9 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&mod_os_sync_obj) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, + // This function is not intended to be public and may be moved elsewhere + { MP_ROM_QSTR(MP_QSTR_dht_readinto), MP_ROM_PTR(&dht_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&pyb_timer_type) }, #if MICROPY_HW_ENABLE_RNG diff --git a/ports/stm32/modules/dht.py b/ports/stm32/modules/dht.py new file mode 120000 index 000000000..2aa2f5cbf --- /dev/null +++ b/ports/stm32/modules/dht.py @@ -0,0 +1 @@ +../../../drivers/dht/dht.py \ No newline at end of file From 925c5b1da2fed80774dd393d597392a2c254effa Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Jan 2018 18:21:07 +1100 Subject: [PATCH 246/828] lib/utils/pyexec.h: Include py/obj.h because its decls are needed. --- lib/utils/pyexec.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/utils/pyexec.h b/lib/utils/pyexec.h index bc98ba94a..678c56cf4 100644 --- a/lib/utils/pyexec.h +++ b/lib/utils/pyexec.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H #define MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H +#include "py/obj.h" + typedef enum { PYEXEC_MODE_RAW_REPL, PYEXEC_MODE_FRIENDLY_REPL, From bd257a838f605d4f45eb11edf9f89aa603ef2f33 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Jan 2018 18:55:35 +1100 Subject: [PATCH 247/828] .gitmodules: Use https URL for lwIP submodule. HTTPS is supported by Savannah and better to be secure than not. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index d2d8dd278..c3f4c55ac 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,7 +7,7 @@ url = https://github.com/atgreen/libffi [submodule "lib/lwip"] path = lib/lwip - url = http://git.savannah.gnu.org/r/lwip.git + url = https://git.savannah.gnu.org/r/lwip.git [submodule "lib/berkeley-db-1.xx"] path = lib/berkeley-db-1.xx url = https://github.com/pfalcon/berkeley-db-1.xx From 23f9f9495f0b64e70470b17ba38cf1f2148724bd Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Jan 2018 19:38:32 +1100 Subject: [PATCH 248/828] esp32/machine_uart: Fix check of UART id so it only allows valid UARTs. --- ports/esp32/machine_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 0b303d424..06d9c0b0d 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -187,7 +187,7 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, // get uart id mp_int_t uart_num = mp_obj_get_int(args[0]); - if (uart_num < 0 || uart_num > UART_NUM_MAX) { + if (uart_num < 0 || uart_num >= UART_NUM_MAX) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) does not exist", uart_num)); } From 524ff3027525832c85bf5411a6302578836b941a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 31 Jan 2018 21:05:21 +1100 Subject: [PATCH 249/828] minimal/README: Update text to better describe what "make run" does. --- ports/minimal/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/minimal/README.md b/ports/minimal/README.md index 14b8c00a3..356fc4b3e 100644 --- a/ports/minimal/README.md +++ b/ports/minimal/README.md @@ -9,7 +9,7 @@ By default the port will be built for the host machine: $ make -To run a small test script do: +To run the executable and get a basic working REPL do: $ make run From a44892dd0d79e039b4a4d1ec3ec7b9a6ed829ee6 Mon Sep 17 00:00:00 2001 From: Hemanth kumar Date: Wed, 17 Jan 2018 01:46:25 +0530 Subject: [PATCH 250/828] drivers/sdcard: Update doc for ESP8266 to use correct SPI number. machine.SPI(0) results in ValueError on ESP8266. SPI(1) is the user hardware SPI port (or use SPI(-1) for software SPI). --- drivers/sdcard/sdcard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sdcard/sdcard.py b/drivers/sdcard/sdcard.py index 815631cae..719eb982e 100644 --- a/drivers/sdcard/sdcard.py +++ b/drivers/sdcard/sdcard.py @@ -14,7 +14,7 @@ Example usage on pyboard: Example usage on ESP8266: import machine, sdcard, os - sd = sdcard.SDCard(machine.SPI(0), machine.Pin(15)) + sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15)) os.umount() os.VfsFat(sd, "") os.listdir() From c0496fd44de562cd11e66acd9e42f796c3dcb5fb Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Feb 2018 11:45:29 +1100 Subject: [PATCH 251/828] stm32/spi: Make SPI DMA wait routine more power efficient by using WFI. The routine waits for the DMA to finish, which is signalled from a DMA IRQ handler. Using WFI makes the CPU sleep while waiting for the IRQ to arrive which decreases power consumption. To make it work correctly the check for the change in state must be atomic and so IRQs must be disabled during the check. The key feature of the Cortex MCU that makes this possible is that WFI will exit when an IRQ arrives even if IRQs are disabled. --- ports/stm32/spi.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index 3cd470ccb..2b5bdb038 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -404,11 +404,16 @@ void spi_deinit(SPI_HandleTypeDef *spi) { } STATIC HAL_StatusTypeDef spi_wait_dma_finished(SPI_HandleTypeDef *spi, uint32_t timeout) { - // Note: we can't use WFI to idle in this loop because the DMA completion - // interrupt may occur before the WFI. Hence we miss it and have to wait - // until the next sys-tick (up to 1ms). uint32_t start = HAL_GetTick(); - while (HAL_SPI_GetState(spi) != HAL_SPI_STATE_READY) { + for (;;) { + // Do an atomic check of the state; WFI will exit even if IRQs are disabled + uint32_t irq_state = disable_irq(); + if (spi->State == HAL_SPI_STATE_READY) { + enable_irq(irq_state); + return HAL_OK; + } + __WFI(); + enable_irq(irq_state); if (HAL_GetTick() - start >= timeout) { return HAL_TIMEOUT; } From fed1b4fb56421e8f8a1ee54b2c16296204176bb3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Feb 2018 12:20:45 +1100 Subject: [PATCH 252/828] stm32/sdcard: Make SD wait routine more power efficient by using WFI. Using WFI allows the CPU to sleep while it is waiting, reducing power consumption. --- ports/stm32/sdcard.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index a54e05011..46f08f78f 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -244,11 +244,20 @@ void SDMMC2_IRQHandler(void) { STATIC HAL_StatusTypeDef sdcard_wait_finished(SD_HandleTypeDef *sd, uint32_t timeout) { // Wait for HAL driver to be ready (eg for DMA to finish) uint32_t start = HAL_GetTick(); - while (sd->State == HAL_SD_STATE_BUSY) { + for (;;) { + // Do an atomic check of the state; WFI will exit even if IRQs are disabled + uint32_t irq_state = disable_irq(); + if (sd->State != HAL_SD_STATE_BUSY) { + enable_irq(irq_state); + break; + } + __WFI(); + enable_irq(irq_state); if (HAL_GetTick() - start >= timeout) { return HAL_TIMEOUT; } } + // Wait for SD card to complete the operation for (;;) { HAL_SD_CardStateTypedef state = HAL_SD_GetCardState(sd); @@ -261,6 +270,7 @@ STATIC HAL_StatusTypeDef sdcard_wait_finished(SD_HandleTypeDef *sd, uint32_t tim if (HAL_GetTick() - start >= timeout) { return HAL_TIMEOUT; } + __WFI(); } return HAL_OK; } From 1d4246a2e8caa52f8381f405bfb37ba24b4246e7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Feb 2018 12:44:16 +1100 Subject: [PATCH 253/828] stm32/usbdev: Reduce dependency on py header files. --- ports/stm32/usbd_conf.h | 2 -- ports/stm32/usbd_desc.c | 3 +-- ports/stm32/usbd_msc_storage.c | 2 +- ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h | 4 ++-- ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c | 1 + 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ports/stm32/usbd_conf.h b/ports/stm32/usbd_conf.h index 34ebe27b9..b066bb2b8 100644 --- a/ports/stm32/usbd_conf.h +++ b/ports/stm32/usbd_conf.h @@ -38,8 +38,6 @@ #include #include -#include "py/mpconfig.h" - /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ /* Common Config */ diff --git a/ports/stm32/usbd_desc.c b/ports/stm32/usbd_desc.c index 1de75aee0..0c7b2dfe5 100644 --- a/ports/stm32/usbd_desc.c +++ b/ports/stm32/usbd_desc.c @@ -33,8 +33,7 @@ #include "usbd_desc.h" #include "usbd_conf.h" -// need these headers just for MP_HAL_UNIQUE_ID_ADDRESS -#include "py/misc.h" +// need this header just for MP_HAL_UNIQUE_ID_ADDRESS #include "py/mphal.h" // So we don't clash with existing ST boards, we use the unofficial FOSS VID. diff --git a/ports/stm32/usbd_msc_storage.c b/ports/stm32/usbd_msc_storage.c index f825c3d70..7d6c19e9f 100644 --- a/ports/stm32/usbd_msc_storage.c +++ b/ports/stm32/usbd_msc_storage.c @@ -36,7 +36,7 @@ #include "usbd_cdc_msc_hid.h" #include "usbd_msc_storage.h" -#include "py/misc.h" +#include "py/mpstate.h" #include "storage.h" #include "sdcard.h" 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 a26b1df0d..c2e7c17fe 100644 --- a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h +++ b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h @@ -36,8 +36,8 @@ typedef struct { uint8_t CmdOpCode; uint8_t CmdLength; - __IO uint32_t TxState; - __IO uint32_t RxState; + volatile uint32_t TxState; + volatile uint32_t RxState; } USBD_CDC_HandleTypeDef; typedef struct _USBD_STORAGE { 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 379a8f32c..81865bc00 100644 --- a/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c +++ b/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include STM32_HAL_H #include "usbd_ioreq.h" #include "usbd_cdc_msc_hid.h" From 583472e06892832edc395a65096b28cd08f90764 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 1 Feb 2018 12:46:37 +1100 Subject: [PATCH 254/828] stm32/usbdev: Combine all str descriptor accessor funcs into one func. There's no need to have these as separate functions, they just take up unnecessary code space and combining them allows to factor common code, and also allows to support arbitrary string descriptor indices. --- ports/stm32/usbd_conf.h | 1 - ports/stm32/usbd_desc.c | 163 +++++++++------------- ports/stm32/usbdev/core/inc/usbd_def.h | 10 +- ports/stm32/usbdev/core/src/usbd_ctlreq.c | 37 +---- 4 files changed, 70 insertions(+), 141 deletions(-) diff --git a/ports/stm32/usbd_conf.h b/ports/stm32/usbd_conf.h index b066bb2b8..5fa3c513d 100644 --- a/ports/stm32/usbd_conf.h +++ b/ports/stm32/usbd_conf.h @@ -44,7 +44,6 @@ #define USBD_MAX_NUM_INTERFACES 1 #define USBD_MAX_NUM_CONFIGURATION 1 #define USBD_MAX_STR_DESC_SIZ 0x100 -#define USBD_SUPPORT_USER_STRING 0 #define USBD_SELF_POWERED 0 #define USBD_DEBUG_LEVEL 0 diff --git a/ports/stm32/usbd_desc.c b/ports/stm32/usbd_desc.c index 0c7b2dfe5..4babebf64 100644 --- a/ports/stm32/usbd_desc.c +++ b/ports/stm32/usbd_desc.c @@ -103,115 +103,82 @@ STATIC uint8_t *USBD_DeviceDescriptor(USBD_HandleTypeDef *pdev, uint16_t *length } /** - * @brief Returns the LangID string descriptor. - * @param speed: Current device speed + * @brief Returns a string descriptor + * @param idx: Index of the string descriptor to retrieve * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer + * @retval Pointer to descriptor buffer, or NULL if idx is invalid */ -STATIC uint8_t *USBD_LangIDStrDescriptor(USBD_HandleTypeDef *pdev, uint16_t *length) { - *length = sizeof(USBD_LangIDDesc); - return (uint8_t*)USBD_LangIDDesc; // the data should only be read from this buf -} +STATIC uint8_t *USBD_StrDescriptor(USBD_HandleTypeDef *pdev, uint8_t idx, uint16_t *length) { + char str_buf[16]; + const char *str = NULL; -/** - * @brief Returns the product string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -STATIC uint8_t *USBD_ProductStrDescriptor(USBD_HandleTypeDef *pdev, uint16_t *length) { - uint8_t *str_desc = ((usbd_cdc_msc_hid_state_t*)pdev->pClassData)->usbd_str_desc; - if (pdev->dev_speed == USBD_SPEED_HIGH) { - USBD_GetString((uint8_t *)USBD_PRODUCT_HS_STRING, str_desc, length); - } else { - USBD_GetString((uint8_t *)USBD_PRODUCT_FS_STRING, str_desc, length); + switch (idx) { + case USBD_IDX_LANGID_STR: + *length = sizeof(USBD_LangIDDesc); + return (uint8_t*)USBD_LangIDDesc; // the data should only be read from this buf + + case USBD_IDX_MFC_STR: + str = USBD_MANUFACTURER_STRING; + break; + + case USBD_IDX_PRODUCT_STR: + if (pdev->dev_speed == USBD_SPEED_HIGH) { + str = USBD_PRODUCT_HS_STRING; + } else { + str = USBD_PRODUCT_FS_STRING; + } + break; + + case USBD_IDX_SERIAL_STR: { + // This document: http://www.usb.org/developers/docs/devclass_docs/usbmassbulk_10.pdf + // says that the serial number has to be at least 12 digits long and that + // the last 12 digits need to be unique. It also stipulates that the valid + // character set is that of upper-case hexadecimal digits. + // + // The onboard DFU bootloader produces a 12-digit serial number based on + // the 96-bit unique ID, so for consistency we go with this algorithm. + // You can see the serial number if you use: lsusb -v + // + // See: https://my.st.com/52d187b7 for the algorithim used. + + uint8_t *id = (uint8_t *)MP_HAL_UNIQUE_ID_ADDRESS; + snprintf(str_buf, sizeof(str_buf), + "%02X%02X%02X%02X%02X%02X", + id[11], id[10] + id[2], id[9], id[8] + id[0], id[7], id[6]); + + str = str_buf; + break; + } + + case USBD_IDX_CONFIG_STR: + if (pdev->dev_speed == USBD_SPEED_HIGH) { + str = USBD_CONFIGURATION_HS_STRING; + } else { + str = USBD_CONFIGURATION_FS_STRING; + } + break; + + case USBD_IDX_INTERFACE_STR: + if (pdev->dev_speed == USBD_SPEED_HIGH) { + str = USBD_INTERFACE_HS_STRING; + } else { + str = USBD_INTERFACE_FS_STRING; + } + break; + + default: + // invalid string index + return NULL; } - return str_desc; -} - -/** - * @brief Returns the manufacturer string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -STATIC uint8_t *USBD_ManufacturerStrDescriptor(USBD_HandleTypeDef *pdev, uint16_t *length) { - uint8_t *str_desc = ((usbd_cdc_msc_hid_state_t*)pdev->pClassData)->usbd_str_desc; - USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, str_desc, length); - return str_desc; -} - -/** - * @brief Returns the serial number string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -STATIC uint8_t *USBD_SerialStrDescriptor(USBD_HandleTypeDef *pdev, uint16_t *length) { - // This document: http://www.usb.org/developers/docs/devclass_docs/usbmassbulk_10.pdf - // says that the serial number has to be at least 12 digits long and that - // the last 12 digits need to be unique. It also stipulates that the valid - // character set is that of upper-case hexadecimal digits. - // - // The onboard DFU bootloader produces a 12-digit serial number based on - // the 96-bit unique ID, so for consistency we go with this algorithm. - // You can see the serial number if you do: - // - // dfu-util -l - // - // See: https://my.st.com/52d187b7 for the algorithim used. - - uint8_t *id = (uint8_t *)MP_HAL_UNIQUE_ID_ADDRESS; - char serial_buf[16]; -