Permalink
Browse files

Use interrupt driven real time clock

  • Loading branch information...
eugmes committed Oct 4, 2011
1 parent 88803f4 commit 82e11861789dc1aed1a79b9706365dcb368e2e6d
Showing with 376 additions and 41 deletions.
  1. +0 −1 gnat.adc
  2. +4 −1 lm3s3749.ld
  3. +145 −0 mcu-core-nvic-operations.adb
  4. +42 −0 mcu-core-nvic-operations.ads
  5. +62 −0 mcu-core-nvic.ads
  6. +22 −2 mcu-utils.adb
  7. +12 −0 mcu-utils.ads
  8. +65 −0 real_time.adb
  9. +9 −0 real_time.ads
  10. +15 −37 systick_test.adb
View
@@ -6,6 +6,5 @@ pragma Restrictions (No_Floating_Point,
No_Default_Stream_Attributes,
No_Dispatch,
No_IO,
- No_Exceptions,
No_Secondary_Stack,
No_Standard_Storage_Pools);
View
@@ -30,12 +30,15 @@ SECTIONS
_flash_edata = _flash_sdata + (_edata - _sdata);
_sbss = .;
- .bss ALIGN(8) : {
+ .bss ALIGN(8) (NOLOAD) : {
*(.bss) *(.bss.*)
. = ALIGN(4);
} > ram AT> ram
_ebss = .;
+ .stack ALIGN(8) (NOLOAD) : { *(.stack) *(.stack.*) } > ram AT> ram
+
+ /* TODO explicitly allocate environment task stack */
_stack_top = ORIGIN(ram) + LENGTH(ram);
/* DWARF 1 */
@@ -0,0 +1,145 @@
+---------------------------------------------------------------------------
+-- Copyright © 2011 Євгеній Мещеряков <eugen@debian.org> --
+-- --
+-- This program is free software: you can redistribute it and/or modify --
+-- it under the terms of the GNU General Public License as published by --
+-- the Free Software Foundation, either version 3 of the License, or --
+-- (at your option) any later version. --
+-- --
+-- This program is distributed in the hope that it will be useful, --
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of --
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --
+-- GNU General Public License for more details. --
+-- --
+-- You should have received a copy of the GNU General Public License --
+-- along with this program. If not, see <http://www.gnu.org/licenses/>. --
+---------------------------------------------------------------------------
+
+with System.Storage_Elements;
+with Interfaces;
+with Ada.Unchecked_Conversion;
+use type System.Storage_Elements.Integer_Address;
+use type Interfaces.Unsigned_8;
+
+package body MCU.Core.NVIC.Operations is
+ package SSE renames System.Storage_Elements;
+
+ subtype Byte is Interfaces.Unsigned_8;
+
+ function Priority_To_Byte is new Ada.Unchecked_Conversion (Interrupt_Priority_Byte, Byte);
+ function Byte_To_Priority is new Ada.Unchecked_Conversion (Byte, Interrupt_Priority_Byte);
+
+ function Interrupt_Byte_Offset (Item : Interrupt) return SSE.Integer_Address is
+ begin
+ return SSE.Integer_Address (Item) / 8;
+ end Interrupt_Byte_Offset;
+ pragma Inline (Interrupt_Byte_Offset);
+
+ function Interrupt_Bit_Mask (Item : Interrupt) return Byte is
+ begin
+ return Interfaces.Shift_Left (1, Integer (Item) mod 8);
+ end Interrupt_Bit_Mask;
+ pragma Inline (Interrupt_Bit_Mask);
+
+ procedure Write_Register (Value : Byte; Offset : SSE.Integer_Address) is
+ Register : Byte;
+ for Register'Address use System'To_Address (Core_Peripherals_Base + Offset);
+ pragma Atomic (Register);
+ pragma Import (Ada, Register);
+ begin
+ Register := Value;
+ end Write_Register;
+ pragma Inline (Write_Register);
+
+ procedure Set_Interrupt_Bit (Item : Interrupt; Block_Offset : SSE.Integer_Address) is
+ begin
+ Write_Register (Interrupt_Bit_Mask (Item), Block_Offset + Interrupt_Byte_Offset (Item));
+ end Set_Interrupt_Bit;
+ pragma Inline (Set_Interrupt_Bit);
+
+ function Read_Register (Offset : SSE.Integer_Address) return Byte is
+ Register : Byte;
+ for Register'Address use System'To_Address (Core_Peripherals_Base + Offset);
+ pragma Atomic (Register);
+ pragma Import (Ada, Register);
+ begin
+ return Register;
+ end Read_Register;
+ pragma Inline (Read_Register);
+
+ function Get_Interrupt_Bit (Item : Interrupt; Block_Offset : SSE.Integer_Address) return Boolean is
+ Tmp : Byte;
+ begin
+ Tmp := Read_Register (Block_Offset + Interrupt_Byte_Offset (Item));
+ return (Tmp and Interrupt_Bit_Mask (Item)) = 0;
+ end Get_Interrupt_Bit;
+ pragma Inline (Get_Interrupt_Bit);
+
+ ----------------------
+ -- Enable_Interrupt --
+ ----------------------
+
+ procedure Enable_Interrupt (Item : Interrupt) is
+ begin
+ Set_Interrupt_Bit (Item, Interrupt_Enable_Block_Offset);
+ end Enable_Interrupt;
+
+ -----------------------
+ -- Disable_Interrupt --
+ -----------------------
+
+ procedure Disable_Interrupt (Item : Interrupt) is
+ begin
+ Set_Interrupt_Bit (Item, Interrupt_Disable_Block_Offset);
+ end Disable_Interrupt;
+
+ --------------------
+ -- Pend_Interrupt --
+ --------------------
+
+ procedure Pend_Interrupt (Item : Interrupt) is
+ begin
+ Set_Interrupt_Bit (Item, Interrupt_Set_Pending_Block_Offset);
+ end Pend_Interrupt;
+
+ ----------------------
+ -- Unpend_Interrupt --
+ ----------------------
+
+ procedure Unpend_Interrupt (Item : Interrupt) is
+ begin
+ Set_Interrupt_Bit (Item, Interrupt_Clear_Pending_Block_Offset);
+ end Unpend_Interrupt;
+
+ -------------------------
+ -- Is_Interrupt_Active --
+ -------------------------
+
+ function Is_Interrupt_Active (Item : Interrupt) return Boolean is
+ begin
+ return Get_Interrupt_Bit (Item, Interrupt_Active_Block_Offset);
+ end Is_Interrupt_Active;
+
+ ----------------------------
+ -- Set_Interrupt_Priority --
+ ----------------------------
+
+ procedure Set_Interrupt_Priority (Item : Interrupt; Priority : Interrupt_Priority) is
+ Tmp : Byte;
+ begin
+ Tmp := Priority_To_Byte ((Reserved => 0, Priority => Priority));
+ Write_Register (Tmp, Interrupt_Priority_Block_Offset + SSE.Integer_Address (Item));
+ end Set_Interrupt_Priority;
+
+ ----------------------------
+ -- Get_Interrupt_Priority --
+ ----------------------------
+
+ function Get_Interrupt_Priority (Item : Interrupt) return Interrupt_Priority is
+ Tmp : Interrupt_Priority_Byte;
+ begin
+ Tmp := Byte_To_Priority (Read_Register (Interrupt_Priority_Block_Offset + SSE.Integer_Address (Item)));
+ return Tmp.Priority;
+ end Get_Interrupt_Priority;
+
+end MCU.Core.NVIC.Operations;
@@ -0,0 +1,42 @@
+---------------------------------------------------------------------------
+-- Copyright © 2011 Євгеній Мещеряков <eugen@debian.org> --
+-- --
+-- This program is free software: you can redistribute it and/or modify --
+-- it under the terms of the GNU General Public License as published by --
+-- the Free Software Foundation, either version 3 of the License, or --
+-- (at your option) any later version. --
+-- --
+-- This program is distributed in the hope that it will be useful, --
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of --
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --
+-- GNU General Public License for more details. --
+-- --
+-- You should have received a copy of the GNU General Public License --
+-- along with this program. If not, see <http://www.gnu.org/licenses/>. --
+---------------------------------------------------------------------------
+
+-- This package contains various routines for accessing the interrupt
+-- controller.
+
+pragma Restrictions (No_Elaboration_Code);
+
+package MCU.Core.NVIC.Operations is
+ pragma Preelaborate;
+
+ procedure Enable_Interrupt (Item : Interrupt);
+
+ procedure Disable_Interrupt (Item : Interrupt);
+
+ procedure Pend_Interrupt (Item : Interrupt);
+
+ procedure Unpend_Interrupt (Item : Interrupt);
+
+ function Is_Interrupt_Active (Item : Interrupt) return Boolean;
+
+ procedure Set_Interrupt_Priority (Item : Interrupt; Priority : Interrupt_Priority);
+
+ function Get_Interrupt_Priority (Item : Interrupt) return Interrupt_Priority;
+
+ -- procedure Trigger_Interrupt (Item : Interrupt);
+
+end MCU.Core.NVIC.Operations;
View
@@ -0,0 +1,62 @@
+---------------------------------------------------------------------------
+-- Copyright © 2011 Євгеній Мещеряков <eugen@debian.org> --
+-- --
+-- This program is free software: you can redistribute it and/or modify --
+-- it under the terms of the GNU General Public License as published by --
+-- the Free Software Foundation, either version 3 of the License, or --
+-- (at your option) any later version. --
+-- --
+-- This program is distributed in the hope that it will be useful, --
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of --
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --
+-- GNU General Public License for more details. --
+-- --
+-- You should have received a copy of the GNU General Public License --
+-- along with this program. If not, see <http://www.gnu.org/licenses/>. --
+---------------------------------------------------------------------------
+
+-- This package contains constants and type definitions for the interrupt
+-- controller module.
+
+pragma Restrictions (No_Elaboration_Code);
+
+package MCU.Core.NVIC is
+ pragma Pure;
+
+ ----------------------
+ -- Register offsets --
+ ----------------------
+ Interrupt_Enable_Block_Offset : constant := 16#100#;
+ Interrupt_Disable_Block_Offset : constant := 16#180#;
+ Interrupt_Set_Pending_Block_Offset : constant := 16#200#;
+ Interrupt_Clear_Pending_Block_Offset : constant := 16#280#;
+ Interrupt_Active_Block_Offset : constant := 16#300#;
+ Interrupt_Priority_Block_Offset : constant := 16#400#;
+ Software_Trigger_Interrupt_Register_Offset : constant := 16#F00#;
+
+ -----------
+ -- Types --
+ -----------
+ type Interrupt is range 0 .. 47;
+ type Interrupt_Priority is mod 2**3; -- TODO use range?
+
+ -----------------------------
+ -- Types for reserved bits --
+ -----------------------------
+ type Reserved_5 is mod 2**5;
+
+ type Interrupt_Priority_Byte is
+ record
+ Reserved : Reserved_5;
+ Priority : Interrupt_Priority;
+ end record;
+
+ for Interrupt_Priority_Byte use
+ record
+ Reserved at 0 range 0 .. 4;
+ Priority at 0 range 5 .. 7;
+ end record;
+
+ for Interrupt_Priority_Byte'Size use 8;
+ for Interrupt_Priority_Byte'Alignment use 1;
+end MCU.Core.NVIC;
View
@@ -17,11 +17,31 @@
pragma Restrictions (No_Elaboration_Code);
with System.Machine_Code;
+use System.Machine_Code;
package body MCU.Utils is
procedure Nop is
- use System.Machine_Code;
begin
- Asm ("nop", Volatile => True, Clobber => "memory");
+ Asm ("nop", Volatile => True);
end Nop;
+
+ procedure Wait_For_Interrupt is
+ begin
+ Asm ("wfi", Volatile => True);
+ end Wait_For_Interrupt;
+
+ procedure Wait_For_Event is
+ begin
+ Asm ("wfe", Volatile => True);
+ end Wait_For_Event;
+
+ procedure Mask_Interrupts is
+ begin
+ Asm ("cpsid i", Volatile => True);
+ end Mask_Interrupts;
+
+ procedure Unmask_Interrupts is
+ begin
+ Asm ("cpsie i", Volatile => True);
+ end Unmask_Interrupts;
end MCU.Utils;
View
@@ -24,4 +24,16 @@ package MCU.Utils is
procedure Nop;
pragma Inline_Always (Nop);
+
+ procedure Wait_For_Interrupt;
+ pragma Inline_Always (Wait_For_Interrupt);
+
+ procedure Wait_For_Event;
+ pragma Inline_Always (Wait_For_Event);
+
+ procedure Mask_Interrupts;
+ pragma Inline_Always (Mask_Interrupts);
+
+ procedure Unmask_Interrupts;
+ pragma Inline_Always (Unmask_Interrupts);
end MCU.Utils;
View
@@ -0,0 +1,65 @@
+with MCU.Core.System_Tick.Registers;
+with MCU.Utils;
+
+package body Real_Time is
+
+ Current_Time : Time := 0;
+ pragma Atomic (Current_Time);
+
+ procedure System_Tick_Interrupt_Handler;
+ pragma Export (Ada, System_Tick_Interrupt_Handler, "__gnat_vector_15");
+
+ procedure System_Tick_Interrupt_Handler is
+ Time_Shadow : Time;
+ begin
+ Time_Shadow := Current_Time;
+ Time_Shadow := Time_Shadow + 1;
+ Current_Time := Time_Shadow;
+ end System_Tick_Interrupt_Handler;
+
+ ----------------
+ -- Initialize --
+ ----------------
+
+ procedure Initialize is
+ use MCU.Core.System_Tick;
+
+ Value_Register_Shadow : Value_Register_Record;
+ Control_And_Status_Register_Shadow : Control_And_Status_Register_Record;
+ begin
+ -- Initialize the system timer with period 100 Hz with system clock 50 MHz
+ Value_Register_Shadow := (Value => 50E+4, Reserved => 0);
+ Registers.Reload_Value_Register := Value_Register_Shadow;
+
+ Control_And_Status_Register_Shadow := Registers.Control_And_Status_Register;
+ Control_And_Status_Register_Shadow.Enable := True;
+ Control_And_Status_Register_Shadow.Interrupt_Enable := True;
+ Control_And_Status_Register_Shadow.Clock_Source := System_Clock;
+ Registers.Control_And_Status_Register := Control_And_Status_Register_Shadow;
+ end Initialize;
+
+ -----------
+ -- Clock --
+ -----------
+
+ function Clock return Time is
+ begin
+ return Current_Time;
+ end Clock;
+
+ procedure Sleep_Until (Wakeup_Time : Time) is
+ Current_Time_Shadow : Time;
+ begin
+ loop
+ MCU.Utils.Mask_Interrupts;
+ Current_Time_Shadow := Current_Time;
+ exit when Current_Time_Shadow >= Wakeup_Time;
+ MCU.Utils.Wait_For_Interrupt;
+ MCU.Utils.Unmask_Interrupts;
+ end loop;
+ MCU.Utils.Unmask_Interrupts;
+ end Sleep_Until;
+
+begin
+ Initialize;
+end Real_Time;
Oops, something went wrong.

0 comments on commit 82e1186

Please sign in to comment.