e contains a stream of tokens. Tokens can be comments, delimiters, numbers, constants, identifiers, and keywords. e is a case-sensitive language. All keywords are in lowercase.
A code segment is enclosed with a begin-code marker <' and an end-code marker '>. Both the begin-code and the end-code markers must be placed at the beginning of a line (leftmost), with no other text on that same line (no code and no comments). The example below shows three lines of code that form a code segment.
<' import cpu_test_env; '>
Several code segments can appear in one file. Each code segment consists of one or more statements.
e files begin as a comment which ends when the first begin-code marker <' is encountered.
Comments within code segments can be marked with double dashes (--) or double slashes (//).
a = 5; -- This is an inline comment b = 7; // This is also an inline comment
The end-code '> and the begin-code <' markers can be used in the middle of code sections, to write several consecutive lines of comment.
Import the basic test environment for the CPU... This is a comment <' import cpu_test_env; '> This particular test requires the code that bypasses bug#72 as well as the constraintsthat focus on the immediate instructions. This is a comment <' import bypass_bug72; import cpu_test0012; '>
There are two types of numbers, sized and unsized.
Unsized numbers are always positive and zero-extended unless preceded by a hyphen. Decimal constants are treated as signed integers and have a default size of 32 bits. Binary, hex, and octal constants are treated as unsigned integers, unless preceded by a hyphen to indicate a negative number, and have a default size of 32 bits.
The notations shown in Table 3-1 can be used to represent unsized numbers.
A sized number is a notation that defines a literal with a specific size in bits. The syntax is:
width-number ' (b|o|d|h|x) value-number;
The width number is a decimal integer specifying the width of the literal in bits. The value number is the value of the literal and it can be specified in one of four radixes, as shown in Table 3-2.
NOTE
If the value number is more than the specified size in bits, its most significant bits are ignored. If the value number is less that the specified size, it is padded by zeros.
Radix | Represented By | Example |
---|---|---|
Binary | A leading 'b or 'B | 8'b11001010 |
Octal | A leading 'o or 'O | 6'o45 |
Decimal | A leading 'd or 'D | 16'd63453 |
Hexadecimal | A leading 'h or 'H or 'x or 'X | 32'h12ffab04 |
A set of constants is predefined in e, as shown in Table 3-3.
Constant | Description |
---|---|
For boolean variables and expressions | |
For boolean variables and expressions | |
For structs, specifies a NULL pointer; for character strings, specifies an empty string | |
UNDEF indicates NONE where an index is expected | |
Represents the largest 32-bit int (231 -1) | |
Represents the largest negative 32-bit int (-231) | |
Represents the largest 32-bit uint (232-1) |
A literal string is a sequence of zero or more ASCII characters enclosed by double quotes (" "). The special escape sequences shown in Table 3-4 are allowed.
Escape Sequence | Meaning |
---|---|
New-line | |
Tab | |
Form-feed | |
Quote | |
Backslash | |
Carriage-return |
This example shows escape sequences used in strings. Although other constructs are introduced here only for the sake of completeness, please focus only on the string syntax.
<' extend sys { m() is { var header: string = //Define a string variable "Name\tSize in Bytes\n----\t-------------\n"; var p: packet = new; var pn: string = p.type().name; var ps: uint = p.type().size_in_bytes; outf("%s%s\t%d", header, pn, ps); }; }; '>
The result of running the example above is shown below.
Specman> sys.m()
Name Size in Bytes
---- -------------
packet 20
The following sections describe the legal syntax for identifiers and keywords.
User-defined identifiers in e code consist of a case-sensitive combination of any length of the characters A-Z, a-z, 0-9, and underscore. They must begin with a letter. Identifiers beginning with an underscore have a special meaning in e and are not recommended for general use. Identifiers beginning with a number are not allowed.
The syntax of an e module name (a file name) is the same as the syntax of UNIX file names, with the following exceptions.
'@' and '~' are not allowed as the first character of a file name.
'[', ']', '{', '}' are not allowed in file names.
Only one '.' is allowed in a file name.
The keywords listed in Table 3-5 below are the reserved words of the e language. Some of the terms are keywords only when used together with other terms, such as "key" in "list(key:key)", "before" in "keep gen x before y", or "computed" in "define def as computed".
all of | all_values | and | as a | as_a |
assert | assume | async | attribute | before |
bit | bits | bool | break | byte |
bytes | c export | case | change | check that |
compute | computed | consume | continue | cover |
cross | cvl call | cvl callback | cvl method | cycle |
default | define | delay | detach | do |
down to | dut_error | each | edges | else |
emit | event | exec | expect | extend |
fail | fall | file | first of | for |
force | from | gen | global | hdl pathname |
if | #ifdef | #ifndef | in | index |
int | is | is a | is also | is c routine |
is empty | is first | is inline | is instance | is not a |
is not empty | is only | is undefined | item | keep |
keeping | key | like | line | list of |
matching | me | nand | new | nor |
not | not in | now | on | only |
or | others | pass | prev_ | |
range | ranges | release | repeat | return |
reverse | rise | routine | select | session |
soft | start | state machine | step | struct |
string | sync | sys | that | then |
time | to | transition | true | try |
type | uint | unit | until | using |
var | verilog code | verilog function | verilog import | verilog simulator |
verilog task | verilog time | verilog timescale | verilog trace | verilog variable |
vhdl code | vhdl driver | vhdl function | vhdl procedure | vhdl driver |
vhdl simulator | vhdl time | when | while | with |
within |
The simplest way to define e macros is with the define statement. An e macro can be defined with or without an initial ` character.
<' define WORD_WIDTH 16; //Definition of the WORD_WIDTH macro struct t { f: uint (bits: WORD_WIDTH); //Usage of WORD_WIDTH macro }; '>
You can also import a file with Verilog 'define macros using the keywords verilog import.
macros.v (Verilog defines file) `define BASIC_DELAY 2 `define TRANS_DELAY `BASIC_DELAY+3 `define WORD_WIDTH 8 ----------------------------------------------------- dut_driver.e (e file) <' verilog import macros.v; //Imports all definitions from //macros.v file //Macros imported from Verilog must be used //with a preceding '. struct dut_driver { ld: list of int(bits: `WORD_WIDTH); //use verilog macro keep ld.size() in [1..'TRANS_DELAY];//use verilog macro }; '>
e files are called modules. An e file can import another e file using the import keyword. The import statement loads additional e modules before continuing to load the current file. If no extension is given for the imported file name, a ".e" extension is assumed. The modules are loaded in the order they are imported. The import statements must be before any other statements in the file.
//File Name: pci_transaction_definition.e <' type PCICommandType: [ IO_READ=0x2, IO_WRITE=0x3, MEM_READ=0x6, MEM_WRITE=0x7 ]; struct pci_transaction { address: uint; command: PCICommandType; bus_id: uint; }; '> //End File: pci_transaction_definition.e ------------------------------------------------------------ //File Name: pci_transaction_extension.e <' //Import the file defined above. Note that the .e //extension is assumed in an import statement import pci_transaction_definition; //.e extension is the default extend pci_transaction { data: list of uint; }; '> //End File: pci_transaction_extension.e
If a specified module has already been loaded or compiled, the statement is ignored. For modules not already loaded or compiled, the search sequence is: