From Teensyduino's cores/teensy4/startup.c:
I'm curious how this was even discovered, but more to the point I'd like to know why SysTick seems to be the only affected peripheral if this circuit sits in the hardware.
For example, same file, in void ResetHandler(void), the PIT/GPT and UART clock muxes are configured to use this same 24 MHz XTALOSC without divisors:
And then in cores/teensy4/HardwareSerial.cpp the UART devices assume a bus frequency of 24 MHz (not 100 kHz) to compute an appropriate divisor and over-sample rate:
Obviously the UARTs work quite well on Teensy 4.0, so what gives? Why can SysTick and UART both use the same external 24 MHz OSC, yet only one of them has to account for this divide-by-240 circuit?
Code:
// ARM SysTick is used for most Ardiuno timing functions, delay(), millis(),
// micros(). SysTick can run from either the ARM core clock, or from an
// "external" clock. NXP documents it as "24 MHz XTALOSC can be the external
// clock source of SYSTICK" (RT1052 ref manual, rev 1, page 411). However,
// NXP actually hid an undocumented divide-by-240 circuit in the hardware, so
// the external clock is really 100 kHz. We use this clock rather than the
// ARM clock, to allow SysTick to maintain correct timing even when we change
// the ARM clock to run at different speeds.
#define SYSTICK_EXT_FREQ 100000
I'm curious how this was even discovered, but more to the point I'd like to know why SysTick seems to be the only affected peripheral if this circuit sits in the hardware.
For example, same file, in void ResetHandler(void), the PIT/GPT and UART clock muxes are configured to use this same 24 MHz XTALOSC without divisors:
Code:
// Configure clocks
// TODO: make sure all affected peripherals are turned off!
// PIT & GPT timers to run from 24 MHz clock (independent of CPU speed)
CCM_CSCMR1 = (CCM_CSCMR1 & ~CCM_CSCMR1_PERCLK_PODF(0x3F)) | CCM_CSCMR1_PERCLK_CLK_SEL;
// UARTs run from 24 MHz clock (works if PLL3 off or bypassed)
CCM_CSCDR1 = (CCM_CSCDR1 & ~CCM_CSCDR1_UART_CLK_PODF(0x3F)) | CCM_CSCDR1_UART_CLK_SEL;
And then in cores/teensy4/HardwareSerial.cpp the UART devices assume a bus frequency of 24 MHz (not 100 kHz) to compute an appropriate divisor and over-sample rate:
Code:
#define UART_CLOCK 24000000
...
void HardwareSerial::begin(uint32_t baud, uint16_t format)
{
//printf("HardwareSerial begin\n");
float base = (float)UART_CLOCK / (float)baud;
float besterr = 1e20;
int bestdiv = 1;
int bestosr = 4;
for (int osr=4; osr <= 32; osr++) {
float div = base / (float)osr;
int divint = (int)(div + 0.5f);
if (divint < 1) divint = 1;
else if (divint > 8191) divint = 8191;
float err = ((float)divint - div) / div;
if (err < 0.0f) err = -err;
if (err <= besterr) {
besterr = err;
bestdiv = divint;
bestosr = osr;
}
}
Obviously the UARTs work quite well on Teensy 4.0, so what gives? Why can SysTick and UART both use the same external 24 MHz OSC, yet only one of them has to account for this divide-by-240 circuit?