Commit c72e411d by Michael Schmid

Minor changes and bugfixes, import amalthea model

parent 51cc596f
# -*- coding: utf-8 -*-
import sys
import os
sys.path.insert(0, os.path.abspath(
os.path.join(os.path.dirname(__file__), '..')))
import simso # nopep8
#!/usr/bin/python3
"""
Example of a script that uses SimSo.
"""
import sys
import random
from numpy import arange
from context import simso
from simso.core import Model
from simso.configuration import Configuration
from simso.generator.task_generator import gen_periods_uniform, UUniFastDiscard, gen_tasksets, gen_faults, gen_arrivals
import pickle
def main(argv):
# Manual configuration:
schedulers = ["./simso/schedulers/RM.py", "./simso/schedulers/EDF.py","./simso/schedulers/P_RM.py","./simso/schedulers/P_EDF.py"]
# schedulers = []
configuration = Configuration()
configuration.cycles_per_ms = 1000
configuration.etm = "wcet"
configuration.duration = 1000 * configuration.cycles_per_ms
num_processors = 8
# Add a processor:
for np in range(0, num_processors):
configuration.add_processor(name=f"CPU {np}" , identifier=np)
nsets = 20
for u in arange(1,num_processors +0.1, 0.5):
print(f"Utilization = {u}")
utilizations = UUniFastDiscard(20, u, nsets)
periods = gen_periods_uniform(20, nsets, 2, 100)
tasksets = gen_tasksets(utilizations, periods)
missed_deadlines = {}
for sched in schedulers:
missed_deadlines[sched] = 0
for n in range(nsets):
configuration._task_info_list = []
for t in tasksets[n]:
identifier = tasksets[n].index(t)
configuration.add_task(name="t" + str(identifier), identifier=identifier,period=t[1],wcet=t[0],deadline=t[1])
faults = gen_faults(n_faults=5,task_info_list=configuration._task_info_list,processor_info_list=configuration._proc_info_list, sim_duration=configuration.duration_ms)
for f in faults:
configuration.add_fault(fault_type=f['fault_type'], start_time=f['start_time'], end_time=f['end_time'],
fault_target=f['fault_target'], parameter=f.get('parameter'), value=f.get('value'))
for sched in schedulers:
configuration.scheduler_info.filename = sched
# Check the config before trying to run it.
configuration.check_all()
# Init a model from the configuration.
model = Model(configuration)
# Execute the simulation.
try:
model.run_model()
except RuntimeError as e:
if e.args[0] != "Bin packing failed!":
raise e
missed_deadlines[sched] += 1
except AssertionError:
missed_deadlines[sched] += 1
except ValueError:
with open('failed_configuration.pkl', 'wb') as out:
pickle.dump(configuration, out, pickle.HIGHEST_PROTOCOL)
if model.results is not None and model.results.total_exceeded_count > 0:
missed_deadlines[sched] += 1
for sched in schedulers:
print(f"{sched}: {missed_deadlines[sched]}")
# # # Print logs.
# # for log in model.logs:
# # print(log)
main(sys.argv)
\ No newline at end of file
...@@ -5,42 +5,23 @@ Example of a script that uses SimSo. ...@@ -5,42 +5,23 @@ Example of a script that uses SimSo.
""" """
import sys import sys
from context import simso
from simso.core import Model from simso.core import Model
from simso.core.Fault import ProcessorFailure, VoltageDrop, PriorityInversion, TimingError from simso.core.Fault import ProcessorFailure, VoltageDrop, PriorityInversion, TimingError
from simso.configuration import Configuration from simso.configuration import Configuration
from simso.configuration.parser import AmaltheaModelParser
import pickle
def main(argv): def main(argv):
if len(argv) == 2: # Manual configuration:
# Configuration load from a file. configuration = Configuration("./misc/mobstr.amxmi")
configuration = Configuration(argv[1])
else:
# Manual configuration:
configuration = Configuration()
configuration.cycles_per_ms = 1 configuration.cycles_per_ms = 1000
configuration.duration = 100 * configuration.cycles_per_ms configuration.duration = 100 * configuration.cycles_per_ms
# Add tasks: configuration.scheduler_info.clas = "simso.schedulers.RM"
configuration.add_task(name="T1", identifier=1, period=8,
activation_date=0, wcet=4, deadline=7)
configuration.add_task(name="T2", identifier=2, period=12,
activation_date=0, wcet=3, deadline=8)
configuration.add_task(name="T3", identifier=3, period=15,
activation_date=0, wcet=5, deadline=14)
task = configuration.add_task(name="T4", identifier=4, period=20,
activation_date=0, wcet=6, deadline=12)
# Add a processor:
configuration.add_processor(name="CPU 1", identifier=1)
proc = configuration.add_processor(name="CPU 2", identifier=2)
configuration.add_fault(TimingError(task, "period", 8), 0, 21)
# Add a scheduler:
#configuration.scheduler_info.filename = "../simso/schedulers/RM.py"
configuration.scheduler_info.clas = "simso.schedulers.EDF"
# Check the config before trying to run it. # Check the config before trying to run it.
configuration.check_all() configuration.check_all()
...@@ -57,3 +38,9 @@ def main(argv): ...@@ -57,3 +38,9 @@ def main(argv):
main(sys.argv) main(sys.argv)
# configuration = None
# with open('failed_configuration.pkl','rb') as input:
# configuration = pickle.load(input)
...@@ -5,6 +5,7 @@ Example of a script that uses SimSo. ...@@ -5,6 +5,7 @@ Example of a script that uses SimSo.
""" """
import sys import sys
from context import simso
from simso.core import Model from simso.core import Model
from simso.configuration import Configuration from simso.configuration import Configuration
...@@ -17,7 +18,7 @@ def main(argv): ...@@ -17,7 +18,7 @@ def main(argv):
# Manual configuration: # Manual configuration:
configuration = Configuration() configuration = Configuration()
configuration.cycles_per_ms = 1 configuration.cycles_per_ms = 1000000
configuration.etm = "pwcet" configuration.etm = "pwcet"
...@@ -38,8 +39,7 @@ def main(argv): ...@@ -38,8 +39,7 @@ def main(argv):
# configuration.add_processor(name="CPU 2", identifier=2) # configuration.add_processor(name="CPU 2", identifier=2)
# Add a scheduler: # Add a scheduler:
# configuration.scheduler_info.filename = "./simso/schedulers/RM.py" configuration.scheduler_info.filename = "./simso/schedulers/RM.py"
configuration.scheduler_info.clas = "simso.schedulers.RM"
# Check the config before trying to run it. # Check the config before trying to run it.
configuration.check_all() configuration.check_all()
......
#!/usr/bin/python3
"""
Example of a script that uses SimSo.
"""
import sys
from simso.core import Model
from simso.configuration import Configuration
def main(argv):
if len(argv) == 2:
# Configuration load from a file.
configuration = Configuration(argv[1])
else:
# Manual configuration:
configuration = Configuration()
configuration.cycles_per_ms = 1
configuration.etm = "pwcet"
configuration.duration = 100 * configuration.cycles_per_ms
# Add tasks:
configuration.add_task(name="T1", identifier=1,
activation_date=0, pwcet=[(2, 0.5), (4, 0.5)], pmit=[(5, 0.2), (6, 0.8)], deadline=5, task_type="Probabilistic", abort_on_miss=True)
configuration.add_task(name="T2", identifier=2,
activation_date=0, pwcet=[(3, 0.5), (4, 0.5)], pmit=[(7, 0.5), (8, 0.5)], deadline=7, task_type="Probabilistic", abort_on_miss=True)
configuration.add_task(name="T3", identifier=3,
activation_date=0, pwcet=[(3, 0.5), (4, 0.5)], pmit=[(8, 0.5), (9, 0.5)], deadline=8, task_type="Probabilistic", abort_on_miss=True)
# Add a processor:
configuration.add_processor(name="CPU 1", identifier=1)
# configuration.add_processor(name="CPU 2", identifier=2)
# Add a scheduler:
configuration.scheduler_info.filename = "./simso/schedulers/RM.py"
# Check the config before trying to run it.
configuration.check_all()
# Init a model from the configuration.
model = Model(configuration)
# Execute the simulation.
model.run_model()
# Print logs.
for log in model.logs:
print(log)
main(sys.argv)
...@@ -17,7 +17,7 @@ setup( ...@@ -17,7 +17,7 @@ setup(
'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering',
'Development Status :: 5 - Production/Stable' 'Development Status :: 5 - Production/Stable'
], ],
python_requires='>=3.6', python_requires='>=3.10',
packages=find_packages(), packages=find_packages(),
install_requires=[ install_requires=[
'SimPy>=4.0.1', 'SimPy>=4.0.1',
......
...@@ -14,7 +14,7 @@ from simso.generator.task_generator import gen_probabilistic_arrivals ...@@ -14,7 +14,7 @@ from simso.generator.task_generator import gen_probabilistic_arrivals
from simso.generator.task_generator import cdf from simso.generator.task_generator import cdf
from .GenerateConfiguration import generate from .GenerateConfiguration import generate
from .parser import Parser from .parser import Parser, AmaltheaModelParser
# Hack for Python2 # Hack for Python2
...@@ -48,7 +48,24 @@ class Configuration(object): ...@@ -48,7 +48,24 @@ class Configuration(object):
Args: Args:
- `filename` A file can be used to initialize the configuration. - `filename` A file can be used to initialize the configuration.
""" """
if filename: if filename and any(x in filename for x in [".amxmi", ".amxml"]):
parser = AmaltheaModelParser(filename)
self.etm = "wcet"
self.duration = 100
self.cycles_per_ms = 1
self._caches_list = []
self.memory_access_time = 0
self._task_info_list = parser.task_info_list
self.task_data_fields = []
self._proc_info_list = parser.proc_info_list
self.proc_data_fields = []
self._scheduler_info = parser.scheduler_info
self.penalty_preemption = 0
self.penalty_migration = 0
self.fault_info_list = []
self.fault_data_fields=[]
self.mapping_dict = parser.task_mappings
elif filename:
parser = Parser(filename) parser = Parser(filename)
self.etm = parser.etm self.etm = parser.etm
self.duration = parser.duration self.duration = parser.duration
...@@ -64,6 +81,7 @@ class Configuration(object): ...@@ -64,6 +81,7 @@ class Configuration(object):
self.penalty_migration = parser.penalty_migration self.penalty_migration = parser.penalty_migration
self.fault_info_list = parser.fault_info_list self.fault_info_list = parser.fault_info_list
self.fault_data_fields = parser.fault_data_fields self.fault_data_fields = parser.fault_data_fields
self.mapping_dict = parser.mapping_dict # TODO:
else: else:
self.etm = "wcet" self.etm = "wcet"
self.duration = 100000000 self.duration = 100000000
...@@ -79,6 +97,7 @@ class Configuration(object): ...@@ -79,6 +97,7 @@ class Configuration(object):
self.fault_data_fields = {} self.fault_data_fields = {}
self.memory_access_time = 100 self.memory_access_time = 100
self._scheduler_info = SchedulerInfo() self._scheduler_info = SchedulerInfo()
self.mapping_dict = None
self.calc_penalty_cache() self.calc_penalty_cache()
self._set_filename(filename) self._set_filename(filename)
...@@ -288,7 +307,7 @@ class Configuration(object): ...@@ -288,7 +307,7 @@ class Configuration(object):
def add_task(self, name, identifier, task_type="Periodic", def add_task(self, name, identifier, task_type="Periodic",
abort_on_miss=True, period=10, activation_date=0, abort_on_miss=True, period=10, activation_date=0,
n_instr=0, mix=0.5, stack_file="", wcet=1, acet=1, pwcet=(1, 1.0), pmit=(0, 1.0), n_instr=0, mix=0.5, stack_file="", wcet=1, acet=1, pwcet=[(1, 1.0)], pmit=[(10, 1.0)],
et_stddev=0, deadline=10, base_cpi=1.0, followed_by=None, et_stddev=0, deadline=10, base_cpi=1.0, followed_by=None,
list_activation_dates=[], preemption_cost=0, data=None): list_activation_dates=[], preemption_cost=0, data=None):
""" """
...@@ -299,7 +318,7 @@ class Configuration(object): ...@@ -299,7 +318,7 @@ class Configuration(object):
if task_type == "Probabilistic": if task_type == "Probabilistic":
list_activation_dates = gen_probabilistic_arrivals( list_activation_dates = gen_probabilistic_arrivals(
pmit, activation_date, self.duration, False) pmit, activation_date, self.duration_ms, False)
pwcet = cdf(pwcet) pwcet = cdf(pwcet)
task = TaskInfo(name, identifier, task_type, abort_on_miss, period, task = TaskInfo(name, identifier, task_type, abort_on_miss, period,
...@@ -322,11 +341,17 @@ class Configuration(object): ...@@ -322,11 +341,17 @@ class Configuration(object):
self.proc_info_list.append(proc) self.proc_info_list.append(proc)
return proc return proc
def add_fault(self, fault_type: str, start_time, end_time, target, value=None, parameter=None): def add_fault(self, fault_type: str, start_time, end_time, fault_target, value=None, parameter=None):
""" """
Helper method to create a FaultInfo and add it to the list of faults. Helper method to create a FaultInfo and add it to the list of faults.
""" """
fault_info = FaultInfo(fault_type, start_time, fault_info = FaultInfo(fault_type, start_time,
end_time, target, value, parameter) end_time, fault_target, value, parameter)
self.fault_info_list.append(fault_info) self.fault_info_list.append(fault_info)
return fault_info return fault_info
def add_mappings(self, mapping_dict):
"""
Add mappings in the form of Dict[task_info, List[processor_info]]
"""
self.mapping_dict= mapping_dict
\ No newline at end of file
# coding=utf-8 # coding=utf-8
from xml.etree import ElementTree
from xml.dom.minidom import parse from xml.dom.minidom import parse
import os.path import os.path
from simso.core.Task import TaskInfo, task_types from simso.core.Task import TaskInfo, task_types
...@@ -40,6 +41,7 @@ class Parser(object): ...@@ -40,6 +41,7 @@ class Parser(object):
def _parse_faults(self): def _parse_faults(self):
# TODO: implement parser for faults # TODO: implement parser for faults
self.fault_info_list = [] self.fault_info_list = []
self.fault_data_fields = {}
def _parse_caches(self): def _parse_caches(self):
self.caches_list = [] self.caches_list = []
...@@ -254,3 +256,171 @@ class Parser(object): ...@@ -254,3 +256,171 @@ class Parser(object):
if filename and filename[0] != '/': if filename and filename[0] != '/':
filename = self.cur_dir + '/' + filename filename = self.cur_dir + '/' + filename
self.scheduler_info.filename = filename self.scheduler_info.filename = filename
class AmaltheaModelParser:
def __init__(self, filename):
self.filename = filename
self.cur_dir = os.path.split(filename)[0]
if not self.cur_dir:
self.cur_dir = '.'
tree = ElementTree.parse(filename)
self.root = tree.getroot()
self._parse_processors()
self._parse_scheduler()
# Tasks have to be parsed after processors due to allocation strategies
self._parse_tasks()
def _parse_tasks(self):
self.task_info_list = []
self.task_mappings = {}
identifier = 0
for tag in self.root.findall('swModel/tasks'):
name = tag.attrib['name']
process = f"{tag.attrib['name']}?type=Task"
activations = []
stimuli = self.root.find(f"stimuliModel/stimuli/[@name='{tag.attrib['stimuli'].split('?')[0]}']")
if stimuli.attrib['{http://www.w3.org/2001/XMLSchema-instance}type'] == 'am:PeriodicStimulus':
task_type = "Periodic"
period = stimuli.find(f"[@name='{tag.attrib['stimuli'].split('?')[0]}']/recurrence")
if period is not None:
unit = period.attrib['unit']
period = int(period.attrib['value'])
if unit == 's':
period *= 1000
elif unit == 'us':
period /=1000
elif unit == 'ns':
period /= 1000000
elif unit == 'ps':
period /= 1000000000
elif stimuli.attrib['{http://www.w3.org/2001/XMLSchema-instance}type'] == 'am:ArrivalCurveStimulus':
task_type = 'Sporadic'
activations= stimuli.find(f"[@name='{tag.attrib['stimuli'].split('?')[0]}']/entries")
# TODO: find example
else:
continue
deadline = self.root.find(f"constraintsModel/requirements/[@process='{process}']/limit/limitValue")
if deadline is not None:
deadline = int(deadline.attrib['value'])
else:
deadline = period
priority = self.root.find(f"mappingModel/taskAllocation/[@task='{process}']/schedulingParameters")
if priority is not None:
priority = int(priority.attrib['priority'])
affinities = next(iter(self.root.findall(f"mappingModel/taskAllocation/[@task='{process}']"))).attrib['affinity']
affinities = affinities.split(' ')
affinity_list = []
for i in affinities:
proc = next((p for p in self.proc_info_list if i.split('?')[0] in p.name), None)
affinity_list.append(proc)
wcet = 0
acet = 0
for r in tag.findall(f"./activityGraph/items/items/[@{{http://www.w3.org/2001/XMLSchema-instance}}type='am:RunnableCall']"):
frequency_domain_list = set([p.name.split('_')[1] for p in affinity_list])
processor = affinity_list[0]
if len(frequency_domain_list) > 1:
processor = min(affinity_list, key=lambda x: x.speed)
pname = processor.name.split('_')[1]+ '?type=ProcessingUnitDefinition'
runnable= r.attrib['runnable'].split('?')[0]
runnable = self.root.find(f"swModel/runnables/[@name='{runnable}']/activityGraph/items/[@{{http://www.w3.org/2001/XMLSchema-instance}}type='am:Ticks']/extended/[@key='{pname}']/value")
if runnable is not None:
if runnable.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']== 'am:DiscreteValueConstant':
wcet += float(runnable.attrib['value'])
acet += float(runnable.attrib['value'])
elif runnable.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']== 'am:DiscreteValueStatistics':
wcet += float(runnable.attrib['upperBound'])
acet += float(runnable.attrib['average'])
# wcet[ms] = Ticks (=wcet before) / frequency[Hz] * 1000 [s -> ms]
wcet = wcet/processor.data['speed'] * 1000
acet = acet/processor.data['speed'] * 1000
task = TaskInfo(name=name,
identifier=identifier,
task_type=task_type,
abort_on_miss=True,
period=period,
activation_date=0,
n_instr = 0,
mix =0,
stack_file=("", self.cur_dir),
wcet = wcet,
acet= acet,
pwcet=None,
et_stddev = 0,
deadline=deadline,
base_cpi=0,
followed_by=None,
list_activation_dates=activations,
preemption_cost=0,
data = {'priority': priority})
identifier += 1
self.task_info_list.append(task)
self.task_mappings[task] = affinity_list
def _parse_scheduler(self):
scheduler = self.root.find("osModel/operatingSystems/taskSchedulers/schedulingAlgorithm")
clas = "simso.schedulers.RM"
if scheduler is not None:
scheduler = scheduler.attrib['{http://www.w3.org/2001/XMLSchema-instance}type']
match scheduler:
case "am:FixedPriorityPreemptive":
clas = "simso.schedulers.C_FP"
case "am:EarliestDeadlineFirst":
clas = "simso.schedulers.EDF"
case "am:PfairPD2":
clas = "simso.schedulers.PD2"
case "am:EarlyReleaseFairPD2":
clas = "simso.schedulers.ER_PD2"
case _:
pass
self.scheduler_info = SchedulerInfo(
clas=clas, overhead=0, overhead_activate=0,
overhead_terminate=0, fields=None)
def _parse_processors(self):
self.proc_info_list = []
num_proc = 0
for tag in self.root.findall("hwModel/definitions/[@puType='CPU']"):
for core in self.root.findall(f"hwModel/structures/structures/modules/[@definition='{tag.attrib['name']}?type=ProcessingUnitDefinition']"):
f_domain = self.root.find(f"hwModel/domains/[@name='{core.attrib['frequencyDomain'].split('?')[0]}']/defaultValue")
unit = f_domain.attrib['unit']
speed = float(f_domain.attrib['value'])
if unit == 'GHz':
speed *= 1000000000
elif unit == 'MHz':
speed *= 1000000
elif unit == 'KHz':
speed *= 1000
proc = ProcInfo(name= core.attrib['name']+ "_" + tag.attrib['name'],
identifier=num_proc,
speed=speed, data={'speed':speed})
self.proc_info_list.append(proc)
num_proc+=1
# adjust speed to range in 0 - 1
max_speed = max(p.speed for p in self.proc_info_list)
for p in self.proc_info_list:
p.speed = p.speed / max_speed
\ No newline at end of file
...@@ -3,6 +3,7 @@ from typing import Callable ...@@ -3,6 +3,7 @@ from typing import Callable
from simso.core.Processor import ProcInfo from simso.core.Processor import ProcInfo
from simso.core.Monitor import Monitor from simso.core.Monitor import Monitor
from simso.core.FaultEvent import ProcessorFailureEvent, VoltageChangeEvent, TimingChangeEvent from simso.core.FaultEvent import ProcessorFailureEvent, VoltageChangeEvent, TimingChangeEvent
from simso.core.JobEvent import JobEvent
class FaultFunction: class FaultFunction:
...@@ -50,11 +51,11 @@ class Fault: ...@@ -50,11 +51,11 @@ class Fault:
@property @property
def duration_ms(self): def duration_ms(self):
return self._fault_info.start_time - self._fault_info.end_time return self._fault_info.end_time - self._fault_info.start_time
@property @property
def duration_cycles(self): def duration_cycles(self):
return (self._fault_info.start_time - self._fault_info.end_time) * self._sim.cycles_per_ms return self.duration_ms * self._sim.cycles_per_ms
def release(self): def release(self):
yield self._sim.timeout(self._fault_info.start_time * self._sim.cycles_per_ms) yield self._sim.timeout(self._fault_info.start_time * self._sim.cycles_per_ms)
...@@ -62,7 +63,7 @@ class Fault: ...@@ -62,7 +63,7 @@ class Fault:
self._start_function(self._sim) self._start_function(self._sim)
yield self._sim.timeout((self._fault_info.end_time - yield self._sim.timeout((self._fault_info.end_time -
self._fault_info.start_time) * self._sim.y) self._fault_info.start_time) * self._sim.cycles_per_ms)
self._end_function(self._sim) self._end_function(self._sim)
...@@ -195,9 +196,8 @@ fault_types = { ...@@ -195,9 +196,8 @@ fault_types = {
"ProcessorFailure": ProcessorFailure "ProcessorFailure": ProcessorFailure
} }
fault_types_names = ["TimingError", "TimingChange", "Redundancy", fault_types_names = ["TimingError", "VoltageDrop", "ProcessorFailure"]
"VoltageChange", "VoltageDrop", "ProcessorFailure"] # "TimingChange", "Redundancy", "VoltageChange",
def FaultFactory(sim, fault_info): def FaultFactory(sim, fault_info):
""" """
......
...@@ -296,7 +296,12 @@ class Job: ...@@ -296,7 +296,12 @@ class Job:
def activate_job(self): def activate_job(self):
self._start_date = self._sim.now self._start_date = self._sim.now
# Notify the OS. # Notify the OS.
self._task.cpu.activate(self) if self._task.cpu.has_failure():
proc = next(p for p in self._sim.processors if not p.has_failure())
proc.activate(self)
proc.resched()
else:
self._task.cpu.activate(self)
# While the job's execution is not finished. # While the job's execution is not finished.
while self._end_date is None: while self._end_date is None:
......
...@@ -61,6 +61,7 @@ class Model(Environment): ...@@ -61,6 +61,7 @@ class Model(Environment):
proc.caches = proc_info.caches proc.caches = proc_info.caches
self._processors.append(proc) self._processors.append(proc)
self._fault_list = [] self._fault_list = []
for fault_info in fault_info_list: for fault_info in fault_info_list:
self._fault_list.append(FaultFactory(self, fault_info)) self._fault_list.append(FaultFactory(self, fault_info))
...@@ -78,6 +79,7 @@ class Model(Environment): ...@@ -78,6 +79,7 @@ class Model(Environment):
self._callback = callback self._callback = callback
self.scheduler.task_list = self._task_list self.scheduler.task_list = self._task_list
self.scheduler.processors = self._processors self.scheduler.processors = self._processors
self.scheduler.configuration = configuration
self.results = None self.results = None
def now_ms(self): def now_ms(self):
......
...@@ -98,8 +98,17 @@ class Processor: ...@@ -98,8 +98,17 @@ class Processor:
self._running = job self._running = job
def fail(self, fault=None): def fail(self, fault=None):
self._evts.append((FAIL, fault)) self._fault = fault
self._fault = fault # TODO: check if I need this if self._fault is not None:
self.sched.processors.remove(self)
self._evts.append((FAIL, fault))
elif not self in self.sched.processors:
self.sched.processors.append(self)
# self.resched() # TODO: changed from preempt, check if still works
def has_failure(self):
return self._fault is not None
def timer(self, timer): def timer(self, timer):
self._evts.append((TIMER, timer)) self._evts.append((TIMER, timer))
...@@ -208,14 +217,8 @@ class Processor: ...@@ -208,14 +217,8 @@ class Processor:
elif evt[0] == SPEED: elif evt[0] == SPEED:
self._speed = evt[1] self._speed = evt[1]
elif evt[0] == FAIL: elif evt[0] == FAIL:
if self._fault is not None: self.monitor.observe(ProcFailedEvent(self._fault))
self.sched.processors.remove(self) yield self._model.timeout(self._fault.duration_cycles)
self.monitor.observe(ProcFailedEvent(self._fault))
# self._model.timeout()
elif not self in self.sched.processors:
self.sched.processors.append(self)
self.resched() # TODO: changed from preempt, check if still works
elif evt[0] == RESCHED: elif evt[0] == RESCHED:
self.monitor.observe(ProcOverheadEvent("Scheduling")) self.monitor.observe(ProcOverheadEvent("Scheduling"))
self.sched.monitor_begin_schedule(self) self.sched.monitor_begin_schedule(self)
......
# coding=utf-8 # coding=utf-8
import simpy
from simpy import Process
from collections import deque from collections import deque
# from SimPy.Simulation import Process, Monitor, hold, passivate # from SimPy.Simulation import Process, Monitor, hold, passivate
from simso.core.Job import Job from simso.core.Job import Job
...@@ -66,7 +64,7 @@ class TaskInfo(object): ...@@ -66,7 +64,7 @@ class TaskInfo(object):
self.list_activation_dates = list_activation_dates self.list_activation_dates = list_activation_dates
self.data = data self.data = data
self.preemption_cost = preemption_cost self.preemption_cost = preemption_cost
self._pwcet = pwcet self.pwcet = pwcet
@property @property
def csdp(self): def csdp(self):
...@@ -263,15 +261,18 @@ class GenericTask: ...@@ -263,15 +261,18 @@ class GenericTask:
self.followed_by.create_job(job) self.followed_by.create_job(job)
if len(self._activations_fifo) > 0: if len(self._activations_fifo) > 0:
self._activations_fifo.popleft() # self._activations_fifo.popleft()
self._activations_fifo.remove(job)
if len(self._activations_fifo) > 0: if len(self._activations_fifo) > 0:
self.job = self._activations_fifo[0] self.job = self._activations_fifo[0]
self.job.process = self._sim.process(self.job.activate_job()) self.job.process = self._sim.process(self.job.activate_job())
def _job_killer(self, job): def _job_killer(self, job):
if job.end_date is None and job.computation_time < job.wcet: if job.end_date is None and job.actual_computation_time < job.wcet:
if self._task_info.abort_on_miss: if self._task_info.abort_on_miss:
# self.cancel(job) TODO: How to cancel Job? # self.cancel(job) TODO: How to cancel Job?
# if job.process and job.process.is_alive:
# job.process.interrupt(self)
job.abort() job.abort()
def create_job(self, pred=None): def create_job(self, pred=None):
...@@ -354,10 +355,9 @@ class ProbabilisticTask(GenericTask): ...@@ -354,10 +355,9 @@ class ProbabilisticTask(GenericTask):
Probabilistic Task process. Inherits from :class:`GenericTask`. The jobs are Probabilistic Task process. Inherits from :class:`GenericTask`. The jobs are
created using a list of probabilistic activation times. created using a list of probabilistic activation times.
""" """
fields = ['list_activation_dates', 'deadline', 'pwcet', 'pmit'] fields = ['period', 'activation_date', 'deadline', 'wcet']
def execute(self): def execute(self):
self._init() self._init()
for ndate in self.list_activation_dates: for ndate in self.list_activation_dates:
yield self._sim.timeout(int(ndate * self._sim.cycles_per_ms) yield self._sim.timeout(int(ndate * self._sim.cycles_per_ms)
...@@ -370,7 +370,7 @@ class ProbabilisticTask(GenericTask): ...@@ -370,7 +370,7 @@ class ProbabilisticTask(GenericTask):
@property @property
def pwcet(self): def pwcet(self):
return self._task_info._pwcet return self._task_info.pwcet
task_types = { task_types = {
......
...@@ -22,7 +22,7 @@ class PWCET(AbstractExecutionTimeModel): ...@@ -22,7 +22,7 @@ class PWCET(AbstractExecutionTimeModel):
def on_activate(self, job): def on_activate(self, job):
self.executed[job] = 0 self.executed[job] = 0
job.wcet = next(x[0] for x in job.task.pwcet if random.uniform( job.wcet = next(x[0] for x in job.task.pwcet if random.uniform(
0, 1) <= x[1]) * self.sim.cycles_per_ms 0, 1) <= x[1]) # * self.sim.cycles_per_ms
def on_execute(self, job): def on_execute(self, job):
self.on_execute_date[job] = self.sim.now self.on_execute_date[job] = self.sim.now
......
from simso.core.etm.AbstractExecutionTimeModel \ from simso.core.etm.AbstractExecutionTimeModel \
import AbstractExecutionTimeModel import AbstractExecutionTimeModel
from math import ceil
class WCET(AbstractExecutionTimeModel): class WCET(AbstractExecutionTimeModel):
def __init__(self, sim, _): def __init__(self, sim, _):
...@@ -42,8 +43,7 @@ class WCET(AbstractExecutionTimeModel): ...@@ -42,8 +43,7 @@ class WCET(AbstractExecutionTimeModel):
def get_ret(self, job): def get_ret(self, job):
wcet_cycles = int(job.wcet * self.sim.cycles_per_ms) wcet_cycles = int(job.wcet * self.sim.cycles_per_ms)
# TODO: this leads to rounding errors, use ceil? return ceil(wcet_cycles - self.get_executed(job))
return int(wcet_cycles - self.get_executed(job))
def update(self): def update(self):
for job in list(self.on_execute_date.keys()): for job in list(self.on_execute_date.keys()):
......
...@@ -6,6 +6,8 @@ import numpy as np ...@@ -6,6 +6,8 @@ import numpy as np
import random import random
import math import math
from functools import reduce from functools import reduce
from simso.core.Fault import TimingError, ProcessorFailure, VoltageDrop
from simso.core.Task import task_types
def UUniFastDiscard(n, u, nsets): def UUniFastDiscard(n, u, nsets):
...@@ -341,3 +343,50 @@ def gen_tasksets(utilizations, periods): ...@@ -341,3 +343,50 @@ def gen_tasksets(utilizations, periods):
return [[(trunc(ui * pi, 6), trunc(pi, 6)) for ui, pi in zip(us, ps)] return [[(trunc(ui * pi, 6), trunc(pi, 6)) for ui, pi in zip(us, ps)]
for us, ps in zip(utilizations, periods)] for us, ps in zip(utilizations, periods)]
def gen_faultset(arrivals, durations, fault_types):
faults = []
for a in arrivals:
f = random.choice(fault_types)
d = random.choice(durations)
faults.append((f, a, a + d))
return faults
def gen_faults(n_faults, task_info_list, processor_info_list, sim_duration):
arrivals = []
for i in range(n_faults):
arrivals.append(random.uniform(0, sim_duration))
durations = gen_periods_uniform(len(arrivals), 1, 2, 5)[0]
fault_set = []
for i in range(len(arrivals)):
a = arrivals[i]
d = durations[i]
f = random.choice(["TimingError", "VoltageDrop"]) #"ProcessorFailure",
if f == 'ProcessorFailure':
p = random.choice(processor_info_list)
fault_set.append({'fault_type': f, 'start_time':a, 'end_time':a+d, 'fault_target': p})
elif f == 'TimingError':
t = random.choice(task_info_list)
p = random.choice(task_types[t.task_type].fields)
v = getattr(t, p)
# TODO: gaussian distribution parameter with v
ve = random.uniform(v-0.15*v, v+0.15*v)
fault_set.append({'fault_type': f, 'start_time':a, 'end_time':a+d, 'fault_target': t, 'parameter':p, 'value':v})
elif f == 'VoltageDrop':
p = random.choice(processor_info_list)
v = random.uniform(0.5, 0.9)
fault_set.append({'fault_type': f, 'start_time':a, 'end_time':a+d, 'fault_target': p, 'value':v})
return fault_set
\ No newline at end of file
from simso.core import Scheduler
from simso.schedulers import scheduler
@scheduler("simso.schedulers.C_FP", required_task_fields = [
{'name': 'priority', 'type' : 'int', 'default' : '0' }
]
)
class C_FP(Scheduler):
""" Clustered Fixed-Priority """
def init(self):
self.ready_list = []
self.mappings_dict = {}
for k,v in self.configuration.mapping_dict.items():
task = next(t for t in self.task_list if t._task_info == k)
cpu_list = [p for p in self.processors if p.identifier in [pi.identifier for pi in v]]
self.mappings_dict[task] = cpu_list
task.cpu = self.mappings_dict[task][0]
def on_activate(self, job):
self.ready_list.append(job)
job.cpu.resched()
def on_terminated(self, job):
if job in self.ready_list:
self.ready_list.remove(job)
else:
job.cpu.resched()
def schedule(self, cpu):
if self.ready_list:
# Get the job with the highest priority.
job = max(self.ready_list, key=lambda x: x.data['priority'])
# Get a free processor or a processor running a low priority job.
key = lambda x: (
1 if x.running else 0,
x.running.data['priority'] if x.running else 0,
0 if x is cpu else 1
)
cpu_min = min(self.mappings_dict[job.task], key=key)
if (cpu_min.running is None or
cpu_min.running.data['priority'] < job.data['priority']):
self.ready_list.remove(job)
if cpu_min.running:
self.ready_list.append(cpu_min.running)
return (job, cpu_min)
return None
\ No newline at end of file
from simso.core import Scheduler
from simso.schedulers import scheduler
@scheduler("simso.schedulers.C_RM"
)
class C_RM(Scheduler):
""" Clustered Rate-Monotonic """
def init(self):
self.ready_list = []
self.mappings_dict = {}
for k,v in self.configuration.mapping_dict.items():
task = next(t for t in self.task_list if t._task_info == k)
cpu_list = [p for p in self.processors if p.identifier in [pi.identifier for pi in v]]
self.mappings_dict[task] = cpu_list
def on_activate(self, job):
self.ready_list.append(job)
job.cpu.resched()
def on_terminated(self, job):
if job in self.ready_list:
self.ready_list.remove(job)
else:
job.cpu.resched()
def schedule(self, cpu):
decision = None
if self.ready_list:
# Job with highest priority.
job = min(self.ready_list, key=lambda x: x.period)
# Get a free processor or a processor running a low priority job.
key = lambda x: (
0 if x.running is None else 1,
-x.running.period if x.running else 0,
0 if x is cpu else 1
)
cpu_min = min(self.mappings_dict[job.task], key=key)
if (cpu_min.running is None or
cpu_min.running.period > job.period):
self.ready_list.remove(job)
if cpu_min.running:
self.ready_list.append(cpu_min.running)
decision = (job, cpu_min)
return decision
\ No newline at end of file
...@@ -3,6 +3,7 @@ Partitionned EDF without the helping class. ...@@ -3,6 +3,7 @@ Partitionned EDF without the helping class.
Use EDF_mono. Use EDF_mono.
""" """
from simpy import Resource
from simso.core import Scheduler from simso.core import Scheduler
from simso.core.Scheduler import SchedulerInfo from simso.core.Scheduler import SchedulerInfo
from simso.schedulers.EDF_mono import EDF_mono from simso.schedulers.EDF_mono import EDF_mono
...@@ -16,6 +17,9 @@ class P_EDF2(Scheduler): ...@@ -16,6 +17,9 @@ class P_EDF2(Scheduler):
# Mapping task to scheduler. # Mapping task to scheduler.
self.map_task_sched = {} self.map_task_sched = {}
self._lock = Resource(self.sim)
self.request = None
cpus = [] cpus = []
for cpu in self.processors: for cpu in self.processors:
# Append the processor to a list with an initial utilization of 0. # Append the processor to a list with an initial utilization of 0.
...@@ -36,7 +40,8 @@ class P_EDF2(Scheduler): ...@@ -36,7 +40,8 @@ class P_EDF2(Scheduler):
while cpus[j][1] + float(task.wcet) / task.period > 1.0: while cpus[j][1] + float(task.wcet) / task.period > 1.0:
j += 1 j += 1
if j >= len(self.processors): if j >= len(self.processors):
print("oops bin packing failed.") # print("oops bin packing failed.")
raise RuntimeError("Bin packing failed!")
return return
# Get the scheduler for this processor. # Get the scheduler for this processor.
...@@ -55,7 +60,9 @@ class P_EDF2(Scheduler): ...@@ -55,7 +60,9 @@ class P_EDF2(Scheduler):
def get_lock(self): def get_lock(self):
# No lock mechanism is needed. # No lock mechanism is needed.
return True self._lock.release(self.request)
self.request = self._lock.request()
return self.request
def schedule(self, cpu): def schedule(self, cpu):
return self.map_cpu_sched[cpu.identifier].schedule(cpu) return self.map_cpu_sched[cpu.identifier].schedule(cpu)
......
from simpy import Resource
from simso.core import Scheduler from simso.core import Scheduler
...@@ -17,7 +18,7 @@ def best_fit(scheduler, task_list=None): ...@@ -17,7 +18,7 @@ def best_fit(scheduler, task_list=None):
while cpus[j][1] * task.period + float(task.wcet) > task.period: while cpus[j][1] * task.period + float(task.wcet) > task.period:
j += 1 j += 1
if j >= len(scheduler.processors): if j >= len(scheduler.processors):
print("oops bin packing failed.") # print("oops bin packing failed.")
return False return False
# Affect it to the task. # Affect it to the task.
...@@ -48,7 +49,7 @@ def worst_fit(scheduler, task_list=None): ...@@ -48,7 +49,7 @@ def worst_fit(scheduler, task_list=None):
while cpus[j][1] * task.period + float(task.wcet) > task.period: while cpus[j][1] * task.period + float(task.wcet) > task.period:
j += 1 j += 1
if j >= len(scheduler.processors): if j >= len(scheduler.processors):
print("oops bin packing failed.") # print("oops bin packing failed.")
return False return False
# Affect it to the task. # Affect it to the task.
...@@ -80,7 +81,7 @@ def next_fit(scheduler, task_list=None): ...@@ -80,7 +81,7 @@ def next_fit(scheduler, task_list=None):
j = (j + 1) % len(scheduler.processors) j = (j + 1) % len(scheduler.processors)
k += 1 k += 1
if k >= len(scheduler.processors): if k >= len(scheduler.processors):
print("oops bin packing failed.") # print("oops bin packing failed.")
return False return False
# Affect it to the task. # Affect it to the task.
...@@ -109,7 +110,7 @@ def first_fit(scheduler, task_list=None): ...@@ -109,7 +110,7 @@ def first_fit(scheduler, task_list=None):
while cpus[j][1] * task.period + float(task.wcet) > task.period: while cpus[j][1] * task.period + float(task.wcet) > task.period:
j += 1 j += 1
if j >= len(scheduler.processors): if j >= len(scheduler.processors):
print("oops bin packing failed.") # print("oops bin packing failed.")
return False return False
# Affect it to the task. # Affect it to the task.
...@@ -177,6 +178,8 @@ class PartitionedScheduler(Scheduler): ...@@ -177,6 +178,8 @@ class PartitionedScheduler(Scheduler):
"PartitionedScheduler requires a monoprocessor scheduler to " \ "PartitionedScheduler requires a monoprocessor scheduler to " \
"instantiate." "instantiate."
self._lock = Resource(self.sim)
self.request = None
# Mapping processor to scheduler. # Mapping processor to scheduler.
self.map_cpu_sched = {} self.map_cpu_sched = {}
# Mapping task to scheduler. # Mapping task to scheduler.
...@@ -211,7 +214,9 @@ class PartitionedScheduler(Scheduler): ...@@ -211,7 +214,9 @@ class PartitionedScheduler(Scheduler):
def get_lock(self): def get_lock(self):
# No lock mechanism is needed. # No lock mechanism is needed.
return True self._lock.release(self.request)
self.request = self._lock.request()
return self.request
def schedule(self, cpu): def schedule(self, cpu):
return self.map_cpu_sched[cpu.identifier].schedule(cpu) return self.map_cpu_sched[cpu.identifier].schedule(cpu)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment