One for the PIO gurus


Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11865
Posted: 11:17am 02 Nov 2025      

If anyone is interested. Here is the fixed version. I found a couple of commands that weren't implemented in the PIO assemble IRQ NEXT and IRQ PREV. These allow a state machine on one PIO to communicate with a state machine on another PIO. I need these as I have run out of commands on PIO 1 so the data output will have to be on PIO 2. These will be in RC10. I'm using IRQ 3 to tell the data output that it can start a new line and IRQ 4 to synchronise the start of frame.
You can run the program as-is on a standard PicoMite and if you connect PIN 17 to VSYNC on a VGA monitor and pin 16 to Hsync you will find the monitor locks up happily at 640x480 @ 60Hz. The 25.2MHz pixel clock is on GP4.
Option explicit
Option default integer
Dim o
' set pins that will be used as outputs
SetPin gp4,pio1
SetPin gp16,pio1
SetPin gp17,pio1
Const cyclesperpixel=10
Const hwholeline=800
Const hvisible=640*cyclesperpixel
Const hsync=96*cyclesperpixel
Const hfrontporch=16*cyclesperpixel
Const hbackporch=48*cyclesperpixel
Const vwholeframe=525*hwholeline*cyclesperpixel
Const vvisible=480*hwholeline*cyclesperpixel
Const vsync=2*hwholeline*cyclesperpixel
Const vfrontporch=10*hwholeline*cyclesperpixel
Const vbackporch=33*hwholeline*cyclesperpixel
Const clock=Val(MM.Info(cpuspeed))

'
o=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  'set a flag for the second program
   IRQ 1 side 0 [3] 'set a flag for the third program
.wrap
.end program list
'PIO CONFIGURE pio, sm,clock [,startaddress][,sidesetbase] [,sidesetno][,sidesetout][,setbase] [,setno] [,setout]
'[,outbase] [,outno] [,outout][,inbase][,jmppin] [,wraptarget] [,wrap][,sideenable] [,sidepindir][,pushthreshold]
'[,pullthreshold] [,autopush][,autopull] [,inshiftdir][,outshiftdir][,joinrxfifo] [,jointxfifo][,joinrxfifoget] [,joinrxfifoput]
' create the setting to initialise program 1
PIO CONFIGURE 1,0,clock,o,gp4,1,1,,,,,,,,,Pio(.wrap target),Pio(.wrap)
' create the setting to initialise program 1
'PIO init machine 1,0,f1,p1,e1,s1,o1,1,0,0
'
o=Pio(next line) 'note the start of the second pio program
Print "compiling hsync @ line: ";o
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 sync
   Mov y,osr side 1 'save the length of the sync pulse into y
   Pull block side 1' get the length of the visible data + front porch in osr
 .wrap target
   Mov x,osr side 1 'get the length of the active+frontporch
   Wait 1 irq 1 side 1 'this instruction waits for irq 1 to be set by the pixel clock and automatically clears it
   IRQ NEXT 3 side 1
   ' this happens at the start of each line
.label loop1
   Jmp x--,loop1 side 1
   Mov x,y side 1 'restore the sync value
.label loop2
   Jmp x--,loop2 side 0
   Nop side 1
'we don't need to consider the back porch as we don't trigger again until the start of a new line
 .wrap
.end program list

' create the setting to initialise program 2
PIO CONFIGURE 1,1,clock,o,gp16,1,1,,,,,,,,,Pio(.wrap target),Pio(.wrap)

o=Pio(next line) 'note the start of the second pio program
Print "compiling vsync @ line: ";o
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 back porch in y
   Pull block side 1
   Mov isr,osr side 1 'store the length of sync in isr
   Pull block side 1 'get the synch length
 .wrap target
   Mov x,osr side 1 'get the length of the active + front porch
   Wait 1 irq 0 side 1 [1]'this instruction waits a line end which we use to be beginning of frame
   IRQ NEXT 4 side 1
.label loop3
   Jmp x--,loop3 side 1 'main data loop
   Mov x,isr side 1 'restore the sync value
.label loop4
   Jmp x--,loop4 side 0 'sync loop
   Mov x,y side 0
.label loop5
   Jmp x--,loop5 side 1 'rest of line loop
   IRQ CLEAR 0 side 1
 .wrap
.end program list

' create the setting to initialise program 3
PIO CONFIGURE 1,2,clock,o,gp17,1,1,,,,,,,,,Pio(.wrap target),Pio(.wrap)

' initialise and start program 1 but note it will block on pull block
PIO start 1,0
' initialise and start program 2, it will block waiting for the irq
PIO start 1,1
PIO write 1,1,2,hsync,hvisible+hfrontporch-2
' initialise and start program 3, it will block waiting for the irq
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,vbackporch-2,vsync,vvisible+vfrontporch-2
' trigger the whole shebang by writing to program 1 fifo
PIO write 1,0,1,hwholeline
' loop, although the PIO will keep running anyway even if the program finishes
Do
Loop