One for the PIO gurus


Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11866
Posted: 07:11pm 01 Nov 2025      

Can anyone help diagnose this?
This code is supposed to create a bitclock, a HSYNC pulse and a VSYNC pulse with VGA timings. It is development in preparation for trying to drive an RGB TFT display which needs a bitclock.
The bitclock runs at 25.1MHz
The HSYNC runs at 31.5 MHz with a 3.8uS sync
The VSYNC runs at 60Hz with a 63.5uS sync
http://www.tinyvga.com/vga-timing/640x480@60Hz
All this works fine except that when you trigger on VSYNC you will see that HSYNC is drifting relative to it whereas given that they should both be triggered nearly simultaneously by the separate irqs.
The problem looks like the hysnc timing is overunning (or visa versa) but I can't see the problem, fiddling with the underrun fudge factor on the vsync numbers changes the drift but it should be locked and the fudge factor of 1000 clocks should not be critical
I've been looking at this too long to see the wood for the trees. Help appreciated.

Option explicit
Option default integer
Dim p1,f1,e1,s1,o1,p2,f2,e2,s2,o2,p3,f3,e3,s3,o3
' set pins that will be used as outputs
SetPin gp4,pio1
SetPin gp16,pio1
SetPin gp17,pio1
'
o1=Pio(next line 1) 'note the start of the first PIO instruction for program 1 - will be 0
Print "compiling pclk"
PIO assemble 1
.program pclk
.side set 1
.line next 'we can always use next unless we specifically want to place the code
   Pull block 'read in the loop counter which will define the timing of the second program
.wrap target
   Mov x,osr side 1 [4] 'we can use osr to keep this and re-use it
.label loop 'loop as specified by the counter
   Nop side 0 [4]
   Jmp x--,loop side 1 [4]
   IRQ 0 side 0 [3] 'set a flag for the second program
   IRQ 4 side 0  'set a flag for the third program
.wrap
.end program list
' create the setting to initialise program 1
p1=Pio(pinctrl 1,,,,gp4)
f1=252000000
e1=Pio(execctrl GP0,Pio(.wrap target),Pio(.wrap))
s1=Pio(shiftctrl 0,0,0,0,0,0)
'
o2=Pio(next line) 'note the start of the second pio program
Print "compiling hsync @ line: ";o2
PIO assemble 1
 .program hsync
 .side set 1
 .line next 'this will pick up o2 automatically
   Pull block side 1 'get the length of the front port
   Mov y,osr side 1 'store the length of front porch in y
   Pull block side 1 'get the length of the sync pulse into osr
 .wrap target
   Mov x,osr side 1 'get the length of the synch
   Wait 1 irq 4 side 1 'this instruction waits for irq 4 to be set and automatically clears it
   ' this happens at the start of each line
.label loop1
   Jmp x--,loop1 side 0
   Mov x,y side 1 'restore the front porch value
.label loop2
   Jmp x--,loop2 side 1
   Nop side 1
'we will trigger the data transfer here
 .wrap
.end program list

' create the setting to initialise program 2
p2=Pio(pinctrl 1,,,,gp16)
f2=252000000
e2=Pio(execctrl GP0,Pio(.wrap target),Pio(.wrap))
s2=Pio(shiftctrl 0,0,0,0,0,0)

o3=Pio(next line) 'note the start of the second pio program
Print "compiling vsync @ line: ";o3
PIO assemble 1
 .program vsync
 .side set 1
 .line next 'this will pick up o3 automatically
   Pull block side 1
   Mov y,osr side 1 'store the length of the front porch in y
   Pull block side 1
   Mov isr,osr side 1 'store the length of the rest of the line in isr
   Pull block side 1 'get the synch length
 .wrap target
   Mov x,osr side 1 'get the synch length
   Wait 1 irq 0 side 1 'this instruction waits a line end which we use to be beginning of frame
.label loop3
   Jmp x--,loop3 side 0 'synch loop
   Mov x,y side 0
.label loop4
   Jmp x--,loop4 side 1 'front porch loop
   Mov x,isr side 1
.label loop5
   Jmp x--,loop5 side 1 'rest of line loop
 .wrap
.end program list

' create the setting to initialise program 3
p3=Pio(pinctrl 1,,,,gp17)
f3=252000000
e3=Pio(execctrl GP0,Pio(.wrap target),Pio(.wrap))
s3=Pio(shiftctrl 0,0,0,0,0,0)

' initialise and start program 1 but note it will block on pull block
PIO init machine 1,0,f1,p1,e1,s1,o1,1,0,0
PIO start 1,0
' initialise and start program 2, it will block waiting for the irq
PIO init machine 1,1,f2,p2,e2,s2,o2,1,0,0
PIO start 1,1
PIO write 1,1,2,48*10-2,96*10-2
' initialise and start program 3, it will block waiting for the irq
PIO init machine 1,2,f3,p3,e3,s3,o3,1,0,0
PIO start 1,2
'rest of line value is reduced to make sure no overun but also don't miss a line
PIO write 1,2,3,8000*33-1,8000*490-1000,8000*2-1
' trigger the whole shebang by writing to program 1 fifo
PIO write 1,0,1,800
' loop, although the PIO will keep running anyway even if the program finishes
Do
Loop

Edited 2025-11-02 05:15 by matherp