From d577c5193c2688c41d72f8d757fc9fcb0f4ee67a Mon Sep 17 00:00:00 2001 From: kuwoyuki Date: Fri, 7 Nov 2025 17:02:04 +0600 Subject: [PATCH] first commit --- .gitignore | 60 ++++++ .gitmodules | 6 + .vscode/c_cpp_properties.json | 16 ++ .vscode/settings.json | 22 +++ Makefile | 54 ++++++ ch32fun | 1 + funconfig.h | 9 + inc/systick.h | 19 ++ lwip | 1 + main.c | 124 ++++++++++++ port/arch/cc.h | 35 ++++ port/ethernetif.c | 344 ++++++++++++++++++++++++++++++++++ port/ethernetif.h | 161 ++++++++++++++++ port/lwipopts.h | 53 ++++++ port/sys_arch.c | 25 +++ systick.c | 21 +++ 16 files changed, 951 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json create mode 100644 Makefile create mode 160000 ch32fun create mode 100644 funconfig.h create mode 100644 inc/systick.h create mode 160000 lwip create mode 100644 main.c create mode 100644 port/arch/cc.h create mode 100644 port/ethernetif.c create mode 100644 port/ethernetif.h create mode 100644 port/lwipopts.h create mode 100644 port/sys_arch.c create mode 100644 systick.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcb8f64 --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# debug information files +*.dwo + +# z +main.lst +main_ext.bin +main.bin diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..80981d5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "ch32fun"] + path = ch32fun + url = https://github.com/cnlohr/ch32fun.git +[submodule "lwip"] + path = lwip + url = git://git.git.savannah.gnu.org/lwip.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..07c08df --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "linux-clang-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b27b960 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "files.associations": { + "*.rmd": "markdown", + "sys.h": "c", + "cstdio": "c", + "__verbose_abort": "c", + "*.inc": "c", + "ch32fun.h": "c", + "stdio.h": "c", + "systick.h": "c", + "def.h": "c", + "ch32v20xhw.h": "c", + "future": "c", + "compare": "c", + "cstdint": "c", + "snmp.h": "c", + "etharp.h": "c", + "dhcp.h": "c", + "netif.h": "c", + "ch32v307gigabit.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..01726ab --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +TARGET ?= main +TARGET_MCU ?= CH32V208 +TARGET_MCU_PACKAGE ?= CH32V208WBU6 + +CH32V003FUN ?= ./ch32fun/ch32fun +MINICHLINK ?= ./ch32fun/minichlink + +PREFIX ?= riscv64-elf +NEWLIB ?= /usr/riscv64-elf/include/ + +# LwIP +LWIP_DIR := ./lwip +PORT_DIR := ./port + +LWIPDIR := $(LWIP_DIR)/src + +include $(LWIP_DIR)/src/Filelists.mk + +LWIP_C_FILES := +LWIP_C_FILES += $(COREFILES) +LWIP_C_FILES += $(CORE4FILES) +LWIP_C_FILES += $(NETIFFILES) +# Add APIFILES if you are using the Netconn/Socket API +# ifeq ($(USE_API), 1) +# LWIP_C_FILES += $(APIFILES) +# endif +LWIP_C_FILES_WITH_PATH := $(LWIP_C_FILES) +LWIP_PORT_FILES := $(wildcard $(PORT_DIR)/*.c) + +INCLUDE_DIRS ?= \ + -I./inc \ + -I$(LWIP_DIR)/src/include \ + -I$(PORT_DIR) + +PROJECT_C_FILES := $(filter-out ./main.c, $(wildcard ./*.c)) +LIB_C_FILES := + +# Add all lwIP sources to the main build variable +ADDITIONAL_C_FILES := \ + $(PROJECT_C_FILES) \ + $(LIB_C_FILES) \ + $(LWIP_C_FILES_WITH_PATH) \ + $(LWIP_PORT_FILES) + +$(info Final ADDITIONAL_C_FILES is: [$(ADDITIONAL_C_FILES)]) + +include $(CH32V003FUN)/ch32fun.mk + +CFLAGS += -Wall -Wextra $(INCLUDE_DIRS) + +all: flash +flash: cv_flash +clean: cv_clean +.PHONY: all flash clean diff --git a/ch32fun b/ch32fun new file mode 160000 index 0000000..57b2ad6 --- /dev/null +++ b/ch32fun @@ -0,0 +1 @@ +Subproject commit 57b2ad6b56ffe25be2ef0903980e8b58f2b3fd9f diff --git a/funconfig.h b/funconfig.h new file mode 100644 index 0000000..1685965 --- /dev/null +++ b/funconfig.h @@ -0,0 +1,9 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define FUNCONF_USE_HSE 1 +#define FUNCONF_SYSTEM_CORE_CLOCK 120000000 +#define FUNCONF_PLL_MULTIPLIER 15 +#define FUNCONF_SYSTICK_USE_HCLK 1 + +#endif diff --git a/inc/systick.h b/inc/systick.h new file mode 100644 index 0000000..a9e59eb --- /dev/null +++ b/inc/systick.h @@ -0,0 +1,19 @@ +#ifndef SYSTICK_H +#define SYSTICK_H + +#include + +#include "ch32fun.h" +#include "ch32v20xhw.h" + +#define SYSTICK_ONE_MILLISECOND ((uint32_t)FUNCONF_SYSTEM_CORE_CLOCK / 1000) +#define SYSTICK_ONE_MICROSECOND ((uint32_t)FUNCONF_SYSTEM_CORE_CLOCK / 1000000) + +extern volatile uint32_t systick_millis; + +#define millis() (systick_millis) +#define micros() (SysTick->CNT / SYSTICK_ONE_MICROSECOND) + +void systick_init(void); + +#endif // SYSTICK_H \ No newline at end of file diff --git a/lwip b/lwip new file mode 160000 index 0000000..4599f55 --- /dev/null +++ b/lwip @@ -0,0 +1 @@ +Subproject commit 4599f551dead9eac233b91c0b9ee5879f5d0620a diff --git a/main.c b/main.c new file mode 100644 index 0000000..c2068f4 --- /dev/null +++ b/main.c @@ -0,0 +1,124 @@ +#include + +#include "ch32fun.h" +#include "ch32v20xhw.h" +#include "ethernetif.h" +#include "lwip/dhcp.h" +#include "lwip/init.h" +#include "lwip/netif.h" +#include "lwip/timeouts.h" +#include "netif/ethernet.h" +#include "systick.h" + +#define LED1_PIN 0 +#define LED2_PIN 2 + +#define RCC_PREDIV1_OFFSET 0 + +struct netif g_netif; + +void init_leds() { + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA; + GPIOA->CFGLR &= ~((0xf << (4 * 0)) | (0xf << (4 * 2))); + GPIOA->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 0); + GPIOA->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP) << (4 * 2); +} + +int main() { + SystemInit(); + + // 1. HSE (32MHz) + // 2. PREDIV1 / 4. + // 3. PLL source = HSE, PLL x15. + // 4. (32MHz / 4) * 15 = 120MHz SYSCLK + + RCC->INTR = 0x009F0000; + RCC->CTLR &= ~(RCC_HSE_ON | RCC_PLLON); + RCC->CFGR0 = 0x00000000; + RCC->CTLR |= RCC_HSE_ON; + + int timeout; + for (timeout = 10000; timeout > 0; timeout--) { + if (RCC->CTLR & RCC_HSERDY) break; // wait for HSE + } + if (timeout == 0) { + printf("Error: HSE failed to start\n"); + return -1; + } + + RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2; + RCC->CFGR2 = ((3) << 0); // PREDIV1 divisor = 3+1 = 4 + RCC->CFGR0 |= RCC_PLLSource_HSE_Div1 | RCC_PLLMul_15; + + RCC->CTLR |= RCC_PLLON; + printf("Main PLL enabled. Waiting for lock...\n"); + for (timeout = 10000; timeout > 0; timeout--) { + if (RCC->CTLR & RCC_PLLRDY) break; + } + if (timeout == 0) { + printf("error: main pll lock failed\n"); + return -1; + } + printf("Main PLL Locked\n"); + + RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL; + + while ((RCC->CFGR0 & RCC_SWS) != RCC_SWS_PLL); + + printf("SysClock set to 120MHz\n"); + + systick_init(); + init_leds(); + + lwip_init(); + + ip_addr_t ipaddr, netmask, gw; + IP4_ADDR(&ipaddr, 0, 0, 0, 0); + IP4_ADDR(&netmask, 0, 0, 0, 0); + IP4_ADDR(&gw, 0, 0, 0, 0); + + netif_add(&g_netif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, + ðernet_input); + + netif_set_default(&g_netif); + netif_set_up(&g_netif); + + dhcp_start(&g_netif); + + ethernetif_init(&g_netif); + + uint32_t last_led_toggle = 0; + uint32_t last_send_time = 0; + int led_state = 0; + + while (1) { + ethernetif_link_poll(&g_netif); + + if (netif_is_link_up(&g_netif)) { + ethernetif_input(&g_netif); + } + + sys_check_timeouts(); + + // run_tx_test(); + + uint32_t now = millis(); + + if (now - last_led_toggle > 500) { + if (led_state) { + GPIOA->BSHR = (1 << 0); + } else { + GPIOA->BSHR = (1 << (0 + 16)); + } + led_state = !led_state; + last_led_toggle = now; + } + + static int ip_printed = 0; + if (g_netif.ip_addr.addr != 0 && !ip_printed) { + printf("IP address assigned: %s\n", + ip4addr_ntoa(netif_ip4_addr(&g_netif))); + ip_printed = 1; + } + } +} \ No newline at end of file diff --git a/port/arch/cc.h b/port/arch/cc.h new file mode 100644 index 0000000..5b99363 --- /dev/null +++ b/port/arch/cc.h @@ -0,0 +1,35 @@ +#ifndef LWIP_ARCH_CC_H +#define LWIP_ARCH_CC_H + +#include +#include +#include +#include +#include + +#define BYTE_ORDER LITTLE_ENDIAN + +#define LWIP_PLATFORM_DIAG(x) \ + do { \ + printf x; \ + } while (0) + +#define LWIP_PLATFORM_ASSERT(x) \ + do { \ + printf("Assertion \"%s\" failed at line %d in %s\n", x, __LINE__, \ + __FILE__); \ + while (1); \ + } while (0) + +#define X8_F "02x" +#define U16_F "u" +#define S16_F "d" +#define X16_F "x" +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "u" + +#define LWIP_RAND() ((u32_t)rand()) + +#endif /* LWIP_ARCH_CC_H */ \ No newline at end of file diff --git a/port/ethernetif.c b/port/ethernetif.c new file mode 100644 index 0000000..9659075 --- /dev/null +++ b/port/ethernetif.c @@ -0,0 +1,344 @@ +#include "ethernetif.h" + +#include +#include + +#include "ch32fun.h" +#include "ch32v20xhw.h" +#include "lwip/def.h" +#include "lwip/etharp.h" +#include "lwip/ethip6.h" +#include "lwip/mem.h" +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/snmp.h" +#include "lwip/stats.h" +#include "netif/ethernet.h" +#include "systick.h" + +#define IFNAME0 'e' +#define IFNAME1 'n' + +#define ETH_RXBUFNB 4 +#define ETH_TXBUFNB 1 +#define ETH_MAX_PACKET_SIZE 1520 +#define ETH_RX_BUF_SZE ETH_MAX_PACKET_SIZE +#define ETH_TX_BUF_SZE ETH_MAX_PACKET_SIZE + +struct ethernetif { + ETH_DMADESCTypeDef* DMARxDescToGet; + ETH_DMADESCTypeDef* DMATxDescToSet; +}; + +__attribute__((aligned(4))) ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB]; +__attribute__((aligned(4))) ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB]; +__attribute__((aligned(4))) uint8_t MACRxBuf[ETH_RXBUFNB * ETH_RX_BUF_SZE]; +__attribute__((aligned(4))) uint8_t MACTxBuf[ETH_TXBUFNB * ETH_TX_BUF_SZE]; + +static volatile uint8_t g_rx_error_cnt = 0; +volatile uint32_t g_isr_call_count = 0; + +static err_t low_level_output(struct netif* netif, struct pbuf* p); +static struct pbuf* low_level_input(struct netif* netif); +static void low_level_init(struct netif* netif); + +void eth_dma_tx_desc_chain_init(struct ethernetif* ethernetif, + ETH_DMADESCTypeDef* DMATxDescTab, + uint8_t* TxBuff, uint32_t TxBuffCount) { + ethernetif->DMATxDescToSet = DMATxDescTab; + DMATxDescTab->Status = 0; + DMATxDescTab->Buffer1Addr = (uint32_t)TxBuff; + DMATxDescTab->Buffer2NextDescAddr = (uint32_t)DMATxDescTab; // ring of 1 +} + +void eth_dma_rx_desc_chain_init(struct ethernetif* ethernetif, + ETH_DMADESCTypeDef* DMARxDescTab, + uint8_t* RxBuff, uint32_t RxBuffCount) { + ethernetif->DMARxDescToGet = DMARxDescTab; + + for (uint32_t i = 0; i < RxBuffCount; i++) { + DMARxDescTab[i].Status = ETH_DMARxDesc_OWN; // give descriptor to DMA + DMARxDescTab[i].ControlBufferSize = ETH_RX_BUF_SZE; + + DMARxDescTab[i].Buffer1Addr = (uint32_t)(&RxBuff[i * ETH_RX_BUF_SZE]); + + if (i < (RxBuffCount - 1)) { + DMARxDescTab[i].Buffer2NextDescAddr = (uint32_t)(DMARxDescTab + i + 1); + } else { + DMARxDescTab[i].Buffer2NextDescAddr = (uint32_t)(DMARxDescTab); + } + } +} + +void ETH_IRQHandler(void) __attribute__((interrupt)); +void ETH_IRQHandler(void) { + g_isr_call_count++; + uint8_t flags = ETH10M->EIR; + + // tx complete/error + if (flags & (RB_ETH_EIR_TXIF | RB_ETH_EIR_TXERIF)) { + // release DMA descriptor back to cpu + if (DMATxDscrTab[0].Status & ETH_DMATxDesc_OWN) { + DMATxDscrTab[0].Status &= ~ETH_DMATxDesc_OWN; + } + } + + if (flags & RB_ETH_EIR_RXERIF) { + if (g_rx_error_cnt < 255) { + g_rx_error_cnt++; + } + } + + ETH10M->EIR = flags; +} + +static void low_level_init(struct netif* netif) { + struct ethernetif* ethernetif = netif->state; + uint8_t i; + + netif->hwaddr_len = ETH_HWADDR_LEN; + netif->hwaddr[0] = 0x00; + netif->hwaddr[1] = 0x80; + netif->hwaddr[2] = 0xE1; + netif->hwaddr[3] = 0x00; + netif->hwaddr[4] = 0x00; + netif->hwaddr[5] = 0x01; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + // clock + RCC->APB2PCENR |= RCC_APB2Periph_AFIO; + RCC->CFGR0 &= ~((uint32_t)1 << 28); + RCC->CFGR0 |= (RCC_ETHCLK_Div2 << 28); + + // interrupts + ETH10M->EIE = 0; // clear + ETH10M->EIE = RB_ETH_EIE_INTIE | RB_ETH_EIE_RXIE | RB_ETH_EIE_LINKIE | + RB_ETH_EIE_TXIE | RB_ETH_EIE_TXERIE | RB_ETH_EIE_RXERIE; + ETH10M->EIE |= RB_ETH_EIE_R_EN50; // 50 ohm pull-up + + ETH10M->EIR = 0xFF; + ETH10M->ESTAT |= RB_ETH_ESTAT_INT | RB_ETH_ESTAT_BUFER; + + // reset mac + ETH10M->ECON1 |= (RB_ETH_ECON1_TXRST | RB_ETH_ECON1_RXRST); + ETH10M->ECON1 &= ~(RB_ETH_ECON1_TXRST | RB_ETH_ECON1_RXRST); + + // // mac regs + ETH10M->ERXFON = 0; // accept unicast, multicast, broadcast + + R8_ETH_MAADRL1 = netif->hwaddr[5]; + R8_ETH_MAADRL2 = netif->hwaddr[4]; + R8_ETH_MAADRL3 = netif->hwaddr[3]; + R8_ETH_MAADRL4 = netif->hwaddr[2]; + R8_ETH_MAADRL5 = netif->hwaddr[1]; + R8_ETH_MAADRL6 = netif->hwaddr[0]; + + ETH10M->MACON1 = RB_ETH_MACON1_MARXEN; + + ETH10M->MACON2 &= ~RB_ETH_MACON2_PADCFG; + ETH10M->MACON2 |= PADCFG_AUTO_3 | RB_ETH_MACON2_TXCRCEN; + ETH10M->MACON2 &= ~RB_ETH_MACON2_HFRMEN; // disable huge frames + ETH10M->MACON2 |= RB_ETH_MACON2_FULDPX; + + ETH10M->MAMXFL = ETH_MAX_PACKET_SIZE; + + // PHY analog block + ETH10M->ECON2 &= ~(0x07 << 1); + ETH10M->ECON2 |= (5 << 1); + + // en PHY block + EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN; + + // tx desc + eth_dma_tx_desc_chain_init(ethernetif, DMATxDscrTab, MACTxBuf, ETH_TXBUFNB); + // rx desc + eth_dma_rx_desc_chain_init(ethernetif, DMARxDscrTab, MACRxBuf, ETH_RXBUFNB); + + printf("set PHY to 10Mbps Full-Duplex mode\n"); + WritePHYReg(PHY_BMCR, PHY_BMCR_FORCE_10BASE_T_FD); + + // init phy and auto neg + // WritePHYReg(PHY_BMCR, PHY_BMCR_RESET); + // Delay_Ms(200); + // WritePHYReg(PHY_BMCR, PHY_BMCR_FORCE_10BASE_T_FD | PHY_BMCR_AN_ENABLE | + // PHY_BMCR_AN_RESTART); + // Delay_Ms(1000); + + NVIC_EnableIRQ(ETH_IRQn); + printf("low_level_init : done\n"); +} + +static err_t low_level_output(struct netif* netif, struct pbuf* p) { + struct ethernetif* ethernetif = netif->state; + struct pbuf* q; + uint32_t len = 0; + uint8_t* tx_buf_ptr = (uint8_t*)ethernetif->DMATxDescToSet->Buffer1Addr; + + if (ethernetif->DMATxDescToSet->Status & ETH_DMATxDesc_OWN) { + return ERR_BUF; + } + + for (q = p; q != NULL; q = q->next) { + memcpy(&tx_buf_ptr[len], q->payload, q->len); + len += q->len; + } + + ethernetif->DMATxDescToSet->Status |= ETH_DMATxDesc_OWN; + ETH10M->ETXLN = len; + ETH10M->ETXST = (uint32_t)tx_buf_ptr; + ETH10M->ECON1 |= RB_ETH_ECON1_TXRTS; + + ethernetif->DMATxDescToSet = + (ETH_DMADESCTypeDef*)ethernetif->DMATxDescToSet->Buffer2NextDescAddr; + + MIB2_STATS_NETIF_ADD(netif, ifoutoctets, len); + return ERR_OK; +} + +static struct pbuf* low_level_input(struct netif* netif) { + struct ethernetif* ethernetif = netif->state; + struct pbuf *p = NULL, *q; + u16_t len; + + if ((ethernetif->DMARxDescToGet->Status & ETH_DMARxDesc_OWN) == 0) { + if (ETH10M->ESTAT & (RB_ETH_ESTAT_BUFER | RB_ETH_ESTAT_RXCRCER)) { + len = 0; // drop packet + // printf("RX ESTAT Error: 0x%02X\n", ETH10M->ESTAT); + ETH10M->ESTAT |= (RB_ETH_ESTAT_BUFER | RB_ETH_ESTAT_RXCRCER); + } else { + len = ETH10M->ERXLN; + if (len > 4) { + len -= 4; + } else { + len = 0; + } + } + + // copy valid packet to pbuf + if (len > 0) { + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + if (p != NULL) { + uint8_t* rx_buffer = (uint8_t*)ethernetif->DMARxDescToGet->Buffer1Addr; + uint32_t bytes_copied = 0; + for (q = p; q != NULL; q = q->next) { + memcpy(q->payload, rx_buffer + bytes_copied, q->len); + bytes_copied += q->len; + } + MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len); + } + } + + ethernetif->DMARxDescToGet->Status |= ETH_DMARxDesc_OWN; + + ethernetif->DMARxDescToGet = + (ETH_DMADESCTypeDef*)ethernetif->DMARxDescToGet->Buffer2NextDescAddr; + + // tell hw where next free buffer is? + ETH10M->ERXST = (uint32_t)ethernetif->DMARxDescToGet->Buffer1Addr; + + return p; + } + + return NULL; // no packet +} + +void ethernetif_input(struct netif* netif) { + struct pbuf* p; + + p = low_level_input(netif); + + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + } + } +} + +err_t ethernetif_init(struct netif* netif) { + struct ethernetif* ethernetif; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + if (ethernetif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + +#if LWIP_NETIF_HOSTNAME + netif->hostname = "lwip-wch"; +#endif + + MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 10000000); // 10 Mbps + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; +#if LWIP_IPV4 + netif->output = etharp_output; +#endif +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif + netif->linkoutput = low_level_output; + + low_level_init(netif); + + return ERR_OK; +} + +void ethernetif_link_poll(struct netif* netif) { + static uint32_t last_poll_time = 0; + uint32_t now = millis(); + + // every 500ms + if (now - last_poll_time < 500) { + return; + } + last_poll_time = now; + + uint16_t bmsr = ReadPHYReg(PHY_BMSR); + + if (bmsr & PHY_Linked_Status) { + if (!netif_is_link_up(netif)) { + printf("Link is UP (10M-FD Mode)\n"); + + ETH10M->ECON1 |= RB_ETH_ECON1_RXEN; + + netif_set_link_up(netif); + g_rx_error_cnt = 0; + } + + // polarity check + // https://github.com/openwch/ch32v20x/blob/main/EVT/EXAM/ETH/NetLib/eth_driver.c#L262 + if (g_rx_error_cnt > 5) { + printf("RX error count: %d. Flipping PHY polarity\n", g_rx_error_cnt); + uint16_t mdix_val = ReadPHYReg(PHY_MDIX); + if ((mdix_val >> 2) & 0x01) { + mdix_val &= ~(3 << 2); // normal + } else { + mdix_val |= (1 << 2); // reverse + } + WritePHYReg(PHY_MDIX, mdix_val); + g_rx_error_cnt = 0; + } + + } else { + if (netif_is_link_up(netif)) { + printf("Link is DOWN\n"); + netif_set_link_down(netif); + ETH10M->ECON1 &= ~RB_ETH_ECON1_RXEN; + } + } +} + +void WritePHYReg(uint8_t reg_add, uint16_t reg_val) { + R32_ETH_MIWR = (reg_add & RB_ETH_MIREGADR_MIRDL) | (1 << 8) | (reg_val << 16); +} + +uint16_t ReadPHYReg(uint8_t reg_add) { + ETH10M->MIERGADR = reg_add; + return ETH10M->MIRD; +} \ No newline at end of file diff --git a/port/ethernetif.h b/port/ethernetif.h new file mode 100644 index 0000000..3953010 --- /dev/null +++ b/port/ethernetif.h @@ -0,0 +1,161 @@ +#ifndef __ETHERNETIF_H +#define __ETHERNETIF_H + +#include "lwip/err.h" +#include "lwip/netif.h" + +void run_tx_test(void); +void WritePHYReg(uint8_t reg_add, uint16_t reg_val); +uint16_t ReadPHYReg(uint8_t reg_add); + +#define PHY_BMCR_FORCE_10BASE_T_HD ((uint16_t)0x0000) +#define PHY_BMCR_FORCE_10BASE_T_FD ((uint16_t)0x0100) // 10M, Full Duplex + +#define PHY_ANAR_SELECTOR_FIELD 0x0001 // Selector for 802.3 +#define PHY_ANAR_10BASET_HD 0x0020 // 10M Half-Duplex +#define PHY_ANAR_10BASET_FD 0x0040 // 10M Full-Duplex + +#define PHY_BMCR_RESET ((uint16_t)0x8000) // Reset PHY +#define PHY_BMCR_AN_ENABLE \ + ((uint16_t)0x1000) // Enable Auto-Negotiation (Bit 12) +#define PHY_BMCR_AN_RESTART \ + ((uint16_t)0x0200) // Restart Auto-Negotiation (Bit 9) + +/** + DMA Tx Desciptor + ----------------------------------------------------------------------------------------------- + TDES0 | OWN(31) | CTRL[30:26] | Reserved[25:24] | CTRL[23:20] | Reserved[19:17] | Status[16:0] | + ----------------------------------------------------------------------------------------------- + TDES1 | Reserved[31:29] | Buffer2 ByteCount[28:16] | Reserved[15:13] | Buffer1 ByteCount[12:0] | + ----------------------------------------------------------------------------------------------- + TDES2 | Buffer1 Address [31:0] | + ----------------------------------------------------------------------------------------------- + TDES3 | Buffer2 Address [31:0] / Next Desciptor Address [31:0] | + ------------------------------------------------------------------------------------------------ +*/ + + +/* Bit or field definition of TDES0 register (DMA Tx descriptor status register)*/ +#define ETH_DMATxDesc_OWN ((uint32_t)0x80000000) /* OWN bit: descriptor is owned by DMA engine */ +#define ETH_DMATxDesc_IC ((uint32_t)0x40000000) /* Interrupt on Completion */ +#define ETH_DMATxDesc_LS ((uint32_t)0x20000000) /* Last Segment */ +#define ETH_DMATxDesc_FS ((uint32_t)0x10000000) /* First Segment */ +#define ETH_DMATxDesc_DC ((uint32_t)0x08000000) /* Disable CRC */ +#define ETH_DMATxDesc_DP ((uint32_t)0x04000000) /* Disable Padding */ +#define ETH_DMATxDesc_TTSE ((uint32_t)0x02000000) /* Transmit Time Stamp Enable */ +#define ETH_DMATxDesc_CIC ((uint32_t)0x00C00000) /* Checksum Insertion Control: 4 cases */ +#define ETH_DMATxDesc_CIC_ByPass ((uint32_t)0x00000000) /* Do Nothing: Checksum Engine is bypassed */ +#define ETH_DMATxDesc_CIC_IPV4Header ((uint32_t)0x00400000) /* IPV4 header Checksum Insertion */ +#define ETH_DMATxDesc_CIC_TCPUDPICMP_Segment ((uint32_t)0x00800000) /* TCP/UDP/ICMP Checksum Insertion calculated over segment only */ +#define ETH_DMATxDesc_CIC_TCPUDPICMP_Full ((uint32_t)0x00C00000) /* TCP/UDP/ICMP Checksum Insertion fully calculated */ +#define ETH_DMATxDesc_TER ((uint32_t)0x00200000) /* Transmit End of Ring */ +#define ETH_DMATxDesc_TCH ((uint32_t)0x00100000) /* Second Address Chained */ +#define ETH_DMATxDesc_TTSS ((uint32_t)0x00020000) /* Tx Time Stamp Status */ +#define ETH_DMATxDesc_IHE ((uint32_t)0x00010000) /* IP Header Error */ +#define ETH_DMATxDesc_ES ((uint32_t)0x00008000) /* Error summary: OR of the following bits: UE || ED || EC || LCO || NC || LCA || FF || JT */ +#define ETH_DMATxDesc_JT ((uint32_t)0x00004000) /* Jabber Timeout */ +#define ETH_DMATxDesc_FF ((uint32_t)0x00002000) /* Frame Flushed: DMA/MTL flushed the frame due to SW flush */ +#define ETH_DMATxDesc_PCE ((uint32_t)0x00001000) /* Payload Checksum Error */ +#define ETH_DMATxDesc_LCA ((uint32_t)0x00000800) /* Loss of Carrier: carrier lost during tramsmission */ +#define ETH_DMATxDesc_NC ((uint32_t)0x00000400) /* No Carrier: no carrier signal from the tranceiver */ +#define ETH_DMATxDesc_LCO ((uint32_t)0x00000200) /* Late Collision: transmission aborted due to collision */ +#define ETH_DMATxDesc_EC ((uint32_t)0x00000100) /* Excessive Collision: transmission aborted after 16 collisions */ +#define ETH_DMATxDesc_VF ((uint32_t)0x00000080) /* VLAN Frame */ +#define ETH_DMATxDesc_CC ((uint32_t)0x00000078) /* Collision Count */ +#define ETH_DMATxDesc_ED ((uint32_t)0x00000004) /* Excessive Deferral */ +#define ETH_DMATxDesc_UF ((uint32_t)0x00000002) /* Underflow Error: late data arrival from the memory */ +#define ETH_DMATxDesc_DB ((uint32_t)0x00000001) /* Deferred Bit */ + +/* Field definition of TDES1 register */ +#define ETH_DMATxDesc_TBS2 ((uint32_t)0x1FFF0000) /* Transmit Buffer2 Size */ +#define ETH_DMATxDesc_TBS1 ((uint32_t)0x00001FFF) /* Transmit Buffer1 Size */ + +/* Field definition of TDES2 register */ +#define ETH_DMATxDesc_B1AP ((uint32_t)0xFFFFFFFF) /* Buffer1 Address Pointer */ + +/* Field definition of TDES3 register */ +#define ETH_DMATxDesc_B2AP ((uint32_t)0xFFFFFFFF) /* Buffer2 Address Pointer */ + +/** + DMA Rx Desciptor + --------------------------------------------------------------------------------------------------------------------- + RDES0 | OWN(31) | Status [30:0] | + --------------------------------------------------------------------------------------------------------------------- + RDES1 | CTRL(31) | Reserved[30:29] | Buffer2 ByteCount[28:16] | CTRL[15:14] | Reserved(13) | Buffer1 ByteCount[12:0] | + --------------------------------------------------------------------------------------------------------------------- + RDES2 | Buffer1 Address [31:0] | + --------------------------------------------------------------------------------------------------------------------- + RDES3 | Buffer2 Address [31:0] / Next Desciptor Address [31:0] | + ---------------------------------------------------------------------------------------------------------------------- +*/ + +/* Bit or field definition of RDES0 register (DMA Rx descriptor status register) */ +#define ETH_DMARxDesc_OWN ((uint32_t)0x80000000) /* OWN bit: descriptor is owned by DMA engine */ +#define ETH_DMARxDesc_AFM ((uint32_t)0x40000000) /* DA Filter Fail for the rx frame */ +#define ETH_DMARxDesc_FL ((uint32_t)0x3FFF0000) /* Receive descriptor frame length */ +#define ETH_DMARxDesc_ES ((uint32_t)0x00008000) /* Error summary: OR of the following bits: DE || OE || IPC || LC || RWT || RE || CE */ +#define ETH_DMARxDesc_DE ((uint32_t)0x00004000) /* Desciptor error: no more descriptors for receive frame */ +#define ETH_DMARxDesc_SAF ((uint32_t)0x00002000) /* SA Filter Fail for the received frame */ +#define ETH_DMARxDesc_LE ((uint32_t)0x00001000) /* Frame size not matching with length field */ +#define ETH_DMARxDesc_OE ((uint32_t)0x00000800) /* Overflow Error: Frame was damaged due to buffer overflow */ +#define ETH_DMARxDesc_VLAN ((uint32_t)0x00000400) /* VLAN Tag: received frame is a VLAN frame */ +#define ETH_DMARxDesc_FS ((uint32_t)0x00000200) /* First descriptor of the frame */ +#define ETH_DMARxDesc_LS ((uint32_t)0x00000100) /* Last descriptor of the frame */ +#define ETH_DMARxDesc_IPV4HCE ((uint32_t)0x00000080) /* IPC Checksum Error: Rx Ipv4 header checksum error */ +#define ETH_DMARxDesc_LC ((uint32_t)0x00000040) /* Late collision occurred during reception */ +#define ETH_DMARxDesc_FT ((uint32_t)0x00000020) /* Frame type - Ethernet, otherwise 802.3 */ +#define ETH_DMARxDesc_RWT ((uint32_t)0x00000010) /* Receive Watchdog Timeout: watchdog timer expired during reception */ +#define ETH_DMARxDesc_RE ((uint32_t)0x00000008) /* Receive error: error reported by MII interface */ +#define ETH_DMARxDesc_DBE ((uint32_t)0x00000004) /* Dribble bit error: frame contains non int multiple of 8 bits */ +#define ETH_DMARxDesc_CE ((uint32_t)0x00000002) /* CRC error */ +#define ETH_DMARxDesc_MAMPCE ((uint32_t)0x00000001) /* Rx MAC Address/Payload Checksum Error: Rx MAC address matched/ Rx Payload Checksum Error */ + +/* Bit or field definition of RDES1 register */ +#define ETH_DMARxDesc_DIC ((uint32_t)0x80000000) /* Disable Interrupt on Completion */ +#define ETH_DMARxDesc_RBS2 ((uint32_t)0x1FFF0000) /* Receive Buffer2 Size */ +#define ETH_DMARxDesc_RER ((uint32_t)0x00008000) /* Receive End of Ring */ +#define ETH_DMARxDesc_RCH ((uint32_t)0x00004000) /* Second Address Chained */ +#define ETH_DMARxDesc_RBS1 ((uint32_t)0x00001FFF) /* Receive Buffer1 Size */ + +/* Field definition of RDES2 register */ +#define ETH_DMARxDesc_B1AP ((uint32_t)0xFFFFFFFF) /* Buffer1 Address Pointer */ + +/* Field definition of RDES3 register */ +#define ETH_DMARxDesc_B2AP ((uint32_t)0xFFFFFFFF) /* Buffer2 Address Pointer */ + +#define ETH_DMARxDesc_FrameLengthShift 16 + +typedef struct { + uint32_t volatile Status; /* Status */ + uint32_t ControlBufferSize; /* Control and Buffer1, Buffer2 lengths */ + uint32_t Buffer1Addr; /* Buffer1 address pointer */ + uint32_t Buffer2NextDescAddr; /* Buffer2 or next descriptor address pointer */ +} ETH_DMADESCTypeDef; + +/** + * @brief Initialize the ethernet interface and lwIP network stack. + * This function should be passed as the init function to netif_add(). + * + * @param netif The lwIP network interface structure to be initialized. + * @return ERR_OK if the loopif is initialized, ERR_MEM if private data couldn't + * be allocated. + */ +err_t ethernetif_init(struct netif* netif); + +/** + * @brief This function should be called periodically from your main loop + * to check for incoming packets and pass them to lwIP. + * + * @param netif The lwIP network interface structure. + */ +void ethernetif_input(struct netif* netif); + +/** + * @brief This function should be called periodically from your main loop + * to check the link status and update lwIP accordingly. + * + * @param netif The lwIP network interface structure.. + */ +void ethernetif_link_poll(struct netif* netif); + +#endif /* __ETHERNETIF_H */ \ No newline at end of file diff --git a/port/lwipopts.h b/port/lwipopts.h new file mode 100644 index 0000000..050987c --- /dev/null +++ b/port/lwipopts.h @@ -0,0 +1,53 @@ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +#define LWIP_DEBUG 1 +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL + +#define DHCP_DEBUG LWIP_DBG_ON +#define NETIF_DEBUG LWIP_DBG_ON +#define ETHARP_DEBUG LWIP_DBG_ON + +#define NO_SYS 1 + +// Core locking +#define SYS_LIGHTWEIGHT_PROT 0 + +// Memory options +#define MEM_ALIGNMENT 4 +#define MEM_SIZE (4 * 1024) // 4KB of RAM for lwIP heap + +// Pbuf options +#define PBUF_POOL_SIZE 8 +#define PBUF_POOL_BUFSIZE 1524 + +// TCP options +#define LWIP_TCP 1 +#define TCP_MSS 1460 +#define TCP_SND_BUF (2 * TCP_MSS) + +// UDP options +#define LWIP_UDP 1 + +// ICMP options +#define LWIP_ICMP 1 + +// DHCP options +#define LWIP_DHCP 1 + +// Checksum options +// #define CHECKSUM_GEN_IP 0 +// #define CHECKSUM_GEN_UDP 0 +// #define CHECKSUM_GEN_TCP 0 +// #define CHECKSUM_CHECK_IP 0 +// #define CHECKSUM_CHECK_UDP 0 +// #define CHECKSUM_CHECK_TCP 0 +// #define LWIP_CHECKSUM_ON_COPY 1 + +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 + +// Statistics +#define LWIP_STATS 0 + +#endif /* __LWIPOPTS_H__ */ diff --git a/port/sys_arch.c b/port/sys_arch.c new file mode 100644 index 0000000..d3c2b3f --- /dev/null +++ b/port/sys_arch.c @@ -0,0 +1,25 @@ +#include "ch32fun.h" +#include "lwip/def.h" +#include "systick.h" + +typedef uint32_t sys_prot_t; +static unsigned long next = 1; + +int rand(void) { + next = next * 1103515245 + 12345; + return (unsigned int)(next / 65536) % 32768; +} + +void srand(unsigned int seed) { next = seed; } + +uint32_t sys_now(void) { return systick_millis; } + +sys_prot_t sys_arch_protect(void) { + unsigned int old_mstatus; + __asm__ volatile("csrrci %0, mstatus, 8" : "=r"(old_mstatus)); + return old_mstatus; +} + +void sys_arch_unprotect(sys_prot_t pval) { + __asm__ volatile("csrw mstatus, %0" : : "r"(pval)); +} diff --git a/systick.c b/systick.c new file mode 100644 index 0000000..015e51b --- /dev/null +++ b/systick.c @@ -0,0 +1,21 @@ +#include "systick.h" + +volatile uint32_t systick_millis; + +void systick_init(void) { + SysTick->CTLR = 0x0000; + SysTick->CMP = SysTick->CNT + SYSTICK_ONE_MILLISECOND; + systick_millis = 0; + SysTick->CTLR = SYSTICK_CTLR_STE | // Enable Counter + SYSTICK_CTLR_STIE | // Enable Interrupts + SYSTICK_CTLR_STCLK; // Set Clock Source to HCLK/1 + + NVIC_EnableIRQ(SysTick_IRQn); +} + +void SysTick_Handler(void) __attribute__((interrupt)); +void SysTick_Handler(void) { + SysTick->CMP = SysTick->CNT + SYSTICK_ONE_MILLISECOND; + SysTick->SR = 0; + systick_millis++; +} \ No newline at end of file