Add STM32H743ZI driver and example

This commit is contained in:
Sergio R. Caprile 2023-02-03 16:32:38 -03:00
parent 43e48d2ba6
commit 958eebb16c
12 changed files with 1043 additions and 39 deletions

View File

@ -2,14 +2,14 @@ CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \
-Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion \
-g3 -Os -ffunction-sections -fdata-sections \
-I. -Ih7/Include -Icmsis/CMSIS/Core/Include \
-mcpu=cortex-m7 -mthumb -mfloat-abi=soft \
-mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 \
$(EXTRA_CFLAGS)
LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = startup.c main.c syscalls.c
# Add Mongoose-specific flags and source files
CFLAGS += -I../../.. -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -DMG_ENABLE_MIP=1 #-DMG_ENABLE_PACKED_FS=1
SOURCES += ../../../mongoose.c #../../device-dashboard/net.c ../../device-dashboard/packed_fs.c
CFLAGS += -I../../.. -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -DMG_ENABLE_MIP=1 -DMG_ENABLE_DRIVER_STM32H=1 -DMG_ENABLE_PACKED_FS=1
SOURCES += ../../../mongoose.c ../../device-dashboard/net.c ../../device-dashboard/packed_fs.c
all build example: firmware.bin
@ -25,11 +25,11 @@ flash: firmware.bin
# Requires env variable VCON_API_KEY set
DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/6
test: update
test: EXTRA_CFLAGS += -DUART_DEBUG=UART1
test: update
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
grep 'ticks:' /tmp/output.txt
# grep 'Ethernet: up' /tmp/output.txt
# grep 'MQTT connected' /tmp/output.txt
grep 'READY, IP:' /tmp/output.txt
grep 'MQTT connected' /tmp/output.txt
update: firmware.bin
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$<

View File

@ -0,0 +1,41 @@
# Baremetal webserver on NUCLEO-H743ZI
This firmware uses MIP, an experimental TCP/IP stack of the Mongoose Network Library,
which implements the following:
- A complete [HTTP device dashboard](../../device-dashboard) with:
- User authentication
- Real-time device data graph
- Coninfiguration display and update
- MQTT communication with a remote MQTT server
- No dependencies: no HAL, no CMSIS, no RTOS
- Hand-written [mcu.h](mcu.h) header based on the [datasheet](https://www.st.com/resource/en/reference_manual/rm0433-stm32h742-stm32h743753-and-stm32h750-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf)
- Interrupt-driven [Ethernet driver](../../../drivers/mip_driver_stm32h.c)
- Debug log on UART3 (st-link)
## Requirements
- [GNU make](http://mongoose.ws/tutorials/tools/#gnu-make)
- [ARM GCC](http://mongoose.ws/tutorials/tools/#arm-gcc)
- [stlink](http://mongoose.ws/tutorials/tools/#stlink) for flashing
The links above will send you to tutorials on how to install each of those tools in your workstation for Linux, Mac, and Windows.
## Usage
Plugin your Nucleo board into USB, and attach an Ethernet cable.
To build and flash:
```sh
$ make clean flash
```
To see debug log, use any serial monitor program like `picocom` at 115200 bps and configure it to insert carriage returns after line feeds:
```sh
$ picocom /dev/ttyACM0 -i -b 115200 --imap=lfcrlf
```
There is also a [tutorial on a similar example](https://mongoose.ws/tutorials/stm32/nucleo-f746zg-baremetal/) but for the NUCLEO-F746ZG board
For more details and benchmark data on MIP, check the [F746ZG example](../nucleo-f746zg-baremetal/)

View File

@ -1,7 +1,10 @@
ENTRY(_reset);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 64k
sram(rwx) : ORIGIN = 0x24000000, LENGTH = 512k /* AXI SRAM in domain D1 */
/* 2.3.2: remaining SRAM is in other (non-contiguous) banks,
DTCM @0x20000000 is in domain D1 and not accessible by the ETH DMA controller in domain D2
SRAM @0x30000000 is in domain D2 and not directly available at startup to be used as stack (8.5.9 page 366) */
}
_estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */

View File

@ -1,4 +1,4 @@
// Copyright (c) 2022 Cesanta Software Limited
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved
#include "mcu.h"
@ -21,9 +21,30 @@ static void timer_cb(void *arg) {
int main(void) {
clock_init();
systick_init(SYS_FREQUENCY / 1000); // Increment s_ticks every ms
systick_init(CPU_FREQUENCY / 1000); // Increment s_ticks every ms
gpio_output(LED2); // Setup LED
uart_init(UART_DEBUG, 115200); // Initialise debug printf
uart_init(UART_DEBUG, 115200); // Initialise debug printf
char rev = chiprev();
MG_INFO(("Chip revision: %c, max cpu clock: %u MHz", rev, (rev == 'V') ? 480 : 400));
// Initialise Ethernet. Enable MAC GPIO pins, see UM2407 for MB1364 boards
// section 6.6.7:
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
uint16_t pins[] = {PIN('A', 1), PIN('A', 2), PIN('A', 7),
PIN('B', 13), PIN('C', 1), PIN('C', 4),
PIN('C', 5), PIN('G', 11), PIN('G', 13)};
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
gpio_init(pins[i], GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_INSANE,
GPIO_PULL_NONE, 11);
}
nvic_enable_irq(61); // Setup Ethernet IRQ handler
RCC->APB4ENR |= BIT(1); // Enable SYSCFG
SETBITS(SYSCFG->PMCR, 7 << 21, 4 << 21); // Use RMII (12.3.1)
RCC->AHB1ENR |= BIT(15) | BIT(16) | BIT(17); // Enable Ethernet clocks
RCC->AHB1RSTR |= BIT(15); // ETHMAC force reset
RCC->AHB1RSTR &= ~BIT(15); // ETHMAC release reset
MG_INFO(("Initialising Mongoose..."));
struct mg_mgr mgr; // Initialise Mongoose event manager
@ -31,10 +52,29 @@ int main(void) {
mg_log_set(MG_LL_DEBUG); // Set log level
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_cb, NULL);
MG_INFO(("Starting event loop"));
for (;;) {
// Initialise Mongoose network stack
// Specify MAC address, and IP/mask/GW in network byte order for static
// IP configuration. If IP/mask/GW are unset, DHCP is going to be used
struct mip_driver_stm32h_data driver_data = {.mdc_cr =
4}; // See driver_stm32h.h
struct mip_if mif = {
.mac = {2, 0, 1, 2, 3, 5},
.driver = &mip_driver_stm32h,
.driver_data = &driver_data,
};
mip_init(&mgr, &mif);
MG_INFO(("Waiting until network is up..."));
while (mif.state != MIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
MG_INFO(("Initialising application..."));
extern void device_dashboard_fn(struct mg_connection *, int, void *, void *);
mg_http_listen(&mgr, "http://0.0.0.0", device_dashboard_fn, &mgr);
MG_INFO(("Starting event loop"));
for (;;) mg_mgr_poll(&mgr, 0); // Infinite event loop
return 0;
}

View File

@ -18,22 +18,32 @@
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
// TODO: Set clock. Now, running @ default of 64 MHz
enum { PLL_HSI = 64, PLL_M = 1, PLL_N = 1, PLL_P = 1 };
#define FLASH_LATENCY 7
#define SYS_FREQUENCY ((PLL_HSI * PLL_N / PLL_M / PLL_P) * 1000000)
#define APB2_FREQUENCY SYS_FREQUENCY
#define APB1_FREQUENCY SYS_FREQUENCY
struct systick {
volatile uint32_t CTRL, LOAD, VAL, CALIB;
// System clock (2.1, Figure 1; 8.5, Figure 45; 8.5.5, Figure 47; 8.5.6, Figure
// 49) CPU_FREQUENCY <= 480 MHz; hclk = CPU_FREQUENCY / HPRE ; hclk <= 240 MHz;
// APB clocks <= 120 MHz. D1 domain bus matrix (and so flash) runs at hclk
// frequency. Configure flash latency (WS) in accordance to hclk freq (4.3.8,
// Table 17) The Ethernet controller is in D2 domain and runs at hclk frequency
enum {
D1CPRE = 1, // actual divisor value
HPRE = 2, // actual divisor value
D1PPRE = 4, // register values, divisor value = BIT(value - 3) = / 2
D2PPRE1 = 4,
D2PPRE2 = 4,
D3PPRE = 4
};
#define SYSTICK ((struct systick *) 0xe000e010)
static inline void systick_init(uint32_t ticks) {
if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit
SYSTICK->LOAD = ticks - 1;
SYSTICK->VAL = 0;
SYSTICK->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick
// PLL1_P: odd division factors are not allowed (8.7.13) (according to Cube, '1'
// is also an "odd division factor").
// Make sure your chip is revision 'V', otherwise set PLL1_N = 400
enum { PLL1_HSI = 64, PLL1_M = 32, PLL1_N = 480, PLL1_P = 2 };
#define FLASH_LATENCY 0x24 // WRHIGHFREQ LATENCY
#define CPU_FREQUENCY ((PLL1_HSI * PLL1_N / PLL1_M / PLL1_P / D1CPRE) * 1000000)
// #define CPU_FREQUENCY ((PLL1_HSI / D1CPRE) * 1000000)
#define AHB_FREQUENCY (CPU_FREQUENCY / HPRE)
#define APB2_FREQUENCY (AHB_FREQUENCY / (BIT(D2PPRE2 - 3)))
#define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(D2PPRE1 - 3)))
static inline void spin(volatile uint32_t n) {
while (n--) (void) 0;
}
struct rcc {
@ -48,10 +58,56 @@ struct rcc {
APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR, RESERVED13[4];
};
#define RCC ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400))
struct pwr {
volatile uint32_t CR1, CSR1, CR2, CR3, CPUCR, RESERVED0, D3CR, RESERVED1,
WKUPCR, WKUPFR, WKUPEPR;
};
#define PWR ((struct pwr *) (0x40000000 + 0x18020000 + 0x4800))
static inline void spin(volatile uint32_t n) {
while (n--) (void) 0;
struct nvic {
volatile uint32_t ISER[8], RESERVED0[24], ICER[8], RSERVED1[24], ISPR[8],
RESERVED2[24], ICPR[8], RESERVED3[24], IABR[8], RESERVED4[56], IP[240],
RESERVED5[644], STIR;
};
#define NVIC ((struct nvic *) 0xe000e100)
static inline void nvic_set_prio(int irq, uint32_t prio) {
NVIC->IP[irq] = prio << 4;
}
static inline void nvic_enable_irq(int irq) {
NVIC->ISER[irq >> 5] = (uint32_t) (1 << (irq & 31));
}
struct systick {
volatile uint32_t CTRL, LOAD, VAL, CALIB;
};
#define SYSTICK ((struct systick *) 0xe000e010)
static inline void systick_init(uint32_t ticks) {
if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit
SYSTICK->LOAD = ticks - 1;
SYSTICK->VAL = 0;
SYSTICK->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick
}
struct flash {
volatile uint32_t ACR, KEYR1, OPTKEYR, CR1, SR1, CCR1, OPTCR, OPTSR_CUR,
OPTSR_PRG, OPTCCR, PRAR_CUR1, PRAR_PRG1, SCAR_CUR1, SCAR_PRG1, WPSN_CUR1,
WPSN_PRG1, BOOT_CUR, BOOT_PRG, RESERVED0, CRCCR1, CRCSADD1, CRCEADD1,
CRCDATA, ECC_FA1, RESERVED1, KEYR2, RESERVED2, CR2, SR2, CCR2, RESERVED3,
PRAR_CUR2, PRAR_PRG2, SCAR_CUR2, SCAR_PRG2, WPSN_CUR2, WPSN_PRG2,
RESERVED4, CRCCR2, CRCSADD2, CRCEADD2, CRCDATA2, ECC_FA2;
};
#define FLASH ((struct flash *) (0x40000000UL + 0x12000000UL + 0x2000UL))
struct scb {
volatile uint32_t CPUID, ICSR, VTOR, AIRCR, SCR, CCR, SHPR[3], SHCSR, CFSR,
HFSR, DFSR, MMFAR, BFAR, AFSR, ID_PFR[2], ID_DFR, ID_AFR, ID_MFR[4],
ID_ISAR[5], RESERVED0[1], CLIDR, CTR, CCSIDR, CSSELR, CPACR,
RESERVED3[93], STIR, RESERVED4[15], MVFR0, MVFR1, MVFR2, RESERVED5[1],
ICIALLU, RESERVED6[1], ICIMVAU, DCIMVAC, DCISW, DCCMVAU, DCCMVAC, DCCSW,
DCCIMVAC, DCCISW, RESERVED7[6], ITCMCR, DTCMCR, AHBPCR, CACR, AHBSCR,
RESERVED8[1], ABFSR;
};
#define SCB ((struct scb *) 0xe000ed00)
enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG };
enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN };
@ -63,7 +119,9 @@ struct gpio {
};
#define GPIO(N) ((struct gpio *) (0x40000000 + 0x18020000UL + 0x400 * (N)))
static struct gpio *gpio_bank(uint16_t pin) { return GPIO(PINBANK(pin)); }
static struct gpio *gpio_bank(uint16_t pin) {
return GPIO(PINBANK(pin));
}
static inline void gpio_toggle(uint16_t pin) {
struct gpio *gpio = gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
@ -97,6 +155,13 @@ static inline void gpio_output(uint16_t pin) {
GPIO_PULL_NONE, 0);
}
struct syscfg {
volatile uint32_t RESERVED1, PMCR, EXTICR[4], CFGR, RESERVED2, CCCSR, CCVR,
CCCR, PWRCR, RESERVED3[61], PKGR, RESERVED4[118], UR0, UR1, UR2, UR3, UR4,
UR5, UR6, UR7, UR8, UR9, UR10, UR11, UR12, UR13, UR14, UR15, UR16, UR17;
};
#define SYSCFG ((struct syscfg *) (0x40000000UL + 0x18000000UL + 0x0400UL))
struct uart {
volatile uint32_t CR1, CR2, CR3, BRR, GTPR, RTOR, RQR, ISR, ICR, RDR, TDR,
PRESC;
@ -106,6 +171,10 @@ struct uart {
#define UART3 ((struct uart *) 0x40004800)
#define UART_DEBUG UART1
// D2 Kernel clock (8.7.21) USART1 defaults to pclk2 (APB2), while USART2,3
// default to pclk1 (APB1). Even if using other kernel clocks, the APBx clocks
// must be enabled for CPU access, as the kernel clock drives the BRR, not the
// APB bus interface
static inline void uart_init(struct uart *uart, unsigned long baud) {
uint8_t af = 7; // Alternate function
uint16_t rx = 0, tx = 0; // pins
@ -119,7 +188,10 @@ static inline void uart_init(struct uart *uart, unsigned long baud) {
if (uart == UART2) tx = PIN('A', 2), rx = PIN('A', 3);
if (uart == UART3) tx = PIN('D', 8), rx = PIN('D', 9);
freq = SYS_FREQUENCY;
#if 0 // CONSTANT BAUD RATE FOR REMOTE DEBUGGING WHILE SETTING THE PLL
SETBITS(RCC->D2CCIP2R, 7 << 3, 3 << 3); // use HSI for UART1
freq = 64000000;
#endif
gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
@ -141,6 +213,66 @@ static inline uint8_t uart_read_byte(struct uart *uart) {
return (uint8_t) (uart->RDR & 255);
}
static inline void clock_init(void) {
// TODO: Enable FPU, set flash latency, set clock
struct dbgmcu {
volatile uint32_t IDCODE, CR, RESERVED4, APB3FZ1, RESERVED5, APB1LFZ1,
RESERVED6, APB1HFZ1, RESERVED7, APB2FZ1, RESERVED8, APB4FZ1;
};
#define DBGMCU ((struct dbgmcu *) 0x5C001000UL)
static inline char chiprev(void) {
uint16_t rev = (uint16_t)(((uint32_t) DBGMCU->IDCODE) >> 16);
if (rev == 0x1003) return 'Y';
if (rev == 0x2003) return 'V';
return '?';
}
static inline unsigned int div2prescval(unsigned int div) {
// 0 --> /1; 8 --> /2 ... 11 --> /16; 12 --> /64 ... 15 --> /512
if (div == 1) return 0;
if (div > 16) div /= 2;
unsigned int val = 7;
while (div >>= 1) ++val;
return val;
}
static inline unsigned int pllrge(unsigned int f) {
unsigned int val = 0;
while (f >>= 1) ++val;
return val - 1;
}
static inline void clock_init(void) {
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
asm("DSB");
asm("ISB");
PWR->CR3 |= BIT(1); // select LDO (reset value)
while ((PWR->CSR1 && BIT(13)) == 0) spin(1); // ACTVOSRDY
PWR->D3CR |= BIT(15) | BIT(14); // Select VOS1
uint32_t f = PWR->D3CR; // fake read to wait for bus clocking
while ((PWR->CSR1 && BIT(13)) == 0) spin(1); // ACTVOSRDY
SYSCFG->PWRCR |= BIT(0); // ODEN
f = SYSCFG->PWRCR;
while ((PWR->CSR1 && BIT(13)) == 0) spin(1); // ACTVOSRDY
(void) f;
SETBITS(
RCC->D1CFGR, (0x0F << 8) | (7 << 4) | (0x0F << 0),
(div2prescval(D1CPRE) << 8) | (D1PPRE << 4) | (div2prescval(HPRE) << 0));
RCC->D2CFGR = (D2PPRE2 << 8) | (D2PPRE1 << 4);
RCC->D3CFGR = (D3PPRE << 4);
SETBITS(RCC->PLLCFGR, 3 << 2,
pllrge(PLL1_HSI / PLL1_M)
<< 2); // keep reset config (DIVP1EN, !PLL1VCOSEL), PLL1RGE
SETBITS(RCC->PLL1DIVR, (0x7F << 9) | (0x1FF << 0),
((PLL1_P - 1) << 9) | ((PLL1_N - 1) << 0)); // Set PLL1_P PLL1_N
SETBITS(RCC->PLLCKSELR, 0x3F << 4,
PLL1_M << 4); // Set PLL1_M (source defaults to HSI)
RCC->CR |= BIT(24); // Enable PLL1
while ((RCC->CR & BIT(25)) == 0) spin(1); // Wait until done
RCC->CFGR |= (3 << 0); // Set clock source to PLL1
while ((RCC->CFGR & (7 << 3)) != (3 << 3)) spin(1); // Wait until done
FLASH->ACR |= FLASH_LATENCY; // default is larger
#if 0
// Enable SRAM block if you want to use it for ETH buffer (needs proper attributes in driver code)
// RCC->AHB2ENR |= BIT(29) | BIT(30) | BIT(31);
#endif
}

View File

@ -30,6 +30,146 @@ WEAK_ALIAS void DebugMon_Handler(void);
WEAK_ALIAS void PendSV_Handler(void);
WEAK_ALIAS void SysTick_Handler(void);
WEAK_ALIAS void WWDG_IRQHandler(void);
WEAK_ALIAS void PVD_AVD_IRQHandler(void);
WEAK_ALIAS void TAMP_STAMP_IRQHandler(void);
WEAK_ALIAS void RTC_WKUP_IRQHandler(void);
WEAK_ALIAS void FLASH_IRQHandler(void);
WEAK_ALIAS void RCC_IRQHandler(void);
WEAK_ALIAS void EXTI0_IRQHandler(void);
WEAK_ALIAS void EXTI1_IRQHandler(void);
WEAK_ALIAS void EXTI2_IRQHandler(void);
WEAK_ALIAS void EXTI3_IRQHandler(void);
WEAK_ALIAS void EXTI4_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream0_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream1_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream2_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream3_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream4_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream5_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream6_IRQHandler(void);
WEAK_ALIAS void ADC_IRQHandler(void);
WEAK_ALIAS void FDCAN1_IT0_IRQHandler(void);
WEAK_ALIAS void FDCAN2_IT0_IRQHandler(void);
WEAK_ALIAS void FDCAN1_IT1_IRQHandler(void);
WEAK_ALIAS void FDCAN2_IT1_IRQHandler(void);
WEAK_ALIAS void EXTI9_5_IRQHandler(void);
WEAK_ALIAS void TIM1_BRK_IRQHandler(void);
WEAK_ALIAS void TIM1_UP_IRQHandler(void);
WEAK_ALIAS void TIM1_TRG_COM_IRQHandler(void);
WEAK_ALIAS void TIM1_CC_IRQHandler(void);
WEAK_ALIAS void TIM2_IRQHandler(void);
WEAK_ALIAS void TIM3_IRQHandler(void);
WEAK_ALIAS void TIM4_IRQHandler(void);
WEAK_ALIAS void I2C1_EV_IRQHandler(void);
WEAK_ALIAS void I2C1_ER_IRQHandler(void);
WEAK_ALIAS void I2C2_EV_IRQHandler(void);
WEAK_ALIAS void I2C2_ER_IRQHandler(void);
WEAK_ALIAS void SPI1_IRQHandler(void);
WEAK_ALIAS void SPI2_IRQHandler(void);
WEAK_ALIAS void USART1_IRQHandler(void);
WEAK_ALIAS void USART2_IRQHandler(void);
WEAK_ALIAS void USART3_IRQHandler(void);
WEAK_ALIAS void EXTI15_10_IRQHandler(void);
WEAK_ALIAS void RTC_Alarm_IRQHandler(void);
WEAK_ALIAS void TIM8_BRK_TIM12_IRQHandler(void);
WEAK_ALIAS void TIM8_UP_TIM13_IRQHandler(void);
WEAK_ALIAS void TIM8_TRG_COM_TIM14_IRQHandler(void);
WEAK_ALIAS void TIM8_CC_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream7_IRQHandler(void);
WEAK_ALIAS void FMC_IRQHandler(void);
WEAK_ALIAS void SDMMC1_IRQHandler(void);
WEAK_ALIAS void TIM5_IRQHandler(void);
WEAK_ALIAS void SPI3_IRQHandler(void);
WEAK_ALIAS void UART4_IRQHandler(void);
WEAK_ALIAS void UART5_IRQHandler(void);
WEAK_ALIAS void TIM6_DAC_IRQHandler(void);
WEAK_ALIAS void TIM7_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream0_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream1_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream2_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream3_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream4_IRQHandler(void);
WEAK_ALIAS void ETH_IRQHandler(void);
WEAK_ALIAS void ETH_WKUP_IRQHandler(void);
WEAK_ALIAS void FDCAN_CAL_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream5_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream6_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream7_IRQHandler(void);
WEAK_ALIAS void USART6_IRQHandler(void);
WEAK_ALIAS void I2C3_EV_IRQHandler(void);
WEAK_ALIAS void I2C3_ER_IRQHandler(void);
WEAK_ALIAS void OTG_HS_EP1_OUT_IRQHandler(void);
WEAK_ALIAS void OTG_HS_EP1_IN_IRQHandler(void);
WEAK_ALIAS void OTG_HS_WKUP_IRQHandler(void);
WEAK_ALIAS void OTG_HS_IRQHandler(void);
WEAK_ALIAS void DCMI_IRQHandler(void);
WEAK_ALIAS void RNG_IRQHandler(void);
WEAK_ALIAS void FPU_IRQHandler(void);
WEAK_ALIAS void UART7_IRQHandler(void);
WEAK_ALIAS void UART8_IRQHandler(void);
WEAK_ALIAS void SPI4_IRQHandler(void);
WEAK_ALIAS void SPI5_IRQHandler(void);
WEAK_ALIAS void SPI6_IRQHandler(void);
WEAK_ALIAS void SAI1_IRQHandler(void);
WEAK_ALIAS void LTDC_IRQHandler(void);
WEAK_ALIAS void LTDC_ER_IRQHandler(void);
WEAK_ALIAS void DMA2D_IRQHandler(void);
WEAK_ALIAS void SAI2_IRQHandler(void);
WEAK_ALIAS void QUADSPI_IRQHandler(void);
WEAK_ALIAS void LPTIM1_IRQHandler(void);
WEAK_ALIAS void CEC_IRQHandler(void);
WEAK_ALIAS void I2C4_EV_IRQHandler(void);
WEAK_ALIAS void I2C4_ER_IRQHandler(void);
WEAK_ALIAS void SPDIF_RX_IRQHandler(void);
WEAK_ALIAS void OTG_FS_EP1_OUT_IRQHandler(void);
WEAK_ALIAS void OTG_FS_EP1_IN_IRQHandler(void);
WEAK_ALIAS void OTG_FS_WKUP_IRQHandler(void);
WEAK_ALIAS void OTG_FS_IRQHandler(void);
WEAK_ALIAS void DMAMUX1_OVR_IRQHandler(void);
WEAK_ALIAS void HRTIM1_Master_IRQHandler(void);
WEAK_ALIAS void HRTIM1_TIMA_IRQHandler(void);
WEAK_ALIAS void HRTIM1_TIMB_IRQHandler(void);
WEAK_ALIAS void HRTIM1_TIMC_IRQHandler(void);
WEAK_ALIAS void HRTIM1_TIMD_IRQHandler(void);
WEAK_ALIAS void HRTIM1_TIME_IRQHandler(void);
WEAK_ALIAS void HRTIM1_FLT_IRQHandler(void);
WEAK_ALIAS void DFSDM1_FLT0_IRQHandler(void);
WEAK_ALIAS void DFSDM1_FLT1_IRQHandler(void);
WEAK_ALIAS void DFSDM1_FLT2_IRQHandler(void);
WEAK_ALIAS void DFSDM1_FLT3_IRQHandler(void);
WEAK_ALIAS void SAI3_IRQHandler(void);
WEAK_ALIAS void SWPMI1_IRQHandler(void);
WEAK_ALIAS void TIM15_IRQHandler(void);
WEAK_ALIAS void TIM16_IRQHandler(void);
WEAK_ALIAS void TIM17_IRQHandler(void);
WEAK_ALIAS void MDIOS_WKUP_IRQHandler(void);
WEAK_ALIAS void MDIOS_IRQHandler(void);
WEAK_ALIAS void JPEG_IRQHandler(void);
WEAK_ALIAS void MDMA_IRQHandler(void);
WEAK_ALIAS void SDMMC2_IRQHandler(void);
WEAK_ALIAS void HSEM1_IRQHandler(void);
WEAK_ALIAS void ADC3_IRQHandler(void);
WEAK_ALIAS void DMAMUX2_OVR_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel0_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel1_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel2_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel3_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel4_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel5_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel6_IRQHandler(void);
WEAK_ALIAS void BDMA_Channel7_IRQHandler(void);
WEAK_ALIAS void COMP1_IRQHandler(void);
WEAK_ALIAS void LPTIM2_IRQHandler(void);
WEAK_ALIAS void LPTIM3_IRQHandler(void);
WEAK_ALIAS void LPTIM4_IRQHandler(void);
WEAK_ALIAS void LPTIM5_IRQHandler(void);
WEAK_ALIAS void LPUART1_IRQHandler(void);
WEAK_ALIAS void CRS_IRQHandler(void);
WEAK_ALIAS void ECC_IRQHandler(void);
WEAK_ALIAS void SAI4_IRQHandler(void);
WEAK_ALIAS void WAKEUP_PIN_IRQHandler(void);
// IRQ table
extern void _estack();
__attribute__((section(".vectors"))) void (*tab[16 + 150])(void) = {
@ -39,4 +179,48 @@ __attribute__((section(".vectors"))) void (*tab[16 + 150])(void) = {
DebugMon_Handler, 0, PendSV_Handler, SysTick_Handler,
// Interrupts from peripherals
DefaultIRQHandler};
WWDG_IRQHandler, PVD_AVD_IRQHandler, TAMP_STAMP_IRQHandler,
RTC_WKUP_IRQHandler, FLASH_IRQHandler, RCC_IRQHandler, EXTI0_IRQHandler,
EXTI1_IRQHandler, EXTI2_IRQHandler, EXTI3_IRQHandler, EXTI4_IRQHandler,
DMA1_Stream0_IRQHandler, DMA1_Stream1_IRQHandler, DMA1_Stream2_IRQHandler,
DMA1_Stream3_IRQHandler, DMA1_Stream4_IRQHandler, DMA1_Stream5_IRQHandler,
DMA1_Stream6_IRQHandler, ADC_IRQHandler, FDCAN1_IT0_IRQHandler,
FDCAN2_IT0_IRQHandler, FDCAN1_IT1_IRQHandler, FDCAN2_IT1_IRQHandler,
EXTI9_5_IRQHandler, TIM1_BRK_IRQHandler, TIM1_UP_IRQHandler,
TIM1_TRG_COM_IRQHandler, TIM1_CC_IRQHandler, TIM2_IRQHandler,
TIM3_IRQHandler, TIM4_IRQHandler, I2C1_EV_IRQHandler, I2C1_ER_IRQHandler,
I2C2_EV_IRQHandler, I2C2_ER_IRQHandler, SPI1_IRQHandler, SPI2_IRQHandler,
USART1_IRQHandler, USART2_IRQHandler, USART3_IRQHandler,
EXTI15_10_IRQHandler, RTC_Alarm_IRQHandler, 0, TIM8_BRK_TIM12_IRQHandler,
TIM8_UP_TIM13_IRQHandler, TIM8_TRG_COM_TIM14_IRQHandler, TIM8_CC_IRQHandler,
DMA1_Stream7_IRQHandler, FMC_IRQHandler, SDMMC1_IRQHandler, TIM5_IRQHandler,
SPI3_IRQHandler, UART4_IRQHandler, UART5_IRQHandler, TIM6_DAC_IRQHandler,
TIM7_IRQHandler, DMA2_Stream0_IRQHandler, DMA2_Stream1_IRQHandler,
DMA2_Stream2_IRQHandler, DMA2_Stream3_IRQHandler, DMA2_Stream4_IRQHandler,
ETH_IRQHandler, ETH_WKUP_IRQHandler, FDCAN_CAL_IRQHandler, 0, 0, 0, 0,
DMA2_Stream5_IRQHandler, DMA2_Stream6_IRQHandler, DMA2_Stream7_IRQHandler,
USART6_IRQHandler, I2C3_EV_IRQHandler, I2C3_ER_IRQHandler,
OTG_HS_EP1_OUT_IRQHandler, OTG_HS_EP1_IN_IRQHandler, OTG_HS_WKUP_IRQHandler,
OTG_HS_IRQHandler, DCMI_IRQHandler, 0, RNG_IRQHandler, FPU_IRQHandler,
UART7_IRQHandler, UART8_IRQHandler, SPI4_IRQHandler, SPI5_IRQHandler,
SPI6_IRQHandler, SAI1_IRQHandler, LTDC_IRQHandler, LTDC_ER_IRQHandler,
DMA2D_IRQHandler, SAI2_IRQHandler, QUADSPI_IRQHandler, LPTIM1_IRQHandler,
CEC_IRQHandler, I2C4_EV_IRQHandler, I2C4_ER_IRQHandler, SPDIF_RX_IRQHandler,
OTG_FS_EP1_OUT_IRQHandler, OTG_FS_EP1_IN_IRQHandler, OTG_FS_WKUP_IRQHandler,
OTG_FS_IRQHandler, DMAMUX1_OVR_IRQHandler, HRTIM1_Master_IRQHandler,
HRTIM1_TIMA_IRQHandler, HRTIM1_TIMB_IRQHandler, HRTIM1_TIMC_IRQHandler,
HRTIM1_TIMD_IRQHandler, HRTIM1_TIME_IRQHandler, HRTIM1_FLT_IRQHandler,
DFSDM1_FLT0_IRQHandler, DFSDM1_FLT1_IRQHandler, DFSDM1_FLT2_IRQHandler,
DFSDM1_FLT3_IRQHandler, SAI3_IRQHandler, SWPMI1_IRQHandler,
TIM15_IRQHandler, TIM16_IRQHandler, TIM17_IRQHandler, MDIOS_WKUP_IRQHandler,
MDIOS_IRQHandler, JPEG_IRQHandler, MDMA_IRQHandler, 0, SDMMC2_IRQHandler,
HSEM1_IRQHandler, 0, ADC3_IRQHandler, DMAMUX2_OVR_IRQHandler,
BDMA_Channel0_IRQHandler, BDMA_Channel1_IRQHandler,
BDMA_Channel2_IRQHandler, BDMA_Channel3_IRQHandler,
BDMA_Channel4_IRQHandler, BDMA_Channel5_IRQHandler,
BDMA_Channel6_IRQHandler, BDMA_Channel7_IRQHandler, COMP1_IRQHandler,
LPTIM2_IRQHandler, LPTIM3_IRQHandler, LPTIM4_IRQHandler, LPTIM5_IRQHandler,
LPUART1_IRQHandler, 0, CRS_IRQHandler, ECC_IRQHandler, SAI4_IRQHandler, 0,
0, WAKEUP_PIN_IRQHandler
}
;

View File

@ -1,7 +1,6 @@
#include "mip.h"
#if MG_ENABLE_MIP && \
(!defined(MG_ENABLE_DRIVER_TM4C) || MG_ENABLE_DRIVER_TM4C == 0)
#if MG_ENABLE_MIP && MG_ENABLE_DRIVER_STM32
struct stm32_eth {
volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,

276
mip/driver_stm32h.c Normal file
View File

@ -0,0 +1,276 @@
#include "mip.h"
#if MG_ENABLE_MIP && defined(MG_ENABLE_DRIVER_STM32H) && MG_ENABLE_DRIVER_STM32H
struct stm32h_eth {
volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR,
MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR,
MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10,
MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR,
RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR,
MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR,
MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR,
RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR,
RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR,
MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R,
RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R,
RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R,
MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR,
MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR,
RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28,
MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR,
RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR,
MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR,
RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR,
MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR,
RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR,
DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR,
DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER,
DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR,
RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2],
DMACMFCR;
};
#undef ETH
#define ETH \
((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
#undef BIT
#define BIT(x) ((uint32_t) 1 << (x))
#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)
static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static struct mip_if *s_ifp; // MIP interface
enum {
PHY_ADDR = 0,
PHY_BCR = 0,
PHY_BSR = 1,
PHY_CSCR = 31
}; // PHY constants
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMDIOAR &= (0xF << 8);
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2;
ETH->MACMDIOAR |= BIT(0);
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
return ETH->MACMDIODR;
}
static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
ETH->MACMDIODR = val;
ETH->MACMDIOAR &= (0xF << 8);
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
ETH->MACMDIOAR |= BIT(0);
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
}
static uint32_t get_hclk(void) {
struct rcc {
volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR,
D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR,
PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R,
D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR,
RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR,
APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9],
RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR,
APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR,
AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR,
RESERVED13[4];
} *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400));
uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */,
csi = 4000000 /* 4MHz */;
unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3;
if (sel == 1) {
clk = csi;
} else if (sel == 2) {
clk = hse;
} else if (sel == 3) {
uint32_t vco, m, n, p;
unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0;
m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4);
n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 +
((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode
p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1;
if (src == 1) {
clk = csi;
} else if (src == 2) {
clk = hse;
} else {
clk = hsi;
clk >>= ((rcc->CR & 3) >> 3);
}
vco = (uint32_t) ((uint64_t) clk * n / m);
clk = vco / p;
} else {
clk = hsi;
clk >>= ((rcc->CR & 3) >> 3);
}
const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8;
if (d1cpre >= 8) clk >>= cptab[d1cpre - 8];
MG_DEBUG(("D1 CLK: %u", clk));
uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0;
if (hpre < 8) return clk;
return ((uint32_t) clk) >> cptab[hpre - 8];
}
// Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral
// clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can
// be derived from HSI or CSI (internal RC) clocks, and those can go above
// specs, the datasheets specify a range of frequencies and activate one of a
// series of dividers to keep the MDC clock safely below 2.5MHz. We guess a
// divider setting based on HCLK with some drift. If the user uses a different
// clock from our defaults, needs to set the macros on top. Valid for
// STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 %
// worst case drift @ max temp)
static int guess_mdc_cr(void) {
const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values
const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
uint32_t hclk = get_hclk(); // Guess system HCLK
int result = -1; // Invalid CR value
for (int i = 0; i < 6; i++) {
if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
result = crs[i];
break;
}
}
if (result < 0) MG_ERROR(("HCLK too high"));
MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
return result;
}
static bool mip_driver_stm32h_init(struct mip_if *ifp) {
struct mip_driver_stm32h_data *d =
(struct mip_driver_stm32h_data *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
}
// Init TX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
}
ETH->DMAMR |= BIT(0); // Software reset
while ((ETH->DMAMR & BIT(0)) != 0) (void) 0; // Wait until done
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
// NOTE(scaprile): We do not use timing facilities so the DMA engine does not
// re-write buffer address
ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value)
ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed
ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
ETH->MACTFCR = BIT(7); // Disable zero-quanta pause
// ETH->MACPFR = BIT(31); // Receive all
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
ETH->DMACRDLAR =
(uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address
ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length
ETH->DMACRDTPR =
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT -
1]; // last valid descriptor address
ETH->DMACTDLAR =
(uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address
ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
ETH->DMACTDTPR =
(uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE
ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) |
BIT(15); // RE, TE, Duplex, Fast, Reserved
ETH->MTLTQOMR |= BIT(1); // TSF
ETH->MTLRQOMR |= BIT(5); // RSF
ETH->DMACTCR |= BIT(0); // ST
ETH->DMACRCR |= BIT(0); // SR
// MAC address filtering
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
if (ifp->queue.len == 0) ifp->queue.len = 8192;
return true;
}
static uint32_t s_txno;
static size_t mip_driver_stm32h_tx(const void *buf, size_t len,
struct mip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big
} else if ((s_txdesc[s_txno][3] & BIT(31))) {
MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
len = 0; // All descriptors are busy, fail
} else {
memcpy(s_txbuf[s_txno], buf, len); // Copy data
s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD
s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS
ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
return len;
(void) ifp;
}
static bool mip_driver_stm32h_up(struct mip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
if ((scsr & BIT(4)) == 0) maccr &= ~BIT(13); // Half-duplex
ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
maccr & BIT(13) ? "full" : "half"));
}
return up;
}
void ETH_IRQHandler(void);
static uint32_t s_rxno;
void ETH_IRQHandler(void) {
qp_mark(QP_IRQTRIGGERED, 0);
if (ETH->DMACSR & BIT(6)) { // Frame received, loop
ETH->DMACSR = BIT(15) | BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
if (s_rxdesc[s_rxno][3] & BIT(31)) break; // exit when done
if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) ==
(BIT(28) | BIT(29))) &&
!(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames
uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1);
// MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
// ETH->DMACSR));
mip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
}
s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
}
ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing
ETH->DMACRDTPR =
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
}
struct mip_driver mip_driver_stm32h = {mip_driver_stm32h_init,
mip_driver_stm32h_tx, mip_driver_rx,
mip_driver_stm32h_up};
#endif

16
mip/driver_stm32h.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
struct mip_driver_stm32h_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
// HCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz HCLK/42 0
// 100-150 MHz HCLK/62 1
// 20-35 MHz HCLK/16 2
// 35-60 MHz HCLK/26 3
// 150-250 MHz HCLK/102 4 <-- value for Nucleo-H* on max speed driven by HSI
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on max speed driven by CSI
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
};

View File

@ -60,6 +60,7 @@ size_t mip_driver_rx(void *buf, size_t len, struct mip_if *ifp);
extern struct mip_driver mip_driver_stm32;
extern struct mip_driver mip_driver_w5500;
extern struct mip_driver mip_driver_tm4c;
extern struct mip_driver mip_driver_stm32h;
// Drivers that require SPI, can use this SPI abstraction
struct mip_spi {
@ -69,6 +70,14 @@ struct mip_spi {
uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply
};
#if MG_ENABLE_MIP
#if !defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_TM4C)
#define MG_ENABLE_DRIVER_STM32 1
#else
#define MG_ENABLE_DRIVER_STM32 0
#endif
#endif
#ifdef MIP_QPROFILE
enum {
QP_IRQTRIGGERED = 0, // payload is number of interrupts so far

View File

@ -5939,8 +5939,7 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
#endif
#if MG_ENABLE_MIP && \
(!defined(MG_ENABLE_DRIVER_TM4C) || MG_ENABLE_DRIVER_TM4C == 0)
#if MG_ENABLE_MIP && MG_ENABLE_DRIVER_STM32
struct stm32_eth {
volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
@ -6162,6 +6161,286 @@ struct mip_driver mip_driver_stm32 = {mip_driver_stm32_init,
mip_driver_stm32_up};
#endif
#ifdef MG_ENABLE_LINES
#line 1 "mip/driver_stm32h.c"
#endif
#if MG_ENABLE_MIP && defined(MG_ENABLE_DRIVER_STM32H) && MG_ENABLE_DRIVER_STM32H
struct stm32h_eth {
volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR,
MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR,
MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10,
MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR,
RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR,
MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR,
MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR,
RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR,
RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR,
MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R,
RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R,
RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R,
MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR,
MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR,
RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28,
MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR,
RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR,
MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR,
RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR,
MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR,
RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR,
DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR,
DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER,
DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR,
RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2],
DMACMFCR;
};
#undef ETH
#define ETH \
((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
#undef BIT
#define BIT(x) ((uint32_t) 1 << (x))
#define ETH_PKT_SIZE 1540 // Max frame size
#define ETH_DESC_CNT 4 // Descriptors count
#define ETH_DS 4 // Descriptor size (words)
static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static struct mip_if *s_ifp; // MIP interface
enum {
PHY_ADDR = 0,
PHY_BCR = 0,
PHY_BSR = 1,
PHY_CSCR = 31
}; // PHY constants
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMDIOAR &= (0xF << 8);
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2;
ETH->MACMDIOAR |= BIT(0);
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
return ETH->MACMDIODR;
}
static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
ETH->MACMDIODR = val;
ETH->MACMDIOAR &= (0xF << 8);
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
ETH->MACMDIOAR |= BIT(0);
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
}
static uint32_t get_hclk(void) {
struct rcc {
volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR,
D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR,
PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R,
D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR,
RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR,
APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9],
RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR,
APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR,
AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR,
RESERVED13[4];
} *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400));
uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */,
csi = 4000000 /* 4MHz */;
unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3;
if (sel == 1) {
clk = csi;
} else if (sel == 2) {
clk = hse;
} else if (sel == 3) {
uint32_t vco, m, n, p;
unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0;
m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4);
n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 +
((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode
p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1;
if (src == 1) {
clk = csi;
} else if (src == 2) {
clk = hse;
} else {
clk = hsi;
clk >>= ((rcc->CR & 3) >> 3);
}
vco = (uint32_t) ((uint64_t) clk * n / m);
clk = vco / p;
} else {
clk = hsi;
clk >>= ((rcc->CR & 3) >> 3);
}
const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8;
if (d1cpre >= 8) clk >>= cptab[d1cpre - 8];
MG_DEBUG(("D1 CLK: %u", clk));
uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0;
if (hpre < 8) return clk;
return ((uint32_t) clk) >> cptab[hpre - 8];
}
// Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral
// clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can
// be derived from HSI or CSI (internal RC) clocks, and those can go above
// specs, the datasheets specify a range of frequencies and activate one of a
// series of dividers to keep the MDC clock safely below 2.5MHz. We guess a
// divider setting based on HCLK with some drift. If the user uses a different
// clock from our defaults, needs to set the macros on top. Valid for
// STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 %
// worst case drift @ max temp)
static int guess_mdc_cr(void) {
const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values
const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
uint32_t hclk = get_hclk(); // Guess system HCLK
int result = -1; // Invalid CR value
for (int i = 0; i < 6; i++) {
if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
result = crs[i];
break;
}
}
if (result < 0) MG_ERROR(("HCLK too high"));
MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
return result;
}
static bool mip_driver_stm32h_init(struct mip_if *ifp) {
struct mip_driver_stm32h_data *d =
(struct mip_driver_stm32h_data *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
}
// Init TX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
}
ETH->DMAMR |= BIT(0); // Software reset
while ((ETH->DMAMR & BIT(0)) != 0) (void) 0; // Wait until done
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
// NOTE(scaprile): We do not use timing facilities so the DMA engine does not
// re-write buffer address
ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value)
ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed
ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
ETH->MACTFCR = BIT(7); // Disable zero-quanta pause
// ETH->MACPFR = BIT(31); // Receive all
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
ETH->DMACRDLAR =
(uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address
ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length
ETH->DMACRDTPR =
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT -
1]; // last valid descriptor address
ETH->DMACTDLAR =
(uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address
ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
ETH->DMACTDTPR =
(uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE
ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) |
BIT(15); // RE, TE, Duplex, Fast, Reserved
ETH->MTLTQOMR |= BIT(1); // TSF
ETH->MTLRQOMR |= BIT(5); // RSF
ETH->DMACTCR |= BIT(0); // ST
ETH->DMACRCR |= BIT(0); // SR
// MAC address filtering
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
if (ifp->queue.len == 0) ifp->queue.len = 8192;
return true;
}
static uint32_t s_txno;
static size_t mip_driver_stm32h_tx(const void *buf, size_t len,
struct mip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big
} else if ((s_txdesc[s_txno][3] & BIT(31))) {
MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
len = 0; // All descriptors are busy, fail
} else {
memcpy(s_txbuf[s_txno], buf, len); // Copy data
s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD
s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
}
ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS
ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
return len;
(void) ifp;
}
static bool mip_driver_stm32h_up(struct mip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
bool up = bsr & BIT(2) ? 1 : 0;
if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
if ((scsr & BIT(4)) == 0) maccr &= ~BIT(13); // Half-duplex
ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
maccr & BIT(13) ? "full" : "half"));
}
return up;
}
void ETH_IRQHandler(void);
static uint32_t s_rxno;
void ETH_IRQHandler(void) {
qp_mark(QP_IRQTRIGGERED, 0);
if (ETH->DMACSR & BIT(6)) { // Frame received, loop
ETH->DMACSR = BIT(15) | BIT(6); // Clear flag
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
if (s_rxdesc[s_rxno][3] & BIT(31)) break; // exit when done
if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) ==
(BIT(28) | BIT(29))) &&
!(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames
uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1);
// MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
// ETH->DMACSR));
mip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
}
s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
}
}
ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing
ETH->DMACRDTPR =
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
}
struct mip_driver mip_driver_stm32h = {mip_driver_stm32h_init,
mip_driver_stm32h_tx, mip_driver_rx,
mip_driver_stm32h_up};
#endif
#ifdef MG_ENABLE_LINES
#line 1 "mip/driver_tm4c.c"
#endif

View File

@ -1509,6 +1509,7 @@ size_t mip_driver_rx(void *buf, size_t len, struct mip_if *ifp);
extern struct mip_driver mip_driver_stm32;
extern struct mip_driver mip_driver_w5500;
extern struct mip_driver mip_driver_tm4c;
extern struct mip_driver mip_driver_stm32h;
// Drivers that require SPI, can use this SPI abstraction
struct mip_spi {
@ -1518,6 +1519,14 @@ struct mip_spi {
uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply
};
#if MG_ENABLE_MIP
#if !defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_TM4C)
#define MG_ENABLE_DRIVER_STM32 1
#else
#define MG_ENABLE_DRIVER_STM32 0
#endif
#endif
#ifdef MIP_QPROFILE
enum {
QP_IRQTRIGGERED = 0, // payload is number of interrupts so far
@ -1552,6 +1561,22 @@ struct mip_driver_stm32_data {
};
struct mip_driver_stm32h_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
// HCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz HCLK/42 0
// 100-150 MHz HCLK/62 1
// 20-35 MHz HCLK/16 2
// 35-60 MHz HCLK/26 3
// 150-250 MHz HCLK/102 4 <-- value for Nucleo-H* on max speed driven by HSI
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on max speed driven by CSI
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
};
struct mip_driver_tm4c_data {
// MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz
// SYSCLK range DIVIDER mdc_cr VALUE