hirtos_1.0.0_e7372ec1/src/hirtos-interrupt_handling.adb

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
--
--  Copyright (c) 2022-2023, German Rivera
--
--
--  SPDX-License-Identifier: Apache-2.0
--

with HiRTOS.RTOS_Private;
with HiRTOS.Timer_Private;
with HiRTOS.Interrupt_Handling_Private;
with HiRTOS.Thread_Private;
with HiRTOS_Cpu_Arch_Interface.Thread_Context;
with HiRTOS_Cpu_Multi_Core_Interface;
with HiRTOS_Cpu_Arch_Interface;
with HiRTOS_Low_Level_Debug_Interface;
with GNAT.Source_Info;
with System.Storage_Elements;

package body HiRTOS.Interrupt_Handling is
   use HiRTOS.RTOS_Private;
   use HiRTOS.Thread_Private;
   use HiRTOS.Interrupt_Handling_Private;
   use HiRTOS_Cpu_Multi_Core_Interface;

   Debug_Tracing_On : constant Boolean := False;

   function Enter_Interrupt_Context (Stack_Pointer : System.Address) return System.Address is
      RTOS_Cpu_Instance : HiRTOS_Cpu_Instance_Type renames
         HiRTOS_Obj.RTOS_Cpu_Instances (Get_Cpu_Id);
      Current_Thread_Id : constant Thread_Id_Type := RTOS_Cpu_Instance.Current_Thread_Id;
      New_Stack_Pointer : System.Address;
      Old_Cpu_Interrupting : HiRTOS_Cpu_Arch_Interface.Cpu_Register_Type;
   begin
      Old_Cpu_Interrupting := HiRTOS_Cpu_Arch_Interface.Disable_Cpu_Interrupting;

      HiRTOS.Interrupt_Handling_Private.Increment_Interrupt_Nesting (
         RTOS_Cpu_Instance.Interrupt_Nesting_Level_Stack, Stack_Pointer);

      if Debug_Tracing_On then
         HiRTOS_Low_Level_Debug_Interface.Print_String (
           "*** enter < " & GNAT.Source_Info.Source_Location & ASCII.LF);
         HiRTOS_Low_Level_Debug_Interface.Print_String ("*** enter interrupt nesting level: ");
         HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (Interfaces.Unsigned_32 (
           HiRTOS.Interrupt_Handling_Private.Get_Current_Interrupt_Nesting_Counter (
             RTOS_Cpu_Instance.Interrupt_Nesting_Level_Stack)));
         HiRTOS_Low_Level_Debug_Interface.Print_String (", old stack pointer: ");
         HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
           Interfaces.Unsigned_32 (System.Storage_Elements.To_Integer (Stack_Pointer)),
           End_Line => True);
      end if;

      if Get_Current_Interrupt_Nesting_Counter (RTOS_Cpu_Instance.Interrupt_Nesting_Level_Stack) = 1
      then
         if Debug_Tracing_On then
            HiRTOS_Low_Level_Debug_Interface.Print_String ("*** enter thread_id: ");
            HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
            Interfaces.Unsigned_32 (Current_Thread_Id), End_Line => True);
         end if;

         --
         --  Interrupted context must be a thread
         --
         pragma Assert (Current_Thread_Id /= Invalid_Thread_Id);

         declare
            Current_Thread_Obj : Thread_Type renames
               RTOS_Cpu_Instance.Thread_Instances (Current_Thread_Id);
         begin
            Current_Thread_Obj.Stats.Times_Preempted_By_Isr := @ + 1;

            --
            --  Save current thread's stack pointer in current RTOS task context:
            --
            --  The current thread's stack pointer points to the thread's CPU
            --  context saved on the thread's stack.
            --

            if Debug_Tracing_On then
               if not Valid_Thread_Stack_Pointer (Current_Thread_Obj, Stack_Pointer) then
                  HiRTOS_Low_Level_Debug_Interface.Print_String ("*** Invalid stack pointer: ");
                  HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
                     Interfaces.Unsigned_32 (System.Storage_Elements.To_Integer (Stack_Pointer)),
                     End_Line => True);
               end if;
            end if;

            HiRTOS.Thread_Private.Save_Thread_Stack_Pointer (Current_Thread_Obj, Stack_Pointer);
         end;

         --
         --  Set new sp to the bottom of the ISR stack:
         --
         New_Stack_Pointer := RTOS_Cpu_Instance.Interrupt_Stack_End_Address;
      else
         New_Stack_Pointer := Stack_Pointer;
      end if;

      if Debug_Tracing_On then
         HiRTOS_Low_Level_Debug_Interface.Print_String (
            "*** enter > " & GNAT.Source_Info.Source_Location & ASCII.LF);
         HiRTOS_Low_Level_Debug_Interface.Print_String ("*** enter new stack pointer: ");
         HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
            Interfaces.Unsigned_32 (System.Storage_Elements.To_Integer (New_Stack_Pointer)),
            End_Line => True);
      end if;

      HiRTOS_Cpu_Arch_Interface.Restore_Cpu_Interrupting (Old_Cpu_Interrupting);
      return New_Stack_Pointer;
   end Enter_Interrupt_Context;

   function Exit_Interrupt_Context (Stack_Pointer : System.Address) return System.Address is
      RTOS_Cpu_Instance : HiRTOS_Cpu_Instance_Type renames
         HiRTOS_Obj.RTOS_Cpu_Instances (Get_Cpu_Id);
      Current_Interrupt_Nesting_Counter : constant Interrupt_Nesting_Counter_Type :=
         Get_Current_Interrupt_Nesting_Counter (RTOS_Cpu_Instance.Interrupt_Nesting_Level_Stack);
      New_Stack_Pointer : System.Address;
      Old_Cpu_Interrupting : HiRTOS_Cpu_Arch_Interface.Cpu_Register_Type;
   begin
      Old_Cpu_Interrupting := HiRTOS_Cpu_Arch_Interface.Disable_Cpu_Interrupting;

      if Debug_Tracing_On then
         HiRTOS_Low_Level_Debug_Interface.Print_String (
            "*** exit < " & GNAT.Source_Info.Source_Location & ASCII.LF);
         HiRTOS_Low_Level_Debug_Interface.Print_String ("*** exit interrupt nesting level: ");
         HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
            Interfaces.Unsigned_32 (Current_Interrupt_Nesting_Counter));
         HiRTOS_Low_Level_Debug_Interface.Print_String (", old stack pointer: ");
         HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
            Interfaces.Unsigned_32 (System.Storage_Elements.To_Integer (Stack_Pointer)),
            End_Line => True);
      end if;

      --
      --  If interrupt nesting level is 1, run the thread scheduler
      --  in case the highest priority runnable thread has changed:
      --
      if Current_Interrupt_Nesting_Counter = 1 then
         declare
            Old_Current_Thread_Id : constant Thread_Id_Type :=
                  RTOS_Cpu_Instance.Current_Thread_Id;
         begin
            HiRTOS.Thread_Private.Run_Thread_Scheduler;
            declare
               New_Current_Thread_Id : constant Valid_Thread_Id_Type :=
                     RTOS_Cpu_Instance.Current_Thread_Id;
               New_Current_Thread_Obj : Thread_Type renames
                  RTOS_Cpu_Instance.Thread_Instances (New_Current_Thread_Id);
            begin
               if New_Current_Thread_Id /= Old_Current_Thread_Id then
                  if Old_Current_Thread_Id /= Invalid_Thread_Id then
                     declare
                        Old_Current_Thread_Obj : Thread_Type renames
                           RTOS_Cpu_Instance.Thread_Instances (Old_Current_Thread_Id);
                     begin
                        Save_Thread_Extended_Context (Old_Current_Thread_Obj);
                     end;
                  end if;

                  Restore_Thread_Extended_Context (New_Current_Thread_Obj);
               end if;

               HiRTOS.Interrupt_Handling_Private.Decrement_Interrupt_Nesting (
                  RTOS_Cpu_Instance.Interrupt_Nesting_Level_Stack);

               --
               --  Restore saved stack pointer from the current RTOS task context:
               --
               New_Stack_Pointer := Thread_Private.Get_Thread_Stack_Pointer (New_Current_Thread_Obj);

               if Debug_Tracing_On then
                  HiRTOS_Low_Level_Debug_Interface.Print_String ("*** exit thread_id: ");
                  HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
                     Interfaces.Unsigned_32 (New_Current_Thread_Id), End_Line => True);
               end if;
            end;
         end;
      else
         pragma Assert (Current_Interrupt_Nesting_Counter > 1);
         HiRTOS.Interrupt_Handling_Private.Decrement_Interrupt_Nesting (
            RTOS_Cpu_Instance.Interrupt_Nesting_Level_Stack);
         New_Stack_Pointer := Stack_Pointer;
      end if;

      if Debug_Tracing_On then
         HiRTOS_Low_Level_Debug_Interface.Print_String (
            "*** exit > " & GNAT.Source_Info.Source_Location & ASCII.LF);
         HiRTOS_Low_Level_Debug_Interface.Print_String ("*** exit new stack pointer: ");
         HiRTOS_Low_Level_Debug_Interface.Print_Number_Hexadecimal (
            Interfaces.Unsigned_32 (System.Storage_Elements.To_Integer (New_Stack_Pointer)),
            End_Line => True);
      end if;

      HiRTOS_Cpu_Arch_Interface.Restore_Cpu_Interrupting (Old_Cpu_Interrupting);
      return New_Stack_Pointer;
   end Exit_Interrupt_Context;

   procedure RTOS_Tick_Timer_Interrupt_Handler is
      RTOS_Cpu_Instance : HiRTOS_Cpu_Instance_Type renames
         HiRTOS_Obj.RTOS_Cpu_Instances (Get_Cpu_Id);
      Current_Thread_Id : constant Valid_Thread_Id_Type :=
               RTOS_Cpu_Instance.Current_Thread_Id;
      Current_Thread_Obj : Thread_Type renames
         RTOS_Cpu_Instance.Thread_Instances (Current_Thread_Id);
   begin
      pragma Assert (Current_Thread_Obj.Time_Slice_Left_Us >= HiRTOS_Config_Parameters.Tick_Timer_Period_Us);
      RTOS_Cpu_Instance.Timer_Ticks_Since_Boot := @ + 1;
      Current_Thread_Obj.Time_Slice_Left_Us := @ - HiRTOS_Config_Parameters.Tick_Timer_Period_Us;
      HiRTOS.Timer_Private.Do_Software_Timers_Bookkeeping;
   end RTOS_Tick_Timer_Interrupt_Handler;

   function Get_Interrupted_PC return System.Address
   is
      RTOS_Cpu_Instance : HiRTOS_Cpu_Instance_Type renames
         HiRTOS_Obj.RTOS_Cpu_Instances (Get_Cpu_Id);
      Saved_Stack_Pointer : constant System.Address :=
         Get_Current_Interrupt_Nesting_Saved_Stack_Pointer (RTOS_Cpu_Instance.Interrupt_Nesting_Level_Stack);
      Cpu_Context : constant HiRTOS_Cpu_Arch_Interface.Thread_Context.Cpu_Context_Type with
         Import, Address => Saved_Stack_Pointer;
   begin
      return HiRTOS_Cpu_Arch_Interface.Thread_Context.Get_Saved_PC (Cpu_Context);
   end Get_Interrupted_PC;

end HiRTOS.Interrupt_Handling;