File : s-bbbosu-tms570.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-2006 The European Space Agency --
11 -- Copyright (C) 2003-2016, AdaCore --
12 -- --
13 -- GNARL 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. GNARL 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 -- GNARL was developed by the GNARL team at Florida State 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.BB.Parameters;
38
39 package body System.BB.Board_Support is
40 use CPU_Primitives, Interrupts;
41
42 -------------------------------
43 -- Real-Time Interrupt (RTI) --
44 -------------------------------
45
46 RTI_Base : constant Address := 16#FFFF_FC00#;
47
48 RTIGCTRL : constant Address := RTI_Base + 16#00#;
49 RTICOMPCTRL : constant Address := RTI_Base + 16#0C#;
50
51 RTIFRC1 : constant Address := RTI_Base + 16#30#;
52 RTIUC1 : constant Address := RTI_Base + 16#34#;
53 RTICPUC1 : constant Address := RTI_Base + 16#38#;
54
55 RTICOMP3 : constant Address := RTI_Base + 16#68#;
56
57 RTISETINTENA : constant Address := RTI_Base + 16#80#;
58 RTIINTFLAG : constant Address := RTI_Base + 16#88#;
59
60 RTI_Compare_Interrupt_3 : constant Interrupts.Interrupt_ID := 5;
61 -- We use the compare unit 3, so the first counter and the first three
62 -- compare units are available for use by the user.
63
64 -------------------------
65 -- Software Interrupts --
66 -------------------------
67
68 -- As result of using a single counter and comparator for alarms, it is
69 -- possible to miss the value to compare against, so the alarm interrupt
70 -- will not be given. For this reason, it is necessary to use a software
71 -- generated interrupt.
72
73 -- The TMS570 has 4 registers, SSIR1 to SSIR4, for generating software
74 -- interrupts. Reading the SSIVEC register yields the highest active
75 -- software interrupt, with its associated data byte, and clears it.
76 -- In order to also allow user handlers for this interrupt and to avoid
77 -- accidentally clearing those, the runtime uses the SSIR1 register. This
78 -- way, the SSIF register tells reliably if the request is ours or not.
79
80 -- The data byte is interpreted as Interrupt_ID to handle. User programs
81 -- can generate arbitrary interrupts by writing the value SSIR1_Key + X,
82 -- where requested X is the Interrupt_ID. The software interrupt is
83 -- automatically cleared.
84
85 SSIR1 : Word;
86 pragma Volatile (SSIR1);
87 for SSIR1'Address use 16#FFFF_FFB0#;
88 -- Register to generate software interrupts
89
90 SSIR1_Key : constant Word := 16#7500#;
91 -- Value to write to SSIR1 to trigger an interrupt
92
93 SSIF : Word;
94 pragma Volatile (SSIF);
95 for SSIF'Address use 16#FFFF_FFF8#;
96 -- Software interrupt flag register. The bits 0 .. 3 are used for
97 -- SSIR1 .. SSIR4 respectively.
98
99 SSIFLAG1 : constant Word := 2**0;
100 -- Bit in the SSIF register corresponding to SSIR4
101
102 SSIVEC : Word;
103 pragma Volatile (SSIVEC);
104 for SSIVEC'Address use 16#FFFF_FFF4#;
105 -- Reading returns 256 * D + N, where N is the number of the SSIR register
106 -- causing the interrupt (1 .. 4) and D is the data byte used. Returns zero
107 -- if no software interrupt is pending.
108
109 Software_Interrupt : constant Interrupts.Interrupt_ID := 21;
110 -- Interrupt ID for software interrupts
111
112 procedure Generate_Software_Interrupt (ID : Interrupt_ID);
113 -- Generate an interrupt request corresponding to the given ID
114
115 function Get_Software_Interrupt return Interrupt_ID;
116 -- Return the Interrupt_ID of the pending SSIR1 software interrupt,
117 -- or 0 when no such software interrupt is pending.
118
119 --------------------------------------
120 -- Vectored Interrupt Manager (VIM) --
121 --------------------------------------
122
123 VIM_Base : constant Address := 16#FFFF_FE00#;
124
125 IRQINDEX : constant Address := VIM_Base + 16#00#;
126 FIQINDEX : constant Address := VIM_Base + 16#04#;
127
128 -- The addresses below refer to the first word of a three-word bitmap
129 -- with one bit per interrupt channel.
130
131 FIRQPR0 : constant Address := VIM_Base + 16#10#;
132 -- Register with one bit set for each interrupt that is an FIQ interrupt.
133 -- Interrupt sources 32 and over are not supported as FIQ by this run time.
134 -- These interrupts should be remapped to lower interrupt channels when
135 -- required as FIQ. The FIQ_Ints must always include the bits set by
136 -- NMI_Ints, as the hardware cannot mask these. Initialize_Boards will
137 -- update this register
138
139 REQENASET0 : constant Address := VIM_Base + 16#30#;
140 -- Writing a bit mask to this register enables the corresponding interrupts
141
142 REQENACLR0 : constant Address := VIM_Base + 16#40#;
143 REQENACLR1 : constant Address := VIM_Base + 16#44#;
144 REQENACLR2 : constant Address := VIM_Base + 16#48#;
145 -- Writing a bit mask to this register clears the corresponding interrupts
146
147 WAKEENASET0 : constant Address := VIM_Base + 16#50#;
148 -- Bit mask allowing corresponding interrupts to wake the processor
149
150 procedure Enable_Interrupt_Request (Interrupt : Interrupt_ID);
151 -- Enable interrupt requests for the given interrupt
152
153 function Index_To_Interrupt (Index : Word) return Interrupt_ID is
154 (case Index is
155 when 0 => 0,
156 when 1 => Interrupt_ID (1),
157 when Word (Software_Interrupt + 1) => Get_Software_Interrupt,
158 when others => Interrupt_ID (Index - 1));
159 -- The IRQINDEX and FIQINDEX registers return the index into a vector table
160 -- that starts with a dummy "phantom" entry, so the VIM Interrupt Channel
161 -- is generally 1 less. While we the mapping from channel to Interrupt_ID
162 -- is generally direct, we map Channel 0 to Interrupt_ID (1), to avoid
163 -- confusion with No_Interrupt. This works out fine since VIM channel 1
164 -- is reserved.
165
166 ------------------------
167 -- Interrupt_Handlers --
168 ------------------------
169
170 type Interrupt_Vector_Table is array (Interrupts.Interrupt_ID) of Address;
171 Interrupt_Vectors : Interrupt_Vector_Table;
172 pragma Volatile (Interrupt_Vectors);
173 for Interrupt_Vectors'Address use 16#FFF8_2004#;
174
175 FIQ_Prio : constant Interrupt_Priority := Interrupt_Priority'Last;
176 IRQ_Prio : constant Interrupt_Priority := Interrupt_Priority'First;
177 pragma Assert (FIQ_Prio = IRQ_Prio + 1);
178
179 NMI_Ints : constant Word := 3;
180 -- Bitmap of unmaskable interrupts, namely interrupt channel 0 and 1
181
182 FIQ_Masked : Boolean := False;
183 -- Reflects wether FIQ interrupts are masked in the VIM or not
184
185 procedure IRQ_Handler;
186 pragma Import (Asm, IRQ_Handler, "__gnat_irq_trap");
187
188 procedure FIQ_Handler;
189 pragma Import (Asm, FIQ_Handler, "__gnat_fiq_trap");
190
191 -- Local utility functions
192
193 function Shift_Left (W : Word; Amount : Natural) return Word
194 with Import, Convention => Intrinsic;
195 -- Efficiently compute W / 2**Amount
196
197 function Read (Addr : Address) return Word with Inline;
198 procedure Write (Addr : Address; Val : Word) with Inline;
199 -- General functions to read/write from/to specific memory locations
200
201 ----------------------------
202 -- Get_Software_Interrupt --
203 ----------------------------
204
205 function Get_Software_Interrupt return Interrupt_ID is
206 (if (SSIF and SSIFLAG1) = 0 then 0 else Interrupt_ID (SSIVEC / 256));
207 -- Return the interrupt for the handler to call. This value is written
208 -- as data byte to the SSIR1 register, and must be a valid Interrupt_ID.
209
210 ----------
211 -- Read --
212 ----------
213
214 function Read (Addr : Address) return Word is
215 R : Word;
216 for R'Address use Addr;
217 pragma Volatile (R);
218 begin
219 return R;
220 end Read;
221
222 -----------
223 -- Write --
224 -----------
225
226 procedure Write (Addr : Address; Val : Word) is
227 R : Word;
228 for R'Address use Addr;
229 pragma Volatile (R);
230 begin
231 R := Val;
232 end Write;
233
234 ---------------------------------
235 -- Generate_Software_Interrupt --
236 ---------------------------------
237
238 procedure Generate_Software_Interrupt (ID : Interrupt_ID) is
239 begin
240 SSIR1 := SSIR1_Key + Word (ID);
241 end Generate_Software_Interrupt;
242
243 ----------------------
244 -- Initialize_Board --
245 ----------------------
246
247 procedure Initialize_Board is
248 begin
249 -- Disable all interrupts, except for NMIs
250
251 Write (REQENACLR0, not NMI_Ints);
252 Write (REQENACLR1, not 0);
253 Write (REQENACLR2, not 0);
254
255 -- Initialize timer
256
257 -- The counter needs to be disabled while programming it
258
259 Write (RTIGCTRL, Read (RTIGCTRL) and not 2); -- Turn off timer/counter 1
260 Write (RTICPUC1, Parameters.Prescaler - 1); -- Program prescaler compare
261 Write (RTIUC1, 0); -- Start prescaler at 0
262 Write (RTIFRC1, 0); -- Start clock at 0
263 Write (RTICOMPCTRL, 2**12);
264 Write (RTIINTFLAG, 2**3);
265 Write (RTIGCTRL, Read (RTIGCTRL) or 2); -- Turn timer/counter 1 on
266 Write (RTISETINTENA, 2**3); -- Enable Interrupts
267
268 -- Allow the generation of software interrupts
269
270 Interrupt_Vectors (Software_Interrupt) := IRQ_Handler'Address;
271 Enable_Interrupt_Request (Software_Interrupt);
272 end Initialize_Board;
273
274 ------------------------
275 -- Max_Timer_Interval --
276 ------------------------
277
278 function Max_Timer_Interval return Timer_Interval is (2**32 - 1);
279
280 ---------------
281 -- Set_Alarm --
282 ---------------
283
284 procedure Set_Alarm (Ticks : Timer_Interval) is
285 Now : constant Timer_Interval := Read_Clock;
286 Alarm : constant Timer_Interval := Now + Ticks;
287 Elapsed : Timer_Interval;
288
289 begin
290 Write (RTIINTFLAG, 2**3); -- Clear any pending alarms
291 Write (RTICOMP3, Word (Alarm));
292 Elapsed := Read_Clock - Now;
293
294 if Elapsed >= Ticks and then (Read (RTIINTFLAG) and 2**3) = 0 then
295 Generate_Software_Interrupt (RTI_Compare_Interrupt_3);
296 end if;
297 end Set_Alarm;
298
299 ----------------
300 -- Read_Clock --
301 ----------------
302
303 function Read_Clock return Timer_Interval is
304 (Timer_Interval (Read (RTIFRC1)));
305
306 ------------------------
307 -- Alarm_Interrupt_ID --
308 ------------------------
309
310 function Alarm_Interrupt_ID return Interrupts.Interrupt_ID is
311 (RTI_Compare_Interrupt_3);
312
313 ---------------------------
314 -- Clear_Alarm_Interrupt --
315 ---------------------------
316
317 procedure Clear_Alarm_Interrupt is
318 begin
319 Write (RTIINTFLAG, 2**3);
320 end Clear_Alarm_Interrupt;
321
322 -----------------------------
323 -- Clear_Interrupt_Request --
324 -----------------------------
325
326 procedure Clear_Interrupt_Request (Interrupt : Interrupts.Interrupt_ID) is
327 begin
328 null;
329 end Clear_Interrupt_Request;
330
331 ---------------------------
332 -- Get_Interrupt_Request --
333 ---------------------------
334
335 function Get_Interrupt_Request
336 (Vector : CPU_Primitives.Vector_Id)
337 return System.BB.Interrupts.Interrupt_ID
338 is
339 (Index_To_Interrupt (case Vector is
340 when 5 => Read (IRQINDEX),
341 when 6 => Read (FIQINDEX),
342 when others => 0));
343
344 procedure Enable_Interrupt_Request (Interrupt : Interrupt_ID) is
345 Regofs : constant Address := Address (Interrupt) / 32 * 4;
346 Regbit : constant Word := Shift_Left (1, Interrupt mod 32);
347 -- Many VIM registers use 3 words of 32 bits each to serve as a bitmap
348 -- for all interrupt channels. Regofs indicates register offset (0..2),
349 -- and Regbit indicates the mask required for addressing the bit.
350
351 begin
352 Write (REQENASET0 + Regofs, Regbit);
353 Write (WAKEENASET0 + Regofs, Regbit);
354 end Enable_Interrupt_Request;
355
356 -------------------------------
357 -- Install_Interrupt_Handler --
358 -------------------------------
359
360 procedure Install_Interrupt_Handler
361 (Handler : Address;
362 Interrupt : Interrupts.Interrupt_ID;
363 Prio : Interrupt_Priority)
364 is
365 pragma Unreferenced (Prio);
366 Hw_Prio : constant Any_Priority := Priority_Of_Interrupt (Interrupt);
367
368 begin
369 -- While we could directly have installed fixed IRQ and FIQ handlers,
370 -- this would have required that all IRQ and FIQ handlers go through
371 -- the Ravenscar run time, which is a bit of a limitation. By using the
372 -- vector capability of the interrupt handler, it is possible to handle
373 -- some interrupts directly for best performance.
374
375 case Interrupt_Priority (Hw_Prio) is
376 when IRQ_Prio =>
377 Install_Trap_Handler (Handler, 5);
378 Interrupt_Vectors (Interrupt) := IRQ_Handler'Address;
379
380 when FIQ_Prio =>
381 Install_Trap_Handler (Handler, 6);
382 Interrupt_Vectors (Interrupt) := FIQ_Handler'Address;
383 end case;
384
385 Enable_Interrupt_Request (Interrupt);
386 end Install_Interrupt_Handler;
387
388 -----------------------
389 -- Poke_Interrupt_ID --
390 -----------------------
391
392 function Poke_Interrupt_ID return Interrupts.Interrupt_ID is
393 (Interrupts.No_Interrupt);
394
395 --------------------------
396 -- Clear_Poke_Interrupt --
397 --------------------------
398
399 procedure Clear_Poke_Interrupt is
400 begin
401 null;
402 end Clear_Poke_Interrupt;
403
404 ---------------------------
405 -- Priority_Of_Interrupt --
406 ---------------------------
407
408 function Priority_Of_Interrupt
409 (Interrupt : System.BB.Interrupts.Interrupt_ID)
410 return System.Any_Priority
411 is
412 (if Interrupt <= 31 and then (Read (FIRQPR0) and 2**Interrupt) /= 0
413 then FIQ_Prio
414 else IRQ_Prio);
415
416 --------------------------
417 -- Set_Current_Priority --
418 --------------------------
419
420 procedure Set_Current_Priority (Priority : Integer) is
421 begin
422 -- On the TMS570, FIQs cannot be masked by the processor. So, we need to
423 -- disable them at the controller when required.
424
425 if (Priority = FIQ_Prio) xor FIQ_Masked then
426 if Priority = FIQ_Prio then
427 Write (REQENACLR0, Read (FIRQPR0));
428 FIQ_Masked := True;
429
430 else
431 Write (REQENASET0, Read (FIRQPR0));
432 FIQ_Masked := False;
433 end if;
434 end if;
435 end Set_Current_Priority;
436
437 ----------------
438 -- Power_Down --
439 ----------------
440
441 procedure Power_Down is
442 begin
443 null;
444 end Power_Down;
445
446 end System.BB.Board_Support;