Commit 51cc596f by Michael Schmid

Much work on faults and fault events

parent b2352129
......@@ -9,7 +9,7 @@ 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.core.Fault import FaultInfo, FaultFactory
from simso.generator.task_generator import gen_probabilistic_arrivals
from simso.generator.task_generator import cdf
......@@ -63,6 +63,7 @@ class Configuration(object):
self.penalty_preemption = parser.penalty_preemption
self.penalty_migration = parser.penalty_migration
self.fault_info_list = parser.fault_info_list
self.fault_data_fields = parser.fault_data_fields
else:
self.etm = "wcet"
self.duration = 100000000
......@@ -75,6 +76,7 @@ class Configuration(object):
self._proc_info_list = []
self.proc_data_fields = {}
self.fault_info_list = []
self.fault_data_fields = {}
self.memory_access_time = 100
self._scheduler_info = SchedulerInfo()
self.calc_penalty_cache()
......@@ -193,20 +195,20 @@ class Configuration(object):
"Activation date must be positive."
# Period >= 0:
assert task.period >= 0, "Tasks' periods must be positives."
assert task.period > 0, "Tasks' periods must be positives."
# Deadline >= 0:
assert task.deadline >= 0, "Tasks' deadlines must be positives."
assert task.deadline > 0, "Tasks' deadlines must be positives."
# N_instr >= 0:
assert task.n_instr >= 0, \
"A number of instructions must be positive."
# WCET >= 0:
assert task.wcet >= 0, "WCET must be positive."
assert task.wcet > 0, "WCET must be positive."
# ACET >= 0:
assert task.acet >= 0, "ACET must be positive."
assert task.acet > 0, "ACET must be positive."
# ET-STDDEV >= 0:
assert task.et_stddev >= 0, \
......@@ -286,7 +288,7 @@ class Configuration(object):
def add_task(self, name, identifier, task_type="Periodic",
abort_on_miss=True, period=10, activation_date=0,
n_instr=0, mix=0.5, stack_file="", wcet=0, acet=0, pwcet=(0, 1.0), pmit=(0, 1.0),
n_instr=0, mix=0.5, stack_file="", wcet=1, acet=1, pwcet=(1, 1.0), pmit=(0, 1.0),
et_stddev=0, deadline=10, base_cpi=1.0, followed_by=None,
list_activation_dates=[], preemption_cost=0, data=None):
"""
......@@ -320,9 +322,11 @@ class Configuration(object):
self.proc_info_list.append(proc)
return proc
def add_fault(self, fault_function, start_time, end_time):
def add_fault(self, fault_type: str, start_time, end_time, target, value=None, parameter=None):
"""
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)
fault_info = FaultInfo(fault_type, start_time,
end_time, target, value, parameter)
self.fault_info_list.append(fault_info)
return fault_info
from simpy import Environment
from typing import Callable
from simso.core.Processor import ProcInfo
from simso.core.Monitor import Monitor
from simso.core.FaultEvent import ProcessorFailureEvent, VoltageChangeEvent, TimingChangeEvent
class FaultFunction:
......@@ -18,72 +20,91 @@ class FaultInfo:
The FaultType class is responsible for causing the error.
"""
def __init__(self, start_time: float, end_time: float, fault_function: FaultFunction):
def __init__(self, fault_type: str, start_time: float, end_time: float,
fault_target, value, parameter: str):
self.fault_type = fault_type
self.start_time = start_time
self.end_time = end_time
self.fault_function = fault_function
self.fault_target = fault_target
self.value = value
self.parameter = parameter
class Fault:
"""
The Fault class is the simpy process for triggering a certain error.
The Fault base class is the simpy process for triggering a certain error.
"""
def __init__(self, sim, fault_type: FaultInfo):
self._fault_type = fault_type
def __init__(self, sim, fault_info: FaultInfo, start_function: Callable, end_function: Callable):
self._start_function = start_function
self._end_function = end_function
self._fault_info = fault_info
self._sim = sim
self._monitor = Monitor(name="Monitor" + fault_info.fault_type,
env=sim)
self.process = self._sim.process(self.release())
@property
def fault_info(self):
return self._fault_info
@property
def duration_ms(self):
return self._fault_info.start_time - self._fault_info.end_time
@property
def duration_cycles(self):
return (self._fault_info.start_time - self._fault_info.end_time) * self._sim.cycles_per_ms
def release(self):
yield self._sim.timeout(self._fault_type.start_time)
yield self._sim.timeout(self._fault_info.start_time * self._sim.cycles_per_ms)
self._fault_type.fault_function.start_function(self._sim)
self._start_function(self._sim)
yield self._sim.timeout(self._fault_type.end_time -
self._fault_type.start_time)
yield self._sim.timeout((self._fault_info.end_time -
self._fault_info.start_time) * self._sim.y)
self._fault_type.fault_function.end_function(self._sim)
self._end_function(self._sim)
class ProcessorFailure(FaultFunction):
# TODO: Does not work when only 1 processor in model, maybe use speed = 0 ?
class ProcessorFailure(Fault):
"""
The ProcessorFailure class simulates a complete stoppage of a processor.
"""
fields = ['fault_target']
def __init__(self, proc_info: ProcInfo):
super().__init__(self.stop_processor, self.start_processor)
self._proc_info = proc_info
def __init__(self, sim, fault_info):
super().__init__(sim, fault_info, self.stop_processor, self.start_processor)
self._processor = None
def stop_processor(self, model):
self._processor = next(
p for p in model.processors if p.identifier == self._proc_info.identifier)
p for p in model.processors if p.identifier == self._fault_info.fault_target.identifier)
self._processor.fail(self)
self._monitor.observe(ProcessorFailureEvent(self))
def start_processor(self, model):
self._processor.fail()
class VoltageChange(FaultFunction):
class VoltageChange(Fault):
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
fields = ['fault_target', 'value']
def __init__(self, sim, fault_info):
super().__init__(sim, fault_info, self.start_voltage_change, self.end_voltage_change)
self._processor = None
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)
self._processor = next(
p for p in model.processors if p.identifier == self._fault_info.fault_target.identifier)
self._processor.set_speed(
self._fault_info.value * self._processor.speed)
self._monitor.observe(VoltageChangeEvent(self))
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)
self._processor.set_speed(self._fault_info.fault_target.speed)
class VoltageDrop(VoltageChange):
......@@ -91,9 +112,9 @@ 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)
def __init__(self, sim, fault_info):
assert fault_info.value >= 0 and fault_info.value <= 1.0, "Voltage drop reduces the speed of a processor, i.e. scaling has to be >= 0 and <= 1"
super().__init__(sim, fault_info)
# TODO: rethink this class
......@@ -126,23 +147,26 @@ class PriorityInversion(FaultFunction):
pass
class TimingChange(FaultFunction):
class TimingChange(Fault):
"""
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)
fields = ['fault_target', 'parameter', 'value']
def __init__(self, sim, fault_info):
super().__init__(sim, fault_info, self.start_timing_change, self.end_timing_change)
self._old_value = getattr(
self._fault_info.fault_target, fault_info.parameter)
def start_timing_change(self, model):
setattr(self._task_info, self._parameter, self._value)
setattr(self._fault_info.fault_target, self._fault_info.parameter,
self._fault_info.value)
self._monitor.observe(TimingChangeEvent(self))
def end_timing_change(self, model):
setattr(self._task_info, self._parameter, self._old_value)
setattr(self._fault_info.fault_target,
self._fault_info.parameter, self._old_value)
class TimingError(TimingChange):
......@@ -150,11 +174,34 @@ 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)
def __init__(self, sim, fault_info):
super().__init__(sim, fault_info)
class Redundancy(TimingChange):
def __init__(self, task_info, value: float):
super().__init__(task_info, "wcet", task_info.wcet + value)
def __init__(self, sim, fault_info):
fault_info.parameter = "wcet"
fault_info.value = fault_info.fault_target.wcet + fault_info.value
super().__init__(sim, fault_info)
fault_types = {
"TimingError": TimingError,
"TimingChange": TimingChange,
"Redundancy": Redundancy,
"VoltageChange": VoltageChange,
"VoltageDrop": VoltageDrop,
"ProcessorFailure": ProcessorFailure
}
fault_types_names = ["TimingError", "TimingChange", "Redundancy",
"VoltageChange", "VoltageDrop", "ProcessorFailure"]
def FaultFactory(sim, fault_info):
"""
Task factory. Return and instantiate the correct class according to the
task_info.
"""
return fault_types[fault_info.fault_type](sim, fault_info)
class FaultEvent:
PROCESSOR_FAILURE = 1
VOLTAGE_DROP = 2
VOLTAGE_CHANGE = 3
TIMING_ERROR = 4
TIMING_CHANGE = 5
REDUNDANCY = 6
def __init__(self, event=0, args=None):
self.event = event
self.args = args
class ProcessorFailureEvent(FaultEvent):
def __init__(self, fault):
FaultEvent.__init__(self, FaultEvent.PROCESSOR_FAILURE, fault)
class VoltageChangeEvent(FaultEvent):
def __init__(self, fault):
FaultEvent.__init__(self, FaultEvent.VOLTAGE_CHANGE, fault)
class TimingChangeEvent(FaultEvent):
def __init__(self, fault):
FaultEvent.__init__(self, FaultEvent.TIMING_CHANGE, fault)
......@@ -8,7 +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
from simso.core.Fault import FaultFactory
class Model(Environment):
......@@ -63,7 +63,7 @@ class Model(Environment):
self._fault_list = []
for fault_info in fault_info_list:
self._fault_list.append(Fault(self, fault_info))
self._fault_list.append(FaultFactory(self, fault_info))
# XXX: too specific.
self.penalty_preemption = configuration.penalty_preemption
......@@ -124,6 +124,13 @@ class Model(Environment):
return self._task_list
@property
def fault_list(self):
"""
List of all faults
"""
return self._fault_list
@property
def duration(self):
"""
Duration of the simulation.
......@@ -140,12 +147,6 @@ class Model(Environment):
self.scheduler.init()
self.progress.start()
# for cpu in self._processors:
# self.process(cpu.run())
# for task in self._task_list:
# self.process(task.execute())
try:
self.run(until=self._duration)
finally:
......
......@@ -5,6 +5,7 @@ class ProcEvent(object):
RUN = 1
IDLE = 2
OVERHEAD = 3
FAILED = 4
def __init__(self, event=0, args=None):
self.event = event
......@@ -20,6 +21,9 @@ class ProcIdleEvent(ProcEvent):
def __init__(self):
ProcEvent.__init__(self, ProcEvent.IDLE)
class ProcFailedEvent(ProcEvent):
def __init__(self, fault):
ProcEvent.__init__(self, ProcEvent.FAILED, fault)
class ProcOverheadEvent(ProcEvent):
def __init__(self, type_overhead):
......
......@@ -3,7 +3,7 @@
from collections import deque
from simpy import Process
# from SimPy.Simulation import Process, Monitor, hold, waituntil
from simso.core.ProcEvent import ProcRunEvent, ProcIdleEvent, \
from simso.core.ProcEvent import ProcRunEvent, ProcIdleEvent, ProcFailedEvent, \
ProcOverheadEvent, ProcCxtSaveEvent, ProcCxtLoadEvent
from simso.core.Monitor import Monitor
from simso.core.EventQueue import EventQueue
......@@ -210,6 +210,8 @@ class Processor:
elif evt[0] == FAIL:
if self._fault is not None:
self.sched.processors.remove(self)
self.monitor.observe(ProcFailedEvent(self._fault))
# self._model.timeout()
elif not self in self.sched.processors:
self.sched.processors.append(self)
......
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