1// CMSIS abstraction functions.
2//
3// Original copyright:
4//
5//     Copyright (c) 2009 - 2015 ARM LIMITED
6//
7//     All rights reserved.
8//     Redistribution and use in source and binary forms, with or without
9//     modification, are permitted provided that the following conditions are met:
10//     - Redistributions of source code must retain the above copyright
11//       notice, this list of conditions and the following disclaimer.
12//     - Redistributions in binary form must reproduce the above copyright
13//       notice, this list of conditions and the following disclaimer in the
14//       documentation and/or other materials provided with the distribution.
15//     - Neither the name of ARM nor the names of its contributors may be used
16//       to endorse or promote products derived from this software without
17//       specific prior written permission.
18//
19//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20//     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21//     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22//     ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
23//     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24//     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25//     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26//     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27//     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28//     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29//     POSSIBILITY OF SUCH DAMAGE.
30package arm
31
32import (
33	"errors"
34	"runtime/volatile"
35	"unsafe"
36)
37
38var errCycleCountTooLarge = errors.New("requested cycle count is too large, overflows 24 bit counter")
39
40// Run the given assembly code. The code will be marked as having side effects,
41// as it doesn't produce output and thus would normally be eliminated by the
42// optimizer.
43func Asm(asm string)
44
45// Run the given inline assembly. The code will be marked as having side
46// effects, as it would otherwise be optimized away. The inline assembly string
47// recognizes template values in the form {name}, like so:
48//
49//     arm.AsmFull(
50//         "str {value}, {result}",
51//         map[string]interface{}{
52//             "value":  1
53//             "result": &dest,
54//         })
55//
56// You can use {} in the asm string (which expands to a register) to set the
57// return value.
58func AsmFull(asm string, regs map[string]interface{}) uintptr
59
60// Run the following system call (SVCall) with 0 arguments.
61func SVCall0(num uintptr) uintptr
62
63// Run the following system call (SVCall) with 1 argument.
64func SVCall1(num uintptr, a1 interface{}) uintptr
65
66// Run the following system call (SVCall) with 2 arguments.
67func SVCall2(num uintptr, a1, a2 interface{}) uintptr
68
69// Run the following system call (SVCall) with 3 arguments.
70func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr
71
72// Run the following system call (SVCall) with 4 arguments.
73func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr
74
75const (
76	SCS_BASE  = 0xE000E000
77	SYST_BASE = SCS_BASE + 0x0010
78	NVIC_BASE = SCS_BASE + 0x0100
79	SCB_BASE  = SCS_BASE + 0x0D00
80)
81
82const (
83	SCB_AIRCR_VECTKEY_Pos     = 16
84	SCB_AIRCR_SYSRESETREQ_Pos = 2
85	SCB_AIRCR_SYSRESETREQ_Msk = 1 << SCB_AIRCR_SYSRESETREQ_Pos
86)
87
88// System Control Block (SCB)
89//
90// SCB_Type provides the definitions for the System Control Block Registers.
91type SCB_Type struct {
92	CPUID volatile.Register32    // CPUID Base Register
93	ICSR  volatile.Register32    // Interrupt Control and State Register
94	VTOR  volatile.Register32    // Vector Table Offset Register
95	AIRCR volatile.Register32    // Application Interrupt and Reset Control Register
96	SCR   volatile.Register32    // System Control Register
97	CCR   volatile.Register32    // Configuration Control Register
98	_     volatile.Register32    // RESERVED1;
99	SHP   [2]volatile.Register32 // System Handlers Priority Registers. [0] is RESERVED
100	SHCSR volatile.Register32    // System Handler Control and State Register
101}
102
103var SCB = (*SCB_Type)(unsafe.Pointer(uintptr(SCB_BASE)))
104
105// Nested Vectored Interrupt Controller (NVIC).
106//
107// Source:
108// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CIHIGCIF.html
109type NVIC_Type struct {
110	ISER [8]volatile.Register32 // Interrupt Set-enable Registers
111	_    [24]uint32
112	ICER [8]volatile.Register32 // Interrupt Clear-enable Registers
113	_    [24]uint32
114	ISPR [8]volatile.Register32 // Interrupt Set-pending Registers
115	_    [24]uint32
116	ICPR [8]volatile.Register32 // Interrupt Clear-pending Registers
117	_    [24]uint32
118	IABR [8]volatile.Register32 // Interrupt Active Bit Registers
119	_    [56]uint32
120	IPR  [60]volatile.Register32 // Interrupt Priority Registers
121}
122
123var NVIC = (*NVIC_Type)(unsafe.Pointer(uintptr(NVIC_BASE)))
124
125// System Timer (SYST)
126//
127// Source: https://static.docs.arm.com/ddi0403/e/DDI0403E_d_armv7m_arm.pdf B3.3
128type SYST_Type struct {
129	SYST_CSR   volatile.Register32
130	SYST_RVR   volatile.Register32
131	SYST_CVR   volatile.Register32
132	SYST_CALIB volatile.Register32
133}
134
135var SYST = (*SYST_Type)(unsafe.Pointer(uintptr(SYST_BASE)))
136
137// Bitfields for SYST: System Timer
138const (
139	// SYST.SYST_CSR: SysTick Control and Status Register
140	SYST_CSR_ENABLE_Pos    = 0x0     // Position of ENABLE field.
141	SYST_CSR_ENABLE_Msk    = 0x1     // Bit mask of ENABLE field.
142	SYST_CSR_ENABLE        = 0x1     // Bit ENABLE.
143	SYST_CSR_TICKINT_Pos   = 0x1     // Position of TICKINT field.
144	SYST_CSR_TICKINT_Msk   = 0x2     // Bit mask of TICKINT field.
145	SYST_CSR_TICKINT       = 0x2     // Bit TICKINT.
146	SYST_CSR_CLKSOURCE_Pos = 0x2     // Position of CLKSOURCE field.
147	SYST_CSR_CLKSOURCE_Msk = 0x4     // Bit mask of CLKSOURCE field.
148	SYST_CSR_CLKSOURCE     = 0x4     // Bit CLKSOURCE.
149	SYST_CSR_COUNTFLAG_Pos = 0x10    // Position of COUNTFLAG field.
150	SYST_CSR_COUNTFLAG_Msk = 0x10000 // Bit mask of COUNTFLAG field.
151	SYST_CSR_COUNTFLAG     = 0x10000 // Bit COUNTFLAG.
152
153	// SYST.SYST_RVR: SysTick Reload Value Register
154	SYST_RVR_RELOAD_Pos = 0x0      // Position of RELOAD field.
155	SYST_RVR_RELOAD_Msk = 0xffffff // Bit mask of RELOAD field.
156
157	// SYST.SYST_CVR: SysTick Current Value Register
158	SYST_CVR_CURRENT_Pos = 0x0      // Position of CURRENT field.
159	SYST_CVR_CURRENT_Msk = 0xffffff // Bit mask of CURRENT field.
160
161	// SYST.SYST_CALIB: SysTick Calibration Value Register
162	SYST_CALIB_TENMS_Pos = 0x0        // Position of TENMS field.
163	SYST_CALIB_TENMS_Msk = 0xffffff   // Bit mask of TENMS field.
164	SYST_CALIB_SKEW_Pos  = 0x1e       // Position of SKEW field.
165	SYST_CALIB_SKEW_Msk  = 0x40000000 // Bit mask of SKEW field.
166	SYST_CALIB_SKEW      = 0x40000000 // Bit SKEW.
167	SYST_CALIB_NOREF_Pos = 0x1f       // Position of NOREF field.
168	SYST_CALIB_NOREF_Msk = 0x80000000 // Bit mask of NOREF field.
169	SYST_CALIB_NOREF     = 0x80000000 // Bit NOREF.
170)
171
172// Enable the given interrupt number.
173func EnableIRQ(irq uint32) {
174	NVIC.ISER[irq>>5].Set(1 << (irq & 0x1F))
175}
176
177// Disable the given interrupt number.
178func DisableIRQ(irq uint32) {
179	NVIC.ICER[irq>>5].Set(1 << (irq & 0x1F))
180}
181
182// Set the priority of the given interrupt number.
183// Note that the priority is given as a 0-255 number, where some of the lower
184// bits are not implemented by the hardware. For example, to set a low interrupt
185// priority, use 0xc0, which is equivalent to using priority level 5 when the
186// hardware has 8 priority levels. Also note that the priority level is inverted
187// in ARM: a lower number means it is a more important interrupt and will
188// interrupt ISRs with a higher interrupt priority.
189func SetPriority(irq uint32, priority uint32) {
190	// Details:
191	// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/Cihgjeed.html
192	regnum := irq / 4
193	regpos := irq % 4
194	mask := uint32(0xff) << (regpos * 8) // bits to clear
195	priority = priority << (regpos * 8)  // bits to set
196	NVIC.IPR[regnum].Set((uint32(NVIC.IPR[regnum].Get()) &^ mask) | priority)
197}
198
199// DisableInterrupts disables all interrupts, and returns the old interrupt
200// state.
201func DisableInterrupts() uintptr {
202	return AsmFull(`
203		mrs {}, PRIMASK
204		cpsid i
205	`, nil)
206}
207
208// EnableInterrupts enables all interrupts again. The value passed in must be
209// the mask returned by DisableInterrupts.
210func EnableInterrupts(mask uintptr) {
211	AsmFull("msr PRIMASK, {mask}", map[string]interface{}{
212		"mask": mask,
213	})
214}
215
216// SystemReset performs a hard system reset.
217func SystemReset() {
218	// SCB->AIRCR  = ((0x5FA << SCB_AIRCR_VECTKEY_Pos)      |
219	//              SCB_AIRCR_SYSRESETREQ_Msk);
220	SCB.AIRCR.Set((0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk)
221
222	for {
223		Asm("wfi")
224	}
225}
226
227// Set up the system timer to generate periodic tick events.
228// This will cause SysTick_Handler to fire once per tick.
229// The cyclecount parameter is a counter value which can range from 0 to
230// 0xffffff.  A value of 0 disables the timer.
231func SetupSystemTimer(cyclecount uint32) error {
232	// turn it off
233	SYST.SYST_CSR.ClearBits(SYST_CSR_TICKINT | SYST_CSR_ENABLE)
234	if cyclecount == 0 {
235		// leave the system timer turned off.
236		return nil
237	}
238	if cyclecount&SYST_RVR_RELOAD_Msk != cyclecount {
239		// The cycle refresh register is only 24 bits wide.  The user-specified value will overflow.
240		return errCycleCountTooLarge
241	}
242
243	// set refresh count
244	SYST.SYST_RVR.Set(cyclecount)
245	// set current counter value
246	SYST.SYST_CVR.Set(cyclecount)
247	// enable clock, enable SysTick interrupt when clock reaches 0, run it off of the processor clock
248	SYST.SYST_CSR.SetBits(SYST_CSR_TICKINT | SYST_CSR_ENABLE | SYST_CSR_CLKSOURCE)
249	return nil
250}
251