Published using Google Docs
TL-X 1a Draft Spec
Updated automatically every 5 minutes

TL-X 1a HDL Extension Syntax Specification

Table of Contents

1. Introduction

1.1. About TL-X

1.2. About This Specification

2. TL-X Source Code Structure

2.1. File Structure

2.2. Contexts

2.3. Example Code (TL-Verilog)

3. Character Classes

4. File Format Line

5. Code Regions

5.1. HDL Code Region

5.2. HDL_plus Code Region

5.3. TL-X Code Region

6. Line Type Character

7. TL-X Identifiers

7.1. Delimited Identifier Names

7.2. Mixed-Case Identifier Names

7.3. Numeric Identifiers

7.4. Identifier Types

8. Ranges and Indices

9. Scope

9.1. Indentation

9.2. Scope Lines

9.3. Reentrance

9.4. Behavioral Scope

9.5. Scope Summary

10. Behavioral Hierarchy Scope

11. Pipeline Scope

12. Pipestage Scope

13. When Scope

14. Source Scope

15. Assignment Statements/Blocks

15.1. HDL Parsing

16. Common Assignment Form

17. HDL_plus Blocks

18. Sequential Blocks (\always_comb)

19. Using Signals

19.1. Signal Identifier Types

19.2. HDL Signal/Structure Type Support

19.3. Assigned and Used Signals

19.4. Pipesignal References

19.5. $RETAIN

1. Introduction

1.1. About TL-X

The Transaction-Level Hardware Description Language Extension (TL-X) is a set of language extension features that raise the abstraction level of digital hardware development without sacrificing control over the physical implementation of the design.  TL-X can be applied to various Hardware Description Languages (HDLs).  These features can be processed by a “TL-X processor” to generate native code in the target language (without parsing the target HDL constructs).  The “X” in TL-X is a wildcard, referring to the target HDL.  This specification covers generic TL-X features as well as those specific to:

The TL-X definition is evolving through voluntary contributions by members of TL-X.org, who maintain this standard.

1.2. About This Specification

This specification is not intended for those first learning TL-X.  It is specifically a reference for the language extension syntax.  It does not describe the complete semantics, or meaning of the features.  Other resources are available at http://tl-x.org to introduce the main concepts of TL-X.  Specifically, the reader should already have an understanding of “behavioral hierarchy” (>hier), TL-X pipelines (|pipe), and “pipestages” (@1).

TL-X syntax is intentionally rigid.  With a few exceptions, this specification defines the syntax of properly-formatted TL-X files and leaves interpretation and handling of improperly-formatted files to the discretion of the TL-X processor.

2. TL-X Source Code Structure

2.1. File Structure

A TL-X file begins with a file format line, similar to a "magic number" that identifies the TL-X language version, and target HDL used in the file.

The remainder of the file is organized in regions that are either HDL regions or TL-X regions.  HDL regions contain native HDL code.  TL-X regions enable the TL-X language extension.  There are also HDL_plus regions that utilize the target HDL’s syntax with the ability to reference TL-X signals.  These exists to ease manual conversions of HDL code to TL-X, and to provide access to native HDL features that are not accessible in TL-X regions.

A TL-X file is expected to define a module in the target HDL.  It should begin with an HDL region defining the module interface and end with an HDL region terminating the module definition.

The body of the module can be defined in a single TL-X code region.  Lower-level module instantiations can be made from within the TL-X code.  HDL and HDL_plus code regions might be used within the module body as well to:

A TL-X region provides logic in assignment statements in the context of hierarchy, pipelines, and pipestages.  This context is provided via scope lines, where each level of scope introduces indentation of three spaces.

2.2. Contexts

Example TL-Verilog code below is colored based on its parse context.  Contexts listed here are defined in detail throughout this specification.

2.3. Example Code (TL-Verilog)

 

\TLV_version 1a: tl-x.org

\SV

module example(

   input [4:0] opcode_u101H [3:0],

   input clk,

   output [3:0] arith_err_u104H

);

 

\TLV

   |pipe

      // Four instructions

      >inst[3:0]

         @0

            $valid = ...;

      ?$valid

         @1

!           $opcode[4:0] = *opcode_u101H[inst];

            $add = $opcode == 5'b00000;

      /* ... */

      >inst[*]

         ?$valid

            @3

               $write = $opcode == 5'b11000 /* precise decode */;

            @4

!              *arith_err_u104H[inst] = ($add && $overflow) || ($sub && $underflow);

\SV

endmodule;

3. Character Types/Classes

A TL-X source code file is a text file using UTF-8 character encoding.  Characters fall into these mutually-exclusive types:

  1. Word: [a-zA-Z_]
  2. Numeric: [0-9]
  3. Space: [ ]
  4. Newline: This is the character sequence used to terminate the file format line.  It can be:
  1. Non-conformant line terminator:  This includes all Unicode line terminators other than newline:
  1. Braces: ()[]{}
  2. Symbols: Everything else that can be produced on a standard keyboard. (~`!@#$%^&*-+=\|:;”’<>,.?/)
  3. Unrecognized: All other UTF-8 characters.

Additionally, we define the following character classes as collections of character types:

  1. Line terminator: {newline, non-conformant line terminator}
  2. Whitespace: {space, newline, non-conformant line terminator}
  3. Conformant whitespace: {space, newline}
  4. Non-TL-X: {non-conformant line terminator, unrecognized}
  5. TL-X: All characters that are not non-TL-X.

Non-TL-X characters are only permitted in non-TL-X contexts -- currently, HDL contexts.  Their use is restricted only by external specifications or processors, such as HDL specifications and tools.  Non-TL-X contexts include HDL regions, HDL_plus regions, and assignment statements outside of signal references.

Note that all TL-X characters, are in the range 0-127, and are therefore equivalent to ASCII text.  Therefore, a TL-X file can be an ASCII text file, though, since Non-TL-X characters can be outside the range of ASCII characters, a legal TL-X file is not necessarily ASCII.  (Future TL-X versions might allow non-ASCII TL-X characters.)

4. File Format Line

The first line of a TL-X file must identify the TL-X File Format Version and HDL Language, as well as a URL to the language specification. Currently, it must be exactly this:

 

For TL-Verilog: \TLV_version 1a: tl-x.org

For TL-VHDL: \TLVHDL_version 1a: tl-x.org

For TL-C: \TLC_version 1a: tl-x.org

The newline sequence used on this line defines the newline sequence for the remainder of the file.

5. Code Regions

There are three types of code regions, HDL, HDL_plus, and TL-X.

5.1. HDL Code Region

HDL regions contain native HDL code.  No TL-X language extensions are accessible in these regions.

HDL regions begin with a line that begins exactly as follows, where the “/” is the line type character:

For TL-Verilog:

\SV

For TL-VHDL:

\VHDL

For TL-C

\C

There may be spaces and HDL-style comments following this region identifier.

The region ends where another code region is started, or with end-of-file.

It is expected that there will be an HDL region prior to any TL-X region that establishes the context for the TL-X code.  This includes, primarily, the module interface definition.  It might include other context as well.  HDL regions and HDL_plus regions should not alter this context.  Additionally, this region should provide the main clock.  A final HDL region is expected to terminate the module definition.

5.2. HDL_plus Code Region

HDL_plus regions are parsed as a single large block of HDL code with TL-X signal references that are relative to the default top-level TL-X scope.  (As described later, this is stage 0 (@0) of pipeline “none” (|none).)

HDL_plus regions' primarily uses are:

HDL_plus regions begin with a line that begins exactly as follows, where the “/” is the line type character:

For TL-Verilog:

\SV_plus

For TL-VHDL:

\VHDL_plus

For TL-C

\C_plus

The region ends when line indentation returns to the top level (to define a new code region).

5.3. TL-X Code Region

TL-X regions begin with a line that begins exactly as follows, where the “/” is the line type character:

For TL-Verilog:

\TLV

For TL-VHDL:

\TLVHDL

For TL-C

\C

The region ends when line indentation returns to the top level (to define a new code region).

6. Line Type Character

Lines of code in a TLV or SV_plus region begin with a character that defines the line type.  Lines with a special line type are generally ones that deserve special attention. Line type chars are:

7. TL-X Identifiers

TL-X defines a rich set of identifier types, each with their own namespace.  Though cryptic at first, once these fundamental language constructs become familiar they convey meaning very succinctly, without the need for naming conventions (that are never followed consistently).

Identifiers begin with a type prefix containing zero or more symbol characters.  If the next character is numeric, the identifier is a numeric identifier.  If the next character is alphabetic it starts the name of a named identifier -- either a delimited identifier, or a mixed-case identifier, determined by the prefix. (Name syntax is defined in 7.1. Delimited Identifier Names and 7.2. Mixed-Case Identifier Names).  If the next character is a whitespace or brace (a non-TL-X character would be illegal), terminating the identifier, the identifier is an operator, though no operators are defined in TL-X 1a.  The identifier's type is defined by its syntax, which includes the prefix and the delimitation style (for delimited identifiers) or the numeric style (for numeric identifiers).  Some named identifiers types are reserved for keywords, defined by TL-X in 7.4. Identifier Types.  This way, keywords do not interfere with the namespace of user-defined names.  There is a single syntax-to-type translation that is consistent in all contexts where identifiers are processed, defined in 7.4. Identifier Types.

7.1. Delimited Identifier Names

A delimited identifier name is a string comprised of delimited alphanumeric tokens. A token is a caseless string of alphabetic characters optionally followed by any number of numeric characters. The first token must begin with a minimum of two alphabetic characters. There are four methods of delimitation, distinguished by case of these first two alphabetic characters. The four identifiers below have the same tokens but different delimitation style:

Most names in TL-X are delimited identifiers, including those that translate to HDL. The restriction on name syntax allows names to carry over directly to HDL with additional available HDL namespace for other generated/derived names.

7.2. Mixed-Case Identifier Names

Some prefixes expect mixed-case identifiers.  Mixed case identifier names can contain any alpha-numeric characters of either case plus '_'. They begin with an alphabetic character (of either case).

Mixed case identifiers generally do not transfer into HDL, as doing so would consume the entire HDL namespace.

7.3. Numeric Identifiers

Numeric identifiers are ones whose prefix character(s) (if any) are followed by a numeric string which begins with a decimal digit.  However, if that decimal digit is preceded by “+” or “-”, the “+”/“-” is taken to be part of the numeric string.  To avoid ambiguity, numeric identifier prefixes must not end with “+”/”-”.  Currently the numeric string consists entirely of the optional “+”/”-” followed by decimal digits, to define a decimal number.

7.4. Identifier Types

TL-X identifier types are as follows.  These are described subsequently.

Identifiers with the following prefixes utilize mixed case:

Keywords (which have syntaxes: \keywords (mixed case); and $KEYWORDS (upper case)):

Scope:

Signals:

Other:

 

8. Ranges and Indices

Behavioral scope indices and signal bit indices appear in range/index expressions in one of the following formats:

[<expr>:<expr>]: Range

[<expr>]: Index

[*]: Complete range as defined elsewhere

 

<expr> use HDL syntax and may contain HDL constants.  They may also contain TL-X signal references in situations where the HDL permits the use of signals.

The HDL-context escape character “\” can be used to avoid special parsing of a character.  For example, “\:” or “\]”.

TL-X processors may provide checking and optimization for the generated HDL if they can evaluate the expressions.  For example, if only some bits of a pipesignal are required in a later pipestage, a TL-X processor could stage only those that are required.

9. Scope

9.1. Indentation

The use of whitespace is meaningful in TL-X.  This enforces consistent coding style and avoids inconsistencies between indentation and scope.  A single level of scope is conveyed by indentation of three spaces (or, in the case of the first level of indentation, the line type character followed by two spaces).  Tabs are forbidden in TL-X regions. (One should beware of one’s editor's auto-indentation mode.)

9.2. Scope Lines

Every level of scope is introduced by a scope line containing an identifier, sometimes followed immediately (no whitespace) by a range and, optionally, (conformant) whitespace and an HDL-style single- or multi-line comment.

9.3. Reentrance

Scope in TL-X is termed reentrant, in the sense that, the lexical code flow can define logic in a particular scope, leave that scope, and reenter that scope to define more logic.  This is unlike HDLs, where a module is defined contiguously, and repeating a similar for loop results in different loop scopes.

9.4. Behavioral Scope

Some scope identifiers are categorized as behavioral scope.  Behavioral scope provides hierarchy for a TL-X model.  It includes behavioral hierarchy and pipelines.  The full path to a pipesignal includes all levels of behavioral scope and does not include other scope.  (See 18. Pipesignal References.)

9.5. Scope Summary

This table summarizes scopes.

Scope

Range

Nesting Requirements

Behavioral

Section

Behavioral Hierarchy

none
[max:min]

[*]

Any

Yes

10. Behavioral Hierarchy Scope

Pipeline

none

Exactly one required

Yes

11. Pipeline Scope

Pipestage

none

Exactly one required, below pipeline

No

12. Pipestage Scope

When

none

At most one

No

13. When Scope

Source

none

Any

No

14. Source Scope

10. Behavioral Hierarchy Scope

Behavioral hierarchy is a form of behavioral scope, and therefore provides a level of hierarchy with its own namespace.  It also can enables replication of logic.  Replication of the scope is specified as a range.  The following forms are permitted:

  1. >my_hier : Behavioral hierarchy namespace scope (no replication).
  2. >my_hier[<expr>:<expr>] : Behavioral hierarchy replication scope.
  3. >my_hier[*] : Wildcarded behavioral hierarchy replication scope.

The first entry into a behavioral hierarchy replication scope requires a range expression (form 2.).  Subsequent entry into this scope requires the range to be specified as a wildcard (form 3.).  A behavioral hierarchy identifier must be distinct from identifiers of all parent levels of behavioral hierarchy.

11. Pipeline Scope

Pipeline scope (|my_pipe) is a form of behavioral scope, and therefore provides a level of hierarchy with its own namespace.  Pipeline scope cannot be replicated, so there can be no range expression.  All logic in TL-X is defined within a pipeline.  Therefore, there must be exactly one pipeline scope for every assignment statement.

12. Pipestage Scope

All logic in TL-X is defined within the context of a pipeline and a pipestage within that pipeline.  Therefore, there must be exactly one pipestage scope for every assignment statement, and that pipestage scope must be within a pipeline scope.  The following forms of pipestage scope are permitted:

13. When Scope

?$valid_sig: "When" condition. The corresponding boolean pipesignal ($valid_sig) provides the condition under which pipesignals in this scope are valid.  When not valid, pipesignals should not impact behavior.

14. Source Scope

Source scope is provided as an aid to situations where TL-X code is generated by a preprocessor.  TL-X.org is intentionally avoiding the temptation to aggressively specify new language features for every situation.  Simplicity is golden, and legacy is an anchor.  Many of the features that are not yet natively supported in TL-X, can be accomplished with standard macro preprocessors, such as M4.  Even with a fully-featured language, it is impossible to plan for every situation, and there are always times when extending capabilities using a preprocessor is the best answer.

The primary difficulty presented by the use of a preprocessor (as with the use of a TL-X processor) is the fact that downstream tools are unaware of the source code and cannot relate error messages and the like back to the source code.  To help, TL-X supports the \source construct:

\source <file> <line>

This is a scope line that indicates that this line and its contained TLV code came from the given source file and line number.  To be precise, the line number identifies the source line that produced the "\source" line.  These lines are not counted when reporting line numbers in error messages.  It is expected that the line that ends this scope is the first that is no longer from the specified source file.

There can be multiple levels of \source scope, enabling multiple levels of preprocessor macro instantiation.  Well-formed error messages will report all levels of instantiation.

15. Assignment Statements/Blocks

Assignment statements and assignment blocks (or just “assignments”) are the leaves of the parse tree.  They define logic.  They utilize HDL syntax for logic expressions, but use TL-X signal references ($sig) in place of HDL signals.  A TL-X processor need not parse the HDL expression syntax.

All assignment statements begin with a “$” or “*” character.  Assignments continue on subsequent lines with deeper indentation (any number of additional spaces of indentation; not necessarily three spaces).  Assignment blocks begin on a line with a keyword that has a “\” prefix and continue on subsequent lines with a minimum of three spaces of additional indentation.

The following assignment forms are permitted:

  1. Common form: “$foo = $bar;“ (or “$foo <= $bar;“ for TL-VHDL)
  2. HDL_plus form:

        \HDL_plus

           <HDL code with pipesignal references>

  1. Sequential form (TL-V only):

        \always_comb

           <SystemVerilog always_comb body with pipesignal references>

15.1. HDL Parsing

The HDL-context escape character “\” can be used within assignment statements to avoid special parsing of a character.  For example, “$display” in SystemVerilog context might be undesirably interpreted as a pipesignal, so “\$display” can be used for the SystemVerilog display task.

16. Common Assignment Form

The common assignment form (form 1.) has an explicit left-hand side.  Signal references on the left-hand side are recognized as assigned without the need for “$$”.  The line begins with “$” or “*”.  If it begins with “*”, an HDL type declaration is expected as in 19.2. HDL Signal/Structure Type Support, followed by a single space, prior to the “$” of the assigned signal.  “=” (“<=” for TL-VHDL) with conformant whitespace on both sides separates left-hand and right-hand sides.  The left-hand and right-hand sides are interpreted as HDL syntax.  “\” is an escape character that is dropped in translation and causes the next character to be interpreted as an HDL character, so it will not be interpreted as the start of a TL-X reference.

For TL-Verilog, “assign“, “always_comb“, “begin”, and “end” are not required.

17. HDL_plus Blocks

The HDL_plus assignment form (form 2.) can be used for any HDL expressions.  Lines of the body must be indented at least three additional spaces.  The body is simply a block of HDL code that can contain TL-X signal references.  It does not inherently distinguish produced signals from consumed signals, so produced signals must have a “$$” prefix.

18. Sequential Blocks (\always_comb)

Sequential, or always_comb blocks are supported in TL-Verilog only.  They provide a code block with sequential execution semantics.  They are a simple shorthand for a \TLV_plus block containing an always_comb block.  The TL-Verilog code:

\always_comb

   expression1;

   expression2;

is equivalent to:

 

\TLV_plus

   always_comb begin

      expression1;

      expression2;

   end

19. Using Signals

19.1. Signal Identifier Types

These signal types are supported within logic expressions.  (See 15. Assignment Statements/Blocks.)

19.2. HDL Signal/Structure Type Support

Pipesignals can have datatypes from the targeted HDL, either user-defined structure types or built-in HDL types. The type of a pipesignal may be declared in a type declaration statement, or along with an assignment. The HDL type is prefixed with "**". A type declaration statement may be outside of stage scope, but must be within a pipeline.

Before declaring an instance with an HDL type within TL-X context, the type must be defined within HDL context.  For example,

 

\SV

   typedef struct {

          node field1;

          node field2;

   } struct_name;

A type declaration statement begins with an HDL type identifier, then the pipesignal, followed by a semicolon, as follows:

\TLV

   |Pipeline

      **struct_name $object_name; // type declaration statement

The type can also be declared within a common form assignment (see 16. Common Assignment Form), as follows:

\TLV

   |Pipeline

          @2

                **struct_name $object_name.field1 = ...;  // assignment statement

                $object_name.field2 = ...;  // assignment statement

Note: Vectorization of HDL-typed signals is not permitted.  Ranges are not permitted following HDL-typed pipesignal references.  As above, fields and bit ranges within fields might be specified in HDL-syntax.

19.3. Assigned and Used Signals

A TL-X processor must be able to determine which pipesignals are produced (aka assigned) and which are consumed (aka used).  (The distinction is not necessary for HDL signal references (*HDL_sig).)  The signal identifier $$sig, explicitly references $sig as assigned.  This syntax must be used for all assigned signals in all assignment forms except the common form (form 1.) (see 15. Assignment Statements/Blocks).

There must generally be exactly one assignment for every pipesignal.  In situations where a pipesignal must be used in multiple places as an assignment, one of those places can be chosen to represent the assignment, and the others can safely be treated as uses.  Within an assignment block multiple assignments to the same signal may be used, but each must specify the same signal type.

Generally, the single assignment (or in rare cases, multiple assignments) specifies the type of the signal.  The exception to this rule is HDL types, which cannot be specified within assignment blocks, and can be specified separately.  (See 19.2. HDL Signal/Structure Type Support.)  The type can be specified as an HDL type within common form assignments, or in any context, a bit range may be provided immediately following the assigned signal identifier (with no intervening whitespace).  If no HDL type or range is specified, the signal is a single bit.

19.4. Pipesignal References

Pipesignal references are used within assignment statements.  A simple signal reference is:

$sig.

This refers to a pipesignal in the same scope.

A reference to a pipesignal produced in a different scope might look like:

>scope|pipe>instr[2]$addr#-2[12:6]

The path (>scope|pipe>instr[2]) identifies the behavioral scope instances of the reference.

 The first identifier of the path (if the path is not empty) refers to any parent hierarchy level of the assignment's scope (each of which is required to have a unique identifier) or to a child scope.  Top level scope has an implicit identifier “>top”.  Subsequent path identifiers identify child scope instances.  A replicated behavioral hierarchy level may be provided an index expression.  If none is provided, the corresponding index of a same-named scope on the ancestry of the assignment’s scope is assumed.  In other words, if >scope is replicated, >scope refers to >scope[scope].

The right-most scope index(es) may be given as wildcards ([*]).  This will reference the concatenation of all instances.  This is most useful for bitwise reduction operators (& >inst[*]$bit) or for providing inputs to HDL modules that expect packed bit masks.

Assigned (left-hand, or $$sig) pipesignals have the scope of the assignment and must have an empty path.

#-2 is a pipeline alignment.  It specifies that the numeric pipestage of the reference is two less than the assignment's stage scope.  The alignment must be a decimal integer literal.

The bit range [12:6] may be interpreted by a TL-X processor to determine what bits must be made available in the consuming stage.  If the expressions cannot be evaluated, more bits may be made available than necessary.

References to signals of an HDL type may not have a bit range.  Note that subsequent text will use HDL syntax, which may be field references and range indexes, such as:

$sv_struct#-2.field1[2].field22

19.5. $RETAIN

$RETAIN can be used as a used signal in an assignment.  It translates to the single destination signal delayed by one cycle. It can be used to retain/recirculate the previous value of the signal.

20. Comments

Comments use HDL comment syntax.  They may appear within assignment statements and blocks or after scope identifiers (together with their range).  Scope identifiers must begin their line, after indentation, so it is not legal to have a scope identifier after a multi-line-style comment that is terminated on the same line.  Comments are otherwise ignored from the standpoint of indentation.  Comments may begin anywhere in their line, including at the line-type character.

Acknowledgements

TL-X evolved from research and development at Intel Corporation, led by Steve Hoover.  (Steve is an active contributor to TL-X and Founder of Redwood EDA.)  Yura Pyatnychko also contributed significantly to the work (and remains involved).  Countless others made supporting contributions.