PIO Set/Clear Frequency

I have been unsuccessful meeting the spec frequency of 35 MHz for I/O pins (Pin Group 2 on pg. 1423 of data sheet). The fastest I can get is ~2 MHz, using the below code fragment:

  pmc_enable_periph_clk(ID_PIOC);
  
  PIO_SetOutput(PIOC, 0x7FFFFFFF, LOW, 0, 0);    
  REG_PIOC_OWSR |= 0x7FFFFFFF;
  //  enable writing PIO_ODSR for I/O line
  REG_PIOC_OWER |= 0x7FFFFFFF;

void loop() {
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
}

I checked the value of my clock register REG_PMC_MCKR, and found that it should output an 84 MHz signal from PLLACK. Looking at data sheet pg. 544, the PMC Clock Diagram shows the MCK go direct into the peripheral clock. This I assume is the same signal that moves to the PIO controller, labeled "System Clock" on pg. 644.

What else do I need to set to meet the spec for Set/Clear frequency of the I/O lines?

That is switching many pins on and off right? If so, have you tried switching just one pin?

Its worth checking how this is being handled -

REG_PIOC_ODSR &= ~0x7FFFFFFF;

Have a look at the assembly by following this process -

Duane B

The external while eats some clock cycles too.
Try to unroll the loop for some iterations:

void loop() {
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
  REG_PIOC_ODSR = 0x7FFFFFFF;
  REG_PIOC_ODSR &= ~0x7FFFFFFF;
}

Also the &= operand is equivalent to REG = REG & VAL; so its probably a 2 step operation (you should check the generated assembly code as suggest by DuaneB).

Can't say that I am familiar with ARM but this is what your getting for loop -

   80128:	4b04      	ldr	r3, [pc, #16]	; (8013c <loop+0x14>)
   8012a:	f06f 4200 	mvn.w	r2, #2147483648	; 0x80000000
   8012e:	601a      	str	r2, [r3, #0]
   80130:	681a      	ldr	r2, [r3, #0]
   80132:	f002 4200 	and.w	r2, r2, #2147483648	; 0x80000000
   80136:	601a      	str	r2, [r3, #0]
   80138:	4770      	bx	lr
   8013a:	bf00      	nop
   8013c:	400e1238 	.word	0x400e1238

I will update the link with a screen shot of the Due disassembly command line now that I have it.

Duane B

This is the fastest I have managed so far. I haven't verified the output signal but it looks plausible.

void setup(){
  Serial.begin(115200);
  for(int p=2;p<70;p++)pinMode(p,OUTPUT);
}

#define do10(x) x x x x x x x x x x
void loop(){
  uint32_t s=0x7fffffff;
  uint32_t r=0x0;
  typeof(REG_PIOC_ODSR) *p=&REG_PIOC_ODSR;
  
  long t=micros();
  for(int x=0;x<1000;x++){
    do10(do10(do10( *p=s;*p=r; )))
  }
  t=micros()-t;
  
  Serial.print("1 million write pairs in ");
  Serial.print(t);
  Serial.print(" uS = ");
  Serial.print(1000000.0/double(t));
  Serial.println(" MHz");
}

1 million write pairs in 47797 uS = 20.92 MHz

Disassembly looks like this:

 f00:   6019            str     r1, [r3, #0]
 f02:   601a            str     r2, [r3, #0]
 f04:   6019            str     r1, [r3, #0]
 f06:   601a            str     r2, [r3, #0]
 f08:   6019            str     r1, [r3, #0]
 f0a:   601a            str     r2, [r3, #0]
 f0c:   6019            str     r1, [r3, #0]
 f0e:   601a            str     r2, [r3, #0]
.....

Remember that there are other ways of getting a signal out of a pin (SPI, timers, PWM, even DMA to the port might do it)

Unless I am missing something, its also a bit pointless - programmatically toggling pins when timers can do it faster and more consistently - i.e. not loop over head or asymmetry.

Duane B

Thank you all for your responses, I was unaware of a method for getting the assembly from the compiler, so DuaneB that was tremendous help. Using a method similar to the post by stimmer, I was able to scope a 21 MHz signal.

And yes, I am aware of the methods for producing a reliable clock through other protocols, timers, etc. The current application is a user-controlled clocking of data in a finite state machine, I just wanted to see my max pin switching frequency.