Example -
Starting simulations
No simultaneous events -
Handling Simultaneous Events
Infinitesimal Delays -
Disadvantages
Operational non-determinism -
Denotational non-determinism
In discrete event systems, changes in system behavior are marked
by discrete occurrences or events.
Events consist of a token and a time stamp.
A token can be a scalar value, a matrix, a data structure, and so forth.
The time stamp might be a floating-point number (Spice or the Ptolemy
Discrete-Event model of computation) or consist of an ordered pair
(global clock tick, delta time tick) of integers (VHDL).
In VHDL, the ordered pair of integers represents a global time index and
a delta time index.
The scheduler runs the simulation by maintaining a record of all of the
events in the system sorted by time stamp, and advancing time to the
earliest time stamp.
Thus, there is a total ordering between all events.
Given any two events, we can tell if they occur simultaneously or after
one another.
CAD tools implementing a discrete event model include
Throughout this section, we will discuss the simulation of discrete
event systems using the following block diagram:
--------------------------------
| |
| --- |
----> | | --- |
--- | C | ---------> | | |
| A | ------> | | | D | ---
--- --- ---> | |
| ---
--- |
| B | -------------------
---
Figure 1: A Discrete Event Block Diagram
This block diagram has two source blocks, A and B, and two input/output
blocks C and D.
There are no sink blocks.
Some discrete event models, such as the Ptolemy Classic Discrete Event
model of computation, use the firing rule that an block is active when
new data is present on at least one input.
According to this rule, source block would never be executed.
If we simply ran a system according to this rule without any initial
events on the arcs, then time would not advance because there are no events
to process and the simulator would terminate.
Two possible solutions are:
- Place an initial token on every arc at the simulation start time
- Require that all source blocks schedule themselves
The Ptolemy Classic Discrete-Event model of computation implements the
second case.
To implement the second case, the blocks and the scheduler would no
longer be decoupled.
You may recall that the Synchronous Dataflow and Process Network models
of computation decouple the scheduler from the node implementation.
Decoupling the nodes and the scheduler
- avoids run-time overhead
- is a necessary condition for compile-time scheduling
- makes it easier to nest models of computation
For the latter point, consider the nesting of a DE subsystem into
a DE system.
The DE subsystem must run lock-step with the parent DE system to
ensure causality.
How does one handle a source block in the DE subsystem?
The Ptolemy Classic DE model of computation requires that all source blocks
schedule themselves.
A Ptolemy Classic DE scheduler for simulation goes through three steps:
- initialization
- parameter initialization
- graph initialization
- parameter validation
- scheduling
- begin
- execution (implemented by the go method)
- wrapup
All DE source blocks are derived from the DERepeatStar
,
whose code is available at
$PTOLEMY/src/domains/de/kernel/DERepeatStar.cc.
The initialize method creates a hidden feedback arc from a hidden output
port to a hidden input port.
(The hidden feedback arc is also given a property of infinitesimal
delay, which will be covered in Case #3 below.)
Hidden means that the arc or port does not show up when the block is
graphically displayed.
In the begin phase of initialization, a source block would output an
initial token at the simulation start time (0.0 s) on the hidden feedback
arc.
When simulation execution begins, every source block will be fired
at the simulation start time.
If the source node would be refired at a future time, then the
go method of the source node would output on the hidden feedback arc
a token with a timestamp equal to the future time.
For the first case using the Ptolemy Classic DE model of computation, we
will not encounter simultaneous events during the simulation.
The blocks in Figure 1 are:
- A executes once at time 1.0 seconds (zero-delay block)
- B executes once at time 1.5 seconds (zero-delay block)
- C has a delay of 1.0 seconds
- D has a delay of 1.0 seconds
That is, both A and B are impulses.
Blocks A and B each put out a token with a time stamp of 0.0 sec on
their hidden feedback arcs.
Source blocks are considered zero-delay blocks.
At the beginning of the simulation, the global queue has
0.0 sec | Block A
|
0.0 sec | Block B
|
The simulator starts at time 0.0 sec.
At time of 0.0 sec, the simulator executes blocks A and B.
The global queue now also has
1.0 sec | Block C
|
1.5 sec | Block D
|
The simulator now advances time to 1.0 sec, deleted the global
queue entries with timestamps prior to 1.0 sec, and executes Block C.
Since upper input to Block C has not been initialized, how should
C handle this?
Two possibilities are
- Allow a default value on all arcs, or
- Force the block to detect which inputs have new data
The Ptolemy Classic Discrete Event domain uses the second option.
When block C executes, it notices new data on the lower input port, and
produces a new event at 2.0 sec.
The global queue is now
1.5 sec | Block D
|
2.0 sec | Block D
|
The scheduler next advances time to 1.5 sec and executes Block D.
When Block D executes, it uses the new data on the lower input
and produces a new event on its output 1 sec later:
2.0 sec | Block D
|
2.5 sec | Block C
|
The scheduler advances time to 2.0 sec and executes D again.
2.5 sec | Block C
|
3.0 sec | Block C
|
Now, step back and take a look at the system under simulation.
Will the system come to a halt by itself?
Generally, it does not, and even if it did, the scheduler could
not predict it in finite time for all possible systems.
The particular system we are using as an example will repeat forever.
So, in practice, the user specifies a stopping time.
For the second case using the Ptolemy Classic DE model of computation, we
define the blocks in Figure 1 as follows:
- A executes every 1.0 seconds starting at 0.0 seconds
(zero-delay block) implemented by a
DEClock
block
- B executes every 1.5 seconds starting at 0.0 seconds
(zero-delay block) implemented by a
DEClock
block
- C has no delay
- D has a delay of 1.0 seconds
Blocks A and B are DEClock
blocks from the Ptolemy Classic
collection.
The DEClock
source code is available at
$PTOLEMY/src/domains/de/stars/DEClock.pl.
DEClock
is derived from DERepeatStar
, whose
code is available at
$PTOLEMY/src/domains/de/kernel/DERepeatStar.cc.
For this second case, we will encounter simultaneous events.
The proper handling of simultaneous events increases the difficulty
of scheduling because the scheduler must obey the order of data
dependencies in the graph.
Blocks A and B pass tokens along a hidden feedback arc to cause
the repeated firings to occur.
After the scheduler has been initialized, the global queue has the
following events in it prior to executing the graph:
0.0 sec | Block A
|
0.0 sec | Block B
|
Let's examine what would happen during the execution at Time 0.0 sec
before we arrive at a schedule.
Block A would produce an output with time stamp of 0.0 sec, which feeds
into Block C.
Block C would produce an output with time stamp of 0.0 sec, which feeds
into block D.
Block B would produce an output with time stamp of 0.0 sec, which feeds
into Block D.
How do we derive a schedule to resolve the simultaneous events at block D
to ensure causality?
When handling simultaneous events, the scheduler must first determine
the data dependencies before executing any blocks in order to enforce
causality.
We can determine the data dependencies by performing a
topological sort, which will be discussed in the
Introduction to Graph
Theory lecture.
A topological sort is not necessarily unique.
In this DE graph, D is a delay block, and A, B, and C are functional blocks.
A and B have hidden feedback arcs with a property of infinitesimal delay
(i.e., a second dimension of time).
The infinitesimal delay indicates that any data produced on the hidden
output arc will be at an infinitesimally later time.
Since we are trying to resolve a schedule for the current time, the delay
property enables the hidden feedback arc to be broken.
In addition, D is a delay block, so that any data it produces will happen
at a later time than the current time stamp.
Here is the amended graph to schedule at a particular time stamp:
---
| | ---
--- | C | ---------> | |
| A | ------> | | | D |
--- --- ----> | |
| ---
--- |
| B | -------------------
---
Figure 2: The Discrete Event block diagram in Figure 1 used in
scheduling execution to resolve simultaneous events. Of the four blocks,
only D is a delay block. Since D is a delay block, the feedback arc from
D to C can be removed for the purposes on scheduling the execution for
the current time stamp.
For the DE system, only D is a delay block.
Here all of the topological sorts:
In finding the topological sort, the scheduler has to know if each block
will produce a delay or not.
In the Ptolemy Classic DE domain, however, delays associated with blocks
must always be positive.
The delay attribute associated with a DE block is set in the block's
constructor in Ptolemy Classic.
What happens if the delay is data-dependent?
It is okay in VHDL.
Delays associated with operations in VHDL must be non-negative.
- At Time = 1.0 seconds
1.0 sec | Block A
|
1.5 sec | Block B
|
During the execution at Time 1.0 sec, Block A will produce an output
with time stamp of 1.0 sec on the visible output.
In turn, Block C will produce an output with time stamp of 1.0 sec,
which feeds into Block D.
Block D will produce an output with time stamp of 2.0 sec.
- At Time = 1.5 seconds
1.5 sec | Block B
|
2.0 sec | Block C (upper port)
|
2.0 sec | Block A
|
During the execution at Time 1.5 sec, Block B will produce an output
with time stamp of 1.5 sec on the visible output, and a time stamp of
3.0 sec on the invisible feedback arc.
In turn, Block D will produce an output with time stamp of 2.5 sec.
- At Time = 2.0 seconds
2.0 sec | Block C (upper port)
|
2.0 sec | Block A
|
2.5 sec | Block C (upper port)
|
3.0 sec | Block B
|
During the execution at Time 2.0 sec, Block A will produce an output
with time stamp of 2.0 sec on the visible output.
In turn, Block C will produce an output with time stamp of 2.0 sec,
which feeds into Block D.
Block D will produce an output with time stamp of 3.0 sec.
- At Time = 2.5 seconds
2.5 sec | Block C (upper port)
|
3.0 sec | Block C (upper port)
|
3.0 sec | Block A
|
3.0 sec | Block B
|
- At Time = 3.0 seconds
3.0 sec | Block C (upper port)
|
3.0 sec | Block A
|
3.0 sec | Block B
|
3.5 sec | Block C (upper port)
|
For the third case, we define the blocks in Figure 1 as follows:
- A executes every 1.0 seconds starting at 0 seconds (zero-delay block)
- B executes every 1.5 seconds starting at 0 seconds (zero-delay block)
- C is a function block (zero-delay block)
- D is a function block (zero-delay block)
Note that this causes blocks C and D to deadlock because a firing
pattern cannot be chosen to satisfy causality.
That is, C can't fire until D fires, but D can't fire until C fires.
How does the scheduler handle this situation?
Possible outcomes:
- Scheduler hangs and fails to advance time
- Scheduler reports an error
In the Ptolemy Classic Discrete Event domain, the user must place an
infinitesimal delay (similar to delta time in VHDL) on one of the two
arcs of the C-D cycle.
Figure 3 places the infinitesimal delay on the feedback arc from
C to D.
The infinitesimal delay is represented by *
, which would
appear as a diamond in Ptolemy Classic.
Although an infinitesimal delay takes 0 seconds, it specifies that C
can execute before D.
For simultaneous events, the DE scheduler can now find a firing
sequence, e.g. ACBD or BACD or ABCD, by using a
topological sort, which will be discussed in the
Introduction to Graph
Theory lecture.
---------------*----------------
| |
| --- |
----> | | --- |
--- | C | ---------> | | |
| A | ------> | | | D | ---
--- --- ---> | |
| ---
--- |
| B | -------------------
---
Figure 3: Adjusting the DE schematic to handle simultaneous events
by adding an infinitesimal delay *
.
After scheduling has been initialized but before the simulation
starts, here is the state of the global queue:
0.0 sec | Block A
|
0.0 sec | Block B
|
Set Time = 0.0 seconds.
The graph consists of four functional (zero-delay) blocks.
Perform a topological sort on the graph.
The topological sort would produce one of the following schedules:
ACBD or BACD or ABCD.
Let's use ABCD.
After A and B are executed, here's the state of the global queue
(note the dequeuing of the tokens consumed when A and B executed):
0.0 sec | Block C (lower port)
|
0.0 sec | Block D (lower port)
|
1.0 sec | Block A
|
1.5 sec | Block B
|
Continue with the schedule to execute C:
0.0 sec | Block D (lower port)
|
0.0 sec | Block D (upper port)
|
1.0 sec | Block A
|
1.5 sec | Block B
|
After executing D, D produces a token on the feedback arc
from D to C.
That token passes through an infinitesimal delay.
The infinitesimal delay can be thought of a second dimension
of time in units of e.
At Time = (0.0 + e) seconds, where e means
one unit of infinitesimal delay,
(0.0 + e) sec | Block C (upper port)
|
1.0 sec | Block A
|
1.5 sec | Block B
|
At Time = (0.0 + 2 e) seconds
(0.0 + 2 e) sec | Block C (upper port)
|
1.0 sec | Block A
|
1.5 sec | Block B
|
This firing pattern could be continued ad infinitum without
the scheduler advancing global time beyond 0.0 seconds.
Since the scheduler and blocks in a discrete-event simulator are heavily
dependent on each other, it is difficult to compose discrete event
models of computation with other models such as Synchronous Dataflow (SDF).
If DE were the parent system and a DE block represented an SDF system,
then whenever the DE block executed, it must send a certain number
of samples to the SDF system to run at least one period of the graph.
If SDF were the parent system and an SDF block represented a DE system,
then whenever the SDF block executed, it must guarantee that the same
number of output samples always got produced.
The discrete event model of computation is not determinate.
This is not too alarming, because discrete event models are
used to model the environment in which a design operates and
(hopefully) not used to model the design itself.
Slide 8 of the block diagram talk
defines determinism:
- A behavior is a set of signals that obeys the semantics
- A system is determinate if knowing the inputs, there is
at most one behavior.
Another way of saying (2) is that for a determinate model,
we can use any scheduling algorithm that obeys that semantics
and we will obtain the same behavior.
Here is a review of the semantics of the discrete-event model:
- Tokens have real-valued time stamps associated with them
- Tokens are processed in a causal manner
- Tokens conceptually flow on arcs and are sorted by their timestamps
- A node may be a functional (no delay) or a delay block.
A delay block is not allowed to have 0 seconds of delay.
- A node is enabled if at least input port contains "new" data,
i.e., a data with a time stamp equal to the current global time
- A feedback path must have at least one delay block or an
infinitesimal delay to be considered valid.
- An infinitesimal delay only breaks a dependence; it does not
advance global time, but instead advances a second dimension
of time (i.e. "delta" time).
Slide 19 of the block diagram talk
gives the following concerning the discrete event model of computation:
- "Determinate under simple conditions (strict causality in
feedback loops)"
- "Simulatable under simple conditions (delta causality in
feedback loops)"
Strict causality means that a delay block (with non-zero delay)
needs to be in a feedback loop.
Sources of non-determinism in an implementation (i.e., in a set
of operational semantics):
- Timestamps are represented in a floating-point representation;
due to rounding/truncation errors, two events that should have
been considered simultaneous are not. (VHDL works around this
by using integer ticks for global time and integer ticks for
delta time as well.)
- Consider a loop without any infinitesimal delays on the arcs
and with functional blocks and only one delay block. The delay
on the delay block is conditional on an input value; if the
conditional delay is zero, then the loop deadlocks and the
scheduling algorithm could either halt (due to the error) or
schedule the rest of the graph. See
Ptolemy Classic Programmer's Manual Section 12.2.5
(by
Prof. Stephen A. Edwards,
Columbia University)
The fundamental problem in DE systems is one of races, e.g. as would
occur in reconvergence as in the following graph:
/----B-----\
A---+ D
\----C-----/
Say A generates an event that is sent simultaneously to B and C, who then
delay the result by the same constant amount (it doesn't have to be zero).
Does D see A, B, or both events when it fires? Most DE systems have a
simple-minded event queue that leaves this question unanswered; D sees an
event from B and C, but not necessarily in that order.
If the blocks in a DE system are well-behaved for some suitable
definition of well-behaved, then this is not a problem.
In general, if D is an event-sensitive object (e.g., an edge-sensitive
latch), then the behavior would be nondeterministic.
Here is another example.
This example concerns an undefined concurrent procedure execution
order in Verilog:
always @(posedge clk) $write( a )
always @(posedge clk) $write( b )
The first simulator moved procedures between two push-down stacks,
which produced
a b b a a b b a a b b a a b a
Later simulators had to match this now-expected behavior.
Updated 04/04/04.