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;