├── README.md ├── nosys.c ├── CMakeLists.txt ├── .gitignore ├── ethpio_arch.h ├── LICENSE ├── picopioeth.h ├── arch └── cc.h ├── ethpio_arch.c ├── pico_sdk_import.cmake ├── lwipopts.h ├── ethserdes.pio └── picopioeth.c /README.md: -------------------------------------------------------------------------------- 1 | # pico_eth 2 | RP2040 Minimal hardware (PIO) Ethernet interface + LwIP TCP/IP stack driver 3 | 4 | 2022 - ROBIN Guillaume 5 | 6 | **This Git repository contains only the source code of the Ethernet interface.** 7 | 8 | **This is a Git submodule. You can also include the files directly.** 9 | 10 | Documentation is in this repository : https://github.com/holysnippet/pico_eth_doc 11 | 12 | Sample project is in this repository : https://github.com/holysnippet/pico_eth_demo 13 | -------------------------------------------------------------------------------- /nosys.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "lwip/init.h" 8 | #include "pico/time.h" 9 | 10 | #if NO_SYS 11 | /* lwip has provision for using a mutex, when applicable */ 12 | sys_prot_t sys_arch_protect(void) { 13 | return 0; 14 | } 15 | 16 | void sys_arch_unprotect(sys_prot_t pval) { 17 | (void) pval; 18 | } 19 | 20 | /* lwip needs a millisecond time source, and the TinyUSB board support code has one available */ 21 | uint32_t sys_now(void) { 22 | return to_ms_since_boot(get_absolute_time()); 23 | } 24 | 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(pico_sdk_import.cmake) 4 | 5 | project(pico_eth VERSION 0.1.1) 6 | 7 | ### 8 | #set(PICO_DEOPTIMIZED_DEBUG 1) 9 | ### 10 | pico_sdk_init() 11 | 12 | add_library(pico_eth) 13 | 14 | target_include_directories(${PROJECT_NAME} PUBLIC 15 | ${CMAKE_CURRENT_LIST_DIR}/. 16 | ) 17 | 18 | pico_generate_pio_header(${PROJECT_NAME} 19 | ${CMAKE_CURRENT_LIST_DIR}/ethserdes.pio 20 | ) 21 | 22 | target_sources(${PROJECT_NAME} PRIVATE 23 | nosys.c 24 | picopioeth.c 25 | ethpio_arch.c 26 | ) 27 | 28 | # Link to pico_stdlib (gpio, time, etc. functions) 29 | target_link_libraries(${PROJECT_NAME} 30 | hardware_dma 31 | hardware_irq 32 | hardware_pio 33 | pico_lwip 34 | pico_stdlib 35 | pico_time 36 | pico_sync 37 | pico_mem_ops 38 | ) 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | .vscode/* 55 | #!.vscode/settings.json 56 | #!.vscode/tasks.json 57 | #!.vscode/launch.json 58 | #!.vscode/extensions.json 59 | #!.vscode/*.code-snippets 60 | 61 | # Local History for Visual Studio Code 62 | .history/ 63 | 64 | # Built Visual Studio Code Extensions 65 | *.vsix 66 | 67 | build/* -------------------------------------------------------------------------------- /ethpio_arch.h: -------------------------------------------------------------------------------- 1 | /* 2 | RP2040 Ethernet/PIO lwIP Driver 3 | This software is released under the same license, terms and conditions as the RP2040 "Pico" SDK 4 | 0.1.0-beta - https://github.com/holysnippet/pico_eth/ 5 | */ 6 | 7 | #ifndef __ETHPIO_ARCH_H__ 8 | #define __ETHPIO_ARCH_H__ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "pico/stdlib.h" 15 | #include "lwip/netif.h" 16 | #include "lwip/init.h" 17 | #include "lwip/etharp.h" 18 | #include "lwip/dhcp.h" 19 | #include "lwip/timeouts.h" 20 | #include "lwip/pbuf.h" 21 | 22 | #include "picopioeth.h" 23 | 24 | #define MAC_ADDR(m, a, b, c, d, e, f) \ 25 | { \ 26 | m[0] = a; \ 27 | m[1] = b; \ 28 | m[2] = c; \ 29 | m[3] = d; \ 30 | m[4] = e; \ 31 | m[5] = f; \ 32 | } 33 | 34 | #define LWIP_TO_CHK_PERIOD_US 1000 35 | 36 | struct ethpio_parameters 37 | { 38 | uint8_t pioNum; 39 | uint8_t mac_address[6]; 40 | uint8_t tx_neg_pin; 41 | uint8_t rx_pin; 42 | ip4_addr_t default_ip_v4; 43 | ip4_addr_t default_netmask_v4; 44 | ip4_addr_t default_gateway_v4; 45 | char hostname[16]; 46 | bool enable_dhcp_client; 47 | }; 48 | 49 | typedef struct ethpio_parameters ethpio_parameters_t; 50 | 51 | bool eth_pio_arch_init(ethpio_parameters_t *params); 52 | void eth_pio_arch_poll(void); 53 | 54 | extern struct netif netif; 55 | 56 | #endif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, https://github.com/holysnippet/pico_eth/ 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /picopioeth.h: -------------------------------------------------------------------------------- 1 | /* 2 | RP2040 Ethernet/PIO Firmware 3 | This software is released under the same license, terms and conditions as the RP2040 "Pico" SDK 4 | 0.1.0-beta - https://github.com/holysnippet/pico_eth/ 5 | */ 6 | 7 | #ifndef __PICO_PIO_ETH__ 8 | #define __PICO_PIO_ETH__ 9 | 10 | #include 11 | #include 12 | #include 13 | #include "pico/stdlib.h" 14 | #include "pico/lock_core.h" 15 | #include "pico/critical_section.h" 16 | #include "hardware/dma.h" 17 | #include "hardware/pio.h" 18 | #include "hardware/irq.h" 19 | #include "ethserdes.pio.h" 20 | 21 | #define PICO_PIO_ETH_MTU 1500u 22 | #define SFD_SIZE 8u 23 | #define FCS_SIZE 4u 24 | #define HDR_SIZE 14u + 2u 25 | #define MINIMUM_TX_FRAME_LENGTH 64u 26 | #define TX_BUFFER_SIZE (SFD_SIZE + HDR_SIZE + PICO_PIO_ETH_MTU + FCS_SIZE) 27 | 28 | #define RX_NCHUNKS_MAX 64u 29 | #define RX_RING_SIZE 16384u 30 | 31 | #define POLY_CRC32_802_3 0xEDB88320 32 | #define CHCK_CRC32_802_3 0xDEBB20E3 33 | 34 | bool eth_hw_init(PIO pio, uint8_t tx_neg_pin, uint8_t rx_pin); 35 | void eth_sw_task(); 36 | 37 | bool eth_tx_busy(); 38 | extern const uint8_t *eth_tx_netif_base; 39 | bool eth_hw_send(uint16_t size); 40 | 41 | uint16_t eth_rx_siz(); 42 | bool eth_rx_get(uint8_t *data); 43 | void eth_rx_next(void); 44 | 45 | struct eth_pio_diag_struct 46 | { 47 | uint32_t diag_rx_raw_frames; 48 | uint32_t diag_rx_raw_bytes; 49 | uint32_t diag_rx_badcrc; 50 | uint32_t diag_rx_framing; 51 | uint32_t diag_rx_ring_overrun; 52 | uint32_t diag_rx_fifo_overrun; 53 | 54 | uint32_t diag_tx_raw_bytes; 55 | }; 56 | 57 | typedef struct eth_pio_diag_struct eth_pio_diag_t; 58 | 59 | extern eth_pio_diag_t eth_pio_diag; 60 | 61 | #endif -------------------------------------------------------------------------------- /arch/cc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * 31 | */ 32 | #ifndef __CC_H__ 33 | #define __CC_H__ 34 | 35 | #include 36 | 37 | #if NO_SYS 38 | // todo really we should just not allow SYS_LIGHTWEIGHT_PROT for nosys mode (it doesn't do anything anyway) 39 | typedef int sys_prot_t; 40 | #endif 41 | 42 | /* define compiler specific symbols */ 43 | #if defined (__ICCARM__) 44 | 45 | #define PACK_STRUCT_BEGIN 46 | #define PACK_STRUCT_STRUCT 47 | #define PACK_STRUCT_END 48 | #define PACK_STRUCT_FIELD(x) x 49 | #define PACK_STRUCT_USE_INCLUDES 50 | 51 | #elif defined (__CC_ARM) 52 | 53 | #define PACK_STRUCT_BEGIN __packed 54 | #define PACK_STRUCT_STRUCT 55 | #define PACK_STRUCT_END 56 | #define PACK_STRUCT_FIELD(x) x 57 | 58 | #elif defined (__GNUC__) 59 | 60 | #define PACK_STRUCT_BEGIN 61 | #define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) 62 | #define PACK_STRUCT_END 63 | #define PACK_STRUCT_FIELD(x) x 64 | 65 | #elif defined (__TASKING__) 66 | 67 | #define PACK_STRUCT_BEGIN 68 | #define PACK_STRUCT_STRUCT 69 | #define PACK_STRUCT_END 70 | #define PACK_STRUCT_FIELD(x) x 71 | 72 | #endif 73 | 74 | #define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0) 75 | 76 | //unsigned int pico_lwip_rand(void); 77 | #ifndef LWIP_RAND 78 | // Use ROSC based random number generation, more for the fact that rand() may not be seeded, than anything else 79 | #define LWIP_RAND rand 80 | #endif 81 | #endif /* __CC_H__ */ 82 | -------------------------------------------------------------------------------- /ethpio_arch.c: -------------------------------------------------------------------------------- 1 | /* 2 | RP2040 Ethernet/PIO lwIP Driver 3 | This software is released under the same license, terms and conditions as the RP2040 "Pico" SDK 4 | 0.1.0-beta - https://github.com/holysnippet/pico_eth/ 5 | */ 6 | 7 | #include "ethpio_arch.h" 8 | 9 | static err_t netif_set_opts(struct netif *netif); 10 | int process_frames(const void *frame, int frame_len); 11 | static err_t netif_output(struct netif *netif, struct pbuf *p); 12 | 13 | struct netif netif; 14 | ip4_addr_t addr; 15 | ip4_addr_t netmask; 16 | ip4_addr_t gw; 17 | char hostname_str[16]; 18 | uint8_t mac_address[6]; 19 | absolute_time_t last_to_check; 20 | 21 | void eth_pio_arch_poll(void) 22 | { 23 | eth_sw_task(); 24 | 25 | const absolute_time_t now = get_absolute_time(); 26 | if (absolute_time_diff_us(last_to_check, now) >= LWIP_TO_CHK_PERIOD_US) 27 | { 28 | last_to_check = now; 29 | sys_check_timeouts(); 30 | } 31 | 32 | bool cFlag = true; 33 | uint16_t siz = eth_rx_siz(); 34 | 35 | while (cFlag && siz > 0) 36 | { 37 | struct pbuf *p = pbuf_alloc(PBUF_RAW, siz, PBUF_RAM); 38 | 39 | if (p != NULL) 40 | { 41 | if (eth_rx_get(p->payload)) 42 | { 43 | if (netif.input(p, &netif) != ERR_OK) 44 | { 45 | pbuf_free(p); 46 | cFlag = false; 47 | } 48 | } 49 | else 50 | pbuf_free(p); 51 | } 52 | else 53 | cFlag = false; 54 | 55 | if (cFlag) 56 | { 57 | eth_rx_next(); 58 | siz = eth_rx_siz(); 59 | } 60 | } 61 | } 62 | 63 | bool eth_pio_arch_init(ethpio_parameters_t *params) 64 | { 65 | if (eth_hw_init((params->pioNum == 0) ? pio0 : pio1, params->tx_neg_pin, params->rx_pin)) 66 | { 67 | memcpy(mac_address, params->mac_address, 6); 68 | strcpy(hostname_str, params->hostname); 69 | lwip_init(); 70 | netif_add(&netif, &addr, &netmask, &gw, NULL, netif_set_opts, netif_input); 71 | 72 | netif.name[0] = 'e'; 73 | netif.name[1] = '0'; 74 | 75 | netif_set_default(&netif); 76 | netif_set_up(&netif); 77 | 78 | if (params->enable_dhcp_client) 79 | dhcp_start(&netif); 80 | 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | static err_t netif_set_opts(struct netif *netif) 87 | { 88 | netif->hostname = hostname_str; 89 | netif->linkoutput = netif_output; 90 | netif->output = etharp_output; 91 | netif->mtu = PICO_PIO_ETH_MTU; 92 | netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_LINK_UP; 93 | netif->hwaddr_len = 6; 94 | memcpy(netif->hwaddr, mac_address, 6); 95 | 96 | return ERR_OK; 97 | } 98 | 99 | static err_t netif_output(struct netif *netif, struct pbuf *p) 100 | { 101 | while (eth_tx_busy()) 102 | sleep_us(10); 103 | 104 | pbuf_copy_partial(p, (void *)eth_tx_netif_base, p->tot_len, 0); 105 | eth_hw_send(p->tot_len); 106 | 107 | return ERR_OK; 108 | } -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | # GIT_SUBMODULES_RECURSE was added in 3.17 33 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 34 | FetchContent_Declare( 35 | pico_sdk 36 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 37 | GIT_TAG master 38 | GIT_SUBMODULES_RECURSE FALSE 39 | ) 40 | else () 41 | FetchContent_Declare( 42 | pico_sdk 43 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 44 | GIT_TAG master 45 | ) 46 | endif () 47 | 48 | if (NOT pico_sdk) 49 | message("Downloading Raspberry Pi Pico SDK") 50 | FetchContent_Populate(pico_sdk) 51 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 52 | endif () 53 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 54 | else () 55 | message(FATAL_ERROR 56 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 57 | ) 58 | endif () 59 | endif () 60 | 61 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 62 | if (NOT EXISTS ${PICO_SDK_PATH}) 63 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 64 | endif () 65 | 66 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 67 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 68 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 69 | endif () 70 | 71 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 72 | 73 | include(${PICO_SDK_INIT_CMAKE_FILE}) 74 | -------------------------------------------------------------------------------- /lwipopts.h: -------------------------------------------------------------------------------- 1 | #ifndef _LWIPOPTS_H 2 | #define _LWIPOPTS_H 3 | 4 | #ifndef NO_SYS 5 | #define NO_SYS 1 6 | #endif 7 | 8 | #ifndef LWIP_SOCKET 9 | #define LWIP_SOCKET 0 10 | #endif 11 | 12 | // MEM_LIBC_MALLOC is incompatible with non polling versions 13 | #define MEM_LIBC_MALLOC 1 14 | 15 | #define LWIP_BROADCAST_PING 0 16 | #define LWIP_MULTICAST_PING 0 17 | 18 | #define MEM_ALIGNMENT 4 19 | #define MEM_SIZE 16384 20 | #define MEMP_NUM_TCP_SEG 32 21 | #define ARP_TABLE_SIZE 15 22 | #define MEMP_NUM_ARP_QUEUE 10 23 | #define PBUF_POOL_SIZE 24 24 | #define LWIP_ARP 1 25 | #define LWIP_ETHERNET 1 26 | #define LWIP_ICMP 1 27 | #define LWIP_RAW 1 28 | #define TCP_WND 2920//(8 * TCP_MSS) 29 | #define TCP_MSS 1460 30 | #define TCP_SND_BUF (8 * TCP_MSS) 31 | #define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) 32 | #define LWIP_NETIF_STATUS_CALLBACK 1 33 | #define LWIP_NETIF_LINK_CALLBACK 0 34 | #define LWIP_NETIF_HOSTNAME 1 35 | #define LWIP_NETCONN 0 36 | 37 | #define LWIP_CHKSUM_ALGORITHM 3 38 | 39 | #define LWIP_DHCP 1 40 | #define LWIP_AUTOIP 0 41 | #define LWIP_DHCP_AUTOIP_COOP 0 42 | #define AUTOIP_TMR_INTERVAL 100 43 | 44 | #define LWIP_IPV4 1 45 | #define LWIP_IPV6 0 46 | #define IP_REASSEMBLY 1 47 | 48 | #define LWIP_TCP 1 49 | #define LWIP_UDP 1 50 | #define LWIP_DNS 1 51 | #define LWIP_TCP_KEEPALIVE 1 52 | #define DHCP_DOES_ARP_CHECK 0 53 | #define LWIP_DHCP_DOES_ACD_CHECK 0 54 | 55 | 56 | #ifdef NDEBUG 57 | #define LWIP_DEBUG 0 58 | #define LWIP_STATS 0 59 | #define LWIP_STATS_DISPLAY 0 60 | #define MEM_STATS 0 61 | #define SYS_STATS 0 62 | #define MEMP_STATS 0 63 | #define LINK_STATS 0 64 | #endif 65 | 66 | #define ETHARP_DEBUG LWIP_DBG_OFF 67 | #define NETIF_DEBUG LWIP_DBG_OFF 68 | #define PBUF_DEBUG LWIP_DBG_OFF 69 | #define API_LIB_DEBUG LWIP_DBG_OFF 70 | #define API_MSG_DEBUG LWIP_DBG_OFF 71 | #define SOCKETS_DEBUG LWIP_DBG_OFF 72 | #define ICMP_DEBUG LWIP_DBG_OFF 73 | #define INET_DEBUG LWIP_DBG_OFF 74 | #define IP_DEBUG LWIP_DBG_OFF 75 | #define IP_REASS_DEBUG LWIP_DBG_OFF 76 | #define RAW_DEBUG LWIP_DBG_OFF 77 | #define MEM_DEBUG LWIP_DBG_OFF 78 | #define MEMP_DEBUG LWIP_DBG_OFF 79 | #define SYS_DEBUG LWIP_DBG_OFF 80 | #define TCP_DEBUG LWIP_DBG_OFF 81 | #define TCP_INPUT_DEBUG LWIP_DBG_OFF 82 | #define TCP_OUTPUT_DEBUG LWIP_DBG_OFF 83 | #define TCP_RTO_DEBUG LWIP_DBG_OFF 84 | #define TCP_CWND_DEBUG LWIP_DBG_OFF 85 | #define TCP_WND_DEBUG LWIP_DBG_OFF 86 | #define TCP_FR_DEBUG LWIP_DBG_OFF 87 | #define TCP_QLEN_DEBUG LWIP_DBG_OFF 88 | #define TCP_RST_DEBUG LWIP_DBG_OFF 89 | #define UDP_DEBUG LWIP_DBG_OFF 90 | #define TCPIP_DEBUG LWIP_DBG_OFF 91 | #define PPP_DEBUG LWIP_DBG_OFF 92 | #define SLIP_DEBUG LWIP_DBG_OFF 93 | #define DHCP_DEBUG LWIP_DBG_OFF 94 | 95 | #endif /* __LWIPOPTS_H__ */ -------------------------------------------------------------------------------- /ethserdes.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; * 802.3 (10BASE-T Ethernet) MAC-like RP2040 PIO Program - ROBIN Guillaume 3 | ; ! This software is released under the same license, terms and conditions as the RP2040 "Pico" SDK 4 | ; ~ https://github.com/holysnippet/pico_eth/ 5 | ; * Version 0.1.0-beta - https://github.com/holysnippet/pico_eth/ 6 | ; 7 | .program eth_ser 8 | stop: 9 | set pins, 0b10 [23] 10 | nlp: 11 | set pins, 0b10 [11] 12 | .wrap_target 13 | init: 14 | set pins, 0b00 15 | .wrap 16 | send_0: 17 | set pins, 0b10 [5] 18 | set pins, 0b01 [2] 19 | next: ;(5) 20 | jmp !osre nextt 21 | jmp stop 22 | nextt: 23 | out X, 1 24 | jmp !X send_0 25 | send_1: 26 | set pins, 0b01 [5] 27 | set pins, 0b10 [1] 28 | jmp next 29 | 30 | .program eth_det 31 | reset: 32 | irq set 4 33 | irq wait 0 34 | waitd0: 35 | wait 0 pin 0 36 | .wrap_target 37 | in pins, 1 [4] 38 | mov X, ISR 39 | jmp X!=Y continue 40 | decl: 41 | nop [3] 42 | irq clear 4 43 | waits: 44 | set X, 9 ;(7) 45 | chk: 46 | jmp pin, incc 47 | jmp waits 48 | incc: 49 | jmp X--, chk 50 | jmp reset 51 | continue: 52 | jmp pin waitd0 53 | wait 1 pin 0 54 | .wrap 55 | 56 | .program eth_des 57 | waite0: 58 | wait 0 pin 0 59 | .wrap_target 60 | wait 0 irq 4 61 | in pins, 1 [6] 62 | jmp pin waite0 63 | wait 1 pin 0 64 | .wrap 65 | 66 | % c-sdk { 67 | #include "hardware/clocks.h" 68 | 69 | static void pio_set_y(PIO pio, uint sm, uint32_t v) 70 | { 71 | const uint instr_shift = pio_encode_in(pio_y, 4); 72 | const uint instr_mov = pio_encode_mov(pio_y, pio_isr); 73 | 74 | for (int i = 7; i >= 0; i--) 75 | { 76 | const uint32_t nibble = (v >> (i * 4)) & 0xf; 77 | pio_sm_exec(pio, sm, pio_encode_set(pio_y, nibble)); 78 | pio_sm_exec(pio, sm, instr_shift); 79 | } 80 | pio_sm_exec(pio, sm, instr_mov); 81 | } 82 | 83 | static void pio_set_x(PIO pio, uint sm, uint32_t v) 84 | { 85 | const uint instr_shift = pio_encode_in(pio_x, 4); 86 | const uint instr_mov = pio_encode_mov(pio_x, pio_isr); 87 | 88 | for (int i = 7; i >= 0; i--) 89 | { 90 | const uint32_t nibble = (v >> (i * 4)) & 0xf; 91 | pio_sm_exec(pio, sm, pio_encode_set(pio_x, nibble)); 92 | pio_sm_exec(pio, sm, instr_shift); 93 | } 94 | pio_sm_exec(pio, sm, instr_mov); 95 | } 96 | 97 | static inline void eth_des_program_init(PIO pio, uint sm, uint offset, uint data_pin) 98 | { 99 | pio_sm_config sm_config = eth_des_program_get_default_config(offset); 100 | 101 | pio_sm_set_pindirs_with_mask(pio, sm, 0u, 1u << data_pin); 102 | 103 | sm_config_set_in_pins(&sm_config, data_pin); 104 | sm_config_set_jmp_pin(&sm_config, data_pin); 105 | sm_config_set_in_shift(&sm_config, true, true, 8); 106 | sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX); 107 | 108 | sm_config_set_clkdiv(&sm_config, clock_get_hz(clk_sys) / (120000000.0f)); 109 | 110 | pio_gpio_init(pio, data_pin); 111 | 112 | pio_sm_init(pio, sm, offset + 0x01, &sm_config); 113 | pio_sm_exec(pio, sm, pio_encode_irq_set(false, 4)); 114 | } 115 | 116 | static inline void eth_ser_program_init(PIO pio, uint sm, uint offset, uint pin_tx) 117 | { 118 | pio_sm_config c = eth_ser_program_get_default_config(offset); 119 | 120 | pio_sm_set_pins_with_mask(pio, sm, 0u, 3u << pin_tx); 121 | pio_sm_set_pindirs_with_mask(pio, sm, ~0u, 3u << pin_tx); 122 | 123 | sm_config_set_set_pins(&c, pin_tx, 2); 124 | sm_config_set_out_shift(&c, true, true, 8); 125 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 126 | 127 | sm_config_set_clkdiv(&c, clock_get_hz(clk_sys) / (120000000.0f)); 128 | 129 | pio_gpio_init(pio, pin_tx); 130 | pio_gpio_init(pio, pin_tx + 1); 131 | 132 | // gpio_set_drive_strength(pin_tx, GPIO_DRIVE_STRENGTH_12MA); 133 | // gpio_set_slew_rate(pin_tx, GPIO_SLEW_RATE_FAST); 134 | // gpio_set_drive_strength(pin_tx + 1, GPIO_DRIVE_STRENGTH_12MA); 135 | // gpio_set_slew_rate(pin_tx + 1, GPIO_SLEW_RATE_FAST); 136 | 137 | pio_sm_init(pio, sm, offset + 0x02, &c); 138 | } 139 | 140 | static inline void eth_det_program_init(PIO pio, uint sm, uint offset, uint data_pin) { 141 | pio_sm_config sm_config = eth_det_program_get_default_config(offset); 142 | 143 | pio_sm_set_pindirs_with_mask(pio, sm, 0u, 1u << data_pin); 144 | 145 | sm_config_set_in_pins(&sm_config, data_pin); 146 | sm_config_set_jmp_pin(&sm_config, data_pin); 147 | sm_config_set_in_shift(&sm_config, true, false, 32); 148 | 149 | sm_config_set_clkdiv(&sm_config, clock_get_hz(clk_sys) / (120000000.0f)); 150 | 151 | pio_gpio_init(pio, data_pin); 152 | 153 | pio_sm_init(pio, sm, offset + 0x02, &sm_config); 154 | pio_set_y(pio, sm, 0x5555555D); 155 | } 156 | 157 | static void pio_jump_nlp(PIO pio, uint sm, uint offset) 158 | { 159 | const uint instr_eth_nlp = pio_encode_jmp(offset + 0x01); 160 | pio_sm_exec(pio, sm, instr_eth_nlp); 161 | } 162 | %} 163 | -------------------------------------------------------------------------------- /picopioeth.c: -------------------------------------------------------------------------------- 1 | /* 2 | RP2040 Ethernet/PIO Firmware 3 | This software is released under the same license, terms and conditions as the RP2040 "Pico" SDK 4 | 0.1.0-beta - https://github.com/holysnippet/pico_eth/ 5 | */ 6 | 7 | #include "picopioeth.h" 8 | 9 | uint8_t eth_tx_neg_pin, eth_rx_pos_pin; 10 | PIO eth_pio; 11 | const uint8_t ser_sm = 0u, des_sm = 1u, det_sm = 2u; 12 | 13 | uint8_t ser_offset, ser_basepin; 14 | uint8_t des_offset, des_basepin; 15 | uint8_t det_offset, det_irq; 16 | 17 | uint32_t des_pp_jmp; 18 | 19 | int32_t ser_dma_chan, des_dma_chan0, des_dma_chan1, copy_dma_chan, des_dma_cur_chan; 20 | dma_channel_config ser_dma_cfg, des_dma_cfg0, des_dma_cfg1, copy_dma_cfg; 21 | 22 | static uint8_t ser_buffer[TX_BUFFER_SIZE] __attribute__((aligned(4))); 23 | const uint8_t *eth_tx_netif_base = &ser_buffer[8]; 24 | 25 | absolute_time_t last_tx; 26 | 27 | uint8_t des_rx_buf[RX_RING_SIZE] __attribute__((aligned(4))); 28 | uint16_t sizes_tab[RX_NCHUNKS_MAX] __attribute__((aligned(4))), bases_tab[RX_NCHUNKS_MAX] __attribute__((aligned(4))); 29 | uint16_t fifo_read __attribute__((aligned(4))), fifo_start __attribute__((aligned(4))), fifo_stop __attribute__((aligned(4))); 30 | uint32_t ring_len, last_wr_ptr; 31 | 32 | eth_pio_diag_t eth_pio_diag __attribute__((aligned(4))); 33 | 34 | inline uint16_t eth_rx_siz() 35 | { 36 | uint16_t result = 0u; 37 | uint16_t size; 38 | bool err = false; 39 | 40 | const uint16_t start = fifo_start; 41 | const uint16_t stop = fifo_stop; 42 | 43 | if (fifo_read != stop) 44 | { 45 | if (stop > start) 46 | { 47 | if ((fifo_read < start) || (fifo_read >= stop)) 48 | fifo_read = start; 49 | } 50 | else if ((fifo_read >= stop) && (fifo_read < start)) 51 | fifo_read = start; 52 | 53 | do 54 | { 55 | err = false; 56 | size = sizes_tab[fifo_read]; 57 | 58 | if ((size > (HDR_SIZE + PICO_PIO_ETH_MTU + FCS_SIZE)) || (size < ((HDR_SIZE + FCS_SIZE) - 2))) 59 | { 60 | err = true; 61 | fifo_read = (fifo_read + 1) % RX_NCHUNKS_MAX; 62 | eth_pio_diag.diag_rx_framing++; 63 | } 64 | 65 | } while (err && (fifo_read != stop)); 66 | 67 | if (!err) 68 | result = size; 69 | } 70 | 71 | return result; 72 | } 73 | 74 | void dma_set_sniff(int32_t channel, bool reset); 75 | 76 | inline bool eth_rx_get(uint8_t *data) 77 | { 78 | const uint16_t base = bases_tab[fifo_read]; 79 | const uint16_t length = sizes_tab[fifo_read]; 80 | 81 | const uint16_t flen = base + length > RX_RING_SIZE ? RX_RING_SIZE - base : length; 82 | const uint16_t slen = length - flen; 83 | 84 | dma_channel_configure(copy_dma_chan, ©_dma_cfg, data, &des_rx_buf[base], flen, false); 85 | dma_set_sniff(copy_dma_chan, true); 86 | dma_channel_start(copy_dma_chan); 87 | dma_channel_wait_for_finish_blocking(copy_dma_chan); 88 | 89 | if (slen) 90 | { 91 | dma_channel_configure(copy_dma_chan, ©_dma_cfg, &data[flen], des_rx_buf, slen, false); 92 | dma_set_sniff(copy_dma_chan, false); 93 | dma_channel_start(copy_dma_chan); 94 | dma_channel_wait_for_finish_blocking(copy_dma_chan); 95 | } 96 | 97 | const bool result = dma_hw->sniff_data == CHCK_CRC32_802_3; 98 | 99 | if (!result) 100 | eth_pio_diag.diag_rx_badcrc++; 101 | 102 | return result; 103 | } 104 | 105 | void eth_rx_next(void) 106 | { 107 | if (fifo_read != fifo_stop) 108 | { 109 | fifo_read = (fifo_read + 1) % RX_NCHUNKS_MAX; 110 | fifo_start = fifo_read; // BugFix 111 | } 112 | } 113 | 114 | void des_setup(void) 115 | { 116 | pio_sm_claim(eth_pio, des_sm); 117 | 118 | des_offset = pio_add_program(eth_pio, ð_des_program); 119 | des_pp_jmp = pio_encode_jmp(des_offset + 1u); 120 | 121 | eth_des_program_init(eth_pio, des_sm, des_offset, eth_rx_pos_pin); 122 | 123 | des_dma_cfg0 = dma_channel_get_default_config(des_dma_chan0); 124 | channel_config_set_transfer_data_size(&des_dma_cfg0, DMA_SIZE_8); 125 | channel_config_set_chain_to(&des_dma_cfg0, des_dma_chan1); 126 | channel_config_set_read_increment(&des_dma_cfg0, false); 127 | channel_config_set_write_increment(&des_dma_cfg0, true); 128 | channel_config_set_dreq(&des_dma_cfg0, pio_get_dreq(eth_pio, des_sm, false)); 129 | 130 | des_dma_cfg1 = dma_channel_get_default_config(des_dma_chan1); 131 | channel_config_set_transfer_data_size(&des_dma_cfg1, DMA_SIZE_8); 132 | channel_config_set_chain_to(&des_dma_cfg1, des_dma_chan0); 133 | channel_config_set_read_increment(&des_dma_cfg1, false); 134 | channel_config_set_write_increment(&des_dma_cfg1, true); 135 | channel_config_set_dreq(&des_dma_cfg1, pio_get_dreq(eth_pio, des_sm, false)); 136 | 137 | dma_channel_configure(des_dma_chan0, &des_dma_cfg0, des_rx_buf, &((uint8_t *)(ð_pio->rxf[des_sm]))[3], RX_RING_SIZE, false); 138 | dma_channel_configure(des_dma_chan1, &des_dma_cfg1, des_rx_buf, &((uint8_t *)(ð_pio->rxf[des_sm]))[3], RX_RING_SIZE, false); 139 | 140 | des_dma_cur_chan = des_dma_chan0; 141 | last_wr_ptr = 0u; 142 | 143 | for (int i = 0; i < RX_NCHUNKS_MAX; i++) 144 | { 145 | sizes_tab[i] = 0u; 146 | bases_tab[i] = 0u; 147 | } 148 | 149 | fifo_read = 0u; 150 | ring_len = 0u; 151 | fifo_start = 0u; 152 | fifo_stop = 0u; 153 | } 154 | 155 | void _det_irq(void) 156 | { 157 | eth_pio->ctrl |= 1u << (PIO_CTRL_SM_RESTART_LSB + det_sm); 158 | 159 | while (eth_pio->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + det_sm)) == 0) 160 | tight_loop_contents(); 161 | 162 | eth_pio->ctrl = eth_pio->ctrl & ~(1u << des_sm); 163 | eth_pio->ctrl |= 1u << (PIO_CTRL_SM_RESTART_LSB + des_sm); 164 | eth_pio->sm[des_sm].instr = des_pp_jmp; 165 | 166 | if (!dma_hw->ch[des_dma_cur_chan].transfer_count) 167 | { 168 | dma_channel_set_write_addr(des_dma_cur_chan, des_rx_buf, false); 169 | des_dma_cur_chan = des_dma_cur_chan == des_dma_chan0 ? des_dma_chan1 : des_dma_chan0; 170 | } 171 | 172 | const uint32_t wr_ptr = RX_RING_SIZE - dma_hw->ch[des_dma_cur_chan].transfer_count; 173 | 174 | eth_pio->ctrl = eth_pio->ctrl | (1u << des_sm); 175 | hw_set_bits(ð_pio->irq, 1u); 176 | 177 | const uint32_t rx_size = wr_ptr >= last_wr_ptr ? wr_ptr - last_wr_ptr : (RX_RING_SIZE - last_wr_ptr) + wr_ptr; 178 | 179 | eth_pio_diag.diag_rx_raw_frames++; 180 | eth_pio_diag.diag_rx_raw_bytes += rx_size; 181 | 182 | ring_len += rx_size; 183 | 184 | bases_tab[fifo_stop] = last_wr_ptr; 185 | sizes_tab[fifo_stop] = rx_size; 186 | 187 | fifo_stop = (fifo_stop + 1) % RX_NCHUNKS_MAX; 188 | 189 | if (fifo_stop == fifo_start) 190 | { 191 | eth_pio_diag.diag_rx_fifo_overrun++; 192 | ring_len -= sizes_tab[fifo_start]; 193 | fifo_start = (fifo_start + 1) % RX_NCHUNKS_MAX; 194 | } 195 | 196 | while (ring_len > RX_RING_SIZE) 197 | { 198 | ring_len -= sizes_tab[fifo_start]; 199 | fifo_start = (fifo_start + 1) % RX_NCHUNKS_MAX; 200 | } 201 | 202 | last_wr_ptr = wr_ptr; 203 | } 204 | 205 | void det_setup(void) 206 | { 207 | pio_sm_claim(eth_pio, det_sm); 208 | det_offset = pio_add_program(eth_pio, ð_det_program); 209 | eth_det_program_init(eth_pio, det_sm, det_offset, eth_rx_pos_pin); 210 | 211 | pio_set_irq0_source_enabled(eth_pio, pis_interrupt0, true); 212 | irq_set_exclusive_handler(det_irq, _det_irq); 213 | } 214 | 215 | void _ser_dma(void) 216 | { 217 | irq_set_enabled(DMA_IRQ_0, false); 218 | dma_channel_acknowledge_irq0(ser_dma_chan); 219 | last_tx = get_absolute_time(); 220 | } 221 | 222 | void ser_setup(void) 223 | { 224 | pio_sm_claim(eth_pio, ser_sm); 225 | ser_offset = pio_add_program(eth_pio, ð_ser_program); 226 | eth_ser_program_init(eth_pio, ser_sm, ser_offset, eth_tx_neg_pin); 227 | 228 | ser_dma_cfg = dma_channel_get_default_config(ser_dma_chan); 229 | 230 | channel_config_set_transfer_data_size(&ser_dma_cfg, DMA_SIZE_8); 231 | channel_config_set_read_increment(&ser_dma_cfg, true); 232 | channel_config_set_write_increment(&ser_dma_cfg, false); 233 | channel_config_set_dreq(&ser_dma_cfg, pio_get_dreq(eth_pio, ser_sm, true)); 234 | 235 | dma_set_irq0_channel_mask_enabled(1 << ser_dma_chan, true); 236 | irq_set_exclusive_handler(DMA_IRQ_0, _ser_dma); 237 | irq_set_enabled(DMA_IRQ_0, false); 238 | } 239 | 240 | bool eth_hw_send(uint16_t size) 241 | { 242 | static uint32_t dummy __attribute__((aligned(4))); 243 | int pktIdx; 244 | 245 | while (eth_tx_busy()) 246 | sleep_us(10); 247 | 248 | for (pktIdx = 0; pktIdx < 7; pktIdx++) 249 | ser_buffer[pktIdx] = 0x55; 250 | ser_buffer[pktIdx++] = 0xD5; 251 | 252 | pktIdx += size; 253 | 254 | while (pktIdx < ((MINIMUM_TX_FRAME_LENGTH - FCS_SIZE) + SFD_SIZE)) 255 | ser_buffer[pktIdx++] = 0x00; 256 | 257 | channel_config_set_write_increment(©_dma_cfg, false); 258 | dma_channel_configure(copy_dma_chan, ©_dma_cfg, &dummy, &ser_buffer[8], pktIdx - 8, false); 259 | channel_config_set_write_increment(©_dma_cfg, true); 260 | dma_set_sniff(copy_dma_chan, true); 261 | dma_channel_start(copy_dma_chan); 262 | dma_channel_wait_for_finish_blocking(copy_dma_chan); 263 | 264 | const uint32_t fcs = dma_hw->sniff_data ^ 0xFFFFFFFF; 265 | 266 | ser_buffer[pktIdx++] = (fcs >> 0) & 0xFF; 267 | ser_buffer[pktIdx++] = (fcs >> 8) & 0xFF; 268 | ser_buffer[pktIdx++] = (fcs >> 16) & 0xFF; 269 | ser_buffer[pktIdx++] = (fcs >> 24) & 0xFF; 270 | 271 | eth_pio_diag.diag_tx_raw_bytes += pktIdx; 272 | 273 | while (absolute_time_diff_us(last_tx, get_absolute_time()) <= 50) // ~ EOT + IPG 274 | tight_loop_contents(); 275 | 276 | irq_set_enabled(DMA_IRQ_0, true); 277 | dma_channel_configure(ser_dma_chan, &ser_dma_cfg, &((uint8_t *)(ð_pio->txf[ser_sm]))[3], ser_buffer, pktIdx, true); 278 | pio_sm_exec(eth_pio, ser_sm, pio_encode_jmp(ser_offset + 0x05)); 279 | 280 | return true; 281 | } 282 | 283 | inline bool eth_tx_busy() 284 | { 285 | return irq_is_enabled(DMA_IRQ_0); 286 | } 287 | 288 | void eth_sw_task() 289 | { 290 | const absolute_time_t now = get_absolute_time(); 291 | 292 | if (absolute_time_diff_us(last_tx, now) >= 16000) 293 | { 294 | if (!eth_tx_busy()) 295 | { 296 | pio_jump_nlp(eth_pio, ser_sm, ser_offset); 297 | last_tx = now; 298 | } 299 | } 300 | } 301 | 302 | bool eth_check_params(PIO pio, uint8_t tx_neg_pin, uint8_t rx_pin) 303 | { 304 | if ((pio == pio0) || (pio == pio1)) 305 | if ((tx_neg_pin < 28) && (rx_pin < 29)) 306 | if ((rx_pin != tx_neg_pin) && (rx_pin != (tx_neg_pin + 1))) 307 | return true; 308 | return false; 309 | } 310 | 311 | bool eth_set_params(PIO pio, uint8_t tx_neg_pin, uint8_t rx_pin) 312 | { 313 | if (!eth_check_params(pio, tx_neg_pin, rx_pin)) 314 | return false; 315 | 316 | gpio_disable_pulls(rx_pin); 317 | gpio_set_dir(tx_neg_pin, true); 318 | gpio_set_dir(tx_neg_pin + 1, true); 319 | 320 | eth_pio = pio; 321 | eth_tx_neg_pin = tx_neg_pin; 322 | eth_rx_pos_pin = rx_pin; 323 | 324 | if (pio == pio0) 325 | det_irq = PIO0_IRQ_0; 326 | else if (pio == pio1) 327 | det_irq = PIO1_IRQ_0; 328 | 329 | ser_dma_chan = dma_claim_unused_channel(false); 330 | if (ser_dma_chan == -1) 331 | return false; 332 | des_dma_chan0 = dma_claim_unused_channel(false); 333 | if (des_dma_chan0 == -1) 334 | return false; 335 | des_dma_chan1 = dma_claim_unused_channel(false); 336 | if (des_dma_chan1 == -1) 337 | return false; 338 | copy_dma_chan = dma_claim_unused_channel(false); 339 | if (copy_dma_chan == -1) 340 | return false; 341 | 342 | copy_dma_cfg = dma_channel_get_default_config(copy_dma_chan); 343 | channel_config_set_transfer_data_size(©_dma_cfg, DMA_SIZE_8); 344 | channel_config_set_read_increment(©_dma_cfg, true); 345 | channel_config_set_write_increment(©_dma_cfg, true); 346 | 347 | last_tx = get_absolute_time(); 348 | 349 | return true; 350 | } 351 | 352 | bool eth_hw_init(PIO pio, uint8_t tx_neg_pin, uint8_t rx_pin) 353 | { 354 | if (!eth_set_params(pio, tx_neg_pin, rx_pin)) 355 | return false; 356 | 357 | memset(ð_pio_diag, 0u, sizeof(eth_pio_diag_t)); 358 | 359 | ser_setup(); 360 | des_setup(); 361 | det_setup(); 362 | 363 | dma_channel_start(des_dma_chan0); 364 | 365 | pio_interrupt_clear(eth_pio, 0); 366 | irq_clear(det_irq); 367 | irq_set_enabled(det_irq, true); 368 | 369 | pio_sm_set_enabled(eth_pio, ser_sm, true); 370 | pio_sm_set_enabled(eth_pio, des_sm, true); 371 | pio_sm_set_enabled(eth_pio, det_sm, true); 372 | 373 | return true; 374 | } 375 | 376 | inline void dma_set_sniff(int32_t channel, bool reset) 377 | { 378 | dma_sniffer_enable(channel, 0x01, true); 379 | dma_sniffer_set_byte_swap_enabled(true); 380 | hw_set_bits(&dma_hw->sniff_ctrl, DMA_SNIFF_CTRL_OUT_REV_BITS); 381 | if (reset) 382 | dma_hw->sniff_data = 0xffffffff; 383 | } 384 | --------------------------------------------------------------------------------