Trying basic direct digital synthesis that works perfectly on the 3.6 and getting strange "wobble" in frequency, seems random and is about 10% around the desired frequency. The basic idea is the make a look-up-table of the waveform to be synthesized and "walk through" it based on the top bits of a 32-bit word that is incremented repetitively with a "fraction" that is determined based on the ratio of the desired output frequency and the effective sample rate of the loop, determined empirically for the actual loop code. The output of the look-up-table is output to a set of pins, which are connected to a D/A converter.

Any ideas? Are there background processes that might be taking variable amounts of time and not suppressed by interrupt priority (assuming that even works with 4.0 as I have used it)?

Code:

// Port-Write DDS Experiments, Teensy 4.0, 600MHz clock
// G. Kovacs, 8/12/19
// Calculate a 8-bit (uint16_t, straight binary) sinewave look-up-table
// LS bit = D0, MS-bit = D7.
#define arraySize 256 //Must be an even power of two, and usually the same number of points as the number of DAC steps, here 256.
int numbitsLUT = int(log(arraySize)/log(2)); //Compute number of bits needed to address DDS LUT, and later, from this, how many to shift the DDS accumulator to address LUT.
const uint16_t DDSshift = 32 - numbitsLUT; //use const for speed
uint8_t waveform[arraySize];
uint16_t synthTemp;
int pinPoint;
uint8_t pointOut;
uint16_t pointer = 0;
uint32_t sum = 0; //This is the DDS "accumulator" that rolls over at a frequency determined by "pointerIncrement," thus defining the output frequency.
float freqOut = 100.000E3; //Desired output frequency
const float measuredSampleRate = 10.1E6; //Effective sample rate, determined by trial and error for a given code version.
uint32_t pointerIncrement = 0;
void setup() {
for (int i = 0; i < 8; i++) {pinMode(i, OUTPUT);}
pointerIncrement = int((freqOut/measuredSampleRate)*pow(2,32)+0.5);
for (uint16_t i=0; i<arraySize; i++)
{
// Data is scaled to 0..255, unsigned, 8-bit binary for DAC.
// This floating point mapping gives a verified good sine LUT.
synthTemp = (32767.5+(32767.5*sin(2*3.141592654*(float(i)/(arraySize-1))))); //Here use a sinewave, but could be anything desired.
waveform[i] = synthTemp>>8; // shift data here for 8-bit external DAC. Adjust as necessary.
}
//See: https://forum.pjrc.com/threads/27690-IntervalTimer-is-not-precise
//Technique to reduce intervalTimer jitter.
SCB_SHPR3 = 0x20200000; // Systick = priority 32 (defaults to zero, or highest priority)
}
void yield () {} //Get rid of the hidden function that checks for serial input and such.
FASTRUN void loop()
// Use FASTRUN to force code to run in RAM.
{
noInterrupts();
while (1) //Loop inside void loop () avoids the overhead of the main loop.
{
pointer = sum >> DDSshift; //Shift to fit range waveform look-up table. Change as needed.
pointOut = waveform[pointer];
digitalWriteFast(7, (0x80 & pointOut)); //MSbit
digitalWriteFast(6, (0x40 & pointOut));
digitalWriteFast(5, (0x20 & pointOut));
digitalWriteFast(4, (0x10 & pointOut));
digitalWriteFast(3, (0x08 & pointOut));
digitalWriteFast(2, (0x04 & pointOut));
digitalWriteFast(1, (0x02 & pointOut));
digitalWriteFast(0, (0x01 & pointOut)); //LSbit
// Value added to "sum" determines the output frequency. Larger values added translate to lower frequencies. That's DDS!
// sum = sum + 0x80000000; //For sine LUT, should be Nyquist rate, or 1/2 of effective sample rate, on the MSBit, pin 29. (mayb 0x7FFFFFFF?).
sum = sum + pointerIncrement; //For sine LUT, should be Nyquist rate, or 1/2 of effective sample rate, on the MSBit, pin 29.
}
}

With a simple R2R D/A it works except for the "wobble" - the spikes are because the output is not yet lowpass filtered. On the Teensy 3.6, I can do >8 Megasamples/second at 16 bits out using port writes.