Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 91 additions & 41 deletions rp2-pio/instr.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ func (instr instructionV0) majorbits() uint16 {
return instr.instr & _INSTR_BITS_Msk
}

func (asm AssemblerV0) instrArgs(instr uint16, arg1 uint8, arg2 uint8) instructionV0 {
return asm.instr(instr | (uint16(arg1) << 5) | uint16(arg2&0x1f))
func (asm AssemblerV0) instrArgs(instr uint16, arg1_5b uint8, arg2 uint8) instructionV0 {
return asm.instr(instr | (uint16(arg1_5b) << 5) | uint16(arg2&0x1f))
}

func (asm AssemblerV0) instrSrcDest(instr uint16, srcDest SrcDest, value uint8) instructionV0 {
return asm.instrArgs(instr, uint8(srcDest)&7, value)
func (asm AssemblerV0) instrSrcDest(instr uint16, srcDest uint8, value uint8) instructionV0 {
return asm.instrArgs(instr, srcDest&7, value)
}

// Encode returns the finalized assembled instruction ready to be stored to the PIO program memory and used by a PIO state machine.
Expand Down Expand Up @@ -131,14 +131,14 @@ func (asm AssemblerV0) WaitGPIO(polarity bool, pin uint8) instructionV0 {

// Shift Bit count bits from Source into the Input Shift Register (ISR). Shift direction is configured for each state machine by
// SHIFTCTRL_IN_SHIFTDIR. Additionally, increase the input shift count by Bit count, saturating at 32.
func (asm AssemblerV0) In(src SrcDest, value uint8) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_IN, src, value)
func (asm AssemblerV0) In(src InSrc, value uint8) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_IN, uint8(src), value)
}

// Shift Bit count bits out of the Output Shift Register (OSR), and write those bits to Destination. Additionally, increase the
// output shift count by Bit count, saturating at 32.
func (asm AssemblerV0) Out(dest SrcDest, value uint8) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_OUT, dest, value)
func (asm AssemblerV0) Out(dest OutDest, value uint8) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_OUT, uint8(dest), value)
}

// Push the contents of the ISR into the RX FIFO, as a single 32-bit word. Clear ISR to all-zeroes.
Expand All @@ -160,18 +160,18 @@ func (asm AssemblerV0) Pull(ifEmpty, block bool) instructionV0 {
}

// Mov copies data from src to dest.
func (asm AssemblerV0) Mov(dest, src SrcDest) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_MOV, dest, uint8(src)&7)
func (asm AssemblerV0) Mov(dest MovDest, src MovSrc) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_MOV, uint8(dest), uint8(src)&7)
}

// MovInvertBits does a Mov but inverting the resulting bits.
func (asm AssemblerV0) MovInvert(dest, src SrcDest) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_MOV, dest, (1<<3)|uint8(src&7))
func (asm AssemblerV0) MovInvert(dest MovDest, src MovSrc) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_MOV, uint8(dest), (1<<3)|uint8(src&7))
}

// MovReverse does a Mov but reversing the order of the resulting bits.
func (asm AssemblerV0) MovReverse(dest, src SrcDest) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_MOV, dest, (2<<3)|uint8(src&7))
func (asm AssemblerV0) MovReverse(dest MovDest, src MovSrc) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_MOV, uint8(dest), (2<<3)|uint8(src&7))
}

// IRQSet sets the IRQ flag selected by irqIndex argument.
Expand All @@ -185,27 +185,27 @@ func (asm AssemblerV0) IRQClear(relative bool, irqIndex uint8) instructionV0 {
}

// Set writes an immediate value Data in range 0..31 to Destination.
func (asm AssemblerV0) Set(dest SrcDest, value uint8) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_SET, dest, value)
func (asm AssemblerV0) Set(dest SetDest, value uint8) instructionV0 {
return asm.instrSrcDest(_INSTR_BITS_SET, uint8(dest), value)
}

// Nop is pseudo instruction that lasts a single PIO cycle. Usually used for timings.
func (asm AssemblerV0) Nop() instructionV0 { return asm.Mov(SrcDestY, SrcDestY) }
func (asm AssemblerV0) Nop() instructionV0 { return asm.Mov(MovDestY, MovSrcY) }

// InstrKind is a enum for the PIO instruction type. It only represents the kind of
// instruction. It cannot store the arguments.
type InstrKind uint8

const (
InstrJMP InstrKind = iota
InstrWAIT
InstrIN
InstrOUT
InstrPUSH
InstrPULL
InstrMOV
InstrIRQ
InstrSET
InstrJMP InstrKind = iota // jmp
InstrWAIT // wait
InstrIN // in
InstrOUT // out
InstrPUSH // push
InstrPULL // pull
InstrMOV // mov
InstrIRQ // irq
InstrSET // set
)

// This file contains the primitives for creating instructions dynamically
Expand All @@ -224,20 +224,68 @@ const (
_INSTR_BITS_Msk = 0xe000
)

type SrcDest uint8
// OutDest encodes Out instruction data destination.
type OutDest uint8

const (
SrcDestPins SrcDest = 0
SrcDestX SrcDest = 1
SrcDestY SrcDest = 2
SrcDestNull SrcDest = 3
SrcDestPinDirs SrcDest = 4
SrcDestExecMov SrcDest = 4
SrcDestStatus SrcDest = 5
SrcDestPC SrcDest = 5
SrcDestISR SrcDest = 6
SrcDestOSR SrcDest = 7
SrcExecOut SrcDest = 7
OutDestPins OutDest = 0b000 // pins
OutDestX OutDest = 0b001 // x
OutDestY OutDest = 0b010 // y
OutDestNull OutDest = 0b011 // null
OutDestPindirs OutDest = 0b100 // pindirs
OutDestPC OutDest = 0b101 // pc
OutDestISR OutDest = 0b110 // isr
OutDestExec OutDest = 0b111 // exec
)

// InSrc encodes In instruction data source.
type InSrc uint8

const (
InSrcPins InSrc = 0b000 // pins
InSrcX InSrc = 0b001 // x
InSrcY InSrc = 0b010 // y
InSrcNull InSrc = 0b011 // null
InSrcISR InSrc = 0b110 // isr
InSrcOSR InSrc = 0b111 // osr
)

// SetDest encodes Set instruction data destination.
type SetDest uint8

const (
SetDestPins SetDest = 0b000 // pins
SetDestX SetDest = 0b001 // x
SetDestY SetDest = 0b010 // y
SetDestPindirs SetDest = 0b100 // pindirs
)

// MovSrc encodes Mov instruction data source.
type MovSrc uint8

const (
MovSrcPins MovSrc = 0b000 // pins
MovSrcX MovSrc = 0b001 // x
MovSrcY MovSrc = 0b010 // y
MovSrcNull MovSrc = 0b011 // null
MovSrcStatus MovSrc = 0b101 // status
MovSrcISR MovSrc = 0b110 // isr
MovSrcOSR MovSrc = 0b111 // osr
)

// MovDest encodes Mov instruction data destination.
type MovDest uint8

const (
MovDestPins MovDest = 0b000 // pins
MovDestX MovDest = 0b001 // x
MovDestY MovDest = 0b010 // y
// MovDestPindirs was introduced in PIO version 1. Not available on RP2040
MovDestPindirs MovDest = 0b011 // pindirs
MovDestExec MovDest = 0b100 // exec
MovDestPC MovDest = 0b101 // pc
MovDestISR MovDest = 0b110 // isr
MovDestOSR MovDest = 0b111 // osr
)

type JmpCond uint8
Expand Down Expand Up @@ -266,16 +314,18 @@ const (
type IRQIndexMode uint8

const (
// Direct: the three LSBs are used directly to index the IRQ flags in this PIO block.
IRQDirect IRQIndexMode = 0b00 // direct
// Prev: the instruction references an IRQ flag from the next-lower-numbered PIO in the system, wrapping to
// the highest-numbered PIO if this is PIO0. Available on RP2350 only.
IRQPrev IRQIndexMode = 0b01
IRQPrev IRQIndexMode = 0b01 // prev
// Rel: the state machine ID (0…3) is added to the IRQ flag index, by way of modulo-4 addition on the two
// LSBs. For example, state machine 2 with a flag value of '0x11' will wait on flag 3, and a flag value of '0x13' will
// wait on flag 1. This allows multiple state machines running the same program to synchronise with each other.
IRQRel IRQIndexMode = 0b10
IRQRel IRQIndexMode = 0b10 // rel
// Next: the instruction references an IRQ flag from the next-higher-numbered PIO in the system, wrapping to
// PIO0 if this is the highest-numbered PIO. Available on RP2350 only.
IRQNext IRQIndexMode = 0b11
IRQNext IRQIndexMode = 0b11 // next
)

// EncodeInstr encodes an arbitrary PIO instruction with the given arguments.
Expand Down
131 changes: 131 additions & 0 deletions rp2-pio/instrv1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package pio

// AssemblerV1 provides a fluent API for programming PIO
// within the Go language for PIO version 1 (RP2350).
// Most logic is shared with [AssemblerV0].
type AssemblerV1 struct {
SidesetBits uint8
}

func (asm AssemblerV1) v0() AssemblerV0 {
return AssemblerV0{
SidesetBits: asm.SidesetBits,
}
}

// Jmp instruction unchanged from [AssemblerV0.Jmp].
func (asm AssemblerV1) Jmp(addr uint8, cond JmpCond) instructionV0 { return asm.v0().Jmp(addr, cond) }

// WaitGPIO instruction unchanged from [AssemblerV0.WaitGPIO].
func (asm AssemblerV1) WaitGPIO(polarity bool, pin uint8) instructionV0 {
return asm.v0().WaitGPIO(polarity, pin)
}

// WaitIRQ instruction unchanged from [AssemblerV0.WaitIRQ].
func (asm AssemblerV1) WaitIRQ(polarity bool, relative bool, irqindex uint8) instructionV0 {
return asm.v0().WaitIRQ(polarity, relative, irqindex)
}

// WaitPin instruction unchanged from [AssemblerV0.WaitPin].
func (asm AssemblerV1) WaitPin(polarity bool, pin uint8) instructionV0 {
return asm.v0().WaitPin(polarity, pin)
}

// WaitJmpPin waits on the pin indexed by the PINCTRL_JMP_PIN configuration, plus an Index in the range 0-3, all
// modulo 32. Other values of Index are reserved.
func (asm AssemblerV1) WaitJmpPin(polarity bool, pin uint8) instructionV0 {
flag := boolAsU8(polarity) << 2
return asm.v0().instrArgs(_INSTR_BITS_WAIT, 0b11|flag, pin)
}

// In instruction unchanged from [AssemblerV0.In].
func (asm AssemblerV1) In(src InSrc, value uint8) instructionV0 {
return asm.v0().In(src, value)
}

// Out instruction unchanged from [AssemblerV0.Out].
func (asm AssemblerV1) Out(dest OutDest, value uint8) instructionV0 {
return asm.v0().Out(dest, value)
}

// Push instruction unchanged from [AssemblerV0.Push].
func (asm AssemblerV1) Push(ifFull bool, block bool) instructionV0 {
return asm.v0().Push(ifFull, block)
}

// Pull instruction unchanged from [AssemblerV0.Pull].
func (asm AssemblerV1) Pull(ifEmpty bool, block bool) instructionV0 {
return asm.v0().Pull(ifEmpty, block)
}

// Mov in version 1 of PIO works identically to version 0 but adding following new functionality.
// - Added Pindirs as destination for MOV: This allows changing the direction of all OUT-mapped pins with a single instruction: MOV PINDIRS, NULL or MOV
// PINDIRS, ~NULL
// - Adds SM IRQ flags as a source for MOV x, STATUS. This allows branching (as well as blocking) on the assertion of SM IRQ flags.
// - Adds the FJOIN_RX_GET FIFO mode. A new MOV encoding reads any of the four RX FIFO storage registers into OSR.
// - New FJOIN_RX_PUT FIFO mode. A new MOV encoding writes the ISR into any of the four RX FIFO storage registers.
func (asm AssemblerV1) Mov(dest MovDest, src MovSrc) instructionV0 {
return asm.v0().Mov(dest, src)
}

// MovInvert is [AssemblerV0.MovInvert] unchanged but with available [AssemblerV1.Mov] functionality.
func (asm AssemblerV1) MovInvert(dest MovDest, src MovSrc) instructionV0 {
return asm.v0().MovInvert(dest, src)
}

// MovReverse is [AssemblerV0.MovReverse] unchanged but with available [AssemblerV1.Mov] functionality.
func (asm AssemblerV1) MovReverse(dest MovDest, src MovSrc) instructionV0 {
return asm.v0().MovReverse(dest, src)
}

// MovOSRFromRx reads the selected RX FIFO entry into the OSR. The PIO state machine can read the FIFO entries in any order, indexed
// either by the Y register, or an immediate Index in the instruction. Requires the SHIFTCTRL_FJOIN_RX_GET configuration field
// to be set, otherwise its operation is undefined.
// - If idxByImmediate (index by immediate) is set, the RX FIFO’s registers are indexed by the two least-significant bits of the Index
// operand. Otherwise, they are indexed by the two least-significant bits of the Y register. When IdxI is clear, all non-zero
// values of Index are reserved encodings, and their operation is undefined.
func (asm AssemblerV1) MovOSRFromRx(idxByImmediate bool, RxFifoIndex uint8) instructionV0 {
instr := _INSTR_BITS_MOV | (0b1001 << 4) | (uint16(boolAsU8(idxByImmediate) << 3)) | uint16(RxFifoIndex)&0b111
return asm.v0().instr(instr)
}

// MovISRToRx writes the ISR to a selected RX FIFO entry. The state machine can write the RX FIFO entries in any order, indexed either
// by the Y register, or an immediate Index in the instruction. Requires the SHIFTCTRL_FJOIN_RX_PUT configuration field to be
// set, otherwise its operation is undefined. The FIFO configuration can be specified for the program via the .fifo directive
// (see pioasm_fifo).
// - If idxByImmediate (index by immediate) is set, the RX FIFO’s registers are indexed by the two least-significant bits of the Index
// operand. Otherwise, they are indexed by the two least-significant bits of the Y register. When IdxI is clear, all non-zero
// values of Index are reserved encodings, and their operation is undefined.
func (asm AssemblerV1) MovISRToRx(idxByImmediate bool, RxFifoIndex uint8) instructionV0 {
instr := _INSTR_BITS_MOV | (0b1000 << 4) | (uint16(boolAsU8(idxByImmediate) << 3)) | uint16(RxFifoIndex)&0b111
return asm.v0().instr(instr)
}

// Set instruction unchanged from [AssemblerV0.Set].
func (asm AssemblerV1) Set(dest SetDest, value uint8) instructionV0 {
return asm.v0().Set(dest, value)
}

// IRQSet sets the IRQ flag selected by irqIndex.
func (asm AssemblerV1) IRQSet(irqIndex uint8, idxMode IRQIndexMode) instructionV0 {
return asm.irq(false, false, irqIndex, idxMode)
}

// IRQClear clears the IRQ flag selected by irqIndex argument. See [AssemblerV1.IRQSet].
func (asm AssemblerV1) IRQClear(irqIndex uint8, idxMode IRQIndexMode) instructionV0 {
return asm.irq(true, false, irqIndex, idxMode)
}

// IRQWait sets the IRQ flag selected by irqIndex and waits for it to be cleared before proceeding.
// If Wait is set, Delay cycles do not begin until after the wait period elapses.
func (asm AssemblerV1) IRQWait(irqIndex uint8, idxMode IRQIndexMode) instructionV0 {
return asm.irq(false, true, irqIndex, idxMode)
}

func (asm AssemblerV1) irq(clear, wait bool, irqIndex uint8, idxMode IRQIndexMode) instructionV0 {
instr := _INSTR_BITS_IRQ | uint16(boolAsU8(clear))<<6 | uint16(boolAsU8(wait))<<6 | uint16(idxMode)<<3 | uint16(irqIndex&0b111)
return asm.v0().instr(instr)
}

// Nop instruction unchanged from [AssemblerV0.Nop].
func (asm AssemblerV1) Nop() instructionV0 { return asm.v0().Nop() }
2 changes: 1 addition & 1 deletion rp2-pio/piolib/parallel.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func NewParallel(sm pio.StateMachine, cfg ParallelConfig) (*Parallel, error) {
SidesetBits: sideSetBitCount,
}
var program = [3]uint16{
asm.Out(pio.SrcDestPins, cfg.BusWidth).Side(0).Encode(), // 0: out pins, <npins> side 0
asm.Out(pio.OutDestPins, cfg.BusWidth).Side(0).Encode(), // 0: out pins, <npins> side 0
asm.Nop().Side(1).Encode(), // 1: nop side 1
asm.Nop().Side(0).Encode(), // 2: nop side 0
}
Expand Down
2 changes: 1 addition & 1 deletion rp2-pio/piolib/spi3w.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func (spi *SPI3w) prepTx(readbits, writebits uint32) {
spi.sm.SetX(writebits)
spi.sm.SetY(readbits)
var asm pio.AssemblerV0
spi.sm.Exec(asm.Set(pio.SrcDestPinDirs, 1).Encode()) // Set Pindir out.
spi.sm.Exec(asm.Set(pio.SetDestPindirs, 1).Encode()) // Set Pindir out.
spi.sm.Jmp(spi.offset+spi3wWrapTarget, pio.JmpAlways)

spi.sm.SetEnabled(true)
Expand Down
Loading