Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 3873

MicroPython • PICO PIO IRQs - max. speed is 1/4 of SYS CLK

$
0
0
I tried to use IRQs for synchronizing other PIO state machines (SM) with a free running clock generator.

The idea is this:
  • have one SM just generating an endlessly running clock (as "clk" source)
  • let other SMs synchronize with raising and falling edges of the generated clock, e.g. to send out or to sample in with edges
  • try to use the max. frequency possible, e.g. 1/2 of SYS CLOCK (because two instructions needed to handle IRQs anyway)
But it seems to work only properly up to a quarter of the the SYS CLK.

Here, my first approach for a clock generator "clk":

Code:

@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW)def clk():    wrap_target()    irq(4)[0].side(1)#1    irq(clear, 4)[0]        #2    irq(5)[0].side(0)  #3    irq(clear, 5)[0]        #4    wrap()
Idea:
generate a fastest clock as possible, generate an IRQ (4 for raising edge, 5 for falling edge) and synchronize other SMs by using code like this:

Code:

    set(x, 14)#5: 15 bits to send    label("loop")    irq(block, 4)#6: wait for rising edge - be in sync with clock generator "clk"    out(pins, 1)#7: shift one bit out    jmp(x_dec, "loop")
But it does not work this way (see y assumptions below):
  • I get an "offset": even I want to change with falling edge, the change happens not in sync with the clock edge (see below, obvious,
    the IRQ signal itself is delayed inside system, but by how much? (assume, at least 1 SYS CLK cycle)
  • if I run the "clk" this way: I get two triggers (it looks like, or one additional SYS CLK needed), and it generates twice as many bits (every bit out has two clock cycles now)
It works only this way:

Code:

@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW)def clk():    wrap_target()    irq(4)[0].side(1)#1    irq(clear, 4)[0]        #2    nop()[1].side(0)#3: needed just to create a 50% duty cycle, without it looks wrong    irq(5)[0]        #4    irq(clear, 5)[0]        #5    nop()[1]     .side(1)#6: just for the duty cycle as approx. 50%    wrap()
So, it tells me:
  • I need already 2 clocks just for "irq(4)" and "irq(clear, 4)" (OK, obvious)
  • but without this "nop() [1]" the clock is not symmetrical (50% duty cycle) anymore
  • so, I have to divide the "clk" generated by 4 already, at least, to make it a symmetrical duty cycle
  • every change on this code does not give me a nice 50% duty cycle
  • or the synchronization with other SMs is messed up, e.g. getting an IRQ edge just two or four clocks later
My assumptions what happens:
  • even I try to generate on every half SYS CLK cycle an IRQ
  • this IRQ signal seems to go internally through a "clock synchronizer" (between PIO and other blocks)
  • even I want to consume the IRQ signal generated in another SM "immediately" - it will be internally synchronized with the SM clock, the
    IRQ logic etc. (all synced to the SYS CLK again)
  • there seems to be a synchronizer for the IRQ signal - it is not populated asynchronously ("immediately") to the consuming SM - so that it can delay the IRQ by at least one addition SYS CLK cycle more
So, even I generate every 1/2 SYS CLOCK cycle an IRQ, it will be populated just every 1/4 clock cycle. Faster is not possible.

So, IRQs work only up to 1/4 of the SYS CLK speed.
And: even I want to be "in sync" with the clock edges: my SM has an "uncertainty" of 1/2 (or even 1/4) of SYS CLK speed, before it will act on the IRQ signal generated.
If I run the generated clock via "clk" faster as 18.75 MHz - it does not work anymore as expected (the waveform looks wrong in terms of duty cycles and when signals are changed).
And the behavior becomes "random" (obvious, sometimes delayed or not).

So, assume, when using IRQ, the max. speed generating IRQs is 1/4 or even 1/8 of the SYS CLK speed.

Statistics: Posted by tjaekel — Thu Aug 29, 2024 2:16 am



Viewing all articles
Browse latest Browse all 3873

Trending Articles