From 0864a6957fe4717c3ec40ceeb373b19614a18434 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Oct 2017 23:34:28 +1100 Subject: [PATCH 01/75] py: Clean up unary and binary enum list to keep groups together. 2 non-bytecode binary ops (NOT_IN and IN_NOT) are moved out of the bytecode group, so this change will change the bytecode format. --- py/bc0.h | 4 ++-- py/runtime0.h | 36 +++++++++++++++++++-------------- py/showbc.c | 4 ++-- py/vmentrytable.h | 4 ++-- tests/cmdline/cmd_showbc.py.exp | 14 ++++++------- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/py/bc0.h b/py/bc0.h index 38e23c0fde..70acfb0cac 100644 --- a/py/bc0.h +++ b/py/bc0.h @@ -113,7 +113,7 @@ #define MP_BC_LOAD_CONST_SMALL_INT_MULTI (0x70) // + N(64) #define MP_BC_LOAD_FAST_MULTI (0xb0) // + N(16) #define MP_BC_STORE_FAST_MULTI (0xc0) // + N(16) -#define MP_BC_UNARY_OP_MULTI (0xd0) // + op( Date: Thu, 5 Oct 2017 10:47:22 +1100 Subject: [PATCH 02/75] py/objtype: Clean up unary- and binary-op enum-to-qstr mapping tables. --- py/objtype.c | 60 ++++++++++++++++------------------------------------ 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/py/objtype.c b/py/objtype.c index d7f409e30e..9a639ef035 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -332,7 +332,7 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size return MP_OBJ_FROM_PTR(o); } -const uint16_t mp_unary_op_method_name[] = { +const uint16_t mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { [MP_UNARY_OP_BOOL] = MP_QSTR___bool__, [MP_UNARY_OP_LEN] = MP_QSTR___len__, [MP_UNARY_OP_HASH] = MP_QSTR___hash__, @@ -344,7 +344,6 @@ const uint16_t mp_unary_op_method_name[] = { #if MICROPY_PY_SYS_GETSIZEOF [MP_UNARY_OP_SIZEOF] = MP_QSTR_getsizeof, #endif - [MP_UNARY_OP_NOT] = MP_QSTR_, // don't need to implement this, used to make sure array has full size }; STATIC mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) { @@ -406,14 +405,23 @@ STATIC mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } } -const uint16_t mp_binary_op_method_name[] = { - /* - MP_BINARY_OP_OR, - MP_BINARY_OP_XOR, - MP_BINARY_OP_AND, - MP_BINARY_OP_LSHIFT, - MP_BINARY_OP_RSHIFT, - */ +// Binary-op enum values not listed here will have the default value of 0 in the +// table, corresponding to MP_QSTR_, and are therefore unsupported (a lookup will +// fail). They can be added at the expense of code size for the qstr. +const uint16_t mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { + [MP_BINARY_OP_LESS] = MP_QSTR___lt__, + [MP_BINARY_OP_MORE] = MP_QSTR___gt__, + [MP_BINARY_OP_EQUAL] = MP_QSTR___eq__, + [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__, + + #if MICROPY_PY_ALL_SPECIAL_METHODS + [MP_BINARY_OP_INPLACE_ADD] = MP_QSTR___iadd__, + [MP_BINARY_OP_INPLACE_SUBTRACT] = MP_QSTR___isub__, + #endif + [MP_BINARY_OP_ADD] = MP_QSTR___add__, [MP_BINARY_OP_SUBTRACT] = MP_QSTR___sub__, #if MICROPY_PY_ALL_SPECIAL_METHODS @@ -421,44 +429,12 @@ const uint16_t mp_binary_op_method_name[] = { [MP_BINARY_OP_FLOOR_DIVIDE] = MP_QSTR___floordiv__, [MP_BINARY_OP_TRUE_DIVIDE] = MP_QSTR___truediv__, #endif - /* - MP_BINARY_OP_MODULO, - MP_BINARY_OP_POWER, - MP_BINARY_OP_DIVMOD, - MP_BINARY_OP_INPLACE_OR, - MP_BINARY_OP_INPLACE_XOR, - MP_BINARY_OP_INPLACE_AND, - MP_BINARY_OP_INPLACE_LSHIFT, - MP_BINARY_OP_INPLACE_RSHIFT,*/ - #if MICROPY_PY_ALL_SPECIAL_METHODS - [MP_BINARY_OP_INPLACE_ADD] = MP_QSTR___iadd__, - [MP_BINARY_OP_INPLACE_SUBTRACT] = MP_QSTR___isub__, - #endif - /*MP_BINARY_OP_INPLACE_MULTIPLY, - MP_BINARY_OP_INPLACE_FLOOR_DIVIDE, - MP_BINARY_OP_INPLACE_TRUE_DIVIDE, - MP_BINARY_OP_INPLACE_MODULO, - MP_BINARY_OP_INPLACE_POWER,*/ #if MICROPY_PY_REVERSE_SPECIAL_METHODS [MP_BINARY_OP_REVERSE_ADD] = MP_QSTR___radd__, [MP_BINARY_OP_REVERSE_SUBTRACT] = MP_QSTR___rsub__, [MP_BINARY_OP_REVERSE_MULTIPLY] = MP_QSTR___rmul__, #endif - - [MP_BINARY_OP_LESS] = MP_QSTR___lt__, - [MP_BINARY_OP_MORE] = MP_QSTR___gt__, - [MP_BINARY_OP_EQUAL] = MP_QSTR___eq__, - [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_IS, - */ - [MP_BINARY_OP_LAST] = 0, // used to make sure array has full size, TODO: FIXME }; STATIC mp_obj_t instance_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { From ff93fd4f50321c6190e1659b19e64fef3045a484 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Oct 2017 10:48:23 +1100 Subject: [PATCH 03/75] py/persistentcode: Bump .mpy version number to version 3. The binary and unary ops have changed bytecode encoding. --- ports/minimal/frozentest.mpy | Bin 255 -> 255 bytes py/persistentcode.c | 2 +- tools/mpy-tool.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/minimal/frozentest.mpy b/ports/minimal/frozentest.mpy index 87f9581bfe7290b67f473a49441fd25db204f459..7c6809bf6522f90339ec72ae0dcd16560ac5aafc 100644 GIT binary patch delta 99 zcmey*_@6P_mzhaEhM55bc)1upGH7XNXlQFNF#MN3$RPHKA%)Qh$PkCoj8HxkgcjNL gewHYZ*^0DZ?AumAu6 delta 99 zcmey*_@6P_mx)O}hM55bc)1w9GiYgOXlQFNF#MN3&LH-KA%)Qh$PkCoj8HxkgcjNL gewHYZ*^0EloK2mk;8 diff --git a/py/persistentcode.c b/py/persistentcode.c index d8b17c7e6b..e0bb8f1d65 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -39,7 +39,7 @@ #include "py/smallint.h" // The current version of .mpy files -#define MPY_VERSION (2) +#define MPY_VERSION (3) // The feature flags byte encodes the compile-time config options that // affect the generate bytecode. diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index ded9624878..5de4ecf1cc 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -57,7 +57,7 @@ class FreezeError(Exception): return 'error while freezing %s: %s' % (self.rawcode.source_file, self.msg) class Config: - MPY_VERSION = 2 + MPY_VERSION = 3 MICROPY_LONGINT_IMPL_NONE = 0 MICROPY_LONGINT_IMPL_LONGLONG = 1 MICROPY_LONGINT_IMPL_MPZ = 2 From 8c7db42ee3d6e47e4a58e4cb573dc1a30c96fa32 Mon Sep 17 00:00:00 2001 From: Li Weiwei Date: Wed, 27 Sep 2017 22:04:42 +0800 Subject: [PATCH 04/75] stm32/modnwwiznet5k: Get the IP address of an established socket. When wiznet5k_socket_accept is called, if a socket is established, get the IP address of the socket. --- ports/stm32/modnwwiznet5k.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 21e4dbbbf4..78249816d4 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -173,11 +173,7 @@ STATIC int wiznet5k_socket_accept(mod_network_socket_obj_t *socket, mod_network_ int sr = getSn_SR((uint8_t)socket->u_param.fileno); if (sr == SOCK_ESTABLISHED) { socket2->u_param = socket->u_param; - // TODO need to populate this with the correct values - ip[0] = 0; - ip[1] = 0; - ip[2] = 0; - ip[3] = 0; + getSn_DIPR((uint8_t)socket2->u_param.fileno, ip); *port = getSn_PORT(socket2->u_param.fileno); // WIZnet turns the listening socket into the client socket, so we From 98dd126e9818309e4ec9b561e7b6b9f7fa039cb9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Oct 2017 11:32:55 +1100 Subject: [PATCH 05/75] tests/extmod: Add test for '-' in character class in regex. --- tests/extmod/ure1.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/extmod/ure1.py b/tests/extmod/ure1.py index 6075990fcb..54471ed4f9 100644 --- a/tests/extmod/ure1.py +++ b/tests/extmod/ure1.py @@ -48,7 +48,12 @@ m = r.match("d") print(m.group(0)) m = r.match("A") print(m.group(0)) +print("===") +# '-' character within character class block +print(re.match("[-a]+", "-a]d").group(0)) +print(re.match("[a-]+", "-a]d").group(0)) +print("===") r = re.compile("o+") m = r.search("foobar") From ea6692a83e7132aa18a8032ae3c76fa91d9d50f2 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 5 Oct 2017 23:40:19 +0300 Subject: [PATCH 06/75] tools/pyboard: Use repr() when quoting data in error messages. As it may contain newlines, etc. --- tools/pyboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index d15f520ac7..8874730718 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -343,7 +343,7 @@ class Pyboard: # check if we could exec command data = self.serial.read(2) if data != b'OK': - raise PyboardError('could not exec command (response: %s)' % data) + raise PyboardError('could not exec command (response: %r)' % data) def exec_raw(self, command, timeout=10, data_consumer=None): self.exec_raw_no_follow(command); From 7df4083ac6344944e895b5911d59b64b68c8c134 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Sat, 4 Feb 2017 23:32:10 -0200 Subject: [PATCH 07/75] drivers/display/ssd1306: Implement SSD1306_I2C poweron method. After a poweroff(), the poweron() method does a soft power-on and any previous state of the display persists. --- 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 d65f2d3502..a05eda7dfc 100644 --- a/drivers/display/ssd1306.py +++ b/drivers/display/ssd1306.py @@ -124,7 +124,7 @@ class SSD1306_I2C(SSD1306): self.i2c.stop() def poweron(self): - pass + self.write_cmd(SET_DISP | 0x01) class SSD1306_SPI(SSD1306): From ca2427c31399834bcc7df6177b3314aa7242046e Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 6 Oct 2017 12:45:16 +1100 Subject: [PATCH 08/75] drivers/display/ssd1306: Make poweron() work the same with SSD1306_SPI. The poweroff() and poweron() methods are used to do soft power control of the display, and this patch makes these methods work the same for both I2C and SPI interfaces. --- drivers/display/ssd1306.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/display/ssd1306.py b/drivers/display/ssd1306.py index a05eda7dfc..cd358d00e2 100644 --- a/drivers/display/ssd1306.py +++ b/drivers/display/ssd1306.py @@ -1,7 +1,6 @@ # MicroPython SSD1306 OLED driver, I2C and SPI interfaces from micropython import const -import time import framebuf @@ -47,7 +46,6 @@ class SSD1306: self.text = fb.text self.scroll = fb.scroll self.blit = fb.blit - self.poweron() self.init_display() def init_display(self): @@ -80,6 +78,9 @@ class SSD1306: def poweroff(self): self.write_cmd(SET_DISP | 0x00) + def poweron(self): + self.write_cmd(SET_DISP | 0x01) + def contrast(self, contrast): self.write_cmd(SET_CONTRAST) self.write_cmd(contrast) @@ -123,9 +124,6 @@ class SSD1306_I2C(SSD1306): self.i2c.write(buf) self.i2c.stop() - def poweron(self): - self.write_cmd(SET_DISP | 0x01) - class SSD1306_SPI(SSD1306): def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): @@ -137,6 +135,12 @@ class SSD1306_SPI(SSD1306): self.dc = dc self.res = res self.cs = cs + import time + self.res(1) + time.sleep_ms(1) + self.res(0) + time.sleep_ms(10) + self.res(1) super().__init__(width, height, external_vcc) def write_cmd(self, cmd): @@ -154,10 +158,3 @@ class SSD1306_SPI(SSD1306): self.cs(0) self.spi.write(buf) self.cs(1) - - def poweron(self): - self.res(1) - time.sleep_ms(1) - self.res(0) - time.sleep_ms(10) - self.res(1) From 6f1a61542746e3ef3704f6e563e2526454df0112 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 6 Oct 2017 14:32:42 +1100 Subject: [PATCH 09/75] stm32/boards: Fix typos in stm32f767_af.csv table. --- ports/stm32/boards/stm32f767_af.csv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/stm32/boards/stm32f767_af.csv b/ports/stm32/boards/stm32f767_af.csv index db27818c64..1708dfcca4 100644 --- a/ports/stm32/boards/stm32f767_af.csv +++ b/ports/stm32/boards/stm32f767_af.csv @@ -15,9 +15,9 @@ 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,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 @@ -41,7 +41,7 @@ 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,I2S_CKIN,,UART5_CTS,,QUADSPI_BK1_IO0,,,SDMMC1_D1,DCMI_D3,,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 PortC,PC12,TRACED3,,,,,,SPI3_MOSI/I2S3_SD,USART3_CK,UART5_TX,,,,SDMMC1_CK,DCMI_D9,,EVENTOUT From 58ea239510530b5ce80aa24cd084b43698309bb4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 7 Oct 2017 14:08:50 +0300 Subject: [PATCH 10/75] zephyr: Use CONFIG_NET_APP_SETTINGS to setup initial network addresses. Ideally, these should be configurable from Python (using network module), but as that doesn't exist, we better off using Zephyr's native bootstrap configuration facility. --- ports/zephyr/main.c | 5 ++++- ports/zephyr/prj_base.conf | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 7255d981f1..7eb9da3e12 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -60,7 +60,9 @@ static char *stack_top; static char heap[MICROPY_HEAP_SIZE]; void init_zephyr(void) { - // TODO: Make addresses configurable + // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap + // network addresses. +#if 0 #ifdef CONFIG_NETWORKING if (net_if_get_default() == NULL) { // If there's no default networking interface, @@ -81,6 +83,7 @@ void init_zephyr(void) { static struct in6_addr in6addr_my = {{{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}}; net_if_ipv6_addr_add(net_if_get_default(), &in6addr_my, NET_ADDR_MANUAL, 0); #endif +#endif } int real_main(void) { diff --git a/ports/zephyr/prj_base.conf b/ports/zephyr/prj_base.conf index c3ba2812f4..a4cc145137 100644 --- a/ports/zephyr/prj_base.conf +++ b/ports/zephyr/prj_base.conf @@ -18,6 +18,14 @@ CONFIG_NET_SOCKETS=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_NET_NBUF_RX_COUNT=5 +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_INIT_TIMEOUT=3 +CONFIG_NET_APP_NEED_IPV6=y +CONFIG_NET_APP_NEED_IPV4=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_APP_MY_IPV4_GW="192.0.2.2" + # DNS CONFIG_DNS_RESOLVER=y CONFIG_DNS_RESOLVER_ADDITIONAL_QUERIES=2 From 71c1a05d88c9ac84212d164458c5a501e59be389 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 7 Oct 2017 15:49:58 +0300 Subject: [PATCH 11/75] tests/run-tests: Close device under test using "finally". We want to close communication object even if there were exceptions somewhere in the code. This is important for --device exec:/execpty: which may otherwise leave processing running in the background. --- tests/run-tests | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/run-tests b/tests/run-tests index 23fc3d9102..6280a5182b 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -492,9 +492,12 @@ def main(): # we need to access feature_check's from the same directory as the # run-tests script itself. base_path = os.path.dirname(sys.argv[0]) or "." - res = run_tests(pyb, tests, args, base_path) - if pyb: - pyb.close() + try: + res = run_tests(pyb, tests, args, base_path) + finally: + if pyb: + pyb.close() + if not res: sys.exit(1) From 4514f073c18ab94b3f0f790ae813de61f38d367b Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 7 Oct 2017 17:09:53 +0300 Subject: [PATCH 12/75] zephyr: Switch to interrupt-driven pull-style console. While this console API improves handling on real hardware boards (e.g. clipboard paste is much more reliable, as well as programmatic communication), it vice-versa poses problems under QEMU, apparently because it doesn't emulate UART interrupt handling faithfully. That leads to inability to run the testsuite on QEMU at all. To work that around, we have to suuport both old and new console routines, and use the old ones under QEMU. --- ports/zephyr/prj_base.conf | 6 ++++++ ports/zephyr/prj_qemu_cortex_m3.conf | 4 ++++ ports/zephyr/prj_qemu_x86.conf | 4 ++++ ports/zephyr/src/zephyr_start.c | 5 +++++ ports/zephyr/uart_core.c | 14 ++++++++++++++ 5 files changed, 33 insertions(+) diff --git a/ports/zephyr/prj_base.conf b/ports/zephyr/prj_base.conf index a4cc145137..fbcedf2600 100644 --- a/ports/zephyr/prj_base.conf +++ b/ports/zephyr/prj_base.conf @@ -4,6 +4,12 @@ CONFIG_REBOOT=y CONFIG_STDOUT_CONSOLE=y CONFIG_CONSOLE_HANDLER=y CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y + +CONFIG_CONSOLE_PULL=y +CONFIG_CONSOLE_GETCHAR=y +CONFIG_CONSOLE_GETCHAR_BUFSIZE=128 +CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128 + CONFIG_NEWLIB_LIBC=y CONFIG_FLOAT=y CONFIG_MAIN_STACK_SIZE=4096 diff --git a/ports/zephyr/prj_qemu_cortex_m3.conf b/ports/zephyr/prj_qemu_cortex_m3.conf index 09614c3620..1ade981e21 100644 --- a/ports/zephyr/prj_qemu_cortex_m3.conf +++ b/ports/zephyr/prj_qemu_cortex_m3.conf @@ -1,3 +1,7 @@ +# Interrupt-driven UART console has emulation artifacts under QEMU, +# disable it +CONFIG_CONSOLE_PULL=n + # Networking drivers # SLIP driver for QEMU CONFIG_NET_SLIP_TAP=y diff --git a/ports/zephyr/prj_qemu_x86.conf b/ports/zephyr/prj_qemu_x86.conf index ef60cfec91..9bc81259a2 100644 --- a/ports/zephyr/prj_qemu_x86.conf +++ b/ports/zephyr/prj_qemu_x86.conf @@ -1,3 +1,7 @@ +# Interrupt-driven UART console has emulation artifacts under QEMU, +# disable it +CONFIG_CONSOLE_PULL=n + # Networking drivers # SLIP driver for QEMU CONFIG_NET_SLIP_TAP=y diff --git a/ports/zephyr/src/zephyr_start.c b/ports/zephyr/src/zephyr_start.c index 9e8a90bebd..452e304cad 100644 --- a/ports/zephyr/src/zephyr_start.c +++ b/ports/zephyr/src/zephyr_start.c @@ -24,11 +24,16 @@ * THE SOFTWARE. */ #include +#include #include "zephyr_getchar.h" int real_main(void); void main(void) { +#ifdef CONFIG_CONSOLE_PULL + console_init(); +#else zephyr_getchar_init(); +#endif real_main(); } diff --git a/ports/zephyr/uart_core.c b/ports/zephyr/uart_core.c index 1e85053cd2..e41fb9acce 100644 --- a/ports/zephyr/uart_core.c +++ b/ports/zephyr/uart_core.c @@ -28,6 +28,7 @@ #include "src/zephyr_getchar.h" // Zephyr headers #include +#include /* * Core UART functions to implement for a port @@ -35,11 +36,23 @@ // Receive single character int mp_hal_stdin_rx_chr(void) { +#ifdef CONFIG_CONSOLE_PULL + return console_getchar(); +#else return zephyr_getchar(); +#endif } // Send string of given length void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { +#ifdef CONFIG_CONSOLE_PULL + while (len--) { + char c = *str++; + while (console_putchar(c) == -1) { + k_sleep(1); + } + } +#else static struct device *uart_console_dev; if (uart_console_dev == NULL) { uart_console_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); @@ -48,4 +61,5 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { while (len--) { uart_poll_out(uart_console_dev, *str++); } +#endif } From c15be989ee195d375f93b39aa13bc4e07b36c1eb Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 8 Oct 2017 00:04:57 +0300 Subject: [PATCH 13/75] tools/pyboard: Update docstring for additional device support. --- tools/pyboard.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/pyboard.py b/tools/pyboard.py index 8874730718..16ee41f703 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -5,6 +5,7 @@ # The MIT License (MIT) # # Copyright (c) 2014-2016 Damien P. George +# Copyright (c) 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 @@ -28,7 +29,11 @@ pyboard interface This module provides the Pyboard class, used to communicate with and -control the pyboard over a serial USB connection. +control a MicroPython device over a communication channel. Both real +boards and emulated devices (e.g. running in QEMU) are supported. +Various communication channels are supported, including a serial +connection, telnet-style network connection, external process +connection. Example usage: From 53966fd9a8c1b7d8a75e8d1c479c8219c8d101d3 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 9 Oct 2017 00:22:30 +0300 Subject: [PATCH 14/75] examples: hwconfig_console: Add .on()/.off() methods. Add these methods to this "GPIO output emulated with console prints" config. --- examples/hwapi/hwconfig_console.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/hwapi/hwconfig_console.py b/examples/hwapi/hwconfig_console.py index 4b0b0d79bd..bbcc0e816e 100644 --- a/examples/hwapi/hwconfig_console.py +++ b/examples/hwapi/hwconfig_console.py @@ -8,6 +8,12 @@ class LEDClass: def value(self, v): print(self.id, v) + def on(self): + self.value(1) + + def off(self): + self.value(0) + LED = LEDClass(1) LED2 = LEDClass(12) From 6db132e1302dcca4671e0d67b872ca226c54682f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Oct 2017 15:27:21 +1100 Subject: [PATCH 15/75] esp8266/modnetwork: Add "bssid" keyword arg to WLAN.connect() method. --- ports/esp8266/modnetwork.c | 44 ++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/ports/esp8266/modnetwork.c b/ports/esp8266/modnetwork.c index 2c61e5dcdb..b41a11f596 100644 --- a/ports/esp8266/modnetwork.c +++ b/ports/esp8266/modnetwork.c @@ -93,29 +93,55 @@ STATIC mp_obj_t esp_active(size_t n_args, const mp_obj_t *args) { } 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) { - require_if(args[0], STATION_IF); +STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_ssid, ARG_password, ARG_bssid }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_bssid, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + // parse args + 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); + + require_if(pos_args[0], STATION_IF); struct station_config config = {{0}}; size_t len; const char *p; + bool set_config = false; - if (n_args > 1) { - p = mp_obj_str_get_data(args[1], &len); + // set parameters based on given args + if (args[ARG_ssid].u_obj != mp_const_none) { + p = mp_obj_str_get_data(args[ARG_ssid].u_obj, &len); len = MIN(len, sizeof(config.ssid)); memcpy(config.ssid, p, len); - if (n_args > 2) { - p = mp_obj_str_get_data(args[2], &len); - len = MIN(len, sizeof(config.password)); - memcpy(config.password, p, len); + set_config = true; + } + if (args[ARG_password].u_obj != mp_const_none) { + p = mp_obj_str_get_data(args[ARG_password].u_obj, &len); + len = MIN(len, sizeof(config.password)); + memcpy(config.password, p, len); + set_config = true; + } + if (args[ARG_bssid].u_obj != mp_const_none) { + p = mp_obj_str_get_data(args[ARG_bssid].u_obj, &len); + if (len != sizeof(config.bssid)) { + mp_raise_ValueError(NULL); } + config.bssid_set = 1; + memcpy(config.bssid, p, sizeof(config.bssid)); + set_config = true; + } + if (set_config) { error_check(wifi_station_set_config(&config), "Cannot set STA config"); } error_check(wifi_station_connect(), "Cannot connect to AP"); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_connect_obj, 1, 7, esp_connect); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_connect_obj, 1, esp_connect); STATIC mp_obj_t esp_disconnect(mp_obj_t self_in) { require_if(self_in, STATION_IF); From add933feaf188073de99cf54e4d251c48034472c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Oct 2017 15:27:50 +1100 Subject: [PATCH 16/75] docs/library/network: Clarify usage of "bssid" arg in connect() method. --- docs/library/network.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/library/network.rst b/docs/library/network.rst index def6bee74e..258b2a20c8 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -72,8 +72,7 @@ parameter should be `id`. connection parameters. For various medium types, there are different sets of predefined/recommended parameters, among them: - * WiFi: *bssid* keyword to connect by BSSID (MAC address) instead - of access point name + * WiFi: *bssid* keyword to connect to a specific BSSID (MAC address) .. method:: disconnect() @@ -333,9 +332,12 @@ parameter should be `id`. argument is passed. Otherwise, query current state if no argument is provided. Most other methods require active interface. - .. method:: wlan.connect(ssid, password) + .. method:: wlan.connect(ssid=None, password=None, \*, bssid=None) Connect to the specified wireless network, using the specified password. + If *bssid* is given then the connection will be restricted to the + access-point with that MAC address (the *ssid* must also be specified + in this case). .. method:: wlan.disconnect() From 933eab46fcf2dcca2c3ca3d04b714f238e6020e1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 10 Oct 2017 10:37:38 +1100 Subject: [PATCH 17/75] py/bc: Update opcode_format_table to match the bytecode. --- py/bc.c | 6 +++--- tools/mpy-tool.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/py/bc.c b/py/bc.c index 991b0cf267..89d8b74f91 100644 --- a/py/bc.c +++ b/py/bc.c @@ -321,7 +321,7 @@ STATIC const byte opcode_format_table[64] = { OC4(O, O, U, U), // 0x38-0x3b OC4(U, O, B, O), // 0x3c-0x3f OC4(O, B, B, O), // 0x40-0x43 - OC4(B, B, O, U), // 0x44-0x47 + OC4(B, B, O, B), // 0x44-0x47 OC4(U, U, U, U), // 0x48-0x4b OC4(U, U, U, U), // 0x4c-0x4f OC4(V, V, U, V), // 0x50-0x53 @@ -361,7 +361,7 @@ STATIC const byte opcode_format_table[64] = { OC4(B, B, B, B), // 0xcc-0xcf OC4(B, B, B, B), // 0xd0-0xd3 - OC4(B, B, B, B), // 0xd4-0xd7 + OC4(U, U, U, B), // 0xd4-0xd7 OC4(B, B, B, B), // 0xd8-0xdb OC4(B, B, B, B), // 0xdc-0xdf @@ -372,7 +372,7 @@ STATIC const byte opcode_format_table[64] = { OC4(B, B, B, B), // 0xf0-0xf3 OC4(B, B, B, B), // 0xf4-0xf7 - OC4(B, B, B, U), // 0xf8-0xfb + OC4(U, U, U, U), // 0xf8-0xfb OC4(U, U, U, U), // 0xfc-0xff }; #undef OC4 diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 5de4ecf1cc..ac7b2c1ccf 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -105,7 +105,7 @@ def make_opcode_format(): OC4(O, O, U, U), # 0x38-0x3b OC4(U, O, B, O), # 0x3c-0x3f OC4(O, B, B, O), # 0x40-0x43 - OC4(B, B, O, U), # 0x44-0x47 + OC4(B, B, O, B), # 0x44-0x47 OC4(U, U, U, U), # 0x48-0x4b OC4(U, U, U, U), # 0x4c-0x4f OC4(V, V, U, V), # 0x50-0x53 @@ -145,7 +145,7 @@ def make_opcode_format(): OC4(B, B, B, B), # 0xcc-0xcf OC4(B, B, B, B), # 0xd0-0xd3 - OC4(B, B, B, B), # 0xd4-0xd7 + OC4(U, U, U, B), # 0xd4-0xd7 OC4(B, B, B, B), # 0xd8-0xdb OC4(B, B, B, B), # 0xdc-0xdf @@ -156,7 +156,7 @@ def make_opcode_format(): OC4(B, B, B, B), # 0xf0-0xf3 OC4(B, B, B, B), # 0xf4-0xf7 - OC4(B, B, B, U), # 0xf8-0xfb + OC4(U, U, U, U), # 0xf8-0xfb OC4(U, U, U, U), # 0xfc-0xff )) From d236d0c415bc34c96e7155ab116258a026b2cda6 Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Fri, 6 Oct 2017 01:01:05 +1100 Subject: [PATCH 18/75] docs/pyboard/quickref: Add info for Switch, RTC, CAN, Accel classes. --- docs/pyboard/quickref.rst | 54 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/docs/pyboard/quickref.rst b/docs/pyboard/quickref.rst index 5690dddb0c..48798aad3e 100644 --- a/docs/pyboard/quickref.rst +++ b/docs/pyboard/quickref.rst @@ -39,17 +39,32 @@ Use the :mod:`time ` module:: start = time.ticks_ms() # get value of millisecond counter delta = time.ticks_diff(time.ticks_ms(), start) # compute time difference -LEDs ----- +Internal LEDs +------------- See :ref:`pyb.LED `. :: from pyb import LED - led = LED(1) # red led + led = LED(1) # 1=red, 2=green, 3=yellow, 4=blue led.toggle() led.on() led.off() + + # LEDs 3 and 4 support PWM intensity (0-255) + LED(4).intensity() # get intensity + LED(4).intensity(128) # set intensity to half + +Internal switch +--------------- + +See :ref:`pyb.Switch `. :: + + from pyb import Switch + + sw = Switch() + sw.value() # returns True or False + sw.callback(lambda: pyb.LED(1).toggle()) Pins and GPIO ------------- @@ -99,6 +114,17 @@ See :ref:`pyb.Timer `. :: tim.freq(0.5) # 0.5 Hz tim.callback(lambda t: pyb.LED(1).toggle()) +RTC (real time clock) +--------------------- + +See :ref:`pyb.RTC ` :: + + from pyb import RTC + + rtc = RTC() + rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime() # get date and time + PWM (pulse width modulation) ---------------------------- @@ -167,3 +193,25 @@ See :ref:`pyb.I2C `. :: i2c.recv(5, 0x42) # receive 5 bytes from slave i2c.mem_read(2, 0x42, 0x10) # read 2 bytes from slave 0x42, slave memory 0x10 i2c.mem_write('xy', 0x42, 0x10) # write 2 bytes to slave 0x42, slave memory 0x10 + +CAN bus (controller area network) +--------------------------------- + +See :ref:`pyb.CAN `. :: + + from pyb import CAN + + can = CAN(1, CAN.LOOPBACK) + can.setfilter(0, CAN.LIST16, 0, (123, 124, 125, 126)) + can.send('message!', 123) # send a message with id 123 + can.recv(0) # receive message on FIFO 0 + +Internal accelerometer +---------------------- + +See :ref:`pyb.Accel `. :: + + from pyb import Accel + + accel = Accel() + print(accel.x(), accel.y(), accel.z(), accel.tilt()) From dc92f1c4ee0924cff0b33b5061622f0621e71eac Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Fri, 6 Oct 2017 01:36:48 +1100 Subject: [PATCH 19/75] docs/pyboard/tutorial: Update now that yellow LED also supports PWM. --- docs/pyboard/tutorial/leds.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/pyboard/tutorial/leds.rst b/docs/pyboard/tutorial/leds.rst index 763eedf01c..6b05f5db05 100644 --- a/docs/pyboard/tutorial/leds.rst +++ b/docs/pyboard/tutorial/leds.rst @@ -60,10 +60,10 @@ One problem you might find is that if you stop the script and then start it agai for l in leds: l.off() -The Fourth Special LED ----------------------- +The Special LEDs +---------------- -The blue LED is special. As well as turning it on and off, you can control the intensity using the intensity() method. This takes a number between 0 and 255 that determines how bright it is. The following script makes the blue LED gradually brighter then turns it off again. :: +The yellow and blue LEDs are special. As well as turning them on and off, you can control their intensity using the intensity() method. This takes a number between 0 and 255 that determines how bright it is. The following script makes the blue LED gradually brighter then turns it off again. :: led = pyb.LED(4) intensity = 0 @@ -72,4 +72,4 @@ The blue LED is special. As well as turning it on and off, you can control the i led.intensity(intensity) pyb.delay(20) -You can call intensity() on the other LEDs but they can only be off or on. 0 sets them off and any other number up to 255 turns them on. +You can call intensity() on LEDs 1 and 2 but they can only be off or on. 0 sets them off and any other number up to 255 turns them on. From f599a380591df2a7bb466eae6a4144634409821b Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Fri, 6 Oct 2017 01:48:43 +1100 Subject: [PATCH 20/75] docs/esp8266/quickref: Add quickref info for RTC class. --- docs/esp8266/quickref.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index ccf6365c83..c510e40640 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -223,6 +223,17 @@ and is accessed via the :ref:`machine.I2C ` class:: buf = bytearray(10) # create a buffer with 10 bytes i2c.writeto(0x3a, buf) # write the given buffer to the slave +Real time clock (RTC) +--------------------- + +See :ref:`machine.RTC ` :: + + from machine import RTC + + rtc = RTC() + rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # set a specific date and time + rtc.datetime() # get date and time + Deep-sleep mode --------------- From 25e140652b946e9a136a16a9831c0b18f1285702 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 4 Oct 2017 21:00:05 +1100 Subject: [PATCH 21/75] py/modmath: Add full checks for math domain errors. This patch changes how most of the plain math functions are implemented: there are now two generic math wrapper functions that take a pointer to a math function (like sin, cos) and perform the necessary conversion to and from MicroPython types. This helps to reduce code size. The generic functions can also check for math domain errors in a generic way, by testing if the result is NaN or infinity combined with finite inputs. The result is that, with this patch, all math functions now have full domain error checking (even gamma and lgamma) and code size has decreased for most ports. Code size changes in bytes for those with the math module are: unix x64: -432 unix nanbox: -792 stm32: -88 esp8266: +12 Tests are also added to check domain errors are handled correctly. --- py/modmath.c | 67 ++++++++++++++++++++---------- tests/float/math_domain.py | 51 +++++++++++++++++++++++ tests/float/math_domain_special.py | 36 ++++++++++++++++ 3 files changed, 133 insertions(+), 21 deletions(-) create mode 100644 tests/float/math_domain.py create mode 100644 tests/float/math_domain_special.py diff --git a/py/modmath.c b/py/modmath.c index 4d627c67f4..6c248844df 100644 --- a/py/modmath.c +++ b/py/modmath.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 @@ -39,13 +39,30 @@ STATIC NORETURN void math_error(void) { mp_raise_ValueError("math domain error"); } -#define MATH_FUN_1(py_name, c_name) \ - STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj) { return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj))); } \ - STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_## py_name ## _obj, mp_math_ ## py_name); +STATIC mp_obj_t math_generic_1(mp_obj_t x_obj, mp_float_t (*f)(mp_float_t)) { + mp_float_t x = mp_obj_get_float(x_obj); + mp_float_t ans = f(x); + if ((isnan(ans) && !isnan(x)) || (isinf(ans) && !isinf(x))) { + math_error(); + } + return mp_obj_new_float(ans); +} -#define MATH_FUN_2(py_name, c_name) \ - STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj, mp_obj_t y_obj) { return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj), mp_obj_get_float(y_obj))); } \ - STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_math_## py_name ## _obj, mp_math_ ## py_name); +STATIC mp_obj_t math_generic_2(mp_obj_t x_obj, mp_obj_t y_obj, mp_float_t (*f)(mp_float_t, mp_float_t)) { + mp_float_t x = mp_obj_get_float(x_obj); + mp_float_t y = mp_obj_get_float(y_obj); + mp_float_t ans = f(x, y); + if ((isnan(ans) && !isnan(x) && !isnan(y)) || (isinf(ans) && !isinf(x))) { + math_error(); + } + return mp_obj_new_float(ans); +} + +#define MATH_FUN_1(py_name, c_name) \ + STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj) { \ + return math_generic_1(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \ + } \ + STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_## py_name ## _obj, mp_math_ ## py_name); #define MATH_FUN_1_TO_BOOL(py_name, c_name) \ STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj) { return mp_obj_new_bool(c_name(mp_obj_get_float(x_obj))); } \ @@ -55,15 +72,17 @@ STATIC NORETURN void math_error(void) { STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj) { return mp_obj_new_int_from_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj))); } \ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_## py_name ## _obj, mp_math_ ## py_name); -#define MATH_FUN_1_ERRCOND(py_name, c_name, error_condition) \ - STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj) { \ - mp_float_t x = mp_obj_get_float(x_obj); \ - if (error_condition) { \ - math_error(); \ - } \ - return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(x)); \ +#define MATH_FUN_2(py_name, c_name) \ + STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \ + return math_generic_2(x_obj, y_obj, MICROPY_FLOAT_C_FUN(c_name)); \ } \ - STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_## py_name ## _obj, mp_math_ ## py_name); + STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_math_## py_name ## _obj, mp_math_ ## py_name); + +#define MATH_FUN_2_FLT_INT(py_name, c_name) \ + STATIC mp_obj_t mp_math_ ## py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \ + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj), mp_obj_get_int(y_obj))); \ + } \ + STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_math_## py_name ## _obj, mp_math_ ## py_name); #if MP_NEED_LOG2 // 1.442695040888963407354163704 is 1/_M_LN2 @@ -71,7 +90,7 @@ STATIC NORETURN void math_error(void) { #endif // sqrt(x): returns the square root of x -MATH_FUN_1_ERRCOND(sqrt, sqrt, (x < (mp_float_t)0.0)) +MATH_FUN_1(sqrt, sqrt) // pow(x, y): returns x to the power of y MATH_FUN_2(pow, pow) // exp(x) @@ -80,9 +99,9 @@ MATH_FUN_1(exp, exp) // expm1(x) MATH_FUN_1(expm1, expm1) // log2(x) -MATH_FUN_1_ERRCOND(log2, log2, (x <= (mp_float_t)0.0)) +MATH_FUN_1(log2, log2) // log10(x) -MATH_FUN_1_ERRCOND(log10, log10, (x <= (mp_float_t)0.0)) +MATH_FUN_1(log10, log10) // cosh(x) MATH_FUN_1(cosh, cosh) // sinh(x) @@ -113,9 +132,15 @@ MATH_FUN_2(atan2, atan2) // ceil(x) MATH_FUN_1_TO_INT(ceil, ceil) // copysign(x, y) -MATH_FUN_2(copysign, copysign) +STATIC mp_float_t MICROPY_FLOAT_C_FUN(copysign_func)(mp_float_t x, mp_float_t y) { + return MICROPY_FLOAT_C_FUN(copysign)(x, y); +} +MATH_FUN_2(copysign, copysign_func) // fabs(x) -MATH_FUN_1(fabs, fabs) +STATIC mp_float_t MICROPY_FLOAT_C_FUN(fabs_func)(mp_float_t x) { + return MICROPY_FLOAT_C_FUN(fabs)(x); +} +MATH_FUN_1(fabs, fabs_func) // floor(x) MATH_FUN_1_TO_INT(floor, floor) //TODO: delegate to x.__floor__() if x is not a float // fmod(x, y) @@ -129,7 +154,7 @@ MATH_FUN_1_TO_BOOL(isnan, isnan) // trunc(x) MATH_FUN_1_TO_INT(trunc, trunc) // ldexp(x, exp) -MATH_FUN_2(ldexp, ldexp) +MATH_FUN_2_FLT_INT(ldexp, ldexp) #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS // erf(x): return the error function of x MATH_FUN_1(erf, erf) diff --git a/tests/float/math_domain.py b/tests/float/math_domain.py new file mode 100644 index 0000000000..0cf10fb2ad --- /dev/null +++ b/tests/float/math_domain.py @@ -0,0 +1,51 @@ +# Tests domain errors in math functions + +try: + import math +except ImportError: + print("SKIP") + raise SystemExit + +inf = float('inf') +nan = float('nan') + +# single argument functions +for name, f, args in ( + ('fabs', math.fabs, ()), + ('ceil', math.ceil, ()), + ('floor', math.floor, ()), + ('trunc', math.trunc, ()), + ('sqrt', math.sqrt, (-1, 0)), + ('exp', math.exp, ()), + ('sin', math.sin, ()), + ('cos', math.cos, ()), + ('tan', math.tan, ()), + ('asin', math.asin, (-1.1, 1, 1.1)), + ('acos', math.acos, (-1.1, 1, 1.1)), + ('atan', math.atan, ()), + ('ldexp', lambda x: math.ldexp(x, 0), ()), + ('radians', math.radians, ()), + ('degrees', math.degrees, ()), + ): + for x in args + (inf, nan): + try: + ans = f(x) + print('%.4f' % ans) + except ValueError: + print(name, 'ValueError') + except OverflowError: + print(name, 'OverflowError') + +# double argument functions +for name, f, args in ( + ('pow', math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))), + ('fmod', math.fmod, ((1.2, inf), (1.2, 0), (inf, 1.2))), + ('atan2', math.atan2, ((0, 0),)), + ('copysign', math.copysign, ()), + ): + for x in args + ((0, inf), (inf, 0), (inf, inf), (inf, nan), (nan, inf), (nan, nan)): + try: + ans = f(*x) + print('%.4f' % ans) + except ValueError: + print(name, 'ValueError') diff --git a/tests/float/math_domain_special.py b/tests/float/math_domain_special.py new file mode 100644 index 0000000000..388920350e --- /dev/null +++ b/tests/float/math_domain_special.py @@ -0,0 +1,36 @@ +# Tests domain errors in special math functions + +try: + import math + math.erf +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +inf = float('inf') +nan = float('nan') + +# single argument functions +for name, f, args in ( + ('expm1', math.exp, ()), + ('log2', math.log2, (-1, 0)), + ('log10', math.log10, (-1, 0)), + ('sinh', math.sinh, ()), + ('cosh', math.cosh, ()), + ('tanh', math.tanh, ()), + ('asinh', math.asinh, ()), + ('acosh', math.acosh, (-1, 0.9, 1)), + ('atanh', math.atanh, (-1, 1)), + ('erf', math.erf, ()), + ('erfc', math.erfc, ()), + ('gamma', math.gamma, (-2, -1, 0, 1)), + ('lgamma', math.lgamma, (-2, -1, 0, 1)), + ): + for x in args + (inf, nan): + try: + ans = f(x) + print('%.4f' % ans) + except ValueError: + print(name, 'ValueError') + except OverflowError: + print(name, 'OverflowError') From d8d4e4dfbe4ca76a1b0adf4f1938562a308c6d48 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 4 Oct 2017 21:46:38 +1100 Subject: [PATCH 22/75] py/modmath: Convert log2 macro into a function. So that a pointer to it can be passed as a pointer to math_generic_1. This patch also makes the function work for single and double precision floating point. --- py/modmath.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/py/modmath.c b/py/modmath.c index 6c248844df..7eda7594d9 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -85,8 +85,12 @@ STATIC mp_obj_t math_generic_2(mp_obj_t x_obj, mp_obj_t y_obj, mp_float_t (*f)(m STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_math_## py_name ## _obj, mp_math_ ## py_name); #if MP_NEED_LOG2 +#undef log2 +#undef log2f // 1.442695040888963407354163704 is 1/_M_LN2 -#define log2(x) (log(x) * 1.442695040888963407354163704) +mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) { + return MICROPY_FLOAT_C_FUN(log)(x) * MICROPY_FLOAT_CONST(1.442695040888963407354163704); +} #endif // sqrt(x): returns the square root of x From 81a06d2c9c3ce081043e1eb948b65014f1b1786a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 4 Oct 2017 21:48:14 +1100 Subject: [PATCH 23/75] lib/libm: Remove implementation of log2f, use MP_NEED_LOG2 instead. --- lib/libm/math.c | 10 ---------- ports/stm32/mpconfigport.h | 3 +++ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/libm/math.c b/lib/libm/math.c index 5e00740d10..6b65202cf0 100644 --- a/lib/libm/math.c +++ b/lib/libm/math.c @@ -48,16 +48,6 @@ float copysignf(float x, float y) { } #endif -// some compilers define log2f in terms of logf -#ifdef log2f -#undef log2f -#endif -// some compilers have _M_LN2 defined in math.h, some don't -#ifndef _M_LN2 -#define _M_LN2 (0.69314718055994530942) -#endif -float log2f(float x) { return logf(x) / (float)_M_LN2; } - static const float _M_LN10 = 2.30258509299404; // 0x40135d8e float log10f(float x) { return logf(x) / (float)_M_LN10; } diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 7888b5355d..06606e62a1 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -335,6 +335,9 @@ static inline mp_uint_t disable_irq(void) { } while (0); #endif +// We need an implementation of the log2 function which is not a macro +#define MP_NEED_LOG2 (1) + // There is no classical C heap in bare-metal ports, only Python // garbage-collected heap. For completeness, emulate C heap via // GC heap. Note that MicroPython core never uses malloc() and friends, From 08a196697c5dfb8cbe3b3e85c1c5f94e3a27804c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 4 Oct 2017 23:15:55 +1100 Subject: [PATCH 24/75] py/formatfloat: Don't print the negative sign of a NaN value. NaN may have the sign bit set but it has no meaning, so don't print it out. --- py/formatfloat.c | 4 ++-- tests/float/complex1.py | 1 + tests/float/float1.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/py/formatfloat.c b/py/formatfloat.c index 35cd5d51af..b61a958a25 100644 --- a/py/formatfloat.c +++ b/py/formatfloat.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "py/formatfloat.h" /*********************************************************************** @@ -82,7 +83,6 @@ static inline int fp_isless1(float x) { union floatbits fb = {x}; return fb.u < #define FPROUND_TO_ONE 0.999999999995 #define FPDECEXP 256 #define FPMIN_BUF_SIZE 7 // +9e+199 -#include #define fp_signbit(x) signbit(x) #define fp_isspecial(x) 1 #define fp_isnan(x) isnan(x) @@ -122,7 +122,7 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch } return buf_size >= 2; } - if (fp_signbit(f)) { + if (fp_signbit(f) && !isnan(f)) { *s++ = '-'; f = -f; } else { diff --git a/tests/float/complex1.py b/tests/float/complex1.py index 8544105453..479b4b3485 100644 --- a/tests/float/complex1.py +++ b/tests/float/complex1.py @@ -59,6 +59,7 @@ ans = (-1.2) ** -3.4; print("%.5g %.5g" % (ans.real, ans.imag)) # check printing of inf/nan print(float('nan') * 1j) +print(float('-nan') * 1j) print(float('inf') * (1 + 1j)) print(float('-inf') * (1 + 1j)) diff --git a/tests/float/float1.py b/tests/float/float1.py index 137dacc233..c64f965a7d 100644 --- a/tests/float/float1.py +++ b/tests/float/float1.py @@ -21,6 +21,7 @@ print(float("INF")) print(float("infinity")) print(float("INFINITY")) print(float("nan")) +print(float("-nan")) print(float("NaN")) try: float("") From dc948e4d54eb8f7fecbd26efcfb3cd5d02bf8500 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 10 Oct 2017 16:27:54 +1100 Subject: [PATCH 25/75] py/formatfloat: Use standard isinf, isnan funcs instead of custom ones. Reduces code size by a tiny bit. --- py/formatfloat.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/py/formatfloat.c b/py/formatfloat.c index b61a958a25..4228f99ff5 100644 --- a/py/formatfloat.c +++ b/py/formatfloat.c @@ -69,12 +69,10 @@ union floatbits { uint32_t u; }; static inline int fp_signbit(float x) { union floatbits fb = {x}; return fb.u & FLT_SIGN_MASK; } -static inline int fp_isspecial(float x) { union floatbits fb = {x}; return (fb.u & FLT_EXP_MASK) == FLT_EXP_MASK; } -static inline int fp_isinf(float x) { union floatbits fb = {x}; return (fb.u & FLT_MAN_MASK) == 0; } +#define fp_isnan(x) isnan(x) +#define fp_isinf(x) isinf(x) static inline int fp_iszero(float x) { union floatbits fb = {x}; return fb.u == 0; } static inline int fp_isless1(float x) { union floatbits fb = {x}; return fb.u < 0x3f800000; } -// Assumes both fp_isspecial() and fp_isinf() were applied before -#define fp_isnan(x) 1 #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE @@ -84,7 +82,6 @@ static inline int fp_isless1(float x) { union floatbits fb = {x}; return fb.u < #define FPDECEXP 256 #define FPMIN_BUF_SIZE 7 // +9e+199 #define fp_signbit(x) signbit(x) -#define fp_isspecial(x) 1 #define fp_isnan(x) isnan(x) #define fp_isinf(x) isinf(x) #define fp_iszero(x) (x == 0) @@ -122,7 +119,7 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch } return buf_size >= 2; } - if (fp_signbit(f) && !isnan(f)) { + if (fp_signbit(f) && !fp_isnan(f)) { *s++ = '-'; f = -f; } else { @@ -135,7 +132,7 @@ int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, ch // It is buf_size minus room for the sign and null byte. int buf_remaining = buf_size - 1 - (s - buf); - if (fp_isspecial(f)) { + { char uc = fmt & 0x20; if (fp_isinf(f)) { *s++ = 'I' ^ uc; From 69da74e53830412cf88ff577aa7d3cbf5741eb83 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 11 Oct 2017 11:25:20 +1100 Subject: [PATCH 26/75] py/modbuiltins: Use existing utf8_get_char helper in builtin ord func. --- py/modbuiltins.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/py/modbuiltins.c b/py/modbuiltins.c index 82b08cdc91..65c03d5231 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -348,31 +348,16 @@ STATIC mp_obj_t mp_builtin_ord(mp_obj_t o_in) { if (MP_OBJ_IS_STR(o_in)) { len = unichar_charlen(str, len); if (len == 1) { - if (!UTF8_IS_NONASCII(*str)) { - goto return_first_byte; - } - mp_int_t ord = *str++ & 0x7F; - for (mp_int_t mask = 0x40; ord & mask; mask >>= 1) { - ord &= ~mask; - } - while (UTF8_IS_CONT(*str)) { - ord = (ord << 6) | (*str++ & 0x3F); - } - return mp_obj_new_int(ord); + return mp_obj_new_int(utf8_get_char((const byte*)str)); } - } else { - // a bytes object + } else + #endif + { + // a bytes object, or a str without unicode support (don't sign extend the char) if (len == 1) { - return_first_byte: return MP_OBJ_NEW_SMALL_INT(((const byte*)str)[0]); } } - #else - if (len == 1) { - // don't sign extend when converting to ord - return mp_obj_new_int(((const byte*)str)[0]); - } - #endif if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_TypeError("ord expects a character"); From b1457db002a7205727f49c48f56ed270e2568d6a Mon Sep 17 00:00:00 2001 From: Mike Causer Date: Wed, 11 Oct 2017 01:24:44 +1100 Subject: [PATCH 27/75] docs/library: Add missing cross-ref links for classes in pyb module. --- docs/library/pyb.Accel.rst | 1 + docs/library/pyb.CAN.rst | 1 + docs/library/pyb.LCD.rst | 1 + docs/library/pyb.Switch.rst | 1 + docs/library/pyb.USB_HID.rst | 1 + docs/library/pyb.USB_VCP.rst | 1 + 6 files changed, 6 insertions(+) diff --git a/docs/library/pyb.Accel.rst b/docs/library/pyb.Accel.rst index 061996485f..9ade5c5c81 100644 --- a/docs/library/pyb.Accel.rst +++ b/docs/library/pyb.Accel.rst @@ -1,4 +1,5 @@ .. currentmodule:: pyb +.. _pyb.Accel: class Accel -- accelerometer control ==================================== diff --git a/docs/library/pyb.CAN.rst b/docs/library/pyb.CAN.rst index 9e71f12b06..232d04d962 100644 --- a/docs/library/pyb.CAN.rst +++ b/docs/library/pyb.CAN.rst @@ -1,4 +1,5 @@ .. currentmodule:: pyb +.. _pyb.CAN: class CAN -- controller area network communication bus ====================================================== diff --git a/docs/library/pyb.LCD.rst b/docs/library/pyb.LCD.rst index 83cf890b63..5ab127edcd 100644 --- a/docs/library/pyb.LCD.rst +++ b/docs/library/pyb.LCD.rst @@ -1,4 +1,5 @@ .. currentmodule:: pyb +.. _pyb.LCD: class LCD -- LCD control for the LCD touch-sensor pyskin ======================================================== diff --git a/docs/library/pyb.Switch.rst b/docs/library/pyb.Switch.rst index 0d5dc63b74..e5ab6bd844 100644 --- a/docs/library/pyb.Switch.rst +++ b/docs/library/pyb.Switch.rst @@ -1,4 +1,5 @@ .. currentmodule:: pyb +.. _pyb.Switch: class Switch -- switch object ============================= diff --git a/docs/library/pyb.USB_HID.rst b/docs/library/pyb.USB_HID.rst index 7d17c3099f..7027044354 100644 --- a/docs/library/pyb.USB_HID.rst +++ b/docs/library/pyb.USB_HID.rst @@ -1,4 +1,5 @@ .. currentmodule:: pyb +.. _pyb.USB_HID: class USB_HID -- USB Human Interface Device (HID) ================================================= diff --git a/docs/library/pyb.USB_VCP.rst b/docs/library/pyb.USB_VCP.rst index 4c4fe45168..80cc40cdde 100644 --- a/docs/library/pyb.USB_VCP.rst +++ b/docs/library/pyb.USB_VCP.rst @@ -1,4 +1,5 @@ .. currentmodule:: pyb +.. _pyb.USB_VCP: class USB_VCP -- USB virtual comm port ====================================== From 1b7d6a795149588eb44c1b33dd7c34fe6669460a Mon Sep 17 00:00:00 2001 From: Vitor Massaru Iha Date: Sun, 8 Oct 2017 15:28:32 -0300 Subject: [PATCH 28/75] esp8266/modules/webrepl_setup: Add info about allowed password length. This patch also makes the code more concise by combining the checks for the password length. --- ports/esp8266/modules/webrepl_setup.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ports/esp8266/modules/webrepl_setup.py b/ports/esp8266/modules/webrepl_setup.py index d91600e6ec..5288c49c0c 100644 --- a/ports/esp8266/modules/webrepl_setup.py +++ b/ports/esp8266/modules/webrepl_setup.py @@ -17,12 +17,9 @@ def getpass(prompt): def input_pass(): while 1: - passwd1 = getpass("New password: ") - if len(passwd1) < 4: - print("Password too short") - continue - elif len(passwd1) > 9: - print("Password too long") + passwd1 = getpass("New password (4-9 chars): ") + if len(passwd1) < 4 or len(passwd1) > 9: + print("Invalid password length") continue passwd2 = getpass("Confirm password: ") if passwd1 == passwd2: From a3afa8cfc46913f471d5eb55da4ae22dee92c25f Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 11 Oct 2017 18:54:34 +1100 Subject: [PATCH 29/75] py/emitnative: Implement floor-division and modulo for viper emitter. --- py/emitnative.c | 16 ++++++++++++ py/nativeglue.c | 3 +++ py/runtime0.h | 2 ++ tests/micropython/viper_binop_divmod.py | 18 +++++++++++++ tests/micropython/viper_binop_divmod.py.exp | 28 +++++++++++++++++++++ 5 files changed, 67 insertions(+) create mode 100644 tests/micropython/viper_binop_divmod.py create mode 100644 tests/micropython/viper_binop_divmod.py.exp diff --git a/py/emitnative.c b/py/emitnative.c index b2c9a73668..e576f767c9 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -123,6 +123,8 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { [MP_F_NEW_CELL] = 1, [MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3, [MP_F_SETUP_CODE_STATE] = 5, + [MP_F_SMALL_INT_FLOOR_DIVIDE] = 2, + [MP_F_SMALL_INT_MODULO] = 2, }; #include "py/asmx86.h" @@ -1843,6 +1845,20 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { return; } #endif + + // special cases for floor-divide and module because we dispatch to helper functions + if (op == MP_BINARY_OP_FLOOR_DIVIDE || op == MP_BINARY_OP_INPLACE_FLOOR_DIVIDE + || op == MP_BINARY_OP_MODULO || op == MP_BINARY_OP_INPLACE_MODULO) { + emit_pre_pop_reg_reg(emit, &vtype_rhs, REG_ARG_2, &vtype_lhs, REG_ARG_1); + if (op == MP_BINARY_OP_FLOOR_DIVIDE || op == MP_BINARY_OP_INPLACE_FLOOR_DIVIDE) { + emit_call(emit, MP_F_SMALL_INT_FLOOR_DIVIDE); + } else { + emit_call(emit, MP_F_SMALL_INT_MODULO); + } + emit_post_push_reg(emit, VTYPE_INT, REG_RET); + return; + } + int reg_rhs = REG_ARG_3; emit_pre_pop_reg_flexible(emit, &vtype_rhs, ®_rhs, REG_RET, REG_ARG_2); emit_pre_pop_reg(emit, &vtype_lhs, REG_ARG_2); diff --git a/py/nativeglue.c b/py/nativeglue.c index 61b624ec71..e63c2fcda6 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -29,6 +29,7 @@ #include #include "py/runtime.h" +#include "py/smallint.h" #include "py/emitglue.h" #include "py/bc.h" @@ -170,6 +171,8 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = { mp_obj_new_cell, mp_make_closure_from_raw_code, mp_setup_code_state, + mp_small_int_floor_divide, + mp_small_int_modulo, }; /* diff --git a/py/runtime0.h b/py/runtime0.h index 3edd8918b8..a72b7feb7a 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -185,6 +185,8 @@ typedef enum { MP_F_NEW_CELL, MP_F_MAKE_CLOSURE_FROM_RAW_CODE, MP_F_SETUP_CODE_STATE, + MP_F_SMALL_INT_FLOOR_DIVIDE, + MP_F_SMALL_INT_MODULO, MP_F_NUMBER_OF, } mp_fun_kind_t; diff --git a/tests/micropython/viper_binop_divmod.py b/tests/micropython/viper_binop_divmod.py new file mode 100644 index 0000000000..822424982a --- /dev/null +++ b/tests/micropython/viper_binop_divmod.py @@ -0,0 +1,18 @@ +# test floor-division and modulo operators + +@micropython.viper +def div(x:int, y:int) -> int: + return x // y + +@micropython.viper +def mod(x:int, y:int) -> int: + return x % y + +def dm(x, y): + print(div(x, y), mod(x, y)) + +for x in (-6, 6): + for y in range(-7, 8): + if y == 0: + continue + dm(x, y) diff --git a/tests/micropython/viper_binop_divmod.py.exp b/tests/micropython/viper_binop_divmod.py.exp new file mode 100644 index 0000000000..4fc971d461 --- /dev/null +++ b/tests/micropython/viper_binop_divmod.py.exp @@ -0,0 +1,28 @@ +0 -6 +1 0 +1 -1 +1 -2 +2 0 +3 0 +6 0 +-6 0 +-3 0 +-2 0 +-2 2 +-2 4 +-1 0 +-1 1 +-1 -1 +-1 0 +-2 -4 +-2 -2 +-2 0 +-3 0 +-6 0 +6 0 +3 0 +2 0 +1 2 +1 1 +1 0 +0 6 From c59fc1419d5760d7be0bcad1ebdfb70b43fe7093 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 12 Oct 2017 12:26:49 +1100 Subject: [PATCH 30/75] py/emitnative: Simplify binary op emitter, no need to check inplace ops. --- py/emitnative.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/py/emitnative.c b/py/emitnative.c index e576f767c9..8e97dda119 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1825,18 +1825,20 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { vtype_kind_t vtype_lhs = peek_vtype(emit, 1); vtype_kind_t vtype_rhs = peek_vtype(emit, 0); if (vtype_lhs == VTYPE_INT && vtype_rhs == VTYPE_INT) { + // for integers, inplace and normal ops are equivalent, so use just normal ops + if (MP_BINARY_OP_INPLACE_OR <= op && op <= MP_BINARY_OP_INPLACE_POWER) { + op += MP_BINARY_OP_OR - MP_BINARY_OP_INPLACE_OR; + } + #if N_X64 || N_X86 // special cases for x86 and shifting - if (op == MP_BINARY_OP_LSHIFT - || op == MP_BINARY_OP_INPLACE_LSHIFT - || op == MP_BINARY_OP_RSHIFT - || op == MP_BINARY_OP_INPLACE_RSHIFT) { + if (op == MP_BINARY_OP_LSHIFT || op == MP_BINARY_OP_RSHIFT) { #if N_X64 emit_pre_pop_reg_reg(emit, &vtype_rhs, ASM_X64_REG_RCX, &vtype_lhs, REG_RET); #else emit_pre_pop_reg_reg(emit, &vtype_rhs, ASM_X86_REG_ECX, &vtype_lhs, REG_RET); #endif - if (op == MP_BINARY_OP_LSHIFT || op == MP_BINARY_OP_INPLACE_LSHIFT) { + if (op == MP_BINARY_OP_LSHIFT) { ASM_LSL_REG(emit->as, REG_RET); } else { ASM_ASR_REG(emit->as, REG_RET); @@ -1847,10 +1849,9 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { #endif // special cases for floor-divide and module because we dispatch to helper functions - if (op == MP_BINARY_OP_FLOOR_DIVIDE || op == MP_BINARY_OP_INPLACE_FLOOR_DIVIDE - || op == MP_BINARY_OP_MODULO || op == MP_BINARY_OP_INPLACE_MODULO) { + if (op == MP_BINARY_OP_FLOOR_DIVIDE || op == MP_BINARY_OP_MODULO) { emit_pre_pop_reg_reg(emit, &vtype_rhs, REG_ARG_2, &vtype_lhs, REG_ARG_1); - if (op == MP_BINARY_OP_FLOOR_DIVIDE || op == MP_BINARY_OP_INPLACE_FLOOR_DIVIDE) { + if (op == MP_BINARY_OP_FLOOR_DIVIDE) { emit_call(emit, MP_F_SMALL_INT_FLOOR_DIVIDE); } else { emit_call(emit, MP_F_SMALL_INT_MODULO); @@ -1865,29 +1866,29 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { if (0) { // dummy #if !(N_X64 || N_X86) - } else if (op == MP_BINARY_OP_LSHIFT || op == MP_BINARY_OP_INPLACE_LSHIFT) { + } else if (op == MP_BINARY_OP_LSHIFT) { ASM_LSL_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); - } else if (op == MP_BINARY_OP_RSHIFT || op == MP_BINARY_OP_INPLACE_RSHIFT) { + } else if (op == MP_BINARY_OP_RSHIFT) { ASM_ASR_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); #endif - } else if (op == MP_BINARY_OP_OR || op == MP_BINARY_OP_INPLACE_OR) { + } else if (op == MP_BINARY_OP_OR) { ASM_OR_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); - } else if (op == MP_BINARY_OP_XOR || op == MP_BINARY_OP_INPLACE_XOR) { + } else if (op == MP_BINARY_OP_XOR) { ASM_XOR_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); - } else if (op == MP_BINARY_OP_AND || op == MP_BINARY_OP_INPLACE_AND) { + } else if (op == MP_BINARY_OP_AND) { ASM_AND_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); - } else if (op == MP_BINARY_OP_ADD || op == MP_BINARY_OP_INPLACE_ADD) { + } else if (op == MP_BINARY_OP_ADD) { ASM_ADD_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); - } else if (op == MP_BINARY_OP_SUBTRACT || op == MP_BINARY_OP_INPLACE_SUBTRACT) { + } else if (op == MP_BINARY_OP_SUBTRACT) { ASM_SUB_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); - } else if (op == MP_BINARY_OP_MULTIPLY || op == MP_BINARY_OP_INPLACE_MULTIPLY) { + } else if (op == MP_BINARY_OP_MULTIPLY) { ASM_MUL_REG_REG(emit->as, REG_ARG_2, reg_rhs); emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2); } else if (MP_BINARY_OP_LESS <= op && op <= MP_BINARY_OP_NOT_EQUAL) { From 7c7c7b161df5be99aee9dea444b6e4beacf78438 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 13 Oct 2017 12:00:47 +1100 Subject: [PATCH 31/75] stm32/usbd_cdc_interface: Don't reset CDC output buf on initialisation. So that characters can be buffered before the USB device is connected (restoring behviour of the driver before recent state refactoring). --- ports/stm32/usbd_cdc_interface.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index 4c49c9321f..2e9fba917c 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -57,10 +57,14 @@ #define CDC_SEND_BREAK 0x23 uint8_t *usbd_cdc_init(usbd_cdc_itf_t *cdc, usbd_cdc_msc_hid_state_t *usbd) { + // Link the parent state cdc->usbd = usbd; + + // Reset all the CDC state + // Note: we don't reset tx_buf_ptr_in in order to allow the output buffer to + // be filled (by usbd_cdc_tx_always) before the USB device is connected. cdc->rx_buf_put = 0; cdc->rx_buf_get = 0; - cdc->tx_buf_ptr_in = 0; cdc->tx_buf_ptr_out = 0; cdc->tx_buf_ptr_out_shadow = 0; cdc->tx_buf_ptr_wait_count = 0; From e39fcda8eb1bfc1ccc1660079189f9ae39392abe Mon Sep 17 00:00:00 2001 From: Li Weiwei Date: Fri, 13 Oct 2017 09:14:07 +0800 Subject: [PATCH 32/75] stm32/usbd_cdc_interface.h: Fix code comments after recent refactor. --- ports/stm32/usbd_cdc_interface.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/stm32/usbd_cdc_interface.h b/ports/stm32/usbd_cdc_interface.h index 6dcab7d479..98b8fc077d 100644 --- a/ports/stm32/usbd_cdc_interface.h +++ b/ports/stm32/usbd_cdc_interface.h @@ -43,8 +43,8 @@ typedef struct _usbd_cdc_itf_t { uint16_t rx_buf_get; // circular buffer index uint8_t tx_buf[USBD_CDC_TX_DATA_SIZE]; // data for USB IN endpoind is stored in this buffer - uint16_t tx_buf_ptr_in; // increment this pointer modulo APP_TX_DATA_SIZE when new data is available - volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo APP_TX_DATA_SIZE when data is drained + uint16_t tx_buf_ptr_in; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when new data is available + volatile uint16_t tx_buf_ptr_out; // increment this pointer modulo USBD_CDC_TX_DATA_SIZE when data is drained uint16_t tx_buf_ptr_out_shadow; // shadow of above uint8_t tx_buf_ptr_wait_count; // used to implement a timeout waiting for low-level USB driver uint8_t tx_need_empty_packet; // used to flush the USB IN endpoint if the last packet was exactly the endpoint packet size From 37282f8fc135a16ff36e2afe0de907cbde531ed0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 13 Oct 2017 20:01:57 +1100 Subject: [PATCH 33/75] extmod/uos_dupterm: Update uos.dupterm() and helper funcs to have index. The uos.dupterm() signature and behaviour is updated to reflect the latest enhancements in the docs. It has minor backwards incompatibility in that it no longer accepts zero arguments. The dupterm_rx helper function is moved from esp8266 to extmod and generalised to support multiple dupterm slots. A port can specify multiple slots by defining the MICROPY_PY_OS_DUPTERM config macro to an integer, being the number of slots it wants to have; 0 means to disable the dupterm feature altogether. The unix and esp8266 ports are updated to work with the new interface and are otherwise unchanged with respect to functionality. --- extmod/misc.h | 3 +- extmod/uos_dupterm.c | 100 +++++++++++++++++++++++-------- ports/esp8266/esp_mphal.c | 37 +----------- ports/esp8266/main.c | 2 - ports/esp8266/modules/webrepl.py | 4 +- ports/unix/unix_mphal.c | 17 +++--- py/mpstate.h | 2 +- py/runtime.c | 7 +++ 8 files changed, 97 insertions(+), 75 deletions(-) diff --git a/extmod/misc.h b/extmod/misc.h index 6c13592c75..d6f6d859c6 100644 --- a/extmod/misc.h +++ b/extmod/misc.h @@ -35,8 +35,9 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj); #if MICROPY_PY_OS_DUPTERM +int mp_uos_dupterm_rx_chr(void); void mp_uos_dupterm_tx_strn(const char *str, size_t len); -void mp_uos_deactivate(const char *msg, mp_obj_t exc); +void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc); #else #define mp_uos_dupterm_tx_strn(s, l) #endif diff --git a/extmod/uos_dupterm.c b/extmod/uos_dupterm.c index 1d6f02dcea..d4326d3265 100644 --- a/extmod/uos_dupterm.c +++ b/extmod/uos_dupterm.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2016 Paul Sokolovsky + * 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 @@ -31,12 +32,13 @@ #include "py/objtuple.h" #include "py/objarray.h" #include "py/stream.h" +#include "lib/utils/interrupt_char.h" #if MICROPY_PY_OS_DUPTERM -void mp_uos_deactivate(const char *msg, mp_obj_t exc) { - mp_obj_t term = MP_STATE_PORT(term_obj); - MP_STATE_PORT(term_obj) = NULL; +void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) { + mp_obj_t term = MP_STATE_VM(dupterm_objs[dupterm_idx]); + MP_STATE_VM(dupterm_objs[dupterm_idx]) = MP_OBJ_NULL; mp_printf(&mp_plat_print, msg); if (exc != MP_OBJ_NULL) { mp_obj_print_exception(&mp_plat_print, exc); @@ -44,48 +46,94 @@ void mp_uos_deactivate(const char *msg, mp_obj_t exc) { mp_stream_close(term); } +int mp_uos_dupterm_rx_chr(void) { + for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { + if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { + continue; + } + + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t readinto_m[3]; + mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_readinto, readinto_m); + readinto_m[2] = MP_STATE_VM(dupterm_arr_obj); + mp_obj_t res = mp_call_method_n_kw(1, 0, readinto_m); + if (res == mp_const_none) { + nlr_pop(); + } else if (res == MP_OBJ_NEW_SMALL_INT(0)) { + mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL); + nlr_pop(); + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(MP_STATE_VM(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ); + nlr_pop(); + if (*(byte*)bufinfo.buf == mp_interrupt_char) { + // Signal keyboard interrupt to be raised as soon as the VM resumes + mp_keyboard_interrupt(); + return -2; + } + return *(byte*)bufinfo.buf; + } + } else { + mp_uos_deactivate(idx, "dupterm: Exception in read() method, deactivating: ", nlr.ret_val); + } + } + + // No chars available + return -1; +} + void mp_uos_dupterm_tx_strn(const char *str, size_t len) { - if (MP_STATE_PORT(term_obj) != MP_OBJ_NULL) { + for (size_t idx = 0; idx < MICROPY_PY_OS_DUPTERM; ++idx) { + if (MP_STATE_VM(dupterm_objs[idx]) == MP_OBJ_NULL) { + continue; + } nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t write_m[3]; - mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_write, write_m); + mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_write, write_m); - mp_obj_array_t *arr = MP_OBJ_TO_PTR(MP_STATE_PORT(dupterm_arr_obj)); + mp_obj_array_t *arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj)); void *org_items = arr->items; arr->items = (void*)str; arr->len = len; - write_m[2] = MP_STATE_PORT(dupterm_arr_obj); + write_m[2] = MP_STATE_VM(dupterm_arr_obj); mp_call_method_n_kw(1, 0, write_m); - arr = MP_OBJ_TO_PTR(MP_STATE_PORT(dupterm_arr_obj)); + arr = MP_OBJ_TO_PTR(MP_STATE_VM(dupterm_arr_obj)); arr->items = org_items; arr->len = 1; nlr_pop(); } else { - mp_uos_deactivate("dupterm: Exception in write() method, deactivating: ", nlr.ret_val); + mp_uos_deactivate(idx, "dupterm: Exception in write() method, deactivating: ", nlr.ret_val); } } } STATIC mp_obj_t mp_uos_dupterm(size_t n_args, const mp_obj_t *args) { - if (n_args == 0) { - if (MP_STATE_PORT(term_obj) == MP_OBJ_NULL) { - return mp_const_none; - } else { - return MP_STATE_PORT(term_obj); - } - } else { - if (args[0] == mp_const_none) { - MP_STATE_PORT(term_obj) = MP_OBJ_NULL; - } else { - MP_STATE_PORT(term_obj) = args[0]; - if (MP_STATE_PORT(dupterm_arr_obj) == MP_OBJ_NULL) { - MP_STATE_PORT(dupterm_arr_obj) = mp_obj_new_bytearray(1, ""); - } - } - return mp_const_none; + mp_int_t idx = 0; + if (n_args == 2) { + idx = mp_obj_get_int(args[1]); } + + if (idx < 0 || idx >= MICROPY_PY_OS_DUPTERM) { + mp_raise_ValueError("invalid dupterm index"); + } + + mp_obj_t previous_obj = MP_STATE_VM(dupterm_objs[idx]); + if (previous_obj == MP_OBJ_NULL) { + previous_obj = mp_const_none; + } + if (args[0] == mp_const_none) { + MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL; + } else { + MP_STATE_VM(dupterm_objs[idx]) = args[0]; + if (MP_STATE_VM(dupterm_arr_obj) == MP_OBJ_NULL) { + MP_STATE_VM(dupterm_arr_obj) = mp_obj_new_bytearray(1, ""); + } + } + + return previous_obj; } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 0, 1, mp_uos_dupterm); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_uos_dupterm_obj, 1, 2, mp_uos_dupterm); #endif diff --git a/ports/esp8266/esp_mphal.c b/ports/esp8266/esp_mphal.c index 71d4c50628..9f4f051fdd 100644 --- a/ports/esp8266/esp_mphal.c +++ b/ports/esp8266/esp_mphal.c @@ -155,41 +155,6 @@ void mp_hal_signal_input(void) { #endif } -static int call_dupterm_read(void) { - if (MP_STATE_PORT(term_obj) == NULL) { - return -1; - } - - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_obj_t readinto_m[3]; - mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_readinto, readinto_m); - readinto_m[2] = MP_STATE_PORT(dupterm_arr_obj); - mp_obj_t res = mp_call_method_n_kw(1, 0, readinto_m); - if (res == mp_const_none) { - nlr_pop(); - return -2; - } - if (res == MP_OBJ_NEW_SMALL_INT(0)) { - mp_uos_deactivate("dupterm: EOF received, deactivating\n", MP_OBJ_NULL); - nlr_pop(); - return -1; - } - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(MP_STATE_PORT(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ); - nlr_pop(); - if (*(byte*)bufinfo.buf == mp_interrupt_char) { - mp_keyboard_interrupt(); - return -2; - } - return *(byte*)bufinfo.buf; - } else { - mp_uos_deactivate("dupterm: Exception in read() method, deactivating: ", nlr.ret_val); - } - - return -1; -} - STATIC void dupterm_task_handler(os_event_t *evt) { static byte lock; if (lock) { @@ -197,7 +162,7 @@ STATIC void dupterm_task_handler(os_event_t *evt) { } lock = 1; while (1) { - int c = call_dupterm_read(); + int c = mp_uos_dupterm_rx_chr(); if (c < 0) { break; } diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 7f5dca84e2..d1b88a8cec 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -51,8 +51,6 @@ STATIC void mp_reset(void) { mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_)); mp_obj_list_init(mp_sys_argv, 0); - MP_STATE_PORT(term_obj) = MP_OBJ_NULL; - MP_STATE_PORT(dupterm_arr_obj) = MP_OBJ_NULL; #if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA extern void esp_native_code_init(void); esp_native_code_init(); diff --git a/ports/esp8266/modules/webrepl.py b/ports/esp8266/modules/webrepl.py index 5a76e9b26d..aa156d1487 100644 --- a/ports/esp8266/modules/webrepl.py +++ b/ports/esp8266/modules/webrepl.py @@ -31,7 +31,9 @@ def setup_conn(port, accept_handler): def accept_conn(listen_sock): global client_s cl, remote_addr = listen_sock.accept() - if uos.dupterm(): + prev = uos.dupterm(None) + uos.dupterm(prev) + if prev: print("\nConcurrent WebREPL connection from", remote_addr, "rejected") cl.close() return diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index 2b273d8341..f27c62fd1d 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -108,11 +108,11 @@ void mp_hal_stdio_mode_orig(void) { #endif #if MICROPY_PY_OS_DUPTERM -static int call_dupterm_read(void) { +static int call_dupterm_read(size_t idx) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t read_m[3]; - mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_read, read_m); + mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_read, read_m); read_m[2] = MP_OBJ_NEW_SMALL_INT(1); mp_obj_t res = mp_call_method_n_kw(1, 0, read_m); if (res == mp_const_none) { @@ -122,18 +122,18 @@ static int call_dupterm_read(void) { mp_get_buffer_raise(res, &bufinfo, MP_BUFFER_READ); if (bufinfo.len == 0) { mp_printf(&mp_plat_print, "dupterm: EOF received, deactivating\n"); - MP_STATE_PORT(term_obj) = NULL; + MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL; return -1; } nlr_pop(); return *(byte*)bufinfo.buf; } else { // Temporarily disable dupterm to avoid infinite recursion - mp_obj_t save_term = MP_STATE_PORT(term_obj); - MP_STATE_PORT(term_obj) = NULL; + mp_obj_t save_term = MP_STATE_VM(dupterm_objs[idx]); + MP_STATE_VM(dupterm_objs[idx]) = NULL; mp_printf(&mp_plat_print, "dupterm: "); mp_obj_print_exception(&mp_plat_print, nlr.ret_val); - MP_STATE_PORT(term_obj) = save_term; + MP_STATE_VM(dupterm_objs[idx]) = save_term; } return -1; @@ -143,10 +143,11 @@ static int call_dupterm_read(void) { int mp_hal_stdin_rx_chr(void) { unsigned char c; #if MICROPY_PY_OS_DUPTERM - if (MP_STATE_PORT(term_obj) != MP_OBJ_NULL) { + // TODO only support dupterm one slot at the moment + if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) { int c; do { - c = call_dupterm_read(); + c = call_dupterm_read(0); } while (c == -2); if (c == -1) { goto main_term; diff --git a/py/mpstate.h b/py/mpstate.h index eca14a9e44..6a39ebdea9 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -168,7 +168,7 @@ typedef struct _mp_state_vm_t { // root pointers for extmod #if MICROPY_PY_OS_DUPTERM - mp_obj_t term_obj; + mp_obj_t dupterm_objs[MICROPY_PY_OS_DUPTERM]; mp_obj_t dupterm_arr_obj; #endif diff --git a/py/runtime.c b/py/runtime.c index 069548debb..17e5d235c9 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -102,6 +102,13 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif + #if MICROPY_PY_OS_DUPTERM + for (size_t i = 0; i < MICROPY_PY_OS_DUPTERM; ++i) { + MP_STATE_VM(dupterm_objs[i]) = MP_OBJ_NULL; + } + MP_STATE_VM(dupterm_arr_obj) = MP_OBJ_NULL; + #endif + #if MICROPY_FSUSERMOUNT // zero out the pointers to the user-mounted devices memset(MP_STATE_VM(fs_user_mount), 0, sizeof(MP_STATE_VM(fs_user_mount))); From 829c329dafb5006f73dc38e499978b9ccc96e6b7 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 15 Oct 2017 10:17:24 +0300 Subject: [PATCH 34/75] README: Add explicit section on contributing. To increase visibility of Contributors' Guidelines and Code Conventions docs. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 13529ff4f3..00a0405f34 100644 --- a/README.md +++ b/README.md @@ -162,3 +162,13 @@ This will use the included `tools/pydfu.py` script. If flashing the firmware does not work it may be because you don't have the correct permissions, and need to use `sudo make deploy`. See the README.md file in the ports/stm32/ directory for further details. + +Contributing +------------ + +MicroPython is an open-source project and welcomes contributions. To be +productive, please be sure to follow the +[Contributors' Guidelines](https://github.com/micropython/micropython/wiki/ContributorGuidelines) +and the [Code Conventions](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md). +Note that MicroPython is licenced under the MIT license, and all contributions +should follow this license. From 65ba481cb0e067a97f8a999e2522b0656ca3b7fe Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 13:16:00 +1100 Subject: [PATCH 35/75] stm32/modnwwiznet5k: Implement WIZNET5K.isconnected() method. --- docs/library/network.rst | 5 +++++ ports/stm32/modnwwiznet5k.c | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/docs/library/network.rst b/docs/library/network.rst index 258b2a20c8..acb578a7c7 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -268,6 +268,11 @@ parameter should be `id`. Methods ------- + .. method:: wiznet5k.isconnected() + + Returns ``True`` if the physical Ethernet link is connected and up. + Returns ``False`` otherwise. + .. method:: wiznet5k.ifconfig([(ip, subnet, gateway, dns)]) Get/set IP address, subnet mask, gateway and DNS. diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 78249816d4..a9e5f5aa91 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -414,6 +414,12 @@ STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_regs_obj, wiznet5k_regs); +STATIC mp_obj_t wiznet5k_isconnected(mp_obj_t self_in) { + (void)self_in; + return mp_obj_new_bool(wizphy_getphylink() == PHY_LINK_ON); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(wiznet5k_isconnected_obj, wiznet5k_isconnected); + /// \method ifconfig([(ip, subnet, gateway, dns)]) /// Get/set IP address, subnet mask, gateway and DNS. STATIC mp_obj_t wiznet5k_ifconfig(size_t n_args, const mp_obj_t *args) { @@ -445,6 +451,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(wiznet5k_ifconfig_obj, 1, 2, wiznet5k STATIC const mp_rom_map_elem_t wiznet5k_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_regs), MP_ROM_PTR(&wiznet5k_regs_obj) }, { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&wiznet5k_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&wiznet5k_isconnected_obj) }, }; STATIC MP_DEFINE_CONST_DICT(wiznet5k_locals_dict, wiznet5k_locals_dict_table); From 5d7b0b237b502b7511624bad5fd8fc3092d91972 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 13:17:15 +1100 Subject: [PATCH 36/75] stm32/modusocket: Make getaddrinfo() work when passed an IP address. --- ports/stm32/modusocket.c | 58 +++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/ports/stm32/modusocket.c b/ports/stm32/modusocket.c index c5ab3b6d79..4d14c355f6 100644 --- a/ports/stm32/modusocket.c +++ b/ports/stm32/modusocket.c @@ -390,29 +390,49 @@ STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { size_t hlen; const char *host = mp_obj_str_get_data(host_in, &hlen); mp_int_t port = mp_obj_get_int(port_in); + uint8_t out_ip[MOD_NETWORK_IPADDR_BUF_SIZE]; + bool have_ip = false; - // find a NIC that can do a name lookup - for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { - mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; - mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); - if (nic_type->gethostbyname != NULL) { - uint8_t out_ip[MOD_NETWORK_IPADDR_BUF_SIZE]; - int ret = nic_type->gethostbyname(nic, host, hlen, out_ip); - if (ret != 0) { - // TODO CPython raises: socket.gaierror: [Errno -2] Name or service not known - mp_raise_OSError(ret); - } - mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL); - tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET); - tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM); - tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); - tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_); - tuple->items[4] = netutils_format_inet_addr(out_ip, port, NETUTILS_BIG); - return mp_obj_new_list(1, (mp_obj_t*)&tuple); + if (hlen > 0) { + // check if host is already in IP form + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + netutils_parse_ipv4_addr(host_in, out_ip, NETUTILS_BIG); + have_ip = true; + nlr_pop(); + } else { + // swallow exception: host was not in IP form so need to do DNS lookup } } - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); + if (!have_ip) { + // find a NIC that can do a name lookup + for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { + mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; + mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); + if (nic_type->gethostbyname != NULL) { + int ret = nic_type->gethostbyname(nic, host, hlen, out_ip); + if (ret != 0) { + // TODO CPython raises: socket.gaierror: [Errno -2] Name or service not known + mp_raise_OSError(ret); + } + have_ip = true; + break; + } + } + } + + if (!have_ip) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "no available NIC")); + } + + mp_obj_tuple_t *tuple = mp_obj_new_tuple(5, NULL); + tuple->items[0] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_AF_INET); + tuple->items[1] = MP_OBJ_NEW_SMALL_INT(MOD_NETWORK_SOCK_STREAM); + tuple->items[2] = MP_OBJ_NEW_SMALL_INT(0); + tuple->items[3] = MP_OBJ_NEW_QSTR(MP_QSTR_); + tuple->items[4] = netutils_format_inet_addr(out_ip, port, NETUTILS_BIG); + return mp_obj_new_list(1, (mp_obj_t*)&tuple); } STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_usocket_getaddrinfo_obj, mod_usocket_getaddrinfo); From 0a30ad96c886aacf897b3e87cbdb24fb46c5c43d Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 13:19:11 +1100 Subject: [PATCH 37/75] stm32/modusocket: Return OSError(-2) if getaddrinfo fails. This matches the behaviour of getaddrinfo in extmod/modlwip.c. --- ports/stm32/modnwcc3k.c | 2 +- ports/stm32/modnwwiznet5k.c | 2 +- ports/stm32/modusocket.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ports/stm32/modnwcc3k.c b/ports/stm32/modnwcc3k.c index 551206da8e..8cc0a613d1 100644 --- a/ports/stm32/modnwcc3k.c +++ b/ports/stm32/modnwcc3k.c @@ -125,7 +125,7 @@ STATIC int cc3k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, uin if (ip == 0) { // unknown host - return MP_ENOENT; + return -2; } out_ip[0] = ip >> 24; diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index a9e5f5aa91..9e9fb9aea6 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -92,7 +92,7 @@ STATIC int wiznet5k_gethostbyname(mp_obj_t nic, const char *name, mp_uint_t len, return 0; } else { // failure - return MP_ENOENT; + return -2; } } diff --git a/ports/stm32/modusocket.c b/ports/stm32/modusocket.c index 4d14c355f6..71a237b0d9 100644 --- a/ports/stm32/modusocket.c +++ b/ports/stm32/modusocket.c @@ -413,7 +413,6 @@ STATIC mp_obj_t mod_usocket_getaddrinfo(mp_obj_t host_in, mp_obj_t port_in) { if (nic_type->gethostbyname != NULL) { int ret = nic_type->gethostbyname(nic, host, hlen, out_ip); if (ret != 0) { - // TODO CPython raises: socket.gaierror: [Errno -2] Name or service not known mp_raise_OSError(ret); } have_ip = true; From 5c437963d742627ba172d48d601c118c4577e7b7 Mon Sep 17 00:00:00 2001 From: Li Weiwei Date: Tue, 10 Oct 2017 14:22:22 +0800 Subject: [PATCH 38/75] stm32/mpconfigport.h: Add MICROPY_THREAD_YIELD() macro. --- ports/stm32/mpconfigport.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 06606e62a1..2d59a1df87 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -326,6 +326,8 @@ static inline mp_uint_t disable_irq(void) { __WFI(); \ } \ } while (0); + +#define MICROPY_THREAD_YIELD() pyb_thread_yield() #else #define MICROPY_EVENT_POLL_HOOK \ do { \ @@ -333,6 +335,8 @@ static inline mp_uint_t disable_irq(void) { mp_handle_pending(); \ __WFI(); \ } while (0); + +#define MICROPY_THREAD_YIELD() #endif // We need an implementation of the log2 function which is not a macro From 73e387cff6a3e31a9ea0c300d92a1a4a62d791ef Mon Sep 17 00:00:00 2001 From: Li Weiwei Date: Tue, 10 Oct 2017 14:27:43 +0800 Subject: [PATCH 39/75] drivers/wiznet5k: Improve the performance of socket ops with threading. Use MICROPY_THREAD_YIELD() instead of HAL_Delay in busy waiting to improve the performance of connect, send, recv, sento and recvfrom. --- drivers/wiznet5k/ethernet/socket.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/wiznet5k/ethernet/socket.c b/drivers/wiznet5k/ethernet/socket.c index 129473ad4d..ec25fcc793 100644 --- a/drivers/wiznet5k/ethernet/socket.c +++ b/drivers/wiznet5k/ethernet/socket.c @@ -52,10 +52,9 @@ #include +#include "py/mpthread.h" #include "socket.h" -extern void HAL_Delay(uint32_t); - #define SOCK_ANY_PORT_NUM 0xC000; static uint16_t sock_any_port = SOCK_ANY_PORT_NUM; @@ -242,7 +241,7 @@ int8_t WIZCHIP_EXPORT(connect)(uint8_t sn, uint8_t * addr, uint16_t port) #endif return SOCKERR_TIMEOUT; } - HAL_Delay(1); + MICROPY_THREAD_YIELD(); } #if _WIZCHIP_ == 5200 // for W5200 ARP errata setSUBR((uint8_t*)"\x00\x00\x00\x00"); @@ -317,6 +316,7 @@ int32_t WIZCHIP_EXPORT(send)(uint8_t sn, uint8_t * buf, uint16_t len) } if( (sock_io_mode & (1< freesize) ) return SOCK_BUSY; if(len <= freesize) break; + MICROPY_THREAD_YIELD(); } wiz_send_data(sn, buf, len); #if _WIZCHIP_ == 5200 @@ -368,7 +368,7 @@ int32_t WIZCHIP_EXPORT(recv)(uint8_t sn, uint8_t * buf, uint16_t len) } if((sock_io_mode & (1< freesize) ) return SOCK_BUSY; if(len <= freesize) break; - HAL_Delay(1); + MICROPY_THREAD_YIELD(); }; wiz_send_data(sn, buf, len); @@ -446,7 +446,7 @@ int32_t WIZCHIP_EXPORT(sendto)(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t return SOCKERR_TIMEOUT; } //////////// - HAL_Delay(1); + MICROPY_THREAD_YIELD(); } #if _WIZCHIP_ == 5200 // for W5200 ARP errata setSUBR((uint8_t*)"\x00\x00\x00\x00"); @@ -486,6 +486,7 @@ int32_t WIZCHIP_EXPORT(recvfrom)(uint8_t sn, uint8_t * buf, uint16_t len, uint8_ if(getSn_SR(sn) == SOCK_CLOSED) return SOCKERR_SOCKCLOSED; if( (sock_io_mode & (1< Date: Mon, 16 Oct 2017 15:34:08 +1100 Subject: [PATCH 40/75] drivers/wiznet5k: Get low-level W5500 driver working. This patch implements the basic SPI read/write functions for the W5500 chip. It also allows _WIZCHIP_ to be configured externally to select the specific Wiznet chip. --- drivers/wiznet5k/ethernet/w5500/w5500.c | 10 ++++++++++ drivers/wiznet5k/ethernet/w5500/w5500.h | 1 - drivers/wiznet5k/ethernet/wizchip_conf.h | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/wiznet5k/ethernet/w5500/w5500.c b/drivers/wiznet5k/ethernet/w5500/w5500.c index 6b3111f996..3107b1b71a 100644 --- a/drivers/wiznet5k/ethernet/w5500/w5500.c +++ b/drivers/wiznet5k/ethernet/w5500/w5500.c @@ -57,6 +57,16 @@ //////////////////////////////////////////////////// +#define LPC_SSP0 (0) + +static void Chip_SSP_ReadFrames_Blocking(int dummy, uint8_t *buf, uint32_t len) { + WIZCHIP.IF.SPI._read_bytes(buf, len); +} + +static void Chip_SSP_WriteFrames_Blocking(int dummy, const uint8_t *buf, uint32_t len) { + WIZCHIP.IF.SPI._write_bytes(buf, len); +} + uint8_t WIZCHIP_READ(uint32_t AddrSel) { uint8_t ret; diff --git a/drivers/wiznet5k/ethernet/w5500/w5500.h b/drivers/wiznet5k/ethernet/w5500/w5500.h index f0ffcb0c8e..c2afb180eb 100644 --- a/drivers/wiznet5k/ethernet/w5500/w5500.h +++ b/drivers/wiznet5k/ethernet/w5500/w5500.h @@ -44,7 +44,6 @@ #include #include "../wizchip_conf.h" -#include "board.h" #define _W5500_IO_BASE_ 0x00000000 diff --git a/drivers/wiznet5k/ethernet/wizchip_conf.h b/drivers/wiznet5k/ethernet/wizchip_conf.h index 55c79ae0a1..4a7a7bd691 100644 --- a/drivers/wiznet5k/ethernet/wizchip_conf.h +++ b/drivers/wiznet5k/ethernet/wizchip_conf.h @@ -56,7 +56,9 @@ * @todo You should select one, \b 5100, \b 5200 ,\b 5500 or etc. \n\n * ex> #define \_WIZCHIP_ 5500 */ +#ifndef _WIZCHIP_ #define _WIZCHIP_ 5200 // 5100, 5200, 5500 +#endif #define _WIZCHIP_IO_MODE_NONE_ 0x0000 #define _WIZCHIP_IO_MODE_BUS_ 0x0100 /**< Bus interface mode */ From e36821a7662e9c65ee2bfb8c23925a10d2167faf Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 15:36:57 +1100 Subject: [PATCH 41/75] stm32/modnwwiznet5k: Add support for W5500 Ethernet chip. Which Wiznet chip to use is a compile-time option: MICROPY_PY_WIZNET5K should be set to either 5200 or 5500 to support either one of these Ethernet chips. The driver is called network.WIZNET5K in both cases. Note that this commit introduces a breaking-change at the build level because previously the valid values for MICROPY_PY_WIZNET5K were 0 and 1 but now they are 0, 5200 and 5500. --- ports/stm32/Makefile | 6 +++--- ports/stm32/modnwwiznet5k.c | 14 ++++++++++++-- ports/stm32/mpconfigport.mk | 5 ++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 913f0913ec..68b007471e 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -286,13 +286,13 @@ SRC_USBDEV = $(addprefix $(USBDEV_DIR)/,\ class/src/usbd_msc_data.c \ ) -ifeq ($(MICROPY_PY_WIZNET5K),1) +ifneq ($(MICROPY_PY_WIZNET5K),0) WIZNET5K_DIR=drivers/wiznet5k INC += -I$(TOP)/$(WIZNET5K_DIR) -CFLAGS_MOD += -DMICROPY_PY_WIZNET5K=1 +CFLAGS_MOD += -DMICROPY_PY_WIZNET5K=$(MICROPY_PY_WIZNET5K) -D_WIZCHIP_=$(MICROPY_PY_WIZNET5K) SRC_MOD += modnwwiznet5k.c SRC_MOD += $(addprefix $(WIZNET5K_DIR)/,\ - ethernet/w5200/w5200.c \ + ethernet/w$(MICROPY_PY_WIZNET5K)/w$(MICROPY_PY_WIZNET5K).c \ ethernet/wizchip_conf.c \ ethernet/socket.c \ internet/dns/dns.c \ diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 9e9fb9aea6..d59125054c 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -398,7 +398,12 @@ STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { if (i % 16 == 0) { printf("\n %04x:", i); } - printf(" %02x", WIZCHIP_READ(i)); + #if MICROPY_PY_WIZNET5K == 5200 + uint32_t reg = i; + #else + uint32_t reg = _W5500_IO_BASE_ | i << 8; + #endif + printf(" %02x", WIZCHIP_READ(reg)); } for (int sn = 0; sn < 4; ++sn) { printf("\nWiz SREG[%d]:", sn); @@ -406,7 +411,12 @@ STATIC mp_obj_t wiznet5k_regs(mp_obj_t self_in) { if (i % 16 == 0) { printf("\n %04x:", i); } - printf(" %02x", WIZCHIP_READ(WIZCHIP_SREG_ADDR(sn, i))); + #if MICROPY_PY_WIZNET5K == 5200 + uint32_t reg = WIZCHIP_SREG_ADDR(sn, i); + #else + uint32_t reg = _W5500_IO_BASE_ | i << 8 | WIZCHIP_SREG_BLOCK(sn) << 3; + #endif + printf(" %02x", WIZCHIP_READ(reg)); } } printf("\n"); diff --git a/ports/stm32/mpconfigport.mk b/ports/stm32/mpconfigport.mk index 64145383eb..e708de6c1b 100644 --- a/ports/stm32/mpconfigport.mk +++ b/ports/stm32/mpconfigport.mk @@ -1,6 +1,9 @@ # Enable/disable extra modules -# wiznet5k module for ethernet support +# wiznet5k module for ethernet support; valid values are: +# 0 : no Wiznet support +# 5200 : support for W5200 module +# 5500 : support for W5500 module MICROPY_PY_WIZNET5K ?= 0 # cc3k module for wifi support From 06f2fdbe6103be55f102962dd6e6a9b5d0a217d2 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 15:51:32 +1100 Subject: [PATCH 42/75] travis: Update build command now that stm32 Wiznet config has changed. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 83ac2a112b..fd23a37072 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ script: - make -C ports/bare-arm - make -C ports/qemu-arm test - make -C ports/stm32 - - make -C ports/stm32 BOARD=PYBV11 MICROPY_PY_WIZNET5K=1 MICROPY_PY_CC3K=1 + - make -C ports/stm32 BOARD=PYBV11 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 - make -C ports/stm32 BOARD=STM32F769DISC - make -C ports/stm32 BOARD=STM32L476DISC - make -C ports/teensy From d90ade5e3e62b28c1f04d18f206509a488d62ec0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Oct 2017 15:51:56 +1100 Subject: [PATCH 43/75] docs/library/network: Update docs to state that W5500 is supported. --- docs/library/network.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/library/network.rst b/docs/library/network.rst index acb578a7c7..3b0aa535e5 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -224,7 +224,9 @@ parameter should be `id`. ============== This class allows you to control WIZnet5x00 Ethernet adaptors based on - the W5200 and W5500 chipsets (only W5200 tested). + the W5200 and W5500 chipsets. The particular chipset that is supported + by the firmware is selected at compile-time via the MICROPY_PY_WIZNET5K + option. Example usage:: From 285ac585322f0ffced76d25a6f19cd1fa30b3514 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 17 Oct 2017 16:31:12 +1100 Subject: [PATCH 44/75] stm32/modnwwiznet5k: Increase SPI bus speed to 42MHz. The W5200 and W5500 can support up to 80MHz so 42MHz (the maximum the pyboard can do in its standard configuration) should be safe. Tested to give around 1050000 kbytes/sec TCP download speed on a W5500, which is about 10% more than with the previous SPI speed of 21MHz. --- ports/stm32/modnwwiznet5k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index d59125054c..717d88b392 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -346,7 +346,7 @@ STATIC mp_obj_t wiznet5k_make_new(const mp_obj_type_t *type, size_t n_args, size wiznet5k_obj.spi->Init.CLKPolarity = SPI_POLARITY_LOW; // clock is low when idle wiznet5k_obj.spi->Init.CLKPhase = SPI_PHASE_1EDGE; // data latched on first edge, which is rising edge for low-idle wiznet5k_obj.spi->Init.NSS = SPI_NSS_SOFT; - wiznet5k_obj.spi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // clock freq = f_PCLK / this_prescale_value; Wiz820i can do up to 80MHz + wiznet5k_obj.spi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // clock freq = f_PCLK / this_prescale_value; Wiz820i can do up to 80MHz wiznet5k_obj.spi->Init.FirstBit = SPI_FIRSTBIT_MSB; wiznet5k_obj.spi->Init.TIMode = SPI_TIMODE_DISABLED; wiznet5k_obj.spi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; From 8fa3d2996c9d69b1a6ba20f6beb354f4ce0001c8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 17 Oct 2017 16:34:10 +1100 Subject: [PATCH 45/75] stm32/modnwwiznet5k: Implement stream ioctl for the Wiznet driver. Now supports polling for read and write ability. --- ports/stm32/modnwwiznet5k.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ports/stm32/modnwwiznet5k.c b/ports/stm32/modnwwiznet5k.c index 717d88b392..763137c703 100644 --- a/ports/stm32/modnwwiznet5k.c +++ b/ports/stm32/modnwwiznet5k.c @@ -30,6 +30,7 @@ #include "py/objlist.h" #include "py/runtime.h" +#include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" #include "lib/netutils/netutils.h" @@ -305,9 +306,19 @@ STATIC int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_ } STATIC int wiznet5k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { - // TODO - *_errno = MP_EINVAL; - return -1; + if (request == MP_STREAM_POLL) { + int ret = 0; + if (arg & MP_STREAM_POLL_RD && getSn_RX_RSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_RD; + } + if (arg & MP_STREAM_POLL_WR && getSn_TX_FSR(socket->u_param.fileno) != 0) { + ret |= MP_STREAM_POLL_WR; + } + return ret; + } else { + *_errno = MP_EINVAL; + return MP_STREAM_ERROR; + } } #if 0 From c53ca32561d19add3c2f6fae9b09d472fe52165e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Oct 2017 12:38:28 +1100 Subject: [PATCH 46/75] README: Add gcc and arm-none-eabi-newlib to list of required components. gcc is required for mpy-cross, and arm-none-eabi-newlib for ports using arm-none-eabi-gcc. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 00a0405f34..44d062e877 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Additional components: The subdirectories above may include READMEs with additional info. "make" is used to build the components, or "gmake" on BSD-based systems. -You will also need bash and Python (at least 2.7 or 3.3). +You will also need bash, gcc, and Python (at least 2.7 or 3.3). The Unix version ---------------- @@ -140,8 +140,8 @@ The STM32 version ----------------- The "stm32" port requires an ARM compiler, arm-none-eabi-gcc, and associated -bin-utils. For those using Arch Linux, you need arm-none-eabi-binutils and -arm-none-eabi-gcc packages. Otherwise, try here: +bin-utils. For those using Arch Linux, you need arm-none-eabi-binutils, +arm-none-eabi-gcc and arm-none-eabi-newlib packages. Otherwise, try here: https://launchpad.net/gcc-arm-embedded To build: From 9725a654bdfc4ec0b7d6bc6aa3f4365f0825c5f3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Oct 2017 14:10:17 +1100 Subject: [PATCH 47/75] extmod/uos_dupterm: Swallow any errors from dupterm closing the stream. Without this the board will crash when deactivating a stream that doesn't have a close() method (eg UART) or that raises an exception within the method (eg user-defined function). --- extmod/uos_dupterm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/extmod/uos_dupterm.c b/extmod/uos_dupterm.c index d4326d3265..f77cff5770 100644 --- a/extmod/uos_dupterm.c +++ b/extmod/uos_dupterm.c @@ -43,7 +43,13 @@ void mp_uos_deactivate(size_t dupterm_idx, const char *msg, mp_obj_t exc) { if (exc != MP_OBJ_NULL) { mp_obj_print_exception(&mp_plat_print, exc); } - mp_stream_close(term); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_stream_close(term); + nlr_pop(); + } else { + // Ignore any errors during stream closing + } } int mp_uos_dupterm_rx_chr(void) { @@ -61,8 +67,8 @@ int mp_uos_dupterm_rx_chr(void) { if (res == mp_const_none) { nlr_pop(); } else if (res == MP_OBJ_NEW_SMALL_INT(0)) { - mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL); nlr_pop(); + mp_uos_deactivate(idx, "dupterm: EOF received, deactivating\n", MP_OBJ_NULL); } else { mp_buffer_info_t bufinfo; mp_get_buffer_raise(MP_STATE_VM(dupterm_arr_obj), &bufinfo, MP_BUFFER_READ); From 0eb333e3cf2319cfb1bf558b8f1c6e3e513fb9d8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Oct 2017 14:15:32 +1100 Subject: [PATCH 48/75] stm32/mphalport: Improve efficiency of mp_hal_stdout_tx_strn_cooked. Also simplifies the code by removing the specialised (and inefficient) cooked functions from UART and USB_VCP. --- ports/stm32/mphalport.c | 20 +++++++++++++++----- ports/stm32/uart.c | 15 --------------- ports/stm32/uart.h | 1 - ports/stm32/usb.c | 14 -------------- ports/stm32/usb.h | 1 - 5 files changed, 15 insertions(+), 36 deletions(-) diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index ab3dc227a6..3bea6e2d91 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -58,13 +58,23 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { } } +// Efficiently convert "\n" to "\r\n" void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { - // send stdout to UART and USB CDC VCP - if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { - uart_tx_strn_cooked(MP_STATE_PORT(pyb_stdio_uart), str, len); + const char *last = str; + while (len--) { + if (*str == '\n') { + if (str > last) { + mp_hal_stdout_tx_strn(last, str - last); + } + mp_hal_stdout_tx_strn("\r\n", 2); + ++str; + last = str; + } else { + ++str; + } } - if (usb_vcp_is_enabled()) { - usb_vcp_send_strn_cooked(str, len); + if (str > last) { + mp_hal_stdout_tx_strn(last, str - last); } } diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 2b2f782f91..0b46d4f040 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -452,26 +452,11 @@ STATIC size_t uart_tx_data(pyb_uart_obj_t *self, const void *src_in, size_t num_ return num_tx; } -STATIC void uart_tx_char(pyb_uart_obj_t *uart_obj, int c) { - uint16_t ch = c; - int errcode; - uart_tx_data(uart_obj, &ch, 1, &errcode); -} - void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len) { int errcode; uart_tx_data(uart_obj, str, len, &errcode); } -void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len) { - for (const char *top = str + len; str < top; str++) { - if (*str == '\n') { - uart_tx_char(uart_obj, '\r'); - } - uart_tx_char(uart_obj, *str); - } -} - // this IRQ handler is set up to handle RXNE interrupts only void uart_irq_handler(mp_uint_t uart_id) { // get the uart object diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index e96b25b5f4..d176520a1b 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -48,6 +48,5 @@ void uart_irq_handler(mp_uint_t uart_id); mp_uint_t uart_rx_any(pyb_uart_obj_t *uart_obj); int uart_rx_char(pyb_uart_obj_t *uart_obj); void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len); -void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len); #endif // MICROPY_INCLUDED_STMHAL_UART_H diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 8f89107ba7..69f381d9b3 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -177,20 +177,6 @@ void usb_vcp_send_strn(const char *str, int len) { #endif } -void usb_vcp_send_strn_cooked(const char *str, int len) { -#ifdef USE_DEVICE_MODE - if (usb_device.enabled) { - for (const char *top = str + len; str < top; str++) { - if (*str == '\n') { - usbd_cdc_tx_always(&usb_device.usbd_cdc_itf, (const uint8_t*)"\r\n", 2); - } else { - usbd_cdc_tx_always(&usb_device.usbd_cdc_itf, (const uint8_t*)str, 1); - } - } - } -#endif -} - /******************************************************************************/ // MicroPython bindings for USB diff --git a/ports/stm32/usb.h b/ports/stm32/usb.h index f60ea80336..41c461fb2f 100644 --- a/ports/stm32/usb.h +++ b/ports/stm32/usb.h @@ -63,7 +63,6 @@ void pyb_usb_dev_deinit(void); bool usb_vcp_is_enabled(void); int usb_vcp_recv_byte(uint8_t *c); // if a byte is available, return 1 and put the byte in *c, else return 0 void usb_vcp_send_strn(const char* str, int len); -void usb_vcp_send_strn_cooked(const char *str, int len); void pyb_usb_host_init(void); void pyb_usb_host_process(void); From d6bf3658f44e22f06e0d293b8fe0542b57538b4f Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Oct 2017 14:16:42 +1100 Subject: [PATCH 49/75] stm32: Make uos.dupterm() conform to specs by using extmod version. The legacy function pyb.repl_uart() is still provided and retains its original behaviour (it only accepts a UART object). uos.dupterm() will now accept any object with write/readinto methods. At the moment there is just 1 dupterm slot. --- ports/stm32/modpyb.c | 26 ++++++++++++++++++++++++-- ports/stm32/moduos.c | 25 ++----------------------- ports/stm32/mpconfigport.h | 1 + ports/stm32/mphalport.c | 6 ++++++ 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/ports/stm32/modpyb.c b/ports/stm32/modpyb.c index 176fc84663..81cbdcc191 100644 --- a/ports/stm32/modpyb.c +++ b/ports/stm32/modpyb.c @@ -27,7 +27,7 @@ #include #include -#include "py/obj.h" +#include "py/runtime.h" #include "py/gc.h" #include "py/builtin.h" #include "py/mphal.h" @@ -104,6 +104,28 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_elapsed_micros_obj, pyb_elapsed_micros); MP_DECLARE_CONST_FUN_OBJ_KW(pyb_main_obj); // defined in main.c +// Get or set the UART object that the REPL is repeated on. +// This is a legacy function, use of uos.dupterm is preferred. +STATIC mp_obj_t pyb_repl_uart(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + if (MP_STATE_PORT(pyb_stdio_uart) == NULL) { + return mp_const_none; + } else { + return MP_STATE_PORT(pyb_stdio_uart); + } + } else { + if (args[0] == mp_const_none) { + MP_STATE_PORT(pyb_stdio_uart) = NULL; + } else if (mp_obj_get_type(args[0]) == &pyb_uart_type) { + MP_STATE_PORT(pyb_stdio_uart) = args[0]; + } else { + mp_raise_ValueError("need a UART object"); + } + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_repl_uart_obj, 0, 1, pyb_repl_uart); + STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pyb) }, @@ -126,7 +148,7 @@ STATIC const mp_rom_map_elem_t pyb_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&machine_sleep_obj) }, { MP_ROM_QSTR(MP_QSTR_standby), MP_ROM_PTR(&machine_deepsleep_obj) }, { MP_ROM_QSTR(MP_QSTR_main), MP_ROM_PTR(&pyb_main_obj) }, - { MP_ROM_QSTR(MP_QSTR_repl_uart), MP_ROM_PTR(&mod_os_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_repl_uart), MP_ROM_PTR(&pyb_repl_uart_obj) }, { MP_ROM_QSTR(MP_QSTR_usb_mode), MP_ROM_PTR(&pyb_usb_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_hid_mouse), MP_ROM_PTR(&pyb_usb_hid_mouse_obj) }, diff --git a/ports/stm32/moduos.c b/ports/stm32/moduos.c index f661b3b5e0..f6e1483d35 100644 --- a/ports/stm32/moduos.c +++ b/ports/stm32/moduos.c @@ -33,6 +33,7 @@ #include "lib/timeutils/timeutils.h" #include "lib/oofatfs/ff.h" #include "lib/oofatfs/diskio.h" +#include "extmod/misc.h" #include "extmod/vfs.h" #include "extmod/vfs_fat.h" #include "genhdr/mpversion.h" @@ -105,28 +106,6 @@ STATIC mp_obj_t os_urandom(mp_obj_t num) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); #endif -// Get or set the UART object that the REPL is repeated on. -// TODO should accept any object with read/write methods. -STATIC mp_obj_t os_dupterm(size_t n_args, const mp_obj_t *args) { - if (n_args == 0) { - if (MP_STATE_PORT(pyb_stdio_uart) == NULL) { - return mp_const_none; - } else { - return MP_STATE_PORT(pyb_stdio_uart); - } - } else { - if (args[0] == mp_const_none) { - MP_STATE_PORT(pyb_stdio_uart) = NULL; - } else if (mp_obj_get_type(args[0]) == &pyb_uart_type) { - MP_STATE_PORT(pyb_stdio_uart) = args[0]; - } else { - mp_raise_ValueError("need a UART object"); - } - return mp_const_none; - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_os_dupterm_obj, 0, 1, os_dupterm); - STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, @@ -154,7 +133,7 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { #endif // these are MicroPython extensions - { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mod_os_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_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) }, { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 2d59a1df87..51d4425616 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -126,6 +126,7 @@ #define MICROPY_PY_USELECT (1) #define MICROPY_PY_UTIMEQ (1) #define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_OS_DUPTERM (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 3bea6e2d91..e9c4f28f79 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -3,6 +3,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" +#include "extmod/misc.h" #include "usb.h" #include "uart.h" @@ -38,6 +39,10 @@ int mp_hal_stdin_rx_chr(void) { } else if (MP_STATE_PORT(pyb_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) { return uart_rx_char(MP_STATE_PORT(pyb_stdio_uart)); } + int dupterm_c = mp_uos_dupterm_rx_chr(); + if (dupterm_c >= 0) { + return dupterm_c; + } MICROPY_EVENT_POLL_HOOK } } @@ -56,6 +61,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { if (usb_vcp_is_enabled()) { usb_vcp_send_strn(str, len); } + mp_uos_dupterm_tx_strn(str, len); } // Efficiently convert "\n" to "\r\n" From 93ce125abe41b5527eb92e483e64cc6d1a027ba0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 19 Oct 2017 18:57:26 +1100 Subject: [PATCH 50/75] py/argcheck: Remove #if guard around terse error message helper func. Not all compilers/analysers are smart enough to realise that this function is never called if MICROPY_ERROR_REPORTING is not TERSE, because the logic in the code uses if statements rather than #if to select whether to call this function or not (MSC in debug mode is an example of this, but there are others). So just unconditionally compile this helper function. The code-base anyway relies on the linker to remove unused functions. --- py/argcheck.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/py/argcheck.c b/py/argcheck.c index add6f8de85..d53bca73a6 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -133,11 +133,9 @@ void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, mp_arg_parse_all(n_pos, args, &kw_args, n_allowed, allowed, out_vals); } -#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE || _MSC_VER NORETURN void mp_arg_error_terse_mismatch(void) { mp_raise_TypeError("argument num/types mismatch"); } -#endif #if MICROPY_CPYTHON_COMPAT NORETURN void mp_arg_error_unimpl_kw(void) { From f2baa9ec245a66c4cd7d990e39a4af3f6fab1cb7 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 19 Oct 2017 12:40:41 +0300 Subject: [PATCH 51/75] py/objtype: Use CPython compatible method name for sizeof. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per https://docs.python.org/3/library/sys.html#sys.getsizeof: getsizeof() calls the object’s __sizeof__ method. Previously, "getsizeof" was used mostly to save on new qstr, as we don't really support calling this method on arbitrary objects (so it was used only for reporting). However, normalize it all now. --- py/objtype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/objtype.c b/py/objtype.c index 9a639ef035..45b119f451 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -342,7 +342,7 @@ const uint16_t mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { [MP_UNARY_OP_INVERT] = MP_QSTR___invert__, #endif #if MICROPY_PY_SYS_GETSIZEOF - [MP_UNARY_OP_SIZEOF] = MP_QSTR_getsizeof, + [MP_UNARY_OP_SIZEOF] = MP_QSTR___sizeof__, #endif }; From 9956fd0710c3866b9df37857c9aa62b8bb681403 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 21 Oct 2017 11:06:32 +0300 Subject: [PATCH 52/75] py/objtype: Fit qstrs for special methods in byte type. Update makeqstrdata.py to sort strings starting with "__" to the beginning of qstr list, so they get low qstr id's, guaranteedly fitting in 8 bits. Then use this property to further compact op_id => qstr mapping arrays. --- py/makeqstrdata.py | 10 +++++++++- py/objtype.c | 10 +++++++--- py/runtime.h | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 7249769f47..38fde1a9c6 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -108,7 +108,15 @@ def parse_input_headers(infiles): continue # add the qstr to the list, with order number to retain original order in file - qstrs[ident] = (len(qstrs), ident, qstr) + order = len(qstrs) + # but put special method names like __add__ at the top of list, so + # that their id's fit into a byte + if ident == "": + # Sort empty qstr above all still + order = -200000 + elif ident.startswith("__"): + order -= 100000 + qstrs[ident] = (order, ident, qstr) if not qcfgs: sys.stderr.write("ERROR: Empty preprocessor output - check for errors above\n") diff --git a/py/objtype.c b/py/objtype.c index 45b119f451..e75407683d 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -332,7 +332,9 @@ mp_obj_t mp_obj_instance_make_new(const mp_obj_type_t *self, size_t n_args, size return MP_OBJ_FROM_PTR(o); } -const uint16_t mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { +// Qstrs for special methods are guaranteed to have a small value, so we use byte +// type to represent them. +const byte mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { [MP_UNARY_OP_BOOL] = MP_QSTR___bool__, [MP_UNARY_OP_LEN] = MP_QSTR___len__, [MP_UNARY_OP_HASH] = MP_QSTR___hash__, @@ -406,9 +408,11 @@ STATIC mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } // Binary-op enum values not listed here will have the default value of 0 in the -// table, corresponding to MP_QSTR_, and are therefore unsupported (a lookup will +// table, corresponding to MP_QSTR_NULL, and are therefore unsupported (a lookup will // fail). They can be added at the expense of code size for the qstr. -const uint16_t mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { +// Qstrs for special methods are guaranteed to have a small value, so we use byte +// type to represent them. +const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { [MP_BINARY_OP_LESS] = MP_QSTR___lt__, [MP_BINARY_OP_MORE] = MP_QSTR___gt__, [MP_BINARY_OP_EQUAL] = MP_QSTR___eq__, diff --git a/py/runtime.h b/py/runtime.h index d410b56141..9c1921cb5d 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -57,8 +57,8 @@ typedef struct _mp_arg_t { } mp_arg_t; // Tables mapping operator enums to qstrs, defined in objtype.c -extern const uint16_t mp_unary_op_method_name[]; -extern const uint16_t mp_binary_op_method_name[]; +extern const byte mp_unary_op_method_name[]; +extern const byte mp_binary_op_method_name[]; void mp_init(void); void mp_deinit(void); From cfff12612f92d6ae460e9748394a0cedf3b3d31d Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 23 Oct 2017 12:09:37 +0300 Subject: [PATCH 53/75] unix: Rename modsocket.c to modusocket.c. Unix naming is historical, before current conventions were established. All other ports however have it as "modusocket.c", so rename for consistency and to avoid confusion. --- ports/unix/Makefile | 2 +- ports/unix/{modsocket.c => modusocket.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename ports/unix/{modsocket.c => modusocket.c} (100%) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 1a1ea01f79..b96391f693 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -97,7 +97,7 @@ SRC_MOD += modtermios.c endif ifeq ($(MICROPY_PY_SOCKET),1) CFLAGS_MOD += -DMICROPY_PY_SOCKET=1 -SRC_MOD += modsocket.c +SRC_MOD += modusocket.c endif ifeq ($(MICROPY_PY_THREAD),1) CFLAGS_MOD += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 diff --git a/ports/unix/modsocket.c b/ports/unix/modusocket.c similarity index 100% rename from ports/unix/modsocket.c rename to ports/unix/modusocket.c From f4059dcc0c5251256a53fcb49f56fdda6e879341 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 24 Oct 2017 22:39:36 +1100 Subject: [PATCH 54/75] all: Use NULL instead of "" when calling mp_raise exception helpers. This is the established way of doing it and reduces code size by a little bit. --- extmod/modlwip.c | 2 +- extmod/modussl_axtls.c | 2 +- extmod/modutimeq.c | 2 +- extmod/modwebrepl.c | 2 +- ports/esp8266/machine_hspi.c | 2 +- ports/esp8266/machine_wdt.c | 2 +- ports/unix/modjni.c | 2 +- py/objarray.c | 2 +- py/objlist.c | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index f7e776af91..bbb01b5d76 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -1032,7 +1032,7 @@ STATIC mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) { break; } case MOD_NETWORK_SOCK_DGRAM: - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); break; } diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 88a89a23d4..719a65cd1f 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -152,7 +152,7 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { // Currently supports only blocking mode (void)self_in; if (!mp_obj_is_true(flag_in)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } return mp_const_none; } diff --git a/extmod/modutimeq.c b/extmod/modutimeq.c index 94cbd20d2f..620e7484b9 100644 --- a/extmod/modutimeq.c +++ b/extmod/modutimeq.c @@ -146,7 +146,7 @@ STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { } mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref); if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 3) { - mp_raise_TypeError(""); + mp_raise_TypeError(NULL); } struct qentry *item = &heap->items[0]; diff --git a/extmod/modwebrepl.c b/extmod/modwebrepl.c index 4ff282aac3..3aba5c0f10 100644 --- a/extmod/modwebrepl.c +++ b/extmod/modwebrepl.c @@ -308,7 +308,7 @@ STATIC mp_obj_t webrepl_set_password(mp_obj_t passwd_in) { size_t len; const char *passwd = mp_obj_str_get_data(passwd_in, &len); if (len > sizeof(webrepl_passwd) - 1) { - mp_raise_ValueError(""); + mp_raise_ValueError(NULL); } strcpy(webrepl_passwd, passwd); return mp_const_none; diff --git a/ports/esp8266/machine_hspi.c b/ports/esp8266/machine_hspi.c index eaabbab7ea..9fd0f48682 100644 --- a/ports/esp8266/machine_hspi.c +++ b/ports/esp8266/machine_hspi.c @@ -149,7 +149,7 @@ mp_obj_t machine_hspi_make_new(const mp_obj_type_t *type, size_t n_args, size_t // args[0] holds the id of the peripheral if (args[0] != MP_OBJ_NEW_SMALL_INT(1)) { // FlashROM is on SPI0, so far we don't support its usage - mp_raise_ValueError(""); + mp_raise_ValueError(NULL); } machine_hspi_obj_t *self = m_new_obj(machine_hspi_obj_t); diff --git a/ports/esp8266/machine_wdt.c b/ports/esp8266/machine_wdt.c index 04b42782e5..4432297fa8 100644 --- a/ports/esp8266/machine_wdt.c +++ b/ports/esp8266/machine_wdt.c @@ -51,7 +51,7 @@ STATIC mp_obj_t machine_wdt_make_new(const mp_obj_type_t *type_in, size_t n_args case 0: return &wdt_default; default: - mp_raise_ValueError(""); + mp_raise_ValueError(NULL); } } diff --git a/ports/unix/modjni.c b/ports/unix/modjni.c index 15b6d9cd78..f29c095cf5 100644 --- a/ports/unix/modjni.c +++ b/ports/unix/modjni.c @@ -266,7 +266,7 @@ STATIC mp_obj_t jobject_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) return mp_const_none; } } - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } if (!JJ(IsInstanceOf, self->obj, List_class)) { diff --git a/py/objarray.c b/py/objarray.c index 8a3e7faade..7003ec9e7d 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -286,7 +286,7 @@ STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs // Otherwise, can only look for a scalar numeric value in an array if (MP_OBJ_IS_INT(rhs_in) || mp_obj_is_float(rhs_in)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } return mp_const_false; diff --git a/py/objlist.c b/py/objlist.c index bc22d9fc35..1a18f937d6 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -158,7 +158,7 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in); mp_bound_slice_t slice; if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } mp_int_t len_adj = slice.start - slice.stop; @@ -198,7 +198,7 @@ STATIC mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { mp_obj_get_array(value, &value_len, &value_items); mp_bound_slice_t slice_out; if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice_out)) { - mp_raise_NotImplementedError(""); + mp_raise_NotImplementedError(NULL); } mp_int_t len_adj = value_len - (slice_out.stop - slice_out.start); //printf("Len adj: %d\n", len_adj); From 9a7e3469b28b02b05b85789acc6c0a78b9804659 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 24 Oct 2017 23:11:42 +0300 Subject: [PATCH 55/75] unix/modusocket: Remove #if MICROPY_SOCKET_EXTRA code blocks. These defined couple of functions added during initial experimentation, which aren't part of MicroPython API and no longer used or needed. --- ports/unix/modusocket.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/ports/unix/modusocket.c b/ports/unix/modusocket.c index 7e82554c7b..cfb6a9f5ec 100644 --- a/ports/unix/modusocket.c +++ b/ports/unix/modusocket.c @@ -60,8 +60,6 @@ should be add to separate modules (C or Python level). */ -#define MICROPY_SOCKET_EXTRA (0) - // This type must "inherit" from mp_obj_fdfile_t, i.e. matching subset of // fields should have the same layout. typedef struct _mp_obj_socket_t { @@ -382,26 +380,6 @@ const mp_obj_type_t mp_type_socket = { .locals_dict = (mp_obj_dict_t*)&usocket_locals_dict, }; -#if MICROPY_SOCKET_EXTRA -STATIC mp_obj_t mod_socket_htons(mp_obj_t arg) { - return MP_OBJ_NEW_SMALL_INT(htons(MP_OBJ_SMALL_INT_VALUE(arg))); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_htons_obj, mod_socket_htons); - - -STATIC mp_obj_t mod_socket_gethostbyname(mp_obj_t arg) { - const char *s = mp_obj_str_get_str(arg); - struct hostent *h = gethostbyname(s); - if (h == NULL) { - // CPython: socket.herror - mp_raise_OSError(h_errno); - } - assert(h->h_length == 4); - return mp_obj_new_int(*(int*)*h->h_addr_list); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_gethostbyname_obj, mod_socket_gethostbyname); -#endif // MICROPY_SOCKET_EXTRA - #define BINADDR_MAX_LEN sizeof(struct in6_addr) STATIC mp_obj_t mod_socket_inet_pton(mp_obj_t family_in, mp_obj_t addr_in) { int family = mp_obj_get_int(family_in); @@ -549,10 +527,6 @@ STATIC const mp_rom_map_elem_t mp_module_socket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_inet_pton), MP_ROM_PTR(&mod_socket_inet_pton_obj) }, { MP_ROM_QSTR(MP_QSTR_inet_ntop), MP_ROM_PTR(&mod_socket_inet_ntop_obj) }, { MP_ROM_QSTR(MP_QSTR_sockaddr), MP_ROM_PTR(&mod_socket_sockaddr_obj) }, -#if MICROPY_SOCKET_EXTRA - { MP_ROM_QSTR(MP_QSTR_htons), MP_ROM_PTR(&mod_socket_htons_obj) }, - { MP_ROM_QSTR(MP_QSTR_gethostbyname), MP_ROM_PTR(&mod_socket_gethostbyname_obj) }, -#endif #define C(name) { MP_ROM_QSTR(MP_QSTR_ ## name), MP_ROM_INT(name) } C(AF_UNIX), From 328c1e78be68a386fe6fb5940027aad887c823a4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 26 Oct 2017 00:28:45 +0300 Subject: [PATCH 56/75] docs/uselect: Document one-shot polling mode. --- docs/library/uselect.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/library/uselect.rst b/docs/library/uselect.rst index e330207dbd..beffce69ae 100644 --- a/docs/library/uselect.rst +++ b/docs/library/uselect.rst @@ -66,12 +66,18 @@ Methods Tuples returned may contain more than 2 elements as described above. -.. method:: poll.ipoll([timeout]) +.. 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 + `callee-owned tuples`. This function provides efficient, allocation-free 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. + .. admonition:: Difference to CPython :class: attention From f36975b6792f6b702dc9184adcdb6e0d89ce23b4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 26 Oct 2017 12:29:24 +1100 Subject: [PATCH 57/75] tests/net_inet: Update tls test to work with CPython and incl new site. CPython only supports the server_hostname keyword arg via the SSLContext object, so use that instead of the top-level ssl.wrap_socket. This allows the test to run on CPython the same as uPy. Also add the "Host:" header to correctly make a GET request (for URLs that are hosted on other servers). This is not strictly needed to test the SSL connection but helps to debug things when printing the response. --- tests/net_inet/test_tls_sites.py | 5 ++++- tests/net_inet/test_tls_sites.py.exp | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/net_inet/test_tls_sites.py b/tests/net_inet/test_tls_sites.py index 67345fd0b9..bf8071d087 100644 --- a/tests/net_inet/test_tls_sites.py +++ b/tests/net_inet/test_tls_sites.py @@ -6,6 +6,8 @@ try: import ussl as ssl except: import ssl + # CPython only supports server_hostname with SSLContext + ssl = ssl.SSLContext() def test_one(site, opts): @@ -22,7 +24,7 @@ def test_one(site, opts): else: s = ssl.wrap_socket(s) - s.write(b"GET / HTTP/1.0\r\n\r\n") + s.write(b"GET / HTTP/1.0\r\nHost: %s\r\n\r\n" % bytes(site, 'latin')) resp = s.read(4096) # print(resp) @@ -34,6 +36,7 @@ SITES = [ "google.com", "www.google.com", "api.telegram.org", + {"host": "api.pushbullet.com", "sni": True}, # "w9rybpfril.execute-api.ap-southeast-2.amazonaws.com", {"host": "w9rybpfril.execute-api.ap-southeast-2.amazonaws.com", "sni": True}, ] diff --git a/tests/net_inet/test_tls_sites.py.exp b/tests/net_inet/test_tls_sites.py.exp index 12732d1fa5..2f3c113d2f 100644 --- a/tests/net_inet/test_tls_sites.py.exp +++ b/tests/net_inet/test_tls_sites.py.exp @@ -1,4 +1,5 @@ google.com ok www.google.com ok api.telegram.org ok +api.pushbullet.com ok w9rybpfril.execute-api.ap-southeast-2.amazonaws.com ok From d1cd533134cc115dfe15e6c22d60bb7fd7589d88 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 26 Oct 2017 14:00:16 +0300 Subject: [PATCH 58/75] docs/usocket: Elaborate descriptions. Use the "usocket" module name everywhere. Use "MicroPython port" terminology. Suggest to avoid using IPPROTO_* constants in socket() call. --- docs/library/usocket.rst | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/docs/library/usocket.rst b/docs/library/usocket.rst index dfdcd68bc9..a3e987a076 100644 --- a/docs/library/usocket.rst +++ b/docs/library/usocket.rst @@ -68,7 +68,16 @@ Functions .. function:: socket(af=AF_INET, type=SOCK_STREAM, proto=IPPROTO_TCP) - Create a new socket using the given address family, socket type and protocol number. + Create a new socket using the given address family, socket type and + protocol number. Note that specifying *proto* in most cases is not + required (and not recommended, as some MicroPython ports may omit + ``IPPROTO_*`` constants). Instead, *type* argument will select needed + protocol automatically:: + + # Create STREAM TCP socket + socket(AF_INET, SOCK_STREAM) + # Create DGRAM UDP socket + socket(AF_INET, SOCK_DGRAM) .. function:: getaddrinfo(host, port) @@ -80,8 +89,8 @@ Functions The following example shows how to connect to a given url:: - s = socket.socket() - s.connect(socket.getaddrinfo('www.micropython.org', 80)[0][-1]) + s = usocket.socket() + s.connect(usocket.getaddrinfo('www.micropython.org', 80)[0][-1]) .. admonition:: Difference to CPython :class: attention @@ -102,7 +111,7 @@ Constants .. data:: AF_INET AF_INET6 - Address family types. Availability depends on a particular board. + Address family types. Availability depends on a particular `MicroPython port`. .. data:: SOCK_STREAM SOCK_DGRAM @@ -112,7 +121,11 @@ Constants .. data:: IPPROTO_UDP IPPROTO_TCP - IP protocol numbers. + IP protocol numbers. Availability depends on a particular `MicroPython port`. + Note that you don't need to specify these in a call to `usocket.socket()`, + because `SOCK_STREAM` socket type automatically selects `IPPROTO_TCP`, and + `SOCK_DGRAM` - `IPPROTO_UDP`. Thus, the only real use of these constants + is as an argument to `setsockopt()`. .. data:: usocket.SOL_* @@ -281,7 +294,7 @@ Methods Return value: number of bytes written. -.. exception:: socket.error +.. exception:: usocket.error MicroPython does NOT have this exception. From a33fca99a1cc9d2026ee56e81f372c1e695379f5 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 27 Oct 2017 00:27:27 +0300 Subject: [PATCH 59/75] docs/usocket: Document inet_ntop(), inet_pton(). --- docs/library/usocket.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/library/usocket.rst b/docs/library/usocket.rst index a3e987a076..53936505b1 100644 --- a/docs/library/usocket.rst +++ b/docs/library/usocket.rst @@ -105,6 +105,22 @@ Functions from an exception object). The use of negative values is a provisional detail which may change in the future. +.. function:: inet_ntop(af, bin_addr) + + Convert a binary network address *bin_addr* of the given address family *af* + to a textual representation:: + + >>> usocket.inet_ntop(usocket.AF_INET, b"\x7f\0\0\1") + '127.0.0.1' + +.. function:: inet_pton(af, txt_addr) + + Convert a textual network address *txt_addr* of the given address family *af* + to a binary representation:: + + >>> usocket.inet_pton(usocket.AF_INET, "1.2.3.4") + b'\x01\x02\x03\x04' + Constants --------- From c64eb4f8ce20b025bb5762d9ce6e093d15a19dce Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 27 Oct 2017 18:01:25 +1100 Subject: [PATCH 60/75] extmod/vfs: Replace VLA in proxy func with small, static sized array. VLAs can be expensive on stack usage due to stack alignment requirements, and also the fact that extra local variables are needed to track the dynamic size of the stack. So using fixed-size arrays when possible can help to reduce code size and stack usage. In this particular case, the maximum value of n_args in the VLA is 2 and so it's more efficient to just allocate this array with a fixed size. This reduces code size by around 30 bytes on Thumb2 and Xtensa archs. It also reduces total stack usage of the function: on Thumb2 the usage with VLA is between 40 and 48 bytes, which is reduced to 32; on Xtensa, VLA usage is between 64 and 80 bytes, reduced to 32; on x86-64 it's at least 88 bytes reduced to 80. --- extmod/vfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extmod/vfs.c b/extmod/vfs.c index a1cd8d5f67..44ad8ffad6 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -38,6 +38,10 @@ #include "extmod/vfs_fat.h" #endif +// For mp_vfs_proxy_call, the maximum number of additional args that can be passed. +// A fixed maximum size is used to avoid the need for a costly variable array. +#define PROXY_MAX_ARGS (2) + // path is the path to lookup and *path_out holds the path within the VFS // object (starts with / if an absolute path). // Returns MP_VFS_ROOT for root dir (and then path_out is undefined) and @@ -97,6 +101,7 @@ STATIC mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) { } STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_args, const mp_obj_t *args) { + assert(n_args <= PROXY_MAX_ARGS); if (vfs == MP_VFS_NONE) { // mount point not found mp_raise_OSError(MP_ENODEV); @@ -105,7 +110,7 @@ STATIC mp_obj_t mp_vfs_proxy_call(mp_vfs_mount_t *vfs, qstr meth_name, size_t n_ // can't do operation on root dir mp_raise_OSError(MP_EPERM); } - mp_obj_t meth[n_args + 2]; + mp_obj_t meth[2 + PROXY_MAX_ARGS]; mp_load_method(vfs->obj, meth_name, meth); if (args != NULL) { memcpy(meth + 2, args, n_args * sizeof(*args)); From b9923262db4600b3618aa143bb4bcc93bf299814 Mon Sep 17 00:00:00 2001 From: Joar Wandborg Date: Wed, 25 Oct 2017 14:18:11 +0200 Subject: [PATCH 61/75] docs/library/network: Add dhcp_hostname parameter I have not actually tested this, going by information available in https://forum.micropython.org/viewtopic.php?t=2584 --- docs/library/network.rst | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/library/network.rst b/docs/library/network.rst index 3b0aa535e5..99a7c242c0 100644 --- a/docs/library/network.rst +++ b/docs/library/network.rst @@ -422,16 +422,17 @@ parameter should be `id`. Following are commonly supported parameters (availability of a specific parameter depends on network technology type, driver, and `MicroPython port`). - ========= =========== - Parameter Description - ========= =========== - mac MAC address (bytes) - essid WiFi access point name (string) - channel WiFi channel (integer) - hidden Whether ESSID is hidden (boolean) - authmode Authentication mode supported (enumeration, see module constants) - password Access password (string) - ========= =========== + ============= =========== + Parameter Description + ============= =========== + mac MAC address (bytes) + essid WiFi access point name (string) + channel WiFi channel (integer) + hidden Whether ESSID is hidden (boolean) + authmode Authentication mode supported (enumeration, see module constants) + password Access password (string) + dhcp_hostname The DHCP hostname to use + ============= =========== From 9b9dbc58155209e07ab7b9d63d63e5e24db5950c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 21 Oct 2017 13:55:02 +0300 Subject: [PATCH 62/75] py/objtype: Define all special methods if requested. If MICROPY_PY_ALL_SPECIAL_METHODS is defined, actually define all special methods (still subject to gating by e.g. MICROPY_PY_REVERSE_SPECIAL_METHODS). This adds quite a number of qstr's, so should be used sparingly. --- py/objtype.c | 32 ++++++++++++++++++++++++++++++++ tests/unix/extra_coverage.py.exp | 4 ++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/py/objtype.c b/py/objtype.c index e75407683d..d1c1dcba4d 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -342,6 +342,7 @@ const byte mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = { [MP_UNARY_OP_POSITIVE] = MP_QSTR___pos__, [MP_UNARY_OP_NEGATIVE] = MP_QSTR___neg__, [MP_UNARY_OP_INVERT] = MP_QSTR___invert__, + [MP_UNARY_OP_ABS] = MP_QSTR___abs__, #endif #if MICROPY_PY_SYS_GETSIZEOF [MP_UNARY_OP_SIZEOF] = MP_QSTR___sizeof__, @@ -422,8 +423,20 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { [MP_BINARY_OP_IN] = MP_QSTR___contains__, #if MICROPY_PY_ALL_SPECIAL_METHODS + // All inplace methods are optional, and normal methods will be used + // as a fallback. [MP_BINARY_OP_INPLACE_ADD] = MP_QSTR___iadd__, [MP_BINARY_OP_INPLACE_SUBTRACT] = MP_QSTR___isub__, + [MP_BINARY_OP_INPLACE_MULTIPLY] = MP_QSTR___imul__, + [MP_BINARY_OP_INPLACE_FLOOR_DIVIDE] = MP_QSTR___ifloordiv__, + [MP_BINARY_OP_INPLACE_TRUE_DIVIDE] = MP_QSTR___itruediv__, + [MP_BINARY_OP_INPLACE_MODULO] = MP_QSTR___imod__, + [MP_BINARY_OP_INPLACE_POWER] = MP_QSTR___ipow__, + [MP_BINARY_OP_INPLACE_OR] = MP_QSTR___ior__, + [MP_BINARY_OP_INPLACE_XOR] = MP_QSTR___ixor__, + [MP_BINARY_OP_INPLACE_AND] = MP_QSTR___iand__, + [MP_BINARY_OP_INPLACE_LSHIFT] = MP_QSTR___ilshift__, + [MP_BINARY_OP_INPLACE_RSHIFT] = MP_QSTR___irshift__, #endif [MP_BINARY_OP_ADD] = MP_QSTR___add__, @@ -432,12 +445,31 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { [MP_BINARY_OP_MULTIPLY] = MP_QSTR___mul__, [MP_BINARY_OP_FLOOR_DIVIDE] = MP_QSTR___floordiv__, [MP_BINARY_OP_TRUE_DIVIDE] = MP_QSTR___truediv__, + [MP_BINARY_OP_MODULO] = MP_QSTR___mod__, + [MP_BINARY_OP_DIVMOD] = MP_QSTR___divmod__, + [MP_BINARY_OP_POWER] = MP_QSTR___pow__, + [MP_BINARY_OP_OR] = MP_QSTR___or__, + [MP_BINARY_OP_XOR] = MP_QSTR___xor__, + [MP_BINARY_OP_AND] = MP_QSTR___and__, + [MP_BINARY_OP_LSHIFT] = MP_QSTR___lshift__, + [MP_BINARY_OP_RSHIFT] = MP_QSTR___rshift__, #endif #if MICROPY_PY_REVERSE_SPECIAL_METHODS [MP_BINARY_OP_REVERSE_ADD] = MP_QSTR___radd__, [MP_BINARY_OP_REVERSE_SUBTRACT] = MP_QSTR___rsub__, + #if MICROPY_PY_ALL_SPECIAL_METHODS [MP_BINARY_OP_REVERSE_MULTIPLY] = MP_QSTR___rmul__, + [MP_BINARY_OP_REVERSE_FLOOR_DIVIDE] = MP_QSTR___rfloordiv__, + [MP_BINARY_OP_REVERSE_TRUE_DIVIDE] = MP_QSTR___rtruediv__, + [MP_BINARY_OP_REVERSE_MODULO] = MP_QSTR___rmod__, + [MP_BINARY_OP_REVERSE_POWER] = MP_QSTR___rpow__, + [MP_BINARY_OP_REVERSE_OR] = MP_QSTR___ror__, + [MP_BINARY_OP_REVERSE_XOR] = MP_QSTR___rxor__, + [MP_BINARY_OP_REVERSE_AND] = MP_QSTR___rand__, + [MP_BINARY_OP_REVERSE_LSHIFT] = MP_QSTR___rlshift__, + [MP_BINARY_OP_REVERSE_RSHIFT] = MP_QSTR___rrshift__, + #endif #endif }; diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 4c4f666639..1db46ab8f4 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -39,8 +39,8 @@ ementation 0 0 # runtime utils -TypeError: unsupported type for : 'str' -TypeError: unsupported types for : 'str', 'str' +TypeError: unsupported type for __abs__: 'str' +TypeError: unsupported types for __divmod__: 'str', 'str' Warning: test # format float ? From 0e80f345f88c5db7c2353a5a9d29ed08b0af42f4 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 27 Oct 2017 22:29:15 +0300 Subject: [PATCH 63/75] py/objtype: Introduce MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS. This allows to configure support for inplace special methods separately, similar to "normal" and reverse special methods. This is useful, because inplace methods are "the most optional" ones, for example, if inplace methods aren't defined, the operation will be executed using normal methods instead. As a caveat, __iadd__ and __isub__ are implemented even if MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS isn't defined. This is similar to the state of affairs before binary operations refactor, and allows to run existing tests even if MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS isn't defined. --- py/mpconfig.h | 20 ++++++++++++++------ py/objtype.c | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/py/mpconfig.h b/py/mpconfig.h index 44de3beebe..1694a13601 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -765,16 +765,24 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_TIMEOUTERROR (0) #endif -// Whether to support complete set of special methods -// for user classes, or only the most used ones. "Reverse" -// methods are controlled by MICROPY_PY_REVERSE_SPECIAL_METHODS -// below. +// Whether to support complete set of special methods for user +// classes, or only the most used ones. "Inplace" methods are +// controlled by MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS below. +// "Reverse" methods are controlled by +// MICROPY_PY_REVERSE_SPECIAL_METHODS below. #ifndef MICROPY_PY_ALL_SPECIAL_METHODS #define MICROPY_PY_ALL_SPECIAL_METHODS (0) #endif -// Whether to support reverse arithmetic operarions methods -// (__radd__, etc.) +// Whether to support all inplace arithmetic operarion methods +// (__imul__, etc.) +#ifndef MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS +#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (0) +#endif + +// Whether to support reverse arithmetic operarion methods +// (__radd__, etc.). Additionally gated by +// MICROPY_PY_ALL_SPECIAL_METHODS. #ifndef MICROPY_PY_REVERSE_SPECIAL_METHODS #define MICROPY_PY_REVERSE_SPECIAL_METHODS (0) #endif diff --git a/py/objtype.c b/py/objtype.c index d1c1dcba4d..6e2ab6c9a8 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -422,11 +422,11 @@ const byte mp_binary_op_method_name[MP_BINARY_OP_NUM_RUNTIME] = { // MP_BINARY_OP_NOT_EQUAL, // a != b calls a == b and inverts result [MP_BINARY_OP_IN] = MP_QSTR___contains__, - #if MICROPY_PY_ALL_SPECIAL_METHODS // All inplace methods are optional, and normal methods will be used // as a fallback. [MP_BINARY_OP_INPLACE_ADD] = MP_QSTR___iadd__, [MP_BINARY_OP_INPLACE_SUBTRACT] = MP_QSTR___isub__, + #if MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS [MP_BINARY_OP_INPLACE_MULTIPLY] = MP_QSTR___imul__, [MP_BINARY_OP_INPLACE_FLOOR_DIVIDE] = MP_QSTR___ifloordiv__, [MP_BINARY_OP_INPLACE_TRUE_DIVIDE] = MP_QSTR___itruediv__, From 24c8eda744e6c13866d70caec1b5c316caee64ac Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 28 Oct 2017 13:05:57 +0300 Subject: [PATCH 64/75] unix: Enable MICROPY_PY_REVERSE_SPECIAL_METHODS. With inplace methods now disabled by default, it makes sense to enable reverse methods, as they allow for more useful features, e.g. allow for datetime module to implement both 2 * HOUR and HOUR * 2 (where HOUR is e.g. timedelta object). --- ports/unix/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 50b979279a..db382e0a71 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -85,6 +85,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) From 8f9af63c20fbdb49a0e9a29248b8c7abe6f01b61 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 29 Oct 2017 19:52:56 +0200 Subject: [PATCH 65/75] lib/axtls: Update, support for SSL_EAGAIN return code. A step towards implementing non-blocking stream support for SSL. --- lib/axtls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/axtls b/lib/axtls index 9b3092eb3b..dac9176cac 160000 --- a/lib/axtls +++ b/lib/axtls @@ -1 +1 @@ -Subproject commit 9b3092eb3b4b230a63c0c389bfbd3c55682c620f +Subproject commit dac9176cac58cc5e49669a9a4d404a6f6dd7cc10 From 05a2bb888f9cd3dc3e005b8eea4258e20a39cba2 Mon Sep 17 00:00:00 2001 From: Yuval Langer Date: Sun, 29 Oct 2017 22:27:06 +0200 Subject: [PATCH 66/75] docs/reference/isr_rules: Minor typo correction. --- docs/reference/isr_rules.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/isr_rules.rst b/docs/reference/isr_rules.rst index 5009f30f79..2db261c09d 100644 --- a/docs/reference/isr_rules.rst +++ b/docs/reference/isr_rules.rst @@ -80,7 +80,7 @@ example causes two LED's to flash at different rates. self.led.toggle() red = Foo(pyb.Timer(4, freq=1), pyb.LED(1)) - greeen = Foo(pyb.Timer(2, freq=0.8), pyb.LED(2)) + green = Foo(pyb.Timer(2, freq=0.8), pyb.LED(2)) In this example the ``red`` instance associates timer 4 with LED 1: when a timer 4 interrupt occurs ``red.cb()`` is called causing LED 1 to change state. The ``green`` instance operates similarly: a timer 2 interrupt From 74ec52d85758ad9da7a3abb24257511b22d74964 Mon Sep 17 00:00:00 2001 From: Eric Poulsen Date: Thu, 26 Oct 2017 21:17:35 -0700 Subject: [PATCH 67/75] extmod/modussl: Add finaliser support for ussl objects. Per the comment found here https://github.com/micropython/micropython-esp32/issues/209#issuecomment-339855157, this patch adds finaliser code to prevent memory leaks from ussl objects, which is especially useful when memory for a ussl context is allocated outside the uPy heap. This patch is in-line with the finaliser code found in many modsocket implementations for various ports. This feature is configured via MICROPY_PY_USSL_FINALISER and is disabled by default because there may be issues using it when the ussl state *is* allocated on the uPy heap, rather than externally. --- extmod/modussl_axtls.c | 7 +++++++ extmod/modussl_mbedtls.c | 7 +++++++ py/mpconfig.h | 2 ++ 3 files changed, 16 insertions(+) diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 719a65cd1f..3ad65ebf32 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -51,7 +51,11 @@ struct ssl_args { STATIC const mp_obj_type_t ussl_socket_type; STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { +#if MICROPY_PY_USSL_FINALISER + mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); +#else mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t); +#endif o->base.type = &ussl_socket_type; o->buf = NULL; o->bytes_left = 0; @@ -178,6 +182,9 @@ STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&socket_close_obj) }, +#if MICROPY_PY_USSL_FINALISER + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socket_close_obj) }, +#endif }; STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_table); diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index d7316cb4ae..59dceb6cff 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -111,7 +111,11 @@ int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { +#if MICROPY_PY_USSL_FINALISER + mp_obj_ssl_socket_t *o = m_new_obj_with_finaliser(mp_obj_ssl_socket_t); +#else mp_obj_ssl_socket_t *o = m_new_obj(mp_obj_ssl_socket_t); +#endif o->base.type = &ussl_socket_type; int ret; @@ -272,6 +276,9 @@ STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&socket_close_obj) }, +#if MICROPY_PY_USSL_FINALISER + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socket_close_obj) }, +#endif { MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) }, }; diff --git a/py/mpconfig.h b/py/mpconfig.h index 1694a13601..6a32ea2a6f 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1109,6 +1109,8 @@ typedef double mp_float_t; #ifndef MICROPY_PY_USSL #define MICROPY_PY_USSL (0) +// Whether to add finaliser code to ussl objects +#define MICROPY_PY_USSL_FINALISER (0) #endif #ifndef MICROPY_PY_WEBSOCKET From 10b76a9620b7407ffed8366d71f5463496f98838 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 30 Oct 2017 15:41:37 +1100 Subject: [PATCH 68/75] extmod/modussl_mbedtls: Allow to compile with unix coverage build. Fixes a few C warnings. No functional changes. --- extmod/modussl_mbedtls.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index 59dceb6cff..69a64d2f7a 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -65,23 +65,30 @@ struct ssl_args { STATIC const mp_obj_type_t ussl_socket_type; -void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) { +#ifdef MBEDTLS_DEBUG_C +STATIC void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) { + (void)ctx; + (void)level; printf("DBG:%s:%04d: %s\n", file, line, str); } +#endif // TODO: FIXME! -int null_entropy_func(void *data, unsigned char *output, size_t len) { +STATIC int null_entropy_func(void *data, unsigned char *output, size_t len) { + (void)data; + (void)output; + (void)len; // enjoy random bytes return 0; } -int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { +STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { mp_obj_t sock = *(mp_obj_t*)ctx; const mp_stream_p_t *sock_stream = mp_get_stream_raise(sock, MP_STREAM_OP_WRITE); int err; - int out_sz = sock_stream->write(sock, buf, len, &err); + mp_uint_t out_sz = sock_stream->write(sock, buf, len, &err); if (out_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(err)) { return MBEDTLS_ERR_SSL_WANT_WRITE; @@ -92,13 +99,13 @@ int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { } } -int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { +STATIC int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { mp_obj_t sock = *(mp_obj_t*)ctx; const mp_stream_p_t *sock_stream = mp_get_stream_raise(sock, MP_STREAM_OP_READ); int err; - int out_sz = sock_stream->read(sock, buf, len, &err); + mp_uint_t out_sz = sock_stream->read(sock, buf, len, &err); if (out_sz == MP_STREAM_ERROR) { if (mp_is_nonblocking_error(err)) { return MBEDTLS_ERR_SSL_WANT_READ; From 6fb093282b9f987618063e1945da98d2e4ed3334 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 30 Oct 2017 18:03:54 +0200 Subject: [PATCH 69/75] docs/ussl: Fix module name refs and use "MicroPython port" term. --- docs/library/ussl.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/library/ussl.rst b/docs/library/ussl.rst index c71b283ccb..3ec609f67b 100644 --- a/docs/library/ussl.rst +++ b/docs/library/ussl.rst @@ -13,7 +13,7 @@ facilities for network sockets, both client-side and server-side. Functions --------- -.. function:: ssl.wrap_socket(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None) +.. 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), and returns an instance of ssl.SSLSocket, which wraps the underlying stream in @@ -23,12 +23,12 @@ Functions server-side SSL socket should be created from a normal socket returned from `accept()` on a non-SSL listening server socket. - Depending on the underlying module implementation for a particular board, - some or all keyword arguments above may be not supported. + Depending on the underlying module implementation in a particular + `MicroPython port`, some or all keyword arguments above may be not supported. .. warning:: - Some implementations of ``ssl`` module do NOT validate server certificates, + Some implementations of ``ussl`` module do NOT validate server certificates, which makes an SSL connection established prone to man-in-the-middle attacks. Exceptions @@ -41,8 +41,8 @@ Exceptions Constants --------- -.. data:: ssl.CERT_NONE - ssl.CERT_OPTIONAL - ssl.CERT_REQUIRED +.. data:: ussl.CERT_NONE + ussl.CERT_OPTIONAL + ussl.CERT_REQUIRED Supported values for *cert_reqs* parameter. From 4dd523adbbe8852c9e02191d4a67ea6c7aa72aec Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 30 Oct 2017 19:49:37 +0200 Subject: [PATCH 70/75] docs/esp8266/general: Add section on TLS limitations. --- docs/esp8266/general.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/esp8266/general.rst b/docs/esp8266/general.rst index e23acb469b..96a4545323 100644 --- a/docs/esp8266/general.rst +++ b/docs/esp8266/general.rst @@ -145,3 +145,43 @@ or by an exeption, for example using try/finally:: # Use sock finally: sock.close() + + +SSL/TLS limitations +~~~~~~~~~~~~~~~~~~~ + +ESP8266 uses `axTLS `_ library, which is one +of the smallest TLS libraries with the compatible licensing. However, it +also has some known issues/limitations: + +1. No support for Diffie-Hellman (DH) key exchange and Elliptic-curve + cryptography (ECC). This means it can't work with sites which force + the use of these features (it works ok with classic RSA certifactes). +2. Half-duplex communication nature. axTLS uses a single buffer for both + sending and receiving, which leads to considerable memory saving and + works well with protocols like HTTP. But there may be problems with + protocols which don't follow classic request-response model. + +Besides axTLS own limitations, the configuration used for MicroPython is +highly optimized for code size, which leads to additional limitations +(these may be lifted in the future): + +3. Optimized RSA algorithms are not enabled, which may lead to slow + SSL handshakes. +4. Stored sessions are not supported (may allow faster repeated connections + to the same site in some circumstances). + +Besides axTLS specific limitations described above, there's another generic +limitation with usage of TLS on the low-memory devices: + +5. The TLS standard specifies the maximum length of the TLS record (unit + of TLS communication, the entire record must be buffered before it can + be processed) as 16KB. That's almost half of the available ESP8266 memory, + and inside a more or less advanced application would be hard to allocate + due to memory fragmentation issues. As a compromise, a smaller buffer is + used, with the idea that the most interesting usage for SSL would be + accessing various REST APIs, which usually require much smaller messages. + The buffers size is on the order of 5KB, and is adjusted from time to + 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. From b81fbf938f1bb9cc798a3933476c80c0d9b83992 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 31 Oct 2017 00:28:28 +0200 Subject: [PATCH 71/75] docs/usocket: Document that settimeout() isn't supported by all ports. And describe an alternative of using uselect.poll(). --- docs/library/usocket.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/library/usocket.rst b/docs/library/usocket.rst index 53936505b1..fab05b652b 100644 --- a/docs/library/usocket.rst +++ b/docs/library/usocket.rst @@ -237,12 +237,30 @@ Methods .. method:: socket.settimeout(value) + **Note**: Not every port supports this method, see below. + Set a timeout on blocking socket operations. The value argument can be a nonnegative floating point number expressing seconds, or None. If a non-zero value is given, subsequent socket operations will raise an `OSError` exception if the timeout period value has elapsed before the operation has completed. If zero is given, the socket is put in non-blocking mode. If None is given, the socket is put in blocking mode. + 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:: + + # Instead of: + s.settimeout(1.0) # time in seconds + s.read(10) # may timeout + + # Use: + poller = uselect.poll() + poller.register(s, uselect.POLLIN) + res = poller.poll(1000) # time in milliseconds + if not res: + # s is still not ready for input, i.e. operation timed out + .. admonition:: Difference to CPython :class: attention From 02b4b23319a84d5b96b9394d8c379ef0549fd208 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 31 Oct 2017 22:01:56 +1100 Subject: [PATCH 72/75] Revert "py/{mkenv.mk,mkrules.mk}: Append .exe for Windows executable files." This reverts commit 3289b9b7a76a1230b6bb631e191a47bfc6c7a8ee. The commit broke building on MINGW because the filename became micropython.exe.exe. A proper solution to support more Windows build environments requires more thought and testing. --- py/mkenv.mk | 6 ------ py/mkrules.mk | 8 ++++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/py/mkenv.mk b/py/mkenv.mk index 8b637e9ac6..b167b2533d 100644 --- a/py/mkenv.mk +++ b/py/mkenv.mk @@ -59,13 +59,7 @@ LD += -m32 endif MAKE_FROZEN = $(TOP)/tools/make-frozen.py -# allow mpy-cross (for WSL) and mpy-cross.exe (for cygwin) to coexist -ifeq ($(OS),Windows_NT) -MPY_CROSS = $(TOP)/mpy-cross/mpy-cross.exe -PROG_EXT = .exe -else MPY_CROSS = $(TOP)/mpy-cross/mpy-cross -endif MPY_TOOL = $(TOP)/tools/mpy-tool.py all: diff --git a/py/mkrules.mk b/py/mkrules.mk index fd579557f3..13545eb6f0 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -111,7 +111,7 @@ FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) # to build .mpy files from .py files -$(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py $(MPY_CROSS) +$(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py $(TOP)/mpy-cross/mpy-cross @$(ECHO) "MPY $<" $(Q)$(MKDIR) -p $(dir $@) $(Q)$(MPY_CROSS) -o $@ -s $(<:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $< @@ -133,13 +133,13 @@ $(PROG): $(OBJ) # we may want to compile using Thumb, but link with non-Thumb libc. $(Q)$(CC) -o $@ $^ $(LIB) $(LDFLAGS) ifndef DEBUG - $(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $(PROG)$(PROG_EXT) + $(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $(PROG) endif - $(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $(PROG)$(PROG_EXT) + $(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $(PROG) clean: clean-prog clean-prog: - $(RM) -f $(PROG)$(PROG_EXT) + $(RM) -f $(PROG) $(RM) -f $(PROG).map .PHONY: clean-prog From 80e3f07e7f760707f20200b6b6a706308d92158c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 31 Oct 2017 14:45:26 +0200 Subject: [PATCH 73/75] docs/ure: Add "|" (alternative) to the list of supported operators. --- docs/library/ure.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/library/ure.rst b/docs/library/ure.rst index 67f4f54a1d..ebae1db5f1 100644 --- a/docs/library/ure.rst +++ b/docs/library/ure.rst @@ -34,6 +34,8 @@ Supported operators are: ``'+?'`` +``'|'`` + ``'()'`` Grouping. Each group is capturing (a substring it captures can be accessed with `match.group()` method). From 5ae9586541f93065d8dd46862f1f226274c4c5c4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 1 Nov 2017 11:00:30 +1100 Subject: [PATCH 74/75] teensy: Get port compiling without any warnings. --- ports/teensy/teensy_hal.c | 1 + ports/teensy/uart.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ports/teensy/teensy_hal.c b/ports/teensy/teensy_hal.c index 84d68cff89..7ce82f1d2a 100644 --- a/ports/teensy/teensy_hal.c +++ b/ports/teensy/teensy_hal.c @@ -49,6 +49,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) { void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { // send stdout to UART and USB CDC VCP if (MP_STATE_PORT(pyb_stdio_uart) != NULL) { + void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len); uart_tx_strn_cooked(MP_STATE_PORT(pyb_stdio_uart), str, len); } if (usb_vcp_is_enabled()) { diff --git a/ports/teensy/uart.c b/ports/teensy/uart.c index 3d5111217b..a8cfd63eac 100644 --- a/ports/teensy/uart.c +++ b/ports/teensy/uart.c @@ -426,11 +426,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_send_obj, 1, pyb_uart_send); /// /// Return value: if `recv` is an integer then a new buffer of the bytes received, /// otherwise the same buffer that was passed in to `recv`. +#if 0 STATIC const mp_arg_t pyb_uart_recv_args[] = { { MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, }; #define PYB_UART_RECV_NUM_ARGS MP_ARRAY_SIZE(pyb_uart_recv_args) +#endif STATIC mp_obj_t pyb_uart_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { // TODO assumes transmission size is 8-bits wide From fe45d78b1edd6d2202c3544797885cb0b12d4f03 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 1 Nov 2017 11:19:56 +1100 Subject: [PATCH 75/75] docs: Bump version to 1.9.3. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index a8df373150..5352d16e45 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -98,7 +98,7 @@ copyright = '2014-2017, Damien P. George, Paul Sokolovsky, and contributors' # # We don't follow "The short X.Y version" vs "The full version, including alpha/beta/rc tags" # breakdown, so use the same version identifier for both to avoid confusion. -version = release = '1.9.2' +version = release = '1.9.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages.