Pages: [1]   Go Down
Author Topic: PIO Set/Clear Frequency  (Read 1713 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
  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? 
Logged

Offline Offline
Newbie
*
Karma: 2
Posts: 35
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 22
Posts: 1675
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Its worth checking how this is being handled -

Code:
REG_PIOC_ODSR &= ~0x7FFFFFFF;

Have a look at the assembly by following this process -

http://rcarduino.blogspot.com/2012/09/how-to-view-arduino-assembly.html

Duane B
Logged


Forum Administrator
Milano, Italy
Offline Offline
Sr. Member
*****
Karma: 23
Posts: 292
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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).
Logged

C.

Dubai, UAE
Offline Offline
Edison Member
*
Karma: 22
Posts: 1675
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
   
   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
Logged


Offline Offline
God Member
*****
Karma: 32
Posts: 507
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is the fastest I have managed so far. I haven't verified the output signal but it looks plausible.
Code:
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:
Code:
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)
Logged


Dubai, UAE
Offline Offline
Edison Member
*
Karma: 22
Posts: 1675
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.   

Logged

Pages: [1]   Go Up
Jump to: