Module Design


module Design: sig .. end
Digital design entry.


Enhanced features compared to HDCaml 0.2.9

This release enhances the design entry module of HDCaml 0.2.9 with the following features:

Instructions: Compile & Run

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.)

Check Rules

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:

  1. The empty signal is not allowed as an argument to any function or operator which takes type signal as a parameter. (#)
  2. No function or operator returns an empty signal, i.e. arguments which would cause an empty signal to be returned are considered erroneous. (#)
  3. Empty Strings ("") are not allowed for names.
  4. Correct bitwidth of signal operands is enforced at operator call (see operator description for details).
  5. Structuring functions like start_circuit/get_circuit and circuit/endcircuit must be balanced, i.e. no opening function without closing one and vice versa.
  6. Negative numbers are no valid argument to any function or operator.
  7. The number 0 is no valid argument to any function or operator except for indices and shift-amounts. (#)
  8. Indices of a signal with width N have to be in the range 0...(N-1).
Note: error conditions 1., 2. and 7. (marked with (#)) are not used for functions/operators of class 2 (see definition below in subsection Configure Strict Checks) when set_strict_checks is set to false.

Info conditions:

  1. Using the same signal twice as arguments for one function or operator might be unwanted and issues an info in most cases (exception: multiplications).
  2. Duplicate names get renamed during design entry. This is reported for every renamed signal.
Configure Strict Checks

Concerning strictness of error checks, the functions and operators of HDCaml can be divided into two classes:

  1. functions/operators where strict checks are absolutely useful
  2. functions/operators where strict checks may be useful or not, depending on the requirements and programming habits of the user
Functions/operators of class 1 are: Functions/operators of class 2 are: (The constants 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):

Other functions/operators of class 2 can be defined in such a way, that they return empty for corner cases, which can also be useful in generally describing a circuit: Functions/operators of class 2 can add expressive power to HDCaml when used carefully. But they can also cause subsequent errors, which are harder to detect in case of unthoughtful use. To reflect this properties, there is a switch that allows the user to turn checks for class 2 on and off.

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
Switch to turn the strict checks on and off for functions/operators of class 2 (see definition above).

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.


Configure Error Reporting

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
Causes an info to have the same consequences as an error, i.e. it raises an exception. This switch can be used to find the context of an info message.

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
Allows to inhibit the raising of an exception after occurrence of an error, that means the program is further executed. This allows to see subsequent messages during one run but produces potentially defective Verilog or SystemC files. Do not enable this switch unless you know exactly what you do!

raise_exception_at_error true; ... raises exception after error (default behavior).

raise_exception_at_error false; ... no exception; program continues to run.


These functions can be used repeatedly at any position in the HDCaml program. E.g. exceptions for infos could be turned on only during a single function call:
  ...
  raise_exception_at_info true;
  some_function a b c ;
  raise_exception_at_info false;
  ...
The same is true for the other configuration functions.

Trace Names and Nodes

val find_name : string -> unit
Creates an info output when the given name is being used in 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:

In these cases this function might come in handy.

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
Creates an info output when the given node number is being generated.

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.


Circuit Types


type signal = Circuit.signal 
Signals represent bit vectors.
type circuit = Circuit.circuit 
A circuit is a completed circuit design.

Circuit Data


val start_circuit : string -> unit
Starts a new circuit design. Initializes the internal circuit database.
val get_circuit : unit -> circuit
Returns the circuit design and resets the internal database.

Checks for unused/unconnected elements and unbalanced use of circuit/endcircuit :

Every element can be searched for. Use find_name "name" for inputs, dangling signals and opening subcircuits (circuit), find_node number for circuit nodes.
val circuit : string -> unit
Creates a new subcircuit and namespace.
val endcircuit : unit -> unit
Closes a subcircuit. Issues an error if too few subcircuits have been opened.
val add_sink : Circuit.sink -> unit
val next_id : unit -> int
val path : unit -> string
Returns the current circuit path.

Signal Assignment and Annotation


val signal : string -> int -> signal
Creates a dangling signal, which may be assigned later.
val (<==) : signal -> signal -> unit
Assigns a value to a dangling signal.

It's an error if the assignment closes a combinational loop.

val (--) : string -> signal -> signal
Applies a label (name) to a signal.

Width Utilities


val width : signal -> int
Width of a signal.
val check_width : int -> signal -> unit
Checks that a signal has a given width.
val check_width_bit : signal -> unit
Checks that a signal is a single bit.
val check_width_nonzero : signal -> unit
Checks that a signal has a nonzero width.
val check_width_same : signal -> signal -> unit
Checks that two signals have the same width.

Top Level Ports


val input : string -> int -> signal
Creates a top-level input.
val output : string -> signal -> unit
Assigns a top-level output.

Constants

Note: Result of const, zero, one and ones must be at least one bit wide.

val const : string -> signal
Creates a constant. const string; String must be 1's and 0's. (strict: string <> ""; else: string may be "")
val zero : int -> signal
Creates a constant 0. zero width; (strict: width>=1 ; else: width>=0)
val one : int -> signal
Creates a constant 1.
val ones : int -> signal
Creates a constant of 111's. ones width; (strict: width>=1 ; else: width>=0)
val empty : signal
An empty signal. (width = 0)
val vdd : signal
A single bit set to 1.
val gnd : signal
A single bit set to 0.

Bit Manipulation


val select : signal -> int -> int -> signal
Selects a section of a signal. LSB (right most) is indexed with 0. select signal msb lsb. (strict: msb>=lsb ; else: msb<lsb is allowed)
val bit : signal -> int -> signal
Selects an individual bit from a signal.
val (++) : signal -> signal -> signal
Concatenates two signals together. left ++ right (strict: only one side may be empty ; else: both sides may be empty)
val concat : signal list -> signal
New in 0.2.9.1. Concatenates a list of signals together. Head of list is MSB. List items may have different widths. (strict: list must not be [] and at least one item must have non-zero width; else: list may be [], all items may be empty )
val repeat : signal -> int -> signal
Repeats concatenation N times. repeat signal n; (strict: signal<>empty, n>=1; else: signal may be empty, n>=0)
val msb : signal -> signal
Selects the most significant bit (left most).
val msbs : signal -> signal
Selects all bits except the LSB. msbs a (strict: width a >= 2; else: width a >= 1)
val lsb : signal -> signal
Selects the least significant bit (right most).
val lsbs : signal -> signal
Selects all bits except the MSB. lsbs a (strict: width a >= 2; else: width a >= 1)
val split : signal -> signal * signal
Splits a signal into two equal parts. If signal has odd number of bits, left side will have one more bit than right. split a (strict: width a >= 2; else: no restrictions)
val bits : signal -> signal list
Splits a signal into a list of bits. MSB is head of list. (strict: width a >= 1; else: no restrictions)

Bitwise Logicals

Notes: (for all operators except (~:) )


val (~:) : signal -> signal
Bitwise NOT.
val (&:) : signal -> signal -> signal
Bitwise AND.
val (^:) : signal -> signal -> signal
Bitwise XOR.
val (|:) : signal -> signal -> signal
Bitwise OR.

Arithmetics

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
Addition. All signals have same width, including the result.
val (-:) : signal -> signal -> signal
Subtraction. All signals have same width, including the result.
val (*:) : signal -> signal -> signal
Unsigned multiplication. a *: b. Width of result is (width a)+(width b).
val (*+) : signal -> signal -> signal
Signed multiplication. a *+ b. Width of result is (width a)+(width b).

Shifting

Notes: signal (shift-op) shift


val (<<:) : signal -> int -> signal
Unsigned shift left.
val (<<+) : signal -> int -> signal
New in 0.2.9.1. Signed shift left. (Just for completeness, identical to <<:. )
val (>>:) : signal -> int -> signal
Unsigned shift right.
val (>>+) : signal -> int -> signal
Signed shift right.

Comparisons

Notes:


val (==:) : signal -> signal -> signal
Equal.
val (/=:) : signal -> signal -> signal
Not equal.
val (<:) : signal -> signal -> signal
Unsigned less than.
val (>:) : signal -> signal -> signal
Unsigned greater than.
val (<=:) : signal -> signal -> signal
Unsigned less than or equal.
val (>=:) : signal -> signal -> signal
Unsigned greater than or equal.
val (<+) : signal -> signal -> signal
Signed less than.
val (>+) : signal -> signal -> signal
Signed greater than.
val (<=+) : signal -> signal -> signal
Signed less than or equal.
val (>=+) : signal -> signal -> signal
Signed greater than or equal.

Muxing


val mux2 : signal -> signal -> signal -> signal
2-input mux: 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
N-Input Mux: 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:

Infos:

Registers


val reg : signal -> signal -> signal
Registers. reg enable data. Width of enable must be 1.

An info is generated if both arguments (enable, data) are the same signal.