Sharing with you: my MDIO interface implementation (clause 22 and clause 45 are possible) with PICO PIO (in MicroPython on RP2350, PICO 2).
What is MDIO?
MDIO is used for instance: talking to an ETH PHY chip (or an STM32 MCU with MDIOS device), in order to configure registers.
It is an interface with two signals, MCLK and MDIO. MDIO is bi-directional: for a read it changes the pindirs before sampling (reading) the response from an external slave chip. There are two TurnAround (TA) bits to let change the direction on the MDIO signal.
In addition, I have added an OEN signal, used to realize the direction on MDIO (e.g. for a level shifter with DIRection control).
How I have implemented?
I use PIO0, with all 4 SMs and I need all 32 instructions (right at the limit).
There is one SM which generates just the endlessly running MCLK: clk.
Using IRQ - I synchronize other SMs with the clock generator, e.g. when sending (writing, dataWrite) to change the MDIO data bits with the falling edge (so that slave would see stable signal on rising edges when reading).
When doing a READ (a 16bit value from a register, via dataRead) - I had to sample the IN with the raising edge. Therefore, there are two IRQs for every edge.
SM0 clk is the clock generator (a free running clock, firing IRQs so that other SMs will synchronize with the MCLK)
SM1 dataWrite is the WRITE transaction: send START, CMD, ADDR and the 16bit data value (the 2bit TA is generated as well)
SM2 dataRead is the READ transaction: send START, CMD, ADDR and generate the 2bit TA, read 16bit as response but with rising edge
SM3 pre is just a "delay" function (for a preamble): before any MDIO transaction, there have to be 32 clock cycles with no MDIO data (MDIO stays at 1), in order to make it automatically depending on the MCLK freq setting - I generate those 32bits (at least) before any transaction with this SM pre
All looks fine on oscilloscope. There is just one question:
Why do I need twice the "irq(block, 4)" instruction in my "pre" SM?
Even I set X to 31 (max. possible value, and I want to wait for 32 clock edges done. at least) - it does not wait really for 32 IRQs generated by (clk SM. Without this "trick": I do not get really 32 clock cycles: shorter, e.g. just 16 or even 8. Why?
Here my MicroPython code for MDIO:
What is MDIO?
MDIO is used for instance: talking to an ETH PHY chip (or an STM32 MCU with MDIOS device), in order to configure registers.
It is an interface with two signals, MCLK and MDIO. MDIO is bi-directional: for a read it changes the pindirs before sampling (reading) the response from an external slave chip. There are two TurnAround (TA) bits to let change the direction on the MDIO signal.
In addition, I have added an OEN signal, used to realize the direction on MDIO (e.g. for a level shifter with DIRection control).
How I have implemented?
I use PIO0, with all 4 SMs and I need all 32 instructions (right at the limit).
There is one SM which generates just the endlessly running MCLK: clk.
Using IRQ - I synchronize other SMs with the clock generator, e.g. when sending (writing, dataWrite) to change the MDIO data bits with the falling edge (so that slave would see stable signal on rising edges when reading).
When doing a READ (a 16bit value from a register, via dataRead) - I had to sample the IN with the raising edge. Therefore, there are two IRQs for every edge.
SM0 clk is the clock generator (a free running clock, firing IRQs so that other SMs will synchronize with the MCLK)
SM1 dataWrite is the WRITE transaction: send START, CMD, ADDR and the 16bit data value (the 2bit TA is generated as well)
SM2 dataRead is the READ transaction: send START, CMD, ADDR and generate the 2bit TA, read 16bit as response but with rising edge
SM3 pre is just a "delay" function (for a preamble): before any MDIO transaction, there have to be 32 clock cycles with no MDIO data (MDIO stays at 1), in order to make it automatically depending on the MCLK freq setting - I generate those 32bits (at least) before any transaction with this SM pre
All looks fine on oscilloscope. There is just one question:
Why do I need twice the "irq(block, 4)" instruction in my "pre" SM?
Even I set X to 31 (max. possible value, and I want to wait for 32 clock edges done. at least) - it does not wait really for 32 IRQs generated by (clk SM. Without this "trick": I do not get really 32 clock cycles: shorter, e.g. just 16 or even 8. Why?
Here my MicroPython code for MDIO:
Code:
import timeimport rp2from machine import Pinimport time#clock generator - free running clock, INTs for rising (read) and falling edge (write)@rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW)def clk(): #this results in 25MHz clock #trim this so that other SM changes bits on falling edge when sending #it divides freq by 6 wrap_target()#counting instructions: irq(4)[1].side(1)#1 irq(clear, 4)[0]#2 irq(5)[1].side(0)#3 irq(clear, 5)[0]#4 wrap() @rp2.asm_pio()def pre(): wrap_target() pull()#5 : just wait for trigger (any value) set(x, 31)#6 label("preloop") irq(block, 4)#7 irq(block, 4)#8 : why do we need 2x?, otherwise too short! jmp(x_dec, "preloop")#9 push(x)#10 : just trigger main() (any value) wrap() #generate a 32bit write transaction: 2bit (START) 2bit (CMD) 10bit (ADDR) 2bit (TA) 16bit (DATA write)@rp2.asm_pio(out_shiftdir=0, pull_thresh=32, out_init=rp2.PIO.OUT_LOW, set_init=rp2.PIO.OUT_LOW, sideset_init=rp3.PIO.OUT_LOW)def dataWrite(): wrap_target() pull()#11 set(x, 32)#12 label("loop") irq(block, 4)#13 out(pins, 1).side(1)#14 jmp(x_dec, "loop")#15 irq(block, 4)#16 set(pins, 1).side(1)#17 wrap() #generate a 16bit read transaction: 2bit (START) 2bit (CMD) 10bit (ADDR), 2bit (TA), 16bit (DATA read) - read with rising edge!@rp2.asm_pio(out_shiftdir=0, pull_thresh=14, push_thresh=16, out_init=rp2.PIO.OUT_LOW, set_init=rp2.PIO.OUT_LOW, sideset_init=rp2.PIO.OUT_LOW)def dataRead(): wrap_target() pull()#18 set(x, 13)#19 label("loop") irq(block, 4)#20 out(pins, 1)#21 jmp(x_dec, "loop")#22 irq(block, 4)#23 set(pindirs, 0).side(0)#24 irq(block, 4)#25 set(x, 15)#26 irq(block, 5)#27 label("rx") irq(block, 5)#28 in_(pins, 1)#29 jmp(x_dec, "rx")#30 push()#31 set(pindirs, 1).side(1)#32 - full 32 instructions on PIO0 - no more code possible (on PIO0) wrap() FREQ = 25000000#CLK generator SM0sm0 = rp2.StateMachine(0, clk, freq=FREQ * 6, sideset_base=Pin(2))sm0.active(1)#read cycle SM2sm2 = rp2.StateMachine(2, dataRead, out_base=Pin(3), in_base=Pin(3), set_base=Pin(3), sideset_base=Pin(4))sm2.active(1)#write cycle SM1sm1 = rp2.StateMachine(1, dataWrite, out_base=Pin(3), in_base=Pin(3), set_base=Pin(3), sideset_base=Pin(4))sm1.active(1)sm3 = rp2.StateMachine(3, pre)sm3.active(1) prevR = 0while True: sm3.put(0) sm3.get() sm2.put(0x1A440000)#generate 13bits of frame start, 2bit TA and read 16bit r = sm2.get()#get the read result if prevR != r: prevR = r#print just if changed (otherwise slows down too much) print(hex(r)) sm3.put(0) sm3.get() sm1.put(0x01A44C084)#generate a write, 2bit (START) + 2bit (CMD) + 10bit (ADDR) + 2bit (TA) + 16bit (DATA write)
Statistics: Posted by tjaekel — Mon Aug 26, 2024 2:28 am