| [Top] | [Contents] | [Index] | [ ? ] |
This file documents syndi version 0.2.0, which is a
compiler that translates a specification language to Petri nets,
delay insensitive circuit netlists, or virtual code executable files.
| 1. Usage | ||
| 2. Syntax | ||
| 3. Semantics | ||
| 4. Status | ||
| GNU GENERAL PUBLIC LICENSE |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
syndi is a compiler that generates Delay Insensitive circuits or
Petri nets from a high level specification language. This document is
intended mainly as a reference manual for users already familiar with
the language, but it may be of some assistance to new users who
consider themselves a quick study.
| 1.1 Options | ||
| 1.2 Files |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The following command line options are recognized.
--help
syndi's usage and options.
--version
syndi source file
from which the compiler was built.
--warranty
--main=definition
syndi syntax. It will usually be
necessary to quote the expression to suppress interpretation by the
shell.
--display
--main option, with names and numeric
subexpressions evaluated, variables transformed out, and recursive
equations solved. This option disables the writing of all
output files.
--phase n
syndi compiler itself.
--preamble-only
--phase option, write an
empty core dump file except for the front matter, so as to save time
on compression.
--library
--petrinet
--main option and write
it to a file named after the identifier.
--circuit
--main option and write it to
a file named after the identifier.
--executable
--main option
whose result type is a statement or circuit to a
stand-alone executable file (may be time consuming for large functions
due to output file compression).
The last four can be applied selectively to definitions in the source by
using the corresponding compiler directives instead, but directives are
overridden by command line options. If --main is specified with
no other options and no files or with only binary files,
--display is implied. More about directives and executable files
is explained in 2.3 Directives.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Any number of input files may be specified on the command line, which
may be any combination of source and binary files. Expressions in
source files may refer to identifiers defined in other source files or
exported from binary files. Binary files are required to be libraries
produced by previous runs of syndi using the library
option or directive.
Input file names are normally assumed to be relative to the current
directory, but on GNU systems, the AVMINPUTS environment
variable may be used to store a colon separated list of additional
directories that will be searched. This feature applies to binary
files only.
Output files are written to the current directory. In the case of
library files, an output file with a .slf suffix is written for
each source file in which any #library+ directive is used, or
for all source files if the --library option is given on the
command line. In all other cases, a separate file is written for each
relevant declaration, with a file name derived from the identifier of
the declaration. Output file formats for Petri nets and DI netlists
are documented in the diana manual.
A file called profile.txt is written to the current directory
on every run, which tabulates the time spent on various aspects of
the compilation.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A source file is organized as a sequence of equations
interspersed with directives. Each equation is of the form
lvalue = expression, where
lvalue ::= identifier | (lvalue) | lvalue params
params ::= variable | (params[,params]*)
expression ::= identifier | variable | number | name | tuple | list |
application | abstraction | expression operator expression
operator ::= ^ | : | - | .
tuple ::= () | (expression[,expression]*)
list ::= nil | <> | <expression[,expression]*>
application::= expression [whitespace] expression
abstraction ::= params . whitespace expression
|
An lvalue can therefore contain only one identifier, which is said to be the identifier declared by the equation. The terms equations, declarations, and definitions are used interchangeably in this document.
Note that the . is an overloaded operator, which is disambiguated according
to whether it is followed immediately by an operand or by white space. In
the latter case, it stands for functional abstraction, also known as lambda
abstraction.
An equation of the form f x = g is syntactic
sugar for f = x. g, just as
g(x) y = h represents g =
x. y. h, and so on. The identifier on the left may
also appear on the right, in which case the equation is interpreted as a
recurrence to be solved. Recurrences can be solved over functions and
statements, and may involve multiple equations. However, other types of
recurrences such as "lazy lists" are not supported.
There are two forms of function application, one with white space and
one without. The latter has higher precedence and is left
associative. All other operators including application with a space
are right associative. Hence, g(x) y is equivalent to
(g x) y, not g(x(y)).
In order of decreasing precedence, the operators are tight application,
- , . , : , ^ , loose application, and
functional abstraction. The operator symbols - , . ,
:, and ^ are semantically equivalent to the built in
functions range, dot, cat, and cons,
respectively, except when the . represents functional
abstraction.
(note to functional programmers: in Haskell, unlike syndi,
function application is always left associative. This feature of
Haskell seeks an accommodation with a historical accident whereby
languages based on lambda calculi compel the simulation tuples
through higher order functions.)
| 2.1 Lexical Elements | ||
| 2.2 Comments | ||
| 2.3 Directives |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As the above grammar suggests, the main lexical classes are identifiers, numbers, variables, and names.
lvalue contains variables).
syndi and are syntactically similar to
identifiers. A name is recognized as such by not being a pre-defined
identifier and not being declared in any equation.
The built in names
+ and * have particular algebraic properties. Any name of
the form a.+.n, where n is a number and . is the dot operator,
is equivalent to a.successor(n), and any name of the form a.*.n
is equivalent to a.double(n). If n is a list of numbers, then
the expressions are equivalent to a.((map successor) n) and
a.((map double) n), respectively. More about names is explained in
3.7 Names.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are five forms of comments in syndi. New users
only need to learn the first one.
(# and #) may be used in matched pairs to indicate a
comment anywhere in a source file, and may be nested.
# followed by white space or a
non-alphabetic character other than a hash designates the remainder of
the line as a comment. A backslash at the end of the line may be used
as a comment continuation character.
###, indicate that the
remainder of the file is a comment.
##, followed
by anything other than a third hash indicates a smart comment, which
may be used to "comment out" a section of syntactically correct code.
A smart comment between declarations comments out the next declaration,
and a smart comment appearing anywhere within a list or tuple comments
out the remainder of the item in which it appears up to the terminating comma
or closing delimiter, taking nested lists or tuples into account.
Standard caveats apply with regard to comment delimiters inside of quoted strings.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
| 2.3.1 Executable Files | ||
| 2.3.2 Eagerness |
The following directives may be used within a source file to control the
production of output files by syndi during a compilation.
Directives begin with a hash, followed with no intervening spaces by the
directive name, and concluded with either a plus or a minus sign. They
may appear only between declarations. By default, all directives are
disabled except sometimes for #eager+. Where a directive is
specified with a plus sign, it is enabled for all declarations following
it until its next occurrence with a minus sign.
#petrinet+
petrinet- will be added.)
#circuit+
circuit-.
#library+
.slf added.
#blackbox+
#executable+
avram virtual machine code format
for each function following this directive whose result type is either
circuit or statement, naming the executable file after the identifier
with a prefix of executable- if necessary to avoid a clash with
the source file name.
#eager+
#lazy+
#eager+ directive if it would otherwise be enabled
by default, which is for any declaration using the
circuit function or the #circuit+ directive.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Any executable file written by syndi is based on a function.
The code generated will automatically incorporate a wrapper that enables
it to read and parse a single expression in a subset of syndi
syntax from standard input. The expression in the input text may
include numbers, names, infix operators, lists, tuples, and comments,
but no pre-defined identifiers or variables. The value obtained for
this expression will be used as an argument to the function. Run time
error messages including parsing errors will be written to standard
error.
If the function in an executable file is evaluated successfully with
the argument obtained from standard input, an automatically supplied
back end will write the result to standard output as either a Petri
net or a netlist, depending on the function result type. The output
file formats are documented in the diana manual. In the case
of a netlist, a comment will be included in the output displaying the
identifier of the function and its argument.
If the result type is anything other than a statement or a circuit, the
back end will attempt to write it to standard output as an expression in
syndi syntax. If that is not possible due to it being of an
unprintable type, an error message displaying the type expression will
be written to standard error.
Executable files produced in this way may of course be used for any
purpose, but they are also compatible with diana for use as
virtual code input files. In this case, the text parameter following
the --dimensions option on the diana command line will be
used as the input text.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
One way syndi synthesizes circuits more efficiently is by
synthesizing them in parts and then putting the parts together. When the
list function is used to make a list of copies of a statement,
all of which are to be synthesized as circuits, syndi will
synthesize the circuit just once, and then make copies of it, rather
than synthesizing a circuit for each copy of the statement. When the
doall function is used to express a list of concurrently
executing statements, which is then translated into a circuit,
syndi will synthesize a circuit for each statement individually,
and form parallel combinations of those circuits that have no inputs in
common, rather than synthesizing a circuit from the combined
statement. This strategy saves time during synthesis and may also lead
to better circuits, and is designated as eager evaluation in this
document.
However, synthesizing a circuit in advance for each part of a statement
is a waste of time if the statement is not going to be translated into a
circuit, so syndi tries to determine whether or not it will be
required and performs no circuit synthesis if it is not.
This determination is based on three criteria by default. An eager
evaluation strategy is precluded in any declaration for which the
#blackbox+ directive is selected. Otherwise, it is indicated if
the #circuit+ directive is selected for the declaration, or if the
circuit function is used anywhere in the declaration.
Although it is not usually necessary, the user can force eager
evaluation by using the #eager+ directive, or forcibly inhibit it
using the #lazy+ directive. Inhibiting eagerness when it should
have been left alone may cause an exponential increase in the time
needed to synthesize a circuit or in the size of the circuit. In other
cases it may save time to inhibit eagerness, such as when most of the
constituent statements in a doall statement have shared inputs
and therefore can not be synthesized as separate circuits.
The main use for these directives is in compiling a library containing
functions returning statements as their results, which are not actually
evaluated on any arguments at compile time. The #eager+ directive
might not be enabled by default in this case, but it should be specified
explicitly unless it is known that the functions will be used only
for Petri nets and not circuits. The #lazy+ directive might be
used if it is known that a library function will not be used for
circuits despite meeting some of the above criteria, as it will allow
a considerable reduction in code size and time needed for library
file compression.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The remainder of this document catalogs the pre-defined functions and
constants in syndi, starting with those that are likely to be
most familiar.
For illustrative purposes, functions are shown as the left side of an
application expression with an example argument on the right, as in
double(n). However, functions in syndi are first class
objects and may appear in other contexts. For example, the list of
functions <half,not,double> is a perfectly valid
expression (although not itself a function).
| 3.1 Arithmetic Operators | ||
| 3.2 Projection Functions | ||
| 3.3 List Mutators | ||
| 3.4 Functional Combinators | ||
| 3.5 Components | ||
| 3.6 Circuits | ||
| 3.7 Names | ||
| 3.8 Statements |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The following arithmetic functions are useful for manipulating compile-time constants.
double(n)
half(n)
not(x)
odd(n)
predecessor(n)
successor(n)
equal(x,y)
equal; due apologies to theoreticians)
sum(n,m)
product(n,m)
quotient(n,m)
mod(n,m)
difference(n,m)
range(n,m)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Some general functions are provided for building lists and tuples or extracting their components.
In the following descriptions, (x1..xn) is written for an n-tuple, and
<x1..xn> for a list of n items. Only binary tuples are actually
provided in syndi, but expressions like (x1,x2,x3) may be written
as an abbreviated form of (x1,(x2,x3)).
There is no semantic distinction between an expression x and the unary
tuple (x), but the unit list <x> differs from x.
A non-empty tuple is any tuple other than (), and a non-empty list
is any list other than nil or <>. Functions defined only
for non-empty lists or tuples will throw an exception in the alternative.
left(x1..xn)
right(x1..xn)
(x2..xn)
swap(x1..xn)
((x2..xn),x1)
head<x1..xn>
tail<x1..xn>
<x2..xn>
identity(x)
cat(<x1..xn>,<y1..yn>)
<x1..xn,y1..yn>
cons(x0,<x1..xn>)
<x0,x1..xn>
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Continuing with the conventions of the previous section, a few more operations particular to lists are also available.
reverse<x1..xn>
<xn..x1>
roll<x1..xn>
<x2..xn,x1>, or return the empty list if the argument is empty.
flat<<a0..an>..<z0..zm>>
<a0..zm>
transpose<<a0..an>..<z0..zn>>
<<a0..z0>,<a1..z1>..<an..zn>>
unzip<(x0,y0)..(xn,yn)>
(<x0..xn>,<y0..yn>)
distribute(d,<x0..xn>)
<(d,x0)..(d,xn)>
zip(<x0..xn>,<y0..yn>)
<(x0,y0)..(xn,yn)>
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A number of functions that take functions as arguments or return
functions as results lend themselves to a particularly expressive
style of programming, and are provided in syndi for the
benefit of users who are disposed to take advantage of them.
apply(f,x)
f(x)
apply_to(x) f
apply_to(x) f
is just an alternative way of writing apply(f,x).
fix(h)
map(f)
<x1..xn> to <f(x1)..f(xn)>
constant(k)
lift(f)
bu(f,k)
fold(f,k)
nil) = k, and g(cons(h,t)) = f(h,g(t))
tuple(f1..fk)
follow<f0..fn>
<f0..fn>, construct
a function g such that g(x) = f0(..fn(x)) for all
arguments x, or is the identity function if the
list of functions is empty
if_then_else(p,f,g)
(p,f,g) construct a
function h such that h(x) = g(x) for values of
x where p(x) has a value of 0 or nil, but h(x) = f(x)
whenever p(x) has some other value
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Components are a primitive data type in syndi that may be used
for constructing circuits. A component is best envisioned as a box
with a fixed number of terminals, of which some are inputs and some
are outputs.
Although the terminals are anonymous, they are ordered, so it is meaningful to speak of the first input terminal on a component, the last output terminal, etc..
It is possible for an output terminal on a component to be connected to an input terminal on the same or another component to form a network, but such a network may be viewed as yet another component. Different terminals on the same component are not necessarily equivalent for the purpose of connections.
Components interact with their environment or with other components with which they are connected by sending and receiving signals or transitions on their terminals.
| 3.5.1 Primitive Components | ||
| 3.5.2 Component Families | ||
| 3.5.3 Component Constructors |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The following components are pre-defined. These are useful mainly as degenerate case results when defining component-valued functions, similarly to the way zero is useful in mathematics.
dead_source
diverter
diana manual for the
semantics.
iwire
wire
live_source
sink
unsafe_sink
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
These functions map numeric arguments to components whose input and
output arity depends on the arguments. Components obtained from these
functions have efficient DI decompositions that will be used by
syndi in the construction of any netlist derived from them, as
well as an efficient Petri net representation. It is therefore more
efficient to use these where possible than to obtain equivalent
circuits by other means.
arbiter(n)
celement(n)
decision_wait(n,m)
fork(n)
majority(k,n)
(k,n) this function returns a k-of-n majority gate.
The device has n inputs and one output. It acknowledges with a transition
on the output whenever any k of the inputs is signaled.
sparse_decision_wait<(r,c)...>
mutex(n)
random(n)
sequencer(n)
toggle(n)
waveform(<<t1..tn>...>,<<s1..sn>...>)
xor(n)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A small selection of functions operating on components is sufficient to allow arbitrarily complex components to be built from simpler ones. In fact, only the first two are really necesary.
zed(x)
arr<x1..xn>
popout(n) x
pushin(n) x
popout, but pertains to inputs
rather than outputs, and moves the last inputs to the first positions
instead of the first to the last.
route(<x1..xn>,<y1..ym>) z
snip(<a1..an>,<b1..bm>) x
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Circuits are another primitive type in syndi, that are similar
to components in that they can be envisioned as boxes that interact
with their environment by sending and receiving signals on their
terminals. However, unlike a component, the terminals on a circuit are
unordered, and have names associated with them. Furthermore, whereas a
component can not be written to a file with the #petrinet+ or
#circuit+ directives, a circuit can. (Either can always be
included in binary library files, as definitions of any type can.)
There are no pre-defined circuits, but there are three functions that allow circuits to be constructed.
circuit(x)
pins(<a1..an>,<b1..bm>) x
pins returns a function that
transforms a component to a circuit (c.f., snip). The names of
the input terminals <a1..an> are on the left and the names of the
output terminals <b1..bm> are on the right. The names are required to
be mutually distinct. The function returned by pins may only be
applied to a component x having matching numbers of input and output
terminals, n and m, or else an exception will be thrown.
connected<x1..xn>
The appropriate operation with shared inputs (i.e., the one to make all the commutative diagrams commute) would call for some sort of non-blocking arbitration between them, which does not have any reasonable circuit-level semantics, and hence causes an exception.
However, the connected function is polymorphic and will also
operate on lists of statements to give a combined statement. In
this case, shared inputs are allowed, and the statement can then
be converted to a circuit.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Names are not only a lexical class but a primitive data type in
syndi, and have a few operations intended to make them more
useful. These may assist in the management of large specifications by
allowing their names to be handled with some uniformity.
instance(n) x
instance(n) x will be a circuit or
statement similar to x, except that wherever a signal name s appears
in x, the name n.s will appear in instance(n) x. Multiple
instances of the same circuit or statement with different values of
n could therefore be used in the same context without having any names
in common. Nested instances are also acceptable.
list(n) x
range).
x.i,
where i is a number from zero to n-1, in order of increasing i.
s.0, the second will have s.1,
etc..
(y,z), the result will be a list of tuples whose
left sides are the items of list(n) y and whose right sides
are the items of list(n) z.
<y0..yn>, the result will be a list of lists, whose
k-th item will be list(n) yk
dot(x,y)
. operator, and is used
for combining names. Both arguments should be either names, numbers,
lists of names, or lists of numbers. Alternatively, the right argument
could be a pair thereof, (w,z), and the result will be
dot(x,dot(w,z)). The reserved names + and * have a
special interpretation as explained in 2.1 Lexical Elements.
Otherwise, the following rules apply.
x.y.
<y0..yn>, the
result is the list of names <x.y0..x.yn>
<x0..xn> and y is a name or number, the result
is the list of names <x0.y..xn.y>
<x0..xn> and y is a list <y0..ym>, the result is the
lists of all names xi.yj, where i ranges from zero to n, and j
ranges from zero to m, in the order <x0.y0,x0.y1...>
rename<(a0,b0)..(an,bn)> x
(ai,bi)
to a function that changes the names of the signals in a circuit or
statement x. Every name ai in x is changed to the
corresponding bi. The assignment of names must be unambiguous and
the names in the result must be unique, or else an exception is
thrown. However, it is not required to rename every signal in x,
nor is every ai required to appear in x. Those that are not
renamed retain their original values.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Statements are another primitive type in syndi, and are
comparable to circuits in that they represent entities that interact
with their environment or with other statements by exchanging named
input and output signals. A statement can also be written to a file as
a Petri net or a netlist by way of the #petrinet+ and
#circuit+ directives. Unlike circuits, statements are specified
in a procedural style as the term suggests, making them a more
abstract description, and are supported by certain combining forms
that are not applicable to circuits.
By analogy with conventional programming languages, statements in
syndi may be considered to begin executing and perhaps to
terminate. Unlike conventional languages, statements are not
inherently constrained to execute sequentially or at predictable
intervals.
| 3.8.1 Communicating Statements | ||
| 3.8.2 Compound Statements | ||
| 3.8.3 Degenerate Statements | ||
| 3.8.4 Predicates | ||
| 3.8.5 Loop Statements | ||
| 3.8.6 Conditional Statements |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The following functions take a list of signal names to a statement that engages in an input or output transaction with its environment. In the case of an empty argument, each of these functions returns a statement that starts executing and then immediately finishes. (The distinction between such a statement and one that never even begins executing is important for reasons discussed in 3.8.2 Compound Statements.)
get<s1..sn>
get function will eventually receive them as a group and
then terminate. If none of the signals or only some of the signals are
available, the statement will be unable even to begin executing, and
therefore will not receive any signals such as may be
available.
getany<s1..sn>
getany may take the opportunity to
begin executing, receive it, and then terminate. If multiple signals
in the list are available, the statement may make a non-deterministic
choice as to which one to receive. If no other concurrently executing
statement is able to receive them, the getany statement
eventually will.
getput<s1..sn>
put<s1..sn>
putany<s1..sn>
putget<s1..sn>
putget statement is analogous to getput, except that
it begins by transmitting rather than receiving, and then alternately
transmits and receives. It is therefore free to begin executing
regardless of whether any of the inputs in the list is available at
the time.
unget<s1..sn>
put statement only in that the signals in the
list are nevertheless regarded as inputs.
It is almost always a programming error for the argument to any of
these functions except getput and putget to contain
repeated signals, but this condition is not checked.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are three basic ways of combining statements so as to influence their schedule of execution relative to one another. Each of these functions takes a list of statements as an argument and returns a statement as a result.
do<s1..sn>
do
statement is not able to begin executing unless the first statement in
the list is able to begin. Subsequent statements in the list that
would normally be able to begin immediately, such as put and
unget, are not able to begin until their predecessor in the list
terminates. The do statement itself terminates when the last
statement in the list terminates.
doall<s1..sn>
doall statement works by allowing all of its constituent
statements to begin concurrently executing immediately. The entire
statement is considered to have begun executing as soon as any one
of the statements in the list begins, but does not terminate until
all of them begin and then terminate.
doany<s1..sn>
doany statement also allows all of the statements in the list
to begin executing immediately, but as soon as one of them begins,
the others are barred from beginning. The whole statement
terminates when the single statement that began executing terminates.
With any of these functions, the statements in the argument list may have some signals in common. Any signal that is an input on one statement and an output on another is exchanged internally and becomes neither an input nor an output for the resulting statement. Any output signal common to multiple statements may be transmitted to the environment by any of them, but multiple concurrent transmissions of the same output are always an error. Any input signal common to multiple statements will be received by at most one of them for each time it is transmitted. If more than one is concurrently executing, the recipient will be determined at random.
The issue of precise criteria whereby statements are said to begin
execution is important mainly because of the doany statement,
which has no equivalent in conventional programming languages but is
essential for concurrent systems. The foregoing specifications imply
that the statement doany<get<a,b>,get<b,c>> will
always terminate successfully if either group of signals <a,b> or
<b,c> is sent to it. On the other hand,
doany<doall<get<a>,get<b>>,doall<get<b>,get<c>>>
might not. In this case, either statement may begin executing if b
arrives first. With no possibility of backtracking after it begins,
deadlock occurs if the next signal to arrive does not match the one
required by the statement that has already started. Note also that by
design there is no provision in the language to stipulate the order in
which signals will arrive, regardless of when they are sent.
Whether a doany statement behaves deterministically or not
depends on the starting criteria of its constituent statements and the
signals provided by the environment. Normal practice would impose
determinacy by having each statement within the doany begin with
an indivisible input operation (e.g., a get statement) whose
signals are not a subset of the ones in any of the other statements.
Non-determinacy prevails only when multiple statements are initially
enabled, for example when they all begin with an output.
An explicit means of expressing a non-deterministic choice among any
list of statements regardless of their starting criteria is provided
by the pre-defined start statement. A statement of the form
do<start,s1..sn> is always free to start in any
environment, and hence a list of them in a doany statement will
mean that one is chosen entirely at random. As this effect is rarely
intended, the explicit use of the start statement indicates
clearly when it is.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
These statements have no useful purpose in themselves but are sometimes convenient for their algebraic properties.
start
start statement does nothing but immediately start executing and
then finish. This statement is a right identity but not a left identity
in a do statement. That is, do<s,start> is
equivalent to s, but do<start,s> is not, for reasons
explained in 3.8.2 Compound Statements. However, it is a left and a right
identity for doall.
hang
do<s0..sn,hang,t0..tn> is
equivalent to do<s0..sn,hang>. It can be used to make a
statement deliberately deadlock.
fail
hang, and the additional properties that both expressions
doany<s0..sn,fail,t0..tn> and
doall<s0..sn,fail,t0..tn> are equivalent to
fail. When used as part of a circuit specification, it may
sometimes facilitate optimization during synthesis by explicitly indicating
"don't care" states reached through prohibited input combinations.
In addition to these statements, it is possible to define an identity
for doany as getput<x,x>, i.e., that which
never starts and never finishes, where x is any name. This
statement is not predefined because no English word adequately describes
it, but it may also be expressed as doany<>. Analogous
expressions for the above statements are also valid, e.g.,
putget<x,x>, doall<> or do<> for
start, do<start,getput<x,x>> for hang,
and doall<unget<x>,put<x>> among others for
fail.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Predicates are a primitive data type in syndi and are used in
conditional statements, just as they are in conventional programming
languages. Predicates are not statements themselves and are useful
only within a statement.
A very informal description of predicates in syndi would be
that they test whether certain combinations of signals are available
to be received from the environment or from other concurrently
executing statements, that they direct the flow of control in a
statement depending on the test result, and that when a test is
successful, they indivisibly receive the relevant signals on behalf of
the statement as a side effect.
Four predicate constructors are provided.
got<s1..sn>
get).
gotany<s1..sn>
getany).
all<p1..pn>
all
predicate as a whole does not succeed and no signals are received.
any<p1..pn>
any
predicate still succeeds, but only the set of signals associated with
one of the successful predicates in the list chosen at random is
received.
Note that the availability of signals is the only condition that can
be tested. A statement may use a predicate in order to wait for a
combination of signals to become available and then do something. It
could also go ahead and do something without waiting. In either case,
there is no need for negation of predicates, and none is
provided. (The not function described in 3.1 Arithmetic Operators
pertains only to compile time constants.)
Unlike the situation with doall and get, a predicate of
the form all<got<a>,got<b>> is semantically
equivalent to got<a,b>. Predicates are always transformed
internally by syndi to sum-of-products form according to Boolean
algebra. A statement will never deadlock due to merely testing a predicate
(but of course, a predicate receiving signals can cause other statements waiting
for those signals to deadlock).
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As in conventional programming languages, statements in syndi
can be made to execute repeatedly, either forever, for a fixed number
of times, or until some condition is met. The following pre-defined
functions accomplish these objectives.
forever(s)
do statement would never execute.) The statement returned will be
observationally equivalent to an endless list of copies of s in a
do statement. E.g., if s performs a single handshake with its
environment, the result will perform infinitely many. Often a
statement intended for circuit synthesis will be of the form
forever(s) at its top level.
for(n) s
do (map constant s) range(1,n). Large values of
n may cause infeasibly large circuits.
until(p) s
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Conditional statements are provided in syndi using predicates,
as well as by other means. Where predicates are concerned, there is an
element of non-determinacy compared to what one might expect from
conventional programming languages, because signals in transit are not
ordinarily certain to be readily receivable.
if(p) s
wait_until(p)
hang_if(p)
hang)
fail_if(p)
fail)
assuming(e) s
assuming(e) s will be a statement that is able
to interact with its environment in whatever ways s interacts with e,
but eschews any capabilities of s that this interaction does not
require.
Some comments on these statements are in order. Whereas if
statements are very common in sequential programming, they would be
used only in unusual circumstances in syndi. The method of
choice for writing specifications requiring very complex conditions
should be with a wait_until statement at the beginning of each
do statement inside of a doany statement.
The assuming statement is useful for efficient verification. If
a statement of the form assuming(e) s is written to a file as a
Petri net listing using the #petrinet+ directive, the file will
contain a closed Petri net rather than one with open transitions. No
existing Petri net analysis tools other than diana can cope with
open Petri nets, and closed Petri nets are helpful even for
diana because their state space is often exponentially smaller.
The assuming statement is also useful for efficient circuit
synthesis. A specification in the absence of any information to the
contrary will call for the synthesis of a circuit that is as receptive
as possible. That may mean arbitration between concurrent inputs and
buffering of inputs received in advance of their use. If it
is known that the deployment environment is less demanding, the
incorporation of the actual requirements through the assuming
function will allow syndi to synthesize a simpler circuit.
The fail_if statement should never be used in a practical
circuit specification. The closest it comes to being useful is in
expressing combinations of inputs at points in the code where the
specification is not required to cope with them, allowing syndi
to synthesize a more efficient circuit. However, this purpose is
better served by the assuming statement, which is more general,
more readable, and more efficient.
Where the fail_if statement should be used is in the
specification of an environment that will be written to a Petri net file
to be used as the test environment along with a circuit to be verified
by diana. By making the environment fragile enough to fail on
those combinations of outputs that the circuit is not supposed to
send, bugs in the circuit can be easily detected.
The hang_if statement serves a similar purpose to fail_if.
If verification is performed with diana, there is probably no
good reason to use it. However, the hang_if statement
effectively allows any circuit verification problem to be converted to
a deadlock detection problem, and deadlock detection is a vigorously
studied subject for which better tools than diana may well be
possible to find. In this case, the hang_if statement might for
example be used in constructing the environment e in a statement of
the form assuming(e) s, which will yield a closed Petri net
suitable for analysis by generic tools (subject to file format
conversion).
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Circuits synthesized by syndi always look plausible, but until
it reaches a 1.0 release, syndi can be recommended for production
use only in conjunction with a strict policy of verifying these
circuits using diana. (It is unlikely that separate bugs in these
two programs would collaborate maliciously, and diana
has also been somewhat more thoroughly tested.) This caveat is less of an
issue when syndi is used for constructing Petri net models or
for semi-automated design using component families, which have been
subject to greater scrutiny.
On non-GNU systems, time stamps on files aren't detected properly, and
dependences listed in library and netlist files will always indicate
an unknown date. This problem is due to a portability issue with
avram rather than a bug in syndi.
The type inference isn't fool-proof. There is a remote possibility of a statement recurrence being mistaken for a function, making it inexpressible as a circuit or Petri net.
No other specific bugs are known, but any error message that doesn't look like it's intended to be self-explanatory is probably a sign of a bug. Any feedback would be greatly appreciated.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Copyright © 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and an idea of what it does. Copyright (C) 19yy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. |
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice |
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
| [Top] | [Contents] | [Index] | [ ? ] |
1. Usage
1.1 Options2. Syntax
1.2 Files
2.1 Lexical Elements3. Semantics
2.2 Comments
2.3 Directives
2.3.1 Executable Files
2.3.2 Eagerness
3.1 Arithmetic Operators4. Status
3.2 Projection Functions
3.3 List Mutators
3.4 Functional Combinators
3.5 Components
3.5.1 Primitive Components3.6 Circuits
3.5.2 Component Families
3.5.3 Component Constructors
3.7 Names
3.8 Statements
3.8.1 Communicating Statements
3.8.2 Compound Statements
3.8.3 Degenerate Statements
3.8.4 Predicates
3.8.5 Loop Statements
3.8.6 Conditional Statements
GNU GENERAL PUBLIC LICENSE