Commit b5016fc8 authored by Christian Dietrich's avatar Christian Dietrich

generator/SSE: alternative SSE with APP-FSM

The symbolic system execution is now able to enumerate all possible
system states with two different approaches. Either we can use the "old"
version which uses states with blocks to be executed in the next step,
like it is described in LCTES'15 (Dietrich et. al).

Or we can use the application finite state machines as basis. These
state machines are smaller (lesser nodes) than the application's
CFG. They are based on the task's ICFG.

Change-Id: I05e050ed3f39bd18ecf86b4d7bb423b4ce0825e0
parent 2f69ade0
...@@ -7,6 +7,11 @@ DOSEK_BINARY( ...@@ -7,6 +7,11 @@ DOSEK_BINARY(
a.cc a.cc
) )
# This has to be done, since FSM is currently build with the APP-FSM
# method and not the CFG->GCFG method for SSE.
config_valid(VALID --systemcalls normal)
if(VALID)
DOSEK_BINARY( DOSEK_BINARY(
NAME bcc1_task1a_sse NAME bcc1_task1a_sse
SYSTEM_DESC system.oil SYSTEM_DESC system.oil
...@@ -15,7 +20,8 @@ DOSEK_BINARY( ...@@ -15,7 +20,8 @@ DOSEK_BINARY(
GENERATOR_ARGS -fgen-asserts -fsse GENERATOR_ARGS -fgen-asserts -fsse
TEST_ISO TEST_ISO
a.cc a.cc
) )
endif()
DOSEK_BINARY( DOSEK_BINARY(
NAME bcc1_task1b NAME bcc1_task1b
......
from .common import * from .common import *
from .Analysis import Analysis from .Analysis import Analysis
from .AtomicBasicBlock import E, S from .AtomicBasicBlock import E, S, AtomicBasicBlock
from .Function import Function from .Function import Function
from datastructures.fsm import * from datastructures.fsm import *
from collections import defaultdict from collections import defaultdict
...@@ -32,7 +32,7 @@ class ApplicationFSM(Analysis, GraphObject): ...@@ -32,7 +32,7 @@ class ApplicationFSM(Analysis, GraphObject):
for subtask in self.system_graph.subtasks: for subtask in self.system_graph.subtasks:
wrapper = GraphObjectContainer(str(subtask), color='red') wrapper = GraphObjectContainer(str(subtask), color='red')
nodes = {} nodes = {}
for trans in subtask.conf.fsm.transitions: for trans in subtask.fsm.transitions:
if not trans.source in nodes: if not trans.source in nodes:
nodes[trans.source] = Node(None, str(trans.source), color='black') nodes[trans.source] = Node(None, str(trans.source), color='black')
if not trans.target in nodes: if not trans.target in nodes:
...@@ -54,7 +54,12 @@ class ApplicationFSM(Analysis, GraphObject): ...@@ -54,7 +54,12 @@ class ApplicationFSM(Analysis, GraphObject):
def block_functor(in_edge, cur_abb): def block_functor(in_edge, cur_abb):
transitions = [] transitions = []
# ICFG Entry transition # Here we do the following. Each Edge becomes a state in our finite state machine.
# So from
# ---[in_edge]--> [in_edge.target] --[out_edge]-->
# we make
# [in_edge] --[in_edge.target == ABB]--> [out_edge]
# Entry and exit nodes are handleded differently
if cur_abb == subtask.entry_abb: if cur_abb == subtask.entry_abb:
in_edge = None in_edge = None
out_edge = cur_abb.get_outgoing_edges(self.get_edge_filter())[0] out_edge = cur_abb.get_outgoing_edges(self.get_edge_filter())[0]
...@@ -63,6 +68,12 @@ class ApplicationFSM(Analysis, GraphObject): ...@@ -63,6 +68,12 @@ class ApplicationFSM(Analysis, GraphObject):
for out_edge in cur_abb.get_outgoing_edges(self.get_edge_filter()): for out_edge in cur_abb.get_outgoing_edges(self.get_edge_filter()):
# Every Edge becomes a state, every state becomes an edge # Every Edge becomes a state, every state becomes an edge
transitions.append(Transition(in_edge, out_edge, out_edge.target)) transitions.append(Transition(in_edge, out_edge, out_edge.target))
# Exit nodes are different
if cur_abb == subtask.exit_abb:
out_edge = None
# Every Edge becomes a state, every state becomes an edge
transitions.append(Transition(in_edge, -1, None))
event = Event(cur_abb, transitions) event = Event(cur_abb, transitions)
ret.add_event(event) ret.add_event(event)
...@@ -76,7 +87,7 @@ class ApplicationFSM(Analysis, GraphObject): ...@@ -76,7 +87,7 @@ class ApplicationFSM(Analysis, GraphObject):
ret.initial_state = None ret.initial_state = None
return ret return ret
def __epsilon_elimination(self, fsm): def epsilon_elimination(self, fsm):
"""The epsilon elimination pass makes every computation """The epsilon elimination pass makes every computation
event/transition to an epsilon transition. With the event/transition to an epsilon transition. With the
standard FSM epslion elimination, we reduce the FSM standard FSM epslion elimination, we reduce the FSM
...@@ -91,7 +102,7 @@ class ApplicationFSM(Analysis, GraphObject): ...@@ -91,7 +102,7 @@ class ApplicationFSM(Analysis, GraphObject):
# epsilon set is the set of states, that can be reached from # epsilon set is the set of states, that can be reached from
# one state by using only epsilon transitions. In our case, # one state by using only epsilon transitions. In our case,
# all computation blocks are epsilon transitions. # all computation blocks are epsilon transitions.
def is_epsilon_transition(transition): def is_epsilon_transition(trans):
return trans.event.isA(S.computation) return trans.event.isA(S.computation)
changed = True changed = True
while changed: while changed:
...@@ -138,8 +149,125 @@ class ApplicationFSM(Analysis, GraphObject): ...@@ -138,8 +149,125 @@ class ApplicationFSM(Analysis, GraphObject):
app_fsm = self.__to_fsm(subtask) app_fsm = self.__to_fsm(subtask)
app_fsm.rename(states=True) app_fsm.rename(states=True)
app_fsm = self.__epsilon_elimination(app_fsm) app_fsm = self.epsilon_elimination(app_fsm)
subtask.fsm = app_fsm
subtask.ApplicationFSMIterator = ApplicationFSMIterator.create
# Rewrite AlarmSubtask
if self.system_graph.AlarmHandlerSubtask:
fsm = self.system_graph.AlarmHandlerSubtask.fsm
for E in fsm.events:
if not E.name.isA([S.CheckAlarm, S.iret]):
continue
old_states = set([T.source for T in E.transitions])
new_state = [T.source for T in E.transitions][0]
def renamer(state):
if state in old_states:
return new_state
return state
fsm.rename(states = renamer)
tgt = {(T.source, T.target): T for T in E.transitions}
E.transitions = tgt.values()
for trans in self.system_graph.idle_subtask.fsm.transitions:
if trans.event.isA(S.Idle):
trans.target = trans.source
fsm_iter_cache = {}
class ApplicationFSMIterator:
"""The Application FSM Iterator is a wrapper for APP-FSM states to be
used by the symbolic system execution. It has a similar
interface to the AtomicBasicBlock.
"""
@staticmethod
def create(subtask, state):
if not (subtask, state) in fsm_iter_cache:
it = ApplicationFSMIterator()
it.__create__(subtask, state)
fsm_iter_cache[(subtask, state)] = it
return fsm_iter_cache[(subtask, state)]
def __init__(self):
pass
def __create__(self, subtask, state):
self.subtask = subtask
self.__state = state
self.dynamic_priority = self.__consistent(
self.__originating_blocks(),
lambda x: x.dynamic_priority
)
computations = self.__originating_blocks(True)
if computations:
self.interrupt_block_all = self.__consistent(
computations,
lambda x: x.interrupt_block_all
)
self.interrupt_block_os = self.__consistent(
computations,
lambda x: x.interrupt_block_os
)
self.possible_systemcalls = set()
for trans in self.subtask.fsm.get_outgoing_transitions(self.__state):
self.possible_systemcalls.add(trans.event)
if self.__state != self.subtask.fsm.initial_state \
and self.subtask.is_user_thread():
# Besides the initial state, we give back an additional
# computation block as possible system-call.
edges = self.subtask.fsm.state_mapping[self.__state]
computations = [x.event for x in edges if x.event.isA(S.computation)]
abb = AtomicBasicBlock(self.subtask.system_graph)
abb.make_it_a_syscall(S.CheckIRQ, computations)
self.possible_systemcalls.add(abb)
self.possible_systemcalls = list(self.possible_systemcalls)
def __originating_blocks(self, only_computation = False):
blocks = [x.event for x in self.subtask.fsm.state_mapping[self.__state]]
if only_computation:
blocks = [x for x in blocks if x.isA(S.computation)]
return blocks
def __consistent(self, seq, mapper=lambda x:x):
ret = [mapper(x) for x in seq]
assert len(ret) > 0, "To have a consistent value, at least one value must be present"
assert all([ret[0] == ret[i] for i in range(1, len(ret))]), "Inconsistent %s" % seq
return ret[0]
def __repr__(self):
try:
return "<It %s:%s prio:%d>" %(self.subtask.name, self.__state, self.dynamic_priority)
except:
return "<It %s:%s prio>" %(self.subtask.name, self.__state)
def isA(self, cls):
return self.__consistent(
self.__originating_blocks(),
lambda x: x.isA(cls)
)
def path(self):
return "%s/%d" % (self.subtask, self.__state)
@property
def abb_id(self):
return self.__state
def get_blocks(self):
return self.__originating_blocks()
def next_iterators(self, event):
ret = []
for trans in self.subtask.fsm.get_outgoing_transitions(self.__state):
if trans.event == event:
ret.append(self.create(self.subtask, trans.target))
# No computation block should ever arrive here as event
assert not event.isA(S.computation)
return ret
subtask.conf.fsm = app_fsm
print()
print(app_fsm)
...@@ -102,8 +102,10 @@ class DynamicPriorityAnalysis(Analysis): ...@@ -102,8 +102,10 @@ class DynamicPriorityAnalysis(Analysis):
# Blocks within a non-preemtable subtask cannot be # Blocks within a non-preemtable subtask cannot be
# rescheduled (except for ISR2) # rescheduled (except for ISR2)
if not abb.function.subtask.conf.preemptable and \ if not abb.function.subtask.conf.preemptable and \
not abb.isA(S.kickoff): not abb.isA(S.kickoff) and \
not abb.subtask.conf.is_isr:
dynamic_priority = RES_SCHEDULER.conf.static_priority dynamic_priority = RES_SCHEDULER.conf.static_priority
# Each abb has a dynamic priority # Each abb has a dynamic priority
abb.dynamic_priority = dynamic_priority abb.dynamic_priority = dynamic_priority
...@@ -112,7 +114,7 @@ class DynamicPriorityAnalysis(Analysis): ...@@ -112,7 +114,7 @@ class DynamicPriorityAnalysis(Analysis):
# StartOS), the priority is the idle priority. # StartOS), the priority is the idle priority.
for syscall in self.system_graph.syscalls: for syscall in self.system_graph.syscalls:
precessors = syscall.get_incoming_nodes(E.task_level) precessors = syscall.get_incoming_nodes(E.task_level)
if syscall.isA(S.kickoff) or syscall.isA(S.WaitEvent): if syscall.isA(S.kickoff):
syscall.dynamic_priority = syscall.function.subtask.static_priority syscall.dynamic_priority = syscall.function.subtask.static_priority
elif len(precessors) == 1: elif len(precessors) == 1:
syscall.dynamic_priority = precessors[0].dynamic_priority syscall.dynamic_priority = precessors[0].dynamic_priority
......
...@@ -19,45 +19,51 @@ class PreciseSystemState(): ...@@ -19,45 +19,51 @@ class PreciseSystemState():
__slots__ = ["frozen", "states", "call_stack", "continuations", __slots__ = ["frozen", "states", "call_stack", "continuations",
"current_abb", "__hash", "__next_states", "__prev_states", "events"] "current_abb", "__hash", "__next_states", "__prev_states", "events"]
def __init__(self, system_graph): def __init__(self, system_graph, init = True):
PreciseSystemState.system_graph = system_graph PreciseSystemState.system_graph = system_graph
self.frozen = False self.frozen = False
self.__hash = None
self.__next_states = []
self.__prev_states = []
if not init:
return
size = len(self.system_graph.subtasks) size = len(self.system_graph.subtasks)
self.states = [0] * size self.states = [0] * size
self.continuations = [None] * size self.continuations = [None] * size
self.current_abb = None self.current_abb = None
self.__hash = None
self.__next_states = []
self.__prev_states = []
self.events = [0] * size self.events = [0] * size
self.call_stack = [] self.call_stack = []
for i in range(0, size): for i in range(0, size):
self.call_stack.append(list()) self.call_stack.append(list())
def add_cfg_edge(self, block, ty): def new(self):
self.__next_states.append((ty, block)) return PreciseSystemState(self.system_graph)
block.__prev_states.append((ty, self))
def add_cfg_edge(self, block, ty, label = None):
self.__next_states.append((ty, block, label))
block.__prev_states.append((ty, self, label))
def remove_cfg_edge(self, block, ty): def remove_cfg_edge(self, block, ty):
for i, elem in enumerate(self.__next_states): for i, elem in enumerate(self.__next_states):
if elem == (ty, block): if tuple(elem[0:2]) == (ty, block):
del self.__next_states[i] del self.__next_states[i]
break break
for i, elem in enumerate(block.__prev_states): for i, elem in enumerate(block.__prev_states):
if elem == (ty, self): if tuple(elem[0:2]) == (ty, self):
del block.__prev_states[i] del block.__prev_states[i]
break break
def get_outgoing_nodes(self, ty): def get_outgoing_nodes(self, ty):
return [block for Ty,block in self.__next_states if Ty == ty] return [block for Ty,block,label in self.__next_states if Ty == ty]
def get_incoming_nodes(self, ty): def get_incoming_nodes(self, ty):
return [block for Ty,block in self.__prev_states if Ty == ty] return [block for Ty,block,label in self.__prev_states if Ty == ty]
def new(self): def get_outgoing_edges(self, ty):
return PreciseSystemState(self.system_graph) return [(block, label) for Ty,block,label in self.__next_states if Ty == ty]
def copy_if_needed(self, multiple=None): def copy_if_needed(self, multiple=None):
if self.frozen: if self.frozen:
...@@ -74,17 +80,18 @@ class PreciseSystemState(): ...@@ -74,17 +80,18 @@ class PreciseSystemState():
# Increase the copy counter # Increase the copy counter
SystemState.copy_count += 1 SystemState.copy_count += 1
state = self.new() # For faster copying, we use the do-not-init constructor and
# do everything ourselves here.
state = PreciseSystemState(self.system_graph, init = False)
state.current_abb = self.current_abb state.current_abb = self.current_abb
def copyto(dst, src):
dst[:] = src
copyto(state.states, self.states)
copyto(state.continuations, self.continuations)
copyto(state.events, self.events)
# Shallow copies
state.states = self.states[:]
state.continuations = self.continuations[:]
state.events = self.events[:]
state.call_stack = []
for subtask_id in range(0, len(self.states)): for subtask_id in range(0, len(self.states)):
state.call_stack[subtask_id] = list(self.call_stack[subtask_id]) state.call_stack.append(self.call_stack[subtask_id][:])
return state return state
...@@ -116,6 +123,9 @@ class PreciseSystemState(): ...@@ -116,6 +123,9 @@ class PreciseSystemState():
def is_surely_ready(self, subtask): def is_surely_ready(self, subtask):
return self.states[subtask.subtask_id] == self.READY return self.states[subtask.subtask_id] == self.READY
def is_surely_waiting(self, subtask):
return self.states[subtask.subtask_id] == self.WAITING
def is_maybe_ready(self, subtask): def is_maybe_ready(self, subtask):
return self.READY & self.states[subtask.subtask_id] return self.READY & self.states[subtask.subtask_id]
...@@ -129,9 +139,13 @@ class PreciseSystemState(): ...@@ -129,9 +139,13 @@ class PreciseSystemState():
def get_continuations(self, subtask): def get_continuations(self, subtask):
if type(subtask) == int: if type(subtask) == int:
return set([self.continuations[subtask]]) return set([self.continuations[subtask]])
return set([self.continuations[subtask.subtask_id]]) return set([self.continuations[subtask.subtask_id]])
def get_continuation(self, subtask):
if type(subtask) == int:
return self.continuations[subtask]
return self.continuations[subtask.subtask_id]
def set_continuation(self, subtask, abb): def set_continuation(self, subtask, abb):
assert not self.frozen assert not self.frozen
self.continuations[subtask.subtask_id] = abb self.continuations[subtask.subtask_id] = abb
...@@ -160,36 +174,72 @@ class PreciseSystemState(): ...@@ -160,36 +174,72 @@ class PreciseSystemState():
return self.call_stack[subtask.subtask_id] return self.call_stack[subtask.subtask_id]
# Events # Events
def __events(self, subtask, set_events = None, set_block_list = None):
events = self.events[subtask.subtask_id] & 0xffff
block_list = (self.events[subtask.subtask_id] >> 16) & 0xffff
if not set_events is None:
assert (set_events & 0xffff == set_events)
self.events[subtask.subtask_id] = (set_events | (block_list << 16))
events = set_events
if not set_block_list is None:
assert (set_block_list & 0xffff == set_block_list)
self.events[subtask.subtask_id] = (events | (set_block_list << 16))
block_list = set_block_list
return (events, block_list)
def get_events(self, subtask): def get_events(self, subtask):
"""Return a tuple (None, set)""" """Return a tuple (None, set)"""
events = self.events[subtask.subtask_id] events, _ = self.__events(subtask)
return (subtask.event_mask_valid ^ events, events) return (subtask.event_mask_valid ^ events, events)
def set_events(self, subtask, event_list): def set_events(self, subtask, event_list):
mask = Event.combine_event_masks(event_list) mask = Event.combine_event_masks(event_list)
self.events[subtask.subtask_id] |= mask self.__events(subtask, set_events = mask)
def clear_events(self, subtask, event_list): def clear_events(self, subtask, event_list):
mask = Event.combine_event_masks(event_list) mask = Event.combine_event_masks(event_list)
self.events[subtask.subtask_id] &= ~mask events, bl = self.__events(subtask)
events &= ~mask
assert bl == 0
self.__events(subtask, set_events = events)
def maybe_waiting(self, subtask, event_list): def clear_block_list(self, subtask):
"""Returns True, if the task may block be waiting at this point""" self.__events(subtask, set_block_list = 0)
def set_block_list(self, subtask, event_list):
mask = Event.combine_event_masks(event_list) mask = Event.combine_event_masks(event_list)
if (self.events[subtask.subtask_id] & mask) == 0: self.__events(subtask, set_block_list = mask)
def get_block_list(self, subtask):
events, bl = self.__events(subtask)
return bl
def maybe_waiting(self, subtask):
"""Returns True, if the task may block be waiting at this point"""
events, block_list = self.__events(subtask)
if (block_list & events) == 0:
return True return True
return False return False
def maybe_not_waiting(self, subtask, event_list): def maybe_not_waiting(self, subtask):
"""Returns True, if the task may be continue without blocking""" """Returns True, if the task may be continue without blocking"""
# In the precise system state, this is the exact opposite # In the precise system state, this is the exact opposite
return not self.maybe_waiting(subtask, event_list) return not self.maybe_waiting(subtask)
def is_event_set(self, subtask, event): def is_event_set(self, subtask, event):
return (self.events[subtask.subtask_id] & event.event_mask) != 0 events, block_list = self.__events(subtask)
return (events & event.event_mask) != 0
def is_event_waiting(self, subtask, event):
events, block_list = self.__events(subtask)
return (block_list & event.event_mask) != 0
def is_event_cleared(self, subtask, event): def is_event_cleared(self, subtask, event):
return (self.events[subtask.subtask_id] & event.event_mask) == 0 events, block_list = self.__events(subtask)
return (events & event.event_mask) == 0
## Continuation accesors ## Continuation accesors
def was_surely_not_kickoffed(self, subtask): def was_surely_not_kickoffed(self, subtask):
...@@ -235,7 +285,7 @@ class PreciseSystemState(): ...@@ -235,7 +285,7 @@ class PreciseSystemState():
@property @property
def current_subtask(self): def current_subtask(self):
return self.current_abb.function.subtask return self.current_abb.subtask
def merge_with(self, other): def merge_with(self, other):
"""Returns a newly created state that is the result of the merge of """Returns a newly created state that is the result of the merge of
...@@ -303,6 +353,7 @@ class PreciseSystemState(): ...@@ -303,6 +353,7 @@ class PreciseSystemState():
for event in subtask.events: for event in subtask.events:
SET = self.is_event_set(subtask, event) SET = self.is_event_set(subtask, event)
CLEARED = self.is_event_cleared(subtask, event) CLEARED = self.is_event_cleared(subtask, event)
WAITING = self.is_event_waiting(subtask, event)
if SET and CLEARED: if SET and CLEARED:
ret.append("%s:*" % event.conf.name) ret.append("%s:*" % event.conf.name)
elif SET: elif SET:
...@@ -310,6 +361,8 @@ class PreciseSystemState(): ...@@ -310,6 +361,8 @@ class PreciseSystemState():
else: else:
assert CLEARED assert CLEARED
ret.append("%s:0" % event.conf.name) ret.append("%s:0" % event.conf.name)
if WAITING:
ret.append("W")
return " ".join(ret) return " ".join(ret)
......
import logging
from .Analysis import Analysis from .Analysis import Analysis
from .Subtask import Subtask from .Subtask import Subtask
...@@ -34,6 +35,7 @@ class PrioritySpreadingPass(Analysis): ...@@ -34,6 +35,7 @@ class PrioritySpreadingPass(Analysis):
p[0] = prio p[0] = prio
p[1].conf.static_priority = prio p[1].conf.static_priority = prio
self.prio_to_participant[prio] = p[1] self.prio_to_participant[prio] = p[1]
logging.debug("Prio %d: %s", prio, p[1])
prio += 1 prio += 1
assert participants[0][1] == self.system_graph.get(Subtask, "Idle") assert participants[0][1] == self.system_graph.get(Subtask, "Idle")
...@@ -41,13 +41,18 @@ class SporadicEvent: ...@@ -41,13 +41,18 @@ class SporadicEvent:
assert state.is_surely_suspended(self.handler), (state, self.handler) assert state.is_surely_suspended(self.handler), (state, self.handler)
# Save current IP # Save current IP
current_subtask = state.current_abb.function.subtask current_subtask = state.current_abb.subtask
copy_state.set_continuation(current_subtask, state.current_abb) copy_state.set_continuation(current_subtask, state.current_abb)