1// +build nrf
2
3package runtime
4
5import (
6	"device/arm"
7	"device/nrf"
8	"machine"
9	"runtime/interrupt"
10	"runtime/volatile"
11)
12
13type timeUnit int64
14
15//go:linkname systemInit SystemInit
16func systemInit()
17
18func postinit() {}
19
20//export Reset_Handler
21func main() {
22	systemInit()
23	preinit()
24	run()
25	abort()
26}
27
28func init() {
29	machine.UART0.Configure(machine.UARTConfig{})
30	initLFCLK()
31	initRTC()
32}
33
34func initLFCLK() {
35	if machine.HasLowFrequencyCrystal {
36		nrf.CLOCK.LFCLKSRC.Set(nrf.CLOCK_LFCLKSTAT_SRC_Xtal)
37	}
38	nrf.CLOCK.TASKS_LFCLKSTART.Set(1)
39	for nrf.CLOCK.EVENTS_LFCLKSTARTED.Get() == 0 {
40	}
41	nrf.CLOCK.EVENTS_LFCLKSTARTED.Set(0)
42}
43
44func initRTC() {
45	nrf.RTC1.TASKS_START.Set(1)
46	intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) {
47		nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0)
48		nrf.RTC1.EVENTS_COMPARE[0].Set(0)
49		rtc_wakeup.Set(1)
50	})
51	intr.SetPriority(0xc0) // low priority
52	intr.Enable()
53}
54
55func putchar(c byte) {
56	machine.UART0.WriteByte(c)
57}
58
59const asyncScheduler = false
60
61func sleepTicks(d timeUnit) {
62	for d != 0 {
63		ticks()                       // update timestamp
64		ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
65		rtc_sleep(ticks)
66		d -= timeUnit(ticks)
67	}
68}
69
70var (
71	timestamp      timeUnit // nanoseconds since boottime
72	rtcLastCounter uint32   // 24 bits ticks
73)
74
75// ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds.
76func ticksToNanoseconds(ticks timeUnit) int64 {
77	// The following calculation is actually the following, but with both sides
78	// reduced to reduce the risk of overflow:
79	//     ticks * 1e9 / 32768
80	return int64(ticks) * 1953125 / 64
81}
82
83// nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz).
84func nanosecondsToTicks(ns int64) timeUnit {
85	// The following calculation is actually the following, but with both sides
86	// reduced to reduce the risk of overflow:
87	//     ns * 32768 / 1e9
88	return timeUnit(ns * 64 / 1953125)
89}
90
91// Monotonically increasing numer of ticks since start.
92//
93// Note: very long pauses between measurements (more than 8 minutes) may
94// overflow the counter, leading to incorrect results. This might be fixed by
95// handling the overflow event.
96func ticks() timeUnit {
97	rtcCounter := uint32(nrf.RTC1.COUNTER.Get())
98	offset := (rtcCounter - rtcLastCounter) & 0xffffff // change since last measurement
99	rtcLastCounter = rtcCounter
100	timestamp += timeUnit(offset)
101	return timestamp
102}
103
104var rtc_wakeup volatile.Register8
105
106func rtc_sleep(ticks uint32) {
107	nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_COMPARE0)
108	rtc_wakeup.Set(0)
109	if ticks == 1 {
110		// Race condition (even in hardware) at ticks == 1.
111		// TODO: fix this in a better way by detecting it, like the manual
112		// describes.
113		ticks = 2
114	}
115	nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff)
116	for rtc_wakeup.Get() == 0 {
117		arm.Asm("wfi")
118	}
119}
120