diff --git a/src/machine/machine_atmega1280.go b/src/machine/machine_atmega1280.go index 62d58659..391c2740 100644 --- a/src/machine/machine_atmega1280.go +++ b/src/machine/machine_atmega1280.go @@ -181,7 +181,7 @@ func (pwm PWM) Configure(config PWMConfig) error { avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01) // monotonic timer is using the same time as PWM:0 // we must adust internal settings of monotonic timer when PWM:0 settings changed - AdjustMonotonicTimer() + adjustMonotonicTimer() } else { avr.TCCR2B.Set(prescaler) // Set the PWM mode to fast PWM (mode = 3). @@ -719,7 +719,7 @@ func (pwm PWM) Set(channel uint8, value uint32) { } // monotonic timer is using the same time as PWM:0 // we must adust internal settings of monotonic timer when PWM:0 settings changed - AdjustMonotonicTimer() + adjustMonotonicTimer() case 1: mask := interrupt.Disable() switch channel { diff --git a/src/machine/machine_atmega328p.go b/src/machine/machine_atmega328p.go index 390b69f4..ecb3c1b8 100644 --- a/src/machine/machine_atmega328p.go +++ b/src/machine/machine_atmega328p.go @@ -71,7 +71,7 @@ func (pwm PWM) Configure(config PWMConfig) error { avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01) // monotonic timer is using the same time as PWM:0 // we must adust internal settings of monotonic timer when PWM:0 settings changed - AdjustMonotonicTimer() + adjustMonotonicTimer() } else { avr.TCCR2B.Set(prescaler) // Set the PWM mode to fast PWM (mode = 3). @@ -400,7 +400,7 @@ func (pwm PWM) Set(channel uint8, value uint32) { } // monotonic timer is using the same time as PWM:0 // we must adust internal settings of monotonic timer when PWM:0 settings changed - AdjustMonotonicTimer() + adjustMonotonicTimer() case 1: mask := interrupt.Disable() switch channel { diff --git a/src/machine/machine_avr.go b/src/machine/machine_avr.go index b791594f..59b494dc 100644 --- a/src/machine/machine_avr.go +++ b/src/machine/machine_avr.go @@ -1,10 +1,10 @@ +//go:build avr // +build avr package machine import ( "device/avr" - "runtime/interrupt" "runtime/volatile" "unsafe" ) @@ -144,75 +144,8 @@ func (a ADC) Get() uint16 { return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8 } -var Ticks int64 // nanoseconds since start -var tickNanos int64 // nanoseconds per each tick +// linked from runtime.adjustMonotonicTimer +func adjustMonotonicTimer() -func InitMonotonicTimer() { - tickNanos = 0 - Ticks = 0 - - interrupt.New(avr.IRQ_TIMER0_OVF, func(i interrupt.Interrupt) { - Ticks += tickNanos - }) - - // initial initialization of the Timer0 - // - Mask interrupt - avr.TIMSK0.ClearBits(avr.TIMSK0_TOIE0 | avr.TIMSK0_OCIE0A | avr.TIMSK0_OCIE0B) - - // - Write new values to TCNT2, OCR2x, and TCCR2x. - avr.TCNT0.Set(0) - avr.OCR0A.Set(0xff) - // - Set mode 3 - avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01) - // - Set prescaler 1 - avr.TCCR0B.Set(avr.TCCR0B_CS00) - - AdjustMonotonicTimer() - - // - Unmask interrupt - avr.TIMSK0.SetBits(avr.TIMSK0_TOIE0) -} - -func AdjustMonotonicTimer() { - // adjust the tickNanos - tickNanos = currentTickNanos() -} - -func currentTickNanos() int64 { - // this time depends on clk_IO, prescale, mode and OCR0A - // assuming the clock source is CPU clock - prescaler := int64(avr.TCCR0B.Get() & 0x7) - clock := (int64(1e12) / prescaler) / int64(CPUFrequency()) - mode := avr.TCCR0A.Get() & 0x7 - - /* - Mode WGM02 WGM01 WGM00 Timer/Counter TOP Update of TOV Flag - Mode of Operation OCRx at Set on - 0 0 0 0 Normal 0xFF Immediate MAX - 1 0 0 1 PWM, Phase Correct 0xFF TOP BOTTOM - 2 0 1 0 CTC OCRA Immediate MAX - 3 0 1 1 Fast PWM 0xFF BOTTOM MAX - 5 1 0 1 PWM, Phase Correct OCRA TOP BOTTOM - 7 1 1 1 Fast PWM OCRA BOTTOM TOP - */ - switch mode { - case 0, 3: - // normal & fast PWM - // TOV0 Interrupt when moving from MAX (0xff) to 0x00 - return clock * 256 / 1000 - case 1: - // Phase Correct PWM - // TOV0 Interrupt when moving from MAX (0xff) to 0x00 - return clock * 256 * 2 / 1000 - case 2, 7: - // CTC & fast PWM - // TOV0 Interrupt when moving from MAX (OCRA) to 0x00 - return clock * int64(avr.OCR0A.Get()) / 1000 - case 5: - // Phase Correct PWM - // TOV0 Interrupt when moving from MAX (OCRA) to 0x00 - return clock * int64(avr.OCR0A.Get()) * 2 / 1000 - } - - return clock / 1000 // for unknown -} +// linked from runtime.initMonotonicTimer +func initMonotonicTimer() diff --git a/src/runtime/runtime_avr.go b/src/runtime/runtime_avr.go index 75102044..34fd7083 100644 --- a/src/runtime/runtime_avr.go +++ b/src/runtime/runtime_avr.go @@ -7,6 +7,7 @@ import ( "device/avr" "machine" "runtime/interrupt" + "runtime/volatile" "unsafe" ) @@ -58,7 +59,7 @@ func preinit() { func initHardware() { initUART() - machine.InitMonotonicTimer() + initMonotonicTimer() nextTimerRecalibrate = ticks() + timerRecalibrateInterval // Enable interrupts after initialization. @@ -81,7 +82,7 @@ func sleepTicks(d timeUnit) { now := waitTill - d if nextTimerRecalibrate < now { nextTimerRecalibrate = now + timerRecalibrateInterval - machine.AdjustMonotonicTimer() + adjustMonotonicTimer() } } for { @@ -98,10 +99,10 @@ func sleepTicks(d timeUnit) { } } -// ticks return time since start in nanoseconds -func ticks() (ticks timeUnit) { +func ticks() (ticksReturn timeUnit) { state := interrupt.Disable() - ticks = timeUnit(machine.Ticks) + // use volatile since ticksCount can be changed when running on multi-core boards. + ticksReturn = timeUnit(volatile.LoadUint64((*uint64)(unsafe.Pointer(&ticksCount)))) interrupt.Restore(state) return } @@ -118,3 +119,80 @@ func abort() { avr.Asm("sleep") } } + +var ticksCount int64 // nanoseconds since start +var nanosecondsInTick int64 // nanoseconds per each tick + +func initMonotonicTimer() { + nanosecondsInTick = 0 + ticksCount = 0 + + interrupt.New(avr.IRQ_TIMER0_OVF, func(i interrupt.Interrupt) { + // use volatile + increment := volatile.LoadUint64((*uint64)(unsafe.Pointer(&nanosecondsInTick))) + ticks := volatile.LoadUint64((*uint64)(unsafe.Pointer(&ticksCount))) + volatile.StoreUint64((*uint64)(unsafe.Pointer(&ticksCount)), ticks+increment) + }) + + // initial initialization of the Timer0 + // - Mask interrupt + avr.TIMSK0.ClearBits(avr.TIMSK0_TOIE0 | avr.TIMSK0_OCIE0A | avr.TIMSK0_OCIE0B) + + // - Write new values to TCNT2, OCR2x, and TCCR2x. + avr.TCNT0.Set(0) + avr.OCR0A.Set(0xff) + // - Set mode 3 + avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01) + // - Set prescaler 1 + avr.TCCR0B.Set(avr.TCCR0B_CS00) + + adjustMonotonicTimer() + + // - Unmask interrupt + avr.TIMSK0.SetBits(avr.TIMSK0_TOIE0) +} + +//go:linkname adjustMonotonicTimer machine.adjustMonotonicTimer +func adjustMonotonicTimer() { + // adjust the nanosecondsInTick using volatile + volatile.StoreUint64((*uint64)(unsafe.Pointer(&nanosecondsInTick)), uint64(currentNanosecondsInTick())) +} + +func currentNanosecondsInTick() int64 { + // this time depends on clk_IO, prescale, mode and OCR0A + // assuming the clock source is CPU clock + prescaler := int64(avr.TCCR0B.Get() & 0x7) + clock := (int64(1e12) / prescaler) / int64(machine.CPUFrequency()) + mode := avr.TCCR0A.Get() & 0x7 + + /* + Mode WGM02 WGM01 WGM00 Timer/Counter TOP Update of TOV Flag + Mode of Operation OCRx at Set on + 0 0 0 0 Normal 0xFF Immediate MAX + 1 0 0 1 PWM, Phase Correct 0xFF TOP BOTTOM + 2 0 1 0 CTC OCRA Immediate MAX + 3 0 1 1 Fast PWM 0xFF BOTTOM MAX + 5 1 0 1 PWM, Phase Correct OCRA TOP BOTTOM + 7 1 1 1 Fast PWM OCRA BOTTOM TOP + */ + switch mode { + case 0, 3: + // normal & fast PWM + // TOV0 Interrupt when moving from MAX (0xff) to 0x00 + return clock * 256 / 1000 + case 1: + // Phase Correct PWM + // TOV0 Interrupt when moving from MAX (0xff) to 0x00 + return clock * 256 * 2 / 1000 + case 2, 7: + // CTC & fast PWM + // TOV0 Interrupt when moving from MAX (OCRA) to 0x00 + return clock * int64(avr.OCR0A.Get()) / 1000 + case 5: + // Phase Correct PWM + // TOV0 Interrupt when moving from MAX (OCRA) to 0x00 + return clock * int64(avr.OCR0A.Get()) * 2 / 1000 + } + + return clock / 1000 // for unknown +}