Cyclicity CDL Language Specification - Grammar

Gavin J Stark
v0.01
April 14th 2004

CDL is a language that, at the top level, specifies four things:

  1. Type definitions

  2. Constant declarations

  3. Module prototypes and timing specifications

  4. Module definitions

The lexical order of the first two of these in a file is important; constant evaluations and type definitions are performed in lexical order, so that a type that utilizes a constant value as a bit width, for example, must not precede the declaration of that constant value.

However, module definitions may refer to modules that have not been defined or prototyped; there is no lexical order requirement between module definitions and prototypes.


Scoping

Constants have their own scope. Enumerations are explicitly typed, and so are within their own type's scope.


Grammatical building blocks

user symbol list

user symbol list:= <user symbol> [ ',' [ <user symbol list> ] ]

type specifier

type specifier:= <user symbol>
|<base type>
|<type specifier> '['<expression> ']'

A type specifier of a bit is essentially a bit vector of size 1.

Strings must not be used in type specifiers; they are for parameter passing only, and their use is not well defined at present.

A type specifier of an array of bit vectors of size 1 is defined to be a bit vector of the specified size, but arrays of bit vectors larger than 1.

base type

base type:= signed bit
|unsigned bit
|bit
|integer
|string

lvar

lvar:= <user symbol>
|<lvar> '.' <user symbol>
|<lvar> '[' <expression>']'
|<lvar> '[' <expression> ';' <expression> ']'

Lvars specify use of signals or state, or a subset thereof.

A double subscript seperated with ';' indicates subscripting a bit vector with a length (the expression prior to the ';') and the lowest bit number (the expression after the ';'). This supports build-time type checking, where the length must be a constant to define the type of the lvar.

expression

expression:= <integer>
|<lvar>
|sizeof '(' <expression> ')'
|bundle '(' <expression list> ')'
|<expression> '?' <expression>':' <expression>
|<expression> ( '+' | '-' | '*' | '/' | '%' ) <expression>
|<expression> ( '&' | '|' | '^' | '&&' | '||' | '^^' ) <expression>
|<expression> ( '<' | '<=' | '>' | '>=' | '==' | '!=' ) <expression>
|<expression> ( '<<' | '>>' ) <expression>
|( '!' | '-' | '~' ) <expression>
|'(' <expression> ')'

Expression operator precedence is as in 'C', with the addition of the '^^' operator which has the same precedence as '||'.

expression list

expression list:= <expression> [ ',' [ <expression list> ] ]

Types and constants

type definition

type definition:= typedef <user symbol> <user symbol> ';'
|typedef enum '[' [ <expression> ] ']' '{' <enumerations> '}' <user symbol> ';'
|typedef struct '{' <structure element>* '}' <user symbol> ';'
|typedef [ one_hot | one_cold ] fsm '{' <fsm state>* '}' <user symbol> ';'

enumerations

enumerations:= <user symbol> [ '=' <expression> ] [ ',' [ <enumerations> ] ]

structure element

structure element:= <type specifier> <user symbol> [ <documentation string> ] ';'

fsm state

fsm state:= <user symbol> [ '{' <user symbol list> '}' [ '{' <user symbol list> '}' ] ]
   [ '=' <expression> ] [<documentation string> ] ';'

A type reference should only refer to a previously defined type.

A type enumeration is given a bit width, and can specify precise values for none, some or all of the enumerated elements; if the first is not specified, it is given the value 0; if others are not specified they take the previous value, and add 1.

Enumerated element names should not be duplicated within the enumerated type; they may duplicate other names, as they will be resolved from context.

A type structure should not duplicate element names.

An FSM that is not explicitly identified as one_hot or one_cold will be enumerated. FSM states may specify a set of preceding states (the first symbol list) and also a set of succeeding states (the second symbol list). If any FSM states are assigned values then all states must be assigned values.

Documentation strings should be utilized; the documentation will be available to GUI tools which interpret the code, or which utilize output from CDL tools.

constant declaration

constant declaration:= constant <type specifier> <user symbol> = <expression>
   [ <optional documentation> ] ';'

A constant may only be of a bit vector type, sized or unsized; they may not be structures.


Module prototypes and timing specifications

Module prototypes provide a mechanism for declaring a module's ports and the timing of those ports. The module may also be defined later in the file. A module prototype may also contain details of how the module may be drawn in a schematic, and its documentation should describe the overal functionality of the module.

module prototype

module prototype:= extern module <user symbol> '(' <port list> ')' [ <documentation> ] '{' <prototype body>* '}'

port list

port list:= <port> [ ',' [ <port list> ] ]

port

port:= clock <user symbol> [ <documentation> ]
|( input | output ) <type specifier> <user symbol> [ <documentation> ]
|parameter <type specifier> <user symbol> [ <documentation> ]

prototype body

prototype body:= <timing specification>

timing specification

timing specification:= timing ( to | from ) [ rising | falling ] <user symbol> <user symbol list> [ <documentation> ] ';'
|timing comb ( input | output ) <user symbol list> [ <documentation> ] ';'

Schematic symbol specification is not yet defined in the language.

Actual values for timing specification are not yet defined in the language.

The language does force limitations on module prototypes combinatorial timing; it will be difficult to specify two different times for paths between combinatorial pins. However, for now this is sufficient, and it may enforce good design practice (clocked logic is easier to specify, comprehend, and build, so combinatorial through paths are in general deprecated in modern design practice).

All types must be determinable at build time, currently. This means parameters must not be used to specify the widths of bit vectors, for example; this would require run-time checks. (Er, not quite true, but parameters in types are certainly tricky at the moment... but there are workarounds with constants and module naming)


Module definitions

Module definitions define the contents of a module. This may include a schematic of the module itself, which is relatable to the code inside the module due to the structuring of the CDL language; however, schematics are not in any sense required.

The documentation of the module should describe the design details of the module; if figures are required for such documentation, they should be referenced from here.

module definition

module definition:= module <user symbol> '(' <port list> ')' [ <documentation> ] '{' <module body>* '}'

module body

module body:= <clock definition>
|<reset definition>
|<clocked variable>
|<comb variable>
|<net variable>
|<labelled code>

clock definition

clock definition:= default <clock specification> [ <documentation> ] ';'

reset definition

reset definition:= default <reset specification> [ <documentation> ] ';'

clocked variable

clocked variable:= clocked [ <clock specification> ] [ <reset specification> ] <type specifier> <user symbol> '=' <nested assignment list> [ <documentation> ] ';'

comb variable

comb variable:= comb <type specifier> <user symbol> [ '=' <nested assignment list> ] [ <documentation> ] ';'

net variable

net variable:= net <type specifier> <user symbol> [ <documentation> ] ';'

clock specification

clock specification:= clock [ rising | falling ] <user symbol>

reset specification

reset specification:= reset [ active_low | active_high ] <user symbol>

labelled code

labelled code:= <user symbol> [ <documentation> ] ':' '{' <statement>* '}'

Statements are defined below.

Schematic specification is not yet defined in the language.

Clocked variables take the last (lexically) default reset and/or default clock specification, if they are not provided one in the definition.

Clocked variables must have reset values specified for their whole state.

Clocked variables are assigned to in labelled code segments using clocked assignment statements.

Combinatorial variables may have be fully defined on their declaration line, or defined fully within labelled code segments; they may not be partially defined on their declaration line (as this leads to obfuscation).

Combinatorial varaibles are assigned to in labelled code segments using combinatorial assignment statements. They must be assigned to in all branches of a labelled code segment that assigns to them at all; they may not be assigned to in more than one labelled code segment. (Is this true for structures, where multiple elements may be assigned in different code segments?)

Net variables must be driven by the outputs of module instantiations; they cannot be assigned to by assignment statements. Each bit must be driven by precisely one module output.

statement

statement:= <for statement>
|<assignment statement>
|<if statement>
|<switch statement>
|<print statement>
|<assertion statement>
|<sequence definition>
|<instantiation>

for statement

for statement:= for '(' <user symbol> ';' <expression> ')' [ '(' <expression> ';' <expression> ';' <expression> ')' ] '{' <statement>* '}'

assignment statement

assignment statement:= <lvar> ( '=' | '<=' ) <nested assignment> ';'

if statement

if statement:= if '(' <expression> ')' '{' <statement>* '}' ( elsif '(' <expression> ')' '{' <statement>* '}' )* [ else '{' <statement>* '}' ]

switch statement

switch statement:= ( full_switch | part_switch | priority ) '(' <expression> ')' '{' <case entries> '}'

case entries

case entries:= ( default | (case <expression list> ) ) ':' [ '{' <statement>* '}' ]

print statement

print statement:= print [<clock specification>] [<reset specification>] '(' <string> ')' ';'

assert statement

assert statement:= assert [<clock specification>] [<reset specification>] '(' <expression> ',' <string> ')' ';'
|assert [<clock specification>] [<reset specification>] '(' <expression> ',' <user symbol> ',' <string> ')' ';'

sequence definition

sequence definition:= sequence '{' <sequence item>* '}' <user symbol> ';'

sequence item

sequence item:= delay <unsized integer> [ to <unsized integer> ]
|<sequence expression>

sequence expression

sequence expression:= <user symbol>
|'(' <expression> ')'
|<sequence_expression> ( '&&' | '||' | '^^' ) <sequence_expression>
|not <sequence expression>

instantiation

instantiation:= <user symbol> <user symbol> [ '[' <expression< ']' ] '(' [<port map list> ] ')' ';'

port map list

port map list:= <port map> [ ',' [ <port map list> ] ]

port map

port map:= <user symbol> '<-' <user symbol>
|<user symbol> '=' <expression>
|<user symbol> '<=' <expression>
|<user symbol> '=>' <lvar>

Note that braces are required inside building blocks, and are not optional as they are in C. This removes problems such as the dangling-else, and requires cleaner code; this is especially important for hardware design.

For statements are compile-time, not run-time. They are supplied to support multiple instantiations of hardware with single statements, where each piece of hardware may be parametrized in some fashion. It encompasses the capability of a 'generate' statement and the more generic 'for' statement in most HDLs; note that as CDL is a hardware design description language, implementing the 'for' statement at compile time mirrors what synthesis must do, so there is no loss of capability.

Instantiations allow for setting of paramters from expressions, driving of clocks from clock ports, driving of inputs with expressions, and driving of outputs (which must be nets or components of nets).

Main links

SourceForge.net Logo

Site map