Skip to content

Commit 4f52530

Browse files
committed
tidy up SPI3w
1 parent 78636b6 commit 4f52530

File tree

5 files changed

+241
-62
lines changed

5 files changed

+241
-62
lines changed

rp2-pio/instr.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ func EncodeNOP() uint16 {
175175
return EncodeMov(SrcDestY, SrcDestY)
176176
}
177177

178+
// encodeTRAP encodes a trap instruction. It must be stored at the argument offset.
179+
func encodeTRAP(trapOffset uint8) uint16 {
180+
return EncodeJmp(trapOffset, JmpAlways)
181+
}
182+
178183
// ClkDivFromPeriod calculates the CLKDIV register values
179184
// to reach a given StateMachine cycle period given the RP2040 CPU frequency.
180185
// period is expected to be in nanoseconds. freq is expected to be in Hz.

rp2-pio/pio.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ var (
2727
errStateMachineClaimed = errors.New("pio: state machine already claimed")
2828
)
2929

30-
const badStateMachineIndex = "invalid state machine index"
31-
const badPIO = "invalid PIO"
30+
const (
31+
badStateMachineIndex = "invalid state machine index"
32+
badPIO = "invalid PIO"
33+
badProgramBounds = "invalid program bounds"
34+
)
3235

3336
// PIO represents one of the two PIO peripherals in the RP2040
3437
type PIO struct {
@@ -165,6 +168,21 @@ func (pio *PIO) findOffsetForProgram(instructions []uint16, origin int8) int8 {
165168
return -1
166169
}
167170

171+
// ClearProgramSection clears a contiguous section of the PIO's program memory.
172+
// To clear all program memory use ClearProgramSection(0, 32).
173+
func (pio *PIO) ClearProgramSection(offset, len uint8) {
174+
if offset+len > 32 { // 32 instructions max
175+
panic(badProgramBounds)
176+
}
177+
hw := pio.HW()
178+
for i := offset; i < offset+len; i++ {
179+
// We encode trap instructions to prevent undefined behaviour if
180+
// a state machine is currently using the program memory.
181+
hw.INSTR_MEM[i].Set(uint32(encodeTRAP(offset)))
182+
}
183+
pio.usedSpaceMask &^= uint32((1<<len)-1) << offset
184+
}
185+
168186
type statemachineHW struct {
169187
CLKDIV volatile.Register32 // 0xC8 for SM0
170188
EXECCTRL volatile.Register32 // 0xCC for SM0

rp2-pio/piolib/all_generate.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const timeoutRetries = 1023
1010
var (
1111
errTimeout = errors.New("piolib:timeout")
1212
errBusy = errors.New("piolib:busy")
13+
14+
errDMAUnavail = errors.New("piolib:DMA channel unavailable")
1315
)
1416

1517
//go:generate pioasm -o go parallel8.pio parallel8_pio.go

rp2-pio/piolib/dma.go

Lines changed: 135 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,89 @@ import (
66
"device/rp"
77
"runtime/volatile"
88
"unsafe"
9+
10+
pio "github.com/tinygo-org/pio/rp2-pio"
911
)
1012

13+
var _DMA = &dmaArbiter{}
14+
15+
type dmaArbiter struct {
16+
claimedChannels uint16
17+
}
18+
19+
// ClaimChannel returns a DMA channel that can be used for DMA transfers.
20+
func (arb *dmaArbiter) ClaimChannel() (channel dmaChannel, ok bool) {
21+
for i := uint8(0); i < 12; i++ {
22+
ch := arb.Channel(i)
23+
if ch.Claim() {
24+
return ch, true
25+
}
26+
}
27+
return dmaChannel{}, false
28+
}
29+
30+
func (arb *dmaArbiter) Channel(channel uint8) dmaChannel {
31+
if channel > 11 {
32+
panic("invalid DMA channel")
33+
}
34+
// DMA channels usable on the RP2040. 12 in total.
35+
var dmaChannels = (*[12]dmaChannelHW)(unsafe.Pointer(rp.DMA))
36+
return dmaChannel{
37+
hw: &dmaChannels[channel],
38+
arb: arb,
39+
idx: channel,
40+
}
41+
}
42+
1143
type dmaChannel struct {
12-
hw *dmaChannelHW
13-
channel uint8
44+
hw *dmaChannelHW
45+
arb *dmaArbiter
46+
idx uint8
47+
}
48+
49+
// Claim claims the DMA channel for use by a peripheral and returns if it succeeded in claiming the channel.
50+
func (ch dmaChannel) Claim() bool {
51+
ch.mustValid()
52+
if ch.IsClaimed() {
53+
return false
54+
}
55+
ch.arb.claimedChannels |= 1 << ch.idx
56+
return true
57+
}
58+
59+
// Unclaim releases the DMA channel so it can be used by other peripherals.
60+
// It does not check if the channel is currently claimed; it force-unclaims the channel.
61+
func (ch dmaChannel) Unclaim() {
62+
ch.mustValid()
63+
ch.arb.claimedChannels &^= 1 << ch.idx
64+
}
65+
66+
// IsClaimed returns true if the DMA channel is currently claimed through software.
67+
func (ch dmaChannel) IsClaimed() bool {
68+
ch.mustValid()
69+
return ch.arb.claimedChannels&(1<<ch.idx) != 0
70+
}
71+
72+
// IsValid returns true if the DMA channel was created successfully.
73+
func (ch dmaChannel) IsValid() bool {
74+
return ch.hw != nil && ch.arb == _DMA
75+
}
76+
77+
// ChannelIndex returns the channel number of the DMA channel. In range 0..11.
78+
func (ch dmaChannel) ChannelIndex() uint8 { return ch.idx }
79+
80+
// HW returns the hardware registers for this DMA channel.
81+
func (ch dmaChannel) HW() *dmaChannelHW { return ch.hw }
82+
83+
func (ch dmaChannel) Init(cfg dmaChannelConfig) {
84+
ch.mustValid()
85+
ch.HW().CTRL_TRIG.Set(cfg.CTRL)
86+
}
87+
88+
func (ch dmaChannel) mustValid() {
89+
if !ch.IsValid() {
90+
panic("use of uninitialized DMA channel")
91+
}
1492
}
1593

1694
// Single DMA channel. See rp.DMA_Type.
@@ -30,13 +108,17 @@ const (
30108
spi1DMAChannel
31109
)
32110

33-
// DMA channels usable on the RP2040.
34-
var dmaChannels = (*[12]dmaChannelHW)(unsafe.Pointer(rp.DMA))
111+
// dmaPIO_TREQ returns the Tx DREQ signal for a PIO state machine.
112+
func dmaPIO_TxDREQ(sm pio.StateMachine) uint32 {
113+
return _DREQ_PIO0_TX0 + uint32(sm.PIO().BlockIndex())*8 + uint32(sm.StateMachineIndex())
114+
}
35115

36-
func getDMAChannel(channel uint8) *dmaChannel {
37-
return &dmaChannel{hw: &dmaChannels[channel], channel: channel}
116+
// dmaPIO_TREQ returns the Rx DREQ signal for a PIO state machine.
117+
func dmaPIO_RxDREQ(sm pio.StateMachine) uint32 {
118+
return dmaPIO_TxDREQ(sm) + 4
38119
}
39120

121+
// 2.5.3.1. System DREQ Table. Note: Another caveat is that multiple channels should not be connected to the same DREQ.
40122
const (
41123
_DREQ_PIO0_TX0 = 0x0
42124
_DREQ_PIO0_TX1 = 0x1
@@ -80,37 +162,9 @@ const (
80162
_DREQ_XIP_SSIRX = 0x27
81163
)
82164

83-
type dmaTxSize uint32
84-
85-
const (
86-
dmaTxSize8 dmaTxSize = iota
87-
dmaTxSize16
88-
dmaTxSize32
89-
)
90-
91-
type dmaChannelConfig struct {
92-
CTRL uint32
93-
}
94-
95-
func getDefaultDMAConfig(channel uint32) (cc dmaChannelConfig) {
96-
cc.setRing(false, 0)
97-
cc.setBSwap(false)
98-
cc.setIRQQuiet(false)
99-
cc.setWriteIncrement(false)
100-
cc.setSniffEnable(false)
101-
cc.setHighPriority(false)
102-
103-
cc.setChainTo(channel)
104-
cc.setTREQ_SEL(rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_PERMANENT)
105-
cc.setReadIncrement(true)
106-
cc.setTransferDataSize(dmaTxSize32)
107-
cc.setEnable(true)
108-
return cc
109-
}
110-
111-
// push32 writes each element of src slice into the memory location at dst.
112-
func (ch *dmaChannel) push32(dst *uint32, src []uint32, dreq uint32) {
113-
hw := ch.hw
165+
// Push32 writes each element of src slice into the memory location at dst.
166+
func (ch dmaChannel) Push32(dst *uint32, src []uint32, dreq uint32) {
167+
hw := ch.HW()
114168
srcPtr := uint32(uintptr(unsafe.Pointer(&src[0])))
115169
dstPtr := uint32(uintptr(unsafe.Pointer(dst)))
116170
hw.READ_ADDR.Set(srcPtr)
@@ -121,7 +175,7 @@ func (ch *dmaChannel) push32(dst *uint32, src []uint32, dreq uint32) {
121175
cc.CTRL = hw.CTRL_TRIG.Get()
122176
cc.setTREQ_SEL(dreq)
123177
cc.setTransferDataSize(dmaTxSize32)
124-
cc.setChainTo(uint32(ch.channel))
178+
cc.setChainTo(ch.idx)
125179
cc.setReadIncrement(true)
126180
cc.setWriteIncrement(false)
127181
cc.setEnable(true)
@@ -138,9 +192,9 @@ func (ch *dmaChannel) push32(dst *uint32, src []uint32, dreq uint32) {
138192
}
139193
}
140194

141-
// pull32 reads the memory location at src into dst slice, incrementing dst pointer but not src.
142-
func (ch *dmaChannel) pull32(dst []uint32, src *uint32, dreq uint32) {
143-
hw := ch.hw
195+
// Pull32 reads the memory location at src into dst slice, incrementing dst pointer but not src.
196+
func (ch dmaChannel) Pull32(dst []uint32, src *uint32, dreq uint32) {
197+
hw := ch.HW()
144198
srcPtr := uint32(uintptr(unsafe.Pointer(src)))
145199
dstPtr := uint32(uintptr(unsafe.Pointer(&dst[0])))
146200
hw.READ_ADDR.Set(srcPtr)
@@ -151,7 +205,7 @@ func (ch *dmaChannel) pull32(dst []uint32, src *uint32, dreq uint32) {
151205
cc.CTRL = hw.CTRL_TRIG.Get()
152206
cc.setTREQ_SEL(dreq)
153207
cc.setTransferDataSize(dmaTxSize32)
154-
cc.setChainTo(uint32(ch.channel))
208+
cc.setChainTo(ch.idx)
155209
cc.setReadIncrement(false)
156210
cc.setWriteIncrement(true)
157211
cc.setEnable(true)
@@ -171,13 +225,13 @@ func (ch *dmaChannel) pull32(dst []uint32, src *uint32, dreq uint32) {
171225
// abort aborts the current transfer sequence on the channel and blocks until
172226
// all in-flight transfers have been flushed through the address and data FIFOs.
173227
// After this, it is safe to restart the channel.
174-
func (ch *dmaChannel) abort() {
228+
func (ch dmaChannel) abort() {
175229
// Each bit corresponds to a channel. Writing a 1 aborts whatever transfer
176230
// sequence is in progress on that channel. The bit will remain high until
177231
// any in-flight transfers have been flushed through the address and data FIFOs.
178232
// After writing, this register must be polled until it returns all-zero.
179233
// Until this point, it is unsafe to restart the channel.
180-
chMask := uint32(1 << ch.channel)
234+
chMask := uint32(1 << ch.idx)
181235
rp.DMA.CHAN_ABORT.Set(chMask)
182236
retries := timeoutRetries
183237
for rp.DMA.CHAN_ABORT.Get()&chMask != 0 && retries > 0 {
@@ -189,8 +243,37 @@ func (ch *dmaChannel) abort() {
189243
}
190244
}
191245

192-
func (ch *dmaChannel) busy() bool {
193-
return ch.hw.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0
246+
func (ch dmaChannel) busy() bool {
247+
hw := ch.HW()
248+
return hw.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0
249+
}
250+
251+
type dmaTxSize uint32
252+
253+
const (
254+
dmaTxSize8 dmaTxSize = iota
255+
dmaTxSize16
256+
dmaTxSize32
257+
)
258+
259+
type dmaChannelConfig struct {
260+
CTRL uint32
261+
}
262+
263+
func dmaDefaultConfig(channel uint8) (cc dmaChannelConfig) {
264+
cc.setRing(false, 0)
265+
cc.setBSwap(false)
266+
cc.setIRQQuiet(false)
267+
cc.setWriteIncrement(false)
268+
cc.setSniffEnable(false)
269+
cc.setHighPriority(false)
270+
271+
cc.setChainTo(channel)
272+
cc.setTREQ_SEL(rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_PERMANENT)
273+
cc.setReadIncrement(true)
274+
cc.setTransferDataSize(dmaTxSize32)
275+
// cc.setEnable(true)
276+
return cc
194277
}
195278

196279
// Select a Transfer Request signal. The channel uses the transfer request signal
@@ -200,8 +283,8 @@ func (cc *dmaChannelConfig) setTREQ_SEL(dreq uint32) {
200283
cc.CTRL = (cc.CTRL & ^uint32(rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_Msk)) | (uint32(dreq) << rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_Pos)
201284
}
202285

203-
func (cc *dmaChannelConfig) setChainTo(chainTo uint32) {
204-
cc.CTRL = (cc.CTRL & ^uint32(rp.DMA_CH0_CTRL_TRIG_CHAIN_TO_Msk)) | (chainTo << rp.DMA_CH0_CTRL_TRIG_CHAIN_TO_Pos)
286+
func (cc *dmaChannelConfig) setChainTo(chainTo uint8) {
287+
cc.CTRL = (cc.CTRL & ^uint32(rp.DMA_CH0_CTRL_TRIG_CHAIN_TO_Msk)) | (uint32(chainTo) << rp.DMA_CH0_CTRL_TRIG_CHAIN_TO_Pos)
205288
}
206289

207290
func (cc *dmaChannelConfig) setTransferDataSize(size dmaTxSize) {
@@ -257,3 +340,7 @@ func setBitPos(cc *uint32, pos uint32, bit bool) {
257340
*cc = *cc & ^(1 << pos) // unset bit.
258341
}
259342
}
343+
344+
func ptrAs[T ~uint32](ptr *T) uint32 {
345+
return uint32(uintptr(unsafe.Pointer(ptr)))
346+
}

0 commit comments

Comments
 (0)