HiRTOS_e7372ec1/src/porting_layer/cpu_architectures/armv8r_aarch32/hirtos_cpu_arch_interface-tick_timer.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
224
--
--  Copyright (c) 2022-2023, German Rivera
--
--
--  SPDX-License-Identifier: Apache-2.0
--

--
--  @summary RTOS to target platform interface - Tick timer driver
--

with HiRTOS.Interrupt_Handling;
with HiRTOS_Cpu_Arch_Interface.Interrupt_Controller;
with HiRTOS_Cpu_Arch_Interface.Interrupts;
with System.Machine_Code;
with System.Storage_Elements;

package body HiRTOS_Cpu_Arch_Interface.Tick_Timer with SPARK_Mode => On is

   procedure Tick_Timer_Interrupt_Handler (Arg : System.Address)
      with Pre => Cpu_In_Privileged_Mode;

   procedure Initialize is
      CNTP_CTL_Value : CNTP_CTL_Type;
      CNTFRQ_Value : CNTFRQ_Type;
   begin
      CNTFRQ_Value := Get_CNTFRQ;
      pragma Assert (CNTFRQ_Value = CNTFRQ_Type (HiRTOS_Platform_Parameters.System_Clock_Frequency_Hz));

      --
      --  Make sure that the timer is disabled:
      --
      --  NOTE: Even if the ENABLE bit is 0, CNTPCT continues to count.
      --
      CNTP_CTL_Value := Get_CNTP_CTL;
      CNTP_CTL_Value.ENABLE := Timer_Disabled;
      CNTP_CTL_Value.IMASK := Timer_Interrupt_Masked;
      Set_CNTP_CTL (CNTP_CTL_Value);

      --
      --  NOTE: the generic timer interrupt is enabled in the GIC and in the
      --  peripheral when Start_Timer is called and disabled when Stop_Timer
      --  is called.
      --
   end Initialize;

   function Get_Timer_Timestamp_Cycles return Timer_Timestamp_Cycles_Type is
      CNTPCT_Value : constant CNTPCT_Type := Get_CNTPCT;
   begin
      return  Timer_Timestamp_Cycles_Type (CNTPCT_Value.Value);
   end Get_Timer_Timestamp_Cycles;

   procedure Start_Timer (Expiration_Time_Us : HiRTOS.Relative_Time_Us_Type) is
      use System.Storage_Elements;
      CNTP_CTL_Value : CNTP_CTL_Type;
      CNTP_TVAL_Value : constant CNTP_TVAL_Type :=
         CNTP_TVAL_Type (Expiration_Time_Us * Timer_Counter_Cycles_Per_Us);
      Timer_Interrupt_Id : constant
         HiRTOS_Cpu_Arch_Interface.Interrupt_Controller.Interrupt_Id_Type :=
         HiRTOS_Cpu_Arch_Interface.Interrupts.Generic_Physical_Timer_Interrupt_Id;
   begin
      pragma Assert (CNTP_TVAL_Value >= CNTP_TVAL_Type (Expiration_Time_Us));

      Set_CNTP_TVAL (CNTP_TVAL_Value);

      --
      --  Enable tick timer interrupt in the generic timer peipheral:
      --
      --  NOTE: Section G8.7.16 of ARMv8 Architecture Reference Manual says:
      --  "When the value of the ENABLE bit is 1, ISTATUS indicates whether
      --   the timer condition is met. ISTATUS takes no account of the value
      --   of the IMASK bit. If the value of ISTATUS is 1 and the value of
      --   IMASK is 0 then the timer interrupt is asserted.
      --   Setting the ENABLE bit to 0 disables the timer output signal,
      --   but the timer value accessible from CNTP_TVAL continues to
      --   count down. Disabling the output signal might be a
      --   power-saving option."
      --
      CNTP_CTL_Value := Get_CNTP_CTL;
      CNTP_CTL_Value.ENABLE := Timer_Enabled;
      CNTP_CTL_Value.IMASK := Timer_Interrupt_Not_Masked;
      Set_CNTP_CTL (CNTP_CTL_Value);

      --  Configure generic physical timer interrupt in the GIC:
      Interrupt_Controller.Configure_Internal_Interrupt (
         Internal_Interrupt_Id => Timer_Interrupt_Id,
         Priority => HiRTOS_Cpu_Arch_Interface.Interrupts.Interrupt_Priorities (Timer_Interrupt_Id),
         Cpu_Interrupt_Line => Interrupt_Controller.Cpu_Interrupt_Irq,
         Trigger_Mode => Interrupt_Controller.Interrupt_Level_Sensitive,
         Interrupt_Handler_Entry_Point => Tick_Timer_Interrupt_Handler'Access,
         Interrupt_Handler_Arg => To_Address (Integer_Address (CNTP_TVAL_Value)));

      --  Enable generic timer interrupt in the GIC:
      Interrupt_Controller.Enable_Internal_Interrupt (Timer_Interrupt_Id);
   end Start_Timer;

   procedure Stop_Timer is
      CNTP_CTL_Value : CNTP_CTL_Type;
   begin
      --  Disable generic timer interrupt in the GIC:
      Interrupt_Controller.Disable_Internal_Interrupt (
         HiRTOS_Cpu_Arch_Interface.Interrupts.Generic_Physical_Timer_Interrupt_Id);

      --  Disable tick timer interrupt in the generic timer peipheral:
      CNTP_CTL_Value := Get_CNTP_CTL;
      CNTP_CTL_Value.ENABLE := Timer_Disabled;
      CNTP_CTL_Value.IMASK := Timer_Interrupt_Masked;
      Set_CNTP_CTL (CNTP_CTL_Value);
   end Stop_Timer;

   procedure Save_Timer_Context (Timer_Context : out Timer_Context_Type) is
   begin
      Timer_Context.CNTP_CTL_Value := Get_CNTP_CTL;
      Timer_Context.CNTP_TVAL_Value := Get_CNTP_TVAL;
   end Save_Timer_Context;

   procedure Restore_Timer_Context (Timer_Context : Timer_Context_Type) is
   begin
      Set_CNTP_CTL (Timer_Context.CNTP_CTL_Value);
      Set_CNTP_TVAL (Timer_Context.CNTP_TVAL_Value);
   end Restore_Timer_Context;

   ----------------------------------------------------------------------------
   --  Private Subprograms
   ----------------------------------------------------------------------------

   procedure Tick_Timer_Interrupt_Handler (Arg : System.Address)
   is
      use System.Storage_Elements;
      CNTP_TVAL_Value : constant CNTP_TVAL_Type := CNTP_TVAL_Type (To_Integer (Arg));
   begin
      --
      --  NOTE: Setting CNTP_TVAL here serves two purposes:
      --  - Clear the timer interrupt at the timer peripheral
      --  - Set the timer to fire for the next tick
      --
      Set_CNTP_TVAL (CNTP_TVAL_Value);
      HiRTOS.Interrupt_Handling.RTOS_Tick_Timer_Interrupt_Handler;
   end Tick_Timer_Interrupt_Handler;

   function Get_CNTFRQ return CNTFRQ_Type is
      CNTFRQ_Value : CNTFRQ_Type;
   begin
      System.Machine_Code.Asm (
         "mrc p15, 0, %0, c14, c0, 0",
         Outputs => CNTFRQ_Type'Asm_Output ("=r", CNTFRQ_Value), --  %0
         Volatile => True);

      return CNTFRQ_Value;
   end Get_CNTFRQ;

   procedure Set_CNTFRQ (CNTFRQ_Value : CNTFRQ_Type) is
   begin
      System.Machine_Code.Asm (
         "mcr p15, 0, %0, c14, c0, 0",
         Inputs => CNTFRQ_Type'Asm_Input ("r", CNTFRQ_Value), --  %0
         Volatile => True);
   end Set_CNTFRQ;

   function Get_CNTP_CTL return CNTP_CTL_Type is
      CNTP_CTL_Value : CNTP_CTL_Type;
   begin
      System.Machine_Code.Asm (
         "mrc p15, 0, %0, c14, c2, 1",
         Outputs => CNTP_CTL_Type'Asm_Output ("=r", CNTP_CTL_Value), --  %0
         Volatile => True);

      return CNTP_CTL_Value;
   end Get_CNTP_CTL;

   procedure Set_CNTP_CTL (CNTP_CTL_Value : CNTP_CTL_Type) is
   begin
      System.Machine_Code.Asm (
         "mcr p15, 0, %0, c14, c2, 1",
         Inputs => CNTP_CTL_Type'Asm_Input ("r", CNTP_CTL_Value), --  %0
         Volatile => True);
   end Set_CNTP_CTL;

   function Get_CNTP_TVAL return CNTP_TVAL_Type is
      CNTP_TVAL_Value : CNTP_TVAL_Type;
   begin
      System.Machine_Code.Asm (
         "mrc p15, 0, %0, c14, c2, 0",
         Outputs => CNTP_TVAL_Type'Asm_Output ("=r", CNTP_TVAL_Value), --  %0
         Volatile => True);

      return CNTP_TVAL_Value;
   end Get_CNTP_TVAL;

   procedure Set_CNTP_TVAL (CNTP_TVAL_Value : CNTP_TVAL_Type) is
   begin
      System.Machine_Code.Asm (
         "mcr p15, 0, %0, c14, c2, 0",
         Inputs => CNTP_TVAL_Type'Asm_Input ("r", CNTP_TVAL_Value), --  %0
         Volatile => True);
   end Set_CNTP_TVAL;

   function Get_CNTPCT return CNTPCT_Type is
      CNTPCT_Value : CNTPCT_Type;
   begin
      --  NOTE: Use "=&r" to ensure different registers is used
      System.Machine_Code.Asm (
         "mrrc p15, 0, %0, %1, c14",
         Outputs => [Interfaces.Unsigned_32'Asm_Output ("=r", CNTPCT_Value.Low_Word),    --  %0
                     Interfaces.Unsigned_32'Asm_Output ("=&r", CNTPCT_Value.High_Word)], --  %1
         Volatile => True);

      return CNTPCT_Value;
   end Get_CNTPCT;

   function Get_CNTPCTSS return CNTPCT_Type is
      CNTPCT_Value : CNTPCT_Type;
   begin
      --  NOTE: Use "=&r" to ensure different registers is used
      System.Machine_Code.Asm (
         "mrrc p15, 8, %0, %1, c14",
         Outputs => [Interfaces.Unsigned_32'Asm_Output ("=r", CNTPCT_Value.Low_Word),    --  %0
                     Interfaces.Unsigned_32'Asm_Output ("=&r", CNTPCT_Value.High_Word)], --  %1
         Volatile => True);

      return CNTPCT_Value;
   end Get_CNTPCTSS;

end HiRTOS_Cpu_Arch_Interface.Tick_Timer;