Sie sind auf Seite 1von 74

Specman Notes

Specman Elite
From Verisity (http://www.verisity.com)
Presents a high-level language for writing
test environments

Test Benches
Coverage
Constraint based test generation and checking

Configuration
On pitteda3 or pitteda4
# Verisity user Environment variables (no newlines)
setenv SPECMAN_HOME
$CAD_DIR/verisity/specman_3.3.3/sn_rel3.3.3
setenv PATH {SPECMAN_HOME}/`${SPECMAN_HOME}/bin/sn_arch.sh`:
${SPECMAN_HOME}/bin:${PATH}
setenv SPECMAN_DIR $SPECMAN_HOME/`$
{SPECMAN_HOME}/bin/sn_arch.sh`
setenv VERISITYLD_LICENSE_FILE
5286@pitteda1.ee.pitt.edu

Files
Copy the tutorial tar file
Copy the emacs specman-mode file

If you use emacs


If you like language editors

Untar the tutorial directories into your own


account ( ./src and ./gold )

Running
(make sure X and DISPLAY are right)
specview &

e language
Looks like verilog to me
Has support for data types

With statistical values


With constraints (hard and soft)

Stimulus
Checking
Events

On Line Help
Verisity has all their help on line

e language ref
Command ref for Specman Elite
Usage etc.

Tutorial is on OUR web page (large pdf)

Do not print it, just do it.


Its not great

File Format
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 (left most), with no other
text on that same line (no code and no comments).
The following three lines of code 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.

Comments
e files begin as a comment which ends when the first begincode 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

Pre Defined Constants


Constant
TRUE
FALSE
NULL

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
UNDEF indicates NONE where an index is
expected.
MAX_INT
Represents the largest 32-bit int (231 -1)
MIN_INT
Represents the smallest 32-bit int (-231).
MAX_UINT Represents the largest 32-bit uint (232-1).

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

Keywords
int
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
matchin
g
me
nand
new
nor
not
not in
now
nxor
on
only

or
others
pass
prev
print
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

Keywords
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

Syntactic Elements
Statements
Statements are top-level constructs and are valid within the begin-code <' and end-code
'> markers. Statements end with a semicolon ;
Struct members
Struct members are second-level constructs and are valid only within a struct definition.
Actions
Actions are third-level constructs and are valid only when associated with a struct
member, such as a method or an event.
Expressions
Expressions are lower-level constructs that can be used only within another e construct.
The syntax hierarchy roughly corresponds to the level of indentation shown below:
statements
struct members
actions
expressions

Statements
Statements are top-level constructs and are valid
within thebegin-code <' and end-code '> markers.
Key Statement Types:

Struct defines a new data structure


Type defines an enumerated/subtype
Extend extends a previously defined struct or type
Define extends language with new commands, actions,
expressions
More: import, verilog-x, vhdl-x

Order is not critical but imports must be first (after macro


defines)

Struct & Struct Members


Struct members are second-level constructs and are
valid only within a struct definition.
struct struct-type: struct-descriptor [like base-struct-type: struct-descriptor] {
[member: struct-member; ...]}
Example:
type packet_kind: [atm, eth];
struct packet {
len: int;
keep len < 256;
kind: packet_kind;
};

Struct Members
field declaration

Defines a data entity that is a member of the enclosing struct and has an explicit data type.

method declaration

Defines an operational procedure that can manipulate the fields of the enclosing struct and
access run-time values in the DUT.

subtype declaration

Defines an instance of the parent struct in which specific struct members have particular
values or behavior. (e.g., when)

constraint declaration

Influences the distribution of values generated for data entities and the order in which values
are generated. (e.g., keep)

coverage declaration

Defines functional test goals and collects data on how well the testing is meeting those goals.

temporal declaration

Defines e events and their associated actions.(e.g., on, expect, assume)

Fields
[!][%] field: field-name[: type: type] [[min-val: int .. max-val: int]]
[((bits | bytes):num: int)]

Syntax example:
type NetworkType: [IP=0x0800, ARP=0x8060] (bits: 16);
struct header {
address: uint (bits: 48);
hdr_type: NetworkType;
!counter: int;
};

Fields
! Ungenerated Fields
A field defined as ungenerated (with the ! option) is not generated
automatically.
This is useful for fields that are to be explicitly assigned during the
test, or whose values involve computations that cannot be expressed in
constraints.
Ungenerated fields get default initial values (0 for scalars, NULL for
structs, empty list for lists).
An ungenerated field whose value is a range (such as [0..100]) gets the
first value in the range.
If the field is a struct, it will not be allocated and none of the fields in
it will be generated.

Fields
% Physical Fields
A field defined as a physical field (with the % option) is packed
when the struct is packed.
Fields that represent data that is to be sent to the HDL device in the
simulator or that are to be used for memories in the simulator or in
Specman Elite, need to be physical fields.
Nonphysical fields are called virtual fields and are not packed
automatically when the struct is packed, although they can be packed
individually.
If no range is specified, the width of the field is determined by the
fields type. For a physical field, if the fields type does not have a
known width, you must use the (bits | bytes : num) syntax to specify
the width.

Actions
Actions are third-level constructs and are valid only when
associated with a struct member, such as a method or an
event.
<'
struct packet{
event xmit_ready is rise('top.ready');
on xmit_ready {transmit();};
transmit() is {
out("transmitting packet...");
};
};
'>

Actions
Creating & modifying variables
var, = , op=,

Interacting with the DUT


force, release

Flow control

Conditionals
if then else, labeled case, boolean case

Iteratation
While, repeat until, for each, for from-to, for each-line, for each-

file-matching

Flow control

break, continue

Actions
Invoking methods and routines
method(), tcm(), start tcm(), routine(), compute method(),

return

Performing time consuming actions


emit, sync, wait, all of , first of, state machine

Generating data items


gen

Detecting/handling errors
check that, dut_error(), assert, warning(), error(), fatal(), try

Printing
print, set_config()

Expressions
Expressions are lower-level constructs that can be
used only within another e construct
Expressions are constructs that combine operands
and operators to represent a value
A literal value
A constant
An e entity, such as a method, field, list, or struct
An HDL entity, such as a signal
A compound expression applies one or more operators to one

or more operands.

Struct Hierarchy
global

sys
switch

ctrl_stub
port_stub1
sender
listener
port_stub2
port_stub3
port_stub4

packing
files
scheduler
simulator
session

Implicit Variables
it : The implicit variable it always refers
to the current item.
for each in sys.packets{
it.len = 5;
.good = TRUE; -- it is assumed
};

Implicit Variables
Me: The implicit variable me refers to the
current struct and can be used anywhere in the
struct.
struct packet {
data: uint;
stm() is {
var tmp: uint;
gen tmp keeping {it < me.data}; - - it is tmp
print data, tmp using hex;
};
};

Implicit Variables
result:The result variable returns a value
of the methods return type. If no return
action is encountered, result is returned by
default. The following method returns the
sum of a and b:
sum(a: int, b: int): int is {
result = a + b;
};

Implicit Variables
index: The index variable holds the
current index of the item referred to by it.
The scope of the index variable is limited
to the action block.
for each in packets do {
packets[index].len = 5;
.id = index;
};

Operators & Precedence


[ ] List indexing (subscripting)
[..] List slicing
[ : ] Bit slicing (selection)
f() Method and routine calls
. Field selection
~ Bitwise not
! (not) Boolean not
{;} List concatenation
%{} Bit concatenation
+ - Unary plus, minus
*, /, % Binary multiply, divide,
modulus
+, - Binary add and subtract

>> << Shift right, shift left


< <= > >= Comparison
is [not] a Subtype identification
== != Equality, inequality
=== !== Verilog four-state comparison
~ !~ String matching
in Range list operator
& Bitwise AND
| Bitwise OR
^ Bitwise XOR
&& (and) Boolean AND

|| Boolean or
=> Boolean implication
? : Conditional operator

Examples
num1 = %{num2,num3};
= pack(packing.high,num2,num3);
e.g.
Num2 = 0x1234;
Num3 = 0x7777;
Num1 = %{Num2,Num3};
What is Num3?

Examples
var loc1: list of colors: {red;green;blue};
print loc1;

Types: Scalars
int Represents numeric data,
both negative and nonnegative integers. (32 bits)
uint Represents unsigned
numeric data, nonnegative integers only. (32
bits)
bit An unsigned integer in
the range 01. (1 bit)

byte An unsigned integer in


the range 0255. (8 bits)
time An unsigned integer in
the range 0263 -1.( 64
bits)
bool Represents truth
(logical) values, TRUE(1)
and FALSE(0). (1 bit)

Subtypes
By range

int [0..100]

By width

int (bits: 8)

Named Subtypes

type int_count : int [0..99] (bits:7);


var count : int_count;

Enumerated Types
You can define the valid values for a variable or
field as a list of symbolic constants.
var kind: [immediate, register];
You can extend the definition:
type packet_protocol: [];
extend the definition of the type with
extend packet_protocol : [Ethernet, IEEE,
foreign];

Struct Subtypes
type packet_protocol: [Ethernet, IEEE, foreign];
struct packet {
protocol: packet_protocol;
size: int [0..1k];
data[size]: list of byte;
legal: bool;
};
extend sys {
gen_eth_packet () is {
var packet: legal Ethernet packet; -- local sub-type
gen packet keeping {it.size < 10;};
print packet;
};
};

Generate example
struct location {
address: int;

}
gen l keeping {it.address == 2*i}

Subtypes with extend &


when
type packet_protocol: [Ethernet, IEEE, foreign];
struct packet {
protocol: packet_protocol;
size: int [0..1k];
data[size]: list of byte;
//
when Ethernet packet {e_field: int;
-- same as extend below
//
show() is {out("I am an Ethernet packet")
//
};
};
extend Ethernet packet {
e_field: int;
show() is {out("I am an Ethernet packet")};
};

Accessing sub-typed
structs
type packet_protocol: [Ethernet, IEEE, foreign];
struct packet {
protocol: packet_protocol;
when IEEE packet { i_val: int;};
};
var pk_inst: IEEE packet;
pk_inst.i_val = 1;
if pk_inst is a IEEE packet (ip) {ip.i_val = 1; };
pk_list.first(it is a IEEE packet (ip) and ip.i_val == 1);

List functions
list.add, list.add0, list.clear, list.insert, list.delete,
list.first
var ilist: list of int;
ilist.add(5);
var iitem: instr;
iitem = instr_list.first(it.op1 > 15);

List add usage


struct p_l {
! packeti : packet
! plst: list of packet;
mklst() @sysclk is {
gen packeti;
plst.add(packeti);
stop.run();
}
}

@
event sim_ready is change(top\ready) @sim;
Simulator object change

@sys.any predefined event occurs everytime anyo


event occurs
extend sys {
event clk is rise(top.clk) @sim;
}

List types
perl-like array/list semantics
my_list[0] refers to the first item in the list
var lob: list of byte = {15;31;63;127;255};
print lob[0..2];

No multi-dimensional lists. To create a list with


sublists in it, create a struct to contain the sublists

Example
type packet_protocol : [Ethernet, IEEE, foreign];
struct packet {
protocol: packet_protocol;
len: int [0..10];
};
extend sys {
packets[10] : list of packet;
do_print() is {
var all_lengths: list of packet'len;
all_lengths = packets.len; -- multi-element assignment
print packets;
print all_lengths;
};
};

Run system:
sys = packets =
item type
protocol

0.
1.
2.
3.
4.
5.
6.
7.
8.
9.

packet
packet
packet
packet
packet
packet
packet
packet
packet
packet

Ethernet
Ethernet
IEEE
IEEE
Ethernet
Ethernet
foreign
Ethernet
Ethernet
foreign

len

10
10
4
0
7
8
8
5
3
6

all_lengths =
0. 10
1. 10
2. 4
3. 0
4. 7
5. 8
6. 8
7. 5
8. 3
9. 6

Keyed Lists
A keyed list data type is similar to hash
tables or association lists found in other
programming languages.
struct person {
name: string;
id: int;
};
struct city {
persons: list(key: name) of person;
street_names: list(key: it) of string;
};

Constraints with Keep


keep constraint-bool-exp
Syntax example:
keep kind != tx or len == 16;
Parameters
constraint-bool-exp A simple or a compound Boolean expression.
States restrictions on the values generated for fields in the struct or the
struct subtree,or describes required relationships between field values
and other items in the struct or its subtree.
Hard constraints are applied whenever the enclosing struct is generated.
For any keep constraint in a generated struct, the generator either meets
the constraint or issues a constraint contradiction message.
Note If the keep constraint appears under a when construct, the constraint
is considered

Keep examples
struct pkt {
kind: [tx, rx];
len: int;
keep kind == tx => len == 16;
// when tx pkt { -- this acts exactly the same way
// keep len == 16;
// };
};
Both these constraints are identical to the constraint:
keep kind != tx or len == 16;

Note: this is just the true meaning of => Boolean


implication

Keep examples
Using the predefined method is_a_permutation()
struct astr {
l_1: list of int;
l_2: list of int;
keep l_2.is_a_permutation(l_1);
};

struct transaction {
address: uint;
keep soft address == select { -- using keep soft and select
10: [0..49];
60: 50;
30: [51..99];
};
};

Keep examples
struct transaction {
address: uint;
keep soft address == select {
10: [0..49];
60: 50;
30: [51..99];
};
};
extend transaction {
keep address in [10..50];
keep soft address == select {
10: min;
60: others;
30: max;
};
};

extend instr {
keep soft opcode == select {
40: [ADD, ADDI, SUB,
SUBI];
20: [AND, ANDI, XOR,
XORI];
10: [JMP, CALL, RET,
NOP];
top.carry' * 90: JMPC;
};
};

Keep examples
type transaction_kind: [good, bad];
struct transaction {
kind: transaction_kind;
address: uint;
length: uint;
data: list of byte;
};
extend transaction {
keep length < 24;
keep data[0] == 0x9a;
keep address in [0x100..0x200];
keep me.kind == good;
};

extend sys {
t: transaction;
keep me.t.length != 0;
};

Generation with keep


Generation order is important because it influences the
distribution of values. For example, in the keep constraint
shown below, if kind is generated first, kind is tx about
1/2 the time because there are only two legal values for kind:
struct packet {
kind: [tx, rx];
size: byte;
keep size > 15 => kind == rx;
};

On the other hand, if size is generated first, there is only a 1


in 16 chance that size will be less than or equal to 15, so
kind will be tx about 1/16 of the time.

Specman Tutorial Example


Use src files from tar archive
CPU and Testbench are both modeled in
specman

No need for nclaunch simulator


easy high level modeling of CPU
Black box testing

CPU_top.e
<'
// Basic:
import CPU_instr, CPU_misc;
// Add dut and drive:
import CPU_dut, CPU_drive;
// Add Coverage:
import CPU_cover;
// Add Checking:
import CPU_checker;
'>

CPU_instr.e
<'
type cpu_opcode: [
ADD, ADDI, SUB, SUBI,
AND, ANDI, XOR, XORI,
JMP, JMPC, CALL, RET,
NOP
] (bits: 4);
type reg : [
REG0, REG1, REG2, REG3
] (bits: 2);

// Opcodes

// Register names

Instruction Formats to test


struct instr {
%opcode : cpu_opcode ;
%op1
: reg ;
kind
: [imm, reg];

// defines legal opcodes for reg instr


keep opcode in [ADD, SUB, AND,
XOR, RET, NOP]
=> kind == reg;

// defines 2nd op of reg instruction


when reg instr {
%op2 : reg ;
};

// defines legal opcodes for imm instr


keep opcode in [ADDI, SUBI, ANDI,
XORI, JMP, JMPC, CALL]
=> kind == imm;

// defines 2nd op of imm instruction


when imm instr {
%op2 : byte;
};

// ensures 4-bit addressing scheme


when imm instr {
keep (opcode in [JMP, JMPC,
CALL]) => op2 < 16;
};
};

Extend the system struc


extend sys {
// creates the stream of instructions
!instrs: list of instr;
-- dont pre-generate the list
};
'>

CPU_misc.e
<'
extend global {
setup_test() is also {
set_config(print,radix,hex);
set_config(cover,mode,normal,show_mode,both);
set_config(print, items, 100);
};
finalize_test() is also{
specman("display print sys.instrs");
};
};
'>

CPU_drive
<
extend sys {
event cpuclk is (fall('top.clk')@sys.any);
cpu_env : cpu_env;
cpu_dut : cpu_dut; //cpu_refmodel : cpu_refmodel;
};
struct cpu_env {
reset_cpu() @sys.cpuclk is {
'top.rst' = 0;
wait [1] * cycle;
'top.rst' = 1;
wait [5] * cycle;
//sys.cpu_refmodel.reset(); // reset reference model
'top.rst' = 0;
};

CPU_drive
drive_one_instr(instr: instr) @sys.cpuclk is {
var fill0 : uint(bits : 2) = 0b00;
wait until rise('top.fetch1');
emit instr.start_drv_DUT;
if instr.kind == reg then {
'top.data' = pack(packing.high, instr);
} else {
// immediate instruction
'top.data' = pack(packing.high, instr.opcode, instr.op1, fill0);
wait until rise('top.fetch2');
'top.data' = pack(packing.high, instr.imm'op2);
};
wait until rise('top.exec');
};

CPU_drive
!next_instr : instr;
num_instrs : uint;
keep soft num_instrs in [40..60];
gen_and_drive_instrs() @sys.cpuclk is {
for i from 0 to num_instrs do {
gen next_instr;
sys.instrs.add(next_instr);
drive_one_instr(next_instr);
};
};

CPU_drive
drive_pregen_instrs() @sys.cpuclk is {
for i from 0 to sys.instrs.size() - 1 {
drive_one_instr(sys.instrs[i]);
};
};
drive_cpu() @sys.cpuclk is {
reset_cpu();
if sys.instrs.size() > 0 then {
drive_pregen_instrs();
} else {
gen_and_drive_instrs();
};
wait [1] * cycle;
stop_run();
};

CPU_drive
run() is also {
start drive_cpu();
};
};
extend instr {
event start_drv_DUT;
};
'>

Test1.e

<'
import CPU_top;
extend instr { // test constraints
keep opcode in [ADD, ADDI];
keep op1 == REG0;
when reg instr {keep op2 == REG1}; // when reg instr
when imm instr {keep op2 == 0x5}; // when imm instr
};
extend sys {
// generate 5 instructions
keep instrs.size() == 5;
};
extend sys {
post_generate() is also {
gen instrs;
// start generating stream of instructions
};
};
'>

Test2.e
<'
import CPU_top;
extend instr {
keep soft opcode ==
30
: [ADD,
30
: [AND,
10
: [JMP,
};
};
'>

select {
ADDI, SUB, SUBI];
ANDI, XOR, XORI];
JMPC, CALL, RET, NOP];

// arithmetic operation
// Logic operation
// Other operation

State Coverage
extend cpu_env {
event cpu_fsm is @sys.cpuclk;
// DUT Coverage: State Machine and State Machine transition coverage
cover cpu_fsm is {
item fsm : FSM_type = 'top.cpu.curr_FSM';
transition fsm;
};
};

Test3.e
extend instr {
keep soft opcode == select {
// high weights on arithmetic instructions
40
: [ADD, ADDI, SUB, SUBI];
20
10

: [AND, ANDI, XOR, XORI]; // Logic operation


: [JMP, CALL, RET, NOP]; // Other operation

// generation of JMPC controlled by the carry signal value


'top.carry' * 90 : JMPC
;
};
};
extend cpu_env {
keep num_instrs == 52;
};
extend global {
setup_test() is also {
set_config(gen, seed, 7);
};
};

Checker.e: data checker


extend cpu_env {
// Data Checker
event exec_done is (fall('top.exec') and
true('top.rst' == 0))@sys.cpuclk;
on_exec_done() is {
// Compare PC - program counter
check that sys.cpu_dut.pc == sys.cpu_refmodel.pc else
dut_error("DATA MISMATCH(pc)");
};

Temporal Checker
// Temporal (Protocol) Checker
event enter_exec_st is (change('top.cpu.curr_FSM') and
true('top.cpu.curr_FSM' == exec_st))@sys.cpuclk;
event fetch1_assert is (change('top.fetch1') and
true('top.fetch1' == 1))@sys.cpuclk;

// Interface Spec: After entering instruction execution state, fetch1


//
signal must be asserted in the following cycle.
expect @enter_exec_st => { @fetch1_assert}@sys.cpuclk else
dut_error("PROTOCOL ERROR");
};

Operation Coverage
extend instr {
cover start_drv_DUT is {
item opcode;
item op1;
item carry: bit = 'top.carry';
cross opcode, carry;
};
};

Reference model
struct cpu_refmodel {
regs[4]
:list of byte;
pc
:byte;
stack :list of byte;

// Current PC
// stack of PC values

fetch(r:reg):byte is {
return(regs[r.as_a(int)]);
};
update(r:reg, val:byte) is {
regs[r.as_a(int)] = val;
};

// compute an ALU function

compare(r:byte, val:byte):bool is { // compare against a reg


result = (r == val);
if ! result {
out("Register has ",r, " While HDL has ", val);
};
};

ret

Contd.
jump(val: byte) is {
pc = val;
};
jumpc(cpu_dut: cpu_dut, val: byte) is {
// cheated a bit here
pc = cpu_dut.pc;
//if (cpu_dut.carry == 1) then {pc = val)}};
//pc = val;
};
call(val: byte) is {
stack.add(pc+2);
pc = val;
};

Reset operation
reset() is {
for i from 0 to 3 do {
regs[i] = 0; // Initialize Specman reference regs
};
pc = 0;
};

Example of execution
execute(instr: instr,
var op2_val :
var reg_instr
var imm_instr

cpu_dut: cpu_dut) is {
byte;
: reg instr;
: imm instr;

// compute next PC
case instr.opcode {
[ADD, SUB, XOR, AND, NOP]: {jump(pc +1);};
[ADDI, SUBI, XORI, ANDI, JMPC]: {jump(pc +2);};
};
if (instr.kind == reg) then{
reg_instr = instr.as_a(reg instr);
op2_val = fetch(reg_instr.op2);
} else {
imm_instr = instr.as_a(imm instr);
op2_val = imm_instr.op2;
};
// compute next CPU State

Das könnte Ihnen auch gefallen