MFM V3 testing
Â
Testing millis/micros/ticks rollover/overflow
To test, we have to set the relevant value to near overflow. To test the rollover of a single time unit, all time units will have to be adjusted. Some system components reference other time units than others (e.g. LMIC uses ticks while sleep uses millis).
Below is a code fragment which adjusts the millis and micros to a set amount indicated by <MILLIS> and <MICROS>.
// TODO: REMOVE, testing time rollover
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
extern volatile unsigned long timer0_overflow_count;
extern volatile unsigned long timer0_millis;
// timer0 overflows every 64 * 256 clock cycles
// https://github.com/arduino/ArduinoCore-avr/blob/6ec80154cd2ca52bce443afbbed8a1ad44d049cb/cores/arduino/wiring.c#L27
timer0_overflow_count = <MICROS> * 8 / 64 / 256;
timer0_millis = <MILLIS>;
TCNT0 = 0; // Max deviation from millis will be 2.048 ms
}
Serial.begin(19200);
Serial.print(millis());
Serial.print(";");
Serial.print(micros());
Serial.print(";");
Serial.print(hal_ticks());
Serial.println();
Serial.flush();
Adjusting the OS ticks is tougher. This variable is based off micros, but is extended by an extra unsigned overflow byte. 1 OS Ticks is always a 2^x microseconds. For example, 1 OS tick == 2^4 == 16 microseconds. Due to this the HAL code can shift the micros value bits X times to the right to get the correct division (remember binary).
The relevant implementation is shown below (slightly modified for readability).
u4_t hal_ticks () {
static uint8_t overflow = 0x00;
// Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0,
// the others will be the lower bits of our return value.
uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT;
// Most significant byte of scaled
uint8_t msb = scaled >> 24;
// Mask pointing to the overlapping bit in msb and overflow.
const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT));
// Update overflow. If the overlapping bit is different
// between overflow and msb, it is added to the stored value,
// so the overlapping bit becomes equal again and, if it changed
// from 1 to 0, the upper bits are incremented.
overflow += (msb ^ overflow) & mask;
// Return the scaled value with the upper bits of stored added. The
// overlapping bit will be equal and the lower bits will be 0, so
// bitwise or is a no-op for them.
return scaled | ((uint32_t)overflow << 24);
}
To correctly adjust the OS Ticks for rollover testing, you will have to adjust micros and the overflow variable.
Example:
OS Ticks rollover test assuming clock speed is 8MHz and 1 OS Tick is 16 microseconds. Setting OS Ticks to 4291217312.
Micros is calculated by multiplying the value times 16, this will cause an integer overflow so use modulus with the max value to keep the remainder:
micros = (4291217312 * 16) % 0xFFFFFFFF = 4234967567
Millis can be calculated by first converting to micros and then to millis (don’t use modulus here).
And last but not least we have to set the correct OS Ticks overflow variable. This is calculated by the amount of overflows that (would) have happened on the micros variable. So instead of modulo just divide and skip everything after the comma.
As you notice the resulting value is almost 16, which makes sense because in essence the first value 4291… is almost equal to 0xffffffff cancelling out in the equation. Remember that 1 OS Ticks == 2^4 == 16 microseconds. As a result the overflow variable only utilizes the four most significant bits. The resulting overflow will equal:
Notice that the 4th bit is also flipped to a 1. This is required because the overflow only increases if the 4th bit overlaps with the 4th bit of the micros its most significant byte. See line 15 in the relevant implementation above.
DO NOT change the last three bits. The overflow variable is shifted 24 times to the left (MSByte of 32 bit integer) causing the rollover to skip a significant part (i.e. starting at a few hundred million).