At these sorts of speeds, most processors work by computing all of the signals that will be needed at a certain clock cycle, waiting for the next clock edge while they stabilize, latching all of those signals and computing the signals needed at the next clock cycle, waiting for that edge while those signals stabilize, etc. If a clock edge arrives before the necessary signals have stabilized, the effect will be that whichever signals hadn't stabilized may not be latched cleanly. If this occurs in a microcontroller, the effects may be unpredictable--for at least two reasons:
- In many cases, execution speed is limited by the response time of the flash array from which the processor reads code. If running the processor too fast causes an occasional bit to be misread here or there, it could easily cause the processor to execute entirely different code from what was intended. In many programs, even a one-time single-bit misread could radically alter the behavior; it is seldom practical to try to make any predictions about what might happen in such cases. The best one can do in some cases is "armor" certain parts of the program so as to make errant execution unlikely. For example, one might leave an EEPROM protected until one wants to write it, and then use code something like:
uint32_t eep_checksum, eep_addr, eep_data;
#define EEPROM_WRITE(address, data, predicate) \
eep_checksum = 0xC0DEFACE, eep_addr = (address), eep_data = (data), \
eep_checksum += eep_addr + eep_data, ((predicate) || HARD_CRASH()), \
eep_checksum += (0xCAFEBABE - C0DEFACE), eep_do_write()
void eep_do_write(void)
{
ENABLE_EEPROM_WRITE_HARDWARE();
if (eep_checksum != eep_addr + eep_data + 0xCAFEBABE)
{
DISABLE_EEPROM_WRITE_HARDWARE();
HARD_CRASH();
}
DO_EEPROM_WRITE();
DISABLE_EEPROM_WRITE_HARDWARE();
}
It is very unlikely that an eeprom_write routine will attempt to write data unless the "eep_checksum = 0xC0DEFACE" is executed before the address and data are loaded. Following the execution of that, the predicate will be checked for validity before adjusting the checksum to the proper value and calling the eeprom_store routine.
- In addition to the clear risks posed by executing incorrect code, another source of potential random behavior is metastability. Normally, on any cycle, every flip flop will latch either a high or low. If, however, the input to a flip flop changes just as the clock arrives, it may for some arbitrary duration output weird stuff which may arbitrarily flip between high and low, in any pattern, until the next clock cycle; it is entirely possible that some devices downstream from the flip flop will see it as "high" while others see it as "low". Generally, processors rely upon many devices agreeing on what they're going to do. If during the execution of a "decrement-and-branch-if-not-equal" instruction, and some circuits think the branch should be taken but others don't, the processor may fall into a really weird state.
Manufacturers specify operating parameters for processors such that, within those parameters, the processors will just plain work. Pushing things outside that envelope may reduce the processor to only being 99.9999999 reliable. That may not sound too evil, but trying to diagnose a processor which does something arbitrarily wrong once a minute or so (figuring 16MHz) is no fun.