From 6f0ba2bfd98a5f1bd3e88a97e423d4046bbd95fa Mon Sep 17 00:00:00 2001 From: James Date: Mon, 30 Jan 2023 16:37:38 -0500 Subject: [PATCH 01/10] ATmega32: Refactor 328p-based files into separate files, add in basic ATmega32 support --- src/index.ts | 28 +++- src/peripherals/adc.spec.ts | 7 +- src/peripherals/adc.ts | 98 +++++-------- src/peripherals/adc_atmega32.ts | 71 ++++++++++ src/peripherals/adc_atmega328p.ts | 48 +++++++ src/peripherals/gpio.spec.ts | 3 +- src/peripherals/gpio.ts | 134 ------------------ src/peripherals/gpio_atmega2560.ts | 57 ++++++++ src/peripherals/gpio_atmega32.ts | 29 ++++ src/peripherals/gpio_atmega328p.ts | 79 +++++++++++ src/peripherals/timer.spec.ts | 120 ++++++++-------- src/peripherals/timer.ts | 204 +++++++--------------------- src/peripherals/timer_atmega32.ts | 154 +++++++++++++++++++++ src/peripherals/timer_atmega328p.ts | 124 +++++++++++++++++ src/peripherals/usart_atmega32.ts | 13 ++ 15 files changed, 750 insertions(+), 419 deletions(-) create mode 100644 src/peripherals/adc_atmega32.ts create mode 100644 src/peripherals/adc_atmega328p.ts create mode 100644 src/peripherals/gpio_atmega2560.ts create mode 100644 src/peripherals/gpio_atmega32.ts create mode 100644 src/peripherals/gpio_atmega328p.ts create mode 100644 src/peripherals/timer_atmega32.ts create mode 100644 src/peripherals/timer_atmega328p.ts create mode 100644 src/peripherals/usart_atmega32.ts diff --git a/src/index.ts b/src/index.ts index e529bb6..0313607 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,18 +20,19 @@ export { AVRClock, clockConfig } from './peripherals/clock'; export type { AVRClockConfig } from './peripherals/clock'; export { AVREEPROM, eepromConfig, EEPROMMemoryBackend } from './peripherals/eeprom'; export type { AVREEPROMConfig, EEPROMBackend } from './peripherals/eeprom'; +export { AVRIOPort, PinState } from './peripherals/gpio'; export { - AVRIOPort, INT0, INT1, PCINT0, PCINT1, PCINT2, - PinState, - portAConfig, portBConfig, portCConfig, portDConfig, +} from './peripherals/gpio_atmega328p'; +export { + portAConfig, portEConfig, portFConfig, portGConfig, @@ -39,7 +40,19 @@ export { portJConfig, portKConfig, portLConfig, -} from './peripherals/gpio'; +} from './peripherals/gpio_atmega2560'; +export { + atmega32PortAConfig, + atmega32PortBConfig, + atmega32PortCConfig, + atmega32PortDConfig, +} from './peripherals/gpio_atmega32'; +export { + atmga32Timer0Config, + atmga32Timer1Config, + atmga32Timer2Config, +} from './peripherals/timer_atmega32'; +export { atmega32Usart0Config } from './peripherals/usart_atmega32'; export type { AVRExternalInterrupt, AVRPinChangeInterrupt, @@ -48,7 +61,12 @@ export type { } from './peripherals/gpio'; export { AVRSPI, spiConfig } from './peripherals/spi'; export type { SPIConfig, SPITransferCallback } from './peripherals/spi'; -export { AVRTimer, timer0Config, timer1Config, timer2Config } from './peripherals/timer'; +export { AVRTimer } from './peripherals/timer'; +export { + atmega328pTimer0Config as atmega329pTimer0Config, + atmega328pTimer1Config as atmega329pTimer1Config, + atmega328pTimer2Config as atmega329pTimer2Config, +} from './peripherals/timer_atmega328p'; export type { AVRTimerConfig } from './peripherals/timer'; export * from './peripherals/twi'; export { AVRUSART, usart0Config } from './peripherals/usart'; diff --git a/src/peripherals/adc.spec.ts b/src/peripherals/adc.spec.ts index 4eb9aff..f8d9849 100644 --- a/src/peripherals/adc.spec.ts +++ b/src/peripherals/adc.spec.ts @@ -1,6 +1,7 @@ import { CPU } from '../cpu/cpu'; import { asmProgram, TestProgramRunner } from '../utils/test-utils'; -import { AVRADC, adcConfig, ADCMuxInputType } from './adc'; +import { AVRADC, ADCMuxInputType } from './adc'; +import { atmega328AdcConfig } from './adc_atmega328p'; const R16 = 16; const R17 = 17; @@ -48,7 +49,7 @@ describe('ADC', () => { break `); const cpu = new CPU(program); - const adc = new AVRADC(cpu, adcConfig); + const adc = new AVRADC(cpu, atmega328AdcConfig); const runner = new TestProgramRunner(cpu); const adcReadSpy = jest.spyOn(adc, 'onADCRead'); @@ -109,7 +110,7 @@ describe('ADC', () => { break `); const cpu = new CPU(program); - const adc = new AVRADC(cpu, adcConfig); + const adc = new AVRADC(cpu, atmega328AdcConfig); const runner = new TestProgramRunner(cpu, () => { /* do nothing on break */ }); diff --git a/src/peripherals/adc.ts b/src/peripherals/adc.ts index 1e0e2d1..3b6289a 100644 --- a/src/peripherals/adc.ts +++ b/src/peripherals/adc.ts @@ -37,13 +37,34 @@ export type ADCMuxInput = export type ADCMuxConfiguration = { [key: number]: ADCMuxInput }; +// ATMega32 vs 328p: 32 has No ADCSRB. 32 has MUX4 in ADCSRA, only difference +// 2650: MUX5 exists in ADCSRB + export interface ADCConfig { + // Register addresses ADMUX: u8; ADCSRA: u8; - ADCSRB: u8; + ADCSRB: u8; // Optional, 0 = unsupported ADCL: u8; ADCH: u8; - DIDR0: u8; + + // ADCSRA bits + ADPS_MASK: u8; + ADIE: u8; + ADIF: u8; + ADSC: u8; + ADEN: u8; + + // ADMUX bits + MUX_MASK: u8; + REFS_SHIFT: u8; + REFS_MASK: u8; + REFS2: u8; // Optional, 0 = unsupported + ADLAR: u8; + + // ADCSRB bits + MUX5: u8; // Optional, 0 = unsupported. Also not used if ADCSRB === 0 + adcInterrupt: u8; numChannels: u8; muxInputMask: u8; @@ -51,58 +72,11 @@ export interface ADCConfig { adcReferences: ADCReference[]; } -export const atmega328Channels: ADCMuxConfiguration = { - 0: { type: ADCMuxInputType.SingleEnded, channel: 0 }, - 1: { type: ADCMuxInputType.SingleEnded, channel: 1 }, - 2: { type: ADCMuxInputType.SingleEnded, channel: 2 }, - 3: { type: ADCMuxInputType.SingleEnded, channel: 3 }, - 4: { type: ADCMuxInputType.SingleEnded, channel: 4 }, - 5: { type: ADCMuxInputType.SingleEnded, channel: 5 }, - 6: { type: ADCMuxInputType.SingleEnded, channel: 6 }, - 7: { type: ADCMuxInputType.SingleEnded, channel: 7 }, - 8: { type: ADCMuxInputType.Temperature }, - 14: { type: ADCMuxInputType.Constant, voltage: 1.1 }, - 15: { type: ADCMuxInputType.Constant, voltage: 0 }, -}; - const fallbackMuxInput = { type: ADCMuxInputType.Constant, voltage: 0, }; -export const adcConfig: ADCConfig = { - ADMUX: 0x7c, - ADCSRA: 0x7a, - ADCSRB: 0x7b, - ADCL: 0x78, - ADCH: 0x79, - DIDR0: 0x7e, - adcInterrupt: 0x2a, - numChannels: 8, - muxInputMask: 0xf, - muxChannels: atmega328Channels, - adcReferences: [ - ADCReference.AREF, - ADCReference.AVCC, - ADCReference.Reserved, - ADCReference.Internal1V1, - ], -}; - -// Register bits: -const ADPS_MASK = 0x7; -const ADIE = 0x8; -const ADIF = 0x10; -const ADSC = 0x40; -const ADEN = 0x80; - -const MUX_MASK = 0x1f; -const ADLAR = 0x20; -const MUX5 = 0x8; -const REFS2 = 0x8; -const REFS_MASK = 0x3; -const REFS_SHIFT = 6; - export class AVRADC { /** * ADC Channel values, in voltage (0..5). The number of channels depends on the chip. @@ -117,6 +91,8 @@ export class AVRADC { /** AREF Reference voltage */ aref = 5; + private hasADCSRB = this.config.ADCSRB > 0; + /** * Invoked whenever the code performs an ADC read. * @@ -158,26 +134,26 @@ export class AVRADC { private ADC: AVRInterruptConfig = { address: this.config.adcInterrupt, flagRegister: this.config.ADCSRA, - flagMask: ADIF, + flagMask: this.config.ADIF, enableRegister: this.config.ADCSRA, - enableMask: ADIE, + enableMask: this.config.ADIE, }; constructor(private cpu: CPU, private config: ADCConfig) { cpu.writeHooks[config.ADCSRA] = (value, oldValue) => { - if (value & ADEN && !(oldValue && ADEN)) { + if (value & this.config.ADEN && !(oldValue && this.config.ADEN)) { this.conversionCycles = 25; } cpu.data[config.ADCSRA] = value; cpu.updateInterruptEnable(this.ADC, value); - if (!this.converting && value & ADSC) { - if (!(value & ADEN)) { + if (!this.converting && value & this.config.ADSC) { + if (!(value & this.config.ADEN)) { // Special case: reading while the ADC is not enabled should return 0 this.cpu.addClockEvent(() => this.completeADCRead(0), this.sampleCycles); return true; } - let channel = this.cpu.data[this.config.ADMUX] & MUX_MASK; - if (cpu.data[config.ADCSRB] & MUX5) { + let channel = this.cpu.data[this.config.ADMUX] & this.config.MUX_MASK; + if (this.hasADCSRB && cpu.data[config.ADCSRB] & this.config.MUX5) { channel |= 0x20; } channel &= config.muxInputMask; @@ -193,21 +169,21 @@ export class AVRADC { const { ADCL, ADCH, ADMUX, ADCSRA } = this.config; this.converting = false; this.conversionCycles = 13; - if (this.cpu.data[ADMUX] & ADLAR) { + if (this.cpu.data[ADMUX] & this.config.ADLAR) { this.cpu.data[ADCL] = (value << 6) & 0xff; this.cpu.data[ADCH] = value >> 2; } else { this.cpu.data[ADCL] = value & 0xff; this.cpu.data[ADCH] = (value >> 8) & 0x3; } - this.cpu.data[ADCSRA] &= ~ADSC; + this.cpu.data[ADCSRA] &= ~this.config.ADSC; this.cpu.setInterruptFlag(this.ADC); } get prescaler() { const { ADCSRA } = this.config; const adcsra = this.cpu.data[ADCSRA]; - const adps = adcsra & ADPS_MASK; + const adps = adcsra & this.config.ADPS_MASK; switch (adps) { case 0: case 1: @@ -230,8 +206,8 @@ export class AVRADC { get referenceVoltageType() { const { ADMUX, adcReferences } = this.config; - let refs = (this.cpu.data[ADMUX] >> REFS_SHIFT) & REFS_MASK; - if (adcReferences.length > 4 && this.cpu.data[ADMUX] & REFS2) { + let refs = (this.cpu.data[ADMUX] >> this.config.REFS_SHIFT) & this.config.REFS_MASK; + if (adcReferences.length > 4 && this.cpu.data[ADMUX] & this.config.REFS2) { refs |= 0x4; } return adcReferences[refs] ?? ADCReference.Reserved; diff --git a/src/peripherals/adc_atmega32.ts b/src/peripherals/adc_atmega32.ts new file mode 100644 index 0000000..484db52 --- /dev/null +++ b/src/peripherals/adc_atmega32.ts @@ -0,0 +1,71 @@ +import { ADCConfig, ADCMuxConfiguration, ADCMuxInputType, ADCReference } from './adc'; + +export const atmega32Channels: ADCMuxConfiguration = { + 0: { type: ADCMuxInputType.SingleEnded, channel: 0 }, + 1: { type: ADCMuxInputType.SingleEnded, channel: 1 }, + 2: { type: ADCMuxInputType.SingleEnded, channel: 2 }, + 3: { type: ADCMuxInputType.SingleEnded, channel: 3 }, + 4: { type: ADCMuxInputType.SingleEnded, channel: 4 }, + 5: { type: ADCMuxInputType.SingleEnded, channel: 5 }, + 6: { type: ADCMuxInputType.SingleEnded, channel: 6 }, + 7: { type: ADCMuxInputType.SingleEnded, channel: 7 }, + 8: { type: ADCMuxInputType.Differential, positiveChannel: 0, negativeChannel: 0, gain: 10 }, + 9: { type: ADCMuxInputType.Differential, positiveChannel: 1, negativeChannel: 0, gain: 10 }, + 10: { type: ADCMuxInputType.Differential, positiveChannel: 0, negativeChannel: 0, gain: 200 }, + 11: { type: ADCMuxInputType.Differential, positiveChannel: 1, negativeChannel: 0, gain: 200 }, + 12: { type: ADCMuxInputType.Differential, positiveChannel: 2, negativeChannel: 2, gain: 10 }, + 13: { type: ADCMuxInputType.Differential, positiveChannel: 3, negativeChannel: 2, gain: 10 }, + 14: { type: ADCMuxInputType.Differential, positiveChannel: 2, negativeChannel: 2, gain: 200 }, + 15: { type: ADCMuxInputType.Differential, positiveChannel: 3, negativeChannel: 2, gain: 200 }, + 16: { type: ADCMuxInputType.Differential, positiveChannel: 0, negativeChannel: 1, gain: 1 }, + 17: { type: ADCMuxInputType.Differential, positiveChannel: 1, negativeChannel: 1, gain: 1 }, + 18: { type: ADCMuxInputType.Differential, positiveChannel: 2, negativeChannel: 1, gain: 1 }, + 19: { type: ADCMuxInputType.Differential, positiveChannel: 3, negativeChannel: 1, gain: 1 }, + 20: { type: ADCMuxInputType.Differential, positiveChannel: 4, negativeChannel: 1, gain: 1 }, + 21: { type: ADCMuxInputType.Differential, positiveChannel: 5, negativeChannel: 1, gain: 1 }, + 22: { type: ADCMuxInputType.Differential, positiveChannel: 6, negativeChannel: 1, gain: 1 }, + 23: { type: ADCMuxInputType.Differential, positiveChannel: 7, negativeChannel: 1, gain: 1 }, + 24: { type: ADCMuxInputType.Differential, positiveChannel: 0, negativeChannel: 2, gain: 1 }, + 25: { type: ADCMuxInputType.Differential, positiveChannel: 1, negativeChannel: 2, gain: 1 }, + 26: { type: ADCMuxInputType.Differential, positiveChannel: 2, negativeChannel: 2, gain: 1 }, + 27: { type: ADCMuxInputType.Differential, positiveChannel: 3, negativeChannel: 2, gain: 1 }, + 28: { type: ADCMuxInputType.Differential, positiveChannel: 4, negativeChannel: 2, gain: 1 }, + 29: { type: ADCMuxInputType.Differential, positiveChannel: 5, negativeChannel: 2, gain: 1 }, + 30: { type: ADCMuxInputType.Constant, voltage: 1.22 }, + 31: { type: ADCMuxInputType.Constant, voltage: 0 }, +}; + +export const atmega32AdcConfig: ADCConfig = { + ADMUX: 0x27, + ADCSRA: 0x26, + ADCSRB: 0, // Not aviliable on ATmega32 + ADCL: 0x24, + ADCH: 0x25, + + // ADCSRA bits + ADPS_MASK: 0x7, + ADIE: 0x8, + ADIF: 0x10, + ADSC: 0x40, + ADEN: 0x80, + + // ADMUX bits + MUX_MASK: 0x1f, + REFS_SHIFT: 0x6, + REFS_MASK: 0x3, + REFS2: 0, // Not supported + ADLAR: 0x20, + MUX5: 0, + + adcInterrupt: 0x20, + numChannels: 32, + muxInputMask: 0x1f, + muxChannels: atmega32Channels, + + adcReferences: [ + ADCReference.AREF, + ADCReference.AVCC, + ADCReference.Reserved, + ADCReference.Internal2V56, + ], +}; diff --git a/src/peripherals/adc_atmega328p.ts b/src/peripherals/adc_atmega328p.ts new file mode 100644 index 0000000..595931a --- /dev/null +++ b/src/peripherals/adc_atmega328p.ts @@ -0,0 +1,48 @@ +import { ADCConfig, ADCMuxConfiguration, ADCMuxInputType, ADCReference } from './adc'; + +export const atmega328Channels: ADCMuxConfiguration = { + 0: { type: ADCMuxInputType.SingleEnded, channel: 0 }, + 1: { type: ADCMuxInputType.SingleEnded, channel: 1 }, + 2: { type: ADCMuxInputType.SingleEnded, channel: 2 }, + 3: { type: ADCMuxInputType.SingleEnded, channel: 3 }, + 4: { type: ADCMuxInputType.SingleEnded, channel: 4 }, + 5: { type: ADCMuxInputType.SingleEnded, channel: 5 }, + 6: { type: ADCMuxInputType.SingleEnded, channel: 6 }, + 7: { type: ADCMuxInputType.SingleEnded, channel: 7 }, + 8: { type: ADCMuxInputType.Temperature }, + 14: { type: ADCMuxInputType.Constant, voltage: 1.1 }, + 15: { type: ADCMuxInputType.Constant, voltage: 0 }, +}; + +export const atmega328AdcConfig: ADCConfig = { + ADMUX: 0x7c, + ADCSRA: 0x7a, + ADCSRB: 0x7b, + ADCL: 0x78, + ADCH: 0x79, + + ADPS_MASK: 0x7, + ADIE: 0x8, + ADIF: 0x10, + ADSC: 0x40, + ADEN: 0x80, + + MUX_MASK: 0xf, + REFS_SHIFT: 0x6, + REFS_MASK: 0x3, + REFS2: 0, + ADLAR: 0x20, + MUX5: 0, + + adcInterrupt: 0x2a, + numChannels: 8, + muxInputMask: 0xf, + muxChannels: atmega328Channels, + + adcReferences: [ + ADCReference.AREF, + ADCReference.AVCC, + ADCReference.Reserved, + ADCReference.Internal1V1, + ], +}; diff --git a/src/peripherals/gpio.spec.ts b/src/peripherals/gpio.spec.ts index 755476e..53cf004 100644 --- a/src/peripherals/gpio.spec.ts +++ b/src/peripherals/gpio.spec.ts @@ -1,6 +1,7 @@ import { CPU } from '../cpu/cpu'; import { asmProgram, TestProgramRunner } from '../utils/test-utils'; -import { AVRIOPort, portBConfig, PinState, portDConfig, PinOverrideMode } from './gpio'; +import { AVRIOPort, PinState, PinOverrideMode } from './gpio'; +import { portBConfig, portDConfig } from './gpio_atmega328p'; // CPU registers const SREG = 95; diff --git a/src/peripherals/gpio.ts b/src/peripherals/gpio.ts index 0da9d50..27e6800 100644 --- a/src/peripherals/gpio.ts +++ b/src/peripherals/gpio.ts @@ -45,143 +45,9 @@ export interface AVRPortConfig { externalInterrupts: (AVRExternalInterrupt | null)[]; } -export const INT0: AVRExternalInterrupt = { - EICR: 0x69, - EIMSK: 0x3d, - EIFR: 0x3c, - index: 0, - iscOffset: 0, - interrupt: 2, -}; - -export const INT1: AVRExternalInterrupt = { - EICR: 0x69, - EIMSK: 0x3d, - EIFR: 0x3c, - index: 1, - iscOffset: 2, - interrupt: 4, -}; - -export const PCINT0 = { - PCIE: 0, - PCICR: 0x68, - PCIFR: 0x3b, - PCMSK: 0x6b, - pinChangeInterrupt: 6, - mask: 0xff, - offset: 0, -}; - -export const PCINT1 = { - PCIE: 1, - PCICR: 0x68, - PCIFR: 0x3b, - PCMSK: 0x6c, - pinChangeInterrupt: 8, - mask: 0xff, - offset: 0, -}; - -export const PCINT2 = { - PCIE: 2, - PCICR: 0x68, - PCIFR: 0x3b, - PCMSK: 0x6d, - pinChangeInterrupt: 10, - mask: 0xff, - offset: 0, -}; - export type GPIOListener = (value: u8, oldValue: u8) => void; export type ExternalClockListener = (pinValue: boolean) => void; -export const portAConfig: AVRPortConfig = { - PIN: 0x20, - DDR: 0x21, - PORT: 0x22, - externalInterrupts: [], -}; - -export const portBConfig: AVRPortConfig = { - PIN: 0x23, - DDR: 0x24, - PORT: 0x25, - - // Interrupt settings - pinChange: PCINT0, - externalInterrupts: [], -}; - -export const portCConfig: AVRPortConfig = { - PIN: 0x26, - DDR: 0x27, - PORT: 0x28, - - // Interrupt settings - pinChange: PCINT1, - externalInterrupts: [], -}; - -export const portDConfig: AVRPortConfig = { - PIN: 0x29, - DDR: 0x2a, - PORT: 0x2b, - - // Interrupt settings - pinChange: PCINT2, - externalInterrupts: [null, null, INT0, INT1], -}; - -export const portEConfig: AVRPortConfig = { - PIN: 0x2c, - DDR: 0x2d, - PORT: 0x2e, - externalInterrupts: [], -}; - -export const portFConfig: AVRPortConfig = { - PIN: 0x2f, - DDR: 0x30, - PORT: 0x31, - externalInterrupts: [], -}; - -export const portGConfig: AVRPortConfig = { - PIN: 0x32, - DDR: 0x33, - PORT: 0x34, - externalInterrupts: [], -}; - -export const portHConfig: AVRPortConfig = { - PIN: 0x100, - DDR: 0x101, - PORT: 0x102, - externalInterrupts: [], -}; - -export const portJConfig: AVRPortConfig = { - PIN: 0x103, - DDR: 0x104, - PORT: 0x105, - externalInterrupts: [], -}; - -export const portKConfig: AVRPortConfig = { - PIN: 0x106, - DDR: 0x107, - PORT: 0x108, - externalInterrupts: [], -}; - -export const portLConfig: AVRPortConfig = { - PIN: 0x109, - DDR: 0x10a, - PORT: 0x10b, - externalInterrupts: [], -}; - export enum PinState { Low, High, diff --git a/src/peripherals/gpio_atmega2560.ts b/src/peripherals/gpio_atmega2560.ts new file mode 100644 index 0000000..2a30646 --- /dev/null +++ b/src/peripherals/gpio_atmega2560.ts @@ -0,0 +1,57 @@ +import { AVRPortConfig } from './gpio'; + +export const portAConfig: AVRPortConfig = { + PIN: 0x20, + DDR: 0x21, + PORT: 0x22, + externalInterrupts: [], +}; + +export const portEConfig: AVRPortConfig = { + PIN: 0x2c, + DDR: 0x2d, + PORT: 0x2e, + externalInterrupts: [], +}; + +export const portFConfig: AVRPortConfig = { + PIN: 0x2f, + DDR: 0x30, + PORT: 0x31, + externalInterrupts: [], +}; + +export const portGConfig: AVRPortConfig = { + PIN: 0x32, + DDR: 0x33, + PORT: 0x34, + externalInterrupts: [], +}; + +export const portHConfig: AVRPortConfig = { + PIN: 0x100, + DDR: 0x101, + PORT: 0x102, + externalInterrupts: [], +}; + +export const portJConfig: AVRPortConfig = { + PIN: 0x103, + DDR: 0x104, + PORT: 0x105, + externalInterrupts: [], +}; + +export const portKConfig: AVRPortConfig = { + PIN: 0x106, + DDR: 0x107, + PORT: 0x108, + externalInterrupts: [], +}; + +export const portLConfig: AVRPortConfig = { + PIN: 0x109, + DDR: 0x10a, + PORT: 0x10b, + externalInterrupts: [], +}; diff --git a/src/peripherals/gpio_atmega32.ts b/src/peripherals/gpio_atmega32.ts new file mode 100644 index 0000000..65f8e85 --- /dev/null +++ b/src/peripherals/gpio_atmega32.ts @@ -0,0 +1,29 @@ +import { AVRPortConfig } from './gpio'; + +export const atmega32PortAConfig: AVRPortConfig = { + PORT: 0x3b, + DDR: 0x3a, + PIN: 0x39, + externalInterrupts: [], +}; + +export const atmega32PortBConfig: AVRPortConfig = { + PORT: 0x38, + DDR: 0x37, + PIN: 0x36, + externalInterrupts: [], +}; + +export const atmega32PortCConfig: AVRPortConfig = { + PORT: 0x35, + DDR: 0x34, + PIN: 0x33, + externalInterrupts: [], +}; + +export const atmega32PortDConfig: AVRPortConfig = { + PORT: 0x32, + DDR: 0x31, + PIN: 0x30, + externalInterrupts: [], +}; diff --git a/src/peripherals/gpio_atmega328p.ts b/src/peripherals/gpio_atmega328p.ts new file mode 100644 index 0000000..a68bf79 --- /dev/null +++ b/src/peripherals/gpio_atmega328p.ts @@ -0,0 +1,79 @@ +import { AVRExternalInterrupt, AVRPortConfig } from './gpio'; + +export const INT0: AVRExternalInterrupt = { + EICR: 0x69, + EIMSK: 0x3d, + EIFR: 0x3c, + index: 0, + iscOffset: 0, + interrupt: 2, +}; + +export const INT1: AVRExternalInterrupt = { + EICR: 0x69, + EIMSK: 0x3d, + EIFR: 0x3c, + index: 1, + iscOffset: 2, + interrupt: 4, +}; + +export const PCINT0 = { + PCIE: 0, + PCICR: 0x68, + PCIFR: 0x3b, + PCMSK: 0x6b, + pinChangeInterrupt: 6, + mask: 0xff, + offset: 0, +}; + +export const PCINT1 = { + PCIE: 1, + PCICR: 0x68, + PCIFR: 0x3b, + PCMSK: 0x6c, + pinChangeInterrupt: 8, + mask: 0xff, + offset: 0, +}; + +export const PCINT2 = { + PCIE: 2, + PCICR: 0x68, + PCIFR: 0x3b, + PCMSK: 0x6d, + pinChangeInterrupt: 10, + mask: 0xff, + offset: 0, +}; + +export const portBConfig: AVRPortConfig = { + PIN: 0x23, + DDR: 0x24, + PORT: 0x25, + + // Interrupt settings + pinChange: PCINT0, + externalInterrupts: [], +}; + +export const portCConfig: AVRPortConfig = { + PIN: 0x26, + DDR: 0x27, + PORT: 0x28, + + // Interrupt settings + pinChange: PCINT1, + externalInterrupts: [], +}; + +export const portDConfig: AVRPortConfig = { + PIN: 0x29, + DDR: 0x2a, + PORT: 0x2b, + + // Interrupt settings + pinChange: PCINT2, + externalInterrupts: [null, null, INT0, INT1], +}; diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 609908d..644db76 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -1,7 +1,13 @@ import { CPU } from '../cpu/cpu'; import { asmProgram, TestProgramRunner } from '../utils/test-utils'; -import { AVRIOPort, PinOverrideMode, portBConfig, portDConfig } from './gpio'; -import { AVRTimer, timer0Config, timer1Config, timer2Config } from './timer'; +import { AVRIOPort, PinOverrideMode } from './gpio'; +import { portBConfig, portDConfig } from './gpio_atmega328p'; +import { AVRTimer } from './timer'; +import { + atmega328pTimer0Config, + atmega328pTimer1Config, + atmega328pTimer2Config, +} from './timer_atmega328p'; // CPU registers const R1 = 1; @@ -78,7 +84,7 @@ const nopOpCode = '0000'; describe('timer', () => { it('should update timer every tick when prescaler is 1', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; cpu.tick(); @@ -90,7 +96,7 @@ describe('timer', () => { it('should update timer every 64 ticks when prescaler is 3', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCCR0B, CS01 | CS00); // Set prescaler to 64 cpu.cycles = 1; cpu.tick(); @@ -102,7 +108,7 @@ describe('timer', () => { it('should not update timer if it has been disabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCCR0B, 0); // No prescaler (timer disabled) cpu.cycles = 1; cpu.tick(); @@ -114,7 +120,7 @@ describe('timer', () => { it('should set the TOV flag when timer wraps above TOP value', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -131,7 +137,7 @@ describe('timer', () => { it('should set the TOV if timer overflows past TOP without reaching TOP', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xfe); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -145,7 +151,7 @@ describe('timer', () => { it('should clear the TOV flag when writing 1 to the TOV bit, and not trigger the interrupt', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -159,7 +165,7 @@ describe('timer', () => { it('should set TOV if timer overflows in FAST PWM mode', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -175,7 +181,7 @@ describe('timer', () => { it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -194,7 +200,7 @@ describe('timer', () => { it('should support overriding TIFR/TOV and TIMSK/TOIE bits (issue #64)', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, { - ...timer0Config, + ...atmega328pTimer0Config, // The following values correspond ATtiny85 config: TOV: 2, @@ -221,7 +227,7 @@ describe('timer', () => { it('should not generate an overflow interrupt when global interrupts disabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -237,7 +243,7 @@ describe('timer', () => { it('should not generate an overflow interrupt when TOIE0 is clear', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -253,7 +259,7 @@ describe('timer', () => { it('should set OCF0A/B flags when OCRA/B == 0 and the timer equals to OCRA (issue #74)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(OCR0A, 0x0); cpu.writeData(OCR0B, 0x0); @@ -271,7 +277,7 @@ describe('timer', () => { it('should set the OCF1A flag when OCR1A == 120 and the timer overflowed past 120 in WGM mode 15 (issue #94)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCNT1, 118); cpu.writeData(OCR1A, 120); cpu.writeData(OCR1B, 4); // To avoid getting the OCF1B flag set @@ -289,7 +295,7 @@ describe('timer', () => { it('should set OCF0A flag when timer equals OCRA', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0x10); cpu.writeData(OCR0A, 0x11); cpu.writeData(TCCR0A, 0x0); // WGM: Normal @@ -305,7 +311,7 @@ describe('timer', () => { it('should reset the counter in CTC mode if it equals to OCRA', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0x10); cpu.writeData(OCR0A, 0x11); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -322,7 +328,7 @@ describe('timer', () => { it('should not set the TOV bit when TOP < MAX in CTC mode (issue #75)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0x1e); cpu.writeData(OCR0A, 0x1f); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -340,7 +346,7 @@ describe('timer', () => { it('should set the TOV bit when TOP == MAX in CTC mode (issue #75)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xfe); cpu.writeData(OCR0A, 0xff); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -361,7 +367,7 @@ describe('timer', () => { it('should not set the TOV bit twice on overflow (issue #80)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0xfe); cpu.writeData(OCR0A, 0xff); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -383,7 +389,7 @@ describe('timer', () => { it('should set OCF0B flag when timer equals OCRB', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0x10); cpu.writeData(OCR0B, 0x11); cpu.writeData(TCCR0A, 0x0); // WGM: (Normal) @@ -399,7 +405,7 @@ describe('timer', () => { it('should generate Timer Compare A interrupt when TCNT0 == TCNTA', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0x20); cpu.writeData(OCR0A, 0x21); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -418,7 +424,7 @@ describe('timer', () => { it('should not generate Timer Compare A interrupt when OCIEA is disabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0x20); cpu.writeData(OCR0A, 0x21); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -436,7 +442,7 @@ describe('timer', () => { it('should generate Timer Compare B interrupt when TCNT0 == TCNTB', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCNT0, 0x20); cpu.writeData(OCR0B, 0x21); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -465,7 +471,7 @@ describe('timer', () => { IN r17, 0x26 ; r17 <- TCNT `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.data[R17]).toEqual(0x31); @@ -473,7 +479,7 @@ describe('timer', () => { it('timer2 should count every 256 ticks when prescaler is 6 (issue #5)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer2Config); + new AVRTimer(cpu, atmega328pTimer2Config); cpu.writeData(TCCR2B, CS22 | CS21); // Set prescaler to 256 cpu.cycles = 1; cpu.tick(); @@ -497,7 +503,7 @@ describe('timer', () => { LDS r1, 0x46 ; r1 <- TCNT0 (2 cycles) `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.data[R1]).toEqual(2); @@ -515,7 +521,7 @@ describe('timer', () => { LDS r17, 0xb2 ; TCNT should equal 2 at this point `); const cpu = new CPU(program); - new AVRTimer(cpu, timer2Config); + new AVRTimer(cpu, atmega328pTimer2Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.readData(R17)).toEqual(2); @@ -531,7 +537,7 @@ describe('timer', () => { LDS r17, 0xb2 ; TCNT2 should equal 2 at this point (not counting the NOP) `); const cpu = new CPU(program); - new AVRTimer(cpu, timer2Config); + new AVRTimer(cpu, atmega328pTimer2Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.readData(R17)).toEqual(2); @@ -539,7 +545,7 @@ describe('timer', () => { it('should clear OC0B pin when writing 1 to FOC0B', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCCR0A, COM0B1); // Listen to Port B's internal callback @@ -576,7 +582,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -629,7 +635,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -675,7 +681,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -718,7 +724,7 @@ describe('timer', () => { IN r22, 0x26 ; TCNT0 will be 1 (end of test) `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -749,7 +755,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -796,7 +802,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -829,7 +835,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -857,7 +863,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -900,7 +906,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -928,7 +934,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -952,7 +958,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - const timer = new AVRTimer(cpu, timer0Config); + const timer = new AVRTimer(cpu, atmega328pTimer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -965,7 +971,7 @@ describe('timer', () => { describe('16 bit timers', () => { it('should increment 16-bit TCNT by 1', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCNT1H, 0x22); // TCNT1 <- 0x2233 cpu.writeData(TCNT1, 0x33); // ... const timerLow = cpu.readData(TCNT1); @@ -983,7 +989,7 @@ describe('timer', () => { it('should set OCF0A flag when timer equals OCRA (16 bit mode)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCNT1H, 0x10); // TCNT1 <- 0x10ee cpu.writeData(TCNT1, 0xee); // ... cpu.writeData(OCR1AH, 0x10); // OCR1 <- 0x10ef @@ -1005,7 +1011,7 @@ describe('timer', () => { const OCR1CH = 0x8d; const OCF1C = 1 << 3; new AVRTimer(cpu, { - ...timer1Config, + ...atmega328pTimer1Config, OCRC: OCR1C, OCFC: OCF1C, }); @@ -1026,7 +1032,7 @@ describe('timer', () => { it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCCR1A, 0x3); // TCCR1A <- WGM10 | WGM11 (Fast PWM, 10-bit) cpu.writeData(TCCR1B, 0x9); // TCCR1B <- WGM12 | CS10 cpu.writeData(TIMSK1, 0x1); // TIMSK1: TOIE1 @@ -1048,7 +1054,7 @@ describe('timer', () => { it('should reset the timer once it reaches ICR value in mode 12', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCNT1H, 0x50); // TCNT1 <- 0x500f cpu.writeData(TCNT1, 0x0f); // ... cpu.writeData(ICR1H, 0x50); // ICR1 <- 0x5010 @@ -1066,7 +1072,7 @@ describe('timer', () => { it('should not update the high byte of TCNT if written after the low byte (issue #37)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCNT1, 0x22); cpu.writeData(TCNT1H, 0x55); cpu.cycles = 1; @@ -1078,7 +1084,7 @@ describe('timer', () => { it('reading from TCNT1H before TCNT1L should return old value (issue #37)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCNT1H, 0xff); cpu.writeData(TCNT1, 0xff); cpu.writeData(TCCR1B, WGM12 | CS10); // Set prescaler to 1, WGM: CTC @@ -1114,7 +1120,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); // Listen to Port B's internal callback const portB = new AVRIOPort(cpu, portBConfig); @@ -1155,7 +1161,7 @@ describe('timer', () => { const cpu = new CPU(program); new AVRTimer(cpu, { - ...timer1Config, + ...atmega328pTimer1Config, OCRC: OCR1C, OCFC: OCF1C, compPortC: portBConfig.PORT, @@ -1182,7 +1188,7 @@ describe('timer', () => { it('should toggle OC1C on when writing 1 to FOC1C', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, { - ...timer1Config, + ...atmega328pTimer1Config, OCRC: OCR1C, OCFC: OCF1C, compPortC: portBConfig.PORT, @@ -1202,7 +1208,7 @@ describe('timer', () => { it('should not toggle OC1C on when writing 1 to FOC1C in PWM mode', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, { - ...timer1Config, + ...atmega328pTimer1Config, OCRC: OCR1C, OCFC: OCF1C, compPortC: portBConfig.PORT, @@ -1251,7 +1257,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -1287,7 +1293,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -1300,7 +1306,7 @@ describe('timer', () => { it('should mask the unused bits of OCR1A when using fixed top values', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, timer1Config); + new AVRTimer(cpu, atmega328pTimer1Config); cpu.writeData(TCCR1A, WGM10 | WGM11); // WGM: FastPWM, top 0x3ff cpu.writeData(TCCR1B, WGM12); cpu.writeData(OCR1AH, 0xff); @@ -1314,7 +1320,7 @@ describe('timer', () => { it('should count on the falling edge of T0 when CS=110', () => { const cpu = new CPU(new Uint16Array(0x1000)); const port = new AVRIOPort(cpu, portDConfig); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCCR0B, CS02 | CS01); // Count on falling edge cpu.cycles = 1; cpu.tick(); @@ -1333,7 +1339,7 @@ describe('timer', () => { it('should count on the rising edge of T0 when CS=111', () => { const cpu = new CPU(new Uint16Array(0x1000)); const port = new AVRIOPort(cpu, portDConfig); - new AVRTimer(cpu, timer0Config); + new AVRTimer(cpu, atmega328pTimer0Config); cpu.writeData(TCCR0B, CS02 | CS01 | CS00); // Count on rising edge cpu.cycles = 1; cpu.tick(); diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts index 93b9d19..00894ab 100644 --- a/src/peripherals/timer.ts +++ b/src/peripherals/timer.ts @@ -7,18 +7,7 @@ */ import { AVRInterruptConfig, CPU } from '../cpu/cpu'; -import { AVRIOPort, PinOverrideMode, portBConfig, portDConfig } from './gpio'; - -const timer01Dividers = { - 0: 0, - 1: 1, - 2: 8, - 3: 64, - 4: 256, - 5: 1024, - 6: 0, // External clock - see ExternalClockMode - 7: 0, // Ditto -}; +import { AVRIOPort, PinOverrideMode } from './gpio'; enum ExternalClockMode { FallingEdge = 6, @@ -28,7 +17,7 @@ enum ExternalClockMode { type u8 = number; type u16 = number; -interface TimerDividers { +export interface TimerDividers { 0: number; 1: number; 2: number; @@ -46,38 +35,38 @@ export interface AVRTimerConfig { // Interrupt vectors captureInterrupt: u8; compAInterrupt: u8; - compBInterrupt: u8; + compBInterrupt: u8; // Optional, 0 = unused compCInterrupt: u8; // Optional, 0 = unused ovfInterrupt: u8; // Register addresses TIFR: u8; OCRA: u8; - OCRB: u8; + OCRB: u8; // Optional, 0 = unused OCRC: u8; // Optional, 0 = unused ICR: u8; TCNT: u8; TCCRA: u8; - TCCRB: u8; - TCCRC: u8; + TCCRB: u8; // Optional, 0 = unused + TCCRC: u8; // Optional, 0 = unused TIMSK: u8; // TIFR bits TOV: u8; OCFA: u8; - OCFB: u8; + OCFB: u8; // Optional, if compBInterrupt != 0 OCFC: u8; // Optional, if compCInterrupt != 0 // TIMSK bits TOIE: u8; OCIEA: u8; - OCIEB: u8; + OCIEB: u8; // Optional, if compBInterrupt != 0 OCIEC: u8; // Optional, if compCInterrupt != 0 // Output compare pins compPortA: u16; compPinA: u8; - compPortB: u16; + compPortB: u16; // Optional, 0 = unused compPinB: u8; compPortC: u16; // Optional, 0 = unused compPinC: u16; @@ -87,117 +76,6 @@ export interface AVRTimerConfig { externalClockPin: u8; } -/** These are differnet for some devices (e.g. ATtiny85) */ -const defaultTimerBits = { - // TIFR bits - TOV: 1, - OCFA: 2, - OCFB: 4, - OCFC: 0, // Unused - - // TIMSK bits - TOIE: 1, - OCIEA: 2, - OCIEB: 4, - OCIEC: 0, // Unused -}; - -export const timer0Config: AVRTimerConfig = { - bits: 8, - captureInterrupt: 0, // not available - compAInterrupt: 0x1c, - compBInterrupt: 0x1e, - compCInterrupt: 0, - ovfInterrupt: 0x20, - TIFR: 0x35, - OCRA: 0x47, - OCRB: 0x48, - OCRC: 0, // not available - ICR: 0, // not available - TCNT: 0x46, - TCCRA: 0x44, - TCCRB: 0x45, - TCCRC: 0, // not available - TIMSK: 0x6e, - dividers: timer01Dividers, - compPortA: portDConfig.PORT, - compPinA: 6, - compPortB: portDConfig.PORT, - compPinB: 5, - compPortC: 0, // Not available - compPinC: 0, - externalClockPort: portDConfig.PORT, - externalClockPin: 4, - ...defaultTimerBits, -}; - -export const timer1Config: AVRTimerConfig = { - bits: 16, - captureInterrupt: 0x14, - compAInterrupt: 0x16, - compBInterrupt: 0x18, - compCInterrupt: 0, - ovfInterrupt: 0x1a, - TIFR: 0x36, - OCRA: 0x88, - OCRB: 0x8a, - OCRC: 0, // not available - ICR: 0x86, - TCNT: 0x84, - TCCRA: 0x80, - TCCRB: 0x81, - TCCRC: 0x82, - TIMSK: 0x6f, - dividers: timer01Dividers, - compPortA: portBConfig.PORT, - compPinA: 1, - compPortB: portBConfig.PORT, - compPinB: 2, - compPortC: 0, // Not available - compPinC: 0, - externalClockPort: portDConfig.PORT, - externalClockPin: 5, - ...defaultTimerBits, -}; - -export const timer2Config: AVRTimerConfig = { - bits: 8, - captureInterrupt: 0, // not available - compAInterrupt: 0x0e, - compBInterrupt: 0x10, - compCInterrupt: 0, - ovfInterrupt: 0x12, - TIFR: 0x37, - OCRA: 0xb3, - OCRB: 0xb4, - OCRC: 0, // not available - ICR: 0, // not available - TCNT: 0xb2, - TCCRA: 0xb0, - TCCRB: 0xb1, - TCCRC: 0, // not available - TIMSK: 0x70, - dividers: { - 0: 0, - 1: 1, - 2: 8, - 3: 32, - 4: 64, - 5: 128, - 6: 256, - 7: 1024, - }, - compPortA: portBConfig.PORT, - compPinA: 3, - compPortB: portDConfig.PORT, - compPinB: 3, - compPortC: 0, // Not available - compPinC: 0, - externalClockPort: 0, // Not available - externalClockPin: 0, - ...defaultTimerBits, -}; - /* All the following types and constants are related to WGM (Waveform Generation Mode) bits: */ enum TimerMode { Normal, @@ -289,6 +167,7 @@ export class AVRTimer { private nextOcrA: u16 = 0; private ocrB: u16 = 0; private nextOcrB: u16 = 0; + private hasOCRB = this.config.OCRB > 0; private hasOCRC = this.config.OCRC > 0; private ocrC: u16 = 0; private nextOcrC: u16 = 0; @@ -367,12 +246,14 @@ export class AVRTimer { this.ocrA = this.nextOcrA; } }; - this.cpu.writeHooks[config.OCRB] = (value: u8) => { - this.nextOcrB = (this.highByteTemp << 8) | value; - if (this.ocrUpdateMode === OCRUpdateMode.Immediate) { - this.ocrB = this.nextOcrB; - } - }; + if (this.hasOCRB) { + this.cpu.writeHooks[config.OCRB] = (value: u8) => { + this.nextOcrB = (this.highByteTemp << 8) | value; + if (this.ocrUpdateMode === OCRUpdateMode.Immediate) { + this.ocrB = this.nextOcrB; + } + }; + } if (this.hasOCRC) { this.cpu.writeHooks[config.OCRC] = (value: u8) => { this.nextOcrC = (this.highByteTemp << 8) | value; @@ -406,18 +287,20 @@ export class AVRTimer { this.updateWGMConfig(); return true; }; - cpu.writeHooks[config.TCCRB] = (value) => { - if (!config.TCCRC) { - this.checkForceCompare(value); - value &= ~(FOCA | FOCB); - } - this.cpu.data[config.TCCRB] = value; - this.updateDivider = true; - this.cpu.clearClockEvent(this.count); - this.cpu.addClockEvent(this.count, 0); - this.updateWGMConfig(); - return true; - }; + if (config.TCCRB) { + cpu.writeHooks[config.TCCRB] = (value) => { + if (!config.TCCRC) { + this.checkForceCompare(value); + value &= ~(FOCA | FOCB); + } + this.cpu.data[config.TCCRB] = value; + this.updateDivider = true; + this.cpu.clearClockEvent(this.count); + this.cpu.addClockEvent(this.count, 0); + this.updateWGMConfig(); + return true; + }; + } if (config.TCCRC) { cpu.writeHooks[config.TCCRC] = (value) => { this.checkForceCompare(value); @@ -525,13 +408,15 @@ export class AVRTimer { this.updateCompA(this.compA ? PinOverrideMode.Enable : PinOverrideMode.None); } - const prevCompB = this.compB; - this.compB = ((TCCRA >> 4) & 0x3) as CompBitsValue; - if (this.compB === 1 && pwmMode) { - this.compB = 0; // Reserved, according to the datasheet - } - if (!!prevCompB !== !!this.compB) { - this.updateCompB(this.compB ? PinOverrideMode.Enable : PinOverrideMode.None); + if (this.hasOCRB) { + const prevCompB = this.compB; + this.compB = ((TCCRA >> 4) & 0x3) as CompBitsValue; + if (this.compB === 1 && pwmMode) { + this.compB = 0; // Reserved, according to the datasheet + } + if (!!prevCompB !== !!this.compB) { + this.updateCompB(this.compB ? PinOverrideMode.Enable : PinOverrideMode.None); + } } if (this.hasOCRC) { @@ -700,7 +585,7 @@ export class AVRTimer { } private timerUpdated(value: number, prevValue: number) { - const { ocrA, ocrB, ocrC, hasOCRC } = this; + const { ocrA, ocrB, ocrC, hasOCRB, hasOCRC } = this; const overflow = prevValue > value; if (((prevValue < ocrA || overflow) && value >= ocrA) || (prevValue < ocrA && overflow)) { this.cpu.setInterruptFlag(this.OCFA); @@ -708,7 +593,10 @@ export class AVRTimer { this.updateCompPin(this.compA, 'A'); } } - if (((prevValue < ocrB || overflow) && value >= ocrB) || (prevValue < ocrB && overflow)) { + if ( + hasOCRB && + (((prevValue < ocrB || overflow) && value >= ocrB) || (prevValue < ocrB && overflow)) + ) { this.cpu.setInterruptFlag(this.OCFB); if (this.compB) { this.updateCompPin(this.compB, 'B'); diff --git a/src/peripherals/timer_atmega32.ts b/src/peripherals/timer_atmega32.ts new file mode 100644 index 0000000..92a5902 --- /dev/null +++ b/src/peripherals/timer_atmega32.ts @@ -0,0 +1,154 @@ +import { atmega32PortBConfig, atmega32PortDConfig } from './gpio_atmega32'; +import { AVRTimerConfig, TimerDividers } from './timer'; + +const atmega32TimerDividers: TimerDividers = { + 0: 0, + 1: 1, + 2: 8, + 3: 64, + 4: 256, + 5: 1024, + 6: 0, // External clock - see ExternalClockMode + 7: 0, // Ditto +}; + +export const atmga32Timer0Config: AVRTimerConfig = { + bits: 8, + dividers: atmega32TimerDividers, + + // Interrupt vectors + captureInterrupt: 0, // not used, + compAInterrupt: 0x14, + compBInterrupt: 0, // not used, + compCInterrupt: 0, // not used, + ovfInterrupt: 0x16, + + // Register addresses + TIFR: 0x58, + OCRA: 0x5c, + OCRB: 0, // No timer 0 OCRB on Atmega32 + OCRC: 0, // No timer 0 OCRC on Atmega32 + ICR: 0, // not avilible, + TCNT: 0x52, + TCCRA: 0x53, + TCCRB: 0, // No timer 0 TCCRB on Atmega32 + TCCRC: 0, // No TCCRC on Atmega32, + TIMSK: 0x59, + + // TIFR bits + TOV: 1 << 0, + OCFA: 1 << 1, + OCFB: 0, // No timer 0 OCFB on Atmega32 + OCFC: 0, // No OCFC on Atmega32, + + // TIMSK bits + TOIE: 1 << 0, + OCIEA: 1 << 1, + OCIEB: 0, // No OCIEB on Atmega32 + OCIEC: 0, // No OCFC on Atmega32, + + // Output compare pins + compPortA: atmega32PortBConfig.PORT, + compPinA: 3, + compPortB: 0, // Not available + compPinB: 0, // Not available + compPortC: 0, // Not available + compPinC: 0, // Not available + + externalClockPort: 0, // Unimplement ? Not available + externalClockPin: 0, // Unimplement ? Not available +}; + +export const atmga32Timer1Config: AVRTimerConfig = { + bits: 16, + dividers: atmega32TimerDividers, + + // Interrupt vectors + captureInterrupt: 0x0c, + compAInterrupt: 0x0e, + compBInterrupt: 0x10, + compCInterrupt: 0, // not used, + ovfInterrupt: 0x12, + + // Register addresses + TIFR: 0x58, + OCRA: 0x4a, + OCRB: 0x48, + OCRC: 0, // Optional, 0 = unused + ICR: 0x46, + TCNT: 0x4c, + TCCRA: 0x4f, + TCCRB: 0x4e, + TCCRC: 0, // No TCCRC on Atmega32, + TIMSK: 0x59, + + // TIFR bits + TOV: 1 << 2, + OCFA: 1 << 4, + OCFB: 1 << 3, + OCFC: 0, // No OCFC on Atmega32, + + // TIMSK bits + TOIE: 1 << 2, + OCIEA: 1 << 4, + OCIEB: 1 << 3, + OCIEC: 0, // No OCFC on Atmega32, + + // Output compare pins + compPortA: atmega32PortDConfig.PORT, + compPinA: 5, + compPortB: atmega32PortDConfig.PORT, + compPinB: 4, + compPortC: 0, // Not available + compPinC: 0, // Not available + + externalClockPort: 0, // Unimplemented ? Not available + externalClockPin: 0, // Unimplemented ? Not available +}; + +export const atmga32Timer2Config: AVRTimerConfig = { + bits: 8, + dividers: atmega32TimerDividers, + + // Interrupt vectors + captureInterrupt: 0, // not used, + compAInterrupt: 0x08, // not used, + compBInterrupt: 0, // not used, + compCInterrupt: 0, // not used, + ovfInterrupt: 0x0a, + + // Register addresses + TIFR: 0x58, + OCRA: 0x43, + OCRB: 0, // No timer 2 OCRB on Atmega32 + OCRC: 0, // Optional, 0 = unused + ICR: 0, // not avilible, + TCNT: 0x44, + TCCRA: 0x45, + TCCRB: 0, // No timer 2 TCCRB on Atmega32 + TCCRC: 0, // No TCCRC on Atmega32, + TIMSK: 0x59, + + // TIFR bits + TOV: 1 << 6, + OCFA: 1 << 7, + OCFB: 0, // No timer 2 OCFB on Atmega32 + OCFC: 0, // No OCFC on Atmega32, + + // TIMSK bits + TOIE: 1 << 6, + OCIEA: 1 << 7, + OCIEB: 0, // No timer 2 OCIEB on Atmega32 + OCIEC: 0, // No OCFC on Atmega32, + + // Output compare pins + compPortA: atmega32PortBConfig.PORT, + compPinA: 3, + compPortB: 0, // Not available + compPinB: 0, // Not available + compPortC: 0, // Not available + compPinC: 0, // Not available + + externalClockPort: 0, // Unimplement ? Not available + externalClockPin: 0, // Unimplement ? Not available +}; diff --git a/src/peripherals/timer_atmega328p.ts b/src/peripherals/timer_atmega328p.ts new file mode 100644 index 0000000..7970a84 --- /dev/null +++ b/src/peripherals/timer_atmega328p.ts @@ -0,0 +1,124 @@ +import { portBConfig, portDConfig } from './gpio_atmega328p'; +import { AVRTimerConfig, TimerDividers } from './timer'; + +/** These are differnet for some devices (e.g. ATtiny85) */ +const defaultTimerBits = { + // TIFR bits + TOV: 1, + OCFA: 2, + OCFB: 4, + OCFC: 0, // Unused + + // TIMSK bits + TOIE: 1, + OCIEA: 2, + OCIEB: 4, + OCIEC: 0, // Unused +}; + +const timer01Dividers: TimerDividers = { + 0: 0, + 1: 1, + 2: 8, + 3: 64, + 4: 256, + 5: 1024, + 6: 0, // External clock - see ExternalClockMode + 7: 0, // Ditto +}; + +export const atmega328pTimer0Config: AVRTimerConfig = { + bits: 8, + captureInterrupt: 0, // not available + compAInterrupt: 0x1c, + compBInterrupt: 0x1e, + compCInterrupt: 0, + ovfInterrupt: 0x20, + TIFR: 0x35, + OCRA: 0x47, + OCRB: 0x48, + OCRC: 0, // not available + ICR: 0, // not available + TCNT: 0x46, + TCCRA: 0x44, + TCCRB: 0x45, + TCCRC: 0, // not available + TIMSK: 0x6e, + dividers: timer01Dividers, + compPortA: portDConfig.PORT, + compPinA: 6, + compPortB: portDConfig.PORT, + compPinB: 5, + compPortC: 0, // Not available + compPinC: 0, + externalClockPort: portDConfig.PORT, + externalClockPin: 4, + ...defaultTimerBits, +}; + +export const atmega328pTimer1Config: AVRTimerConfig = { + bits: 16, + captureInterrupt: 0x14, + compAInterrupt: 0x16, + compBInterrupt: 0x18, + compCInterrupt: 0, + ovfInterrupt: 0x1a, + TIFR: 0x36, + OCRA: 0x88, + OCRB: 0x8a, + OCRC: 0, // not available + ICR: 0x86, + TCNT: 0x84, + TCCRA: 0x80, + TCCRB: 0x81, + TCCRC: 0x82, + TIMSK: 0x6f, + dividers: timer01Dividers, + compPortA: portBConfig.PORT, + compPinA: 1, + compPortB: portBConfig.PORT, + compPinB: 2, + compPortC: 0, // Not available + compPinC: 0, + externalClockPort: portDConfig.PORT, + externalClockPin: 5, + ...defaultTimerBits, +}; + +export const atmega328pTimer2Config: AVRTimerConfig = { + bits: 8, + captureInterrupt: 0, // not available + compAInterrupt: 0x0e, + compBInterrupt: 0x10, + compCInterrupt: 0, + ovfInterrupt: 0x12, + TIFR: 0x37, + OCRA: 0xb3, + OCRB: 0xb4, + OCRC: 0, // not available + ICR: 0, // not available + TCNT: 0xb2, + TCCRA: 0xb0, + TCCRB: 0xb1, + TCCRC: 0, // not available + TIMSK: 0x70, + dividers: { + 0: 0, + 1: 1, + 2: 8, + 3: 32, + 4: 64, + 5: 128, + 6: 256, + 7: 1024, + }, + compPortA: portBConfig.PORT, + compPinA: 3, + compPortB: portDConfig.PORT, + compPinB: 3, + compPortC: 0, // Not available + compPinC: 0, + externalClockPort: 0, // Not available + externalClockPin: 0, + ...defaultTimerBits, +}; diff --git a/src/peripherals/usart_atmega32.ts b/src/peripherals/usart_atmega32.ts new file mode 100644 index 0000000..75b0d35 --- /dev/null +++ b/src/peripherals/usart_atmega32.ts @@ -0,0 +1,13 @@ +import { USARTConfig } from './usart'; + +export const atmega32Usart0Config: USARTConfig = { + rxCompleteInterrupt: 0x1a, + dataRegisterEmptyInterrupt: 0x1c, + txCompleteInterrupt: 0x1e, + UCSRA: 0x2b, + UCSRB: 0x2a, + UCSRC: 0x40, // <----------- TODO + UBRRL: 0x29, + UBRRH: 0x40, // <----------- TODO + UDR: 0x2c, +}; From 5ec4459b4130784db40fb2086eba340b86e4a84b Mon Sep 17 00:00:00 2001 From: James Date: Thu, 2 Feb 2023 13:24:55 -0500 Subject: [PATCH 02/10] Wip: Getting ATmega32 values in for USART, tests failing --- package.json | 1 + src/index.ts | 15 +++---- src/peripherals/usart.ts | 65 ++++++++++++++++++++----------- src/peripherals/usart_atmega32.ts | 4 +- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 4596bbf..8af5483 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "build:demo": "vite build demo", "prepare": "husky install && npm run build", "start": "vite demo", + "start2": "vite clidemo", "lint": "eslint src/**/*.ts demo/**/*.ts", "test": "npm run lint && jest", "test:watch": "jest --watch", diff --git a/src/index.ts b/src/index.ts index 0313607..5f1903f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,13 +8,8 @@ export { CPU } from './cpu/cpu'; export type { CPUMemoryHook, CPUMemoryHooks } from './cpu/cpu'; export { avrInstruction } from './cpu/instruction'; export { avrInterrupt } from './cpu/interrupt'; -export { - adcConfig, - ADCMuxInputType, - ADCReference, - atmega328Channels, - AVRADC, -} from './peripherals/adc'; +export { ADCMuxInputType, ADCReference, AVRADC } from './peripherals/adc'; +export { atmega328AdcConfig, atmega328Channels } from './peripherals/adc_atmega328p'; export type { ADCConfig, ADCMuxConfiguration, ADCMuxInput } from './peripherals/adc'; export { AVRClock, clockConfig } from './peripherals/clock'; export type { AVRClockConfig } from './peripherals/clock'; @@ -63,9 +58,9 @@ export { AVRSPI, spiConfig } from './peripherals/spi'; export type { SPIConfig, SPITransferCallback } from './peripherals/spi'; export { AVRTimer } from './peripherals/timer'; export { - atmega328pTimer0Config as atmega329pTimer0Config, - atmega328pTimer1Config as atmega329pTimer1Config, - atmega328pTimer2Config as atmega329pTimer2Config, + atmega328pTimer0Config, + atmega328pTimer1Config, + atmega328pTimer2Config, } from './peripherals/timer_atmega328p'; export type { AVRTimerConfig } from './peripherals/timer'; export * from './peripherals/twi'; diff --git a/src/peripherals/usart.ts b/src/peripherals/usart.ts index 1b7546b..cb71006 100644 --- a/src/peripherals/usart.ts +++ b/src/peripherals/usart.ts @@ -7,7 +7,7 @@ */ import { AVRInterruptConfig, CPU } from '../cpu/cpu'; -import { u8 } from '../types'; +import { u16, u8 } from '../types'; export interface USARTConfig { rxCompleteInterrupt: u8; @@ -58,11 +58,11 @@ const UCSRB_UCSZ2 = 0x4; // Character Size 2 const UCSRB_RXB8 = 0x2; // Receive Data Bit 8 const UCSRB_TXB8 = 0x1; // Transmit Data Bit 8 const UCSRB_CFG_MASK = UCSRB_UCSZ2 | UCSRB_RXEN | UCSRB_TXEN; -const UCSRC_UMSEL1 = 0x80; // USART Mode Select 1 -const UCSRC_UMSEL0 = 0x40; // USART Mode Select 0 -const UCSRC_UPM1 = 0x20; // Parity Mode 1 -const UCSRC_UPM0 = 0x10; // Parity Mode 0 -const UCSRC_USBS = 0x8; // Stop Bit Select +const UCSRC_URSEL = 0x80; // Register select +const UCSRC_UMSEL = 0x40; // Mode USART Mode Select +const UCSRC_UPM1 = 0x20; // Parity mode 1 +const UCSRC_UPM0 = 0x10; // Parity mode 1 +const UCSRC_USBS = 0x8; // Stop bit select const UCSRC_UCSZ1 = 0x4; // Character Size 1 const UCSRC_UCSZ0 = 0x2; // Character Size 0 const UCSRC_UCPOL = 0x1; // Clock Polarity @@ -75,12 +75,17 @@ const rxMasks = { 8: 0xff, 9: 0xff, }; + export class AVRUSART { public onByteTransmit: USARTTransmitCallback | null = null; public onLineTransmit: USARTLineTransmitCallback | null = null; public onRxComplete: (() => void) | null = null; public onConfigurationChange: USARTConfigurationChangeCallback | null = null; + private UBRRH: u8 = 0; + private UCSRC: u8 = 0; + private lastUbrrhReadCycle = 0; + private rxBusyValue = false; private rxByte = 0; private lineBuffer = ''; @@ -136,11 +141,8 @@ export class AVRUSART { } return true; }; - this.cpu.writeHooks[config.UCSRC] = (value) => { - cpu.data[config.UCSRC] = value; - this.onConfigurationChange?.(); - return true; - }; + this.cpu.writeHooks[config.UCSRC] = (value) => this.writeUCSRCOrUBRRH(value); + this.cpu.readHooks[config.UCSRC] = () => this.readUCSRCOrUBRRH(); this.cpu.readHooks[config.UDR] = () => { const mask = rxMasks[this.bitsPerChar] ?? 0xff; const result = this.rxByte & mask; @@ -168,11 +170,8 @@ export class AVRUSART { this.cpu.clearInterrupt(this.TXC); this.cpu.clearInterrupt(this.UDRE); }; - this.cpu.writeHooks[config.UBRRH] = (value) => { - this.cpu.data[config.UBRRH] = value; - this.onConfigurationChange?.(); - return true; - }; + this.cpu.writeHooks[config.UBRRH] = (value) => this.writeUCSRCOrUBRRH(value); + this.cpu.readHooks[config.UBRRH] = () => this.readUCSRCOrUBRRH(); this.cpu.writeHooks[config.UBRRL] = (value) => { this.cpu.data[config.UBRRL] = value; this.onConfigurationChange?.(); @@ -183,10 +182,11 @@ export class AVRUSART { reset() { this.cpu.data[this.config.UCSRA] = UCSRA_UDRE; this.cpu.data[this.config.UCSRB] = 0; - this.cpu.data[this.config.UCSRC] = UCSRC_UCSZ1 | UCSRC_UCSZ0; // default: 8 bits per byte + this.UCSRC = UCSRC_URSEL | UCSRC_UCSZ1 | UCSRC_UCSZ0; // default: 8 bits per byte this.rxBusyValue = false; this.rxByte = 0; this.lineBuffer = ''; + this.lastUbrrhReadCycle = 0; } get rxBusy() { @@ -212,14 +212,33 @@ export class AVRUSART { } } + // Reference ATmega32 datasheet section "Accessing UBRRH/ UCSRC Registers" + private writeUCSRCOrUBRRH(value: number) { + if (value & UCSRC_URSEL) { + this.UCSRC = value; + } else { + this.UBRRH = value; + } + this.onConfigurationChange?.(); + return true; + } + + private readUCSRCOrUBRRH() { + if (this.lastUbrrhReadCycle === this.cpu.cycles - 1) { + return this.UCSRC; + } + this.lastUbrrhReadCycle = this.cpu.cycles; + return this.UBRRH; + } + private get cyclesPerChar() { const symbolsPerChar = 1 + this.bitsPerChar + this.stopBits + (this.parityEnabled ? 1 : 0); return (this.UBRR + 1) * this.multiplier * symbolsPerChar; } private get UBRR() { - const { UBRRH, UBRRL } = this.config; - return (this.cpu.data[UBRRH] << 8) | this.cpu.data[UBRRL]; + const { UBRRL } = this.config; + return (this.UBRRH << 8) | this.cpu.data[UBRRL]; } private get multiplier() { @@ -240,7 +259,7 @@ export class AVRUSART { get bitsPerChar() { const ucsz = - ((this.cpu.data[this.config.UCSRC] & (UCSRC_UCSZ1 | UCSRC_UCSZ0)) >> 1) | + ((this.UCSRC & (UCSRC_UCSZ1 | UCSRC_UCSZ0)) >> 1) | (this.cpu.data[this.config.UCSRB] & UCSRB_UCSZ2); switch (ucsz) { case 0: @@ -258,14 +277,14 @@ export class AVRUSART { } get stopBits() { - return this.cpu.data[this.config.UCSRC] & UCSRC_USBS ? 2 : 1; + return this.UCSRC & UCSRC_USBS ? 2 : 1; } get parityEnabled() { - return this.cpu.data[this.config.UCSRC] & UCSRC_UPM1 ? true : false; + return this.UCSRC & UCSRC_UPM1 ? true : false; } get parityOdd() { - return this.cpu.data[this.config.UCSRC] & UCSRC_UPM0 ? true : false; + return this.UCSRC & UCSRC_UPM0 ? true : false; } } diff --git a/src/peripherals/usart_atmega32.ts b/src/peripherals/usart_atmega32.ts index 75b0d35..1008b90 100644 --- a/src/peripherals/usart_atmega32.ts +++ b/src/peripherals/usart_atmega32.ts @@ -6,8 +6,8 @@ export const atmega32Usart0Config: USARTConfig = { txCompleteInterrupt: 0x1e, UCSRA: 0x2b, UCSRB: 0x2a, - UCSRC: 0x40, // <----------- TODO + UCSRC: 0x40, UBRRL: 0x29, - UBRRH: 0x40, // <----------- TODO + UBRRH: 0x40, UDR: 0x2c, }; From 8d13edd1530f0b81aefc44cc6bc71d15734be807 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 4 Feb 2023 00:13:52 -0500 Subject: [PATCH 03/10] ATmega32: More work on separating out USART to compose off of 328p --- src/index.ts | 2 +- src/peripherals/usart.spec.ts | 58 ++--- src/peripherals/usart.ts | 330 ++++++---------------------- src/peripherals/usart_atmega32.ts | 63 +++++- src/peripherals/usart_atmega328p.ts | 241 ++++++++++++++++++++ 5 files changed, 397 insertions(+), 297 deletions(-) create mode 100644 src/peripherals/usart_atmega328p.ts diff --git a/src/index.ts b/src/index.ts index 5f1903f..49de267 100644 --- a/src/index.ts +++ b/src/index.ts @@ -64,7 +64,7 @@ export { } from './peripherals/timer_atmega328p'; export type { AVRTimerConfig } from './peripherals/timer'; export * from './peripherals/twi'; -export { AVRUSART, usart0Config } from './peripherals/usart'; +export { AVRUSART, atmega328pUsart0Config } from './peripherals/usart_atmega328p'; export { AVRUSI } from './peripherals/usi'; export { AVRWatchdog, watchdogConfig } from './peripherals/watchdog'; export type { WatchdogConfig } from './peripherals/watchdog'; diff --git a/src/peripherals/usart.spec.ts b/src/peripherals/usart.spec.ts index 8dbd5ce..85115a4 100644 --- a/src/peripherals/usart.spec.ts +++ b/src/peripherals/usart.spec.ts @@ -1,5 +1,5 @@ import { CPU } from '../cpu/cpu'; -import { AVRUSART, usart0Config } from './usart'; +import { AVRUSART, atmega328pUsart0Config } from './usart_atmega328p'; const FREQ_16MHZ = 16e6; const FREQ_11_0529MHZ = 11059200; @@ -38,7 +38,7 @@ const UCSZ2 = 4; describe('USART', () => { it('should correctly calculate the baudRate from UBRR', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_11_0529MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_11_0529MHZ); cpu.writeData(UBRR0H, 0); cpu.writeData(UBRR0L, 5); expect(usart.baudRate).toEqual(115200); @@ -46,7 +46,7 @@ describe('USART', () => { it('should correctly calculate the baudRate from UBRR in double-speed mode', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UBRR0H, 3); cpu.writeData(UBRR0L, 64); cpu.writeData(UCSR0A, U2X0); @@ -55,7 +55,7 @@ describe('USART', () => { it('should call onConfigurationChange when the baudRate changes', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); const onConfigurationChange = jest.fn(); usart.onConfigurationChange = onConfigurationChange; @@ -74,35 +74,35 @@ describe('USART', () => { describe('bitsPerChar', () => { it('should return 5-bits per byte when UCSZ = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, 0); expect(usart.bitsPerChar).toEqual(5); }); it('should return 6-bits per byte when UCSZ = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ0); expect(usart.bitsPerChar).toEqual(6); }); it('should return 7-bits per byte when UCSZ = 2', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ1); expect(usart.bitsPerChar).toEqual(7); }); it('should return 8-bits per byte when UCSZ = 3', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ0 | UCSZ1); expect(usart.bitsPerChar).toEqual(8); }); it('should return 9-bits per byte when UCSZ = 7', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ0 | UCSZ1); cpu.writeData(UCSR0B, UCSZ2); expect(usart.bitsPerChar).toEqual(9); @@ -110,7 +110,7 @@ describe('USART', () => { it('should call onConfigurationChange when bitsPerChar change', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); const onConfigurationChange = jest.fn(); usart.onConfigurationChange = onConfigurationChange; @@ -130,13 +130,13 @@ describe('USART', () => { describe('stopBits', () => { it('should return 1 when USBS = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); expect(usart.stopBits).toEqual(1); }); it('should return 2 when USBS = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, USBS); expect(usart.stopBits).toEqual(2); }); @@ -145,13 +145,13 @@ describe('USART', () => { describe('parityEnabled', () => { it('should return false when UPM1 = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); expect(usart.parityEnabled).toEqual(false); }); it('should return true when UPM1 = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UPM1); expect(usart.parityEnabled).toEqual(true); }); @@ -160,13 +160,13 @@ describe('USART', () => { describe('parityOdd', () => { it('should return false when UPM0 = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); expect(usart.parityOdd).toEqual(false); }); it('should return true when UPM0 = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UPM0); expect(usart.parityOdd).toEqual(true); }); @@ -174,7 +174,7 @@ describe('USART', () => { it('should invoke onByteTransmit when UDR0 is written to', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); usart.onByteTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x61); @@ -184,7 +184,7 @@ describe('USART', () => { describe('txEnable/rxEnable', () => { it('txEnable should equal true when the transitter is enabled', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); usart.onByteTransmit = jest.fn(); expect(usart.txEnable).toEqual(false); cpu.writeData(UCSR0B, TXEN); @@ -193,7 +193,7 @@ describe('USART', () => { it('rxEnable should equal true when the transitter is enabled', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); usart.onByteTransmit = jest.fn(); expect(usart.rxEnable).toEqual(false); cpu.writeData(UCSR0B, RXEN); @@ -204,7 +204,7 @@ describe('USART', () => { describe('tick()', () => { it('should trigger data register empty interrupt if UDRE is set', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, UDRIE | TXEN); cpu.data[SREG] = 0x80; // SREG: I------- cpu.tick(); @@ -215,7 +215,7 @@ describe('USART', () => { it('should trigger data TX Complete interrupt if TXCIE is set', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, TXCIE | TXEN); cpu.writeData(UDR0, 0x61); cpu.data[SREG] = 0x80; // SREG: I------- @@ -228,7 +228,7 @@ describe('USART', () => { it('should not trigger data TX Complete interrupt if UDR was not written to', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, TXCIE | TXEN); cpu.data[SREG] = 0x80; // SREG: I------- cpu.tick(); @@ -238,7 +238,7 @@ describe('USART', () => { it('should not trigger any interrupt if interrupts are disabled', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, UDRIE | TXEN); cpu.writeData(UDR0, 0x61); cpu.data[SREG] = 0; // SREG: 0 (disable interrupts) @@ -253,7 +253,7 @@ describe('USART', () => { describe('onLineTransmit', () => { it('should call onLineTransmit with the current line buffer after every newline', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); usart.onLineTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x48); // 'H' @@ -267,7 +267,7 @@ describe('USART', () => { it('should not call onLineTransmit if no newline was received', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); usart.onLineTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x48); // 'H' @@ -277,7 +277,7 @@ describe('USART', () => { it('should clear the line buffer after each call to onLineTransmit', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); usart.onLineTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x48); // 'H' @@ -296,7 +296,7 @@ describe('USART', () => { describe('writeByte', () => { it('should return false if called when RX is busy', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, RXEN); cpu.writeData(UBRR0L, 103); // baud: 9600 expect(usart.writeByte(10)).toEqual(true); @@ -309,7 +309,7 @@ describe('USART', () => { describe('Integration tests', () => { it('should set the TXC bit after ~1.04mS when baud rate set to 9600', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UBRR0L, 103); // baud: 9600 cpu.writeData(UDR0, 0x48); // 'H' @@ -323,7 +323,7 @@ describe('USART', () => { it('should be ready to recieve the next byte after ~1.04ms when baudrate set to 9600', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); const rxCompleteCallback = jest.fn(); usart.onRxComplete = rxCompleteCallback; cpu.writeData(UCSR0B, RXEN); diff --git a/src/peripherals/usart.ts b/src/peripherals/usart.ts index cb71006..21b7481 100644 --- a/src/peripherals/usart.ts +++ b/src/peripherals/usart.ts @@ -6,10 +6,71 @@ * Copyright (C) 2019, 2020, 2021 Uri Shaked */ -import { AVRInterruptConfig, CPU } from '../cpu/cpu'; -import { u16, u8 } from '../types'; +import { u8 } from '../types'; + +interface USARTRegisterBits { + // UCSRA bits + UCSRA_RXC: u8; // USART Receive Complete + UCSRA_TXC: u8; // USART Transmit Complete + UCSRA_UDRE: u8; // USART Data Register Empty + UCSRA_FE: u8; // Frame Error + UCSRA_DOR: u8; // Data OverRun + UCSRA_UPE: u8; // USART Parity Error + UCSRA_U2X: u8; // Double the USART Transmission Speed + UCSRA_MPCM: u8; // Multi-processor Communication Mode + + // UCSRB bits + UCSRB_RXCIE: u8; // RX Complete Interrupt Enable + UCSRB_TXCIE: u8; // TX Complete Interrupt Enable + UCSRB_UDRIE: u8; // USART Data Register Empty Interrupt Enable + UCSRB_RXEN: u8; // Receiver Enable + UCSRB_TXEN: u8; // Transmitter Enable + UCSRB_UCSZ2: u8; // Character Size 2 + UCSRB_RXB8: u8; // Receive Data Bit 8 + UCSRB_TXB8: u8; // Transmit Data Bit 8 + + // UCSRC bits + UCSRC_URSEL: u8; // Register select, 0 = unsupported + UCSRC_UMSEL1: u8; // USART Mode Select 1, 0 = unsupported + UCSRC_UMSEL0: u8; // USART Mode Select 0 + UCSRC_UPM1: u8; // Parity Mode 1 + UCSRC_UPM0: u8; // Parity Mode 0 + UCSRC_USBS: u8; // Stop Bit Select + UCSRC_UCSZ1: u8; // Character Size 1 + UCSRC_UCSZ0: u8; // Character Size 0 + UCSRC_UCPOL: u8; // Clock Polarity +} + +// Bits shared amongst "many" AVRs: 328p, 2650, 16, 32, and 64 +export const standardUartRegisterBits: Omit = { + UCSRA_RXC: 0x80, + UCSRA_TXC: 0x40, + UCSRA_UDRE: 0x20, + UCSRA_FE: 0x10, + UCSRA_DOR: 0x8, + UCSRA_UPE: 0x4, + UCSRA_U2X: 0x2, + UCSRA_MPCM: 0x1, + + UCSRB_RXCIE: 0x80, + UCSRB_TXCIE: 0x40, + UCSRB_UDRIE: 0x20, + UCSRB_RXEN: 0x10, + UCSRB_TXEN: 0x8, + UCSRB_UCSZ2: 0x4, + UCSRB_RXB8: 0x2, + UCSRB_TXB8: 0x1, + + UCSRC_UMSEL0: 0x40, + UCSRC_UPM1: 0x20, + UCSRC_UPM0: 0x10, + UCSRC_USBS: 0x8, + UCSRC_UCSZ1: 0x4, + UCSRC_UCSZ0: 0x2, + UCSRC_UCPOL: 0x1, +}; -export interface USARTConfig { +export interface USARTConfig extends USARTRegisterBits { rxCompleteInterrupt: u8; dataRegisterEmptyInterrupt: u8; txCompleteInterrupt: u8; @@ -22,269 +83,6 @@ export interface USARTConfig { UDR: u8; } -export const usart0Config: USARTConfig = { - rxCompleteInterrupt: 0x24, - dataRegisterEmptyInterrupt: 0x26, - txCompleteInterrupt: 0x28, - UCSRA: 0xc0, - UCSRB: 0xc1, - UCSRC: 0xc2, - UBRRL: 0xc4, - UBRRH: 0xc5, - UDR: 0xc6, -}; - export type USARTTransmitCallback = (value: u8) => void; export type USARTLineTransmitCallback = (value: string) => void; export type USARTConfigurationChangeCallback = () => void; - -/* eslint-disable @typescript-eslint/no-unused-vars */ -// Register bits: -const UCSRA_RXC = 0x80; // USART Receive Complete -const UCSRA_TXC = 0x40; // USART Transmit Complete -const UCSRA_UDRE = 0x20; // USART Data Register Empty -const UCSRA_FE = 0x10; // Frame Error -const UCSRA_DOR = 0x8; // Data OverRun -const UCSRA_UPE = 0x4; // USART Parity Error -const UCSRA_U2X = 0x2; // Double the USART Transmission Speed -const UCSRA_MPCM = 0x1; // Multi-processor Communication Mode -const UCSRA_CFG_MASK = UCSRA_U2X; -const UCSRB_RXCIE = 0x80; // RX Complete Interrupt Enable -const UCSRB_TXCIE = 0x40; // TX Complete Interrupt Enable -const UCSRB_UDRIE = 0x20; // USART Data Register Empty Interrupt Enable -const UCSRB_RXEN = 0x10; // Receiver Enable -const UCSRB_TXEN = 0x8; // Transmitter Enable -const UCSRB_UCSZ2 = 0x4; // Character Size 2 -const UCSRB_RXB8 = 0x2; // Receive Data Bit 8 -const UCSRB_TXB8 = 0x1; // Transmit Data Bit 8 -const UCSRB_CFG_MASK = UCSRB_UCSZ2 | UCSRB_RXEN | UCSRB_TXEN; -const UCSRC_URSEL = 0x80; // Register select -const UCSRC_UMSEL = 0x40; // Mode USART Mode Select -const UCSRC_UPM1 = 0x20; // Parity mode 1 -const UCSRC_UPM0 = 0x10; // Parity mode 1 -const UCSRC_USBS = 0x8; // Stop bit select -const UCSRC_UCSZ1 = 0x4; // Character Size 1 -const UCSRC_UCSZ0 = 0x2; // Character Size 0 -const UCSRC_UCPOL = 0x1; // Clock Polarity -/* eslint-enable @typescript-eslint/no-unused-vars */ - -const rxMasks = { - 5: 0x1f, - 6: 0x3f, - 7: 0x7f, - 8: 0xff, - 9: 0xff, -}; - -export class AVRUSART { - public onByteTransmit: USARTTransmitCallback | null = null; - public onLineTransmit: USARTLineTransmitCallback | null = null; - public onRxComplete: (() => void) | null = null; - public onConfigurationChange: USARTConfigurationChangeCallback | null = null; - - private UBRRH: u8 = 0; - private UCSRC: u8 = 0; - private lastUbrrhReadCycle = 0; - - private rxBusyValue = false; - private rxByte = 0; - private lineBuffer = ''; - - // Interrupts - private RXC: AVRInterruptConfig = { - address: this.config.rxCompleteInterrupt, - flagRegister: this.config.UCSRA, - flagMask: UCSRA_RXC, - enableRegister: this.config.UCSRB, - enableMask: UCSRB_RXCIE, - constant: true, - }; - private UDRE: AVRInterruptConfig = { - address: this.config.dataRegisterEmptyInterrupt, - flagRegister: this.config.UCSRA, - flagMask: UCSRA_UDRE, - enableRegister: this.config.UCSRB, - enableMask: UCSRB_UDRIE, - }; - private TXC: AVRInterruptConfig = { - address: this.config.txCompleteInterrupt, - flagRegister: this.config.UCSRA, - flagMask: UCSRA_TXC, - enableRegister: this.config.UCSRB, - enableMask: UCSRB_TXCIE, - }; - - constructor(private cpu: CPU, private config: USARTConfig, private freqHz: number) { - this.reset(); - this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => { - cpu.data[config.UCSRA] = value & (UCSRA_MPCM | UCSRA_U2X); - cpu.clearInterruptByFlag(this.TXC, value); - if ((value & UCSRA_CFG_MASK) !== (oldValue & UCSRA_CFG_MASK)) { - this.onConfigurationChange?.(); - } - return true; - }; - this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => { - cpu.updateInterruptEnable(this.RXC, value); - cpu.updateInterruptEnable(this.UDRE, value); - cpu.updateInterruptEnable(this.TXC, value); - if (value & UCSRB_RXEN && oldValue & UCSRB_RXEN) { - cpu.clearInterrupt(this.RXC); - } - if (value & UCSRB_TXEN && !(oldValue & UCSRB_TXEN)) { - // Enabling the transmission - mark UDR as empty - cpu.setInterruptFlag(this.UDRE); - } - cpu.data[config.UCSRB] = value; - if ((value & UCSRB_CFG_MASK) !== (oldValue & UCSRB_CFG_MASK)) { - this.onConfigurationChange?.(); - } - return true; - }; - this.cpu.writeHooks[config.UCSRC] = (value) => this.writeUCSRCOrUBRRH(value); - this.cpu.readHooks[config.UCSRC] = () => this.readUCSRCOrUBRRH(); - this.cpu.readHooks[config.UDR] = () => { - const mask = rxMasks[this.bitsPerChar] ?? 0xff; - const result = this.rxByte & mask; - this.rxByte = 0; - this.cpu.clearInterrupt(this.RXC); - return result; - }; - this.cpu.writeHooks[config.UDR] = (value) => { - if (this.onByteTransmit) { - this.onByteTransmit(value); - } - if (this.onLineTransmit) { - const ch = String.fromCharCode(value); - if (ch === '\n') { - this.onLineTransmit(this.lineBuffer); - this.lineBuffer = ''; - } else { - this.lineBuffer += ch; - } - } - this.cpu.addClockEvent(() => { - cpu.setInterruptFlag(this.UDRE); - cpu.setInterruptFlag(this.TXC); - }, this.cyclesPerChar); - this.cpu.clearInterrupt(this.TXC); - this.cpu.clearInterrupt(this.UDRE); - }; - this.cpu.writeHooks[config.UBRRH] = (value) => this.writeUCSRCOrUBRRH(value); - this.cpu.readHooks[config.UBRRH] = () => this.readUCSRCOrUBRRH(); - this.cpu.writeHooks[config.UBRRL] = (value) => { - this.cpu.data[config.UBRRL] = value; - this.onConfigurationChange?.(); - return true; - }; - } - - reset() { - this.cpu.data[this.config.UCSRA] = UCSRA_UDRE; - this.cpu.data[this.config.UCSRB] = 0; - this.UCSRC = UCSRC_URSEL | UCSRC_UCSZ1 | UCSRC_UCSZ0; // default: 8 bits per byte - this.rxBusyValue = false; - this.rxByte = 0; - this.lineBuffer = ''; - this.lastUbrrhReadCycle = 0; - } - - get rxBusy() { - return this.rxBusyValue; - } - - writeByte(value: number, immediate = false) { - const { cpu } = this; - if (this.rxBusyValue || !this.rxEnable) { - return false; - } - if (immediate) { - this.rxByte = value; - cpu.setInterruptFlag(this.RXC); - this.onRxComplete?.(); - } else { - this.rxBusyValue = true; - cpu.addClockEvent(() => { - this.rxBusyValue = false; - this.writeByte(value, true); - }, this.cyclesPerChar); - return true; - } - } - - // Reference ATmega32 datasheet section "Accessing UBRRH/ UCSRC Registers" - private writeUCSRCOrUBRRH(value: number) { - if (value & UCSRC_URSEL) { - this.UCSRC = value; - } else { - this.UBRRH = value; - } - this.onConfigurationChange?.(); - return true; - } - - private readUCSRCOrUBRRH() { - if (this.lastUbrrhReadCycle === this.cpu.cycles - 1) { - return this.UCSRC; - } - this.lastUbrrhReadCycle = this.cpu.cycles; - return this.UBRRH; - } - - private get cyclesPerChar() { - const symbolsPerChar = 1 + this.bitsPerChar + this.stopBits + (this.parityEnabled ? 1 : 0); - return (this.UBRR + 1) * this.multiplier * symbolsPerChar; - } - - private get UBRR() { - const { UBRRL } = this.config; - return (this.UBRRH << 8) | this.cpu.data[UBRRL]; - } - - private get multiplier() { - return this.cpu.data[this.config.UCSRA] & UCSRA_U2X ? 8 : 16; - } - - get rxEnable() { - return !!(this.cpu.data[this.config.UCSRB] & UCSRB_RXEN); - } - - get txEnable() { - return !!(this.cpu.data[this.config.UCSRB] & UCSRB_TXEN); - } - - get baudRate() { - return Math.floor(this.freqHz / (this.multiplier * (1 + this.UBRR))); - } - - get bitsPerChar() { - const ucsz = - ((this.UCSRC & (UCSRC_UCSZ1 | UCSRC_UCSZ0)) >> 1) | - (this.cpu.data[this.config.UCSRB] & UCSRB_UCSZ2); - switch (ucsz) { - case 0: - return 5; - case 1: - return 6; - case 2: - return 7; - case 3: - return 8; - default: // 4..6 are reserved - case 7: - return 9; - } - } - - get stopBits() { - return this.UCSRC & UCSRC_USBS ? 2 : 1; - } - - get parityEnabled() { - return this.UCSRC & UCSRC_UPM1 ? true : false; - } - - get parityOdd() { - return this.UCSRC & UCSRC_UPM0 ? true : false; - } -} diff --git a/src/peripherals/usart_atmega32.ts b/src/peripherals/usart_atmega32.ts index 1008b90..373d925 100644 --- a/src/peripherals/usart_atmega32.ts +++ b/src/peripherals/usart_atmega32.ts @@ -1,4 +1,14 @@ -import { USARTConfig } from './usart'; +/** + * AVR-8 USART Peripheral + * Part of AVR8js + * Reference: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf + * + * Copyright (C) 2019, 2020, 2021 Uri Shaked + */ +import { CPU } from '../cpu/cpu'; +import { u8 } from '../types'; +import { standardUartRegisterBits, USARTConfig } from './usart'; +import { AVRUSART } from './usart_atmega328p'; export const atmega32Usart0Config: USARTConfig = { rxCompleteInterrupt: 0x1a, @@ -10,4 +20,55 @@ export const atmega32Usart0Config: USARTConfig = { UBRRL: 0x29, UBRRH: 0x40, UDR: 0x2c, + + ...standardUartRegisterBits, + UCSRC_URSEL: 0x80, + UCSRC_UMSEL1: 0, // Not in Atmega32 + UCSRC_UMSEL0: 0x40, }; + +// +// In the Atmega32 (and ATmega16, but not ATmega64): +// Reference ATmega32 datasheet section "Accessing UBRRH/ UCSRC Registers" +// +// The UBRRH Register shares the same I/O location as the UCSRC Register. Therefore some +// special consideration must be taken when accessing this I/O location. +// + +export class AVRUSARTATmega32 extends AVRUSART { + private UBRRH: u8 = 0; + private UCSRC: u8 = 0; + private lastUbrrhReadCycle = 0; + + constructor(cpu: CPU, config: USARTConfig, freqHz: number) { + super(cpu, config, freqHz); + this.cpu.writeHooks[config.UCSRC] = (value) => this.writeUCSRCOrUBRRH(value); + this.cpu.readHooks[config.UCSRC] = () => this.readUCSRCOrUBRRH(); + this.cpu.writeHooks[config.UBRRH] = (value) => this.writeUCSRCOrUBRRH(value); + this.cpu.readHooks[config.UBRRH] = () => this.readUCSRCOrUBRRH(); + } + + reset() { + const { UCSRC_URSEL, UCSRC_UCSZ1, UCSRC_UCSZ0 } = this.config; + super.reset(); + this.UCSRC = UCSRC_URSEL | UCSRC_UCSZ1 | UCSRC_UCSZ0; // default: 8 bits per byte + } + + private writeUCSRCOrUBRRH(value: number) { + if (value & this.config.UCSRC_URSEL) { + this.UCSRC = value; + } else { + this.UBRRH = value; + } + this.onConfigurationChange?.(); + return true; + } + + private readUCSRCOrUBRRH() { + if (this.lastUbrrhReadCycle === this.cpu.cycles - 1) { + return this.UCSRC; + } + this.lastUbrrhReadCycle = this.cpu.cycles; + return this.UBRRH; + } +} diff --git a/src/peripherals/usart_atmega328p.ts b/src/peripherals/usart_atmega328p.ts new file mode 100644 index 0000000..3b3d5bd --- /dev/null +++ b/src/peripherals/usart_atmega328p.ts @@ -0,0 +1,241 @@ +/** + * AVR-8 USART Peripheral + * Part of AVR8js + * Reference: http: + * + * Copyright (C) 2019, 2020, 2021 Uri Shaked + */ + +import { AVRInterruptConfig, CPU } from '../cpu/cpu'; +import { + standardUartRegisterBits, + USARTConfig, + USARTConfigurationChangeCallback, + USARTLineTransmitCallback, + USARTTransmitCallback, +} from './usart'; + +export const atmega328pUsart0Config: USARTConfig = { + rxCompleteInterrupt: 0x24, + dataRegisterEmptyInterrupt: 0x26, + txCompleteInterrupt: 0x28, + + UCSRA: 0xc0, + UCSRB: 0xc1, + UCSRC: 0xc2, + UBRRL: 0xc4, + UBRRH: 0xc5, + UDR: 0xc6, + + ...standardUartRegisterBits, + UCSRC_URSEL: 0, // Not applicable + UCSRC_UMSEL1: 0x80, + UCSRC_UMSEL0: 0x40, +}; + +const rxMasks = { + 5: 0x1f, + 6: 0x3f, + 7: 0x7f, + 8: 0xff, + 9: 0xff, +}; + +export class AVRUSART { + public onByteTransmit: USARTTransmitCallback | null = null; + public onLineTransmit: USARTLineTransmitCallback | null = null; + public onRxComplete: (() => void) | null = null; + public onConfigurationChange: USARTConfigurationChangeCallback | null = null; + + private rxBusyValue = false; + private rxByte = 0; + private lineBuffer = ''; + + private RXC: AVRInterruptConfig = { + address: this.config.rxCompleteInterrupt, + flagRegister: this.config.UCSRA, + flagMask: this.config.UCSRA_RXC, + enableRegister: this.config.UCSRB, + enableMask: this.config.UCSRB_RXCIE, + constant: true, + }; + private UDRE: AVRInterruptConfig = { + address: this.config.dataRegisterEmptyInterrupt, + flagRegister: this.config.UCSRA, + flagMask: this.config.UCSRA_UDRE, + enableRegister: this.config.UCSRB, + enableMask: this.config.UCSRB_UDRIE, + }; + private TXC: AVRInterruptConfig = { + address: this.config.txCompleteInterrupt, + flagRegister: this.config.UCSRA, + flagMask: this.config.UCSRA_TXC, + enableRegister: this.config.UCSRB, + enableMask: this.config.UCSRB_TXCIE, + }; + + constructor(protected cpu: CPU, protected config: USARTConfig, protected freqHz: number) { + const UCSRA_CFG_MASK = this.config.UCSRA_U2X; + const UCSRB_CFG_MASK = + this.config.UCSRB_UCSZ2 | this.config.UCSRB_RXEN | this.config.UCSRB_TXEN; + const UCSRC_CFG_MASK = + this.config.UCSRB_UCSZ2 | this.config.UCSRB_RXEN | this.config.UCSRB_TXEN; + this.reset(); + this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => { + cpu.data[config.UCSRA] = value & (this.config.UCSRA_MPCM | this.config.UCSRA_U2X); + cpu.clearInterruptByFlag(this.TXC, value); + if ((value & UCSRA_CFG_MASK) !== (oldValue & UCSRA_CFG_MASK)) { + this.onConfigurationChange?.(); + } + return true; + }; + this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => { + cpu.updateInterruptEnable(this.RXC, value); + cpu.updateInterruptEnable(this.UDRE, value); + cpu.updateInterruptEnable(this.TXC, value); + if (value & this.config.UCSRB_RXEN && oldValue & this.config.UCSRB_RXEN) { + cpu.clearInterrupt(this.RXC); + } + if (value & this.config.UCSRB_TXEN && !(oldValue & this.config.UCSRB_TXEN)) { + cpu.setInterruptFlag(this.UDRE); + } + cpu.data[config.UCSRB] = value; + if ((value & UCSRB_CFG_MASK) !== (oldValue & UCSRB_CFG_MASK)) { + this.onConfigurationChange?.(); + } + return true; + }; + this.cpu.writeHooks[config.UCSRC] = (value) => { + cpu.data[config.UCSRC] = value; + this.onConfigurationChange?.(); + return true; + }; + this.cpu.readHooks[config.UDR] = () => { + const mask = rxMasks[this.bitsPerChar] ?? 0xff; + const result = this.rxByte & mask; + this.rxByte = 0; + this.cpu.clearInterrupt(this.RXC); + return result; + }; + this.cpu.writeHooks[config.UDR] = (value) => { + if (this.onByteTransmit) { + this.onByteTransmit(value); + } + if (this.onLineTransmit) { + const ch = String.fromCharCode(value); + if (ch === '\n') { + this.onLineTransmit(this.lineBuffer); + this.lineBuffer = ''; + } else { + this.lineBuffer += ch; + } + } + this.cpu.addClockEvent(() => { + cpu.setInterruptFlag(this.UDRE); + cpu.setInterruptFlag(this.TXC); + }, this.cyclesPerChar); + this.cpu.clearInterrupt(this.TXC); + this.cpu.clearInterrupt(this.UDRE); + }; + this.cpu.writeHooks[config.UBRRH] = (value) => { + this.cpu.data[config.UBRRH] = value; + this.onConfigurationChange?.(); + return true; + }; + this.cpu.writeHooks[config.UBRRL] = (value) => { + this.cpu.data[config.UBRRL] = value; + this.onConfigurationChange?.(); + return true; + }; + } + + reset() { + this.cpu.data[this.config.UCSRA] = this.config.UCSRA_UDRE; + this.cpu.data[this.config.UCSRB] = 0; + this.cpu.data[this.config.UCSRC] = this.config.UCSRC_UCSZ1 | this.config.UCSRC_UCSZ0; + this.rxBusyValue = false; + this.rxByte = 0; + this.lineBuffer = ''; + } + + get rxBusy() { + return this.rxBusyValue; + } + + writeByte(value: number, immediate = false) { + const { cpu } = this; + if (this.rxBusyValue || !this.rxEnable) { + return false; + } + if (immediate) { + this.rxByte = value; + cpu.setInterruptFlag(this.RXC); + this.onRxComplete?.(); + } else { + this.rxBusyValue = true; + cpu.addClockEvent(() => { + this.rxBusyValue = false; + this.writeByte(value, true); + }, this.cyclesPerChar); + return true; + } + } + + private get cyclesPerChar() { + const symbolsPerChar = 1 + this.bitsPerChar + this.stopBits + (this.parityEnabled ? 1 : 0); + return (this.UBRR + 1) * this.multiplier * symbolsPerChar; + } + + private get UBRR() { + const { UBRRH, UBRRL } = this.config; + return (this.cpu.data[UBRRH] << 8) | this.cpu.data[UBRRL]; + } + + private get multiplier() { + return this.cpu.data[this.config.UCSRA] & this.config.UCSRA_U2X ? 8 : 16; + } + + get rxEnable() { + return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_RXEN); + } + + get txEnable() { + return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_TXEN); + } + + get baudRate() { + return Math.floor(this.freqHz / (this.multiplier * (1 + this.UBRR))); + } + + get bitsPerChar() { + const ucsz = + ((this.cpu.data[this.config.UCSRC] & (this.config.UCSRC_UCSZ1 | this.config.UCSRC_UCSZ0)) >> + 1) | + (this.cpu.data[this.config.UCSRB] & this.config.UCSRB_UCSZ2); + switch (ucsz) { + case 0: + return 5; + case 1: + return 6; + case 2: + return 7; + case 3: + return 8; + default: + case 7: + return 9; + } + } + + get stopBits() { + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_USBS ? 2 : 1; + } + + get parityEnabled() { + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM1 ? true : false; + } + + get parityOdd() { + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM0 ? true : false; + } +} From 783b77a7c1f5d13025979e46e47fca1dd717afe7 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 6 Feb 2023 21:31:29 -0500 Subject: [PATCH 04/10] ATmega32: merge work from simpler-api to split up chip config, standardize exports around --- src/chips/ATmega2560.ts | 43 ++++++ src/chips/ATmega32.ts | 25 ++++ src/chips/ATmega324p.ts | 25 ++++ src/chips/ATmega328p.ts | 25 ++++ src/chips/chip.ts | 24 +++ src/create-avr.ts | 35 +++++ src/index.ts | 24 +-- src/peripherals/adc_atmega32.ts | 2 +- src/peripherals/adc_atmega328p.ts | 2 +- src/peripherals/gpio_atmega32.ts | 8 +- src/peripherals/timer.spec.ts | 116 +++++++-------- src/peripherals/timer_atmega32.ts | 16 +- src/peripherals/timer_atmega328p.ts | 6 +- src/peripherals/usart.spec.ts | 59 ++++---- src/peripherals/usart.ts | 208 ++++++++++++++++++++++++++ src/peripherals/usart_atmega32.ts | 4 +- src/peripherals/usart_atmega328p.ts | 219 +--------------------------- 17 files changed, 496 insertions(+), 345 deletions(-) create mode 100644 src/chips/ATmega2560.ts create mode 100644 src/chips/ATmega32.ts create mode 100644 src/chips/ATmega324p.ts create mode 100644 src/chips/ATmega328p.ts create mode 100644 src/chips/chip.ts create mode 100644 src/create-avr.ts diff --git a/src/chips/ATmega2560.ts b/src/chips/ATmega2560.ts new file mode 100644 index 0000000..e577bcd --- /dev/null +++ b/src/chips/ATmega2560.ts @@ -0,0 +1,43 @@ +import { adcConfig } from '../peripherals/adc_atmega328p'; +import { clockConfig } from '../peripherals/clock'; +import { eepromConfig } from '../peripherals/eeprom'; +import { + portAConfig, + portEConfig, + portFConfig, + portGConfig, + portHConfig, + portJConfig, +} from '../peripherals/gpio_atmega2560'; +import { portBConfig, portCConfig, portDConfig } from '../peripherals/gpio_atmega328p'; +import { spiConfig } from '../peripherals/spi'; +import { timer0Config, timer1Config, timer2Config } from '../peripherals/timer_atmega328p'; +import { twiConfig } from '../peripherals/twi'; +import { usart0Config } from '../peripherals/usart_atmega328p'; +import { Chip } from './chip'; + +export const ATmega324p: Chip = { + flashSize: 0x40000, + ramSize: 0x2000, + eepromSize: 0x1000, + registerSpace: 0x100, + defaultFrequency: 16e6, + clock: clockConfig, + eeprom: eepromConfig, + gpio: { + A: portAConfig, + B: portBConfig, + C: portCConfig, + D: portDConfig, + E: portEConfig, + F: portFConfig, + G: portGConfig, + H: portHConfig, + J: portJConfig, + }, + timers: [timer0Config, timer1Config, timer2Config], + spi: [spiConfig], + usart: [usart0Config], + twi: [twiConfig], + adc: adcConfig, +}; diff --git a/src/chips/ATmega32.ts b/src/chips/ATmega32.ts new file mode 100644 index 0000000..c6339bc --- /dev/null +++ b/src/chips/ATmega32.ts @@ -0,0 +1,25 @@ +import { adcConfig } from '../peripherals/adc_atmega32'; +import { clockConfig } from '../peripherals/clock'; +import { eepromConfig } from '../peripherals/eeprom'; +import { portAConfig, portBConfig, portCConfig, portDConfig } from '../peripherals/gpio_atmega32'; +import { spiConfig } from '../peripherals/spi'; +import { timer0Config, timer1Config, timer2Config } from '../peripherals/timer_atmega32'; +import { twiConfig } from '../peripherals/twi'; +import { usart0Config } from '../peripherals/usart_atmega32'; +import { Chip } from './chip'; + +export const ATmega32: Chip = { + flashSize: 0x8000, + ramSize: 0x800, + eepromSize: 0x400, + registerSpace: 0x100, + defaultFrequency: 16e6, + clock: clockConfig, + eeprom: eepromConfig, + gpio: { A: portAConfig, B: portBConfig, C: portCConfig, D: portDConfig }, + timers: [timer0Config, timer1Config, timer2Config], + spi: [spiConfig], + usart: [usart0Config], + twi: [twiConfig], + adc: adcConfig, +}; diff --git a/src/chips/ATmega324p.ts b/src/chips/ATmega324p.ts new file mode 100644 index 0000000..983774c --- /dev/null +++ b/src/chips/ATmega324p.ts @@ -0,0 +1,25 @@ +import { adcConfig } from '../peripherals/adc_atmega328p'; +import { clockConfig } from '../peripherals/clock'; +import { eepromConfig } from '../peripherals/eeprom'; +import { portBConfig, portCConfig, portDConfig } from '../peripherals/gpio_atmega328p'; +import { spiConfig } from '../peripherals/spi'; +import { timer0Config, timer1Config, timer2Config } from '../peripherals/timer_atmega328p'; +import { twiConfig } from '../peripherals/twi'; +import { usart0Config } from '../peripherals/usart_atmega328p'; +import { Chip } from './chip'; + +export const ATmega324p: Chip = { + flashSize: 0x8000, + ramSize: 0x800, + eepromSize: 0x400, + registerSpace: 0x100, + defaultFrequency: 16e6, + clock: clockConfig, + eeprom: eepromConfig, + gpio: { B: portBConfig, C: portCConfig, D: portDConfig }, + timers: [timer0Config, timer1Config, timer2Config], + spi: [spiConfig], + usart: [usart0Config], + twi: [twiConfig], + adc: adcConfig, +}; diff --git a/src/chips/ATmega328p.ts b/src/chips/ATmega328p.ts new file mode 100644 index 0000000..4cc211b --- /dev/null +++ b/src/chips/ATmega328p.ts @@ -0,0 +1,25 @@ +import { adcConfig } from '../peripherals/adc_atmega328p'; +import { clockConfig } from '../peripherals/clock'; +import { eepromConfig } from '../peripherals/eeprom'; +import { portBConfig, portCConfig, portDConfig } from '../peripherals/gpio_atmega328p'; +import { spiConfig } from '../peripherals/spi'; +import { timer0Config, timer1Config, timer2Config } from '../peripherals/timer_atmega328p'; +import { twiConfig } from '../peripherals/twi'; +import { usart0Config } from '../peripherals/usart_atmega328p'; +import { Chip } from './chip'; + +export const ATmega328p: Chip = { + flashSize: 0x8000, + ramSize: 0x800, + eepromSize: 0x400, + registerSpace: 0x100, + defaultFrequency: 16e6, + clock: clockConfig, + eeprom: eepromConfig, + gpio: { B: portBConfig, C: portCConfig, D: portDConfig }, + timers: [timer0Config, timer1Config, timer2Config], + spi: [spiConfig], + usart: [usart0Config], + twi: [twiConfig], + adc: adcConfig, +}; diff --git a/src/chips/chip.ts b/src/chips/chip.ts new file mode 100644 index 0000000..50700ef --- /dev/null +++ b/src/chips/chip.ts @@ -0,0 +1,24 @@ +import { ADCConfig } from '../peripherals/adc'; +import { AVRClockConfig } from '../peripherals/clock'; +import { AVREEPROMConfig } from '../peripherals/eeprom'; +import { AVRPortConfig } from '../peripherals/gpio'; +import { SPIConfig } from '../peripherals/spi'; +import { AVRTimerConfig } from '../peripherals/timer'; +import { TWIConfig } from '../peripherals/twi'; +import { USARTConfig } from '../peripherals/usart'; + +export interface Chip { + flashSize: number; + ramSize: number; + eepromSize: number; + registerSpace: number; + defaultFrequency: number; + clock: AVRClockConfig; + eeprom?: AVREEPROMConfig; + gpio: { [key: string]: AVRPortConfig }; + timers: AVRTimerConfig[]; + spi: SPIConfig[]; + usart: USARTConfig[]; + twi: TWIConfig[]; + adc: ADCConfig; +} diff --git a/src/create-avr.ts b/src/create-avr.ts new file mode 100644 index 0000000..13c94a3 --- /dev/null +++ b/src/create-avr.ts @@ -0,0 +1,35 @@ +import { Chip } from './chips/chip'; +import { CPU } from './cpu/cpu'; +import { AVRClock } from './peripherals/clock'; +import { AVREEPROM, EEPROMBackend, EEPROMMemoryBackend } from './peripherals/eeprom'; +import { AVRIOPort } from './peripherals/gpio'; +import { AVRSPI } from './peripherals/spi'; +import { AVRTimer } from './peripherals/timer'; +import { AVRTWI } from './peripherals/twi'; +import { AVRUSART } from './peripherals/usart'; + +export interface CreateAVROptions { + eepromBackend?: EEPROMBackend; +} + +export function createAVR(config: Chip, options: CreateAVROptions = {}) { + const frequency = config.defaultFrequency; + const cpu = new CPU(new Uint16Array(config.flashSize / 2), config.ramSize); + const timers = config.timers.map((timerConfig) => new AVRTimer(cpu, timerConfig)); + const clock = new AVRClock(cpu, frequency, config.clock); + const eeprom = + config.eeprom && + new AVREEPROM( + cpu, + options.eepromBackend ?? new EEPROMMemoryBackend(config.eepromSize), + config.eeprom + ); + const spi = config.spi.map((spiConfig) => new AVRSPI(cpu, spiConfig, frequency)); + const usart = config.usart.map((usartConfig) => new AVRUSART(cpu, usartConfig, frequency)); + const twi = config.twi.map((twiConfig) => new AVRTWI(cpu, twiConfig, frequency)); + const gpio: { [key: string]: AVRIOPort } = {}; + for (const port of Object.keys(config.gpio)) { + gpio[port] = new AVRIOPort(cpu, config.gpio[port]); + } + return { cpu, timers, clock, eeprom, spi, usart, twi, gpio }; +} diff --git a/src/index.ts b/src/index.ts index 49de267..a8d3c93 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,13 +3,12 @@ * * Copyright (C) 2019, 2020, Uri Shaked */ - export { CPU } from './cpu/cpu'; export type { CPUMemoryHook, CPUMemoryHooks } from './cpu/cpu'; export { avrInstruction } from './cpu/instruction'; export { avrInterrupt } from './cpu/interrupt'; export { ADCMuxInputType, ADCReference, AVRADC } from './peripherals/adc'; -export { atmega328AdcConfig, atmega328Channels } from './peripherals/adc_atmega328p'; +export { adcConfig, atmega328Channels } from './peripherals/adc_atmega328p'; export type { ADCConfig, ADCMuxConfiguration, ADCMuxInput } from './peripherals/adc'; export { AVRClock, clockConfig } from './peripherals/clock'; export type { AVRClockConfig } from './peripherals/clock'; @@ -36,18 +35,6 @@ export { portKConfig, portLConfig, } from './peripherals/gpio_atmega2560'; -export { - atmega32PortAConfig, - atmega32PortBConfig, - atmega32PortCConfig, - atmega32PortDConfig, -} from './peripherals/gpio_atmega32'; -export { - atmga32Timer0Config, - atmga32Timer1Config, - atmga32Timer2Config, -} from './peripherals/timer_atmega32'; -export { atmega32Usart0Config } from './peripherals/usart_atmega32'; export type { AVRExternalInterrupt, AVRPinChangeInterrupt, @@ -57,14 +44,11 @@ export type { export { AVRSPI, spiConfig } from './peripherals/spi'; export type { SPIConfig, SPITransferCallback } from './peripherals/spi'; export { AVRTimer } from './peripherals/timer'; -export { - atmega328pTimer0Config, - atmega328pTimer1Config, - atmega328pTimer2Config, -} from './peripherals/timer_atmega328p'; +export { timer0Config, timer1Config, timer2Config } from './peripherals/timer_atmega328p'; export type { AVRTimerConfig } from './peripherals/timer'; export * from './peripherals/twi'; -export { AVRUSART, atmega328pUsart0Config } from './peripherals/usart_atmega328p'; +export { AVRUSART } from './peripherals/usart'; +export { usart0Config } from './peripherals/usart_atmega328p'; export { AVRUSI } from './peripherals/usi'; export { AVRWatchdog, watchdogConfig } from './peripherals/watchdog'; export type { WatchdogConfig } from './peripherals/watchdog'; diff --git a/src/peripherals/adc_atmega32.ts b/src/peripherals/adc_atmega32.ts index 484db52..f910168 100644 --- a/src/peripherals/adc_atmega32.ts +++ b/src/peripherals/adc_atmega32.ts @@ -35,7 +35,7 @@ export const atmega32Channels: ADCMuxConfiguration = { 31: { type: ADCMuxInputType.Constant, voltage: 0 }, }; -export const atmega32AdcConfig: ADCConfig = { +export const adcConfig: ADCConfig = { ADMUX: 0x27, ADCSRA: 0x26, ADCSRB: 0, // Not aviliable on ATmega32 diff --git a/src/peripherals/adc_atmega328p.ts b/src/peripherals/adc_atmega328p.ts index 595931a..0d7e136 100644 --- a/src/peripherals/adc_atmega328p.ts +++ b/src/peripherals/adc_atmega328p.ts @@ -14,7 +14,7 @@ export const atmega328Channels: ADCMuxConfiguration = { 15: { type: ADCMuxInputType.Constant, voltage: 0 }, }; -export const atmega328AdcConfig: ADCConfig = { +export const adcConfig: ADCConfig = { ADMUX: 0x7c, ADCSRA: 0x7a, ADCSRB: 0x7b, diff --git a/src/peripherals/gpio_atmega32.ts b/src/peripherals/gpio_atmega32.ts index 65f8e85..3f2a6c4 100644 --- a/src/peripherals/gpio_atmega32.ts +++ b/src/peripherals/gpio_atmega32.ts @@ -1,27 +1,27 @@ import { AVRPortConfig } from './gpio'; -export const atmega32PortAConfig: AVRPortConfig = { +export const portAConfig: AVRPortConfig = { PORT: 0x3b, DDR: 0x3a, PIN: 0x39, externalInterrupts: [], }; -export const atmega32PortBConfig: AVRPortConfig = { +export const portBConfig: AVRPortConfig = { PORT: 0x38, DDR: 0x37, PIN: 0x36, externalInterrupts: [], }; -export const atmega32PortCConfig: AVRPortConfig = { +export const portCConfig: AVRPortConfig = { PORT: 0x35, DDR: 0x34, PIN: 0x33, externalInterrupts: [], }; -export const atmega32PortDConfig: AVRPortConfig = { +export const portDConfig: AVRPortConfig = { PORT: 0x32, DDR: 0x31, PIN: 0x30, diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 644db76..9e8bd8c 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -3,11 +3,7 @@ import { asmProgram, TestProgramRunner } from '../utils/test-utils'; import { AVRIOPort, PinOverrideMode } from './gpio'; import { portBConfig, portDConfig } from './gpio_atmega328p'; import { AVRTimer } from './timer'; -import { - atmega328pTimer0Config, - atmega328pTimer1Config, - atmega328pTimer2Config, -} from './timer_atmega328p'; +import { timer0Config, timer1Config, timer2Config } from './timer_atmega328p'; // CPU registers const R1 = 1; @@ -84,7 +80,7 @@ const nopOpCode = '0000'; describe('timer', () => { it('should update timer every tick when prescaler is 1', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; cpu.tick(); @@ -96,7 +92,7 @@ describe('timer', () => { it('should update timer every 64 ticks when prescaler is 3', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCCR0B, CS01 | CS00); // Set prescaler to 64 cpu.cycles = 1; cpu.tick(); @@ -108,7 +104,7 @@ describe('timer', () => { it('should not update timer if it has been disabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCCR0B, 0); // No prescaler (timer disabled) cpu.cycles = 1; cpu.tick(); @@ -120,7 +116,7 @@ describe('timer', () => { it('should set the TOV flag when timer wraps above TOP value', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -137,7 +133,7 @@ describe('timer', () => { it('should set the TOV if timer overflows past TOP without reaching TOP', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xfe); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -151,7 +147,7 @@ describe('timer', () => { it('should clear the TOV flag when writing 1 to the TOV bit, and not trigger the interrupt', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -165,7 +161,7 @@ describe('timer', () => { it('should set TOV if timer overflows in FAST PWM mode', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -181,7 +177,7 @@ describe('timer', () => { it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -200,7 +196,7 @@ describe('timer', () => { it('should support overriding TIFR/TOV and TIMSK/TOIE bits (issue #64)', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, { - ...atmega328pTimer0Config, + ...timer0Config, // The following values correspond ATtiny85 config: TOV: 2, @@ -227,7 +223,7 @@ describe('timer', () => { it('should not generate an overflow interrupt when global interrupts disabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -243,7 +239,7 @@ describe('timer', () => { it('should not generate an overflow interrupt when TOIE0 is clear', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 cpu.cycles = 1; @@ -259,7 +255,7 @@ describe('timer', () => { it('should set OCF0A/B flags when OCRA/B == 0 and the timer equals to OCRA (issue #74)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xff); cpu.writeData(OCR0A, 0x0); cpu.writeData(OCR0B, 0x0); @@ -277,7 +273,7 @@ describe('timer', () => { it('should set the OCF1A flag when OCR1A == 120 and the timer overflowed past 120 in WGM mode 15 (issue #94)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCNT1, 118); cpu.writeData(OCR1A, 120); cpu.writeData(OCR1B, 4); // To avoid getting the OCF1B flag set @@ -295,7 +291,7 @@ describe('timer', () => { it('should set OCF0A flag when timer equals OCRA', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x10); cpu.writeData(OCR0A, 0x11); cpu.writeData(TCCR0A, 0x0); // WGM: Normal @@ -311,7 +307,7 @@ describe('timer', () => { it('should reset the counter in CTC mode if it equals to OCRA', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x10); cpu.writeData(OCR0A, 0x11); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -328,7 +324,7 @@ describe('timer', () => { it('should not set the TOV bit when TOP < MAX in CTC mode (issue #75)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x1e); cpu.writeData(OCR0A, 0x1f); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -346,7 +342,7 @@ describe('timer', () => { it('should set the TOV bit when TOP == MAX in CTC mode (issue #75)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xfe); cpu.writeData(OCR0A, 0xff); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -367,7 +363,7 @@ describe('timer', () => { it('should not set the TOV bit twice on overflow (issue #80)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0xfe); cpu.writeData(OCR0A, 0xff); cpu.writeData(TCCR0A, WGM01); // WGM: CTC @@ -389,7 +385,7 @@ describe('timer', () => { it('should set OCF0B flag when timer equals OCRB', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x10); cpu.writeData(OCR0B, 0x11); cpu.writeData(TCCR0A, 0x0); // WGM: (Normal) @@ -405,7 +401,7 @@ describe('timer', () => { it('should generate Timer Compare A interrupt when TCNT0 == TCNTA', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x20); cpu.writeData(OCR0A, 0x21); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -424,7 +420,7 @@ describe('timer', () => { it('should not generate Timer Compare A interrupt when OCIEA is disabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x20); cpu.writeData(OCR0A, 0x21); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -442,7 +438,7 @@ describe('timer', () => { it('should generate Timer Compare B interrupt when TCNT0 == TCNTB', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCNT0, 0x20); cpu.writeData(OCR0B, 0x21); cpu.writeData(TCCR0B, CS00); // Set prescaler to 1 @@ -471,7 +467,7 @@ describe('timer', () => { IN r17, 0x26 ; r17 <- TCNT `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.data[R17]).toEqual(0x31); @@ -479,7 +475,7 @@ describe('timer', () => { it('timer2 should count every 256 ticks when prescaler is 6 (issue #5)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer2Config); + new AVRTimer(cpu, timer2Config); cpu.writeData(TCCR2B, CS22 | CS21); // Set prescaler to 256 cpu.cycles = 1; cpu.tick(); @@ -503,7 +499,7 @@ describe('timer', () => { LDS r1, 0x46 ; r1 <- TCNT0 (2 cycles) `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.data[R1]).toEqual(2); @@ -521,7 +517,7 @@ describe('timer', () => { LDS r17, 0xb2 ; TCNT should equal 2 at this point `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer2Config); + new AVRTimer(cpu, timer2Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.readData(R17)).toEqual(2); @@ -537,7 +533,7 @@ describe('timer', () => { LDS r17, 0xb2 ; TCNT2 should equal 2 at this point (not counting the NOP) `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer2Config); + new AVRTimer(cpu, timer2Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.readData(R17)).toEqual(2); @@ -545,7 +541,7 @@ describe('timer', () => { it('should clear OC0B pin when writing 1 to FOC0B', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCCR0A, COM0B1); // Listen to Port B's internal callback @@ -582,7 +578,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -635,7 +631,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -681,7 +677,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -724,7 +720,7 @@ describe('timer', () => { IN r22, 0x26 ; TCNT0 will be 1 (end of test) `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -755,7 +751,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -802,7 +798,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -835,7 +831,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -863,7 +859,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback const portD = new AVRIOPort(cpu, portDConfig); @@ -906,7 +902,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -934,7 +930,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -958,7 +954,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - const timer = new AVRTimer(cpu, atmega328pTimer0Config); + const timer = new AVRTimer(cpu, timer0Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -971,7 +967,7 @@ describe('timer', () => { describe('16 bit timers', () => { it('should increment 16-bit TCNT by 1', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCNT1H, 0x22); // TCNT1 <- 0x2233 cpu.writeData(TCNT1, 0x33); // ... const timerLow = cpu.readData(TCNT1); @@ -989,7 +985,7 @@ describe('timer', () => { it('should set OCF0A flag when timer equals OCRA (16 bit mode)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCNT1H, 0x10); // TCNT1 <- 0x10ee cpu.writeData(TCNT1, 0xee); // ... cpu.writeData(OCR1AH, 0x10); // OCR1 <- 0x10ef @@ -1011,7 +1007,7 @@ describe('timer', () => { const OCR1CH = 0x8d; const OCF1C = 1 << 3; new AVRTimer(cpu, { - ...atmega328pTimer1Config, + ...timer1Config, OCRC: OCR1C, OCFC: OCF1C, }); @@ -1032,7 +1028,7 @@ describe('timer', () => { it('should generate an overflow interrupt if timer overflows and interrupts enabled', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCCR1A, 0x3); // TCCR1A <- WGM10 | WGM11 (Fast PWM, 10-bit) cpu.writeData(TCCR1B, 0x9); // TCCR1B <- WGM12 | CS10 cpu.writeData(TIMSK1, 0x1); // TIMSK1: TOIE1 @@ -1054,7 +1050,7 @@ describe('timer', () => { it('should reset the timer once it reaches ICR value in mode 12', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCNT1H, 0x50); // TCNT1 <- 0x500f cpu.writeData(TCNT1, 0x0f); // ... cpu.writeData(ICR1H, 0x50); // ICR1 <- 0x5010 @@ -1072,7 +1068,7 @@ describe('timer', () => { it('should not update the high byte of TCNT if written after the low byte (issue #37)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCNT1, 0x22); cpu.writeData(TCNT1H, 0x55); cpu.cycles = 1; @@ -1084,7 +1080,7 @@ describe('timer', () => { it('reading from TCNT1H before TCNT1L should return old value (issue #37)', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCNT1H, 0xff); cpu.writeData(TCNT1, 0xff); cpu.writeData(TCCR1B, WGM12 | CS10); // Set prescaler to 1, WGM: CTC @@ -1120,7 +1116,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); // Listen to Port B's internal callback const portB = new AVRIOPort(cpu, portBConfig); @@ -1161,7 +1157,7 @@ describe('timer', () => { const cpu = new CPU(program); new AVRTimer(cpu, { - ...atmega328pTimer1Config, + ...timer1Config, OCRC: OCR1C, OCFC: OCF1C, compPortC: portBConfig.PORT, @@ -1188,7 +1184,7 @@ describe('timer', () => { it('should toggle OC1C on when writing 1 to FOC1C', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, { - ...atmega328pTimer1Config, + ...timer1Config, OCRC: OCR1C, OCFC: OCF1C, compPortC: portBConfig.PORT, @@ -1208,7 +1204,7 @@ describe('timer', () => { it('should not toggle OC1C on when writing 1 to FOC1C in PWM mode', () => { const cpu = new CPU(new Uint16Array(0x1000)); new AVRTimer(cpu, { - ...atmega328pTimer1Config, + ...timer1Config, OCRC: OCR1C, OCFC: OCF1C, compPortC: portBConfig.PORT, @@ -1257,7 +1253,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -1293,7 +1289,7 @@ describe('timer', () => { `); const cpu = new CPU(program); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -1306,7 +1302,7 @@ describe('timer', () => { it('should mask the unused bits of OCR1A when using fixed top values', () => { const cpu = new CPU(new Uint16Array(0x1000)); - new AVRTimer(cpu, atmega328pTimer1Config); + new AVRTimer(cpu, timer1Config); cpu.writeData(TCCR1A, WGM10 | WGM11); // WGM: FastPWM, top 0x3ff cpu.writeData(TCCR1B, WGM12); cpu.writeData(OCR1AH, 0xff); @@ -1320,7 +1316,7 @@ describe('timer', () => { it('should count on the falling edge of T0 when CS=110', () => { const cpu = new CPU(new Uint16Array(0x1000)); const port = new AVRIOPort(cpu, portDConfig); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCCR0B, CS02 | CS01); // Count on falling edge cpu.cycles = 1; cpu.tick(); @@ -1339,7 +1335,7 @@ describe('timer', () => { it('should count on the rising edge of T0 when CS=111', () => { const cpu = new CPU(new Uint16Array(0x1000)); const port = new AVRIOPort(cpu, portDConfig); - new AVRTimer(cpu, atmega328pTimer0Config); + new AVRTimer(cpu, timer0Config); cpu.writeData(TCCR0B, CS02 | CS01 | CS00); // Count on rising edge cpu.cycles = 1; cpu.tick(); diff --git a/src/peripherals/timer_atmega32.ts b/src/peripherals/timer_atmega32.ts index 92a5902..2fbbe9a 100644 --- a/src/peripherals/timer_atmega32.ts +++ b/src/peripherals/timer_atmega32.ts @@ -1,4 +1,4 @@ -import { atmega32PortBConfig, atmega32PortDConfig } from './gpio_atmega32'; +import { portBConfig, portDConfig } from './gpio_atmega32'; import { AVRTimerConfig, TimerDividers } from './timer'; const atmega32TimerDividers: TimerDividers = { @@ -12,7 +12,7 @@ const atmega32TimerDividers: TimerDividers = { 7: 0, // Ditto }; -export const atmga32Timer0Config: AVRTimerConfig = { +export const timer0Config: AVRTimerConfig = { bits: 8, dividers: atmega32TimerDividers, @@ -48,7 +48,7 @@ export const atmga32Timer0Config: AVRTimerConfig = { OCIEC: 0, // No OCFC on Atmega32, // Output compare pins - compPortA: atmega32PortBConfig.PORT, + compPortA: portBConfig.PORT, compPinA: 3, compPortB: 0, // Not available compPinB: 0, // Not available @@ -59,7 +59,7 @@ export const atmga32Timer0Config: AVRTimerConfig = { externalClockPin: 0, // Unimplement ? Not available }; -export const atmga32Timer1Config: AVRTimerConfig = { +export const timer1Config: AVRTimerConfig = { bits: 16, dividers: atmega32TimerDividers, @@ -95,9 +95,9 @@ export const atmga32Timer1Config: AVRTimerConfig = { OCIEC: 0, // No OCFC on Atmega32, // Output compare pins - compPortA: atmega32PortDConfig.PORT, + compPortA: portDConfig.PORT, compPinA: 5, - compPortB: atmega32PortDConfig.PORT, + compPortB: portDConfig.PORT, compPinB: 4, compPortC: 0, // Not available compPinC: 0, // Not available @@ -106,7 +106,7 @@ export const atmga32Timer1Config: AVRTimerConfig = { externalClockPin: 0, // Unimplemented ? Not available }; -export const atmga32Timer2Config: AVRTimerConfig = { +export const timer2Config: AVRTimerConfig = { bits: 8, dividers: atmega32TimerDividers, @@ -142,7 +142,7 @@ export const atmga32Timer2Config: AVRTimerConfig = { OCIEC: 0, // No OCFC on Atmega32, // Output compare pins - compPortA: atmega32PortBConfig.PORT, + compPortA: portBConfig.PORT, compPinA: 3, compPortB: 0, // Not available compPinB: 0, // Not available diff --git a/src/peripherals/timer_atmega328p.ts b/src/peripherals/timer_atmega328p.ts index 7970a84..fca68d8 100644 --- a/src/peripherals/timer_atmega328p.ts +++ b/src/peripherals/timer_atmega328p.ts @@ -27,7 +27,7 @@ const timer01Dividers: TimerDividers = { 7: 0, // Ditto }; -export const atmega328pTimer0Config: AVRTimerConfig = { +export const timer0Config: AVRTimerConfig = { bits: 8, captureInterrupt: 0, // not available compAInterrupt: 0x1c, @@ -56,7 +56,7 @@ export const atmega328pTimer0Config: AVRTimerConfig = { ...defaultTimerBits, }; -export const atmega328pTimer1Config: AVRTimerConfig = { +export const timer1Config: AVRTimerConfig = { bits: 16, captureInterrupt: 0x14, compAInterrupt: 0x16, @@ -85,7 +85,7 @@ export const atmega328pTimer1Config: AVRTimerConfig = { ...defaultTimerBits, }; -export const atmega328pTimer2Config: AVRTimerConfig = { +export const timer2Config: AVRTimerConfig = { bits: 8, captureInterrupt: 0, // not available compAInterrupt: 0x0e, diff --git a/src/peripherals/usart.spec.ts b/src/peripherals/usart.spec.ts index 85115a4..dda6abb 100644 --- a/src/peripherals/usart.spec.ts +++ b/src/peripherals/usart.spec.ts @@ -1,5 +1,6 @@ import { CPU } from '../cpu/cpu'; -import { AVRUSART, atmega328pUsart0Config } from './usart_atmega328p'; +import { AVRUSART } from './usart'; +import { usart0Config } from './usart_atmega328p'; const FREQ_16MHZ = 16e6; const FREQ_11_0529MHZ = 11059200; @@ -38,7 +39,7 @@ const UCSZ2 = 4; describe('USART', () => { it('should correctly calculate the baudRate from UBRR', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_11_0529MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_11_0529MHZ); cpu.writeData(UBRR0H, 0); cpu.writeData(UBRR0L, 5); expect(usart.baudRate).toEqual(115200); @@ -46,7 +47,7 @@ describe('USART', () => { it('should correctly calculate the baudRate from UBRR in double-speed mode', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UBRR0H, 3); cpu.writeData(UBRR0L, 64); cpu.writeData(UCSR0A, U2X0); @@ -55,7 +56,7 @@ describe('USART', () => { it('should call onConfigurationChange when the baudRate changes', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); const onConfigurationChange = jest.fn(); usart.onConfigurationChange = onConfigurationChange; @@ -74,35 +75,35 @@ describe('USART', () => { describe('bitsPerChar', () => { it('should return 5-bits per byte when UCSZ = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, 0); expect(usart.bitsPerChar).toEqual(5); }); it('should return 6-bits per byte when UCSZ = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ0); expect(usart.bitsPerChar).toEqual(6); }); it('should return 7-bits per byte when UCSZ = 2', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ1); expect(usart.bitsPerChar).toEqual(7); }); it('should return 8-bits per byte when UCSZ = 3', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ0 | UCSZ1); expect(usart.bitsPerChar).toEqual(8); }); it('should return 9-bits per byte when UCSZ = 7', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UCSZ0 | UCSZ1); cpu.writeData(UCSR0B, UCSZ2); expect(usart.bitsPerChar).toEqual(9); @@ -110,7 +111,7 @@ describe('USART', () => { it('should call onConfigurationChange when bitsPerChar change', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); const onConfigurationChange = jest.fn(); usart.onConfigurationChange = onConfigurationChange; @@ -130,13 +131,13 @@ describe('USART', () => { describe('stopBits', () => { it('should return 1 when USBS = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); expect(usart.stopBits).toEqual(1); }); it('should return 2 when USBS = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, USBS); expect(usart.stopBits).toEqual(2); }); @@ -145,13 +146,13 @@ describe('USART', () => { describe('parityEnabled', () => { it('should return false when UPM1 = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); expect(usart.parityEnabled).toEqual(false); }); it('should return true when UPM1 = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UPM1); expect(usart.parityEnabled).toEqual(true); }); @@ -160,13 +161,13 @@ describe('USART', () => { describe('parityOdd', () => { it('should return false when UPM0 = 0', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); expect(usart.parityOdd).toEqual(false); }); it('should return true when UPM0 = 1', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0C, UPM0); expect(usart.parityOdd).toEqual(true); }); @@ -174,7 +175,7 @@ describe('USART', () => { it('should invoke onByteTransmit when UDR0 is written to', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); usart.onByteTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x61); @@ -184,7 +185,7 @@ describe('USART', () => { describe('txEnable/rxEnable', () => { it('txEnable should equal true when the transitter is enabled', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); usart.onByteTransmit = jest.fn(); expect(usart.txEnable).toEqual(false); cpu.writeData(UCSR0B, TXEN); @@ -193,7 +194,7 @@ describe('USART', () => { it('rxEnable should equal true when the transitter is enabled', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); usart.onByteTransmit = jest.fn(); expect(usart.rxEnable).toEqual(false); cpu.writeData(UCSR0B, RXEN); @@ -204,7 +205,7 @@ describe('USART', () => { describe('tick()', () => { it('should trigger data register empty interrupt if UDRE is set', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, UDRIE | TXEN); cpu.data[SREG] = 0x80; // SREG: I------- cpu.tick(); @@ -215,7 +216,7 @@ describe('USART', () => { it('should trigger data TX Complete interrupt if TXCIE is set', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, TXCIE | TXEN); cpu.writeData(UDR0, 0x61); cpu.data[SREG] = 0x80; // SREG: I------- @@ -228,7 +229,7 @@ describe('USART', () => { it('should not trigger data TX Complete interrupt if UDR was not written to', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, TXCIE | TXEN); cpu.data[SREG] = 0x80; // SREG: I------- cpu.tick(); @@ -238,7 +239,7 @@ describe('USART', () => { it('should not trigger any interrupt if interrupts are disabled', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, UDRIE | TXEN); cpu.writeData(UDR0, 0x61); cpu.data[SREG] = 0; // SREG: 0 (disable interrupts) @@ -253,7 +254,7 @@ describe('USART', () => { describe('onLineTransmit', () => { it('should call onLineTransmit with the current line buffer after every newline', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); usart.onLineTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x48); // 'H' @@ -267,7 +268,7 @@ describe('USART', () => { it('should not call onLineTransmit if no newline was received', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); usart.onLineTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x48); // 'H' @@ -277,7 +278,7 @@ describe('USART', () => { it('should clear the line buffer after each call to onLineTransmit', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); usart.onLineTransmit = jest.fn(); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UDR0, 0x48); // 'H' @@ -296,7 +297,7 @@ describe('USART', () => { describe('writeByte', () => { it('should return false if called when RX is busy', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, RXEN); cpu.writeData(UBRR0L, 103); // baud: 9600 expect(usart.writeByte(10)).toEqual(true); @@ -309,7 +310,7 @@ describe('USART', () => { describe('Integration tests', () => { it('should set the TXC bit after ~1.04mS when baud rate set to 9600', () => { const cpu = new CPU(new Uint16Array(1024)); - new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + new AVRUSART(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(UCSR0B, TXEN); cpu.writeData(UBRR0L, 103); // baud: 9600 cpu.writeData(UDR0, 0x48); // 'H' @@ -323,7 +324,7 @@ describe('USART', () => { it('should be ready to recieve the next byte after ~1.04ms when baudrate set to 9600', () => { const cpu = new CPU(new Uint16Array(1024)); - const usart = new AVRUSART(cpu, atmega328pUsart0Config, FREQ_16MHZ); + const usart = new AVRUSART(cpu, usart0Config, FREQ_16MHZ); const rxCompleteCallback = jest.fn(); usart.onRxComplete = rxCompleteCallback; cpu.writeData(UCSR0B, RXEN); diff --git a/src/peripherals/usart.ts b/src/peripherals/usart.ts index 21b7481..964873e 100644 --- a/src/peripherals/usart.ts +++ b/src/peripherals/usart.ts @@ -6,6 +6,7 @@ * Copyright (C) 2019, 2020, 2021 Uri Shaked */ +import { AVRInterruptConfig, CPU } from '../cpu/cpu'; import { u8 } from '../types'; interface USARTRegisterBits { @@ -86,3 +87,210 @@ export interface USARTConfig extends USARTRegisterBits { export type USARTTransmitCallback = (value: u8) => void; export type USARTLineTransmitCallback = (value: string) => void; export type USARTConfigurationChangeCallback = () => void; + +const rxMasks = { + 5: 0x1f, + 6: 0x3f, + 7: 0x7f, + 8: 0xff, + 9: 0xff, +}; + +export class AVRUSART { + public onByteTransmit: USARTTransmitCallback | null = null; + public onLineTransmit: USARTLineTransmitCallback | null = null; + public onRxComplete: (() => void) | null = null; + public onConfigurationChange: USARTConfigurationChangeCallback | null = null; + + private rxBusyValue = false; + private rxByte = 0; + private lineBuffer = ''; + + private RXC: AVRInterruptConfig = { + address: this.config.rxCompleteInterrupt, + flagRegister: this.config.UCSRA, + flagMask: this.config.UCSRA_RXC, + enableRegister: this.config.UCSRB, + enableMask: this.config.UCSRB_RXCIE, + constant: true, + }; + private UDRE: AVRInterruptConfig = { + address: this.config.dataRegisterEmptyInterrupt, + flagRegister: this.config.UCSRA, + flagMask: this.config.UCSRA_UDRE, + enableRegister: this.config.UCSRB, + enableMask: this.config.UCSRB_UDRIE, + }; + private TXC: AVRInterruptConfig = { + address: this.config.txCompleteInterrupt, + flagRegister: this.config.UCSRA, + flagMask: this.config.UCSRA_TXC, + enableRegister: this.config.UCSRB, + enableMask: this.config.UCSRB_TXCIE, + }; + + constructor(protected cpu: CPU, protected config: USARTConfig, protected freqHz: number) { + const UCSRA_CFG_MASK = this.config.UCSRA_U2X; + const UCSRB_CFG_MASK = + this.config.UCSRB_UCSZ2 | this.config.UCSRB_RXEN | this.config.UCSRB_TXEN; + const UCSRC_CFG_MASK = + this.config.UCSRB_UCSZ2 | this.config.UCSRB_RXEN | this.config.UCSRB_TXEN; + this.reset(); + this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => { + cpu.data[config.UCSRA] = value & (this.config.UCSRA_MPCM | this.config.UCSRA_U2X); + cpu.clearInterruptByFlag(this.TXC, value); + if ((value & UCSRA_CFG_MASK) !== (oldValue & UCSRA_CFG_MASK)) { + this.onConfigurationChange?.(); + } + return true; + }; + this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => { + cpu.updateInterruptEnable(this.RXC, value); + cpu.updateInterruptEnable(this.UDRE, value); + cpu.updateInterruptEnable(this.TXC, value); + if (value & this.config.UCSRB_RXEN && oldValue & this.config.UCSRB_RXEN) { + cpu.clearInterrupt(this.RXC); + } + if (value & this.config.UCSRB_TXEN && !(oldValue & this.config.UCSRB_TXEN)) { + cpu.setInterruptFlag(this.UDRE); + } + cpu.data[config.UCSRB] = value; + if ((value & UCSRB_CFG_MASK) !== (oldValue & UCSRB_CFG_MASK)) { + this.onConfigurationChange?.(); + } + return true; + }; + this.cpu.writeHooks[config.UCSRC] = (value) => { + cpu.data[config.UCSRC] = value; + this.onConfigurationChange?.(); + return true; + }; + this.cpu.readHooks[config.UDR] = () => { + const mask = rxMasks[this.bitsPerChar] ?? 0xff; + const result = this.rxByte & mask; + this.rxByte = 0; + this.cpu.clearInterrupt(this.RXC); + return result; + }; + this.cpu.writeHooks[config.UDR] = (value) => { + if (this.onByteTransmit) { + this.onByteTransmit(value); + } + if (this.onLineTransmit) { + const ch = String.fromCharCode(value); + if (ch === '\n') { + this.onLineTransmit(this.lineBuffer); + this.lineBuffer = ''; + } else { + this.lineBuffer += ch; + } + } + this.cpu.addClockEvent(() => { + cpu.setInterruptFlag(this.UDRE); + cpu.setInterruptFlag(this.TXC); + }, this.cyclesPerChar); + this.cpu.clearInterrupt(this.TXC); + this.cpu.clearInterrupt(this.UDRE); + }; + this.cpu.writeHooks[config.UBRRH] = (value) => { + this.cpu.data[config.UBRRH] = value; + this.onConfigurationChange?.(); + return true; + }; + this.cpu.writeHooks[config.UBRRL] = (value) => { + this.cpu.data[config.UBRRL] = value; + this.onConfigurationChange?.(); + return true; + }; + } + + reset() { + this.cpu.data[this.config.UCSRA] = this.config.UCSRA_UDRE; + this.cpu.data[this.config.UCSRB] = 0; + this.cpu.data[this.config.UCSRC] = this.config.UCSRC_UCSZ1 | this.config.UCSRC_UCSZ0; + this.rxBusyValue = false; + this.rxByte = 0; + this.lineBuffer = ''; + } + + get rxBusy() { + return this.rxBusyValue; + } + + writeByte(value: number, immediate = false) { + const { cpu } = this; + if (this.rxBusyValue || !this.rxEnable) { + return false; + } + if (immediate) { + this.rxByte = value; + cpu.setInterruptFlag(this.RXC); + this.onRxComplete?.(); + } else { + this.rxBusyValue = true; + cpu.addClockEvent(() => { + this.rxBusyValue = false; + this.writeByte(value, true); + }, this.cyclesPerChar); + return true; + } + } + + private get cyclesPerChar() { + const symbolsPerChar = 1 + this.bitsPerChar + this.stopBits + (this.parityEnabled ? 1 : 0); + return (this.UBRR + 1) * this.multiplier * symbolsPerChar; + } + + private get UBRR() { + const { UBRRH, UBRRL } = this.config; + return (this.cpu.data[UBRRH] << 8) | this.cpu.data[UBRRL]; + } + + private get multiplier() { + return this.cpu.data[this.config.UCSRA] & this.config.UCSRA_U2X ? 8 : 16; + } + + get rxEnable() { + return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_RXEN); + } + + get txEnable() { + return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_TXEN); + } + + get baudRate() { + return Math.floor(this.freqHz / (this.multiplier * (1 + this.UBRR))); + } + + get bitsPerChar() { + const ucsz = + ((this.cpu.data[this.config.UCSRC] & (this.config.UCSRC_UCSZ1 | this.config.UCSRC_UCSZ0)) >> + 1) | + (this.cpu.data[this.config.UCSRB] & this.config.UCSRB_UCSZ2); + switch (ucsz) { + case 0: + return 5; + case 1: + return 6; + case 2: + return 7; + case 3: + return 8; + default: + case 7: + return 9; + } + } + + get stopBits() { + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_USBS ? 2 : 1; + } + + get parityEnabled() { + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM1 ? true : false; + } + + get parityOdd() { + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM0 ? true : false; + } +} diff --git a/src/peripherals/usart_atmega32.ts b/src/peripherals/usart_atmega32.ts index 373d925..d341c42 100644 --- a/src/peripherals/usart_atmega32.ts +++ b/src/peripherals/usart_atmega32.ts @@ -8,9 +8,9 @@ import { CPU } from '../cpu/cpu'; import { u8 } from '../types'; import { standardUartRegisterBits, USARTConfig } from './usart'; -import { AVRUSART } from './usart_atmega328p'; +import { AVRUSART } from './usart'; -export const atmega32Usart0Config: USARTConfig = { +export const usart0Config: USARTConfig = { rxCompleteInterrupt: 0x1a, dataRegisterEmptyInterrupt: 0x1c, txCompleteInterrupt: 0x1e, diff --git a/src/peripherals/usart_atmega328p.ts b/src/peripherals/usart_atmega328p.ts index 3b3d5bd..b7ff3db 100644 --- a/src/peripherals/usart_atmega328p.ts +++ b/src/peripherals/usart_atmega328p.ts @@ -5,17 +5,9 @@ * * Copyright (C) 2019, 2020, 2021 Uri Shaked */ +import { standardUartRegisterBits, USARTConfig } from './usart'; -import { AVRInterruptConfig, CPU } from '../cpu/cpu'; -import { - standardUartRegisterBits, - USARTConfig, - USARTConfigurationChangeCallback, - USARTLineTransmitCallback, - USARTTransmitCallback, -} from './usart'; - -export const atmega328pUsart0Config: USARTConfig = { +export const usart0Config: USARTConfig = { rxCompleteInterrupt: 0x24, dataRegisterEmptyInterrupt: 0x26, txCompleteInterrupt: 0x28, @@ -32,210 +24,3 @@ export const atmega328pUsart0Config: USARTConfig = { UCSRC_UMSEL1: 0x80, UCSRC_UMSEL0: 0x40, }; - -const rxMasks = { - 5: 0x1f, - 6: 0x3f, - 7: 0x7f, - 8: 0xff, - 9: 0xff, -}; - -export class AVRUSART { - public onByteTransmit: USARTTransmitCallback | null = null; - public onLineTransmit: USARTLineTransmitCallback | null = null; - public onRxComplete: (() => void) | null = null; - public onConfigurationChange: USARTConfigurationChangeCallback | null = null; - - private rxBusyValue = false; - private rxByte = 0; - private lineBuffer = ''; - - private RXC: AVRInterruptConfig = { - address: this.config.rxCompleteInterrupt, - flagRegister: this.config.UCSRA, - flagMask: this.config.UCSRA_RXC, - enableRegister: this.config.UCSRB, - enableMask: this.config.UCSRB_RXCIE, - constant: true, - }; - private UDRE: AVRInterruptConfig = { - address: this.config.dataRegisterEmptyInterrupt, - flagRegister: this.config.UCSRA, - flagMask: this.config.UCSRA_UDRE, - enableRegister: this.config.UCSRB, - enableMask: this.config.UCSRB_UDRIE, - }; - private TXC: AVRInterruptConfig = { - address: this.config.txCompleteInterrupt, - flagRegister: this.config.UCSRA, - flagMask: this.config.UCSRA_TXC, - enableRegister: this.config.UCSRB, - enableMask: this.config.UCSRB_TXCIE, - }; - - constructor(protected cpu: CPU, protected config: USARTConfig, protected freqHz: number) { - const UCSRA_CFG_MASK = this.config.UCSRA_U2X; - const UCSRB_CFG_MASK = - this.config.UCSRB_UCSZ2 | this.config.UCSRB_RXEN | this.config.UCSRB_TXEN; - const UCSRC_CFG_MASK = - this.config.UCSRB_UCSZ2 | this.config.UCSRB_RXEN | this.config.UCSRB_TXEN; - this.reset(); - this.cpu.writeHooks[config.UCSRA] = (value, oldValue) => { - cpu.data[config.UCSRA] = value & (this.config.UCSRA_MPCM | this.config.UCSRA_U2X); - cpu.clearInterruptByFlag(this.TXC, value); - if ((value & UCSRA_CFG_MASK) !== (oldValue & UCSRA_CFG_MASK)) { - this.onConfigurationChange?.(); - } - return true; - }; - this.cpu.writeHooks[config.UCSRB] = (value, oldValue) => { - cpu.updateInterruptEnable(this.RXC, value); - cpu.updateInterruptEnable(this.UDRE, value); - cpu.updateInterruptEnable(this.TXC, value); - if (value & this.config.UCSRB_RXEN && oldValue & this.config.UCSRB_RXEN) { - cpu.clearInterrupt(this.RXC); - } - if (value & this.config.UCSRB_TXEN && !(oldValue & this.config.UCSRB_TXEN)) { - cpu.setInterruptFlag(this.UDRE); - } - cpu.data[config.UCSRB] = value; - if ((value & UCSRB_CFG_MASK) !== (oldValue & UCSRB_CFG_MASK)) { - this.onConfigurationChange?.(); - } - return true; - }; - this.cpu.writeHooks[config.UCSRC] = (value) => { - cpu.data[config.UCSRC] = value; - this.onConfigurationChange?.(); - return true; - }; - this.cpu.readHooks[config.UDR] = () => { - const mask = rxMasks[this.bitsPerChar] ?? 0xff; - const result = this.rxByte & mask; - this.rxByte = 0; - this.cpu.clearInterrupt(this.RXC); - return result; - }; - this.cpu.writeHooks[config.UDR] = (value) => { - if (this.onByteTransmit) { - this.onByteTransmit(value); - } - if (this.onLineTransmit) { - const ch = String.fromCharCode(value); - if (ch === '\n') { - this.onLineTransmit(this.lineBuffer); - this.lineBuffer = ''; - } else { - this.lineBuffer += ch; - } - } - this.cpu.addClockEvent(() => { - cpu.setInterruptFlag(this.UDRE); - cpu.setInterruptFlag(this.TXC); - }, this.cyclesPerChar); - this.cpu.clearInterrupt(this.TXC); - this.cpu.clearInterrupt(this.UDRE); - }; - this.cpu.writeHooks[config.UBRRH] = (value) => { - this.cpu.data[config.UBRRH] = value; - this.onConfigurationChange?.(); - return true; - }; - this.cpu.writeHooks[config.UBRRL] = (value) => { - this.cpu.data[config.UBRRL] = value; - this.onConfigurationChange?.(); - return true; - }; - } - - reset() { - this.cpu.data[this.config.UCSRA] = this.config.UCSRA_UDRE; - this.cpu.data[this.config.UCSRB] = 0; - this.cpu.data[this.config.UCSRC] = this.config.UCSRC_UCSZ1 | this.config.UCSRC_UCSZ0; - this.rxBusyValue = false; - this.rxByte = 0; - this.lineBuffer = ''; - } - - get rxBusy() { - return this.rxBusyValue; - } - - writeByte(value: number, immediate = false) { - const { cpu } = this; - if (this.rxBusyValue || !this.rxEnable) { - return false; - } - if (immediate) { - this.rxByte = value; - cpu.setInterruptFlag(this.RXC); - this.onRxComplete?.(); - } else { - this.rxBusyValue = true; - cpu.addClockEvent(() => { - this.rxBusyValue = false; - this.writeByte(value, true); - }, this.cyclesPerChar); - return true; - } - } - - private get cyclesPerChar() { - const symbolsPerChar = 1 + this.bitsPerChar + this.stopBits + (this.parityEnabled ? 1 : 0); - return (this.UBRR + 1) * this.multiplier * symbolsPerChar; - } - - private get UBRR() { - const { UBRRH, UBRRL } = this.config; - return (this.cpu.data[UBRRH] << 8) | this.cpu.data[UBRRL]; - } - - private get multiplier() { - return this.cpu.data[this.config.UCSRA] & this.config.UCSRA_U2X ? 8 : 16; - } - - get rxEnable() { - return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_RXEN); - } - - get txEnable() { - return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_TXEN); - } - - get baudRate() { - return Math.floor(this.freqHz / (this.multiplier * (1 + this.UBRR))); - } - - get bitsPerChar() { - const ucsz = - ((this.cpu.data[this.config.UCSRC] & (this.config.UCSRC_UCSZ1 | this.config.UCSRC_UCSZ0)) >> - 1) | - (this.cpu.data[this.config.UCSRB] & this.config.UCSRB_UCSZ2); - switch (ucsz) { - case 0: - return 5; - case 1: - return 6; - case 2: - return 7; - case 3: - return 8; - default: - case 7: - return 9; - } - } - - get stopBits() { - return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_USBS ? 2 : 1; - } - - get parityEnabled() { - return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM1 ? true : false; - } - - get parityOdd() { - return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM0 ? true : false; - } -} From 922c6be80bd7544e00243daab0280cdce9c4786a Mon Sep 17 00:00:00 2001 From: James Date: Sun, 12 Feb 2023 23:16:52 -0500 Subject: [PATCH 05/10] Correct interrupt vectors for many 324p parts --- src/chips/ATmega324p.ts | 13 ++++++----- src/peripherals/adc_atmega324p.ts | 7 ++++++ src/peripherals/eeprom_atmega324p.ts | 7 ++++++ src/peripherals/timer_atmega324p.ts | 33 ++++++++++++++++++++++++++++ src/peripherals/twi_atmega324p.ts | 7 ++++++ src/peripherals/usart_atmega324p.ts | 9 ++++++++ 6 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 src/peripherals/adc_atmega324p.ts create mode 100644 src/peripherals/eeprom_atmega324p.ts create mode 100644 src/peripherals/timer_atmega324p.ts create mode 100644 src/peripherals/twi_atmega324p.ts create mode 100644 src/peripherals/usart_atmega324p.ts diff --git a/src/chips/ATmega324p.ts b/src/chips/ATmega324p.ts index 983774c..5af8e8b 100644 --- a/src/chips/ATmega324p.ts +++ b/src/chips/ATmega324p.ts @@ -1,11 +1,12 @@ -import { adcConfig } from '../peripherals/adc_atmega328p'; +import { adcConfig } from '../peripherals/adc_atmega324p'; import { clockConfig } from '../peripherals/clock'; -import { eepromConfig } from '../peripherals/eeprom'; +import { eepromConfig } from '../peripherals/eeprom_atmega324p'; +import { portAConfig } from '../peripherals/gpio_atmega2560'; import { portBConfig, portCConfig, portDConfig } from '../peripherals/gpio_atmega328p'; import { spiConfig } from '../peripherals/spi'; -import { timer0Config, timer1Config, timer2Config } from '../peripherals/timer_atmega328p'; -import { twiConfig } from '../peripherals/twi'; -import { usart0Config } from '../peripherals/usart_atmega328p'; +import { timer0Config, timer1Config, timer2Config } from '../peripherals/timer_atmega324p'; +import { twiConfig } from '../peripherals/twi_atmega324p'; +import { usart0Config } from '../peripherals/usart_atmega324p'; import { Chip } from './chip'; export const ATmega324p: Chip = { @@ -16,7 +17,7 @@ export const ATmega324p: Chip = { defaultFrequency: 16e6, clock: clockConfig, eeprom: eepromConfig, - gpio: { B: portBConfig, C: portCConfig, D: portDConfig }, + gpio: { A: portAConfig, B: portBConfig, C: portCConfig, D: portDConfig }, timers: [timer0Config, timer1Config, timer2Config], spi: [spiConfig], usart: [usart0Config], diff --git a/src/peripherals/adc_atmega324p.ts b/src/peripherals/adc_atmega324p.ts new file mode 100644 index 0000000..7f190ae --- /dev/null +++ b/src/peripherals/adc_atmega324p.ts @@ -0,0 +1,7 @@ +import { ADCConfig } from './adc'; +import { adcConfig as adcConfigAtmega328p } from './adc_atmega328p'; + +export const adcConfig: ADCConfig = { + ...adcConfigAtmega328p, + adcInterrupt: 0x2a, +}; diff --git a/src/peripherals/eeprom_atmega324p.ts b/src/peripherals/eeprom_atmega324p.ts new file mode 100644 index 0000000..204f560 --- /dev/null +++ b/src/peripherals/eeprom_atmega324p.ts @@ -0,0 +1,7 @@ +import { AVREEPROMConfig } from './eeprom'; +import { eepromConfig as eepromConfigAtmega328p } from './eeprom'; + +export const eepromConfig: AVREEPROMConfig = { + ...eepromConfigAtmega328p, + eepromReadyInterrupt: 0x32, +}; diff --git a/src/peripherals/timer_atmega324p.ts b/src/peripherals/timer_atmega324p.ts new file mode 100644 index 0000000..2e11963 --- /dev/null +++ b/src/peripherals/timer_atmega324p.ts @@ -0,0 +1,33 @@ +import { AVRTimerConfig } from './timer'; +import { + timer0Config as timer0Config328p, + timer0Config as timer1Config328p, + timer0Config as timer2Config328p, +} from './timer_atmega328p'; + +export const timer0Config: AVRTimerConfig = { + ...timer0Config328p, + captureInterrupt: 0, // Not applicable + compAInterrupt: 0x20, + compBInterrupt: 0x22, + compCInterrupt: 0, // Not applicable + ovfInterrupt: 0x24, +}; + +export const timer1Config: AVRTimerConfig = { + ...timer1Config328p, + captureInterrupt: 0x18, + compAInterrupt: 0x1a, + compBInterrupt: 0x1c, + compCInterrupt: 0, // Not applicable + ovfInterrupt: 0x1e, +}; + +export const timer2Config: AVRTimerConfig = { + ...timer2Config328p, + captureInterrupt: 0, // Not applicable + compAInterrupt: 0x12, + compBInterrupt: 0x14, + compCInterrupt: 0, // Not applicable + ovfInterrupt: 0x16, +}; diff --git a/src/peripherals/twi_atmega324p.ts b/src/peripherals/twi_atmega324p.ts new file mode 100644 index 0000000..8c8ab69 --- /dev/null +++ b/src/peripherals/twi_atmega324p.ts @@ -0,0 +1,7 @@ +import { TWIConfig } from './twi'; +import { twiConfig as twiconfigAtmega328p } from './twi'; + +export const twiConfig: TWIConfig = { + ...twiconfigAtmega328p, + twiInterrupt: 0x34, +}; diff --git a/src/peripherals/usart_atmega324p.ts b/src/peripherals/usart_atmega324p.ts new file mode 100644 index 0000000..4b9cfd7 --- /dev/null +++ b/src/peripherals/usart_atmega324p.ts @@ -0,0 +1,9 @@ +import { USARTConfig } from './usart'; +import { usart0Config as usart0ConfigAtmega328p } from './usart_atmega328p'; + +export const usart0Config: USARTConfig = { + ...usart0ConfigAtmega328p, + rxCompleteInterrupt: 0x28, + dataRegisterEmptyInterrupt: 0x2a, + txCompleteInterrupt: 0x2c, +}; From b374c38965b615e7876d016dddd0a7d55d6bb520 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 13 Feb 2023 20:30:37 -0500 Subject: [PATCH 06/10] Atmega32: Add in more 324-speciic mappings fr GPIO and SPI. 20MHZ clock usually too --- src/chips/ATmega324p.ts | 7 +- src/peripherals/gpio_atmega324p.ts | 106 +++++++++++++++++++++++++++++ src/peripherals/spi_atmega324p.ts | 7 ++ 3 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 src/peripherals/gpio_atmega324p.ts create mode 100644 src/peripherals/spi_atmega324p.ts diff --git a/src/chips/ATmega324p.ts b/src/chips/ATmega324p.ts index 5af8e8b..7732fc4 100644 --- a/src/chips/ATmega324p.ts +++ b/src/chips/ATmega324p.ts @@ -1,9 +1,8 @@ import { adcConfig } from '../peripherals/adc_atmega324p'; import { clockConfig } from '../peripherals/clock'; import { eepromConfig } from '../peripherals/eeprom_atmega324p'; -import { portAConfig } from '../peripherals/gpio_atmega2560'; -import { portBConfig, portCConfig, portDConfig } from '../peripherals/gpio_atmega328p'; -import { spiConfig } from '../peripherals/spi'; +import { portAConfig, portBConfig, portCConfig, portDConfig } from '../peripherals/gpio_atmega324p'; +import { spiConfig } from '../peripherals/spi_atmega324p'; import { timer0Config, timer1Config, timer2Config } from '../peripherals/timer_atmega324p'; import { twiConfig } from '../peripherals/twi_atmega324p'; import { usart0Config } from '../peripherals/usart_atmega324p'; @@ -14,7 +13,7 @@ export const ATmega324p: Chip = { ramSize: 0x800, eepromSize: 0x400, registerSpace: 0x100, - defaultFrequency: 16e6, + defaultFrequency: 20e6, clock: clockConfig, eeprom: eepromConfig, gpio: { A: portAConfig, B: portBConfig, C: portCConfig, D: portDConfig }, diff --git a/src/peripherals/gpio_atmega324p.ts b/src/peripherals/gpio_atmega324p.ts new file mode 100644 index 0000000..c0e3681 --- /dev/null +++ b/src/peripherals/gpio_atmega324p.ts @@ -0,0 +1,106 @@ +import { AVRExternalInterrupt, AVRPortConfig } from './gpio'; + +export const INT0: AVRExternalInterrupt = { + EICR: 0x69, + EIMSK: 0x3d, + EIFR: 0x3c, + index: 0, + iscOffset: 0, + interrupt: 2, +}; + +export const INT1: AVRExternalInterrupt = { + EICR: 0x69, + EIMSK: 0x3d, + EIFR: 0x3c, + index: 1, + iscOffset: 2, + interrupt: 4, +}; + +export const INT2: AVRExternalInterrupt = { + EICR: 0x69, + EIMSK: 0x3d, + EIFR: 0x3c, + index: 2, + iscOffset: 4, + interrupt: 6, +}; + +export const PCINT0 = { + PCIE: 0, + PCICR: 0x68, + PCIFR: 0x3b, + PCMSK: 0x6b, + pinChangeInterrupt: 0x8, + mask: 0xff, + offset: 0, +}; + +export const PCINT1 = { + PCIE: 1, + PCICR: 0x68, + PCIFR: 0x3b, + PCMSK: 0x6c, + pinChangeInterrupt: 0xa, + mask: 0xff, + offset: 0, +}; + +export const PCINT2 = { + PCIE: 2, + PCICR: 0x68, + PCIFR: 0x3b, + PCMSK: 0x6d, + pinChangeInterrupt: 0xc, + mask: 0xff, + offset: 0, +}; + +export const PCINT3 = { + PCIE: 3, + PCICR: 0x68, + PCIFR: 0x3b, + PCMSK: 0x73, + pinChangeInterrupt: 0xe, + mask: 0xff, + offset: 0, +}; + +export const portAConfig: AVRPortConfig = { + PIN: 0x20, + DDR: 0x21, + PORT: 0x22, + pinChange: PCINT0, + externalInterrupts: [], +}; + +export const portBConfig: AVRPortConfig = { + PIN: 0x23, + DDR: 0x24, + PORT: 0x25, + + // Interrupt settings + pinChange: PCINT1, + externalInterrupts: [], +}; + +export const portCConfig: AVRPortConfig = { + PIN: 0x26, + DDR: 0x27, + PORT: 0x28, + + // Interrupt settings + pinChange: PCINT2, + externalInterrupts: [], +}; + +export const portDConfig: AVRPortConfig = { + PIN: 0x29, + DDR: 0x2a, + PORT: 0x2b, + + // Interrupt settings + pinChange: PCINT3, + externalInterrupts: [INT0, INT1], +}; diff --git a/src/peripherals/spi_atmega324p.ts b/src/peripherals/spi_atmega324p.ts new file mode 100644 index 0000000..f921f4c --- /dev/null +++ b/src/peripherals/spi_atmega324p.ts @@ -0,0 +1,7 @@ +import { SPIConfig } from './spi'; +import { spiConfig as spiConfigAtmega328p } from './spi'; + +export const spiConfig: SPIConfig = { + ...spiConfigAtmega328p, + spiInterrupt: 0x26, +}; From 8a893e005d5630979e6a3a8ae97f550767abc9aa Mon Sep 17 00:00:00 2001 From: James Date: Mon, 13 Feb 2023 22:01:48 -0500 Subject: [PATCH 07/10] ATmega32: Add Uart tests, fix bugs from tests, thanks tests. --- src/peripherals/usart_atmega32.spec.ts | 40 ++++++++++++++++++++++++++ src/peripherals/usart_atmega32.ts | 19 ++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/peripherals/usart_atmega32.spec.ts diff --git a/src/peripherals/usart_atmega32.spec.ts b/src/peripherals/usart_atmega32.spec.ts new file mode 100644 index 0000000..4e340e2 --- /dev/null +++ b/src/peripherals/usart_atmega32.spec.ts @@ -0,0 +1,40 @@ +import { CPU } from '../cpu/cpu'; +import { AVRUSARTATmega32, usart0Config } from './usart_atmega32'; + +const FREQ_16MHZ = 16e6; + +describe('ATmege32 USART', () => { + describe('UBRRH AND UCSRC', () => { + it('has has both registers equal in config', () => { + expect(usart0Config.UBRRH).toEqual(usart0Config.UCSRC); + }); + + it('writes to fake UCSRC when URSEL is 1', () => { + const cpu = new CPU(new Uint16Array(1024)); + const usart = new AVRUSARTATmega32(cpu, usart0Config, FREQ_16MHZ); + cpu.writeData(usart0Config.UCSRC, usart0Config.UCSRC_URSEL | 0x40); + expect(usart.Ucsrc).toEqual(0x40); + expect(usart.Ubrrh).toEqual(0); + }); + + it('writes to fake UBRRH when URSEL is 0', () => { + const cpu = new CPU(new Uint16Array(1024)); + const usart = new AVRUSARTATmega32(cpu, usart0Config, FREQ_16MHZ); + cpu.writeData(usart0Config.UCSRC, 0x40); + expect(usart.Ucsrc).toEqual(0); + expect(usart.Ubrrh).toEqual(0x40); + }); + + it('reads UBRRH and UCSRC sequentually', () => { + const cpu = new CPU(new Uint16Array(1024)); + cpu.cycles = 100; + const usart = new AVRUSARTATmega32(cpu, usart0Config, FREQ_16MHZ); + cpu.writeData(usart0Config.UCSRC, usart0Config.UCSRC_URSEL | 0x40); + cpu.writeData(usart0Config.UCSRC, 0x0a); + cpu.cycles += 1; + expect(cpu.readData(usart0Config.UCSRC)).toEqual(0x0a); + cpu.cycles += 1; + expect(cpu.readData(usart0Config.UCSRC)).toEqual(0x40); + }); + }); +}); diff --git a/src/peripherals/usart_atmega32.ts b/src/peripherals/usart_atmega32.ts index d341c42..a335102 100644 --- a/src/peripherals/usart_atmega32.ts +++ b/src/peripherals/usart_atmega32.ts @@ -29,6 +29,7 @@ export const usart0Config: USARTConfig = { // // In the Atmega32 (and ATmega16, but not ATmega64): +// https://ww1.microchip.com/downloads/en/DeviceDoc/doc2503.pdf // Reference ATmega32 datasheet section "Accessing UBRRH/ UCSRC Registers" // // The UBRRH Register shares the same I/O location as the UCSRC Register. Therefore some @@ -56,7 +57,7 @@ export class AVRUSARTATmega32 extends AVRUSART { private writeUCSRCOrUBRRH(value: number) { if (value & this.config.UCSRC_URSEL) { - this.UCSRC = value; + this.UCSRC = value & ~this.config.UCSRC_URSEL; } else { this.UBRRH = value; } @@ -64,11 +65,25 @@ export class AVRUSARTATmega32 extends AVRUSART { return true; } + // See Atmega32 Datasheet + // https://ww1.microchip.com/downloads/en/DeviceDoc/doc2503.pdf + // Reading the I/O location once returns the UBRRH Register contents. + // If the register location was read in previous system clock cycle, + // reading the register in the current clock cycle will return the UCSRC contents. + // private readUCSRCOrUBRRH() { - if (this.lastUbrrhReadCycle === this.cpu.cycles - 1) { + if (this.cpu.cycles > 0 && this.lastUbrrhReadCycle === this.cpu.cycles - 1) { return this.UCSRC; } this.lastUbrrhReadCycle = this.cpu.cycles; return this.UBRRH; } + + get Ubrrh() { + return this.UBRRH; + } + + get Ucsrc() { + return this.UCSRC; + } } From 1bb309f544adc50fe52e40ed659c3cb7a81a4cc8 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 13 Feb 2023 22:06:31 -0500 Subject: [PATCH 08/10] Add another 32 USART test --- src/peripherals/usart_atmega32.spec.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/peripherals/usart_atmega32.spec.ts b/src/peripherals/usart_atmega32.spec.ts index 4e340e2..7f82f9a 100644 --- a/src/peripherals/usart_atmega32.spec.ts +++ b/src/peripherals/usart_atmega32.spec.ts @@ -31,10 +31,21 @@ describe('ATmege32 USART', () => { const usart = new AVRUSARTATmega32(cpu, usart0Config, FREQ_16MHZ); cpu.writeData(usart0Config.UCSRC, usart0Config.UCSRC_URSEL | 0x40); cpu.writeData(usart0Config.UCSRC, 0x0a); - cpu.cycles += 1; expect(cpu.readData(usart0Config.UCSRC)).toEqual(0x0a); cpu.cycles += 1; expect(cpu.readData(usart0Config.UCSRC)).toEqual(0x40); }); + + it('resets the sequential read after multiple cycles', () => { + const cpu = new CPU(new Uint16Array(1024)); + cpu.cycles = 100; + const usart = new AVRUSARTATmega32(cpu, usart0Config, FREQ_16MHZ); + cpu.writeData(usart0Config.UCSRC, usart0Config.UCSRC_URSEL | 0x40); + cpu.writeData(usart0Config.UCSRC, 0x0a); + expect(cpu.readData(usart0Config.UCSRC)).toEqual(0x0a); + cpu.cycles += 1; + cpu.cycles += 1; + expect(cpu.readData(usart0Config.UCSRC)).toEqual(0x0a); + }); }); }); From 522eb062af10503b14a45e3a02a388feff23f7e7 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 16 Feb 2023 19:09:07 -0500 Subject: [PATCH 09/10] Clean up a bit --- package.json | 1 - src/peripherals/adc.spec.ts | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8af5483..4596bbf 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "build:demo": "vite build demo", "prepare": "husky install && npm run build", "start": "vite demo", - "start2": "vite clidemo", "lint": "eslint src/**/*.ts demo/**/*.ts", "test": "npm run lint && jest", "test:watch": "jest --watch", diff --git a/src/peripherals/adc.spec.ts b/src/peripherals/adc.spec.ts index f8d9849..b1ffca0 100644 --- a/src/peripherals/adc.spec.ts +++ b/src/peripherals/adc.spec.ts @@ -1,7 +1,7 @@ import { CPU } from '../cpu/cpu'; import { asmProgram, TestProgramRunner } from '../utils/test-utils'; import { AVRADC, ADCMuxInputType } from './adc'; -import { atmega328AdcConfig } from './adc_atmega328p'; +import { adcConfig } from './adc_atmega328p'; const R16 = 16; const R17 = 17; @@ -49,7 +49,7 @@ describe('ADC', () => { break `); const cpu = new CPU(program); - const adc = new AVRADC(cpu, atmega328AdcConfig); + const adc = new AVRADC(cpu, adcConfig); const runner = new TestProgramRunner(cpu); const adcReadSpy = jest.spyOn(adc, 'onADCRead'); @@ -110,7 +110,7 @@ describe('ADC', () => { break `); const cpu = new CPU(program); - const adc = new AVRADC(cpu, atmega328AdcConfig); + const adc = new AVRADC(cpu, adcConfig); const runner = new TestProgramRunner(cpu, () => { /* do nothing on break */ }); From 748add2a0dcb5c72631a2befefdd4a2a5a769231 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 24 Feb 2023 19:42:21 -0500 Subject: [PATCH 10/10] Merge review: type for createAvr, options, and fix 2560 export --- src/chips/ATmega2560.ts | 2 +- src/create-avr.ts | 21 ++++++++++++++++++--- src/index.ts | 5 +++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/chips/ATmega2560.ts b/src/chips/ATmega2560.ts index e577bcd..7c404af 100644 --- a/src/chips/ATmega2560.ts +++ b/src/chips/ATmega2560.ts @@ -16,7 +16,7 @@ import { twiConfig } from '../peripherals/twi'; import { usart0Config } from '../peripherals/usart_atmega328p'; import { Chip } from './chip'; -export const ATmega324p: Chip = { +export const ATmega2560: Chip = { flashSize: 0x40000, ramSize: 0x2000, eepromSize: 0x1000, diff --git a/src/create-avr.ts b/src/create-avr.ts index 13c94a3..da87f8e 100644 --- a/src/create-avr.ts +++ b/src/create-avr.ts @@ -10,11 +10,26 @@ import { AVRUSART } from './peripherals/usart'; export interface CreateAVROptions { eepromBackend?: EEPROMBackend; + program?: Uint16Array; + clockSpeedHz?: number; } -export function createAVR(config: Chip, options: CreateAVROptions = {}) { - const frequency = config.defaultFrequency; - const cpu = new CPU(new Uint16Array(config.flashSize / 2), config.ramSize); +export interface AVR { + cpu: CPU; + timers: AVRTimer[]; + clock: AVRClock; + eeprom?: AVREEPROM; + spi: AVRSPI[]; + usart: AVRUSART[]; + twi: AVRTWI[]; + gpio: { + [key: string]: AVRIOPort; + }; +} + +export function createAVR(config: Chip, options: CreateAVROptions = {}): AVR { + const frequency = options.clockSpeedHz ?? config.defaultFrequency; + const cpu = new CPU(options.program ?? new Uint16Array(config.flashSize / 2), config.ramSize); const timers = config.timers.map((timerConfig) => new AVRTimer(cpu, timerConfig)); const clock = new AVRClock(cpu, frequency, config.clock); const eeprom = diff --git a/src/index.ts b/src/index.ts index a8d3c93..1c87ea2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -52,3 +52,8 @@ export { usart0Config } from './peripherals/usart_atmega328p'; export { AVRUSI } from './peripherals/usi'; export { AVRWatchdog, watchdogConfig } from './peripherals/watchdog'; export type { WatchdogConfig } from './peripherals/watchdog'; +export { ATmega324p } from './chips/ATmega324p'; +export { ATmega32 } from './chips/ATmega32'; +export { ATmega328p } from './chips/ATmega328p'; +export { ATmega2560 } from './chips/ATmega2560'; +export { createAVR } from './create-avr';