From 6b1faeb882e579287835e8a7449f662e26fa0a9b Mon Sep 17 00:00:00 2001 From: Infinoid Date: Thu, 24 Oct 2019 15:17:06 -0400 Subject: [PATCH] device/arm: add system timer registers (#654) * device/arm: add system timer registers Add SYST registers and bit definitions to device/arm. Add a setup function. Add an example that uses it to blink an LED. --- src/device/arm/arm.go | 74 +++++++++++++++++++++++++++++++++ src/examples/systick/README.md | 12 ++++++ src/examples/systick/systick.go | 28 +++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 src/examples/systick/README.md create mode 100644 src/examples/systick/systick.go diff --git a/src/device/arm/arm.go b/src/device/arm/arm.go index 05749ea7..fcac1e57 100644 --- a/src/device/arm/arm.go +++ b/src/device/arm/arm.go @@ -30,6 +30,7 @@ package arm import ( + "errors" "runtime/volatile" "unsafe" ) @@ -72,6 +73,7 @@ func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr const ( SCS_BASE = 0xE000E000 + SYST_BASE = SCS_BASE + 0x0010 NVIC_BASE = SCS_BASE + 0x0100 SCB_BASE = SCS_BASE + 0x0D00 ) @@ -119,6 +121,53 @@ type NVIC_Type struct { var NVIC = (*NVIC_Type)(unsafe.Pointer(uintptr(NVIC_BASE))) +// System Timer (SYST) +// +// Source: https://static.docs.arm.com/ddi0403/e/DDI0403E_d_armv7m_arm.pdf B3.3 +type SYST_Type struct { + SYST_CSR volatile.Register32 + SYST_RVR volatile.Register32 + SYST_CVR volatile.Register32 + SYST_CALIB volatile.Register32 +} + +var SYST = (*SYST_Type)(unsafe.Pointer(uintptr(SYST_BASE))) + +// Bitfields for SYST: System Timer +const ( + // SYST.SYST_CSR: SysTick Control and Status Register + SYST_CSR_ENABLE_Pos = 0x0 // Position of ENABLE field. + SYST_CSR_ENABLE_Msk = 0x1 // Bit mask of ENABLE field. + SYST_CSR_ENABLE = 0x1 // Bit ENABLE. + SYST_CSR_TICKINT_Pos = 0x1 // Position of TICKINT field. + SYST_CSR_TICKINT_Msk = 0x2 // Bit mask of TICKINT field. + SYST_CSR_TICKINT = 0x2 // Bit TICKINT. + SYST_CSR_CLKSOURCE_Pos = 0x2 // Position of CLKSOURCE field. + SYST_CSR_CLKSOURCE_Msk = 0x4 // Bit mask of CLKSOURCE field. + SYST_CSR_CLKSOURCE = 0x4 // Bit CLKSOURCE. + SYST_CSR_COUNTFLAG_Pos = 0x10 // Position of COUNTFLAG field. + SYST_CSR_COUNTFLAG_Msk = 0x10000 // Bit mask of COUNTFLAG field. + SYST_CSR_COUNTFLAG = 0x10000 // Bit COUNTFLAG. + + // SYST.SYST_RVR: SysTick Reload Value Register + SYST_RVR_RELOAD_Pos = 0x0 // Position of RELOAD field. + SYST_RVR_RELOAD_Msk = 0xffffff // Bit mask of RELOAD field. + + // SYST.SYST_CVR: SysTick Current Value Register + SYST_CVR_CURRENT_Pos = 0x0 // Position of CURRENT field. + SYST_CVR_CURRENT_Msk = 0xffffff // Bit mask of CURRENT field. + + // SYST.SYST_CALIB: SysTick Calibration Value Register + SYST_CALIB_TENMS_Pos = 0x0 // Position of TENMS field. + SYST_CALIB_TENMS_Msk = 0xffffff // Bit mask of TENMS field. + SYST_CALIB_SKEW_Pos = 0x1e // Position of SKEW field. + SYST_CALIB_SKEW_Msk = 0x40000000 // Bit mask of SKEW field. + SYST_CALIB_SKEW = 0x40000000 // Bit SKEW. + SYST_CALIB_NOREF_Pos = 0x1f // Position of NOREF field. + SYST_CALIB_NOREF_Msk = 0x80000000 // Bit mask of NOREF field. + SYST_CALIB_NOREF = 0x80000000 // Bit NOREF. +) + // Enable the given interrupt number. func EnableIRQ(irq uint32) { NVIC.ISER[irq>>5].Set(1 << (irq & 0x1F)) @@ -169,3 +218,28 @@ func SystemReset() { Asm("wfi") } } + +// Set up the system timer to generate periodic tick events. +// This will cause SysTick_Handler to fire once per tick. +// The cyclecount parameter is a counter value which can range from 0 to +// 0xffffff. A value of 0 disables the timer. +func SetupSystemTimer(cyclecount uint32) error { + // turn it off + SYST.SYST_CSR.ClearBits(SYST_CSR_TICKINT | SYST_CSR_ENABLE) + if cyclecount == 0 { + // leave the system timer turned off. + return nil + } + if cyclecount&SYST_RVR_RELOAD_Msk != cyclecount { + // The cycle refresh register is only 24 bits wide. The user-specified value will overflow. + return errors.New("requested cycle count is too large, overflows 24 bit counter") + } + + // set refresh count + SYST.SYST_RVR.Set(cyclecount) + // set current counter value + SYST.SYST_CVR.Set(cyclecount) + // enable clock, enable SysTick interrupt when clock reaches 0, run it off of the processor clock + SYST.SYST_CSR.SetBits(SYST_CSR_TICKINT | SYST_CSR_ENABLE | SYST_CSR_CLKSOURCE) + return nil +} diff --git a/src/examples/systick/README.md b/src/examples/systick/README.md new file mode 100644 index 00000000..e11c0503 --- /dev/null +++ b/src/examples/systick/README.md @@ -0,0 +1,12 @@ +# TinyGo ARM SysTick example + +This example uses the ARM System Timer to blink an LED. The timer fires +an interrupt 10 times per second. The interrupt handler toggles the LED on +and off. + +Many ARM-based chips have this timer feature. If you run the example and the +LED blinks, then you have one. + +The System Timer runs from a cycle counter. The more cycles, the slower the +LED will blink. This counter is 24 bits wide, which places an upper bound on +the number of cycles, and the slowness of the blinking. diff --git a/src/examples/systick/systick.go b/src/examples/systick/systick.go new file mode 100644 index 00000000..73316bbf --- /dev/null +++ b/src/examples/systick/systick.go @@ -0,0 +1,28 @@ +package main + +import ( + "device/arm" + "machine" +) + +func main() { + machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + // timer fires 10 times per second + arm.SetupSystemTimer(machine.CPU_FREQUENCY / 10) + + for { + } +} + +var led_state bool + +//go:export SysTick_Handler +func timer_isr() { + if led_state { + machine.LED.Low() + } else { + machine.LED.High() + } + led_state = !led_state +}