Merge pull request #1727 from cesanta/mdc

Pass MDC clock divider explicitly rather than guess
This commit is contained in:
Sergio R. Caprile 2022-09-12 20:43:00 -03:00 committed by GitHub
commit 30acef2452
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 156 deletions

View File

@ -161,7 +161,7 @@ mongoose.c: Makefile $(wildcard src/*) $(wildcard mip/*.c)
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c mip/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
mongoose.h: $(HDRS) Makefile
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/config.h src/str.h src/fmt.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h mip/mip.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/config.h src/str.h src/fmt.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h mip/mip.h mip/driver_*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
clean:
rm -rf $(PROG) *.exe *.o *.dSYM *_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack unpacked

View File

@ -70,8 +70,9 @@ int main(void) {
// Initialise Mongoose network stack
// Specify MAC address, and use 0 for IP, mask, GW - i.e. use DHCP
// For static configuration, specify IP/mask/GW in network byte order
struct mip_cfg c = {.mac = {0, 0, 1, 2, 3, 4}, .ip = 0, .mask = 0, .gw = 0};
mip_init(&mgr, &c, &mip_driver_stm32, NULL);
struct mip_cfg c = {.mac = {0, 0, 1, 2, 3, 5}, .ip = 0, .mask = 0, .gw = 0};
struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h
mip_init(&mgr, &c, &mip_driver_stm32, &driver_data);
MG_INFO(("Init done, starting main loop"));
extern void device_dashboard_fn(struct mg_connection *, int, void *, void *);

View File

@ -71,7 +71,8 @@ int main(void) {
// Specify MAC address, and use 0 for IP, mask, GW - i.e. use DHCP
// For static configuration, specify IP/mask/GW in network byte order
struct mip_cfg c = {.mac = {0, 0, 1, 2, 3, 4}, .ip = 0, .mask = 0, .gw = 0};
mip_init(&mgr, &c, &mip_driver_stm32, NULL);
struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h
mip_init(&mgr, &c, &mip_driver_stm32, &driver_data);
MG_INFO(("Init done, starting main loop"));
extern void device_dashboard_fn(struct mg_connection *, int, void *, void *);

View File

@ -1,17 +1,6 @@
#include "mip.h"
#if MG_ENABLE_MIP && defined(__arm__)
// define to your own clock if using external clocking
#if !defined(MG_STM32_CLK_HSE)
#define MG_STM32_CLK_HSE 8000000UL
#endif
// define to your chip internal clock if different
#if !defined(MG_STM32_CLK_HSI)
#define MG_STM32_CLK_HSI 16000000UL
#endif
struct stm32_eth {
volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
@ -44,9 +33,6 @@ static inline void spin(volatile uint32_t count) {
while (count--) asm("nop");
}
static uint32_t hclk_get(void);
static uint8_t cr_guess(uint32_t hclk);
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMIIAR &= (7 << 2);
ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
@ -63,7 +49,62 @@ static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
while (ETH->MACMIIAR & BIT(0)) spin(1);
}
static uint32_t get_hclk(void) {
struct rcc {
volatile uint32_t CR, PLLCFGR, CFGR;
} *RCC = (struct rcc *) 0x40023800;
uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
if (RCC->CFGR & (1 << 2)) {
clk = hse;
} else if (RCC->CFGR & (1 << 3)) {
uint32_t vco, m, n, p;
m = (RCC->PLLCFGR & (0x3f << 0)) >> 0;
n = (RCC->PLLCFGR & (0x1ff << 6)) >> 6;
p = (((RCC->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
clk = (RCC->PLLCFGR & (1 << 22)) ? hse : hsi;
vco = (uint32_t) ((uint64_t) clk * n / m);
clk = vco / p;
} else {
clk = hsi;
}
int hpre = (RCC->CFGR & (0x0F << 4)) >> 4;
if (hpre < 8) return clk;
uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
return ((uint32_t) clk) >> ahbptab[hpre - 8];
}
// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
// from the HSI (internal RC), and it 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 a +5% drift. If the user uses a different clock from our
// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
static int guess_mdc_cr(void) {
uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
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
if (hclk < 25000000) {
MG_ERROR(("HCLK too low"));
} else {
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_stm32_init(uint8_t *mac, void *userdata) {
struct mip_driver_stm32 *d = (struct mip_driver_stm32 *) userdata;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own
@ -82,11 +123,15 @@ static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) {
ETH->DMABMR |= BIT(0); // Software reset
while ((ETH->DMABMR & BIT(0)) != 0) spin(1); // 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->MACMIIAR = (cr & 3) << 2;
// NOTE(cpq): we do not use extended descriptor bit 7, and do not use
// hardware checksum. Therefore, descriptor size is 4, not 8
// ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT
ETH->MACMIIAR = cr_guess(hclk_get()) << 2; // MDC clock
ETH->MACFCR = BIT(7); // Disable zero quarta pause
ETH->MACFFR = BIT(31); // Receive all
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
@ -101,7 +146,6 @@ static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) {
ETH->MACA0HR = ((uint32_t) mac[5] << 8U) | mac[4];
ETH->MACA0LR = (uint32_t) (mac[3] << 24) | ((uint32_t) mac[2] << 16) |
((uint32_t) mac[1] << 8) | mac[0];
(void) userdata;
return true;
}
@ -161,63 +205,4 @@ struct mip_driver mip_driver_stm32 = {.init = mip_driver_stm32_init,
.tx = mip_driver_stm32_tx,
.setrx = mip_driver_stm32_setrx,
.up = mip_driver_stm32_up};
// Calculate HCLK from clock settings,
// valid for STM32F74xxx/75xxx (5.3) and STM32F42xxx/43xxx (6.3)
static const uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
struct rcc {
volatile uint32_t CR, PLLCFGR, CFGR;
};
#define RCC ((struct rcc *) 0x40023800)
static uint32_t hclk_get(void) {
uint32_t clk = 0;
if (RCC->CFGR & (1 << 2)) {
clk = MG_STM32_CLK_HSE;
} else if (RCC->CFGR & (1 << 3)) {
uint32_t vco, m, n, p;
m = (RCC->PLLCFGR & (0x3FUL << 0)) >> 0;
n = (RCC->PLLCFGR & (0x1FFUL << 6)) >> 6;
p = (((RCC->PLLCFGR & (0x03UL << 16)) >> 16) + 1) * 2;
if (RCC->PLLCFGR & (1UL << 22))
clk = MG_STM32_CLK_HSE;
else
clk = MG_STM32_CLK_HSI;
vco = (uint32_t) ((uint64_t) (((uint32_t) clk * (uint32_t) n)) /
((uint32_t) m));
clk = vco / p;
} else {
clk = MG_STM32_CLK_HSI;
}
int hpre = (RCC->CFGR & (0x0F << 4)) >> 4;
if (hpre < 8) return clk;
return ((uint32_t) clk) >> ahbptab[hpre - 8];
}
// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
// from the HSI (internal RC), and it 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 a +5% drift. If the user uses a different clock from our
// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
#define CRDTAB_LEN 6
static const uint8_t crdtab[CRDTAB_LEN][2] = {
// [{setting, div ratio},...]
{2, 16}, {3, 26}, {0, 42}, {1, 62}, {4, 102}, {5, 124},
};
static uint8_t cr_guess(uint32_t hclk) {
MG_DEBUG(("HCLK: %u", hclk));
if (hclk < 25000000) {
MG_ERROR(("HCLK too low"));
return CRDTAB_LEN;
}
for (int i = 0; i < CRDTAB_LEN; i++)
if (hclk / crdtab[i][1] <= 2375000UL) return crdtab[i][0]; // 2.5MHz - 5%
MG_ERROR(("HCLK too high"));
return CRDTAB_LEN;
}
#endif // MG_ENABLE_MIP
#endif

16
mip/driver_stm32.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
struct mip_driver_stm32 {
// 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-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed
// 216-310 MHz HCLK/124 5
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
};

View File

@ -5983,17 +5983,6 @@ struct mip_driver mip_driver_enc28j60 = {.init = mip_driver_enc28j60_init,
#if MG_ENABLE_MIP && defined(__arm__)
// define to your own clock if using external clocking
#if !defined(MG_STM32_CLK_HSE)
#define MG_STM32_CLK_HSE 8000000UL
#endif
// define to your chip internal clock if different
#if !defined(MG_STM32_CLK_HSI)
#define MG_STM32_CLK_HSI 16000000UL
#endif
struct stm32_eth {
volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
@ -6026,9 +6015,6 @@ static inline void spin(volatile uint32_t count) {
while (count--) asm("nop");
}
static uint32_t hclk_get(void);
static uint8_t cr_guess(uint32_t hclk);
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMIIAR &= (7 << 2);
ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
@ -6045,7 +6031,62 @@ static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
while (ETH->MACMIIAR & BIT(0)) spin(1);
}
static uint32_t get_hclk(void) {
struct rcc {
volatile uint32_t CR, PLLCFGR, CFGR;
} *RCC = (struct rcc *) 0x40023800;
uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
if (RCC->CFGR & (1 << 2)) {
clk = hse;
} else if (RCC->CFGR & (1 << 3)) {
uint32_t vco, m, n, p;
m = (RCC->PLLCFGR & (0x3f << 0)) >> 0;
n = (RCC->PLLCFGR & (0x1ff << 6)) >> 6;
p = (((RCC->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
clk = (RCC->PLLCFGR & (1 << 22)) ? hse : hsi;
vco = (uint32_t) ((uint64_t) clk * n / m);
clk = vco / p;
} else {
clk = hsi;
}
int hpre = (RCC->CFGR & (0x0F << 4)) >> 4;
if (hpre < 8) return clk;
uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
return ((uint32_t) clk) >> ahbptab[hpre - 8];
}
// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
// from the HSI (internal RC), and it 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 a +5% drift. If the user uses a different clock from our
// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
static int guess_mdc_cr(void) {
uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
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
if (hclk < 25000000) {
MG_ERROR(("HCLK too low"));
} else {
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_stm32_init(uint8_t *mac, void *userdata) {
struct mip_driver_stm32 *d = (struct mip_driver_stm32 *) userdata;
// Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own
@ -6064,11 +6105,15 @@ static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) {
ETH->DMABMR |= BIT(0); // Software reset
while ((ETH->DMABMR & BIT(0)) != 0) spin(1); // 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->MACMIIAR = (cr & 3) << 2;
// NOTE(cpq): we do not use extended descriptor bit 7, and do not use
// hardware checksum. Therefore, descriptor size is 4, not 8
// ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT
ETH->MACMIIAR = cr_guess(hclk_get()) << 2; // MDC clock
ETH->MACFCR = BIT(7); // Disable zero quarta pause
ETH->MACFFR = BIT(31); // Receive all
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
@ -6083,7 +6128,6 @@ static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) {
ETH->MACA0HR = ((uint32_t) mac[5] << 8U) | mac[4];
ETH->MACA0LR = (uint32_t) (mac[3] << 24) | ((uint32_t) mac[2] << 16) |
((uint32_t) mac[1] << 8) | mac[0];
(void) userdata;
return true;
}
@ -6143,66 +6187,7 @@ struct mip_driver mip_driver_stm32 = {.init = mip_driver_stm32_init,
.tx = mip_driver_stm32_tx,
.setrx = mip_driver_stm32_setrx,
.up = mip_driver_stm32_up};
// Calculate HCLK from clock settings,
// valid for STM32F74xxx/75xxx (5.3) and STM32F42xxx/43xxx (6.3)
static const uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
struct rcc {
volatile uint32_t CR, PLLCFGR, CFGR;
};
#define RCC ((struct rcc *) 0x40023800)
static uint32_t hclk_get(void) {
uint32_t clk = 0;
if (RCC->CFGR & (1 << 2)) {
clk = MG_STM32_CLK_HSE;
} else if (RCC->CFGR & (1 << 3)) {
uint32_t vco, m, n, p;
m = (RCC->PLLCFGR & (0x3FUL << 0)) >> 0;
n = (RCC->PLLCFGR & (0x1FFUL << 6)) >> 6;
p = (((RCC->PLLCFGR & (0x03UL << 16)) >> 16) + 1) * 2;
if (RCC->PLLCFGR & (1UL << 22))
clk = MG_STM32_CLK_HSE;
else
clk = MG_STM32_CLK_HSI;
vco = (uint32_t) ((uint64_t) (((uint32_t) clk * (uint32_t) n)) /
((uint32_t) m));
clk = vco / p;
} else {
clk = MG_STM32_CLK_HSI;
}
int hpre = (RCC->CFGR & (0x0F << 4)) >> 4;
if (hpre < 8) return clk;
return ((uint32_t) clk) >> ahbptab[hpre - 8];
}
// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
// from the HSI (internal RC), and it 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 a +5% drift. If the user uses a different clock from our
// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
#define CRDTAB_LEN 6
static const uint8_t crdtab[CRDTAB_LEN][2] = {
// [{setting, div ratio},...]
{2, 16}, {3, 26}, {0, 42}, {1, 62}, {4, 102}, {5, 124},
};
static uint8_t cr_guess(uint32_t hclk) {
MG_DEBUG(("HCLK: %u", hclk));
if (hclk < 25000000) {
MG_ERROR(("HCLK too low"));
return CRDTAB_LEN;
}
for (int i = 0; i < CRDTAB_LEN; i++)
if (hclk / crdtab[i][1] <= 2375000UL) return crdtab[i][0]; // 2.5MHz - 5%
MG_ERROR(("HCLK too high"));
return CRDTAB_LEN;
}
#endif // MG_ENABLE_MIP
#endif
#ifdef MG_ENABLE_LINES
#line 1 "mip/driver_w5500.c"

View File

@ -1472,6 +1472,22 @@ void qp_init(void);
#define qp_mark(a, b)
#endif
struct mip_driver_stm32 {
// 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-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed
// 216-310 MHz HCLK/124 5
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
};
#ifdef __cplusplus
}
#endif