Commit b2352129 by Michael Schmid

added Faults and minor changes

parent 9aed003b
#!/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"
configuration.scheduler_info.clas = "simso.schedulers.RM"
# 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)
......@@ -6,6 +6,7 @@ Example of a script that uses SimSo.
import sys
from simso.core import Model
from simso.core.Fault import ProcessorFailure, VoltageDrop, PriorityInversion, TimingError
from simso.configuration import Configuration
......@@ -22,22 +23,24 @@ def main(argv):
configuration.duration = 100 * configuration.cycles_per_ms
# Add tasks:
configuration.add_task(name="T1", identifier=1, period=7,
activation_date=0, wcet=3, deadline=7)
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="T4", identifier=4, period=15,
configuration.add_task(name="T3", identifier=3, period=15,
activation_date=0, wcet=5, deadline=14)
configuration.add_task(name="T3", identifier=3, period=20,
activation_date=0, wcet=5, deadline=9)
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, cs_overhead=1)
configuration.add_processor(name="CPU 2", identifier=2, cs_overhead=1)
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.RM"
configuration.scheduler_info.clas = "simso.schedulers.EDF"
# Check the config before trying to run it.
configuration.check_all()
......
......@@ -35,7 +35,7 @@ def main(argv):
# Add a processor:
configuration.add_processor(name="CPU 1", identifier=1)
configuration.add_processor(name="CPU 2", identifier=2)
# configuration.add_processor(name="CPU 2", identifier=2)
# Add a scheduler:
configuration.scheduler_info.filename = "./simso/schedulers/RM.py"
......
......@@ -4,10 +4,12 @@
import os
import re
from xml.dom import minidom
from functools import reduce
from simso.core.Scheduler import SchedulerInfo
from simso.core import Scheduler
from simso.core.Task import TaskInfo
from simso.core.Processor import ProcInfo
from simso.core.Fault import FaultInfo, FaultFunction
from simso.generator.task_generator import gen_probabilistic_arrivals
from simso.generator.task_generator import cdf
......@@ -22,7 +24,7 @@ if not hasattr(minidom.NamedNodeMap, '__contains__'):
def _gcd(*numbers):
"""Return the greatest common divisor of the given integers"""
from fractions import gcd
from math import gcd
return reduce(gcd, numbers)
......@@ -60,6 +62,7 @@ class Configuration(object):
self._scheduler_info = parser.scheduler_info
self.penalty_preemption = parser.penalty_preemption
self.penalty_migration = parser.penalty_migration
self.fault_info_list = parser.fault_info_list
else:
self.etm = "wcet"
self.duration = 100000000
......@@ -71,6 +74,7 @@ class Configuration(object):
self.task_data_fields = {}
self._proc_info_list = []
self.proc_data_fields = {}
self.fault_info_list = []
self.memory_access_time = 100
self._scheduler_info = SchedulerInfo()
self.calc_penalty_cache()
......@@ -315,3 +319,10 @@ class Configuration(object):
speed)
self.proc_info_list.append(proc)
return proc
def add_fault(self, fault_function, start_time, end_time):
"""
Helper method to create a FaultInfo and add it to the list of faults.
"""
fault = FaultInfo(start_time, end_time, fault_function)
self.fault_info_list.append(fault)
......@@ -20,6 +20,7 @@ class Parser(object):
"""
Simulation file parser.
"""
def __init__(self, filename):
self.filename = filename
self.cur_dir = os.path.split(filename)[0]
......@@ -34,6 +35,11 @@ class Parser(object):
self._parse_processors()
self._parse_scheduler()
self._parse_penalty()
self._parse_faults()
def _parse_faults(self):
# TODO: implement parser for faults
self.fault_info_list = []
def _parse_caches(self):
self.caches_list = []
......@@ -90,29 +96,31 @@ class Parser(object):
map(float, attr['list_activation_dates'].value.split(',')))
t = TaskInfo(
attr['name'].value,
int(attr['id'].value),
task_type,
'abort_on_miss' not in attr
name=attr['name'].value,
identifier=int(attr['id'].value),
task_type=task_type,
abort_on_miss='abort_on_miss' not in attr
or attr['abort_on_miss'].value == 'yes',
float(attr['period'].value),
float(attr['activationDate'].value)
period=float(attr['period'].value),
activation_date=float(attr['activationDate'].value)
if 'activationDate' in attr else 0,
int(attr['instructions'].value),
float(attr['mix'].value),
(self.cur_dir + '/' + attr['stack'].value,
self.cur_dir) if 'stack' in attr else ("", self.cur_dir),
float(attr['WCET'].value),
float(attr['ACET'].value) if 'ACET' in attr else 0,
float(attr['et_stddev'].value) if 'et_stddev' in attr else 0,
float(attr['deadline'].value),
float(attr['base_cpi'].value),
int(attr['followed_by'].value)
n_instr=int(attr['instructions'].value),
mix=float(attr['mix'].value),
stack_file=(self.cur_dir + '/' + attr['stack'].value,
self.cur_dir) if 'stack' in attr else ("", self.cur_dir),
wcet=float(attr['WCET'].value),
acet=float(attr['ACET'].value) if 'ACET' in attr else 0,
pwcet=float(attr['PWCET'].value) if 'PWCET' in attr else 0,
et_stddev=float(
attr['et_stddev'].value) if 'et_stddev' in attr else 0,
deadline=float(attr['deadline'].value),
base_cpi=float(attr['base_cpi'].value),
followed_by=int(attr['followed_by'].value)
if 'followed_by' in attr else None,
list_activation_dates,
int(float(attr['preemption_cost'].value))
list_activation_dates=list_activation_dates,
preemption_cost=int(float(attr['preemption_cost'].value))
if 'preemption_cost' in attr else 0,
data)
data=data)
self.task_info_list.append(t)
def _parse_processors(self):
......@@ -244,5 +252,5 @@ class Parser(object):
clas=clas, overhead=overhead, overhead_activate=overhead_activate,
overhead_terminate=overhead_terminate, fields=data)
if filename and filename[0] != '/':
filename = self.cur_dir + '/' + filename
filename = self.cur_dir + '/' + filename
self.scheduler_info.filename = filename
from simpy import Environment
from typing import Callable
from simso.core.Processor import ProcInfo
class FaultFunction:
"""
The FaultFunction class changes the actual behaviour of the faulty element.
"""
def __init__(self, start_function: Callable, end_function: Callable):
self.start_function = start_function
self.end_function = end_function
class FaultInfo:
"""
The FaultType class is responsible for causing the error.
"""
def __init__(self, start_time: float, end_time: float, fault_function: FaultFunction):
self.start_time = start_time
self.end_time = end_time
self.fault_function = fault_function
class Fault:
"""
The Fault class is the simpy process for triggering a certain error.
"""
def __init__(self, sim, fault_type: FaultInfo):
self._fault_type = fault_type
self._sim = sim
self.process = self._sim.process(self.release())
def release(self):
yield self._sim.timeout(self._fault_type.start_time)
self._fault_type.fault_function.start_function(self._sim)
yield self._sim.timeout(self._fault_type.end_time -
self._fault_type.start_time)
self._fault_type.fault_function.end_function(self._sim)
class ProcessorFailure(FaultFunction):
"""
The ProcessorFailure class simulates a complete stoppage of a processor.
"""
def __init__(self, proc_info: ProcInfo):
super().__init__(self.stop_processor, self.start_processor)
self._proc_info = proc_info
self._processor = None
def stop_processor(self, model):
self._processor = next(
p for p in model.processors if p.identifier == self._proc_info.identifier)
self._processor.fail(self)
def start_processor(self, model):
self._processor.fail()
class VoltageChange(FaultFunction):
def __init__(self, proc_info_list, scaling: float):
super().__init__(self.start_voltage_change, self.end_voltage_change)
self._proc_info_list = proc_info_list
self._processor_list = []
self._scaling = scaling
def start_voltage_change(self, model):
affected_ids = [p.identifier for p in self._proc_info_list]
self._processor_list = [
p for p in model.processors if p.identifier in affected_ids]
for p in self._processor_list:
p.set_speed(self._scaling * p.speed)
def end_voltage_change(self, model):
for p in self._processor_list:
speed = next(
pi for pi in self._proc_info_list if pi.identifier == p.identifier).speed
p.set_speed(speed)
class VoltageDrop(VoltageChange):
"""
The VoltageDrop class simulates the slowdown of one or more processors due to voltage drops.
"""
def __init__(self, proc_info_list, scaling: float):
assert scaling >= 0 and scaling <= 1.0, "Voltage drop reduces the speed of a processor, i.e. scaling has to be >= 0 and <= 1"
super().__init__(proc_info_list, scaling)
# TODO: rethink this class
class PriorityInversion(FaultFunction):
"""
The PriorityInversion class simulates changes in the priority of a job.
"""
def __init__(self, proc_info, until_sched: bool = False):
"""
lambd: Expects task and new value as parameter and returns old value
"""
super().__init__(self.start_priority_inversion, self.end_priority_inversion)
self._proc_info = proc_info
self._until_sched = until_sched
def start_priority_inversion(self, model):
proc = next(p for p in model.processors if p.identifier ==
self._proc_info.identifier)
sched = model.scheduler
running_jobs = [
p.running for p in model.processors if p.running is not None]
job = next(t.job for t in sched.task_list if t.job.is_active()
and t.job not in running_jobs)
if job is not None:
pass
def end_priority_inversion(self, model):
pass
class TimingChange(FaultFunction):
"""
The TimingError class simulates changes in the timing parameters, e.g. deadline, period etc.
"""
def __init__(self, task_info, parameter: str, value: float):
super().__init__(self.start_timing_change, self.end_timing_change)
self._task_info = task_info
self._parameter = parameter
self._value = value
self._old_value = getattr(self._task_info, parameter)
def start_timing_change(self, model):
setattr(self._task_info, self._parameter, self._value)
def end_timing_change(self, model):
setattr(self._task_info, self._parameter, self._old_value)
class TimingError(TimingChange):
"""
The TimingError class simulates changes in the timing parameters, e.g. deadline, period etc.
"""
def __init__(self, task_info, parameter: str, value: float):
super().__init__(task_info, parameter, value)
class Redundancy(TimingChange):
def __init__(self, task_info, value: float):
super().__init__(task_info, "wcet", task_info.wcet + value)
......@@ -94,6 +94,7 @@ class Job:
self._sim.logger.log(self.name + " Preempted! ret: " +
str(self.ret), kernel=True) # TODO: what to pass as interrupted?
# TODO: Jobs that terminate after their deadline are not recorded
def _on_terminated(self):
self._on_stop_exec()
self._etm.on_terminated(self)
......@@ -322,11 +323,3 @@ class Job:
if ret <= 0:
# End of job.
self._on_terminated()
class SequentialJob(Job):
pass
class ParallelJob(Job):
pass
......@@ -8,6 +8,7 @@ from simso.core.Timer import Timer
from simso.core.etm import execution_time_models
from simso.core.Logger import Logger
from simso.core.results import Results
from simso.core.Fault import Fault
class Model(Environment):
......@@ -31,6 +32,7 @@ class Model(Environment):
self._logger = Logger(self)
task_info_list = configuration.task_info_list
proc_info_list = configuration.proc_info_list
fault_info_list = configuration.fault_info_list
self._cycles_per_ms = configuration.cycles_per_ms
self.scheduler = configuration.scheduler_info.instantiate(self)
......@@ -59,6 +61,10 @@ class Model(Environment):
proc.caches = proc_info.caches
self._processors.append(proc)
self._fault_list = []
for fault_info in fault_info_list:
self._fault_list.append(Fault(self, fault_info))
# XXX: too specific.
self.penalty_preemption = configuration.penalty_preemption
self.penalty_migration = configuration.penalty_migration
......
......@@ -14,6 +14,7 @@ TERMINATE = 3
TIMER = 4
PREEMPT = 5
SPEED = 6
FAIL = 7
class ProcInfo(object):
......@@ -75,6 +76,7 @@ class Processor:
env=model, name="Monitor Timer" + proc_info.name)
self._speed = proc_info.speed
self.process = self._model.process(self.run())
self._fault = None
def resched(self):
"""
......@@ -91,11 +93,14 @@ class Processor:
self._running = None
def preempt(self, job=None):
# self._evts = deque([e for e in self._evts if e[0] != PREEMPT])
self._evts.clear_events(PREEMPT)
self._evts.append((PREEMPT,))
self._running = job
def fail(self, fault=None):
self._evts.append((FAIL, fault))
self._fault = fault # TODO: check if I need this
def timer(self, timer):
self._evts.append((TIMER, timer))
......@@ -202,6 +207,13 @@ class Processor:
evt[1].call_handler()
elif evt[0] == SPEED:
self._speed = evt[1]
elif evt[0] == FAIL:
if self._fault is not None:
self.sched.processors.remove(self)
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:
self.monitor.observe(ProcOverheadEvent("Scheduling"))
self.sched.monitor_begin_schedule(self)
......
......@@ -14,12 +14,12 @@ from simso.core.SchedulerEvent import SchedulerBeginScheduleEvent, \
SchedulerEndTerminateEvent
class SchedulerInfo(object):
"""
SchedulerInfo groups the data that characterize a Scheduler (such as the
scheduling overhead) and do the dynamic loading of the scheduler.
"""
def __init__(self, clas='', overhead=0, overhead_activate=0,
overhead_terminate=0, fields=None):
"""
......@@ -142,7 +142,7 @@ class Scheduler(object):
self.sim = sim
self.processors = []
self.task_list = []
self._lock = Resource(sim)
self._lock = Resource(sim)
self.overhead = scheduler_info.overhead
self.overhead_activate = scheduler_info.overhead_activate
self.overhead_terminate = scheduler_info.overhead_terminate
......@@ -220,7 +220,7 @@ class Scheduler(object):
"""
Release the lock. Goes in pair with :meth:`get_lock`.
"""
self._lock.release(request)
self._lock.release(request)
def monitor_begin_schedule(self, cpu):
self.monitor.observe(SchedulerBeginScheduleEvent(cpu))
......
......@@ -16,7 +16,6 @@ class InstanceTimer:
self.cpu = timer.cpu
self.running = False
self.overhead = timer.overhead
self.process = self._sim.process(self.run())
def call_handler(self):
if self.running:
......
......@@ -42,6 +42,7 @@ class WCET(AbstractExecutionTimeModel):
def get_ret(self, job):
wcet_cycles = int(job.wcet * self.sim.cycles_per_ms)
# TODO: this leads to rounding errors, use ceil?
return int(wcet_cycles - self.get_executed(job))
def update(self):
......
......@@ -8,6 +8,7 @@ class ProcessorR(object):
Add information about a processor such as the number of CxtSave and
CxtLoad and their total overhead.
"""
def __init__(self):
self.context_save_overhead = 0
self.context_save_count = 0
......@@ -20,6 +21,7 @@ class SchedulerR(object):
Add information about the scheduler such as the number of scheduling
events and their total overhead.
"""
def __init__(self):
self.schedule_overhead = 0
self.activate_overhead = 0
......@@ -36,6 +38,7 @@ class TaskR(object):
The attribute jobs contains a list of JobR, sorted by activation date.
"""
def __init__(self, task, delta_preemption=100):
self.task = task
self.delta_preemption = delta_preemption
......@@ -144,6 +147,7 @@ class JobR(object):
Add a set of metrics to a job. Such metrics include: preemption count,
migration count, response time, etc.
"""
def __init__(self, date, job):
self.job = job
self.preemption_count = 0
......@@ -206,6 +210,7 @@ class Results(object):
.
"""
def __init__(self, model):
self.model = model
self.error = None
......@@ -235,7 +240,7 @@ class Results(object):
for task in self.model.task_list:
if indices[task] < len(monitors[task]):
evt = monitors[task][indices[task]]
if m is None or evt[1].id_ < m[0][1].id_:
if m is None or evt[1].id_ < m[0][1].id_: # pylint: disable=E1136
m = (evt, task)
if m is None:
break
......
......@@ -5,9 +5,11 @@ architectures.
from simso.core import Scheduler
from simso.schedulers import scheduler
@scheduler("simso.schedulers.EDF")
class EDF(Scheduler):
"""Earliest Deadline First"""
def on_activate(self, job):
job.cpu.resched()
......@@ -22,7 +24,7 @@ class EDF(Scheduler):
if ready_jobs:
# Select a free processor or, if none,
# the one with the greatest deadline (self in case of equality):
key = lambda x: (
def key(x): return (
1 if not x.running else 0,
x.running.absolute_deadline if x.running else 0,
1 if x is cpu else 0
......@@ -34,5 +36,5 @@ class EDF(Scheduler):
if (cpu_min.running is None or
cpu_min.running.absolute_deadline > job.absolute_deadline):
print(self.sim.now, job.name, cpu_min.name)
# print(self.sim.now, job.name, cpu_min.name)
return (job, cpu_min)
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