Next: , Previous: Running THUD, Up: Top


3 TH Reference

3.1 Language overview

THUD's HDL (TH) is based on Scheme, but at this time is not a proper superset, although there are long term plans in that direction. This means that general Scheme code cannot be used; instead, a limited subset is available, in addition to the HDL-specific constructs. This chapter fully presents the language that THUD understands. THUD HDL filenames typically have the extension .th, although that is not required.

Identifier names are relatively unconstrained. You can use dashes, dollar-signs or whatever makes you happy. The one exception is that a name cannot contain the "/" character, as this is used as a scope separator. Also, for the sake of interoperability, be aware that other tools may impose restrictions on identifier names.

In the following discussion, upper-case symbols are non-terminal, while lower-case symbols, strings, periods and parentheses should be used verbatim. The only meta syntax is the open and close square brace to indicate optional items. The term form indicates a balanced expression (all left parens have a corresponding right paren).

Briefly, here are the language operators and keywords:

        ;; operators
        ^ % ! ~ + - * / & | = << >>
        ;; keywords
        bx Bx begin c. case cond if or and die fln
          eq? else bx Bx begin not dump n!

3.2 Language description

Comments begin with a semicolon and run to the end of the line. Whitespace is ignored.

To start a block, use the blk top-level form, which looks like:

     (blk BLK-NAME [PORTS])

ports is an optional list of ports. Omitting it means blk-name has no ports. Each element in the list looks like:

     (PORT-DIR [BIT-WIDTH] PORT-NAME)

A port is a communication channel from this block to whomever instantiates it. Thus, port-dir must be either in or out. bit-width is optional and defaults to 1 if not specified.

The blk form does not surround all the definitions in the block; using it is enough to tell the THUD scanner that the following text (until either EOF or the next blk form) is part of the current block.

Each port is a type of wire. Inside the block, you may have additional wires and registers to hold state. They are introduced after the blk form by wire and reg declarations, which look like:

     (wire [BIT-WIDTH] WIRE-NAME)
     (reg [BIT-WIDTH] REG-NAME)

bit-width is optional and defaults to 1 if not specified. These declarations can be interspersed with their definitions as long as the declaration is scanned before the definition. (A definition will not auto-declare anything, unlike with some other HDLs.)

There are two types of definition, assign and next. These are used for wires and registers, respectively. Definitions, always written at the top-level, look like:

     (a. WIRE-NAME EXPRESSION)
     (n. REG-NAME EXPRESSION)

Note: the dot in a. and n. is required. expression can be one of the following:

A number
E.g., #x24 (0x24).
A port, wire or reg.
A bit-extraction, indicated by square-braces, of a port, wire or reg.
An operator applied to its arguments.
Arguments are taken to be exact integers, although in the future, it may make more sense to consider them as bit-vectors. The following operators are available:
^ ARG ...
Logical (bitwise) XOR.
| ARG ...
Logical (bitwise) OR.
& ARG ...
Logical (bitwise) AND.
+ ARG ...
Arithmetic 2's-complement addition.
- ARG ...
Arithmetic 2's-complement subtraction.
* ARG ...
Arithmetic multiplication. If the result is too wide to fit in the expression's presumed bit-width, the upper bits are truncated. (Actually, currently the truncation step is temporarily disabled while low-level issues resolve.)
% ARG1 ARG2
Modulo. ARG1 is divided by ARG2 and the remainder is returned.
= ARG1 ARG2
Numerical equality. This returns 0 (not equal) or 1 (equal).
<< ARG1 ARG2
Arithmetic shift left. ARG1 is shifted to the left by the shift-value specified by ARG2. If ARG2 is negative, ARG1 is shifted to the right.
>> ARG1 ARG2
Arithmetic shift right. ARG1 is shifted to the right by shift-value specified by ARG2. If ARG2 is negative, ARG1 is shifted to the left.

Note that each ARG can also be an EXPRESSION.

A (possibly side-effecting) behavioral construct.
The construct should return a value that fits in the bit-width of the wire or register. If the value is wider, it will be silently truncated. The symbol #f is interpreted as "unspecified".
begin case cond if or and eq? else not
These are standard Scheme keywords with standard definitions. For more info, see Identifiers.
bx signal bitpos
Extract a single bit from signal. bitpos is zero-based, starting from the least-significant bit.
Bx signal bitpos len
Extract a bitfield from signal. bitpos is zero-based, starting from the least-significant bit. len is how many bits to extract.
c. bit ...
Concatenate each single bit, with the first argument becoming the most-significant bit of the result, and so on. The number of arguments determines the bit-width of the result which is returned.
n! signal value
For signal, which must be a flop, explicitly override its value for next clock rise with value. This overrides the particular n. for signal for one cycle. If n! is used more than once (for a particular signal), THUD issues a warning and ignores subsequent calls.

This construct is useful for blocks that serve as stimulus for other blocks. [TODO: give example.]

die fln dump
These procedures are covered in another part of this manual. See Miscellaneous procedures, for more info.

As a convenience, declarations and definitions can be elided into one form. For example, the following two lines are equivalent.

     (wire sig) (a. sig expression)  ; two forms: decl and def
     (wire sig expression)           ; one form: elided decl and def

A block may also instantiate other blocks by referring to them with a proper port mapping. Each instance reference, or iref, is a top-level form that looks like:

     (w/ BLK-NAME IREF-NAME PORT-MAPS)

References must be named. PORT-MAPS looks like:

     ((IREF-PORT-NAME . HOOKUP) ...)

Because a direct connection is made via IREF-PORT-NAME, the ordering in PORT-MAPS does not matter. All ports must be addressed. HOOKUP is either a port, wire or reg.