Skip to content

Commit

Permalink
feat(spi): DMA support (#131)
Browse files Browse the repository at this point in the history
* initial SPI DMA support

* spi dma: add dma channel update points

* micropython spi test

---------

Co-authored-by: Guy Sviry <[email protected]>
  • Loading branch information
guysv and Guy Sviry authored Sep 22, 2023
1 parent 8964055 commit e32a11f
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 10 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci-micropython.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ jobs:
env:
VERSION: ${{ matrix.micropython_version }}
- name: Create filesystem
run: python test/mklittlefs.py
run: python test/mklittlefs.py littlefs.img test/micropython/main.py
- name: Test Micropython
run: timeout 10 npm run start:micropython -- --image micropython.uf2 --expect-text "Hello, MicroPython!"
- name: Create SPI test filesystem
run: python test/mklittlefs.py littlefs-spi.img test/micropython/main-spi.py
- name: Test Micropython SPI
run: timeout 10 npm run test:micropython-spi -- "hello world" "h" "0123456789abcdef0123456789abcdef0123456789abcdef"
1 change: 1 addition & 0 deletions demo/micropython-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ console.log(`Loading uf2 image ${imageName}`);
loadUF2(imageName, mcu);

if (fs.existsSync('littlefs.img') && !args.circuitpython) {
console.log(`Loading uf2 image littlefs.img`);
loadMicropythonFlashImage('littlefs.img', mcu);
} else if (fs.existsSync('fat12.img') && args.circuitpython) {
loadCircuitpythonFlashImage('fat12.img', mcu);
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"start:micropython": "ts-node demo/micropython-run.ts",
"start:circuitpython": "ts-node demo/micropython-run.ts --circuitpython",
"start:gdbdiff": "ts-node debug/gdbdiff.ts",
"test": "jest"
"test": "jest",
"test:micropython-spi": "ts-node test/micropython-spi-test.ts"
},
"devDependencies": {
"@types/jest": "^27.4.1",
Expand Down
32 changes: 30 additions & 2 deletions src/peripherals/spi.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RP2040 } from '../rp2040';
import { FIFO } from '../utils/fifo';
import { DREQChannel } from './dma';
import { BasePeripheral, Peripheral } from './peripheral';

const SSPCR0 = 0x000; // Control register 0, SSPCR0 on page 3-4
Expand Down Expand Up @@ -58,6 +59,11 @@ const SSPRXINTR = 1 << 2;
const SSPRTINTR = 1 << 1;
const SSPRORINTR = 1 << 0;

export interface ISPIDMAChannels {
rx: DREQChannel;
tx: DREQChannel;
}

export class RPSPI extends BasePeripheral implements Peripheral {
readonly rxFIFO = new FIFO(8);
readonly txFIFO = new FIFO(8);
Expand Down Expand Up @@ -105,8 +111,26 @@ export class RPSPI extends BasePeripheral implements Peripheral {
return this.rp2040.clkPeri / (this.clockDivisor * (1 + scr));
}

constructor(rp2040: RP2040, name: string, readonly irq: number) {
private updateDMATx() {
if (this.txFIFO.full) {
this.rp2040.dma.clearDREQ(this.dreq.tx);
} else {
this.rp2040.dma.setDREQ(this.dreq.tx);
}
}

private updateDMARx() {
if (this.rxFIFO.empty) {
this.rp2040.dma.clearDREQ(this.dreq.rx);
} else {
this.rp2040.dma.setDREQ(this.dreq.rx);
}
}

constructor(rp2040: RP2040, name: string, readonly irq: number, readonly dreq: ISPIDMAChannels) {
super(rp2040, name);
this.updateDMATx();
this.updateDMARx();
}

private doTX() {
Expand Down Expand Up @@ -148,6 +172,9 @@ export class RPSPI extends BasePeripheral implements Peripheral {
if (this.intStatus !== prevStatus) {
this.checkInterrupts();
}

this.updateDMATx();
this.updateDMARx();
}

readUint32(offset: number) {
Expand Down Expand Up @@ -211,7 +238,8 @@ export class RPSPI extends BasePeripheral implements Peripheral {
return;
case SSPDR:
if (!this.txFIFO.full) {
this.txFIFO.push(value);
// decoded with respect to SSPCR0.DSS
this.txFIFO.push(value & ((1 << this.dataBits) - 1));
this.doTX();
this.fifosUpdated();
}
Expand Down
11 changes: 10 additions & 1 deletion src/rp2040.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export class RP2040 {
}),
];
readonly i2c = [new RPI2C(this, 'I2C0', IRQ.I2C0), new RPI2C(this, 'I2C1', IRQ.I2C1)];
readonly spi = [new RPSPI(this, 'SPI0', IRQ.SPI0), new RPSPI(this, 'SPI1', IRQ.SPI1)];
readonly pwm = new RPPWM(this, 'PWM_BASE');
readonly adc = new RPADC(this, 'ADC');

Expand Down Expand Up @@ -121,6 +120,16 @@ export class RP2040 {
new RPPIO(this, 'PIO1', IRQ.PIO1_IRQ0, 1),
];
readonly usbCtrl = new RPUSBController(this, 'USB');
readonly spi = [
new RPSPI(this, 'SPI0', IRQ.SPI0, {
rx: DREQChannel.DREQ_SPI0_RX,
tx: DREQChannel.DREQ_SPI0_TX,
}),
new RPSPI(this, 'SPI1', IRQ.SPI1, {
rx: DREQChannel.DREQ_SPI1_RX,
tx: DREQChannel.DREQ_SPI1_TX,
}),
];

private stopped = true;

Expand Down
53 changes: 53 additions & 0 deletions test/micropython-spi-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { GPIOPinState, RP2040 } from '../src';
import { ConsoleLogger, LogLevel } from '../src/utils/logging';
import { bootromB1 } from '../demo/bootrom';
import { loadUF2, loadMicropythonFlashImage } from '../demo/load-flash';
import fs from 'fs';
import minimist from 'minimist';

const args = minimist(process.argv.slice(2));

const mcu = new RP2040();
mcu.loadBootrom(bootromB1);
mcu.logger = new ConsoleLogger(LogLevel.Error);

const imageName = 'micropython.uf2';
console.log(`Loading uf2 image ${imageName}`);
loadUF2(imageName, mcu);

const littlefs = 'littlefs-spi.img';

if (fs.existsSync(littlefs)) {
console.log(`Loading littlefs image ${littlefs}`);
loadMicropythonFlashImage(littlefs, mcu);
}

let spiBuf = '';
mcu.gpio[5].addListener((state: GPIOPinState, oldState: GPIOPinState) => {
if (!spiBuf) {
return;
}

if (state === GPIOPinState.High && oldState === GPIOPinState.Low) {
if (spiBuf !== args._?.shift()) {
console.log('SPI TEST FAILED.');
process.exit(1);
} else {
console.log('SPI MESSAGE RECEIVED.');
spiBuf = '';
}

if (args._.length === 0) {
console.log('SPI TEST PASSED.');
process.exit(0);
}
}
});

mcu.spi[0].onTransmit = (char) => {
spiBuf += String.fromCharCode(char);
mcu.spi[0].completeTransmit(0);
};

mcu.core.PC = 0x10000000;
mcu.execute();
18 changes: 18 additions & 0 deletions test/micropython/main-spi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from machine import Pin, SPI

spi = SPI(0)
cs = Pin(5, Pin.OUT)
cs(1)

spi.init(baudrate=10 * 1024 * 1024, polarity=0, phase=0)
cs(0)
spi.write(b'hello world')
cs(1)

cs(0)
spi.write(b'h')
cs(1)

cs(0)
spi.write(b'0123456789abcdef0123456789abcdef0123456789abcdef')
cs(1)
13 changes: 8 additions & 5 deletions test/mklittlefs.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# Simple script to create a littlefs image for running the MicroPython test

from littlefs import LittleFS
from os.path import join
from os.path import basename
from sys import argv

files = ['main.py']
source_dir = 'test/micropython'
output_image = 'littlefs.img'
files = argv[2:]
# files = ['test/micropython/main.py']
output_image = argv[1]

lfs = LittleFS(block_size=4096, block_count=352, prog_size=256)
main = True
for filename in files:
with open(join(source_dir, filename), 'r') as src_file, lfs.open(filename, 'w') as lfs_file:
with open(filename, 'r') as src_file, lfs.open("main.py" if main else basename(filename), 'w') as lfs_file:
lfs_file.write(src_file.read())
main = False
with open(output_image, 'wb') as fh:
fh.write(lfs.context.buffer)

Expand Down

0 comments on commit e32a11f

Please sign in to comment.