Skip to content

Commit

Permalink
feat(uart): implement RX #41
Browse files Browse the repository at this point in the history
this allows us to run the MicroPython REPL! 🐍

We still need to write some more tests, and implement the FIFO level interrupts correctly. Yet it's functional!

close #41
  • Loading branch information
urish committed May 27, 2021
1 parent f9bcdc0 commit 8386d1e
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 2 deletions.
13 changes: 13 additions & 0 deletions src/peripherals/uart.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { RP2040 } from '../rp2040';
import { RPUART } from './uart';

const OFFSET_UARTLCR_H = 0x2c;

describe('UART', () => {
it('should correctly return wordLength based on UARTLCR_H value', () => {
const rp2040 = new RP2040();
const uart = new RPUART(rp2040, 'UART', 0);
uart.writeUint32(OFFSET_UARTLCR_H, 0x70);
expect(uart.wordLength).toEqual(8);
});
});
112 changes: 111 additions & 1 deletion src/peripherals/uart.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,107 @@
import { RP2040 } from '../rp2040';
import { FIFO } from '../utils/fifo';
import { BasePeripheral, Peripheral } from './peripheral';

const UARTDR = 0x0;
const UARTFR = 0x18;
const UARTLCR_H = 0x2c;
const UARTCR = 0x30;
const UARTIMSC = 0x38;
const UARTIRIS = 0x3c;
const UARTIMIS = 0x40;
const UARTICR = 0x44;

// UARTFR bits:
const RXFF = 1 << 6;
const RXFE = 1 << 4;

// UARTLCR_H bits:
const FEN = 1 << 4;

// UARTCR bits:
const RXE = 1 << 9;
const TXE = 1 << 8;
const UARTEN = 1 << 0;

// Interrupt bits
const UARTRXINTR = 1 << 4;

export class RPUART extends BasePeripheral implements Peripheral {
private ctrlRegister = RXE | TXE;
private lineCtrlRegister = 0;
private rxFIFO = new FIFO(32);
private interruptMask = 0;
private interruptStatus = 0;

public onByte?: (value: number) => void;

constructor(rp2040: RP2040, name: string, readonly irq: number) {
super(rp2040, name);
}

get enabled() {
return !!(this.ctrlRegister & UARTEN);
}

get txEnabled() {
return !!(this.ctrlRegister & TXE);
}

get rxEnabled() {
return !!(this.ctrlRegister & RXE);
}

get fifosEnabled() {
return !!(this.lineCtrlRegister & FEN);
}

/**
* Number of bits per UART character
*/
get wordLength() {
switch ((this.lineCtrlRegister >>> 5) & 0x3) {
case 0b00:
return 5;
case 0b01:
return 6;
case 0b10:
return 7;
case 0b11:
return 8;
}
}

get flags() {
return (this.rxFIFO.full ? RXFF : 0) | (this.rxFIFO.empty ? RXFE : 0);
}

checkInterrupts() {
this.rp2040.setInterrupt(this.irq, !!(this.interruptStatus & this.interruptMask));
}

feedByte(value: number) {
this.rxFIFO.push(value);
// TODO check if the FIFO has reached the threshold level
this.interruptStatus |= UARTRXINTR;
this.checkInterrupts();
}

readUint32(offset: number) {
switch (offset) {
case UARTDR:
return this.rxFIFO.pull();
case UARTFR:
return 0;
return this.flags;
case UARTLCR_H:
return this.lineCtrlRegister;
case UARTCR:
return this.ctrlRegister;
case UARTIMSC:
return this.interruptMask;
case UARTIRIS:
return this.interruptStatus;
case UARTIMIS:
return this.interruptStatus & this.interruptMask;
}
return super.readUint32(offset);
}
Expand All @@ -20,6 +112,24 @@ export class RPUART extends BasePeripheral implements Peripheral {
this.onByte?.(value & 0xff);
break;

case UARTLCR_H:
this.lineCtrlRegister = value;
break;

case UARTCR:
this.ctrlRegister = value;
break;

case UARTIMSC:
this.interruptMask = value & 0x7ff;
this.checkInterrupts();
break;

case UARTICR:
this.interruptStatus &= ~this.rawWriteValue;
this.checkInterrupts();
break;

default:
super.writeUint32(offset, value);
}
Expand Down
4 changes: 3 additions & 1 deletion src/rp2040.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export const SYSM_PRIMASK = 16;
export const SYSM_CONTROL = 20;

const IO_IRQ_BANK0 = 13;
const UART0_IRQ = 20;
const UART1_IRQ = 21;
const MAX_HARDWARE_IRQ = 25; // That's RTC_IRQ

/* eslint-enable @typescript-eslint/no-unused-vars */
Expand Down Expand Up @@ -115,7 +117,7 @@ export class RP2040 {

readonly sio = new RPSIO(this);

readonly uart = [new RPUART(this, 'UART0'), new RPUART(this, 'UART1')];
readonly uart = [new RPUART(this, 'UART0', UART0_IRQ), new RPUART(this, 'UART1', UART1_IRQ)];

readonly gpio = [
new GPIOPin(this, 0),
Expand Down
26 changes: 26 additions & 0 deletions src/utils/fifo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { FIFO } from './fifo';

describe('FIFO', () => {
it('should successfully push and pull 4 items', () => {
const fifo = new FIFO(3);
expect(fifo.empty).toBe(true);
fifo.push(1);
expect(fifo.empty).toBe(false);
fifo.push(2);
expect(fifo.itemCount).toBe(2);
expect(fifo.full).toBe(false);
fifo.push(3);
expect(fifo.full).toBe(true);
expect(fifo.pull()).toBe(1);
expect(fifo.full).toBe(false);
fifo.push(4);
expect(fifo.full).toBe(true);
expect(fifo.pull()).toBe(2);
expect(fifo.pull()).toBe(3);
expect(fifo.empty).toBe(false);
expect(fifo.itemCount).toBe(1);
expect(fifo.pull()).toBe(4);
expect(fifo.full).toBe(false);
expect(fifo.empty).toBe(true);
});
});
50 changes: 50 additions & 0 deletions src/utils/fifo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export class FIFO {
readonly buffer: Uint32Array;

private start = 0;
private used = 0;

constructor(size: number) {
this.buffer = new Uint32Array(size);
}

get size() {
return this.buffer.length;
}

get itemCount() {
return this.used;
}

push(value: number) {
const { length } = this.buffer;
const { start, used } = this;
if (this.used < length) {
this.buffer[(start + used) % length] = value;
this.used++;
}
}

pull() {
const { start, used } = this;
const { length } = this.buffer;
if (used) {
this.start = (start + 1) % length;
this.used--;
return this.buffer[start];
}
return 0;
}

reset() {
this.used = 0;
}

get empty() {
return this.used == 0;
}

get full() {
return this.used === this.buffer.length;
}
}

0 comments on commit 8386d1e

Please sign in to comment.