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;