/**
 * AVR8 Watchdog Timer
 * Part of AVR8js
 * Reference: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf
 *
 * Copyright (C) 2021 Uri Shaked
 */ import { AVRClock } from '..' ; import { AVRInterruptConfig , CPU } from '../cpu/cpu' ; import { u8 } from '../types' ; export interface WatchdogConfig { watchdogInterrupt : u8 ; MCUSR : u8 ; WDTCSR : u8 ; } const MCUSR_WDRF = 0x8 ; const WDTCSR_WDIF = 0x80 ; const WDTCSR_WDIE = 0x40 ; const WDTCSR_WDP3 = 0x20 ; const WDTCSR_WDCE = 0x10 ; const WDTCSR_WDE = 0x8 ; const WDTCSR_WDP2 = 0x4 ; const WDTCSR_WDP1 = 0x2 ; const WDTCSR_WDP0 = 0x1 ; const WDTCSR_WDP210 = WDTCSR_WDP2 | WDTCSR_WDP1 | WDTCSR_WDP0 ; const WDTCSR_PROTECT_MASK = WDTCSR_WDE | WDTCSR_WDP3 | WDTCSR_WDP210 ; export const watchdogConfig = { watchdogInterrupt : 0x0c , MCUSR : 0x54 , WDTCSR : 0x60 , } ; export class AVRWatchdog { readonly clockFrequency = 128000 ; /**
     * Used to keep track on the last write to WDCE. Once written, the WDE/WDP* bits can be changed.
     */ private changeEnabledCycles = 0 ; private watchdogTimeout = 0 ; private enabledValue = false ; private scheduled = false ; private Watchdog : AVRInterruptConfig = { address : this . config . watchdogInterrupt , flagRegister : this . config . WDTCSR , flagMask : WDTCSR_WDIF , enableRegister : this . config . WDTCSR , enableMask : WDTCSR_WDIE , } ; constructor ( private cpu , private config , private clock ) { const { WDTCSR } = config ; this . cpu . onWatchdogReset = ( ) => { this . resetWatchdog ( ) ; } ; cpu . writeHooks [ WDTCSR ] = ( value , oldValue ) => { if ( value & WDTCSR_WDCE && value & WDTCSR_WDE ) { this . changeEnabledCycles = this . cpu . cycles + 4 ; value = value & ~ WDTCSR_PROTECT_MASK ; } else { if ( this . cpu . cycles >= this . changeEnabledCycles ) { value = ( value & ~ WDTCSR_PROTECT_MASK ) | ( oldValue & WDTCSR_PROTECT_MASK ) ; } this . enabledValue = ! ! ( value & WDTCSR_WDE || value & WDTCSR_WDIE ) ; this . cpu . data [ WDTCSR ] = value ; } if ( this . enabled ) { this . resetWatchdog ( ) ; } if ( this . enabled && ! this . scheduled ) { this . cpu . addClockEvent ( this . checkWatchdog , this . watchdogTimeout - this . cpu . cycles ) ; } this . cpu . clearInterruptByFlag ( this . Watchdog , value ) ; return true ; } ; } resetWatchdog ( ) { const cycles = Math . floor ( ( this . clock . frequency / this . clockFrequency ) * this . prescaler ) ; this . watchdogTimeout = this . cpu . cycles + cycles ; } checkWatchdog = ( ) => { if ( this . enabled && this . cpu . cycles >= this . watchdogTimeout ) { const wdtcsr = this . cpu . data [ this . config . WDTCSR ] ; if ( wdtcsr & WDTCSR_WDIE ) { this . cpu . setInterruptFlag ( this . Watchdog ) ; } if ( wdtcsr & WDTCSR_WDE ) { if ( wdtcsr & WDTCSR_WDIE ) { this . cpu . data [ this . config . WDTCSR ] &= ~ WDTCSR_WDIE ; } else { this . cpu . reset ( ) ; this . scheduled = false ; this . cpu . data [ this . config . MCUSR ] |= MCUSR_WDRF ; return ; } } this . resetWatchdog ( ) ; } if ( this . enabled ) { this . scheduled = true ; this . cpu . addClockEvent ( this . checkWatchdog , this . watchdogTimeout - this . cpu . cycles ) ; } else { this . scheduled = false ; } } ; get enabled ( ) { return this . enabledValue ; } /**
     * The base clock frequency is 128KHz. Thus, a prescaler of 2048 gives 16ms timeout.
     */ get prescaler ( ) { const wdtcsr = this . cpu . data [ this . config . WDTCSR ] ; const value = ( ( wdtcsr & WDTCSR_WDP3 ) >> 2 ) | ( wdtcsr & WDTCSR_WDP210 ) ; return 2048 << value ; } }