From 522460ceac36d42f2903d160e6812df66d2688ed Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 21:53:07 +1100 Subject: [PATCH 01/15] stm32/boards/NUCLEO_F767ZI: Fix up comments about HCLK computation. --- ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h index 3f23d77d48163..8c7a34339ee6f 100644 --- a/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h @@ -16,20 +16,12 @@ #define MICROPY_BOARD_EARLY_INIT NUCLEO_F767ZI_board_early_init void NUCLEO_F767ZI_board_early_init(void); -// HSE is 25MHz -// VCOClock = HSE * PLLN / PLLM = 25 MHz * 432 / 25 = 432 MHz -// SYSCLK = VCOClock / PLLP = 432 MHz / 2 = 216 MHz -// USB/SDMMC/RNG Clock = VCOClock / PLLQ = 432 MHz / 9 = 48 MHz +// HSE is 8MHz #define MICROPY_HW_CLK_PLLM (4) #define MICROPY_HW_CLK_PLLN (216) #define MICROPY_HW_CLK_PLLP (RCC_PLLP_DIV2) #define MICROPY_HW_CLK_PLLQ (9) - -// From the reference manual, for 2.7V to 3.6V -// 151-180 MHz => 5 wait states -// 181-210 MHz => 6 wait states -// 211-216 MHz => 7 wait states -#define MICROPY_HW_FLASH_LATENCY FLASH_LATENCY_7 // 210-216 MHz needs 7 wait states +#define MICROPY_HW_FLASH_LATENCY (FLASH_LATENCY_7) // 210-216 MHz needs 7 wait states // UART config #define MICROPY_HW_UART2_TX (pin_D5) From d9e5d6f9915a1ab1f63a192be45dee5739f16f8f Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 21:53:41 +1100 Subject: [PATCH 02/15] stm32/mphalport: Add mp_hal_get_mac() helper function. --- ports/stm32/mphalport.c | 9 +++++++++ ports/stm32/mphalport.h | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 206221721bb80..8d85195bc0992 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -162,3 +162,12 @@ void mp_hal_pin_config_speed(mp_hal_pin_obj_t pin_obj, uint32_t speed) { uint32_t pin = pin_obj->pin; gpio->OSPEEDR = (gpio->OSPEEDR & ~(3 << (2 * pin))) | (speed << (2 * pin)); } + +MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { + buf[0] = 'H'; + buf[1] = 'J'; + buf[2] = '0'; + buf[3] = 0; + buf[4] = 0; + buf[5] = ((uint8_t*)MP_HAL_UNIQUE_ID_ADDRESS)[0] << 2 | idx; +} diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h index a60365087e7fd..d828bb9dd4983 100644 --- a/ports/stm32/mphalport.h +++ b/ports/stm32/mphalport.h @@ -79,3 +79,12 @@ void mp_hal_gpio_clock_enable(GPIO_TypeDef *gpio); void mp_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt); bool mp_hal_pin_config_alt(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint8_t fn, uint8_t unit); void mp_hal_pin_config_speed(mp_hal_pin_obj_t pin_obj, uint32_t speed); + +enum { + MP_HAL_MAC_WLAN0 = 0, + MP_HAL_MAC_WLAN1, + MP_HAL_MAC_BDADDR, + MP_HAL_MAC_ETH0, +}; + +void mp_hal_get_mac(int idx, uint8_t buf[6]); From 608a8ebe31a36c645d434f518264b9a8bc088f5b Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 21:54:58 +1100 Subject: [PATCH 03/15] extmod/modlwip: Add concurrency protection macros. Some users of this module may require the LwIP stack to run at an elevated priority, to protect against concurrency issues with processing done by the underlying network interface. Since LwIP doesn't provide such protection it must be done here (the other option is to run LwIP in a separate thread, and use thread protection mechanisms, but that is a more heavyweight solution). --- extmod/modlwip.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/extmod/modlwip.c b/extmod/modlwip.c index bd952111d656a..83104a487f6f1 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -447,9 +447,12 @@ STATIC mp_uint_t lwip_udp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui len = 0xffff; } + MICROPY_PY_LWIP_ENTER + // FIXME: maybe PBUF_ROM? struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); if (p == NULL) { + MICROPY_PY_LWIP_EXIT *_errno = MP_ENOMEM; return -1; } @@ -467,6 +470,8 @@ STATIC mp_uint_t lwip_udp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui pbuf_free(p); + MICROPY_PY_LWIP_EXIT + // udp_sendto can return 1 on occasion for ESP8266 port. It's not known why // but it seems that the send actually goes through without error in this case. // So we treat such cases as a success until further investigation. @@ -505,10 +510,14 @@ STATIC mp_uint_t lwip_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ struct pbuf *p = socket->incoming.pbuf; + MICROPY_PY_LWIP_ENTER + u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : p->tot_len), 0); pbuf_free(p); socket->incoming.pbuf = NULL; + MICROPY_PY_LWIP_EXIT + return (mp_uint_t) result; } @@ -526,11 +535,14 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui // Check for any pending errors STREAM_ERROR_CHECK(socket); + MICROPY_PY_LWIP_ENTER + u16_t available = tcp_sndbuf(socket->pcb.tcp); if (available == 0) { // Non-blocking socket if (socket->timeout == 0) { + MICROPY_PY_LWIP_EXIT *_errno = MP_EAGAIN; return MP_STREAM_ERROR; } @@ -543,11 +555,13 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui // reset) by error callback. // Avoid sending too small packets, so wait until at least 16 bytes available while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + MICROPY_PY_LWIP_EXIT if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) { *_errno = MP_ETIMEDOUT; return MP_STREAM_ERROR; } poll_sockets(); + MICROPY_PY_LWIP_REENTER } // While we waited, something could happen @@ -563,6 +577,8 @@ STATIC mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui err = tcp_output(socket->pcb.tcp); } + MICROPY_PY_LWIP_EXIT + if (err != ERR_OK) { *_errno = error_lookup_table[-err]; return MP_STREAM_ERROR; @@ -608,6 +624,8 @@ STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ } } + MICROPY_PY_LWIP_ENTER + assert(socket->pcb.tcp != NULL); struct pbuf *p = socket->incoming.pbuf; @@ -633,6 +651,8 @@ STATIC mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_ } tcp_recved(socket->pcb.tcp, len); + MICROPY_PY_LWIP_EXIT + return len; } @@ -865,16 +885,21 @@ STATIC mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { mp_raise_OSError(MP_EALREADY); } } + // Register our receive callback. + MICROPY_PY_LWIP_ENTER tcp_recv(socket->pcb.tcp, _lwip_tcp_recv); socket->state = STATE_CONNECTING; err = tcp_connect(socket->pcb.tcp, &dest, port, _lwip_tcp_connected); if (err != ERR_OK) { + MICROPY_PY_LWIP_EXIT socket->state = STATE_NEW; mp_raise_OSError(error_lookup_table[-err]); } socket->peer_port = (mp_uint_t)port; memcpy(socket->peer, &dest, sizeof(socket->peer)); + MICROPY_PY_LWIP_EXIT + // And now we wait... if (socket->timeout != -1) { for (mp_uint_t retries = socket->timeout / 100; retries--;) { @@ -1209,6 +1234,8 @@ STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in); mp_uint_t ret; + MICROPY_PY_LWIP_ENTER + if (request == MP_STREAM_POLL) { uintptr_t flags = arg; ret = 0; @@ -1259,6 +1286,7 @@ STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ bool socket_is_listener = false; if (socket->pcb.tcp == NULL) { + MICROPY_PY_LWIP_EXIT return 0; } @@ -1305,6 +1333,8 @@ STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ ret = MP_STREAM_ERROR; } + MICROPY_PY_LWIP_EXIT + return ret; } @@ -1452,7 +1482,10 @@ STATIC mp_obj_t lwip_getaddrinfo(size_t n_args, const mp_obj_t *args) { getaddrinfo_state_t state; state.status = 0; + MICROPY_PY_LWIP_ENTER err_t ret = dns_gethostbyname(host, (ip_addr_t*)&state.ipaddr, lwip_getaddrinfo_cb, &state); + MICROPY_PY_LWIP_EXIT + switch (ret) { case ERR_OK: // cached From c02989074b9e119a6dfa5c4480e829d077f0e7df Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:11:26 +1100 Subject: [PATCH 04/15] lib/netutils: Add function to print tracing info for Ethernet frames. --- lib/netutils/netutils.h | 6 ++ lib/netutils/trace.c | 150 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 lib/netutils/trace.c diff --git a/lib/netutils/netutils.h b/lib/netutils/netutils.h index 4befc90db0e91..9261337858963 100644 --- a/lib/netutils/netutils.h +++ b/lib/netutils/netutils.h @@ -29,6 +29,10 @@ #define NETUTILS_IPV4ADDR_BUFSIZE 4 +#define NETUTILS_TRACE_IS_TX (0x0001) +#define NETUTILS_TRACE_PAYLOAD (0x0002) +#define NETUTILS_TRACE_NEWLINE (0x0004) + typedef enum _netutils_endian_t { NETUTILS_LITTLE, NETUTILS_BIG, @@ -47,4 +51,6 @@ void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian // puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); +void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags); + #endif // MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H diff --git a/lib/netutils/trace.c b/lib/netutils/trace.c new file mode 100644 index 0000000000000..7c79713b32532 --- /dev/null +++ b/lib/netutils/trace.c @@ -0,0 +1,150 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" +#include "lib/netutils/netutils.h" + +static uint32_t get_be16(const uint8_t *buf) { + return buf[0] << 8 | buf[1]; +} + +static uint32_t get_be32(const uint8_t *buf) { + return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; +} + +static void dump_hex_bytes(const mp_print_t *print, size_t len, const uint8_t *buf) { + for (size_t i = 0; i < len; ++i) { + mp_printf(print, " %02x", buf[i]); + } +} + +static const char *ethertype_str(uint16_t type) { + // A value between 0x0000 - 0x05dc (inclusive) indicates a length, not type + switch (type) { + case 0x0800: return "IPv4"; + case 0x0806: return "ARP"; + case 0x86dd: return "IPv6"; + default: return NULL; + } +} + +void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags) { + mp_printf(print, "[% 8d] ETH%cX len=%u", mp_hal_ticks_ms(), flags & NETUTILS_TRACE_IS_TX ? 'T' : 'R', len); + mp_printf(print, " dst=%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + mp_printf(print, " src=%02x:%02x:%02x:%02x:%02x:%02x", buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]); + + const char *ethertype = ethertype_str(buf[12] << 8 | buf[13]); + if (ethertype) { + mp_printf(print, " type=%s", ethertype); + } else { + mp_printf(print, " type=0x%04x", buf[12] << 8 | buf[13]); + } + if (len > 14) { + len -= 14; + buf += 14; + if (buf[-2] == 0x08 && buf[-1] == 0x00 && buf[0] == 0x45) { + // IPv4 packet + len = get_be16(buf + 2); + mp_printf(print, " srcip=%u.%u.%u.%u dstip=%u.%u.%u.%u", + buf[12], buf[13], buf[14], buf[15], + buf[16], buf[17], buf[18], buf[19]); + uint8_t prot = buf[9]; + buf += 20; + len -= 20; + if (prot == 6) { + // TCP packet + uint16_t srcport = get_be16(buf); + uint16_t dstport = get_be16(buf + 2); + uint32_t seqnum = get_be32(buf + 4); + uint32_t acknum = get_be32(buf + 8); + uint16_t dataoff_flags = get_be16(buf + 12); + uint16_t winsz = get_be16(buf + 14); + mp_printf(print, " TCP srcport=%u dstport=%u seqnum=%u acknum=%u dataoff=%u flags=%x winsz=%u", + srcport, dstport, (unsigned)seqnum, (unsigned)acknum, dataoff_flags >> 12, dataoff_flags & 0x1ff, winsz); + buf += 20; + len -= 20; + if (dataoff_flags >> 12 > 5) { + mp_printf(print, " opts="); + size_t opts_len = ((dataoff_flags >> 12) - 5) * 4; + dump_hex_bytes(print, opts_len, buf); + buf += opts_len; + len -= opts_len; + } + } else if (prot == 17) { + // UDP packet + uint16_t srcport = get_be16(buf); + uint16_t dstport = get_be16(buf + 2); + mp_printf(print, " UDP srcport=%u dstport=%u", srcport, dstport); + len = get_be16(buf + 4); + buf += 8; + if ((srcport == 67 && dstport == 68) || (srcport == 68 && dstport == 67)) { + // DHCP + if (srcport == 67) { + mp_printf(print, " DHCPS"); + } else { + mp_printf(print, " DHCPC"); + } + dump_hex_bytes(print, 12 + 16 + 16 + 64, buf); + size_t n = 12 + 16 + 16 + 64 + 128; + len -= n; + buf += n; + mp_printf(print, " opts:"); + switch (buf[6]) { + case 1: mp_printf(print, " DISCOVER"); break; + case 2: mp_printf(print, " OFFER"); break; + case 3: mp_printf(print, " REQUEST"); break; + case 4: mp_printf(print, " DECLINE"); break; + case 5: mp_printf(print, " ACK"); break; + case 6: mp_printf(print, " NACK"); break; + case 7: mp_printf(print, " RELEASE"); break; + case 8: mp_printf(print, " INFORM"); break; + } + } + } else { + // Non-UDP packet + mp_printf(print, " prot=%u", prot); + } + } else if (buf[-2] == 0x86 && buf[-1] == 0xdd && (buf[0] >> 4) == 6) { + // IPv6 packet + uint32_t h = get_be32(buf); + uint16_t l = get_be16(buf + 4); + mp_printf(print, " tclass=%u flow=%u len=%u nexthdr=%u hoplimit=%u", (unsigned)((h >> 20) & 0xff), (unsigned)(h & 0xfffff), l, buf[6], buf[7]); + mp_printf(print, " srcip="); + dump_hex_bytes(print, 16, buf + 8); + mp_printf(print, " dstip="); + dump_hex_bytes(print, 16, buf + 24); + buf += 40; + len -= 40; + } + if (flags & NETUTILS_TRACE_PAYLOAD) { + mp_printf(print, " data="); + dump_hex_bytes(print, len, buf); + } + } + if (flags & NETUTILS_TRACE_NEWLINE) { + mp_printf(print, "\n"); + } +} From 4897f9b0404b76df93da486408eb2aa705a0327d Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:20:48 +1100 Subject: [PATCH 05/15] stm32/eth: Add low-level Ethernet MAC driver. --- ports/stm32/Makefile | 2 + ports/stm32/eth.c | 616 ++++++++++++++++++++++++++++++++ ports/stm32/eth.h | 39 ++ ports/stm32/lwip_inc/lwipopts.h | 1 + 4 files changed, 658 insertions(+) create mode 100644 ports/stm32/eth.c create mode 100644 ports/stm32/eth.h diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 20a26f23443f3..f509246682909 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -110,6 +110,7 @@ SRC_LIB = $(addprefix lib/,\ oofatfs/option/unicode.c \ mp-readline/readline.c \ netutils/netutils.c \ + netutils/trace.c \ timeutils/timeutils.c \ utils/pyexec.c \ utils/interrupt_char.c \ @@ -240,6 +241,7 @@ SRC_C = \ can.c \ usb.c \ wdt.c \ + eth.c \ gccollect.c \ help.c \ machine_i2c.c \ diff --git a/ports/stm32/eth.c b/ports/stm32/eth.c new file mode 100644 index 0000000000000..74db532f2c54f --- /dev/null +++ b/ports/stm32/eth.c @@ -0,0 +1,616 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/mphal.h" +#include "py/mperrno.h" +#include "lib/netutils/netutils.h" +#include "lwip/etharp.h" +#include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "netif/ethernet.h" +#include "pin_static_af.h" +#include "modnetwork.h" +#include "eth.h" + +#if defined(MICROPY_HW_ETH_MDC) + +// ETH PHY register definitions (for LAN8742) + +#undef PHY_BCR +#define PHY_BCR (0x0000) +#define PHY_BCR_SOFT_RESET (0x8000) +#define PHY_BCR_AUTONEG_EN (0x1000) + +#undef PHY_BSR +#define PHY_BSR (0x0001) +#define PHY_BSR_LINK_STATUS (0x0004) +#define PHY_BSR_AUTONEG_DONE (0x0020) + +#define PHY_SCSR (0x001f) +#define PHY_SCSR_SPEED_Pos (2) +#define PHY_SCSR_SPEED_Msk (7 << PHY_SCSR_SPEED_Pos) +#define PHY_SCSR_SPEED_10HALF (1 << PHY_SCSR_SPEED_Pos) +#define PHY_SCSR_SPEED_10FULL (5 << PHY_SCSR_SPEED_Pos) +#define PHY_SCSR_SPEED_100HALF (2 << PHY_SCSR_SPEED_Pos) +#define PHY_SCSR_SPEED_100FULL (6 << PHY_SCSR_SPEED_Pos) + +// ETH DMA RX and TX descriptor definitions + +#define RX_DESCR_0_OWN_Pos (31) +#define RX_DESCR_0_FL_Pos (16) +#define RX_DESCR_0_FL_Msk (0x3fff << RX_DESCR_0_FL_Pos) +#define RX_DESCR_1_RER_Pos (15) +#define RX_DESCR_1_RCH_Pos (14) +#define RX_DESCR_1_RBS2_Pos (16) +#define RX_DESCR_1_RBS1_Pos (0) + +#define TX_DESCR_0_OWN_Pos (31) +#define TX_DESCR_0_LS_Pos (29) +#define TX_DESCR_0_FS_Pos (28) +#define TX_DESCR_0_DP_Pos (26) +#define TX_DESCR_0_CIC_Pos (22) +#define TX_DESCR_0_TER_Pos (21) +#define TX_DESCR_0_TCH_Pos (20) +#define TX_DESCR_1_TBS1_Pos (0) + +// Configuration values + +#define PHY_INIT_TIMEOUT_MS (10000) + +#define RX_BUF_SIZE (1524) // includes 4-byte CRC at end +#define TX_BUF_SIZE (1524) + +#define RX_BUF_NUM (5) +#define TX_BUF_NUM (5) + +typedef struct _eth_dma_rx_descr_t { + volatile uint32_t rdes0, rdes1, rdes2, rdes3; +} eth_dma_rx_descr_t; + +typedef struct _eth_dma_tx_descr_t { + volatile uint32_t tdes0, tdes1, tdes2, tdes3; +} eth_dma_tx_descr_t; + +typedef struct _eth_dma_t { + eth_dma_rx_descr_t rx_descr[RX_BUF_NUM]; + eth_dma_tx_descr_t tx_descr[TX_BUF_NUM]; + uint8_t rx_buf[RX_BUF_NUM * RX_BUF_SIZE] __attribute__((aligned(4))); + uint8_t tx_buf[TX_BUF_NUM * TX_BUF_SIZE] __attribute__((aligned(4))); + size_t rx_descr_idx; + size_t tx_descr_idx; + uint8_t padding[16384 - 15408]; +} eth_dma_t; + +typedef struct _eth_t { + mod_network_nic_type_t base; + uint32_t trace_flags; + struct netif netif; + struct dhcp dhcp_struct; +} eth_t; + +static eth_dma_t eth_dma __attribute__((aligned(16384))); + +eth_t eth_instance; + +STATIC void eth_mac_deinit(eth_t *self); +STATIC void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf); + +STATIC void eth_phy_write(uint32_t reg, uint32_t val) { + while (ETH->MACMIIAR & ETH_MACMIIAR_MB) { + } + ETH->MACMIIDR = val; + uint32_t ar = ETH->MACMIIAR; + ar = reg << ETH_MACMIIAR_MR_Pos | (ar & ETH_MACMIIAR_CR_Msk) | ETH_MACMIIAR_MW | ETH_MACMIIAR_MB; + ETH->MACMIIAR = ar; + while (ETH->MACMIIAR & ETH_MACMIIAR_MB) { + } +} + +STATIC uint32_t eth_phy_read(uint32_t reg) { + while (ETH->MACMIIAR & ETH_MACMIIAR_MB) { + } + uint32_t ar = ETH->MACMIIAR; + ar = reg << ETH_MACMIIAR_MR_Pos | (ar & ETH_MACMIIAR_CR_Msk) | ETH_MACMIIAR_MB; + ETH->MACMIIAR = ar; + while (ETH->MACMIIAR & ETH_MACMIIAR_MB) { + } + return ETH->MACMIIDR; +} + +STATIC void mpu_config(uint32_t region, uint32_t base_addr, uint32_t size) { + __DMB(); + + // Disable MPU + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; + MPU->CTRL = 0; + + // Config MPU region + MPU->RNR = region; + MPU->RBAR = base_addr; + MPU->RASR = + MPU_INSTRUCTION_ACCESS_DISABLE << MPU_RASR_XN_Pos + | MPU_REGION_FULL_ACCESS << MPU_RASR_AP_Pos + | MPU_TEX_LEVEL1 << MPU_RASR_TEX_Pos + | MPU_ACCESS_SHAREABLE << MPU_RASR_S_Pos + | MPU_ACCESS_NOT_CACHEABLE << MPU_RASR_C_Pos + | MPU_ACCESS_NOT_BUFFERABLE << MPU_RASR_B_Pos + | 0x00 << MPU_RASR_SRD_Pos + | size << MPU_RASR_SIZE_Pos + | MPU_REGION_ENABLE << MPU_RASR_ENABLE_Pos; + + // Enable MPU + MPU->CTRL = MPU_PRIVILEGED_DEFAULT | MPU_CTRL_ENABLE_Msk; + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; + + __DSB(); + __ISB(); +} + +void eth_init(eth_t *self, int mac_idx) { + mp_hal_get_mac(mac_idx, &self->netif.hwaddr[0]); + self->netif.hwaddr_len = 6; +} + +void eth_set_trace(eth_t *self, uint32_t value) { + self->trace_flags = value; +} + +STATIC int eth_mac_init(eth_t *self) { + // Configure MPU + mpu_config(MPU_REGION_NUMBER0, (uint32_t)ð_dma, MPU_REGION_SIZE_16KB); + + // Configure GPIO + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDC, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDC); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_MDIO, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_MDIO); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_REF_CLK, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_REF_CLK); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_CRS_DV, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_CRS_DV); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD0); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_RXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_RXD1); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TX_EN, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TX_EN); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD0, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD0); + mp_hal_pin_config_alt_static(MICROPY_HW_ETH_RMII_TXD1, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_NONE, STATIC_AF_ETH_RMII_TXD1); + + __HAL_RCC_ETH_CLK_ENABLE(); + __HAL_RCC_ETHMAC_FORCE_RESET(); + + // Select RMII interface + __HAL_RCC_SYSCFG_CLK_ENABLE(); + SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; + + __HAL_RCC_ETHMAC_RELEASE_RESET(); + + __HAL_RCC_ETHMAC_CLK_SLEEP_ENABLE(); + __HAL_RCC_ETHMACTX_CLK_SLEEP_ENABLE(); + __HAL_RCC_ETHMACRX_CLK_SLEEP_ENABLE(); + + // Do a soft reset of the MAC core + ETH->DMABMR = ETH_DMABMR_SR; + mp_hal_delay_ms(2); + + // Wait for soft reset to finish + uint32_t t0 = mp_hal_ticks_ms(); + while (ETH->DMABMR & ETH_DMABMR_SR) { + if (mp_hal_ticks_ms() - t0 > 1000) { + return -MP_ETIMEDOUT; + } + } + + // Set MII clock range + uint32_t hclk = HAL_RCC_GetHCLKFreq(); + uint32_t cr_div; + if (hclk < 35000000) { + cr_div = ETH_MACMIIAR_CR_Div16; + } else if (hclk < 60000000) { + cr_div = ETH_MACMIIAR_CR_Div26; + } else if (hclk < 100000000) { + cr_div = ETH_MACMIIAR_CR_Div42; + } else if (hclk < 150000000) { + cr_div = ETH_MACMIIAR_CR_Div62; + } else { + cr_div = ETH_MACMIIAR_CR_Div102; + } + ETH->MACMIIAR = cr_div; + + // Reset the PHY + eth_phy_write(PHY_BCR, PHY_BCR_SOFT_RESET); + mp_hal_delay_ms(50); + + // Wait for the PHY link to be established + int phy_state = 0; + t0 = mp_hal_ticks_ms(); + while (phy_state != 3) { + if (mp_hal_ticks_ms() - t0 > PHY_INIT_TIMEOUT_MS) { + eth_mac_deinit(self); + return -MP_ETIMEDOUT; + } + uint16_t bcr = eth_phy_read(0); + uint16_t bsr = eth_phy_read(1); + switch (phy_state) { + case 0: + if (!(bcr & PHY_BCR_SOFT_RESET)) { + phy_state = 1; + } + break; + case 1: + if (bsr & PHY_BSR_LINK_STATUS) { + eth_phy_write(PHY_BCR, PHY_BCR_AUTONEG_EN); + phy_state = 2; + } + break; + case 2: + if ((bsr & (PHY_BSR_AUTONEG_DONE | PHY_BSR_LINK_STATUS)) + == (PHY_BSR_AUTONEG_DONE | PHY_BSR_LINK_STATUS)) { + phy_state = 3; + } + break; + } + mp_hal_delay_ms(2); + } + + // Get register with link status + uint16_t phy_scsr = eth_phy_read(PHY_SCSR); + + // Burst mode configuration + ETH->DMABMR = 0; + mp_hal_delay_ms(2); + + // Select DMA interrupts + ETH->DMAIER = + ETH_DMAIER_NISE // enable normal interrupts + | ETH_DMAIER_RIE // enable RX interrupt + ; + + // Configure RX descriptor lists + for (size_t i = 0; i < RX_BUF_NUM; ++i) { + eth_dma.rx_descr[i].rdes0 = 1 << RX_DESCR_0_OWN_Pos; + eth_dma.rx_descr[i].rdes1 = + 1 << RX_DESCR_1_RCH_Pos // chained + | RX_BUF_SIZE << RX_DESCR_1_RBS1_Pos + ; + eth_dma.rx_descr[i].rdes2 = (uint32_t)ð_dma.rx_buf[i * RX_BUF_SIZE]; + eth_dma.rx_descr[i].rdes3 = (uint32_t)ð_dma.rx_descr[(i + 1) % RX_BUF_NUM]; + } + ETH->DMARDLAR = (uint32_t)ð_dma.rx_descr[0]; + eth_dma.rx_descr_idx = 0; + + // Configure TX descriptor lists + for (size_t i = 0; i < TX_BUF_NUM; ++i) { + eth_dma.tx_descr[i].tdes0 = 1 << TX_DESCR_0_TCH_Pos; + eth_dma.tx_descr[i].tdes1 = 0; + eth_dma.tx_descr[i].tdes2 = 0; + eth_dma.tx_descr[i].tdes3 = (uint32_t)ð_dma.tx_descr[(i + 1) % TX_BUF_NUM]; + } + ETH->DMATDLAR = (uint32_t)ð_dma.tx_descr[0]; + eth_dma.tx_descr_idx = 0; + + // Configure DMA + ETH->DMAOMR = + ETH_DMAOMR_RSF // read from RX FIFO after a full frame is written + | ETH_DMAOMR_TSF // transmit when a full frame is in TX FIFO (needed by errata) + ; + mp_hal_delay_ms(2); + + // Select MAC filtering options + ETH->MACFFR = + ETH_MACFFR_RA // pass all frames up + ; + mp_hal_delay_ms(2); + + // Set MAC address + u8_t *mac = &self->netif.hwaddr[0]; + ETH->MACA0HR = mac[5] << 8 | mac[4]; + mp_hal_delay_ms(2); + ETH->MACA0LR = mac[3] << 24 | mac[2] << 16 | mac[1] << 8 | mac[0]; + mp_hal_delay_ms(2); + + // Set main MAC control register + ETH->MACCR = + (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_10FULL ? ETH_MACCR_DM + : (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_100HALF ? ETH_MACCR_FES + : (phy_scsr & PHY_SCSR_SPEED_Msk) == PHY_SCSR_SPEED_100FULL ? (ETH_MACCR_FES | ETH_MACCR_DM) + : 0 + ; + mp_hal_delay_ms(2); + + // Start MAC layer + ETH->MACCR |= + ETH_MACCR_TE // enable TX + | ETH_MACCR_RE // enable RX + ; + mp_hal_delay_ms(2); + + // Start DMA layer + ETH->DMAOMR |= + ETH_DMAOMR_ST // start TX + | ETH_DMAOMR_SR // start RX + ; + mp_hal_delay_ms(2); + + // Enable interrupts + NVIC_SetPriority(ETH_IRQn, IRQ_PRI_PENDSV); + HAL_NVIC_EnableIRQ(ETH_IRQn); + + return 0; +} + +STATIC void eth_mac_deinit(eth_t *self) { + (void)self; + HAL_NVIC_DisableIRQ(ETH_IRQn); + __HAL_RCC_ETHMAC_FORCE_RESET(); + __HAL_RCC_ETHMAC_RELEASE_RESET(); + __HAL_RCC_ETH_CLK_DISABLE(); +} + +STATIC int eth_tx_buf_get(size_t len, uint8_t **buf) { + if (len > TX_BUF_SIZE) { + return -MP_EINVAL; + } + + // Wait for DMA to release the current TX descriptor (if it has it) + eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma.tx_descr_idx]; + uint32_t t0 = mp_hal_ticks_ms(); + for (;;) { + if (!(tx_descr->tdes0 & (1 << TX_DESCR_0_OWN_Pos))) { + break; + } + if (mp_hal_ticks_ms() - t0 > 1000) { + return -MP_ETIMEDOUT; + } + } + + // Update TX descriptor with length, buffer pointer and linked list pointer + *buf = ð_dma.tx_buf[eth_dma.tx_descr_idx * TX_BUF_SIZE]; + tx_descr->tdes1 = len << TX_DESCR_1_TBS1_Pos; + tx_descr->tdes2 = (uint32_t)*buf; + tx_descr->tdes3 = (uint32_t)ð_dma.tx_descr[(eth_dma.tx_descr_idx + 1) % TX_BUF_NUM]; + + return 0; +} + +STATIC int eth_tx_buf_send(void) { + // Get TX descriptor and move to next one + eth_dma_tx_descr_t *tx_descr = ð_dma.tx_descr[eth_dma.tx_descr_idx]; + eth_dma.tx_descr_idx = (eth_dma.tx_descr_idx + 1) % TX_BUF_NUM; + + // Schedule to send next outgoing frame + tx_descr->tdes0 = + 1 << TX_DESCR_0_OWN_Pos // owned by DMA + | 1 << TX_DESCR_0_LS_Pos // last segment + | 1 << TX_DESCR_0_FS_Pos // first segment + | 3 << TX_DESCR_0_CIC_Pos // enable all checksums inserted by hardware + | 1 << TX_DESCR_0_TCH_Pos // TX descriptor is chained + ; + + // Notify ETH DMA that there is a new TX descriptor for sending + __DMB(); + if (ETH->DMASR & ETH_DMASR_TBUS) { + ETH->DMASR = ETH_DMASR_TBUS; + ETH->DMATPDR = 0; + } + + return 0; +} + +STATIC void eth_dma_rx_free(void) { + // Get RX descriptor, RX buffer and move to next one + eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma.rx_descr_idx]; + uint8_t *buf = ð_dma.rx_buf[eth_dma.rx_descr_idx * RX_BUF_SIZE]; + eth_dma.rx_descr_idx = (eth_dma.rx_descr_idx + 1) % RX_BUF_NUM; + + // Schedule to get next incoming frame + rx_descr->rdes1 = + 1 << RX_DESCR_1_RCH_Pos // RX descriptor is chained + | RX_BUF_SIZE << RX_DESCR_1_RBS1_Pos // maximum buffer length + ; + rx_descr->rdes2 = (uint32_t)buf; + rx_descr->rdes3 = (uint32_t)ð_dma.rx_descr[eth_dma.rx_descr_idx]; + rx_descr->rdes0 = 1 << RX_DESCR_0_OWN_Pos; // owned by DMA + + // Notify ETH DMA that there is a new RX descriptor available + __DMB(); + ETH->DMARPDR = 0; +} + +void ETH_IRQHandler(void) { + uint32_t sr = ETH->DMASR; + ETH->DMASR = ETH_DMASR_NIS; + if (sr & ETH_DMASR_RS) { + ETH->DMASR = ETH_DMASR_RS; + for (;;) { + eth_dma_rx_descr_t *rx_descr = ð_dma.rx_descr[eth_dma.rx_descr_idx]; + if (rx_descr->rdes0 & (1 << RX_DESCR_0_OWN_Pos)) { + // No more RX descriptors ready to read + break; + } + + // Get RX buffer containing new frame + size_t len = (rx_descr->rdes0 & RX_DESCR_0_FL_Msk) >> RX_DESCR_0_FL_Pos; + len -= 4; // discard CRC at end + uint8_t *buf = (uint8_t*)rx_descr->rdes2; + + // Process frame + eth_process_frame(ð_instance, len, buf); + eth_dma_rx_free(); + } + } +} + +/*******************************************************************************/ +// ETH-LwIP bindings + +#define TRACE_ASYNC_EV (0x0001) +#define TRACE_ETH_TX (0x0002) +#define TRACE_ETH_RX (0x0004) +#define TRACE_ETH_FULL (0x0008) + +#define ETH_ENTER() uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV) +#define ETH_EXIT() restore_irq_pri(irq_state) + +STATIC void eth_trace(eth_t *self, size_t len, const void *data, unsigned int flags) { + if (((flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_TX)) + || (!(flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_RX))) { + const uint8_t *buf; + if (len == (size_t)-1) { + // data is a pbuf + const struct pbuf *pbuf = data; + buf = pbuf->payload; + len = pbuf->len; // restricted to print only the first chunk of the pbuf + } else { + // data is actual data buffer + buf = data; + } + if (self->trace_flags & TRACE_ETH_FULL) { + flags |= NETUTILS_TRACE_PAYLOAD; + } + netutils_ethernet_trace(MP_PYTHON_PRINTER, len, buf, flags); + } +} + +STATIC err_t eth_netif_output(struct netif *netif, struct pbuf *p) { + // This function should always be called from a context where PendSV-level IRQs are disabled + + LINK_STATS_INC(link.xmit); + eth_trace(netif->state, (size_t)-1, p, NETUTILS_TRACE_IS_TX | NETUTILS_TRACE_NEWLINE); + + uint8_t *buf; + int ret = eth_tx_buf_get(p->tot_len, &buf); + if (ret == 0) { + pbuf_copy_partial(p, buf, p->tot_len, 0); + ret = eth_tx_buf_send(); + } + + return ret ? ERR_BUF : ERR_OK; +} + +STATIC err_t eth_netif_init(struct netif *netif) { + netif->linkoutput = eth_netif_output; + netif->output = etharp_output; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP; + // Checksums only need to be checked on incoming frames, not computed on outgoing frames + NETIF_SET_CHECKSUM_CTRL(netif, + NETIF_CHECKSUM_CHECK_IP + | NETIF_CHECKSUM_CHECK_UDP + | NETIF_CHECKSUM_CHECK_TCP + | NETIF_CHECKSUM_CHECK_ICMP + | NETIF_CHECKSUM_CHECK_ICMP6); + return ERR_OK; +} + +STATIC void eth_lwip_init(eth_t *self) { + ip_addr_t ipconfig[4]; + IP4_ADDR(&ipconfig[0], 0, 0, 0, 0); + IP4_ADDR(&ipconfig[2], 192, 168, 0, 1); + IP4_ADDR(&ipconfig[1], 255, 255, 255, 0); + IP4_ADDR(&ipconfig[3], 8, 8, 8, 8); + + ETH_ENTER(); + + struct netif *n = &self->netif; + n->name[0] = 'e'; + n->name[1] = '0'; + netif_add(n, &ipconfig[0], &ipconfig[1], &ipconfig[2], self, eth_netif_init, ethernet_input); + netif_set_hostname(n, "MPY"); + netif_set_default(n); + netif_set_up(n); + + dns_setserver(0, &ipconfig[3]); + dhcp_set_struct(n, &self->dhcp_struct); + dhcp_start(n); + + netif_set_link_up(n); + + ETH_EXIT(); +} + +STATIC void eth_lwip_deinit(eth_t *self) { + ETH_ENTER(); + for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) { + if (netif == &self->netif) { + netif_remove(netif); + netif->ip_addr.addr = 0; + netif->flags = 0; + } + } + ETH_EXIT(); +} + +STATIC void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf) { + eth_trace(self, len, buf, NETUTILS_TRACE_NEWLINE); + + struct netif *netif = &self->netif; + if (netif->flags & NETIF_FLAG_LINK_UP) { + struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + if (p != NULL) { + pbuf_take(p, buf, len); + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } + } +} + +struct netif *eth_netif(eth_t *self) { + return &self->netif; +} + +int eth_link_status(eth_t *self) { + struct netif *netif = &self->netif; + if ((netif->flags & (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) + == (NETIF_FLAG_UP | NETIF_FLAG_LINK_UP)) { + if (netif->ip_addr.addr != 0) { + return 3; // link up + } else { + return 2; // link no-ip; + } + } else { + int s = eth_phy_read(0) | eth_phy_read(0x10) << 16; + if (s == 0) { + return 0; // link down + } else { + return 1; // link join + } + } +} + +int eth_start(eth_t *self) { + eth_lwip_deinit(self); + int ret = eth_mac_init(self); + if (ret < 0) { + return ret; + } + eth_lwip_init(self); + return 0; +} + +int eth_stop(eth_t *self) { + eth_lwip_deinit(self); + eth_mac_deinit(self); + return 0; +} + +#endif // defined(MICROPY_HW_ETH_MDC) diff --git a/ports/stm32/eth.h b/ports/stm32/eth.h new file mode 100644 index 0000000000000..fd46c7fa7c01b --- /dev/null +++ b/ports/stm32/eth.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32_ETH_H +#define MICROPY_INCLUDED_STM32_ETH_H + +typedef struct _eth_t eth_t; +extern eth_t eth_instance; + +void eth_init(eth_t *self, int mac_idx); +void eth_set_trace(eth_t *self, uint32_t value); +struct netif *eth_netif(eth_t *self); +int eth_link_status(eth_t *self); +int eth_start(eth_t *self); +int eth_stop(eth_t *self); + +#endif // MICROPY_INCLUDED_STM32_ETH_H diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index b8ab8a2ab095f..64ff104f7126b 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -8,6 +8,7 @@ #define MEM_ALIGNMENT 4 #define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 #define LWIP_ARP 1 #define LWIP_ETHERNET 1 From 9079689c7ea3b8d47c739e69bbdeedc80fba7746 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:24:01 +1100 Subject: [PATCH 06/15] stm32/network_lan: Add high-level network.LAN interface to ETH driver. --- ports/stm32/Makefile | 1 + ports/stm32/modnetwork.c | 4 + ports/stm32/modnetwork.h | 1 + ports/stm32/network_lan.c | 160 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 ports/stm32/network_lan.c diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index f509246682909..1a75bf0504a1d 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -253,6 +253,7 @@ SRC_C = \ moduos.c \ modutime.c \ modusocket.c \ + network_lan.c \ modnetwork.c \ extint.c \ usrsw.c \ diff --git a/ports/stm32/modnetwork.c b/ports/stm32/modnetwork.c index 4fa3d8c05bf16..0a0cd0e4a5d80 100644 --- a/ports/stm32/modnetwork.c +++ b/ports/stm32/modnetwork.c @@ -114,6 +114,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(network_route_obj, network_route); STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_network) }, + #if defined(MICROPY_HW_ETH_MDC) + { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&network_lan_type) }, + #endif + #if MICROPY_PY_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, #endif diff --git a/ports/stm32/modnetwork.h b/ports/stm32/modnetwork.h index dd8113ebf53a5..41a0017dab007 100644 --- a/ports/stm32/modnetwork.h +++ b/ports/stm32/modnetwork.h @@ -44,6 +44,7 @@ typedef struct _mod_network_nic_type_t { void (*poll_callback)(void *data, struct netif *netif); } mod_network_nic_type_t; +extern const mp_obj_type_t network_lan_type; extern const mp_obj_type_t mod_network_nic_type_wiznet5k; void mod_network_lwip_poll_wrapper(uint32_t ticks_ms); diff --git a/ports/stm32/network_lan.c b/ports/stm32/network_lan.c new file mode 100644 index 0000000000000..95a5b31f484b2 --- /dev/null +++ b/ports/stm32/network_lan.c @@ -0,0 +1,160 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "lwip/netif.h" +#include "modnetwork.h" +#include "eth.h" + +typedef struct _network_lan_obj_t { + mp_obj_base_t base; + eth_t *eth; +} network_lan_obj_t; + +STATIC const network_lan_obj_t network_lan_eth0 = { { &network_lan_type }, ð_instance }; + +STATIC void network_lan_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(self_in); + struct netif *netif = eth_netif(self->eth); + int status = eth_link_status(self->eth); + mp_printf(print, "", + status, + netif->ip_addr.addr & 0xff, + netif->ip_addr.addr >> 8 & 0xff, + netif->ip_addr.addr >> 16 & 0xff, + netif->ip_addr.addr >> 24 + ); +} + +STATIC mp_obj_t network_lan_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 0, false); + const network_lan_obj_t *self = &network_lan_eth0; + eth_init(self->eth, MP_HAL_MAC_ETH0); + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t network_lan_active(size_t n_args, const mp_obj_t *args) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args == 1) { + return mp_obj_new_bool(eth_link_status(self->eth)); + } else { + int ret; + if (mp_obj_is_true(args[1])) { + ret = eth_start(self->eth); + } else { + ret = eth_stop(self->eth); + } + if (ret < 0) { + mp_raise_OSError(-ret); + } + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_active_obj, 1, 2, network_lan_active); + +STATIC mp_obj_t network_lan_isconnected(mp_obj_t self_in) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(eth_link_status(self->eth) == 3); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(network_lan_isconnected_obj, network_lan_isconnected); + +STATIC mp_obj_t network_lan_ifconfig(size_t n_args, const mp_obj_t *args) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ifconfig(eth_netif(self->eth), n_args - 1, args + 1); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_ifconfig_obj, 1, 2, network_lan_ifconfig); + +STATIC mp_obj_t network_lan_status(size_t n_args, const mp_obj_t *args) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + (void)self; + + if (n_args == 1) { + // No arguments: return link status + return MP_OBJ_NEW_SMALL_INT(eth_link_status(self->eth)); + } + + mp_raise_ValueError("unknown status param"); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_lan_status_obj, 1, 2, network_lan_status); + +STATIC mp_obj_t network_lan_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_lan_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (kwargs->used == 0) { + // Get config value + if (n_args != 2) { + mp_raise_TypeError("must query one param"); + } + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_mac: { + return mp_obj_new_bytes(ð_netif(self->eth)->hwaddr[0], 6); + } + default: + mp_raise_ValueError("unknown config param"); + } + } else { + // Set config value(s) + if (n_args != 1) { + mp_raise_TypeError("can't specify pos and kw args"); + } + + for (size_t i = 0; i < kwargs->alloc; ++i) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + mp_map_elem_t *e = &kwargs->table[i]; + switch (mp_obj_str_get_qstr(e->key)) { + case MP_QSTR_trace: { + eth_set_trace(self->eth, mp_obj_get_int(e->value)); + break; + } + default: + mp_raise_ValueError("unknown config param"); + } + } + } + + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(network_lan_config_obj, 1, network_lan_config); + +STATIC const mp_rom_map_elem_t network_lan_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_lan_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_lan_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_lan_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_lan_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_lan_config_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(network_lan_locals_dict, network_lan_locals_dict_table); + +const mp_obj_type_t network_lan_type = { + { &mp_type_type }, + .name = MP_QSTR_LAN, + .print = network_lan_print, + .make_new = network_lan_make_new, + .locals_dict = (mp_obj_dict_t*)&network_lan_locals_dict, +}; From 82979a8ce4f3c0794e7ddc48f32863d7f605b9ca Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:24:36 +1100 Subject: [PATCH 07/15] stm32/mpconfigport.h: Enable lwIP concurrency protection mechanism. --- ports/stm32/mpconfigport.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index 7ae2ac77b0468..712697c80e03f 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -357,6 +357,11 @@ static inline mp_uint_t disable_irq(void) { #define MICROPY_THREAD_YIELD() #endif +// The LwIP interface must run at a raised IRQ priority +#define MICROPY_PY_LWIP_ENTER uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_LWIP_REENTER irq_state = raise_irq_pri(IRQ_PRI_PENDSV); +#define MICROPY_PY_LWIP_EXIT restore_irq_pri(irq_state); + // We need an implementation of the log2 function which is not a macro #define MP_NEED_LOG2 (1) From a45752df1ae720b093928b034b56ea19611e1928 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:25:06 +1100 Subject: [PATCH 08/15] stm32/modnetwork: Don't call NIC callback if it's NULL. --- ports/stm32/modnetwork.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ports/stm32/modnetwork.c b/ports/stm32/modnetwork.c index 0a0cd0e4a5d80..ea43f75573680 100644 --- a/ports/stm32/modnetwork.c +++ b/ports/stm32/modnetwork.c @@ -57,7 +57,9 @@ STATIC void pyb_lwip_poll(void) { for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) { if (netif->flags & NETIF_FLAG_LINK_UP) { mod_network_nic_type_t *nic = netif->state; - nic->poll_callback(nic, netif); + if (nic->poll_callback) { + nic->poll_callback(nic, netif); + } } } // Run the lwIP internal updates From 3a67f11c37ebc53c7277fca25fb6d5f6443042e7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:26:54 +1100 Subject: [PATCH 09/15] stm32/boards/NUCLEO_F767ZI: Enable lwIP and Ethernet peripheral. --- ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h | 13 +++++++++++++ ports/stm32/boards/NUCLEO_F767ZI/pins.csv | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h index 8c7a34339ee6f..e3f255de4d43a 100644 --- a/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F767ZI/mpconfigboard.h @@ -6,6 +6,8 @@ #define MICROPY_HW_BOARD_NAME "NUCLEO-F767ZI" #define MICROPY_HW_MCU_NAME "STM32F767" +#define MICROPY_PY_LWIP (1) + #define MICROPY_HW_HAS_SWITCH (1) #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_ENABLE_RNG (1) @@ -70,3 +72,14 @@ void NUCLEO_F767ZI_board_early_init(void); #define MICROPY_HW_USB_FS (1) #define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) #define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) + +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_C1) +#define MICROPY_HW_ETH_MDIO (pin_A2) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_A1) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_A7) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_C4) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_C5) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_G11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_G13) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_B13) diff --git a/ports/stm32/boards/NUCLEO_F767ZI/pins.csv b/ports/stm32/boards/NUCLEO_F767ZI/pins.csv index 3cae615dabfe9..d84f8e9d1957b 100644 --- a/ports/stm32/boards/NUCLEO_F767ZI/pins.csv +++ b/ports/stm32/boards/NUCLEO_F767ZI/pins.csv @@ -70,3 +70,12 @@ UART6_RX,PG9 SPI_B_NSS,PA4 SPI_B_SCK,PB3 SPI_B_MOSI,PB5 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PB13 From fac230acd1acc81151c464b84f09c6b878e994e9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:27:24 +1100 Subject: [PATCH 10/15] stm32/boards/STM32F7DISC: Enable lwIP and Ethernet peripheral. --- ports/stm32/boards/STM32F7DISC/mpconfigboard.h | 14 ++++++++++++++ ports/stm32/boards/STM32F7DISC/pins.csv | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/ports/stm32/boards/STM32F7DISC/mpconfigboard.h b/ports/stm32/boards/STM32F7DISC/mpconfigboard.h index 792206c400735..ff135b8a75426 100644 --- a/ports/stm32/boards/STM32F7DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F7DISC/mpconfigboard.h @@ -1,6 +1,8 @@ #define MICROPY_HW_BOARD_NAME "F7DISC" #define MICROPY_HW_MCU_NAME "STM32F746" +#define MICROPY_PY_LWIP (1) + #define MICROPY_HW_HAS_SWITCH (1) #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_HAS_SDCARD (1) @@ -81,6 +83,18 @@ void STM32F7DISC_board_early_init(void); /*#define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_J12)*/ #define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_C1) +#define MICROPY_HW_ETH_MDIO (pin_A2) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_A1) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_A7) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_C4) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_C5) +#define MICROPY_HW_ETH_RMII_RXER (pin_G2) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_G11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_G13) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_G14) + // SDRAM #define MICROPY_HW_SDRAM_SIZE (64 / 8 * 1024 * 1024) // 64 Mbit #define MICROPY_HW_SDRAM_STARTUP_TEST (1) diff --git a/ports/stm32/boards/STM32F7DISC/pins.csv b/ports/stm32/boards/STM32F7DISC/pins.csv index dfafe67f52b38..99fa969322200 100644 --- a/ports/stm32/boards/STM32F7DISC/pins.csv +++ b/ports/stm32/boards/STM32F7DISC/pins.csv @@ -53,6 +53,16 @@ VCP_TX,PA9 VCP_RX,PB7 CAN_TX,PB13 CAN_RX,PB12 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_RXER,PG2 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PG14 SDRAM_SDCKE0,PC3 SDRAM_SDNE0,PH3 SDRAM_SDCLK,PG8 From 80f3d1627d08e964795e325b3549e52f48e50a9c Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 22:27:48 +1100 Subject: [PATCH 11/15] stm32/boards/STM32F769DISC: Enable lwIP and Ethernet peripheral. --- ports/stm32/boards/STM32F769DISC/mpconfigboard.h | 13 +++++++++++++ ports/stm32/boards/STM32F769DISC/pins.csv | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h index 4f41a81f9e16c..fac7c98fd8dcf 100644 --- a/ports/stm32/boards/STM32F769DISC/mpconfigboard.h +++ b/ports/stm32/boards/STM32F769DISC/mpconfigboard.h @@ -5,6 +5,8 @@ #define MICROPY_HW_BOARD_NAME "F769DISC" #define MICROPY_HW_MCU_NAME "STM32F769" +#define MICROPY_PY_LWIP (1) + #define MICROPY_HW_HAS_SWITCH (1) #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_HAS_SDCARD (1) @@ -76,6 +78,17 @@ // USB config (CN15 - USB OTG HS with external PHY) #define MICROPY_HW_USB_HS (1) +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_C1) +#define MICROPY_HW_ETH_MDIO (pin_A2) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_A1) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_A7) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_C4) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_C5) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_G11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_G13) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_G14) + #if 0 // Optional SDRAM configuration; requires SYSCLK <= 200MHz #define MICROPY_HW_SDRAM_SIZE (128 * 1024 * 1024 / 8) // 128 Mbit diff --git a/ports/stm32/boards/STM32F769DISC/pins.csv b/ports/stm32/boards/STM32F769DISC/pins.csv index f587d44c1451f..2dcc374419273 100644 --- a/ports/stm32/boards/STM32F769DISC/pins.csv +++ b/ports/stm32/boards/STM32F769DISC/pins.csv @@ -69,6 +69,15 @@ UART5_TX,PC12 UART5_RX,PD2 CAN2_TX,PB13 CAN2_RX,PB12 +ETH_REF_CLK,PA1 +ETH_MDIO,PA2 +ETH_CRS_DV,PA7 +ETH_MDC,PC1 +ETH_RXD0,PC4 +ETH_RXD1,PC5 +ETH_TX_EN,PG11 +ETH_TXD0,PG13 +ETH_TXD1,PG14 FMC_SDCKE0,PH2 FMC_SDNE0,PH3 FMC_SDCLK,PG8 From 62ca69e2e759e9dff47bfb7aabecacfaa11d5387 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 22 Feb 2019 23:58:03 +1100 Subject: [PATCH 12/15] stm32/network_lan: Don't build if ETH pins are not defined. --- ports/stm32/network_lan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/stm32/network_lan.c b/ports/stm32/network_lan.c index 95a5b31f484b2..8c210802ceb81 100644 --- a/ports/stm32/network_lan.c +++ b/ports/stm32/network_lan.c @@ -30,6 +30,8 @@ #include "modnetwork.h" #include "eth.h" +#if defined(MICROPY_HW_ETH_MDC) + typedef struct _network_lan_obj_t { mp_obj_base_t base; eth_t *eth; @@ -158,3 +160,5 @@ const mp_obj_type_t network_lan_type = { .make_new = network_lan_make_new, .locals_dict = (mp_obj_dict_t*)&network_lan_locals_dict, }; + +#endif // defined(MICROPY_HW_ETH_MDC) From 4ecda175a06571e3c50bdff9d0dbdbd1f34b771a Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 25 Feb 2019 16:43:47 +1100 Subject: [PATCH 13/15] stm32/eth: Replace ETH_ENTER/ETH_EXIT with MICROPY_PY_LWIP_xxx macros. --- ports/stm32/eth.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ports/stm32/eth.c b/ports/stm32/eth.c index 74db532f2c54f..c3a18bde77d06 100644 --- a/ports/stm32/eth.c +++ b/ports/stm32/eth.c @@ -467,9 +467,6 @@ void ETH_IRQHandler(void) { #define TRACE_ETH_RX (0x0004) #define TRACE_ETH_FULL (0x0008) -#define ETH_ENTER() uint32_t irq_state = raise_irq_pri(IRQ_PRI_PENDSV) -#define ETH_EXIT() restore_irq_pri(irq_state) - STATIC void eth_trace(eth_t *self, size_t len, const void *data, unsigned int flags) { if (((flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_TX)) || (!(flags & NETUTILS_TRACE_IS_TX) && (self->trace_flags & TRACE_ETH_RX))) { @@ -528,7 +525,7 @@ STATIC void eth_lwip_init(eth_t *self) { IP4_ADDR(&ipconfig[1], 255, 255, 255, 0); IP4_ADDR(&ipconfig[3], 8, 8, 8, 8); - ETH_ENTER(); + MICROPY_PY_LWIP_ENTER struct netif *n = &self->netif; n->name[0] = 'e'; @@ -544,11 +541,11 @@ STATIC void eth_lwip_init(eth_t *self) { netif_set_link_up(n); - ETH_EXIT(); + MICROPY_PY_LWIP_EXIT } STATIC void eth_lwip_deinit(eth_t *self) { - ETH_ENTER(); + MICROPY_PY_LWIP_ENTER for (struct netif *netif = netif_list; netif != NULL; netif = netif->next) { if (netif == &self->netif) { netif_remove(netif); @@ -556,7 +553,7 @@ STATIC void eth_lwip_deinit(eth_t *self) { netif->flags = 0; } } - ETH_EXIT(); + MICROPY_PY_LWIP_EXIT } STATIC void eth_process_frame(eth_t *self, size_t len, const uint8_t *buf) { From 10b1f5fb3bd80c0be7928dbb48d6825ba8df7324 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 25 Feb 2019 16:45:53 +1100 Subject: [PATCH 14/15] stm32/boards/NUCLEO_F429ZI: Enable lwIP and Ethernet peripheral. --- ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h | 13 +++++++++++++ ports/stm32/boards/NUCLEO_F429ZI/pins.csv | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h b/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h index 17883a19202d7..3d587435563a7 100644 --- a/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_F429ZI/mpconfigboard.h @@ -1,6 +1,8 @@ #define MICROPY_HW_BOARD_NAME "NUCLEO-F429ZI" #define MICROPY_HW_MCU_NAME "STM32F429" +#define MICROPY_PY_LWIP (1) + #define MICROPY_HW_HAS_SWITCH (1) #define MICROPY_HW_HAS_FLASH (1) #define MICROPY_HW_ENABLE_RNG (1) @@ -81,3 +83,14 @@ #define MICROPY_HW_USB_FS (1) #define MICROPY_HW_USB_VBUS_DETECT_PIN (pin_A9) #define MICROPY_HW_USB_OTG_ID_PIN (pin_A10) + +// Ethernet via RMII +#define MICROPY_HW_ETH_MDC (pin_C1) +#define MICROPY_HW_ETH_MDIO (pin_A2) +#define MICROPY_HW_ETH_RMII_REF_CLK (pin_A1) +#define MICROPY_HW_ETH_RMII_CRS_DV (pin_A7) +#define MICROPY_HW_ETH_RMII_RXD0 (pin_C4) +#define MICROPY_HW_ETH_RMII_RXD1 (pin_C5) +#define MICROPY_HW_ETH_RMII_TX_EN (pin_G11) +#define MICROPY_HW_ETH_RMII_TXD0 (pin_G13) +#define MICROPY_HW_ETH_RMII_TXD1 (pin_B13) diff --git a/ports/stm32/boards/NUCLEO_F429ZI/pins.csv b/ports/stm32/boards/NUCLEO_F429ZI/pins.csv index 8a892d3c2f298..c8fe3355d3f3c 100644 --- a/ports/stm32/boards/NUCLEO_F429ZI/pins.csv +++ b/ports/stm32/boards/NUCLEO_F429ZI/pins.csv @@ -115,3 +115,12 @@ PG2,PG2 SW,PA0 LED_GREEN,PG13 LED_RED,PG14 +ETH_MDC,PC1 +ETH_MDIO,PA2 +ETH_RMII_REF_CLK,PA1 +ETH_RMII_CRS_DV,PA7 +ETH_RMII_RXD0,PC4 +ETH_RMII_RXD1,PC5 +ETH_RMII_TX_EN,PG11 +ETH_RMII_TXD0,PG13 +ETH_RMII_TXD1,PB13 From 90571a520678b7aac513898d240f80d849ec9a33 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 26 Feb 2019 00:09:00 +1100 Subject: [PATCH 15/15] stm32/mphalport: Generate LAA based on MCU unique id. --- ports/stm32/mphalport.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index 8d85195bc0992..1a9392b5808af 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -164,10 +164,13 @@ void mp_hal_pin_config_speed(mp_hal_pin_obj_t pin_obj, uint32_t speed) { } MP_WEAK void mp_hal_get_mac(int idx, uint8_t buf[6]) { - buf[0] = 'H'; - buf[1] = 'J'; - buf[2] = '0'; - buf[3] = 0; - buf[4] = 0; - buf[5] = ((uint8_t*)MP_HAL_UNIQUE_ID_ADDRESS)[0] << 2 | idx; + // Generate a random locally administered MAC address (LAA) + // The algorithm here to is based on the DFU USB serial number algorithm + uint8_t *id = (uint8_t *)MP_HAL_UNIQUE_ID_ADDRESS; + buf[0] = 0x02; // LAA range + buf[1] = id[10] + id[2]; + buf[2] = id[9]; + buf[3] = id[8] + id[0]; + buf[4] = id[6]; + buf[5] = (id[7] & 0xfc) | idx; }