Skip to content

Commit 78636b6

Browse files
committed
begin adding local DMA implementation
1 parent d9206f2 commit 78636b6

File tree

3 files changed

+291
-2
lines changed

3 files changed

+291
-2
lines changed

rp2-pio/piolib/all_generate.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"runtime"
66
)
77

8+
const timeoutRetries = 1023
9+
810
var (
911
errTimeout = errors.New("piolib:timeout")
1012
errBusy = errors.New("piolib:busy")

rp2-pio/piolib/dma.go

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
//go:build rp2040
2+
3+
package piolib
4+
5+
import (
6+
"device/rp"
7+
"runtime/volatile"
8+
"unsafe"
9+
)
10+
11+
type dmaChannel struct {
12+
hw *dmaChannelHW
13+
channel uint8
14+
}
15+
16+
// Single DMA channel. See rp.DMA_Type.
17+
type dmaChannelHW struct {
18+
READ_ADDR volatile.Register32
19+
WRITE_ADDR volatile.Register32
20+
TRANS_COUNT volatile.Register32
21+
CTRL_TRIG volatile.Register32
22+
_ [12]volatile.Register32 // aliases
23+
}
24+
25+
// Static assignment of DMA channels to peripherals.
26+
// Allocating them statically is good enough for now. If lots of peripherals use
27+
// DMA, these might need to be assigned at runtime.
28+
const (
29+
spi0DMAChannel = iota
30+
spi1DMAChannel
31+
)
32+
33+
// DMA channels usable on the RP2040.
34+
var dmaChannels = (*[12]dmaChannelHW)(unsafe.Pointer(rp.DMA))
35+
36+
func getDMAChannel(channel uint8) *dmaChannel {
37+
return &dmaChannel{hw: &dmaChannels[channel], channel: channel}
38+
}
39+
40+
const (
41+
_DREQ_PIO0_TX0 = 0x0
42+
_DREQ_PIO0_TX1 = 0x1
43+
_DREQ_PIO0_TX2 = 0x2
44+
_DREQ_PIO0_TX3 = 0x3
45+
_DREQ_PIO0_RX0 = 0x4
46+
_DREQ_PIO0_RX1 = 0x5
47+
_DREQ_PIO0_RX2 = 0x6
48+
_DREQ_PIO0_RX3 = 0x7
49+
_DREQ_PIO1_TX0 = 0x8
50+
_DREQ_PIO1_TX1 = 0x9
51+
_DREQ_PIO1_TX2 = 0xa
52+
_DREQ_PIO1_TX3 = 0xb
53+
_DREQ_PIO1_RX0 = 0xc
54+
_DREQ_PIO1_RX1 = 0xd
55+
_DREQ_PIO1_RX2 = 0xe
56+
_DREQ_PIO1_RX3 = 0xf
57+
_DREQ_SPI0_TX = 0x10
58+
_DREQ_SPI0_RX = 0x11
59+
_DREQ_SPI1_TX = 0x12
60+
_DREQ_SPI1_RX = 0x13
61+
_DREQ_UART0_TX = 0x14
62+
_DREQ_UART0_RX = 0x15
63+
_DREQ_UART1_TX = 0x16
64+
_DREQ_UART1_RX = 0x17
65+
_DREQ_PWM_WRAP0 = 0x18
66+
_DREQ_PWM_WRAP1 = 0x19
67+
_DREQ_PWM_WRAP2 = 0x1a
68+
_DREQ_PWM_WRAP3 = 0x1b
69+
_DREQ_PWM_WRAP4 = 0x1c
70+
_DREQ_PWM_WRAP5 = 0x1d
71+
_DREQ_PWM_WRAP6 = 0x1e
72+
_DREQ_PWM_WRAP7 = 0x1f
73+
_DREQ_I2C0_TX = 0x20
74+
_DREQ_I2C0_RX = 0x21
75+
_DREQ_I2C1_TX = 0x22
76+
_DREQ_I2C1_RX = 0x23
77+
_DREQ_ADC = 0x24
78+
_DREQ_XIP_STREAM = 0x25
79+
_DREQ_XIP_SSITX = 0x26
80+
_DREQ_XIP_SSIRX = 0x27
81+
)
82+
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
114+
srcPtr := uint32(uintptr(unsafe.Pointer(&src[0])))
115+
dstPtr := uint32(uintptr(unsafe.Pointer(dst)))
116+
hw.READ_ADDR.Set(srcPtr)
117+
hw.WRITE_ADDR.Set(dstPtr)
118+
hw.TRANS_COUNT.Set(uint32(len(src)))
119+
// memfence
120+
var cc dmaChannelConfig
121+
cc.CTRL = hw.CTRL_TRIG.Get()
122+
cc.setTREQ_SEL(dreq)
123+
cc.setTransferDataSize(dmaTxSize32)
124+
cc.setChainTo(uint32(ch.channel))
125+
cc.setReadIncrement(true)
126+
cc.setWriteIncrement(false)
127+
cc.setEnable(true)
128+
129+
hw.CTRL_TRIG.Set(cc.CTRL)
130+
131+
retries := timeoutRetries
132+
for ch.busy() && retries > 0 {
133+
gosched()
134+
retries--
135+
}
136+
if retries == 0 {
137+
println("DMA push32 timeout")
138+
}
139+
}
140+
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
144+
srcPtr := uint32(uintptr(unsafe.Pointer(src)))
145+
dstPtr := uint32(uintptr(unsafe.Pointer(&dst[0])))
146+
hw.READ_ADDR.Set(srcPtr)
147+
hw.WRITE_ADDR.Set(dstPtr)
148+
hw.TRANS_COUNT.Set(uint32(len(dst)))
149+
// memfence
150+
var cc dmaChannelConfig
151+
cc.CTRL = hw.CTRL_TRIG.Get()
152+
cc.setTREQ_SEL(dreq)
153+
cc.setTransferDataSize(dmaTxSize32)
154+
cc.setChainTo(uint32(ch.channel))
155+
cc.setReadIncrement(false)
156+
cc.setWriteIncrement(true)
157+
cc.setEnable(true)
158+
159+
hw.CTRL_TRIG.Set(cc.CTRL)
160+
161+
retries := timeoutRetries
162+
for ch.busy() && retries > 0 {
163+
gosched()
164+
retries--
165+
}
166+
if retries == 0 {
167+
println("DMA push32 timeout")
168+
}
169+
}
170+
171+
// abort aborts the current transfer sequence on the channel and blocks until
172+
// all in-flight transfers have been flushed through the address and data FIFOs.
173+
// After this, it is safe to restart the channel.
174+
func (ch *dmaChannel) abort() {
175+
// Each bit corresponds to a channel. Writing a 1 aborts whatever transfer
176+
// sequence is in progress on that channel. The bit will remain high until
177+
// any in-flight transfers have been flushed through the address and data FIFOs.
178+
// After writing, this register must be polled until it returns all-zero.
179+
// Until this point, it is unsafe to restart the channel.
180+
chMask := uint32(1 << ch.channel)
181+
rp.DMA.CHAN_ABORT.Set(chMask)
182+
retries := timeoutRetries
183+
for rp.DMA.CHAN_ABORT.Get()&chMask != 0 && retries > 0 {
184+
gosched()
185+
retries--
186+
}
187+
if retries == 0 {
188+
println("DMA abort timeout")
189+
}
190+
}
191+
192+
func (ch *dmaChannel) busy() bool {
193+
return ch.hw.CTRL_TRIG.Get()&rp.DMA_CH0_CTRL_TRIG_BUSY != 0
194+
}
195+
196+
// Select a Transfer Request signal. The channel uses the transfer request signal
197+
// to pace its data transfer rate. Sources for TREQ signals are internal (TIMERS)
198+
// or external (DREQ, a Data Request from the system). 0x0 to 0x3a -> select DREQ n as TREQ
199+
func (cc *dmaChannelConfig) setTREQ_SEL(dreq uint32) {
200+
cc.CTRL = (cc.CTRL & ^uint32(rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_Msk)) | (uint32(dreq) << rp.DMA_CH0_CTRL_TRIG_TREQ_SEL_Pos)
201+
}
202+
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)
205+
}
206+
207+
func (cc *dmaChannelConfig) setTransferDataSize(size dmaTxSize) {
208+
cc.CTRL = (cc.CTRL & ^uint32(rp.DMA_CH0_CTRL_TRIG_DATA_SIZE_Msk)) | (uint32(size) << rp.DMA_CH0_CTRL_TRIG_DATA_SIZE_Pos)
209+
}
210+
211+
func (cc *dmaChannelConfig) setRing(write bool, sizeBits uint32) {
212+
/*
213+
static inline void channel_config_set_ring(dma_channel_config *c, bool write, uint size_bits) {
214+
assert(size_bits < 32);
215+
c->ctrl = (c->ctrl & ~(DMA_CH0_CTRL_TRIG_RING_SIZE_BITS | DMA_CH0_CTRL_TRIG_RING_SEL_BITS)) |
216+
(size_bits << DMA_CH0_CTRL_TRIG_RING_SIZE_LSB) |
217+
(write ? DMA_CH0_CTRL_TRIG_RING_SEL_BITS : 0);
218+
}
219+
*/
220+
cc.CTRL = (cc.CTRL & ^uint32(rp.DMA_CH0_CTRL_TRIG_RING_SIZE_Msk)) |
221+
(sizeBits << rp.DMA_CH0_CTRL_TRIG_RING_SIZE_Pos)
222+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_RING_SEL_Pos, write)
223+
}
224+
225+
func (cc *dmaChannelConfig) setReadIncrement(incr bool) {
226+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_INCR_READ_Pos, incr)
227+
}
228+
229+
func (cc *dmaChannelConfig) setWriteIncrement(incr bool) {
230+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_INCR_WRITE_Pos, incr)
231+
}
232+
233+
func (cc *dmaChannelConfig) setBSwap(bswap bool) {
234+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_BSWAP_Pos, bswap)
235+
}
236+
237+
func (cc *dmaChannelConfig) setIRQQuiet(irqQuiet bool) {
238+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_IRQ_QUIET_Pos, irqQuiet)
239+
}
240+
241+
func (cc *dmaChannelConfig) setHighPriority(highPriority bool) {
242+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_Pos, highPriority)
243+
}
244+
245+
func (cc *dmaChannelConfig) setEnable(enable bool) {
246+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_EN_Pos, enable)
247+
}
248+
249+
func (cc *dmaChannelConfig) setSniffEnable(sniffEnable bool) {
250+
setBitPos(&cc.CTRL, rp.DMA_CH0_CTRL_TRIG_SNIFF_EN_Pos, sniffEnable)
251+
}
252+
253+
func setBitPos(cc *uint32, pos uint32, bit bool) {
254+
if bit {
255+
*cc = *cc | (1 << pos)
256+
} else {
257+
*cc = *cc & ^(1 << pos) // unset bit.
258+
}
259+
}

rp2-pio/piolib/spi3w.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,36 @@ func NewSPI3(sm pio.StateMachine, dio, clk machine.Pin, baud uint32) (*SPI3w, er
6262

6363
func (spi *SPI3w) CmdRead(cmd uint32, r []uint32) error {
6464
spi.sm.SetEnabled(false)
65-
// writeBits := 31
66-
// readBits := len(r)*32 + 32 - 1
65+
const writeBits = 31
66+
readBits := len(r)*32 + 32 - 1
6767

68+
spi.sm.SetX(uint32(readBits))
69+
spi.sm.SetY(uint32(writeBits))
70+
spi.sm.Exec(pio.EncodeSet(pio.SrcDestPinDirs, 1)) // Set Pindir out.
71+
spi.sm.Jmp(spi.offset+spi3wWrapTarget, pio.JmpAlways)
72+
73+
spi.sm.SetEnabled(true)
74+
75+
spi.sm.TxPut(cmd)
76+
77+
return spi.read(r)
78+
}
79+
80+
func (spi *SPI3w) read(r []uint32) error {
81+
i := 0
82+
retries := timeoutRetries
83+
for i < len(r) && retries > 0 {
84+
if spi.sm.IsRxFIFOEmpty() {
85+
gosched()
86+
retries--
87+
continue
88+
}
89+
r[i] = spi.sm.RxGet()
90+
spi.sm.TxPut(r[i])
91+
i++
92+
}
93+
if retries <= 0 {
94+
return errTimeout
95+
}
6896
return nil
6997
}

0 commit comments

Comments
 (0)