diff --git a/src/chips/ATmega2560.ts b/src/chips/ATmega2560.ts new file mode 100644 index 0000000..7c404af --- /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 ATmega2560: 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..7732fc4 --- /dev/null +++ b/src/chips/ATmega324p.ts @@ -0,0 +1,25 @@ +import { adcConfig } from '../peripherals/adc_atmega324p'; +import { clockConfig } from '../peripherals/clock'; +import { eepromConfig } from '../peripherals/eeprom_atmega324p'; +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'; +import { Chip } from './chip'; + +export const ATmega324p: Chip = { + flashSize: 0x8000, + ramSize: 0x800, + eepromSize: 0x400, + registerSpace: 0x100, + defaultFrequency: 20e6, + 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/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..da87f8e --- /dev/null +++ b/src/create-avr.ts @@ -0,0 +1,50 @@ +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; + program?: Uint16Array; + clockSpeedHz?: number; +} + +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 = + 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 e529bb6..1c87ea2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,35 +3,30 @@ * * 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 { - adcConfig, - ADCMuxInputType, - ADCReference, - atmega328Channels, - AVRADC, -} from './peripherals/adc'; +export { ADCMuxInputType, ADCReference, AVRADC } from './peripherals/adc'; +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'; 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 +34,7 @@ export { portJConfig, portKConfig, portLConfig, -} from './peripherals/gpio'; +} from './peripherals/gpio_atmega2560'; export type { AVRExternalInterrupt, AVRPinChangeInterrupt, @@ -48,10 +43,17 @@ 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 { timer0Config, timer1Config, timer2Config } from './peripherals/timer_atmega328p'; export type { AVRTimerConfig } from './peripherals/timer'; export * from './peripherals/twi'; -export { AVRUSART, usart0Config } from './peripherals/usart'; +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'; +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'; diff --git a/src/peripherals/adc.spec.ts b/src/peripherals/adc.spec.ts index 4eb9aff..b1ffca0 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 { adcConfig } from './adc_atmega328p'; const R16 = 16; const R17 = 17; 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..f910168 --- /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 adcConfig: 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_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/adc_atmega328p.ts b/src/peripherals/adc_atmega328p.ts new file mode 100644 index 0000000..0d7e136 --- /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 adcConfig: 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/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/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..3f2a6c4 --- /dev/null +++ b/src/peripherals/gpio_atmega32.ts @@ -0,0 +1,29 @@ +import { AVRPortConfig } from './gpio'; + +export const portAConfig: AVRPortConfig = { + PORT: 0x3b, + DDR: 0x3a, + PIN: 0x39, + externalInterrupts: [], +}; + +export const portBConfig: AVRPortConfig = { + PORT: 0x38, + DDR: 0x37, + PIN: 0x36, + externalInterrupts: [], +}; + +export const portCConfig: AVRPortConfig = { + PORT: 0x35, + DDR: 0x34, + PIN: 0x33, + externalInterrupts: [], +}; + +export const portDConfig: AVRPortConfig = { + PORT: 0x32, + DDR: 0x31, + PIN: 0x30, + externalInterrupts: [], +}; 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/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/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, +}; diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 609908d..9e8bd8c 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -1,7 +1,9 @@ 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 { timer0Config, timer1Config, timer2Config } from './timer_atmega328p'; // CPU registers const R1 = 1; 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..2fbbe9a --- /dev/null +++ b/src/peripherals/timer_atmega32.ts @@ -0,0 +1,154 @@ +import { portBConfig, portDConfig } 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 timer0Config: 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: portBConfig.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 timer1Config: 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: portDConfig.PORT, + compPinA: 5, + compPortB: portDConfig.PORT, + compPinB: 4, + compPortC: 0, // Not available + compPinC: 0, // Not available + + externalClockPort: 0, // Unimplemented ? Not available + externalClockPin: 0, // Unimplemented ? Not available +}; + +export const timer2Config: 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: portBConfig.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_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/timer_atmega328p.ts b/src/peripherals/timer_atmega328p.ts new file mode 100644 index 0000000..fca68d8 --- /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 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, +}; 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.spec.ts b/src/peripherals/usart.spec.ts index 8dbd5ce..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, usart0Config } from './usart'; +import { AVRUSART } from './usart'; +import { usart0Config } from './usart_atmega328p'; const FREQ_16MHZ = 16e6; const FREQ_11_0529MHZ = 11059200; diff --git a/src/peripherals/usart.ts b/src/peripherals/usart.ts index 1b7546b..964873e 100644 --- a/src/peripherals/usart.ts +++ b/src/peripherals/usart.ts @@ -9,7 +9,69 @@ import { AVRInterruptConfig, CPU } from '../cpu/cpu'; import { u8 } from '../types'; -export interface USARTConfig { +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 extends USARTRegisterBits { rxCompleteInterrupt: u8; dataRegisterEmptyInterrupt: u8; txCompleteInterrupt: u8; @@ -22,52 +84,10 @@ 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_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_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, @@ -75,6 +95,7 @@ const rxMasks = { 8: 0xff, 9: 0xff, }; + export class AVRUSART { public onByteTransmit: USARTTransmitCallback | null = null; public onLineTransmit: USARTLineTransmitCallback | null = null; @@ -85,34 +106,38 @@ export class AVRUSART { private rxByte = 0; private lineBuffer = ''; - // Interrupts private RXC: AVRInterruptConfig = { address: this.config.rxCompleteInterrupt, flagRegister: this.config.UCSRA, - flagMask: UCSRA_RXC, + flagMask: this.config.UCSRA_RXC, enableRegister: this.config.UCSRB, - enableMask: UCSRB_RXCIE, + enableMask: this.config.UCSRB_RXCIE, constant: true, }; private UDRE: AVRInterruptConfig = { address: this.config.dataRegisterEmptyInterrupt, flagRegister: this.config.UCSRA, - flagMask: UCSRA_UDRE, + flagMask: this.config.UCSRA_UDRE, enableRegister: this.config.UCSRB, - enableMask: UCSRB_UDRIE, + enableMask: this.config.UCSRB_UDRIE, }; private TXC: AVRInterruptConfig = { address: this.config.txCompleteInterrupt, flagRegister: this.config.UCSRA, - flagMask: UCSRA_TXC, + flagMask: this.config.UCSRA_TXC, enableRegister: this.config.UCSRB, - enableMask: UCSRB_TXCIE, + enableMask: this.config.UCSRB_TXCIE, }; - constructor(private cpu: CPU, private config: USARTConfig, private freqHz: number) { + 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 & (UCSRA_MPCM | UCSRA_U2X); + 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?.(); @@ -123,11 +148,10 @@ export class AVRUSART { cpu.updateInterruptEnable(this.RXC, value); cpu.updateInterruptEnable(this.UDRE, value); cpu.updateInterruptEnable(this.TXC, value); - if (value & UCSRB_RXEN && oldValue & UCSRB_RXEN) { + if (value & this.config.UCSRB_RXEN && oldValue & this.config.UCSRB_RXEN) { cpu.clearInterrupt(this.RXC); } - if (value & UCSRB_TXEN && !(oldValue & UCSRB_TXEN)) { - // Enabling the transmission - mark UDR as empty + if (value & this.config.UCSRB_TXEN && !(oldValue & this.config.UCSRB_TXEN)) { cpu.setInterruptFlag(this.UDRE); } cpu.data[config.UCSRB] = value; @@ -181,9 +205,9 @@ export class AVRUSART { } reset() { - this.cpu.data[this.config.UCSRA] = UCSRA_UDRE; + this.cpu.data[this.config.UCSRA] = this.config.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.cpu.data[this.config.UCSRC] = this.config.UCSRC_UCSZ1 | this.config.UCSRC_UCSZ0; this.rxBusyValue = false; this.rxByte = 0; this.lineBuffer = ''; @@ -223,15 +247,15 @@ export class AVRUSART { } private get multiplier() { - return this.cpu.data[this.config.UCSRA] & UCSRA_U2X ? 8 : 16; + return this.cpu.data[this.config.UCSRA] & this.config.UCSRA_U2X ? 8 : 16; } get rxEnable() { - return !!(this.cpu.data[this.config.UCSRB] & UCSRB_RXEN); + return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_RXEN); } get txEnable() { - return !!(this.cpu.data[this.config.UCSRB] & UCSRB_TXEN); + return !!(this.cpu.data[this.config.UCSRB] & this.config.UCSRB_TXEN); } get baudRate() { @@ -240,8 +264,9 @@ export class AVRUSART { get bitsPerChar() { const ucsz = - ((this.cpu.data[this.config.UCSRC] & (UCSRC_UCSZ1 | UCSRC_UCSZ0)) >> 1) | - (this.cpu.data[this.config.UCSRB] & UCSRB_UCSZ2); + ((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; @@ -251,21 +276,21 @@ export class AVRUSART { return 7; case 3: return 8; - default: // 4..6 are reserved + default: case 7: return 9; } } get stopBits() { - return this.cpu.data[this.config.UCSRC] & UCSRC_USBS ? 2 : 1; + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_USBS ? 2 : 1; } get parityEnabled() { - return this.cpu.data[this.config.UCSRC] & UCSRC_UPM1 ? true : false; + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM1 ? true : false; } get parityOdd() { - return this.cpu.data[this.config.UCSRC] & UCSRC_UPM0 ? true : false; + return this.cpu.data[this.config.UCSRC] & this.config.UCSRC_UPM0 ? true : false; } } diff --git a/src/peripherals/usart_atmega32.spec.ts b/src/peripherals/usart_atmega32.spec.ts new file mode 100644 index 0000000..7f82f9a --- /dev/null +++ b/src/peripherals/usart_atmega32.spec.ts @@ -0,0 +1,51 @@ +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); + 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); + }); + }); +}); diff --git a/src/peripherals/usart_atmega32.ts b/src/peripherals/usart_atmega32.ts new file mode 100644 index 0000000..a335102 --- /dev/null +++ b/src/peripherals/usart_atmega32.ts @@ -0,0 +1,89 @@ +/** + * 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'; + +export const usart0Config: USARTConfig = { + rxCompleteInterrupt: 0x1a, + dataRegisterEmptyInterrupt: 0x1c, + txCompleteInterrupt: 0x1e, + UCSRA: 0x2b, + UCSRB: 0x2a, + UCSRC: 0x40, + 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): +// 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 +// 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 & ~this.config.UCSRC_URSEL; + } else { + this.UBRRH = value; + } + this.onConfigurationChange?.(); + 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.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; + } +} 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, +}; diff --git a/src/peripherals/usart_atmega328p.ts b/src/peripherals/usart_atmega328p.ts new file mode 100644 index 0000000..b7ff3db --- /dev/null +++ b/src/peripherals/usart_atmega328p.ts @@ -0,0 +1,26 @@ +/** + * AVR-8 USART Peripheral + * Part of AVR8js + * Reference: http: + * + * Copyright (C) 2019, 2020, 2021 Uri Shaked + */ +import { standardUartRegisterBits, USARTConfig } from './usart'; + +export const usart0Config: 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, +};