File : s-bbbosu-armv7m.adb
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
5 -- S Y S T E M . B B . B O A R D _ S U P P O R T --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1999-2002 Universidad Politecnica de Madrid --
10 -- Copyright (C) 2003-2005 The European Space Agency --
11 -- Copyright (C) 2003-2016, AdaCore --
12 -- --
13 -- GNAT is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 3, or (at your option) any later ver- --
16 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. --
19 -- --
20 -- --
21 -- --
22 -- --
23 -- --
24 -- You should have received a copy of the GNU General Public License and --
25 -- a copy of the GCC Runtime Library Exception along with this program; --
26 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
27 -- <http://www.gnu.org/licenses/>. --
28 -- --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
31 -- --
32 -- The port of GNARL to bare board targets was initially developed by the --
33 -- Real-Time Systems Group at the Technical University of Madrid. --
34 -- --
35 ------------------------------------------------------------------------------
36
37 with System.Machine_Code;
38
39 with System.BB.Parameters; use System.BB.Parameters;
40
41 package body System.BB.Board_Support is
42 use CPU_Primitives, Interrupts, Machine_Code;
43
44 Sys_Tick_Vector : constant Vector_Id := 15;
45 Interrupt_Request_Vector : constant Vector_Id := 16;
46 -- See vector definitions in ARMv7-M version of System.BB.CPU_Primitives.
47 -- Defined by ARMv7-M specifications.
48
49 First_IRQ : constant Interrupt_ID := 2;
50 -- This corresponds to the first IRQ number (handled by the NVIC). This
51 -- offset is present so that the Sys_Tick exception can be handled like
52 -- other interrupts, and because interrupt id 0 is reserved.
53
54 Alarm_Time : Timer_Interval;
55 pragma Volatile (Alarm_Time);
56 pragma Export (C, Alarm_Time, "__gnat_alarm_time");
57
58 ---------------------------
59 -- System control and ID --
60 ---------------------------
61
62 ICSR : Word with Volatile, Address => 16#E000_ED04#;
63 -- Interrupt Control State (part of the System Control Block - SCB)
64
65 ICSR_Pend_ST_Set : constant := 2**26; -- Set pending Sys_Tick (RW)
66 ICSR_Pend_ST_Clr : constant := 2**25; -- Clear pending Sys_Tick (W)
67
68 -----------------------
69 -- Sys_Tick Handling --
70 -----------------------
71
72 -- We use the Sys_Tick timer as a periodic timer with 1 kHz rate. This
73 -- is a trade-off between accurate delays, limited overhead and maximum
74 -- time that interrupts may be disabled.
75
76 Tick_Period : constant Timer_Interval := Clock_Frequency / 1000;
77
78 type Sys_Tick_Registers is record
79 SYST_CSR : Word;
80 SYST_RVR : Word;
81 SYST_CVR : Word;
82 SYST_CALIB : Word;
83 end record;
84
85 CSR_Count_Flag : constant := 2**16;
86 CSR_Clk_Source : constant := 2**2;
87 CSR_Tick_Int : constant := 2**1;
88 CSR_Enable : constant := 2**0;
89
90 RVR_Last : constant := 2**24 - 1;
91 pragma Assert (Tick_Period <= RVR_Last + 1);
92
93 SYST : Sys_Tick_Registers with Volatile, Address => 16#E000_E010#;
94 -- SysTick control and status register (Part of SYST).
95
96 Next_Tick_Time : Timer_Interval with Volatile;
97 -- Time when systick will expire. This gives the high digits of the time
98
99 ----------------------------------------------
100 -- New Vectored Interrupt Controller (NVIC) --
101 ----------------------------------------------
102
103 NVIC_Base : constant := 16#E000_E000#;
104 -- Nested Vectored Interrupt Controller (NVIC) base.
105
106 NVIC_ISER0 : constant Address := NVIC_Base + 16#100#;
107 -- Writing a bit mask to this register enables the corresponding interrupts
108
109 type PRI is mod 2**8;
110 -- Type for ARMv7-M interrupt priorities. Note that 0 is the highest
111 -- priority, which is reserved for the kernel and has no corresponding
112 -- Interrupt_Priority value, and 255 is the lowest. We assume the PRIGROUP
113 -- setting is such that the 4 most significant bits determine the priority
114 -- group used for preemption. However, if less bits are implemented, this
115 -- should still work.
116
117 function To_PRI (P : Integer) return PRI is
118 (if P not in Interrupt_Priority then 0
119 else PRI (Interrupt_Priority'Last - P + 1) * 16);
120 -- Return the BASEPRI mask for the given Ada priority. Note that the zero
121 -- value here means no mask, so no interrupts are masked.
122
123 function To_Priority (P : PRI) return Interrupt_Priority is
124 (if P = 0 then Interrupt_Priority'Last
125 else (Interrupt_Priority'Last - Any_Priority'Base (P / 16) + 1));
126 -- Given an ARM interrupt priority (PRI value), determine the Ada priority
127 -- While the value 0 is reserved for the kernel and has no Ada priority
128 -- that represents it, Interrupt_Priority'Last is closest.
129
130 IP : array (Interrupt_ID) of PRI with Volatile, Address => 16#E000_E400#;
131
132 -- Local utility functions
133
134 procedure Enable_Interrupt_Request
135 (Interrupt : Interrupt_ID;
136 Prio : Interrupt_Priority);
137 -- Enable interrupt requests for the given interrupt
138
139 ----------------------
140 -- Initialize_Board --
141 ----------------------
142
143 procedure Initialize_Board is
144 begin
145 -- Mask interrupts
146 Disable_Interrupts;
147
148 -- Because we operate the SysTick clock as a periodic timer, and 24 bits
149 -- at 168 MHz is sufficient for that, use the unscaled system clock.
150
151 -- To initialize the Sys_Tick timer, first disable the clock, then
152 -- program it and finally enable it. This way an accidentally
153 -- misconfigured timer will not cause pending interrupt while
154 -- reprogramming.
155
156 SYST.SYST_CSR := CSR_Clk_Source; -- disable clock
157 SYST.SYST_RVR := Word (Tick_Period - 1);
158 SYST.SYST_CVR := 0;
159 SYST.SYST_CSR := CSR_Clk_Source or CSR_Enable;
160
161 Next_Tick_Time := Tick_Period;
162 Set_Alarm (Timer_Interval'Last);
163 Clear_Alarm_Interrupt;
164 Enable_Interrupts (Priority'Last);
165 end Initialize_Board;
166
167 ------------------------
168 -- Max_Timer_Interval --
169 ------------------------
170
171 function Max_Timer_Interval return Timer_Interval is (2**32 - 1);
172
173 ----------------
174 -- Read_Clock --
175 ----------------
176
177 function Read_Clock return Timer_Interval is
178 PRIMASK : Word;
179 Flag : Boolean;
180 Count : Timer_Interval;
181 Res : Timer_Interval;
182
183 begin
184 -- As several registers and variables need to be read or modified, do
185 -- it atomically.
186
187 Asm ("mrs %0, PRIMASK",
188 Outputs => Word'Asm_Output ("=&r", PRIMASK),
189 Volatile => True);
190 Asm ("msr PRIMASK, %0",
191 Inputs => Word'Asm_Input ("r", 1),
192 Volatile => True);
193
194 -- We must read the counter register before the flag
195
196 Count := Timer_Interval (SYST.SYST_CVR);
197
198 -- If we read the flag first, a reload can occur just after the read and
199 -- the count register would wrap around. We'd end up with a Count value
200 -- close to the Tick_Period value but a flag at zero and therefore miss
201 -- the reload and return a wrong clock value.
202
203 -- This flag is set when the counter has reached zero. Next_Tick_Time
204 -- has to be incremented. This will trigger an interrupt very soon (or
205 -- has just triggered the interrupt), so count is either zero or not far
206 -- from Tick_Period.
207
208 Flag := (SYST.SYST_CSR and CSR_Count_Flag) /= 0;
209
210 if Flag then
211
212 -- Systick counter has just reached zero, pretend it is still zero
213
214 -- This function is called by the interrupt handler that is executed
215 -- when the counter reaches zero. Therefore, we signal that the next
216 -- interrupt will happen within a period. Note that reading the
217 -- Control and Status register (SYST_CSR) clears the COUNTFLAG bit,
218 -- so even if we have sequential calls to this function, the
219 -- increment of Next_Tick_Time will happen only once.
220
221 Res := Next_Tick_Time;
222 Next_Tick_Time := Next_Tick_Time + Tick_Period;
223
224 else
225 -- The counter is decremented, so compute the actual time
226
227 Res := Next_Tick_Time - Count;
228 end if;
229
230 -- Restore interrupt mask
231
232 Asm ("msr PRIMASK, %0",
233 Inputs => Word'Asm_Input ("r", PRIMASK),
234 Volatile => True);
235
236 return Res;
237 end Read_Clock;
238
239 ---------------------------
240 -- Clear_Alarm_Interrupt --
241 ---------------------------
242
243 procedure Clear_Alarm_Interrupt is
244 begin
245 ICSR := ICSR_Pend_ST_Clr;
246 end Clear_Alarm_Interrupt;
247
248 --------------------------
249 -- Clear_Poke_Interrupt --
250 --------------------------
251
252 procedure Clear_Poke_Interrupt is
253 begin
254 null;
255 end Clear_Poke_Interrupt;
256
257 ---------------
258 -- Set_Alarm --
259 ---------------
260
261 procedure Set_Alarm (Ticks : Timer_Interval) is
262 Now : constant Timer_Interval := Read_Clock;
263
264 begin
265 -- As we will have periodic interrupts for alarms regardless, the only
266 -- thing to do is force an interrupt if the alarm has already expired.
267
268 Alarm_Time := Now + Timer_Interval'Min (Timer_Interval'Last / 2, Ticks);
269
270 if Ticks = 0 then
271 ICSR := ICSR_Pend_ST_Set;
272 end if;
273 end Set_Alarm;
274
275 ------------------------
276 -- Alarm_Interrupt_ID --
277 ------------------------
278
279 function Alarm_Interrupt_ID return Interrupt_ID is (1);
280 -- Return the interrupt level to use for the alarm clock handler. Note that
281 -- we use a "fake" Interrupt_ID for the alarm interrupt, as it is handled
282 -- specially (not through the NVIC).
283
284 -----------------------
285 -- Poke_Interrupt_ID --
286 -----------------------
287
288 function Poke_Interrupt_ID return Interrupt_ID is (No_Interrupt);
289
290 ---------------------------
291 -- Get_Interrupt_Request --
292 ---------------------------
293
294 function Get_Interrupt_Request
295 (Vector : Vector_Id) return Interrupt_ID
296 is
297 Res : Word;
298
299 begin
300 if Vector = Sys_Tick_Vector then
301 return Alarm_Interrupt_ID;
302 end if;
303
304 Asm ("mrs %0, ipsr",
305 Word'Asm_Output ("=r", Res),
306 Volatile => True);
307
308 Res := Res and 16#FF#;
309
310 -- The exception number is read from the IPSR, convert it to IRQ
311 -- number by substracting 16 (number of cpu exceptions) and then
312 -- convert it to GNAT interrupt_id by adding First_IRQ.
313
314 return Interrupt_ID'Base (Res) - 16 + First_IRQ;
315 end Get_Interrupt_Request;
316
317 ------------------------------
318 -- Enable_Interrupt_Request --
319 ------------------------------
320
321 procedure Enable_Interrupt_Request
322 (Interrupt : Interrupt_ID;
323 Prio : Interrupt_Priority)
324 is
325 begin
326 if Interrupt = Alarm_Interrupt_ID then
327
328 -- Consistency check with Priority_Of_Interrupt
329
330 pragma Assert (Prio = Interrupt_Priority'Last);
331
332 Clear_Alarm_Interrupt;
333 SYST.SYST_CSR := SYST.SYST_CSR or CSR_Tick_Int;
334
335 else
336 declare
337 pragma Assert (Interrupt >= First_IRQ);
338 IRQ : constant Natural := Interrupt - First_IRQ;
339 Regofs : constant Natural := IRQ / 32;
340 Regbit : constant Word := 2** (IRQ mod 32);
341 NVIC_ISER : array (0 .. 15) of Word
342 with Volatile, Address => NVIC_ISER0;
343
344 -- Many NVIC registers use 16 words of 32 bits each to serve as a
345 -- bitmap for all interrupt channels. Regofs indicates register
346 -- offset (0 .. 15), and Regbit indicates the mask required for
347 -- addressing the bit.
348
349 begin
350 NVIC_ISER (Regofs) := Regbit;
351 end;
352 end if;
353 end Enable_Interrupt_Request;
354
355 -------------------------------
356 -- Install_Interrupt_Handler --
357 -------------------------------
358
359 procedure Install_Interrupt_Handler
360 (Handler : Address;
361 Interrupt : Interrupts.Interrupt_ID;
362 Prio : Interrupt_Priority)
363 is
364 begin
365 if Interrupt = Alarm_Interrupt_ID then
366 Install_Trap_Handler (Handler, Sys_Tick_Vector);
367
368 else
369 IP (Interrupt - First_IRQ) := To_PRI (Prio);
370 Install_Trap_Handler (Handler, Interrupt_Request_Vector);
371 end if;
372
373 Enable_Interrupt_Request (Interrupt, Prio);
374 end Install_Interrupt_Handler;
375
376 ---------------------------
377 -- Priority_Of_Interrupt --
378 ---------------------------
379
380 function Priority_Of_Interrupt
381 (Interrupt : Interrupt_ID) return Any_Priority
382 is
383 -- Interrupt 2 .. 83 correspond to IRQ0 .. IRQ81
384
385 (if Interrupt = Alarm_Interrupt_ID then Interrupt_Priority'Last
386 else To_Priority (IP (Interrupt - First_IRQ)));
387
388 ----------------
389 -- Power_Down --
390 ----------------
391
392 procedure Power_Down is
393 begin
394 Asm ("wfi", Volatile => True);
395 end Power_Down;
396
397 -----------------------------
398 -- Clear_Interrupt_Request --
399 -----------------------------
400
401 procedure Clear_Interrupt_Request (Interrupt : Interrupts.Interrupt_ID)
402 is null;
403
404 --------------------------
405 -- Set_Current_Priority --
406 --------------------------
407
408 procedure Set_Current_Priority (Priority : Integer) is
409 begin
410 -- Writing a 0 to BASEPRI disables interrupt masking, while values
411 -- 15 .. 1 correspond to interrupt priorities 255 .. 241 in that order.
412
413 Asm ("msr BASEPRI, %0",
414 Inputs => PRI'Asm_Input ("r", To_PRI (Priority)),
415 Volatile => True);
416 end Set_Current_Priority;
417 end System.BB.Board_Support;