Basic e Concepts
- Conventions
- Data Types
- Simulator Variables
- Syntax Hierarchy
- Summary
- Exercises
In this chapter we discuss the basic constructs and conventions in e. These conventions and constructs are used throughout the later chapters. These conventions provide the necessary framework for understanding e. This chapter may seem dry, but understanding these concepts is a necessary foundation for the successive chapters.
Chapter Objectives
-
Understand conventions for code segments, comments, white space, numbers, constants, and macros.
-
Describe how to import other e files.
-
Define the data types such as scalar type and subtypes, enumerated scalar type, list type, and string type.
-
Understand syntax hierarchy of statements, struct members, actions, and expressions.
-
Explain the use of simulator variables in e.
3.1 Conventions
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.
3.1.1 Code Segments
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.
3.1.2 Comments and White Space
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 constraints that focus on the immediate instructions. This is a comment <' import bypass_bug72; import cpu_test0012; '>
3.1.3 Numbers
There are two types of numbers, sized and unsized.
3.1.3.1 Unsized Numbers
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.
Table 3-1. Representing Unsized Numbers in Expressions
Notation |
Legal Characters |
Examples |
---|---|---|
Decimal integer |
Any combination of 0-9 possibly preceded by a hyphen - for negative numbers. An underscore (_) can be added anywhere in the number for readability. |
12, 55_32, -764 |
Binary integer |
Any combination of 0-1 preceded by 0b. An underscore (_) can be added anywhere in the number for readability. |
0b100111, 0b1100_0101 |
Hexadecimal integer |
Any combination of 0-9 and a-f preceded by 0x. An underscore (_) can be added anywhere in the number for readability. |
0xff, 0x99_aa_bb_cc |
Octal integer |
Any combination of 0-7 preceded by 0o. An underscore (_) can be added anywhere in the number for readability. |
0o66_123 |
K (kilo: multiply by 1024) |
A decimal integer followed by a K or k. For example, 32K = 32768. |
32K, 32k, 128k |
M (mega: multiply by 1024*1024) |
A decimal integer followed by an M or m. For example, 2m = 2097152. |
1m, 4m, 4M |
3.1.3.2 Sized 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.
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.
Table 3-2. Radix Specification Characters
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 |
3.1.4 Predefined Constants
A set of constants is predefined in e, as shown in Table 3-3.
Table 3-3. Predefined Constants
Constant |
Description |
---|---|
TRUE |
For boolean variables and expressions |
FALSE |
For boolean variables and expressions |
NULL |
For structs, specifies a NULL pointer; for character strings, specifies an empty string |
UNDEF |
UNDEF indicates NONE where an index is expected |
MAX_INT |
Represents the largest 32-bit int (231 -1) |
MIN_INT |
Represents the largest negative 32-bit int (-231) |
MAX_UINT |
Represents the largest 32-bit uint (232-1) |
3.1.4.1 Literal String
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.
Table 3-4. Escape Sequences in Strings
Escape Sequence |
Meaning |
---|---|
\n |
New-line |
\t |
Tab |
\f |
Form-feed |
\” |
Quote |
\\ |
Backslash |
\r |
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
3.1.5 Identifiers and Keywords
The following sections describe the legal syntax for identifiers and keywords.
3.1.5.1 Legal e Identifiers
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.
3.1.5.2 e Keywords
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”.
Table 3-5. List of Keywords
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 |
3.1.6 Macros
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 }; '>
3.1.7 Importing e Files
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:
-
The current directory
-
Directories specified by the SPECMAN_PATH1 environment variable
-
The directory in which the importing module resides