This ring oscillator runs on the ATSAMD51J19 MCU using the Adafruit Feather dev board (though dev board price comes from Adafruit ItsyBitsy M4 Express). The ATSAMD51J19 has an ARM Cortex-M4 running with a max speed of 120 MHz. Code for the oscillator is available here, or visible below.
#define portin PORT->Group[PORTA].IN.reg
#define outclr PORT->Group[PORTA].OUTCLR.reg
#define outset PORT->Group[PORTA].OUTSET.reg
#define out PORT->Group[PORTA].OUT.reg
#define outtgl PORT->Group[PORTA].OUTTGL.reg
const uint8_t in_pin = 21;
const uint32_t in_mask = 1ul<<in_pin;
const uint8_t out_pin = 22;
const uint32_t out_mask = 1ul<<out_pin;
void setup() {
// Set pin mode according to chapter '32.6.3 I/O Pin Configuration'
// enable input, to support reading back values, with pullups disabled
PORT->Group[PORTA].PINCFG[out_pin].reg = (uint8_t)(PORT_PINCFG_INEN) ;
// Set pin to output mode
PORT->Group[PORTA].DIRSET.reg = out_mask;
// Set in pin to input mode
PORT->Group[PORTA].PINCFG[in_pin].reg=(uint8_t)(PORT_PINCFG_INEN) ;
PORT->Group[PORTA].DIRCLR.reg = in_pin ;
//make sure port synchronizer always active, else we'll use extra clock cycles.
//I'm not sure I'm activating the synchronizer correctly, because I don't see a change when I add this...
PORT->Group[PORTA].CTRL.reg = in_mask;
//loop
while(1){
//ternary operator: assymetric 300ns (200-100)
//portin & in_mask ? outclr = out_mask : outset = out_mask;
//portin & in_mask ? outclr = out_mask : out = out_mask;
//full port inversion: symmetric 300ns
out = ~(portin);
//bitwise xor: 300ns
//out = ((portin ^ in_mask) << 1);
//if else, 300ns
/*if (portin & in_mask){
outclr = out_mask;
} else {
outset = out_mask;
}*/
}
}
void loop() {}
By default the port consumes extra clock cycles to save power and only sample the input pin when a read is requested (see description below). We can set the input synchronizer to always be active to avoid this delay. I've tried to do this above, but I need to confirm if this was successful.