A circuit description in the Iowa Logic Specification Language consists of 5 sections. These sections describe the circuit name, the circuit inputs, the circuit outputs, the parts used to make the circuit, and how the inputs, outputs, and parts are to be wired together. These sections must be given in the order listed.
Circuit descriptions are free format, so the indenting rules used in the following material are actually optional. The punctuation marks comma and semicolon are also optional, but their use is strongly encouraged, as they greatly improve the readability of circuit descriptions.
Comments may be included in circuit descriptions, and their use
is encouraged. Blank lines may be used to delimit or separate blocks
within a description.
Marginal comments (along the right margin, as in assembly language)
may be used, set off from the circuit description text by pairs of
dashes (--
). Comments within the circuit description may be
bracketed with curly brackets (as in Pascal, where {
and }
or
(*
and *)
mark comments).
Within a circuit description, a number of identifiers are used to name such things as gates and input or output connections. Identifiers may consist of any number of characters as long as the indentifier fits on one line, and as long as it consists of a letter followed by a sequence of letters and digits. All characters of an identifier are significant, and reserved words may not be redefined.
Every logic circuit description must begin with a declaration
of the circuit name.
Name declarations begin with the keyword circuit
, followed by
an identifier used as the circuit name.
For the example circuit, which turns out to be a type D latch,
the following declaration might be used:
By convention, the circuit designers name should be put in a comment in the circuit header, as shown above.circuit dlatch; -- designed by D. W. Jones
Most logic circuits will have one or more input connections.
These must be declared immediately following the circuit name using the
keyword inputs
, followed by a list of input names.
For the example circuit, the inputs may be declared as follows:
It would be better to put a comment on each input explaining what it is for, as in the following:inputs d, c;
Spelling out the appropriate mnemonic names would have eliminated the need to include these comments, but long names for the inputs or outputs can lead to hard to read simulation results (see Chapter 3), and they do not improve the readability of small circuits such as the example.inputs d {data}, c {clock};
To be worth simulating, a logic circuit must have output
connections. These are declared immediately after the inputs, using
the keyword outputs
and a list of output names.
For the example circuit, the following declaration would be
appropriate:
Note thatoutputs q {the stored data value}, qbar, dout {echo input d}, cout {echo input c};
qbar
needs no comment, since (by convention), the suffix
bar
is used to indicate that this is the inverse of q
.
After the inputs and outputs are declared, any parts used in the
circuit must be declared. (Trivial circuits without parts are
possible, since these may consist of inputs wired directly to outputs).
The parts used in a circuit are declared after the keyword parts
.
Each part declaration consists of a list of new part names, a colon,
and the part type to be used for those names.
There are a number of predefined part types, and new part types may
be defined as needed using the subcircuit mechanism.
The predefined part types are listed below:
Note that with many of the Boolean logic gates, the number of inputs is variable and must be specified as a parameter to the part type. It should also be noted that, for the purposes of logic simulation, a three-state bus is a device, even though in a real circuit, it is merely a piece of wire. For the example circuit, there are 4 two-input nand gates, and 1 not gate, so the following parts declaration would do:simple Boolean logic gates:not
- inverterand(n)
- n input and gateor(n)
- n input or gatenand(n)
- n input nand gatenor(n)
- n input nor gatexor
- exclusive or gateequ
- equivalence gate special gates:tsgate
- non-inverting three-state driverntsgate
- inverting three-state driverbus
- three-state buslatch
- type D latch (1 bit memory)
It would be better to put comments on the individual definitions as follows:parts gater, gates, ffq, ffqbar: nand(2); inverter: not;
Commenting could be carried further, but would probably not make the resulting circuit definition any easier to read.parts { basic rs flipflop gates } ffq, ffqbar: nand(2); { gates used to convert d and c to r and s } gater, gates: nand(2); inverter: not;
After declaring the parts, all that is needed to finish the circuit
is a description of how the parts are connected to the
inputs and outputs. This is done with a wire list starting
with the keyword wires
and ending with the keyword end
.
Each entry in the wire list consists of the name of a signal source,
the keyword to
, and a list of names of signal destinations.
Any input to the circuit is a valid signal source, as is the
output connection of any previously declared part. The special
signal sources high
and low
are also provided for
constant inputs.
The valid signal destinations include the outputs of the circuit and
any unused inputs of a previously declared part.
The following table lists the forms of the input and output connections
of each of the types of gates supported by the logic simulator:
part name | type of part | inputs | outputs | |||
a | not | a.in | a.out | |||
b | and(n) | b.in(1) | b.out | |||
or(n) | to | |||||
nand(n) | b.in(n) | |||||
nor(n) | ||||||
c | xor | c.in(1) | c.out | |||
equ | c.in(2) | |||||
d | tsgate | d.control | d.out | |||
ntsgate | d.data | |||||
latch | ||||||
e | bus | e.in | e.out |
b
is the name of a 3 input nor gate, it will have
inputs b.in(1)
, b.in(2)
, and b.in(3)
, and it will have an
output named b.out
. The example circuit performs exactly the same
function as the built-in latch
part type.
The wire list for the example circuit could
be given in the following form, but appropriate comments and blank lines
(as shown in the next section) would make it easier to read:
wires d to gater.in(1), dout, inverter.in; inverter.out to gates.in(1); c to gater.in(2), gates.in(2), cout; gater.out to ffq.in(1); gates.out to ffqbar.in(1); ffq.out to q, ffqbar.in(2); ffqbar.out to qbar, ffq.in(2); end.
Combining the heading, input/output lists, parts list and wire list from the previous sections gives the following description for the example circuit:
In the following sections, it will be assumed that this circuit description is stored in filecircuit dlatch; -- designed by D. W. Jones inputs d {data}, c {clock}; outputs q {the stored data value}, qbar, dout {echo input d}, cout {echo input c}; parts { basic rs flipflop gates } ffq, ffqbar: nand(2); { gates used to convert d and c to r and s } gater, gates: nand(2); inverter: not; wires { distribution of inputs to rs conversion gates } d to gater.in(1), dout, inverter.in; inverter.out to gates.in(1); c to gater.in(2), gates.in(2), cout; { transfer of r and s values to basic rs flipflop } gater.out to ffq.in(1); gates.out to ffqbar.in(1); { basic rs flipflop } ffq.out to q, ffqbar.in(2); ffqbar.out to qbar, ffq.in(2); end.
dlatch
(the convention of using the
circuit name as the file name is strongly encouraged).
Unlike most computer programming languages, the order of the statements in the wire list has no effect on the meaning of the circuit description; nonetheless, careful ordering and grouping of wires is important to human readers. In this example, the wire list has been broken up into functional sections, each headed with a comment.
The Iowa Logic Specification Language has a number of features which allow it to be used to specify large and complex systems of logic gates. Primary among these are a subcircuit mechanism and an array mechanism. The former allows a circuit to be defined once and used many times, while the latter allows input or output connections from a circuit to consist not of individual wires but of named arrays of wires.
In some applications, it is necessary to simulate not only the logical behavior of a circuit, but also the time that the circuit takes to act on changes in its inputs. The Iowa Logic Specification Language provides complete support for specification of such time delays, and the associated simulator accurately simulates them. If time delays are not specified, default delays are assumed that are typical of breadboard circuit construction with TTL gates.
Large digital systems frequently consist of many logical subsystems, and the Iowa Logic Specification Language reflects this by allowing the declaration and use of subcircuits. Any circuit described in the Iowa Logic Specification Language may be used as a subcircuit of some larger circuit. Subcircuits of a circuit may be declared immediately after the circuit name, before the declarations of inputs and outputs. A subcircuit declaration consists of exactly the same parts as any other circuit declaration; it starts with a name, followed by an input list, an output list, a parts list, and a wire list.
When a circuit is used as a subcircuit, its name becomes
a new gate type, and any number of parts of this type may be declared.
For example, if the circuit defined in the previous sections is used as
a subcircuit, any
number of parts of type dlatch
may be declared, as in the following
example:
This declares the partsparts bit1, bit2, bit3, bit4: dlatch;
bit1
through bit4
to be instances of the
circuit dlatch
.
These instances are completely independent of each other; all that they
have in common is that they are described by the same circuit
description.
Connections between an instance of a subcircuit and its user
are made through the inputs and outputs of the subcircuit.
For example, given the above definitions, it would be legal to
connect
bit1
through bit4
into a shift register as follows:
This relies on the fact that each instance ofwires bit1.q to bit2.d; bit2.q to bit3.d; bit3.q to bit4.d;
dlatch
has an output
named q
and an input named d
.
All of the inputs of a subcircuit must be connected for that subcircuit
to function correctly; for example, bit1.c
and bit1.d
must be
connected to make the subcircuit instance bit1
work.
Some subcircuits may have inputs which are not needed in a particular
application; these should be connected to the global inputs high
or
low
, as appropriate.
The outputs
of bit1
are bit1.q
, bit1.qbar
, bit1.cout
,
and bit1.dout
.
It is not necessary to use all of the outputs of a subcircuit.
In many ways, subcircuits are similar to procedures in a conventional
programming language. Thus, it is correct to talk about the main
circuit and the subcircuits it uses. The inputs and outputs of
a subcircuit are similar to procedure parameters, since they allow
communication between the user of a subcircuit and the subcircuit.
The Iowa Logic Specification Language has scope rules similar to those
of Pascal.
If subcircuit X
is declared within subcircuit Y
,
then X
may only be used in Y
and in other subcircuits
declared in Y
. That is, X
is considered to be a
local subcircuit of Y
.
The Iowa Logic Specification Language provides a text inclusion statement
as an alternative to physically including the text of a subcircuit
in the definition of a circuit which uses it.
The text inclusion statement consists of the keyword use
followed by
the name (or pathname)
of the file where the desired subcircuit definition is to be
found. Use statements are only allowed in places where a subcircuit
definition would be legal.
For example, to use the circuit dlatch
as a subcircuit,
assuming it is stored in a file with the same name,
all that would be needed is the following statement:
The use statement textually inserts the contents of the named file into the original source file as the logic simulator reads it. The file referenced by a use statement may contain a sequence of subcircuit or constant definitions, and each subcircuit in the referenced file may contain local subcircuits and other definitions.use dlatch
Files referenced by use statements may contain other use statements, but such nesting should be limited to 3 levels beyond the original source file (the logic simulator can have no more than 4 open input files at any time). Circuits with deeply nested use statements can frequently be fixed by putting all of the use statements in the main circuit, in effect, making all of the used subcircuits global. Note that use statements do not introduce any scope rules!
Try to avoid multiple use statements referencing the same file, since this will result in a waste of memory and time when the logic simulator processes a circuit. If two subcircuits both require definitions from the same file of definitions, put a use statement for that file global to both subcircuits instead of two use statements, one local to each subcircuit.
The examples given above suggest the construction of a shift register made of type D latches. The complete specification of this circuit would have the following structure:
In this example, note that there is no conflict between the inputcircuit dregister; -- designed by D. W. Jones circuit dlatch; inputs d {data}, c {clock}; outputs q {the stored data value}, qbar, ... rest of definition from Section 2.6 ... end; -- dlatch -- start of dregister inputs i { the data to shift into the register }, c { the clock signal to shift the register }; outputs o1, o2, o3, o4 { output the 4 bits of the register }; parts invert: not { used to invert the clock }; bit1, bit2, bit3, bit4: dlatch { storage for bits }; wires { wires for the data flowing through the register } i to bit1.d; bit1.q to o1, bit2.d; bit2.q to o2, bit3.d; bit3.q to o3, bit4.d; bit4.q to o4; { wires for the clock signals of the register } c to bit1.c, bit3.c, invert.in; invert.out to bit2.c, bit4.c; end. -- dregister
c
of the circuit dregister
and the input c
of the subcircuit
dlatch
. Within the subcircuit, the name c
is the input to that
subcircuit; outside the subcircuit, c
is the input to the entire
circuit, and bit1.c
through bit4.c
are the inputs to the
instances of the subcircuit.
Within the subcircuit, c
is a source of data; outside the subcircuit,
bit1.c
through bit4.c
are destinations.
As with the previous example, commenting and indenting have been used here to help the reader find logically related wires and to set off the subcircuit from the main circuit. Note the comment at the end of each circuit and subcircuit which gives the circuit or subcircuit name. Also note that a comment is included after the end of the subcircuit definition to help the reader find the start of the main circuit.
This example of the use of subcircuits could be written in more
compact form with the aid of the use
statement.
Assuming that the circuit dlatch
is stored in a file with that
name, the file defining the shift register would have
the following form:
circuit dregister; -- designed by D. W. Jones use dlatch; -- start of dregister inputs ... rest of definition as given above ... end. -- dregister
The Iowa Logic Specification Language allows inputs, outputs or parts to
be declared as arrays. This is done by declaring a named range of allowed
subscripts with a range
declaration, and then using the range
identifier as a subscript on the input, output, or part definition.
Declarations of ranges and named constants may appear anywhere a subcircuit
declaration is allowed, that is, between the circuit header and the
input
keyword, or in a file included by a use
statement.
The outputs o1
through o4
and the
parts bit1
through bit4
used in the dregister
example in the previous
section would much more appropriately have been declared as arrays.
The following example illustrates this:
In this example, the outputrange nibble = 1 .. 4; inputs i, c; outputs o(nibble); parts invert: not; bit(nibble): dlatch;
o
is a 4 bit wide output array,
with subscripts ranging from 1 to 4. Similarly, bit
is a part array.
Arrays of inputs, outputs, and parts may be interconnected one wire at a time, explicit iterators may be used, or implicit iterators may be used. In the context of the above definition, it would be legal to make individual connections as follows:
If such an interconnection pattern is desired, a much more direct expression of the pattern can be made with an iterator.wires i to bit(1).d; bit(1).q to o(1), bit(2).d; bit(2).q to o(2), bit(3).d; bit(3).q to o(3), bit(4).d; bit(4).q to o(4);
Iteration in the Iowa Logic Specification Language is done with a for
statement; for
statements control the iteration of a wire list, and
they may appear anywhere in the body of a wire list. This implies that
for
statements may be nested. The following
example illustrates the use of a for
statement equivalent to the above:
Note that thewires i to bit(1).d; for j in first(nibble)..last(nibble)-1 do bit(j).q to o(j), bit(j+1).d; endfor; bit(4).q to o(4);
for
statement declares an identifier, j
in this
example. The range of values over which this identifier is iterated is
given by the expression between the keywords in
and do
. The order in which values
are assigned to j
is not specified and has no effect on the meaning
of the result.
The identifier declared by a for
statement must not have been
declared as a local identifier of the current circuit or of an enclosing
for
statement. The identifier may override an identifier of the
same name declared in an outer block, and two for
statements in
the same block may use the same identifier if neither encloses the other.
Implicit iteration may be applied to wiring outputs of one subcircuit to
inputs of another. It is applicable whenever the source pin and destination
pin of a wire list entry are arrays of the same dimension.
For example,
if byte
is a global range, perhaps declared as 8 bits, the following
would be legal:
This single wire list entry actually implies the followinginputs a(byte); outputs b(byte); wires a to b;
for
loop:
wires for i in byte do a(i) to b(i); endfor;
The expression giving the range over which a for
loop iterates may
be the name of a range or an explicit statement of the upper and lower
bounds of the range. Although numerical bounds may be used, it is
usually preferable to state the bounds in symbolic terms.
The predicates first
, last
, and size
are provided
in order to simplify the construction of new ranges from old.
These extract the lower bound of a range, the upper bound of a range, and
the size of a range.
For example, the following would be a legal
iterator equivalent to the one used in the array version of dregister
:
For the example declaration offor i in first(nibble) .. first(nibble) + size(nibble) - 2 do
nibble
, this iterates over the range
1..3.
Note that would have been legal to use the explicit range 1..3 in this
context, but that this would make it hard to change the size of the
register. A simple change to one range declaration is much easier than
a search through the code for all related range expressions.
Constant declarations allow commonly used constants and
the values of commonly used expressions to be named. The keywords
range
, integer
, real
, boolean
and time
introduce constant declarations for constants of the associated types.
As already mentioned in the context of range constants, these may be put
anywhere a subcircuit declaration is allowed.
The following example illustrates
a typical use of constant declarations:
The types of expressions follow from the types of their components in obvious ways. Therange word = 0 .. 15; integer wordsize = size(word); integer doublewordsize = 2 * wordsize; range doubleword = 0 .. doublewordsize - 1;
time
type obeys the usual rules for time intervals; thus,
times may be added or subtracted, times may not be multiplied, and the ratio
of two times is a real number, not a time.
The identifiers created by range
and constant declarations obey all
of the hierarchic
scope rules of the specification language. Thus, it is legal, and in fact,
common, to declare a range in the main circuit and then use that range in
subcircuits. This provides a major abstraction facility, since it allows
circuits to be interconnected without knowledge of the actual numbers of wires
needed to interconnect them.
In some contexts, a subcircuit may need to expand differently depending on
the context of the expansion; for example, additional gates may be needed if
the number of elements in an array is greater than some threshold. In this
case, conditional part and wire lists can be added. The notation used for
this is based on the familiar if
then
else
endif
constructs of programming languages. Note that an if
construct must
always end with the keyword endif
. Within an if
construct, the
phrase else if
is allowed for testing secondary Boolean conditions;
the phrase else if
does not introduce a new if
construct,
it only introduces an alternate then
clause.
For example, consider a circuit for adding two numbers. This might use the
global range word
to describe the structure of a word. Suppose that
a simple adder, with ripple carry, is preferable when there are fewer than
K bits in a word, but that fancy carry anticipation logic is desired for
larger word sizes. A generic adder circuit could include conditional sections
in its part and wire lists so that it selects one of the two implementations
depending on the word size. This might be done as follows:
parts if size(word) < K then { use ripple carry } adders(word): fulladder; else { use carry anticipation } adders(word): addanticipate; lookahead(1 .. size(word)/4): anticipator; endif; wires if size(word) < 1 then { something is wrong } error; else if size(word) < K then { use ripple carry } ... else { use carry anticipation } ... endif; end { adder };
Note that the conditional structure of the parts list will frequently mirror
that of the wire list. This is because each change in the parts list
frequently requires a corresponding change in the wire list. It is legal
to declare parts with the same name in both the then
and else
clauses, but this can be very confusing unless the parts correspond in some
way. In the above example, the part adders
has two possible
definitions depending on the size of a word. This would be sensible if
the circuit addanticipate
is similar to fuladder
in both
function and input/output pin naming conventions. For example,
addanticipate
might have the same pin names as fulladder
for the two addends, the sum, and the carry in and out, but it might have
additional input and output pins for communication with the anticipation
units.
The Boolean expression controlling an
if
statement must be composed using the usual relational and Boolean
operators; these include >
, <
, =
, <=
, >=
and <>
(not equal), as well as &
(and), |
(or) and
\
(not),
with parentheses allowed to control the order of evaluation.
It is sometimes useful to make generic subcircuits which can be parameterized
when they are used in a parts list. For example, the predefined gates are
parameterized with the number of input pins they allow, and they have an
optional time delay as a parameter. Subcircuits may have a
formal parameter list following the circuit name. This may include formal
parameters of types integer
, real
, time
, range
or circuit
. The following example illustrates this:
This example will only work if the subcircuit passed as parametercircuit generic( circuit x; integer n ); inputs i, c; outputs o(1..n); parts bit(1..n): x; invert: not; wires i to bit(1).d; for j in 1 .. n-1 do bit(j).q to o(j), bit(j+1).d; endfor; bit(n).q to o(n); c to invert.in; for j in 1 .. n / 2 do c to bit(2 * j - 1).c; invert.out to bit(2 * j).c; endfor; end;
x
has inputs called c
and d
and an output called q
.
The subcircuit dlatch
meets these criteria, so it would be legal
to include a: generic(dlatch,4)
in a parts list where generic
and dlatch
are visible. As a result of
this, a
would have exactly the same behavior as an instance of
circuit dregister
given previously.
The combination of parameterized circuits with conditional part and wire lists allows recursive circuit descriptions. For example, an n input decoder with k auxiliary enable inputs can be described either as an inverter and a pair of k + 1 input and gates, in the case where n = 1, or as an inverter and a pair of n - 1 input decoders with k + 1 enable inputs, in the case where n > 1.
For most purposes, a logic description such as is given above will be sufficient, but there are times when it is desirable to simulate not only the static interconnection of a circuit, but also the timing characteristics of that circuit. The Iowa Logic Specification Language allows timing characteristics to be specified for both logic gates and wires. If timing characteristics are not given, the logic simulator will assume defaults typical of kluge card or breadboard implementations of TTL circuits (gates are given a nominal propagation delay 10ns (nanoseconds), and wires are given delays between 0.5ns and 1.5ns, corresponding to wires from about 15mm to 45mm long).
If the default gate propagation delay is not appropriate, an explicit delay can be specified as an additional parameter on the part type. This is illustrated in the following example:
In this example, gatesparts a: not; b: not(1.5 * ns); c: nand(2); d: nand(2, 2 * ns);
a
and c
have the default delay, while
gates b
and d
are relatively fast gates,
with delays of 1.5 and 2
nanoseconds.
Delays are expressions or constants of type time
.
These are usually constructed by multiplying or dividing a predefined time
constant by a factor, as in the above examples. Times may be added or
subtracted to yield new times, but they may not be multiplied, and division
of two times yields a real number, not a time.
The predefined constants of type time
are:
The symbol
s - second ms - millisecond (ms = s/1000) us - microsecond (us = ms/1000) ns - nanosecond (ns = us/1000)
u
in the abbreviation of microseconds is used to approximate
the shape of the Greek letter (mu) which is a standard abbreviation
for the prefix micro.
It should be noted that, during the simulation of a circuit, the propagation delay specified for a gate is modified by a small random factor each time it is used. Thus, a gate with a nominal 10ns delay will have actual delays ranging from 9.5ns to 10.5ns. This jitter in the propagation delay results in slightly unpredictable results for some circuits, but it is necessary to make flipflops decay realistically to one or the other of their stable states after being simultaneously set and reset.
If the default signal delay along a wire is not appropriate,
a specific delay can be given as a parameter on the to
keyword
used in the wire list part of the circuit description, as shown
in the following example.
Here, note that all of the destination connections listed on any particularwires a.out to(15 * ns) c.in(1); a.out to(1 * ns) c.in(2), b.in;
to
clause have the same delay, so a separate to
clause must be used for each different delay which is
specified.
In the above example, a.out
is connected to three different inputs,
using two different delays.
If subcircuits are used, delays may be specified in both the connections to an instance of a subcircuit, and in the connections from the inputs and outputs of the subcircuit to its components. The Iowa Logic Simulator will correctly sum these delays in order to determine the total time taken by a signal as it travels from one component to another.
Some circuits require periodic clocks for their operation; these may be constructed from an inverter with its output fed back into its input. The period of the clock will be twice the delay of the inverter, so a clock with a period of 1s may be constructed from an inverter with a delay of 500ns; the same effect may be obtained by using an inverter with a shorter delay and putting extra delay in the wire connecting the output of the inverter back to its input. Note that the technology used to construct a gate determines its delay, and thus, that real gates have nothing analogous to a delay parameter. Furthermore, long wires used to achieve long delays are impractical: a 500ns delay would require about 150 meters of wire, or perhaps 50 meters of coaxial cable. Thus, the periodic clocks used in real systems usually rely on more complex electronic solutions.
The Iowa Logic Specification Language includes the following reserved words:
In addition to these words, the identifierboolean for range circuit if real do inputs then else integer time end mod to endfor outputs use endif parts wires
in
appears to be a keyword
in the context of the third lexeme of a for
statement, but not
elsewhere.
The identifier tally
may be placed before the keyword circuit
in
the heading of the main circuit to request a tally of all parts and
subcircuits used in building that circuit. No predefined meaning is ascribed
to tally
in any other context, and tally
may be freely redefined.
The following identifiers are predefined as input or output
pins of predefined gates. With the exception of in
, they have no
meaning elsewhere, and their
redefinition will not have any effect on their meaning in the context
of the predefined gates or in the context of the for
statement.
in control data out
The following identifiers are the names of predefined global inputs to the circuit; they may be redefined, but such redefinition will mask their predefined meaning.
high low
The following identifiers are predefined constants; they may be
redefined, but such redefinition will mask their predefined meaning.
Of these, true
and false
have Boolean values, the others
are time units.
true s ms false ns us
The following identifiers are names of predefined gates; they may be redefined, but such redefinition will mask their predefined meaning.
Note thatand latch not tsgate bus nand ntsgate xor equ nor or
and
, or
, and not
are not legal operators
in Boolean expressions, but that the symbols &
, |
and \
are used in expressions.
The and
, nand
, or
and nor
gates have a required parameter, the number of inputs; all gates other than
the bus have a second optional parameter giving the time delay of the gate.
The following identifiers are names of predefined functions which may
be used within expressions; they may be redefined, but such redefinition
will mask their predefined meaning. All but odd
take a single range
expression as an argument; odd
takes an integer argument.
first last size odd
Predefined identifiers are considered to be declared globally to the main
circuit. Local declarations within any circuit or subcircuit may redefine
identifiers declared outside that circuit or subcircuit. It is illegal
to redefine an identifier which was locally defined within a circuit or
subcircuit. Although the identifier declared by a for
statement
has a scope limited to the for
statement body, it may not
have the same name as any locally declared identifier within the same
circuit or subcircuit, and it may not have the same name as the identifier
declared by any enclosing for
statement. It is legal for two for
statements in the same wire list to declare the same identifier as long as
the two are not nested.
Expressions may be composed using the following operators, listed in order of decreasing precedence:
(x) f(x) \ |
- nested parentheses, functions, Boolean not | |
** |
- integer exponentiate | |
* / mod & |
- multiply, divide, remainder, Boolean and | |
+ - | |
- add, subtract, Boolean or | |
.. < <= = <> >= > |
- range construction, comparison |
circuit
may be used to declare formal parameters of that type.
The following Extended BNF grammar describes
the Iowa Logic Specification Language. The notation used here is traditional:
angle brackets surround nonterminal symbols, square brackets surround optional
constructs, curly braces surround constructs that may be repeated more than one
time, and a vertical bar is used to separate alternatives.
When one of these metasymbols also occurs as a symbol in the language, it is
quoted.
Lexical details are not covered by this grammar.
There are two top-level constructs, files containing a main circuit, and files
intended to be included by a use
directive; these are defined first.
<main file> ::= [ tally ] <circuit declaration> [ . ] <included file> ::= <subcircuit and constant declarations> [ . ] <circuit declaration> ::= circuit <name> [ <formal parameter list> ] [ ; ] [ <subcircuit and constant declarations> [ ; ] ] [ inputs <identifier list> [ ; ] ] outputs <identifier list> [ ; ] [ parts <part list> ] wires <wire list> end <formal parameter list> ::= ( <formal declaration> { [ ; ] <formal declaration> } ) <formal declaration> ::= <formal keyword> <identifier list> <formal keyword> ::= boolean | integer | real | time | range | circuit <identifier list> ::= <identifier> { [ , ] <identifier> } <subcircuit and constant declarations> ::= <declaration> { [ ; ] <declaration> } <declaration> ::= <constant declaration> | <use directive> | <circuit declaration> <constant declaration> ::= <constant keyword> { <identifier> = <expression> } <constant keyword> ::= boolean | integer | real | time | range <use directive> ::= use <file name> <part list> ::= { <part declaration> | <part list if> } <part declaration> ::= <part names> : <part type> [ ; ] <part names> ::= <part name> { [ , ] <part name> } <part name> ::= <identifier> [ ( <expression> ) ] <part type> ::= <identifier> [ ( <actual parameter list> ) ] <actual parameter list> ::= <expression> { [ , ] <expression> } <part list if> ::= if <expression> then <part list> [ else if <expression> then <part list> ] [ else <part list> ] endif [ ; ] <wire list> ::= { <wire list element> | <wire list if> | <wire list for> } <wire list element> ::= <pin> to [ ( <expression> ) ] <pin> { [ , ] <pin> } [ ; ] <pin> ::= <identifier> [ ( <expression> ) ] [ . <identifier> [ ( <expression> ) ] ] <wire list if> ::= if <expression> then <wire list> [ else if <expression> then <wire list> ] [ else <wire list> ] endif [ ; ] <wire list for> ::= for <identifier> in <expression> do <wire list> endfor [ ; ] <expression> ::= <simple expression> [ <relop> <simple expression> ] <relop> ::= < | <= | <> | = | > | >= | .. <simple expression> ::= [ + | - ] <term> { <addop> <term> } <addop> ::= + | - | "|" <term> ::= <factor> { <mulop> <factor> } <mulop> ::= * | / | mod | & <factor> ::= <fact> [ ** <fact> ] <fact> ::= <identifer> | <function> | <number> | \ <fact> | ( <expression> ) <function> ::= <identifier> ( <expression> )
This grammar is ambiguous. One of the major problems is the keyword
sequence else if
; the parser is greedy, and as a result, it cannot
parse an if
construct at the start of a list of items in an else
clause. The next version of the language will correct this problem by
introducing the keyword elseif
.