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
, --
, start_circuit
and circuit
.<==
operator.start_circuit/get_circuit
and circuit/endcircuit
get_circuit
)
must not be used before start_circuit
This release introduces a workaround which enables localization of HDCaml errors by source-code line numbers. It works by raising an uncaught exception, which causes display of a stack trace. This way one HDCaml error per compilation can be conveniently located. Compile and run your HDCaml program as shown below 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 signal/circuit/port names
(exception: anonymous signals: see Design.signal
).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:
HDCaml 0.2.10 allows only names which are legal in all of its output languages: Verilog, VHDL, C (C++, SystemC). This boils down to the following rules:
a-z
, A-Z
),
digits (0-9
) and the underscore (_
)raise_exception_at_info true
.
In Version 0.2.10 the renaming was moved completely into the module at hand (Design) and it gives the user exact reasons for why certain names are not acceptable and how they get renamed.
There are reasons to rename identifiers although they are legal in terms of the rules above. These are:
_renamed
),
immediately followed by a number, e.g. x_renamed15
x__y
n_
)
immediately followed by a number,
e.g. n_1
sig
(if there are more of
these completely illegal names, this results in
names of the form sig_renamed<n>
later on)._renamed
plus a consecutive number.
In case the original identifier ended with
_renamed<n>
, this is removed prior to the operation."if"
of a signal in subcircuit "x"
,
which would be a legal name ("x__if"
) in VHDL and Verilog
would nevertheless create an error in the generated C code,
where "if"
would be used as a member name
of the struct "x"
.
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
, gnd
, high
and low
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.10 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.
val get_setting_strict_checks : unit -> bool
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
true
to make optimal use of find_name
and find_node
.
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 get_setting_exception_at_info : unit -> bool
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.
val get_setting_exception_at_error : unit -> bool
... 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
,
start_circuit
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 or even impossible:
signal "" width
) are used,
see Design.signal
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.3),
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 "n_17" in the Verilog
or VHDL 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 signal : string -> int -> signal
signal name w
.
Creates a dangling signal, which may be assigned later.
Anonymous signals:
Passing an empty string as name (signal "" w
)
creates an "anonymous signal".
This means that a name is being created automatically by HDCaml.
The created name looks like that of other unnamed nodes, e.g. "n_15"
for a anonymous signal which happens to
get assigned the id 15 internally.
The use of anonymous signals is convenient in
user written library functions which
define sequential logic (i.e. registers with feedback and therefore
dangling signals are used). In these cases the user no longer
has to make up meaningful names.
val (<==) : signal -> signal -> unit
It's an error if the assignment closes a combinational loop.
val (--) : string -> signal -> signal
val width : signal -> int
Design_error
otherwise.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 high : signal
val low : 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 last element is replicated to get a
list of the correct length (e.g. if ctrl
is 3 bits wide, but items
only has 6 elements (2 missing), ctrl
= 5 or 6 or 7 will all select
the last item in your list).
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.