module Design:Digital design entry.sig
..end
This release enhances the design entry module of HDCaml 0.2.9 with the following features:
input
, output
, signal
, --
and circuit
.<==
operator.start_circuit/get_circuit
and circuit/endcircuit
get_circuit
)
must not be used before start_circuit
This release uses a workaround which makes it possible to get line numbers at instances of HDCaml errors. It works by raising uncaught exceptions which cause display of stack traces. Compile and run the HDCaml programs like this to make it work:
ocamlc -g -o example_circuit.exe hdcaml.cma example_circuit.ml
ocamlrun -b example_circuit.exe
(Option -g
of ocamlc
causes the executable
to contain debug info
which is needed for the next command.
Option -b
of ocamlrun
causes the run time system to print
a stack trace when an uncaught exception occurs.)
Any function or operator can check for different error or info conditions which are documented as part of the description of each function/operator. There are a couple of common principles, though, which apply to every function/operator:
Error conditions:
empty
signal is not allowed as an argument to any function or
operator which takes type signal
as a parameter. (#)empty
signal,
i.e. arguments which would cause an empty
signal
to be returned are considered erroneous. (#)""
) are not allowed for names.start_circuit/get_circuit
and
circuit/endcircuit
must be balanced, i.e. no opening function
without closing one and vice versa.set_strict_checks
is set to false
.
Info conditions:
Concerning strictness of error checks, the functions and operators of HDCaml can be divided into two classes:
start_circuit
, get_circuit
, circuit
, endcircuit
signal
, <==
, --
input
, output
one
, bit
, msb
, lsb
~:, &:, ^:, |:
+:, -:, *:, *+
==:, /=:, <:, >:, <=:, >=:, <+, >+, <=+, >=+
<<:, <<+, >>:, >>+
mux2
, mux
reg
const
, zero
, ones
select
, ++
, concat
, repeat
, msbs
, lsbs
, split
, bits
empty
, vdd
and gnd
don't belong
to any of the two classes.)
The functions/operators of class 2 are often used to build signals recursively, like it is usual in functional languages with lists. The following similarities can be observed (left side: functional languages, right side: HDCaml functions/operators):
list
<-> signal
[]
<-> empty
::
<-> ++
List.hd
<-> msb
or lsb
(only defined for list/signal <>
[]/empty; msb
and lsb
belong to class 1 therefore)List.tl
<-> lsbs
or msbs
empty
for corner cases, which can also
be useful in generally describing a circuit:const ""
zero 0
ones 0
select signal msb lsb
with msb < lsb
(not very intuitive but status quo)concat []
repeat signal 0
split signal
with width signal < 2
(returns a pair where one or both elements can be empty
)bits empty
(returns [])
You can find additional descriptions of differences between the
two modes further below, at the documentation of the
respective functions/operators. These are of the form:
"(strict: limitations when set_strict_checks true;
;
else: limitations when set_strict_checks false;
)".
val set_strict_checks : bool -> unit
The default setting is true
, i.e. strict checks are active
for all functions/operators, including those of class 2.
This default was chosen to provide HDCaml beginners
with more useful error messages at the start.
Experienced HDCaml users can turn the switch off to enable additional expressive power of HDCaml. This may also be useful to make old HDCaml programs compatible with 0.2.9.1 in an easy way.
You can even have both benefits (more useful checks and more expressive power), although not at the very same time. But you can set strict checks on and off for single functions or parts of your HDCaml program like this:
... set_strict_checks false; some_tricky_recursive_function x y z ; set_strict_checks true; ...
There is no recommended setting of that switch that fits the needs of all users. Just be aware, that you will get potentially less useful error messages if you disable strict checks.
set_strict_checks true;
... strict checks are turned on
for class 2 (default behavior).
set_strict_checks false;
... strict checks are turned off for class 2.
There is a number of new functions in this release which allow the user to specify the behavior of the error reporting. These functions need only be called if the default behavior should be changed. Therefore old HDCaml designs are fully compatible.
There are two kinds of messages: errors and infos. An error denotes a critical problem in the source code and leads to a termination of the program via an uncaught exception. An info is a message the user might be interested in. The program continues to run after an info. (This is the default behavior which can be changed for both kinds of messages.)
Default behavior (when switches are not used):
val raise_exception_at_info : bool -> unit
raise_exception_at_info true;
... next info will terminate program.
raise_exception_at_info false;
... program
will continue to run after an info (default behavior).
val raise_exception_at_error : bool -> unit
raise_exception_at_error true;
...
raises exception after error (default behavior).
raise_exception_at_error false;
...
no exception; program continues to run.
... raise_exception_at_info true; some_function a b c ; raise_exception_at_info false; ...The same is true for the other configuration functions.
val find_name : string -> unit
signal
, --
(annotation), input
, output
or circuit
(start of subcircuit).
Together with the lists of unconnected signals and
unused inputs (which are displayed
when get_circuit ()
is called) and
raise_exception_at_info true;
this can
be used to track down one unconnected dangling
signal or unused input per run (plus one initial run to get
the list).
The function can be used to find any name, by the way,
not only unconnected signals or unused inputs.
Example: find_name "subcircuit__mysignal";
Note: signal names, annotated names and subcircuit names
may include a subcircuit prefix
of the form "subcircuit__"
(subcircuit name plus double underscore)
if they are used inside of a subcircuit.
Inputs and outputs don't get such prefixes.
This function might seem pointless because the user knows when he uses which name. There are situations, however, that make tracing of names tedious:
This function can be used at any position in the HDCaml program. Only the trace for the last name ("name2" in this example) will be active if you use it repeatedly, like:
... find_name "name1"; find_name "name2"; ...
The empty string as an argument (i.e. find_name "";
)
turns name tracing off. This has the same effect as not
using this function at all.
val find_node : int -> unit
Together with the list of unused nodes
(which is displayed when get_circuit ()
is called) and
raise_exception_at_info true;
this can
be used to track down one unused node
per run (plus one initial run to get the list).
The OCaml-compiler is sometimes helpful in finding
unused nodes, too, by reporting unused variables.
But it doesn't find every unused node (as of version 3.09.2),
for example in this code fragment (inp_list is used once,
so ocamlc
doesn't complain):
... let inp = input "name" 4 in let inp_list = bits inp in output "first_bit" (List.hd inp_list); ...
The function can be used to find any node, by the way,
not only unused ones. Have you ever wondered, for example,
which construct in your HDCaml program
generates nodes like "_17" in the Verilog code? With this function
you can simply look it up, like find_node 17;
.
Attention: node numbers change when a HDCaml program is changed.
So don't forget to turn find_node
off after finding a certain
node and changing the code, because otherwise you would
find a completely different node which happens to get the
same number afterwards.
This function can be used at any position in the HDCaml program. Only the trace for the last node (2 in this example) will be active if you use it repeatedly, like:
... find_node 1; find_node 2; ...
Zero as an argument (i.e. find_node 0;
)
turns node tracing off. This has the same effect as not
using this function at all.
typesignal =
Circuit.signal
typecircuit =
Circuit.circuit
val start_circuit : string -> unit
val get_circuit : unit -> circuit
Checks for unused/unconnected elements and
unbalanced use of circuit/endcircuit
:
find_name "name"
for
inputs, dangling signals and opening subcircuits (circuit
),
find_node number
for circuit nodes.val circuit : string -> unit
val endcircuit : unit -> unit
val add_sink : Circuit.sink -> unit
val next_id : unit -> int
val path : unit -> string
val signal : string -> int -> signal
val (<==) : signal -> signal -> unit
It's an error if the assignment closes a combinational loop.
val (--) : string -> signal -> signal
val width : signal -> int
val check_width : int -> signal -> unit
val check_width_bit : signal -> unit
val check_width_nonzero : signal -> unit
val check_width_same : signal -> signal -> unit
val input : string -> int -> signal
val output : string -> signal -> unit
Note: Result of const
, zero
, one
and ones
must be at least one bit wide.
val const : string -> signal
const string;
String must be 1's and 0's.
(strict: string <> ""; else: string may be "")val zero : int -> signal
zero width;
(strict: width>=1 ; else: width>=0)val one : int -> signal
val ones : int -> signal
ones width;
(strict: width>=1 ; else: width>=0)val empty : signal
val vdd : signal
val gnd : signal
val select : signal -> int -> int -> signal
select signal msb lsb
.
(strict: msb>=lsb ; else: msb<lsb is allowed)val bit : signal -> int -> signal
val (++) : signal -> signal -> signal
left ++ right
(strict: only one side may be empty
;
else: both sides may be empty
)val concat : signal list -> signal
[]
and at least one item
must have non-zero width;
else: list may be []
, all items may be empty
)val repeat : signal -> int -> signal
repeat signal n;
(strict: signal<>empty, n>=1;
else: signal may be empty, n>=0)val msb : signal -> signal
val msbs : signal -> signal
msbs a
(strict: width a >= 2; else: width a >= 1)val lsb : signal -> signal
val lsbs : signal -> signal
lsbs a
(strict: width a >= 2; else: width a >= 1)val split : signal -> signal * signal
split a
(strict: width a >= 2; else: no restrictions)val bits : signal -> signal list
Notes: (for all operators except (~:) )
val (~:) : signal -> signal
val (&:) : signal -> signal -> signal
val (^:) : signal -> signal -> signal
val (|:) : signal -> signal -> signal
Note:
An info is generated if both arguments of addition or subtraction
are the same signal. (This is not the case for multiplications.)
val (+:) : signal -> signal -> signal
val (-:) : signal -> signal -> signal
val (*:) : signal -> signal -> signal
a *: b
.
Width of result is (width a)+(width b)
.val (*+) : signal -> signal -> signal
a *+ b
.
Width of result is (width a)+(width b)
.
Notes: signal (shift-op) shift
shift
must not be negativeshift=0
or shift >= width a
.val (<<:) : signal -> int -> signal
val (<<+) : signal -> int -> signal
<<:
. )val (>>:) : signal -> int -> signal
val (>>+) : signal -> int -> signal
Notes:
val (==:) : signal -> signal -> signal
val (/=:) : signal -> signal -> signal
val (<:) : signal -> signal -> signal
val (>:) : signal -> signal -> signal
val (<=:) : signal -> signal -> signal
val (>=:) : signal -> signal -> signal
val (<+) : signal -> signal -> signal
val (>+) : signal -> signal -> signal
val (<=+) : signal -> signal -> signal
val (>=+) : signal -> signal -> signal
val mux2 : signal -> signal -> signal -> signal
mux2 select on_high on_low
. Width of select must be 1.
Both signals (on_high, on_low) must have the same width.
An info is generated if both arguments (on_high, on_low) are the same signal.
val mux : signal -> signal list -> signal
mux ctrl items
The control input ctrl
is binary encoded (a N
bit wide control
signal can select 2^N
items). If ctrl
has more bits than necessary,
the most significant redundant bits are simply ignored. If the number of
items is no power of 2 the behavior is undefined for values of ctrl
greater than (List.length items)-1
.
Errors:
items
) is empty.val reg : signal -> signal -> signal
reg enable data
. Width of enable must be 1.
An info is generated if both arguments (enable, data) are the same signal.