------------------------------------------------------------------------------ ------------------------------------------------------------------------------ -- Cheddar is a GNU GPL real-time scheduling analysis tool. -- This program provides services to automatically check schedulability and -- other performance criteria of real-time architecture models. -- -- Copyright (C) 2002-2020, Frank Singhoff, Alain Plantec, Jerome Legrand, -- Hai Nam Tran, Stephane Rubini -- -- The Cheddar project was started in 2002 by -- Frank Singhoff, Lab-STICC UMR 6285, Université de Bretagne Occidentale -- -- Cheddar has been published in the "Agence de Protection des Programmes/France" in 2008. -- Since 2008, Ellidiss technologies also contributes to the development of -- Cheddar and provides industrial support. -- -- The full list of contributors and sponsors can be found in AUTHORS.txt and SPONSORS.txt -- -- 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 2 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, write to the Free Software -- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- -- -- Contact : cheddar@listes.univ-brest.fr -- ------------------------------------------------------------------------------ -- Last update : -- $Rev$ -- $Date$ -- $Author: nam $ ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ with Text_IO; use Text_IO; with Offsets; use Offsets; with Offsets; use Offsets.Offsets_Table_Package; with Qs_Tools; use Qs_Tools; with Ada.Exceptions; use Ada.Exceptions; with Expressions; use Expressions; with Dependencies; use Dependencies; with Caches; use Caches; with Cache_Set; use Cache_Set; use cache_set.Generic_Cache_Set; with Caches; use Caches.Cache_Blocks_Table_Package; with Cache_Block_Set; use Cache_Block_Set; with Cache_Access_Profile_Set; use Cache_Access_Profile_Set.Cache_Access_Profile_Set; with unbounded_strings; use unbounded_strings; with Debug; use Debug; with run_trees; use run_trees; with Cache_Utility; use Cache_Utility; with Buffers.Extended; use Buffers.Extended; package body Scheduler is procedure Put (My_Scheduler : in Generic_Scheduler_Ptr) is begin Put (My_Scheduler.all); end Put; function Export_Aadl_Properties (My_Scheduler : in Generic_Scheduler; Number_Of_Ht : in Natural) return Unbounded_String is Result : Unbounded_String := empty_string; begin for I in 1 .. Number_Of_Ht loop Result := Result & ASCII.HT; end loop; Result := Result & To_Unbounded_String ("Scheduling_Protocol => " & To_String (Get_Name (My_Scheduler)) & ";") & unbounded_lf; for I in 1 .. Number_Of_Ht loop Result := Result & ASCII.HT; end loop; Result := Result & To_Unbounded_String ("Cheddar_Properties::Scheduler_Quantum => " & Get_Quantum (My_Scheduler) & " ms ;") & unbounded_lf; for I in 1 .. Number_Of_Ht loop Result := Result & ASCII.HT; end loop; if Get_Preemptive (My_Scheduler) = preemptive then Result := Result & To_Unbounded_String ("Cheddar_Properties::Preemptive_Scheduler => True;") & unbounded_lf; else Result := Result & To_Unbounded_String ("Cheddar_Properties::Preemptive_Scheduler => False;") & unbounded_lf; end if; return Result; end Export_Aadl_Properties; procedure Set_Preemptive (My_Scheduler : in out Generic_Scheduler'Class; Preempt : in Preemptives_Type) is begin My_Scheduler.parameters.preemptive_type := Preempt; end Set_Preemptive; function Get_Preemptive (My_Scheduler : in Generic_Scheduler'Class) return Preemptives_Type is begin return My_Scheduler.parameters.preemptive_type; end Get_Preemptive; function Get_Preemptive (My_Scheduler : in Generic_Scheduler'Class) return String is begin return My_Scheduler.parameters.preemptive_type'Img; end Get_Preemptive; procedure Put (My_Scheduler : in Generic_Scheduler) is begin Put (My_Scheduler.parameters); end Put; procedure Reset (A_Scheduler : in out Generic_Scheduler'Class) is begin Initialize (A_Scheduler.parameters); end Reset; function Get_Name (My_Scheduler : in Generic_Scheduler'Class) return Unbounded_String is begin return To_Unbounded_String (My_Scheduler.parameters.scheduler_type'Img); end Get_Name; function Get_Name (My_Scheduler : in Generic_Scheduler_Ptr) return Unbounded_String is begin return Get_Name (My_Scheduler.all); end Get_Name; function Get_Name (My_Scheduler : in Generic_Scheduler'Class) return Schedulers_Type is begin return My_Scheduler.parameters.scheduler_type; end Get_Name; function Get_Name (My_Scheduler : in Generic_Scheduler_Ptr) return Schedulers_Type is begin return Get_Name (My_Scheduler.all); end Get_Name; function Build_Tcb (My_Scheduler : in Generic_Scheduler; A_Task : Generic_Task_Ptr) return Tcb_Ptr is A_Tcb : Tcb_Ptr; begin A_Tcb := new Tcb; Initialize (A_Tcb.all, A_Task); return A_Tcb; end Build_Tcb; procedure Initialize (A_Tcb : in out Tcb; A_Task : Generic_Task_Ptr) is Seed : Generator; begin A_Tcb.Tsk := A_Task; A_Tcb.Activation := 1; A_Tcb.End_Time := 0; A_Tcb.Used_Cpu := 0; A_Tcb.Wake_Up_Time := A_Tcb.Tsk.start_time; A_Tcb.Rest_Of_Capacity := A_Tcb.Tsk.capacity; A_Tcb.Used_Capacity := 0; A_Tcb.Suspended := False; A_Tcb.CRPD_capacity := 0; A_Tcb.UCBs_Loaded := 0.0; A_Tcb.Block_Reload_Time := 1; if (A_Tcb.Tsk.task_type = Poisson_Type) or (A_Tcb.Tsk.task_type = Parametric_Type) then if (Poisson_Task_Ptr (A_Tcb.Tsk).predictable) then Reset (Seed, Poisson_Task_Ptr (A_Tcb.Tsk).seed); else Reset (Seed); end if; Save (Seed, A_Tcb.Task_Seed); end if; end Initialize; procedure processor_Initialization (My_Scheduler : in out Generic_Scheduler'Class; Si : in out Scheduling_Information; Processor_Name : in Unbounded_String; My_Tasks : in out Tasks_Set; My_Resources : in out Resources_Set; My_Buffers : in out Buffers_Set; Result : in out Scheduling_Sequence_Ptr; With_Offsets : Boolean; With_Precedencies : Boolean; With_Resources : Boolean; With_Task_Specific_Seed : Boolean := True; Global_Seed_Value : Integer := 0; Predictable_Global_Seed : Boolean := True; Given_Last_Time : in Natural; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table; My_Cache_Access_Profiles : in Cache_Access_Profiles_Set; My_Caches : in Caches_Set; With_CRPD : Boolean := False) is Iterator1 : Tasks_Iterator; Iterator2 : Resources_Iterator; A_Buffer_Size : Buffer_Scheduling_Information_Ptr; Iterator3 : Buffers_Iterator; A_Buffer : Buffer_Ptr; A_Task : Generic_Task_Ptr; A_Resource : Generic_Resource_Ptr; Seed : Generator; A_Item : Time_Unit_Event_Ptr; A_Cache : Generic_Cache_Ptr; A_CAP : Cache_Access_Profile_Ptr; begin if (Predictable_Global_Seed) then Reset (Seed, Global_Seed_Value); else Reset (Seed); end if; Si.With_Specific_Task_Seed := With_Task_Specific_Seed; Save (Seed, Si.Global_Seed); -- Build Task Control Blocks -- reset_iterator (My_Tasks, Iterator1); loop current_element (My_Tasks, A_Task, Iterator1); Si.Tcbs (Si.Number_Of_Tasks) := Build_Tcb (My_Scheduler, A_Task); Si.Number_Of_Tasks := Si.Number_Of_Tasks + 1; exit when is_last_element (My_Tasks, Iterator1); next_element (My_Tasks, Iterator1); end loop; -- Build of buffer list with their current size -- (in order to generate Write/Read buffer events -- if (With_Precedencies) then if (get_number_of_elements (My_Buffers) > 0) then reset_iterator (My_Buffers, Iterator3); loop current_element (My_Buffers, A_Buffer, Iterator3); A_Buffer_Size := new Buffer_Scheduling_Information; A_Buffer_Size.Written_Buffer := A_Buffer; A_Buffer_Size.Current_Size := A_Buffer.buffer_initial_data_size; add (Si.Written_Buffers, A_Buffer_Size); exit when is_last_element (My_Buffers, Iterator3); next_element (My_Buffers, Iterator3); end loop; end if; end if; if (With_Resources) then if (get_number_of_elements (My_Resources) > 0) then reset_iterator (My_Resources, Iterator2); loop current_element (My_Resources, A_Resource, Iterator2); Si.Shared_Resources (Si.Number_Of_Resources) := Build_Resource (My_Scheduler, A_Resource); Si.Number_Of_Resources := Si.Number_Of_Resources + 1; exit when is_last_element (My_Resources, Iterator2); next_element (My_Resources, Iterator2); end loop; end if; end if; -- Initialize event table (store the computed scheduling) -- initialize (Result.all); -- First activation for all tasks -- for I in 0 .. Si.Number_Of_Tasks - 1 loop if Si.Tcbs (I).Tsk.cpu_name = Processor_Name then if Event_To_Generate (Task_Activation) then A_Item := new Time_Unit_Event (Task_activation); A_Item.activation_task := Si.Tcbs (I).Tsk; add (Result.all, Si.Tcbs (I).Tsk.start_time, a_item); end if; end if; end loop; if (With_CRPD) then if(get_number_of_elements(My_Caches) > 0) then if(get_number_of_elements(My_Cache_Access_Profiles) <=0) then raise Cache_Access_Profile_Must_Be_Defined; elsif (Integer(get_number_of_elements(My_Cache_Access_Profiles)) < Integer(Si.Number_Of_Tasks)) then raise Cache_Access_Profile_Must_Be_Defined_For_All_Tasks; end if; for i in 0..Si.Number_Of_Tasks-1 loop Initialize(Si.Tcbs(Tasks_Range(i)).UCBs); Initialize(Si.Tcbs(Tasks_Range(i)).ECBs); Initialize(Si.Tcbs(Tasks_Range(i)).UCBs_In_Cache); Si.Tcbs(Tasks_Range(i)).CRPD_capacity := 0; -- Block reload time is associated to a Tcb in scheduling simulation -- We do not take into account task migration in CRPD-Aware scheduling simulation -- A_Cache := Search_Cache(my_caches => My_Caches, name => My_Scheduler.corresponding_core_unit.l1_cache_system_name); Si.Tcbs(Tasks_Range(i)).Block_Reload_Time := A_Cache.block_reload_time; A_CAP := Search_Cache_Access_Profile(My_Cache_Access_Profiles, Si.Tcbs(Tasks_Range(i)).Tsk.cache_access_profile_name); for j in 0..A_CAP.UCBs.Nb_Entries-1 loop Add(Si.Tcbs(Tasks_Range(i)).UCBs,A_CAP.UCBs.Entries(j).cache_block_number); Add(Si.Tcbs(Tasks_Range(i)).UCBs_In_Cache,A_CAP.UCBs.Entries(j).cache_block_number); end loop; Put_Debug("Cache Access Profile: " & A_CAP.ECBs.Nb_Entries'Img); for j in 0..A_CAP.ECBs.Nb_Entries-1 loop Add(Si.Tcbs(Tasks_Range(i)).ECBs,A_CAP.ECBs.Entries(j).cache_block_number); end loop; end loop; end if; end if; -- Specific initialization for the RUN scheduler -- if (my_scheduler.parameters.scheduler_type = Reduction_to_uniprocessor_protocol) then initialize_run_tree(my_tasks, processor_name); reduce; end if; end processor_Initialization; procedure core_unit_Initialization (My_Scheduler : in out Generic_Scheduler'Class; Si : in out Scheduling_Information; Processor_Name : in Unbounded_String; My_Tasks : in out Tasks_Set; My_Resources : in out Resources_Set; My_Buffers : in out Buffers_Set; Result : in out Scheduling_Sequence_Ptr; With_Offsets : Boolean; With_Precedencies : Boolean; With_Resources : Boolean; With_Task_Specific_Seed : Boolean := True; Global_Seed_Value : Integer := 0; Predictable_Global_Seed : Boolean := True; Given_Last_Time : in Natural; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is begin My_Scheduler.Previously_Elected := Tasks_Range'First; My_Scheduler.previous_running_task_is_not_completed := False; My_Scheduler.Previously_Busy := False; end core_unit_Initialization; -- ellidiss simulation optimization procedure Generate_Running_Event ( My_Scheduler : in out Generic_Scheduler'Class; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Elected : in Tasks_Range; Current_Time : in Natural) is A_Item : Time_Unit_Event_Ptr; begin A_Item := Produce_Running_Task_Event (My_Scheduler, Si.Tcbs (Elected)); add (Result.all, Current_Time, a_item); end Generate_Running_Event; ---------------------------------------------------------------------- -- If we have run or NOT a task during the current time unit, -- produce events and update simulation data ---------------------------------------------------------------------- procedure Update_Task_Simulation_Data_And_Produce_Events (My_Scheduler : in out Generic_Scheduler'Class; Processor_Name : in Unbounded_String; Si : in out Scheduling_Information; My_Dependencies : in Tasks_Dependencies_Ptr; Elected : in Tasks_Range; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; Last_Time : in Natural; With_Offsets : Boolean := True; With_Precedencies : Boolean := True; With_Resources : Boolean := True; With_Jitters : Boolean := True; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table; With_CRPD : in Boolean := False) is A_Item : Time_Unit_Event_Ptr; -- CRPD analysis -- crpd : Integer := 0; crpd_U : Integer := 0; crpd_ECB : Integer := 0; evicted_ucbs : Integer := 0; comp_model : CRPD_Computation_Model := C_UE; begin Put_Debug("Call Update_Task_Simulation_Data_And_Produce_Events ",very_verbose); My_Scheduler.Previously_Busy := False; if With_Resources then -- We may notice that some runnable tasks were blocked due to -- shared resource ... in this case, we generate the corresponding event -- for i in 0..Si.Number_Of_Tasks-1 loop if (si.tcbs(i).wait_for_a_resource/=null) then put_debug(to_string(si.tcbs(i).tsk.name) & " : generate wait_for_event_resource " & to_string(Si.tcbs(i).wait_for_a_resource.name) ); if Event_To_Generate (Wait_For_Resource) then A_Item := new Time_Unit_Event (Wait_For_Resource); A_Item.wait_for_resource := Si.tcbs(i).wait_for_a_resource; A_Item.wait_for_resource_task := si.tcbs(i).Tsk; add (Result.all, current_time, A_Item); end if; end if; end loop; end if; end Update_Task_Simulation_Data_And_Produce_Events; ---------------------------------------------------------------------- -- If we have run a task during the current time unit, -- produce events and update simulation data ---------------------------------------------------------------------- procedure Update_Task_Simulation_Data_And_Produce_Events_when_task_is_run (My_Scheduler : in out Generic_Scheduler'Class; Processor_Name : in Unbounded_String; Si : in out Scheduling_Information; My_Dependencies : in Tasks_Dependencies_Ptr; Elected : in Tasks_Range; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; Last_Time : in Natural; With_Offsets : Boolean := True; With_Precedencies : Boolean := True; With_Resources : Boolean := True; With_Jitters : Boolean := True; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table; With_CRPD : in Boolean := False) is -- For Poisson Process task -- Seed : Generator; Temp : Natural; -- For Parametric task -- Parametric_Delay : Natural := 0; A_Item : Time_Unit_Event_Ptr; -- CRPD analysis -- crpd : Integer := 0; crpd_U : Integer := 0; crpd_ECB : Integer := 0; evicted_ucbs : Integer := 0; flag : Boolean; comp_model : CRPD_Computation_Model := C_UE; begin Put_Debug("Call Update_Task_Simulation_Data_And_Produce_Events_when_task_is_run ",very_verbose); ------------------------------------------------------ -- Update variables for multi core scheduling ------------------------------------------------------ -- Assign the task to the current core unit and put it -- in the already run state -- Si.Tcbs (Elected).assigned_core_unit := my_scheduler.corresponding_core_unit; Si.Tcbs (Elected).already_run_at_current_time := True; ------------------------------------------------------ -- Allocate/release shared resources -- and produce associated events ------------------------------------------------------ if With_Resources then Allocate_Resource (My_Scheduler, Si, Result, Current_Time, Si.Tcbs (Elected), Event_To_Generate); Release_Resource (My_Scheduler, Si, Result, Current_Time, Si.Tcbs (Elected), Event_To_Generate); end if; ------------------------------------------------------ -- Send/Receive Messages -- write/read on buffers -- and produce associated events ------------------------------------------------------ if With_Precedencies then Send_Message (My_Scheduler, Si, Result, Current_Time, Si.Tcbs (Elected), My_Dependencies, Event_To_Generate); Receive_Message (My_Scheduler, Si, Result, Current_Time, Si.Tcbs (Elected), My_Dependencies, Event_To_Generate); Buffer_Write (My_Scheduler, Si, Result, Current_Time, Si.Tcbs (Elected), My_Dependencies, Event_To_Generate); Buffer_Read (My_Scheduler, Si, Result, Current_Time, Si.Tcbs (Elected), My_Dependencies, Event_To_Generate); end if; ------------------------------------------------------ -- CRPD -- -- ------------------------------------------------------ if (With_CRPD) then crpd := 0; evicted_ucbs := 0; flag := false; ----------------------------------------------------------------------- --Handling event START OF TASK CAPACITY ----------------------------------------------------------------------- if (Si.Tcbs (Elected).Rest_Of_Capacity = Si.Tcbs (Elected).Tsk.capacity AND Si.Tcbs (Elected).Used_Capacity = 0) then Cache_Utility.Fill_Tasks_UCBs_In_Cache(Si.Tcbs(Elected)); Si.Tcbs(Elected).UCBs_Loaded := 0.0; --#Debug Info Put_debug(Current_Time'Img & ": Start of task capacity:" & To_String(Si.Tcbs(Elected).Tsk.name)); for i in 0..Si.Number_Of_Tasks-1 loop if (Si.Tcbs(i).Rest_Of_Capacity > 0 AND Si.Tcbs(i).Wake_Up_Time + Si.Tcbs(i).Tsk.offsets.Entries(0).offset_value < Current_Time AND i /= Elected) then Put_debug("Incompleted tasks: " & To_String(Si.Tcbs(i).Tsk.name) & ", "); end if; end loop; -- end if; ----------------------------------------------------------------------- --Handling event RUNNING TASK -- + The number of actual UCB in cache is tracked -- by Si.Tcbs(Elected).UCBs_Loaded -- ----------------------------------------------------------------------- if(comp_model = C_UE) then crpd := Cache_Utility.Compute_CRPD(Si.Tcbs(Elected)); elsif (comp_model = C_UE_lim) then crpd := Cache_Utility.Compute_CRPD(Si.Tcbs(Elected)); if(crpd > Integer(Si.Tcbs(Elected).UCBs_Loaded)) then crpd := Integer(Si.Tcbs(Elected).UCBs_Loaded); Si.Tcbs(Elected).UCBs_Loaded := 0.0; else Si.Tcbs(Elected).UCBs_Loaded := Si.Tcbs(Elected).UCBs_Loaded - Float(crpd); end if; end if; -- Block Reload Time is taken into account when we have identified all the UCB evicted; -- crpd := crpd*Si.Tcbs(Elected).Block_Reload_Time; if(crpd > 0) then Put_Debug(Current_Time'Img & ": Preemption cost:" & To_String(Si.Tcbs(Elected).Tsk.name & " -" & crpd'Img)); Si.Total_Preemption_Cost := Si.Total_Preemption_Cost + crpd; Si.Tcbs(Elected).Rest_Of_Capacity := Si.Tcbs(Elected).Rest_Of_Capacity + crpd; Si.Tcbs(Elected).CRPD_capacity := Si.Tcbs(Elected).CRPD_capacity + crpd; end if; Cache_Utility.Fill_Tasks_UCBs_In_Cache(Si.Tcbs(Elected)); if(Si.Tcbs(Elected).UCBs_Loaded < Float(Si.Tcbs(Elected).UCBs.Size)) then Si.Tcbs(Elected).UCBs_Loaded := Si.Tcbs(Elected).UCBs_Loaded + Float(1.0/Si.Tcbs(Elected).Block_Reload_Time); end if; ----------------------------------------------------------------------- --Handling event PREEMPTION -- + When perform scheduling simulation with CRPD, -- PREEMPTION event is generated for EACH preempted task ! -- ----------------------------------------------------------------------- if (My_Scheduler.Previously_Elected /= Elected) then for i in 0..Si.Number_Of_Tasks-1 loop if(Tasks_Range(i) /= Elected AND Si.Tcbs(Tasks_Range(i)).Tsk.cpu_name = Si.Tcbs(Elected).Tsk.cpu_name AND Si.Tcbs(Tasks_Range(i)).Rest_Of_Capacity > 0 AND Si.Tcbs(Tasks_Range(i)).Used_Capacity > 0 AND Si.Tcbs(Tasks_Range(i)).Wake_Up_Time < Current_Time) then for j in 0..Si.Tcbs(Elected).ECBs.Size - 1 loop if(Remove_By_Value(Si.Tcbs(Tasks_Range(i)).UCBs_In_Cache, Si.Tcbs(Elected).ECBs.Elements(j)))then evicted_ucbs := evicted_ucbs + 1; end if; end loop; if (Event_To_Generate (Preemption)) then A_Item := new Time_Unit_Event (Preemption); A_Item.preempting_task := Si.Tcbs(Elected).Tsk; A_Item.preempted_task := Si.Tcbs(Tasks_Range(i)).Tsk; A_Item.evicted_ucbs := evicted_ucbs; Add (Result.all, Current_Time, a_item); end if; flag := true; end if; end loop; if(flag) then Si.Number_Of_Preemption := Si.Number_Of_Preemption + 1; end if; end if; end if; --------------------------------------------------------- -- Update task properties and produce associated events --------------------------------------------------------- -- -- Produce events -- if Event_To_Generate (Start_Of_Task_Capacity) then if Si.Tcbs (Elected).Rest_Of_Capacity = Si.Tcbs (Elected).Tsk.capacity then A_Item := new Time_Unit_Event (Start_Of_Task_Capacity); A_Item.start_task := Si.Tcbs (Elected).Tsk; add (Result.all, Current_Time, a_item); end if; end if; if Event_To_Generate (Running_Task) then A_Item := Produce_Running_Task_Event (My_Scheduler, Si.Tcbs (Elected)); add (Result.all, Current_Time, a_item); end if; if Event_To_Generate (End_Of_Task_Capacity) then if Si.Tcbs (Elected).Rest_Of_Capacity - 1 = 0 then A_Item := new Time_Unit_Event (End_Of_Task_Capacity); A_Item.end_task := Si.Tcbs (Elected).Tsk; add (Result.all, Current_Time + 1, a_item); end if; end if; if (Event_To_Generate (Preemption) AND (NOT With_CRPD)) then if (My_Scheduler.Previously_Elected /= Elected) then for i in 0..Si.Number_Of_Tasks-1 loop if(Tasks_Range(i) /= Elected AND Si.Tcbs(Tasks_Range(i)).Tsk.cpu_name = Si.Tcbs(Elected).Tsk.cpu_name AND Si.Tcbs(Tasks_Range(i)).Rest_Of_Capacity > 0 AND Si.Tcbs(Tasks_Range(i)).Used_Capacity > 0 AND Si.Tcbs(Tasks_Range(i)).Wake_Up_Time < Current_Time) then A_Item := new Time_Unit_Event (Preemption); A_Item.preempting_task := Si.Tcbs(Elected).Tsk; A_Item.preempted_task := Si.Tcbs(Tasks_Range(i)).Tsk; A_Item.evicted_ucbs := 0; Add (Result.all, Current_Time, a_item); flag := true; end if; exit when flag = true; end loop; if(flag) then Si.Number_Of_Preemption := Si.Number_Of_Preemption + 1; end if; end if; end if; -- Update task properties -- -- update of various "capacities" -- Si.Tcbs (Elected).Used_Capacity := Si.Tcbs (Elected).Used_Capacity + My_scheduler.corresponding_core_unit.speed; if (Si.Tcbs (Elected).Rest_Of_Capacity - My_scheduler.corresponding_core_unit.speed < 0) then Si.Tcbs (Elected).Rest_Of_Capacity := 0; else Si.Tcbs (Elected).Rest_Of_Capacity := Si.Tcbs (Elected).Rest_Of_Capacity - My_scheduler.corresponding_core_unit.speed; end if; Si.Tcbs (Elected).Used_Cpu := Si.Tcbs (Elected).Used_Cpu + My_scheduler.corresponding_core_unit.speed; if(Si.Tcbs(Elected).CRPD_capacity > 0) then if (Si.Tcbs (Elected).CRPD_capacity - My_scheduler.corresponding_core_unit.speed < 0) then Si.Tcbs (Elected).CRPD_capacity := 0; else Si.Tcbs (Elected).CRPD_capacity := Si.Tcbs (Elected).CRPD_capacity - My_scheduler.corresponding_core_unit.speed; end if; end if; -- The task is not assigned anymore to a core unit -- My_Scheduler.previous_running_task_is_not_completed:= true; if (Si.Tcbs (Elected).Rest_Of_Capacity = 0) then Si.Tcbs (Elected).assigned_core_unit := null; My_Scheduler.previous_running_task_is_not_completed:= false; end if; -- -- Compute the next task activations -- if (Si.Tcbs (Elected).Rest_Of_Capacity = 0) then Si.Tcbs (Elected).Used_Capacity := 0; Si.Tcbs (Elected).End_Time := Current_Time + 1; Si.Tcbs (Elected).Activation := Si.Tcbs (Elected).Activation + 1; case Si.Tcbs (Elected).Tsk.task_type is when Periodic_Type => Si.Tcbs (Elected).Rest_Of_Capacity := Si.Tcbs (Elected).Tsk.capacity; Si.Tcbs (Elected).Wake_Up_Time := Si.Tcbs (Elected).Wake_Up_Time + Periodic_Task_Ptr (Si.Tcbs (Elected).Tsk).period; when Poisson_Type => Si.Tcbs (Elected).Rest_Of_Capacity := Si.Tcbs (Elected).Tsk.capacity; if Si.With_Specific_Task_Seed then Reset (Seed, Si.Tcbs (Elected).Task_Seed); else Reset (Seed, Si.Global_Seed); end if; Temp := Natural (Get_Exponential_Time (Double ( Poisson_Task_Ptr (Si.Tcbs (Elected).Tsk).period), Seed)); Si.Tcbs (Elected).Wake_Up_Time := Si.Tcbs (Elected).Wake_Up_Time + Temp; if Si.With_Specific_Task_Seed then Save (Seed, Si.Tcbs (Elected).Task_Seed); else Save (Seed, Si.Global_Seed); end if; when Sporadic_Type => Si.Tcbs (Elected).Rest_Of_Capacity := Si.Tcbs (Elected).Tsk.capacity; if Si.With_Specific_Task_Seed then Reset (Seed, Si.Tcbs (Elected).Task_Seed); else Reset (Seed, Si.Global_Seed); end if; Temp := Natural(Get_Exponential_Time (Double (Poisson_Task_Ptr (Si.Tcbs (Elected).Tsk).period), Seed)); Si.Tcbs (Elected).Wake_Up_Time := Si.Tcbs (Elected).Wake_Up_Time + Natural'Max(Temp,Periodic_Task_Ptr (Si.Tcbs (Elected).Tsk).period); if Si.With_Specific_Task_Seed then Save (Seed, Si.Tcbs (Elected).Task_Seed); else Save (Seed, Si.Global_Seed); end if; when Aperiodic_Type => null; when Scheduling_Task_Type => null; when Parametric_Type => Si.Tcbs (Elected).Rest_Of_Capacity := Si.Tcbs (Elected).Tsk.capacity; -- The scheduler has to be a parametric one !!! -- if (My_Scheduler.parameters.scheduler_type /= pipeline_User_Defined_Protocol) and (My_Scheduler.parameters.scheduler_type /= Compiled_User_Defined_Protocol) and (My_Scheduler.parameters.scheduler_type /= Automata_User_Defined_Protocol) then Raise_Exception (Statement_Error'Identity, "User-defined task only permitted with user-defined schedulers"); end if; Compute_Activation_Time (My_Scheduler, Si, Elected, Parametric_Delay); Si.Tcbs (Elected).Wake_Up_Time := Si.Tcbs (Elected).Wake_Up_Time + Parametric_Delay; when Frame_Task_Type => Si.Tcbs (Elected).Rest_Of_Capacity := Si.Tcbs (Elected).Tsk.capacity; Si.Tcbs (Elected).Wake_Up_Time := Si.Tcbs (Elected).Wake_Up_Time + Frame_Task_Ptr (Si.Tcbs (Elected).Tsk).period; end case; -- Add to the scheduling table the next activation time -- if Event_To_Generate (Task_Activation) then if (Si.Tcbs (Elected).Tsk.task_type /= Aperiodic_Type and Si.Tcbs (Elected).Wake_Up_Time <= Last_Time) then A_Item := new Time_Unit_Event (Task_activation); A_Item.activation_task := Si.Tcbs (Elected).Tsk; add (Result.all, Si.Tcbs (Elected).Wake_Up_Time, a_item); end if; end if; end if; -- Store the previous ran task -- My_Scheduler.Previously_Elected := Elected; My_Scheduler.Previously_Busy := True; end Update_Task_Simulation_Data_And_Produce_Events_when_task_is_run; -- Return true is the task "a_tcb" can be schedule now -- according its jitter -- procedure Check_Jitter (A_Tcb : in Tcb_Ptr; At_Time : in Natural; is_ready : out Boolean) is Seed : Generator; Temp : Natural; begin is_ready := true; if (a_tcb.tsk.task_type = Sporadic_Type) or (a_tcb.tsk.task_type = periodic_Type) or (a_tcb.tsk.task_type = poisson_Type) then if Periodic_Task_Ptr(a_Tcb.Tsk).jitter/=0 then if (a_tcb.tsk.capacity = a_tcb.rest_of_capacity) then Reset (Seed, a_tcb.Task_Seed); Temp := natural'min(Periodic_Task_Ptr(a_Tcb.Tsk).jitter, Natural(Get_Exponential_Time(Double(Periodic_Task_Ptr(a_Tcb.Tsk).jitter), Seed)) ); if a_tcb.Wake_Up_Time + temp > at_time then is_ready:=false; else is_ready:=true; end if; Save (Seed, a_Tcb.Task_Seed); end if; end if; end if; end check_jitter; -- Return true is the task "a_tcb" can be schedule now -- according its offsets -- function Check_Offset (A_Tcb : Tcb_Ptr; At_Time : Natural) return Boolean is Target_Time : Integer := 0; begin for K in 0 .. A_Tcb.Tsk.offsets.nb_entries - 1 loop if (A_Tcb.Tsk.offsets.entries (K).activation = A_Tcb.Activation) or (A_Tcb.Tsk.offsets.entries (K).activation = 0) then Target_Time := A_Tcb.Wake_Up_Time + A_Tcb.Tsk.offsets.entries (K).offset_value; if At_Time < Target_Time then return False; end if; end if; end loop; return True; end Check_Offset; procedure Send_Message (My_Scheduler : in out Generic_Scheduler; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; A_Tcb : Tcb_Ptr; Deps : Tasks_Dependencies_Ptr; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is A_Item : Time_Unit_Event_Ptr; My_Iterator : Tasks_Dependencies_Iterator; A_Half_Dep_Ptr : Dependency_Ptr; A_Message : Generic_Message_Ptr; A_Message_Scheduling_Information : Message_Scheduling_Information_Ptr; begin -- is the time to send message ? -- if (A_Tcb.Rest_Of_Capacity = 1) then -- Check if the task has a message to send -- if not is_empty (Deps.Depends) then reset_iterator (Deps.Depends, My_Iterator); loop current_element (Deps.Depends, A_Half_Dep_Ptr, My_Iterator); -- Is the task concerned ? -- Is it a communication dependency ? -- Is it a sending operation ? -- if (A_Half_Dep_Ptr.type_of_dependency = asynchronous_Communication_Dependency) then if (A_Half_Dep_Ptr.asynchronous_communication_dependent_task.name = A_Tcb.Tsk.name) and (A_Half_Dep_Ptr.asynchronous_communication_orientation = From_Task_To_Object) then A_Message := A_Half_Dep_Ptr.asynchronous_communication_dependency_object; A_Message_Scheduling_Information := new Message_Scheduling_Information; A_Message_Scheduling_Information.Send_Time := Current_Time + 1; A_Message_Scheduling_Information.Sended_Message := A_Message; add (Si.Sended_Messages, A_Message_Scheduling_Information); if Event_To_Generate (Send_Message) then A_Item := new Time_Unit_Event (Send_Message); A_Item.send_message := A_Message; A_Item.send_task := A_Tcb.Tsk; add (Result.all, A_Message_Scheduling_Information.Send_Time, A_Item); end if; end if; end if; exit when is_last_element (Deps.Depends, My_Iterator); next_element (Deps.Depends, My_Iterator); end loop; end if; end if; end Send_Message; procedure Receive_Message (My_Scheduler : in out Generic_Scheduler; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; A_Tcb : Tcb_Ptr; Deps : Tasks_Dependencies_Ptr; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is A_Item : Time_Unit_Event_Ptr; My_Iterator : Tasks_Dependencies_Iterator; A_Half_Dep_Ptr : Dependency_Ptr; A_Message : Generic_Message_Ptr; List_Ite : Message_Scheduling_Information_Iterator; Msg : Message_Scheduling_Information_Ptr; begin -- Is The time to receive a message ? -- if (A_Tcb.Rest_Of_Capacity = A_Tcb.Tsk.capacity) then -- Check the sended_messages to see if this -- messages was already sent -- if not is_empty (Si.Sended_Messages) then reset_head_iterator (Si.Sended_Messages, List_Ite); loop current_element (Si.Sended_Messages, Msg, List_Ite); reset_iterator (Deps.Depends, My_Iterator); loop current_element (Deps.Depends, A_Half_Dep_Ptr, My_Iterator); -- Is the task concerned ? -- Is it a communication dependency ? -- Is it a receiving operation ? -- if (A_Half_Dep_Ptr.type_of_dependency = asynchronous_Communication_Dependency) then if (A_Half_Dep_Ptr.asynchronous_communication_dependent_task.name = A_Tcb.Tsk.name) and (A_Half_Dep_Ptr.asynchronous_communication_orientation = From_Object_To_Task) then A_Message := A_Half_Dep_Ptr.asynchronous_communication_dependency_object; -- Check the sended_messages to see if this -- messages was already sent -- -- Is it the right message ? -- Is the message arrived at the receiving processor ? -- if (Msg.Sended_Message.name = A_Message.name) then if (Current_Time >= (Msg.Send_Time + A_Message.response_time - 1)) then if Event_To_Generate (Receive_Message) then A_Item := new Time_Unit_Event (Receive_Message); A_Item.receive_message := A_Message; A_Item.receive_task := A_Tcb.Tsk; add (Result.all, Current_Time, A_item); end if; -- Delete received message -- delete (Si.Sended_Messages, Msg); -- Currently, a task reads only one message per --activation -- return; end if; end if; end if; end if; exit when is_last_element (Deps.Depends, My_Iterator); next_element (Deps.Depends, My_Iterator); end loop; if is_tail_element (Si.Sended_Messages, List_Ite) then exit; end if; next_element (Si.Sended_Messages, List_Ite); end loop; end if; end if; end Receive_Message; procedure Buffer_Read (My_Scheduler : in out Generic_Scheduler; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; A_Tcb : Tcb_Ptr; Deps : Tasks_Dependencies_Ptr; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is A_Item : Time_Unit_Event_Ptr; My_Iterator : Tasks_Dependencies_Iterator; A_Half_Dep_Ptr : Dependency_Ptr; A_Buffer : Buffer_Ptr; A_Buffer_Size : Buffer_Scheduling_Information_Ptr; List_Ite : Buffer_Scheduling_Information_Iterator; Read_Size : Integer := 0; begin -- Check if the task has a buffer to write -- if (Has_Buffer_To_Read (Deps, A_Tcb.Tsk)) then -- for all tasks which have a precedency with the current task -- loop current_element (Deps.Depends, A_Half_Dep_Ptr, My_Iterator); if (A_Half_Dep_Ptr.type_of_dependency = Queueing_Buffer_Dependency) then if (A_Half_Dep_Ptr.buffer_dependent_task.name = A_Tcb.Tsk.name) and (A_Half_Dep_Ptr.buffer_orientation = From_Object_To_Task) then A_Buffer := A_Half_Dep_Ptr.buffer_dependency_object; -- Must we generate Read event ? -- if Event_To_Generate (Read_From_Buffer) then for I in 0 .. A_Buffer.roles.nb_entries - 1 loop -- Is the right task ? -- if (A_Buffer.roles.entries (I).item = A_Tcb.Tsk.name) then -- Is it time to read ? -- if (A_Buffer.roles.entries (I).data.time = (A_Tcb.Tsk.capacity + 1 - A_Tcb.Rest_Of_Capacity)) then -- Does the task is consumer ? -- if (A_Buffer.roles.entries (I).data.the_role = Queuing_Consumer OR A_Buffer.roles.entries (I).data.the_role = UCSDF_Consumer) then -- Can we find some information to read ? -- if not is_empty (Si.Written_Buffers) then reset_head_iterator (Si.Written_Buffers, List_Ite); loop current_element (Si.Written_Buffers, A_Buffer_Size, List_Ite); if (A_Buffer_Size.Written_Buffer.name = A_Buffer.name) then -- Compute the read data size -- if (A_Buffer.roles.entries (I).data.the_role = Queuing_Consumer) then Read_Size := A_Buffer.roles.entries (I).data.size; elsif (A_Buffer.roles.entries (I).data.the_role = UCSDF_Consumer) then Read_Size := Get_Data_Size(To_String(A_Buffer.roles.entries (I).data.amplitude_function), A_Tcb.Activation); end if; -- Do we have enough data to read -- if (A_Buffer_Size.Current_Size >= Read_Size) then -- If yes, produce a read event A_Item := new Time_Unit_Event (Read_From_Buffer); A_Item.read_buffer := A_Buffer; A_Item.read_task := A_Tcb.Tsk; A_Item.read_size := Read_Size; A_Item.read_buffer_current_data_size := A_Buffer_Size.Current_Size; add (Result.all, Current_Time, A_Item); -- Update buffer current data size A_Buffer_Size.Current_Size := A_Buffer_Size.Current_Size - Read_Size; exit; else -- If not, produce an underflow event -- No update is done on buffer current data size A_Item := new Time_Unit_Event (Buffer_Underflow); A_Item.underflow_buffer := A_Buffer; A_item.underflow_task := A_Tcb.Tsk; A_Item.underflow_read_size := Read_Size; A_Item.underflow_buffer_current_data_size := A_Buffer_Size.Current_Size; add (Result.all, Current_Time, A_Item); exit; end if; end if; if is_tail_element (Si.Written_Buffers, List_Ite) then exit; end if; next_element (Si.Written_Buffers, List_Ite); end loop; end if; end if; end if; end if; end loop; end if; end if; end if; exit when is_last_element (Deps.Depends, My_Iterator); next_element (Deps.Depends, My_Iterator); end loop; end if; -- Free objects -- end Buffer_Read; procedure Buffer_Write (My_Scheduler : in out Generic_Scheduler; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; A_Tcb : Tcb_Ptr; Deps : Tasks_Dependencies_Ptr; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is A_Item : Time_Unit_Event_Ptr; My_Iterator : Tasks_Dependencies_Iterator; A_Half_Dep_Ptr : Dependency_Ptr; A_Buffer : Buffer_Ptr; A_Buffer_Size : Buffer_Scheduling_Information_Ptr; List_Ite : Buffer_Scheduling_Information_Iterator; Write_Size : Integer := 0; begin -- Check if the task has a buffer to write -- if (Has_Buffer_To_Write (Deps, A_Tcb.Tsk)) then -- for all tasks which have a precedency with the current task -- loop current_element (Deps.Depends, A_Half_Dep_Ptr, My_Iterator); if (A_Half_Dep_Ptr.type_of_dependency = Queueing_Buffer_Dependency) then if (A_Half_Dep_Ptr.buffer_dependent_task.name = A_Tcb.Tsk.name) and (A_Half_Dep_Ptr.buffer_orientation = From_Task_To_Object) then A_Buffer := A_Half_Dep_Ptr.buffer_dependency_object; -- Must we generate Write event ? -- if Event_To_Generate (Write_To_Buffer) then for I in 0 .. A_Buffer.roles.nb_entries - 1 loop -- Is the right task ? -- if (A_Buffer.roles.entries (I).item = A_Tcb.Tsk.name) then -- Does the task is producer ? -- if (A_Buffer.roles.entries (I).data.the_role = Queuing_Producer OR A_Buffer.roles.entries (I).data.the_role = UCSDF_Producer) then -- Is it time to write ? -- if (A_Buffer.roles.entries (I).data.time = (A_Tcb.Tsk.capacity + 1 - A_Tcb.Rest_Of_Capacity)) then -- Add the size write event to the buffer list -- if not is_empty(Si.Written_Buffers) then reset_head_iterator(Si.Written_Buffers, List_Ite); loop current_element(Si.Written_Buffers, A_Buffer_Size, List_Ite); if A_Buffer_Size.Written_Buffer.name = A_Buffer.name then -- Compute the write data size -- if (A_Buffer.roles.entries (I).data.the_role = Queuing_Producer) then Write_Size := A_Buffer.roles.entries (I).data.size; elsif (A_Buffer.roles.entries (I).data.the_role = UCSDF_Producer) then Put_Debug("Amp Function: " & To_String(A_Buffer.roles.entries (I).data.amplitude_function)); Write_Size := Get_Data_Size(To_String(A_Buffer.roles.entries (I).data.amplitude_function), A_Tcb.Activation); end if; -- Check if we can write to the buffer -- if(A_Buffer_Size.Written_Buffer.buffer_size >= A_Buffer_Size.Current_Size + Write_Size) then -- If yes, produce a write event and update buffer current size A_Item := new Time_Unit_Event (Write_To_Buffer); A_Item.write_buffer := A_Buffer; A_Item.write_task := A_Tcb.Tsk; A_Item.write_size := Write_Size; A_Item.write_buffer_current_data_size := A_Buffer_Size.Current_Size; add (Result.all, Current_Time, a_item); A_Buffer_Size.Current_Size := A_Buffer_Size.Current_Size + Write_Size; exit; else -- If no, produce an overflow event A_Item := new Time_Unit_Event (Buffer_Overflow); A_Item.overflow_buffer := A_Buffer; A_Item.overflow_task := A_Tcb.Tsk; A_Item.overflow_write_size := Write_Size; A_Item.overflow_buffer_current_data_size := A_Buffer_Size.Current_Size; add (Result.all, Current_Time, a_item); exit; end if; end if; if is_tail_element(Si.Written_Buffers, List_Ite) then exit; end if; next_element (Si.Written_Buffers, List_Ite); end loop; end if; end if; end if; end if; end loop; end if; end if; end if; exit when is_last_element (Deps.Depends, My_Iterator); next_element (Deps.Depends, My_Iterator); end loop; end if; -- Free objects -- end Buffer_Write; procedure Release_Resource (My_Scheduler : in out Generic_Scheduler; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; A_Tcb : Tcb_Ptr; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is Current_Capacity : Natural := 0; A_Item : Time_Unit_Event_Ptr; begin if (Si.Number_Of_Resources > 0) then Current_Capacity := A_Tcb.Tsk.capacity - A_Tcb.Rest_Of_Capacity + 1; for Index1 in 0 .. Si.Number_Of_Resources - 1 loop -- For each resource, check if the task hold resources -- and then release them -- for Index2 in 0 .. Si.Shared_Resources (Index1).Shared.critical_sections.nb_entries - 1 loop if (Current_Capacity = Si.Shared_Resources (Index1).Shared.critical_sections.entries ( Index2).data.task_end) and (Si.Shared_Resources (Index1).Shared.critical_sections.entries ( Index2).item = A_Tcb.Tsk.name) then Si.Shared_Resources (Index1).Shared.state := Si.Shared_Resources (Index1).Shared.state + 1; for I in 0 .. Si.Shared_Resources (Index1).Nb_Allocated - 1 loop if Si.Shared_Resources (Index1).Allocated_By (I) = A_Tcb.Tsk.name then Si.Shared_Resources (Index1).Allocated_By (I) := Si.Shared_Resources (Index1).Allocated_By ( Si.Shared_Resources (Index1).Nb_Allocated - 1); Si.Shared_Resources (Index1).Nb_Allocated := Si.Shared_Resources (Index1).Nb_Allocated - 1; if Event_To_Generate (Release_Resource) then A_Item := new Time_Unit_Event (Release_Resource); A_Item.release_resource := Si.Shared_Resources (Index1).Shared; A_Item.release_task := A_Tcb.Tsk; add (Result.all, Current_Time, a_item); end if; exit; end if; end loop; end if; end loop; end loop; end if; end Release_Resource; procedure Allocate_Resource (My_Scheduler : in out Generic_Scheduler; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; A_Tcb : Tcb_Ptr; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is Current_Capacity : Natural := 0; A_Item : Time_Unit_Event_Ptr; begin if (Si.Number_Of_Resources > 0) then Current_Capacity := A_Tcb.Tsk.capacity - A_Tcb.Rest_Of_Capacity + 1; for Index1 in 0 .. Si.Number_Of_Resources - 1 loop -- For each resource, check if the task request the resource -- and if the resource is free -- otherwise, block the task -- for Index2 in 0 .. Si.Shared_Resources (Index1).Shared.critical_sections.nb_entries - 1 loop if (Current_Capacity = Si.Shared_Resources (Index1).Shared.critical_sections.entries ( Index2).data.task_begin) and (Si.Shared_Resources (Index1).Shared.critical_sections.entries ( Index2).item = A_Tcb.Tsk.name) then Si.Shared_Resources (Index1).Shared.state := Si.Shared_Resources (Index1).Shared.state - 1; Si.Shared_Resources (Index1).Allocated_By ( Si.Shared_Resources (Index1).Nb_Allocated) := A_Tcb.Tsk.name; Si.Shared_Resources (Index1).Nb_Allocated := Si.Shared_Resources (Index1).Nb_Allocated + 1; if Event_To_Generate (Allocate_Resource) then A_Item := new Time_Unit_Event (Allocate_Resource); A_Item.allocate_resource := Si.Shared_Resources (Index1).Shared; A_Item.allocate_task := A_Tcb.Tsk; add (Result.all, Current_Time, a_item); end if; end if; end loop; end loop; end if; end Allocate_Resource; procedure Check_Resource (My_Scheduler : in out Generic_Scheduler; Si : in out Scheduling_Information; Result : in out Scheduling_Sequence_Ptr; Current_Time : in Natural; A_Tcb : in Tcb_Ptr; Is_Ready : out Boolean; Event_To_Generate : in Time_Unit_Event_Type_Boolean_Table) is Current_Capacity : Natural := 0; begin Is_Ready := True; if (Si.Number_Of_Resources > 0) then Current_Capacity := A_Tcb.Tsk.capacity - A_Tcb.Rest_Of_Capacity + 1; for Index1 in 0 .. Si.Number_Of_Resources - 1 loop -- For each resource, check if the task request the resource -- and if the resource is free -- for Index2 in 0 .. Si.Shared_Resources (Index1).Shared.critical_sections.nb_entries - 1 loop if (Current_Capacity = Si.Shared_Resources (Index1).Shared.critical_sections.entries ( Index2).data.task_begin) and (Si.Shared_Resources (Index1).Shared.critical_sections.entries ( Index2).item = A_Tcb.Tsk.name) then if (Si.Shared_Resources (Index1).Shared.state = 0) then Is_Ready := False; -- If the task is blocked due to a resource, we store this data -- in its Tcb -- put_debug(to_string(a_tcb.tsk.name) & " is waiting for " & to_string(Si.Shared_Resources (Index1).Shared.name) ); a_tcb.wait_for_a_resource:=Si.Shared_Resources (Index1).Shared; end if; end if; end loop; end loop; end if; end Check_Resource; procedure Set_Quantum (My_Scheduler : in out Generic_Scheduler'Class; Q : in Natural) is begin My_Scheduler.parameters.quantum := Q; end Set_Quantum; function Get_Quantum (My_Scheduler : in Generic_Scheduler'Class) return Natural is begin return My_Scheduler.parameters.quantum; end Get_Quantum; function Get_Quantum (My_Scheduler : in Generic_Scheduler'Class) return Unbounded_String is begin return To_Unbounded_String (My_Scheduler.parameters.quantum'Img); end Get_Quantum; function Get_Quantum (My_Scheduler : in Generic_Scheduler'Class) return String is begin return My_Scheduler.parameters.quantum'Img; end Get_Quantum; function Build_Resource (My_Scheduler : in Generic_Scheduler; A_Resource : Generic_Resource_Ptr) return Shared_Resource_Ptr is New_A_Resource : Shared_Resource_Ptr; begin New_A_Resource := new Shared_Resource; New_A_Resource.Shared := A_Resource; return New_A_Resource; end Build_Resource; procedure Initialize (S : in out Scheduling_Information) is begin S.Number_Of_Tasks := 0; S.Number_Of_Resources := 0; end Initialize; -- If this function return "True", it means that -- the task "A_Tcb" can be selected because of all its -- predecessors have ended their activation -- function Check_Precedencies (Si : in Scheduling_Information; Deps : Tasks_Dependencies_Ptr; Current_Time : in Natural; A_Tcb : Tcb_Ptr) return Boolean is Has_Message_To_Receive : Boolean := False; Wait_Message : Boolean := False; Previous : Tasks_Set; Left : Generic_Task_Ptr; Ite1 : Tasks_Iterator; A_Half_Dep_Ptr : Dependency_Ptr; A_Message : Generic_Message_Ptr; My_Iterator : Tasks_Dependencies_Iterator; List_Ite : Message_Scheduling_Information_Iterator; Msg : Message_Scheduling_Information_Ptr; nbr_message : integer := 0 ; nbr_received_message : integer := 0 ; Firstmessage : Boolean := False ; Allmessages : Boolean := False ; begin ------------------------------------------------- -- Check if the task has a task precedency and -- if they are met ------------------------------------------------- if Has_Predecessor (Deps, A_Tcb.Tsk) then reset (Previous); Previous := Get_Predecessors_List (Deps, A_Tcb.Tsk); reset_iterator (Previous, Ite1); for J in 0 .. get_number_of_elements (Previous) - 1 loop current_element (Previous, Left, Ite1); for I in 0 .. Si.Number_Of_Tasks - 1 loop if Si.Tcbs (I).Tsk.name = Left.name then if (Si.Tcbs (I).Activation <= A_Tcb.Activation) then return False; end if; -- Same activation ... be sure the previous task -- is ended -- if (Si.Tcbs (I).Activation = A_Tcb.Activation + 1) and (Si.Tcbs (I).End_Time > Current_Time) then return False; end if; end if; end loop; next_element (Previous, Ite1); end loop; end if; ------------------------------------------------- -- Check communication dependencies first -- A "receiver" has to read at most one message -- to be activated ------------------------------------------------- if not is_empty (Deps.Depends) then reset_iterator (Deps.Depends, My_Iterator); loop current_element (Deps.Depends, A_Half_Dep_Ptr, My_Iterator); -- Is the task concerned ? -- Is it a communication dependency ? -- Is it a receiving operation ? -- if (A_Half_Dep_Ptr.type_of_dependency = asynchronous_Communication_Dependency) then ------------------------------ -- First Message ---- ------------------------------ if (A_Half_Dep_Ptr.asynchronous_communication_dependent_task.name = A_Tcb.Tsk.name) and (A_Half_Dep_Ptr.asynchronous_communication_orientation = From_Object_To_Task) and (A_Half_Dep_Ptr.asynchronous_communication_protocol_property = First_Message ) then Firstmessage := True ; A_Message := A_Half_Dep_Ptr.asynchronous_communication_dependency_object; -- Is The task waiting for a message ? -- if (A_Tcb.Rest_Of_Capacity = A_Tcb.Tsk.capacity) then Wait_Message := True; end if; -- Check the sended_messages to see if this -- messages is just arrived -- if not is_empty (Si.Sended_Messages) then reset_head_iterator (Si.Sended_Messages, List_Ite); loop current_element (Si.Sended_Messages, Msg, List_Ite); -- Is it the right message ? -- Is the message arrived at the receiving processor ? -- if Wait_Message then if (Msg.Sended_Message.name = A_Message.name) then if (Current_Time >= (Msg.Send_Time + A_Message.response_time)) then Has_Message_To_Receive := True; end if; end if; end if; if is_tail_element (Si.Sended_Messages, List_Ite) then exit; end if; next_element (Si.Sended_Messages, List_Ite); end loop; end if; end if; ------------------------------ -- All Message ---- ------------------------------ if (A_Half_Dep_Ptr.asynchronous_communication_dependent_task.name = A_Tcb.Tsk.name) and (A_Half_Dep_Ptr.asynchronous_communication_orientation = From_Object_To_Task) and (A_Half_Dep_Ptr.asynchronous_communication_protocol_property = All_Messages ) then Allmessages := True ; nbr_message := nbr_message + 1; A_Message := A_Half_Dep_Ptr.asynchronous_communication_dependency_object; -- Is The task waiting for a message ? -- if (A_Tcb.Rest_Of_Capacity = A_Tcb.Tsk.capacity) then Wait_Message := True; end if; -- Check the sended_messages to see if this -- messages is just arrived -- if not is_empty (Si.Sended_Messages) then reset_head_iterator (Si.Sended_Messages, List_Ite); loop current_element (Si.Sended_Messages, Msg, List_Ite); -- Is it the right message ? -- Is the message arrived at the receiving processor ? -- if Wait_Message then if (Msg.Sended_Message.name = A_Message.name) then if (Current_Time >= (Msg.Send_Time + A_Message.response_time)) then Has_Message_To_Receive := True; nbr_received_message := nbr_received_message + 1 ; end if; end if; end if; if is_tail_element (Si.Sended_Messages, List_Ite) then exit; end if; next_element (Si.Sended_Messages, List_Ite); end loop; end if; end if; end if; exit when is_last_element (Deps.Depends, My_Iterator); next_element (Deps.Depends, My_Iterator); end loop; end if; -- If the task wait for a message which is not -- arrived, do not wake up it -- if Wait_Message and (not Has_Message_To_Receive) and Firstmessage then return False; end if; if Wait_Message and (nbr_received_message < nbr_message) and Allmessages then return False; end if; -- Free resources -- Free(Previous); free_container(Previous); -- All dependency constraints are met -- return True; end Check_Precedencies; procedure Compute_Activation_Time (My_Scheduler : in Generic_Scheduler; Si : in out Scheduling_Information; Elected : in Tasks_Range; Value : in out Natural) is begin Value := 0; end Compute_Activation_Time; procedure Put (M : in Message_Scheduling_Information_Ptr) is begin Put ("Message " & To_String (M.Sended_Message.name)); Put_Line (" ; send_time = " & M.Send_Time'Img); end Put; procedure Put (M : in Buffer_Scheduling_Information_Ptr) is begin Put ("Buffer " & To_String (M.Written_Buffer.name)); Put_Line (" ; current_size = " & M.Current_Size'Img); end Put; function Produce_Running_Task_Event (My_Scheduler : in Generic_Scheduler; A_Task : in Tcb_Ptr) return Time_Unit_Event_Ptr is A_Item : Time_Unit_Event_Ptr; Cache_State : Unbounded_String := empty_string; begin A_Item := new Time_Unit_Event (Running_Task); A_Item.running_task := A_Task.Tsk; A_Item.current_priority := 0; A_Item.running_core := My_Scheduler.corresponding_core_unit.name; A_Item.CRPD := A_Task.CRPD_capacity; for i in 0..A_Task.UCBs_In_Cache.Size-1 loop Append(Cache_State,To_Unbounded_String(" " & A_Task.UCBs_In_Cache.Elements(i)'Img)); end loop; A_Item.cache_state := Cache_State; return A_Item; end Produce_Running_Task_Event; function Export_Xml_Event_Write_To_Buffer (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.write_buffer.name & " " & An_Event.write_task.name & " " & An_Event.write_size'Img; return Result; end Export_Xml_Event_Write_To_Buffer; function Export_Xml_Event_Read_From_Buffer (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.read_buffer.name & " " & An_Event.read_task.name & " " & An_Event.read_size'Img; return Result; end Export_Xml_Event_Read_From_Buffer; function Export_Xml_Event_Running_Task (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.running_task.name & " " & An_Event.running_core; return Result; end Export_Xml_Event_Running_Task; function Export_Xml_Event_context_switch_overhead (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.switched_task.name; return Result; end Export_Xml_Event_context_switch_overhead; function Export_Xml_Event_address_space_Activation (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.activation_address_space; return Result; end Export_Xml_Event_address_space_Activation; function Export_Xml_Event_Task_Activation (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.activation_task.name; return Result; end Export_Xml_Event_Task_Activation; function Export_Xml_Event_Start_Of_Task_Capacity (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.start_task.name; return Result; end Export_Xml_Event_Start_Of_Task_Capacity; function Export_Xml_Event_End_Of_Task_Capacity (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.end_task.name; return Result; end Export_Xml_Event_End_Of_Task_Capacity; function Export_Xml_Event_Send_Message (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.send_message.name & " " & An_Event.send_task.name; return Result; end Export_Xml_Event_Send_Message; function Export_Xml_Event_Receive_Message (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.receive_message.name & " " & An_Event.receive_task.name; return Result; end Export_Xml_Event_Receive_Message; function Export_Xml_Event_Allocate_Resource (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.allocate_resource.name & " " & An_Event.allocate_task.name; return Result; end Export_Xml_Event_Allocate_Resource; function Export_Xml_Event_Release_Resource (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.release_resource.name & " " & An_Event.release_task.name; return Result; end Export_Xml_Event_Release_Resource; function Export_Xml_Event_Wait_For_Resource (My_Scheduler : in Generic_Scheduler; An_Event : in Time_Unit_Event_Ptr) return Unbounded_String is Result : Unbounded_String := empty_string; begin Result := " " & An_Event.wait_for_resource.name & " " & An_Event.wait_for_resource_task.name; return Result; end Export_Xml_Event_Wait_For_Resource; function XML_String (obj : in Generic_Scheduler_Ptr) return Unbounded_String is begin return XML_String (obj.parameters); end XML_String; --Added by Gaudel -- function Copy (obj : Buffer_Scheduling_Information_Ptr) return Buffer_Scheduling_Information_Ptr is ret : Buffer_Scheduling_Information_Ptr; begin ret := new Buffer_Scheduling_Information; ret.all := obj.all; return ret; end Copy; function Copy (obj : Message_Scheduling_Information_Ptr) return Message_Scheduling_Information_Ptr is ret : Message_Scheduling_Information_Ptr; begin ret := new Message_Scheduling_Information; ret.all := obj.all; return ret; end Copy; function Check_Core_Assignment (A_Scheduler : in Generic_Scheduler; A_Tcb : in Tcb_Ptr) return Boolean is begin -- monocore processeur : return always true -- if (A_Scheduler.corresponding_processor.processor_type = monocore_type) then return true; end if; -- The task never migrates => it can be run on only one core -- if (A_Scheduler.corresponding_processor.migration_type = no_migration_type) then return false; end if; -- The scheduler is not preemptive and the task has started to run : -- the task cannot migrate -- during one of its activation : it works as a job level migration policy -- if (A_Tcb.assigned_core_unit /= null) and (A_Scheduler.parameters.preemptive_type = not_preemptive) then return false; end if; -- The task does not started yet to run ... it can migrate -- if (A_Tcb.assigned_core_unit = null) then return true; end if; -- The task has already run on a core (on the A_Tcb.assigned_core_unit core) -- and is not allowed to migrate due to its migration policy before the end of -- its current job/activation -- if (A_Tcb.assigned_core_unit.name /= A_Scheduler.corresponding_core_unit.name) and (A_Scheduler.corresponding_processor.migration_type = job_level_Migration_Type) then return false; end if; return true; end Check_Core_Assignment; procedure Build_Attributes_XML_String (obj : in Generic_Scheduler_Ptr; result : in out Unbounded_String) is begin Build_Attributes_XML_String (obj.parameters, result); end Build_Attributes_XML_String; function XML_String(obj : in Buffer_Scheduling_Information_Ptr) return Unbounded_String is begin return empty_string; end xml_string; function XML_String(obj : in Message_Scheduling_Information_Ptr) return Unbounded_String is begin return empty_string; end xml_string; procedure Put(obj : in entity_scheduler) is begin null; end put; procedure Put(obj : in entity_scheduler_Ptr) is begin null; end put; function XML_String(obj : in entity_scheduler) return Unbounded_String is begin return empty_string; end XML_String; function XML_String(obj : in entity_scheduler_Ptr) return Unbounded_String is begin return empty_string; end XML_String; end Scheduler;