Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

Commit

Permalink
DMA driven PWM implementation (#193)
Browse files Browse the repository at this point in the history
Implements PWM via two DMA controlBlock that sets and resets a GPIO.
  • Loading branch information
simokawa authored and maruel committed Dec 18, 2017
1 parent d337762 commit 5388a53
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 47 deletions.
108 changes: 84 additions & 24 deletions host/bcm283x/dma.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ type controlBlock struct {
//
// dreq can be dmaFire, dmaPwm, dmaPcmTx, etc. waits is additional wait state
// between clocks.
func (c *controlBlock) initBlock(srcAddr, dstAddr, l uint32, srcIO, dstIO bool, dreq dmaTransferInfo, waits int) error {
func (c *controlBlock) initBlock(srcAddr, dstAddr, l uint32, srcIO, dstIO, srcInc, dstInc bool, dreq dmaTransferInfo, waits int) error {
if srcIO && dstIO {
return errors.New("only one of src and dst can be I/O")
}
Expand All @@ -444,27 +444,32 @@ func (c *controlBlock) initBlock(srcAddr, dstAddr, l uint32, srcIO, dstIO bool,
if srcAddr == 0 {
t |= dmaSrcIgnore
c.srcAddr = 0
} else if srcIO {
// Memory mapped register
c.srcAddr = physToBus(srcAddr)
// BUG: EXPERIMENTING.
//t |= dmaSrcInc
//c.srcAddr = physToUncachedPhys(srcAddr)
} else {
// Normal memory
t |= dmaSrcInc
c.srcAddr = physToUncachedPhys(srcAddr)
if srcIO {
// Memory mapped register
c.srcAddr = physToBus(srcAddr)
} else {
// Normal memory
c.srcAddr = physToUncachedPhys(srcAddr)
}
if srcInc {
t |= dmaSrcInc
}
}
if dstAddr == 0 {
t |= dmaDstIgnore
c.dstAddr = 0
} else if dstIO {
// Memory mapped register
c.dstAddr = physToBus(dstAddr)
} else {
// Normal memory
t |= dmaDstInc
c.dstAddr = physToUncachedPhys(dstAddr)
if dstIO {
// Memory mapped register
c.dstAddr = physToBus(dstAddr)
} else {
// Normal memory
c.dstAddr = physToUncachedPhys(dstAddr)
}
if dstInc {
t |= dmaDstInc
}
}
if dreq != dmaFire {
// dmaSrcDReq |
Expand Down Expand Up @@ -508,15 +513,12 @@ func (d *dmaChannel) isAvailable() bool {
//
// It doesn't clear the local controlBlock cached values.
func (d *dmaChannel) reset() {
// Make sure nothing is happening.
d.cs = dmaReset
// Clear bits if needed.
d.cs = dmaEnd | dmaInterrupt
// Clear values and error bits.
// TODO(simokawa): Test if it works as expected.
d.cs = dmaInterrupt
d.cbAddr = 0
d.nextCB = 0
d.debug = dmaReadError | dmaFIFOError | dmaReadLastNotSetError
d.cs = 0
}

// startIO initializes the DMA channel to start a transmission.
Expand Down Expand Up @@ -642,6 +644,53 @@ func runIO(pCB pmem.Mem, liteOk bool) error {
return ch.wait()
}

func allocateCB(size int) ([]controlBlock, *videocore.Mem, error) {
buf, err := videocore.Alloc((size + 0xFFF) &^ 0xFFF)
if err != nil {
return nil, nil, err
}
var cb []controlBlock
if err := buf.AsPOD(&cb); err != nil {
buf.Close()
return nil, nil, err
}
return cb, buf, nil
}

func startPWMbyDMA(p *Pin, rng, data uint32) (*dmaChannel, *videocore.Mem, error) {
cb, buf, err := allocateCB(4096)
if err != nil {
return nil, nil, err
}
u := buf.Uint32()
cbBytes := uint32(32)
offsetBytes := cbBytes * 2
u[offsetBytes/4] = uint32(1) << uint(p.number&31)
physBuf := uint32(buf.PhysAddr())
physBit := physBuf + offsetBytes
dest := [2]uint32{
gpioBaseAddr + 0x28 + 4*uint32(p.number/32), // clear
gpioBaseAddr + 0x1C + 4*uint32(p.number/32), // set
}
waits := 0
// High
cb[0].initBlock(physBit, dest[1], data*4, false, true, false, false, dmaPWM, waits)
cb[0].nextCB = physBuf + cbBytes
// Low
cb[1].initBlock(physBit, dest[0], (rng-data)*4, false, true, false, false, dmaPWM, waits)
cb[1].nextCB = physBuf // Loop back to cb[0]

// OK with lite channels.
_, ch := pickChannel()
if ch == nil {
buf.Close()
return nil, nil, errors.New("bcm283x-dma: no channel available")
}
ch.startIO(physBuf)

return ch, buf, nil
}

// physToUncachedPhys returns the uncached physical memory address backing a
// physical memory address.
//
Expand Down Expand Up @@ -696,16 +745,16 @@ func smokeTest() error {
// process startup, which may cause undesirable glitches.

// Initializes the PWM clock right away to 1MHz.
_, waits, err := setPWMClockSource(1000000)
_, waits, err := setPWMClockSource(1000000, 10)
if err != nil {
return err
}
if err := cb.initBlock(uint32(pSrc), uint32(pDst)+holeSize, size-2*holeSize, false, false, dmaPWM, waits); err != nil {
if err := cb.initBlock(uint32(pSrc), uint32(pDst)+holeSize, size-2*holeSize, false, false, true, true, dmaPWM, waits); err != nil {
return err
}
} else {
// Use maximum performance.
if err := cb.initBlock(uint32(pSrc), uint32(pDst)+holeSize, size-2*holeSize, false, false, dmaFire, 0); err != nil {
if err := cb.initBlock(uint32(pSrc), uint32(pDst)+holeSize, size-2*holeSize, false, false, true, true, dmaFire, 0); err != nil {
return err
}
}
Expand Down Expand Up @@ -763,6 +812,17 @@ func (d *driverDMA) Close() error {
return nil
}

func resetDMA(ch int) error {
if ch < len(dmaMemory.channels) {
dmaMemory.channels[ch].reset()
} else if ch == 15 {
dmaChannel15.reset()
} else {
return fmt.Errorf("Invalid dma channel %d.", ch)
}
return nil
}

func init() {
if isArm {
periph.MustRegister(&driverDMA{})
Expand Down
26 changes: 13 additions & 13 deletions host/bcm283x/dma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,45 +51,45 @@ func TestDmaStride_String(t *testing.T) {

func TestControlBlock(t *testing.T) {
c := controlBlock{}
if c.initBlock(0, 0, 0, true, true, dmaFire, 0) == nil {
if c.initBlock(0, 0, 0, true, true, false, false, dmaFire, 0) == nil {
t.Fatal("can't set both")
}
if c.initBlock(0, 0, 0, false, false, dmaFire, 0) == nil {
if c.initBlock(0, 0, 0, false, false, true, true, dmaFire, 0) == nil {
t.Fatal("need at least one addr")
}
if c.initBlock(0, 1, 0, true, false, dmaFire, 0) == nil {
if c.initBlock(0, 1, 0, true, false, false, true, dmaFire, 0) == nil {
t.Fatal("srcIO requires srcAddr")
}
if c.initBlock(1, 0, 0, false, true, dmaFire, 0) == nil {
if c.initBlock(1, 0, 0, false, true, true, false, dmaFire, 0) == nil {
t.Fatal("dstIO requires dstAddr")
}
if c.initBlock(1, 1, 0, false, false, dmaSrcIgnore, 0) == nil {
if c.initBlock(1, 1, 0, false, false, true, true, dmaSrcIgnore, 0) == nil {
t.Fatal("must not specify anything other than clock source")
}
if c.initBlock(1, 1, 0, false, false, dmaFire, 100) == nil {
if c.initBlock(1, 1, 0, false, false, true, true, dmaFire, 100) == nil {
t.Fatal("dstIO requires dstAddr")
}
if c.initBlock(1, 1, 0, false, false, dmaFire, 1) == nil {
if c.initBlock(1, 1, 0, false, false, true, true, dmaFire, 1) == nil {
t.Fatal("dmaFire can't use waits")
}

if err := c.initBlock(1, 0, 0, false, false, dmaFire, 0); err != nil {
if err := c.initBlock(1, 0, 0, false, false, true, true, dmaFire, 0); err != nil {
t.Fatal(err)
}
if err := c.initBlock(0, 1, 0, false, false, dmaFire, 0); err != nil {
if err := c.initBlock(0, 1, 0, false, false, true, true, dmaFire, 0); err != nil {
t.Fatal(err)
}
if err := c.initBlock(1, 0, 0, true, false, dmaFire, 0); err != nil {
if err := c.initBlock(1, 0, 0, true, false, false, true, dmaFire, 0); err != nil {
t.Fatal(err)
}
if err := c.initBlock(0, 1, 0, false, true, dmaPCMTX, 0); err != nil {
if err := c.initBlock(0, 1, 0, false, true, true, false, dmaPCMTX, 0); err != nil {
t.Fatal(err)
}
}

func TestControlBlockGo_String(t *testing.T) {
c := controlBlock{}
if err := c.initBlock(0, 1, 0, false, true, dmaPCMTX, 0); err != nil {
if err := c.initBlock(0, 1, 0, false, true, false, false, dmaPCMTX, 0); err != nil {
t.Fatal(err)
}
expected := "{\n transferInfo: NoWideBursts|SrcIgnore|DstDReq|WaitResp|PCMTX,\n srcAddr: 0x0,\n dstAddr: 0x7e000001,\n txLen: 0,\n stride: 0x0,\n nextCB: 0x0,\n}"
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestDmaChannel_GoString(t *testing.T) {
d := dmaChannel{}
d.reset()
d.startIO(0)
expected := "{\n cs: WaitForOutstandingWrites|Active|pp8|p8,\n cbAddr: 0x0,\n transferInfo: Fire,\n srcAddr: 0x0,\n dstAddr: 0x0,\n txLen: 0,\n stride: 0x0,\n nextCB: 0x0,\n debug: ReadError|FIFOError|ReadLastNotSetError,\n reserved: {...},\n}"
expected := "{\n cs: WaitForOutstandingWrites|Active|pp8|p8,\n cbAddr: 0x0,\n transferInfo: Fire,\n srcAddr: 0x0,\n dstAddr: 0x0,\n txLen: 0,\n stride: 0x0,\n nextCB: 0x0,\n debug: 0,\n reserved: {...},\n}"
if s := d.GoString(); s != expected {
t.Fatalf("%q", s)
}
Expand Down
13 changes: 5 additions & 8 deletions host/bcm283x/pwm.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func (p *pwmMap) reset() {
// It may select an higher frequency than the one requested.
//
// Other potentially good clock sources are PCM, SPI and UART.
func setPWMClockSource(hz uint64) (uint64, int, error) {
func setPWMClockSource(hz uint64, div uint32) (uint64, int, error) {
if pwmMemory == nil {
return 0, 0, errors.New("subsystem PWM not initialized")
}
Expand All @@ -229,22 +229,19 @@ func setPWMClockSource(hz uint64) (uint64, int, error) {
}
actual, divs, err := clockMemory.pwm.set(hz, dmaWaitcyclesMax+1)
if err == nil {
pwmMemory.ctl = 0
Nanospin(10 * time.Microsecond)
pwmMemory.status = pwmStatusMask
Nanospin(10 * time.Microsecond)
// It acts as a clock multiplier, since this amount of data is sent per
// clock tick.
pwmMemory.rng1 = 10 // 32?
pwmMemory.rng1 = uint32(divs) * div
Nanospin(10 * time.Microsecond)
// Periph data (?)

// Use low priority.
pwmMemory.dmaCfg = pwmDMAEnable | pwmDMACfg(15<<pwmPanicShift|15)
Nanospin(10 * time.Microsecond)
pwmMemory.ctl = pwmClearFIFO
pwmMemory.ctl |= pwmClearFIFO
Nanospin(10 * time.Microsecond)
pwmMemory.ctl = pwm1UseFIFO | pwm1Enable
old := pwmMemory.ctl
pwmMemory.ctl = (old & ^pwmControl(0xff)) | pwm1UseFIFO | pwm1Enable
}
// Convert divisor into wait cycles.
return actual, divs - 1, err
Expand Down
4 changes: 2 additions & 2 deletions host/bcm283x/pwm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import "testing"
func TestPWMMap(t *testing.T) {
p := pwmMap{}
p.reset()
if _, _, err := setPWMClockSource(10); err == nil {
if _, _, err := setPWMClockSource(10, 10); err == nil {
t.Fatal("pwmMemory is nil")
}
defer func() {
pwmMemory = nil
}()
pwmMemory = &p
if _, _, err := setPWMClockSource(10); err == nil {
if _, _, err := setPWMClockSource(10, 10); err == nil {
t.Fatal("clockMemory is nil")
}
}

0 comments on commit 5388a53

Please sign in to comment.