Project

General

Profile

C++TESK Getting Started » History » Version 6

Mikhail Chupilko, 09/19/2013 03:47 PM

1 1 Mikhail Chupilko
h1. C++TESK Getting Started
2
3
h2. Introduction
4
5 3 Mikhail Chupilko
_Hardware verification_ is usually understood as the process of checking _behavior_ of hardware on conformity to its _specification_. Such a process can be done formally by means of, e.g., model checking, automatic theorem proving, etc. Also, verification can be done by means of _simulation of_ separated hardware modules with the help of _simulator_.
6 1 Mikhail Chupilko
7 3 Mikhail Chupilko
Accounting the complexity of hardware models under verification, the task of automation should have usually been solved before the actual verification. The more processes will be done automatically and the less manual labor will be needed, the more effective check will be made. Without touching upon the formal verification methods, in this course we will focus only on simulation-based verification. Moreover, we will further speak only about one of the existing verification tool, created in the Institute for system programming of RAS. The tool''s capabilities allow speaking about it as a powerful and quite modern solution. So, we will speak about using _C++TESK Testing ToolKit_ (or C++TESK for short).
8 1 Mikhail Chupilko
9 3 Mikhail Chupilko
C++TESK implements simulation based approach to verification. The main element of the tool is its _core library_, implemented in programming languages C and C++. All core components are arranged in one package and are available at http://forge.ispras.ru/projects/cpptesk-toolkit/files. The tool is designed for creating test systems using C++ for different models of synchronous hardware at different levels of abstraction. Test systems are created using any means, provided by C++, basing on the approach, macros and classes defined by C++TESK.
10 1 Mikhail Chupilko
11 3 Mikhail Chupilko
When creating test systems for simulation based verification, three main tasks are usually solved. The first one is _test sequence construction_, the second one is _checking of behavior correctness_, and the third one is test completeness estimation. C++TESK allows construction test sequences of two types: selection _random stimulus set_ from the previously described stimuli at each simulation cycle or _selective choice_ of stimuli based on techniques of _exploration_ of implicitly defined FSMs. Checking of behavior correctness is made at each simulation cycle by means of _executable reference model_, created by verification engineer at some level of abstraction. External model (e.g., system simulator) can also be used. _Test completeness_ is determined either by the number of testing cycles for randomly selected stimuli, or on the basis of the information about _completeness of FSM exploration_.
12 1 Mikhail Chupilko
13 3 Mikhail Chupilko
Common scheme of test system is represented in figure 1.
14
15
!figure1.png!
16
*Figure1. Generalized structure of test system*
17
18
Let us shortly describe elements represented at the common scheme.
19
# _Stimulus generator_ is a component making test sequence (stimuli sequence). It is adjusted by test scenarios;
20
# _Test oracle_ is a component receiving data flows from stimulus generator and target component, sending stimulus flow from generator to target component, estimating correctness of target system behavior;
21
# _Target system_ is a hardware model, developed at one of hardware description languages (here, in Verilog), receiving stimulus flow and responding to it by reactions which should be checked;
22
# _Coverage tracker_ is a component grabbing information about reference model functional coverage, which in general affects stimulus generator work (e.g., by information of reached coverage);
23
# _Test report generator_ is a component making reports (test traces) with information of traversed transitions, reached coverage, found errors, etc.
24
25
26
The following tasks will be overviewed below.
27
* Analysis of documentation for development of C++TESK test systems;
28
* Development of test oracles including reference models and their adapters;
29
* Definition of test coverage;
30
* Setting up stimulus generator;
31
* Verification itself.
32
33 5 Mikhail Chupilko
h2. 1. Documentation analysis for development of test systems
34 3 Mikhail Chupilko
35
Under _verification_ we mean a process of checking observed behavior against specification. In the other words, verification is an establishing of the correspondence between target system behavior and its specification. Therefore, we should have not only the target system, but its specification too, being a document written at the beginning of target system development, slightly modified during development process, and containing information of target system functionality.
36
37
In reality specification is often very poor or even absent, and target system developers might have written only lists of _input_ and _output interface_ signals. In this case to conduct verification is difficult as to speak about bug in target system is possible only having information about correct behavior. Verification engineers have to interview target system developers, making list of requirements which are obligatory for target system. When list of requirements (specification) is obtained, verification can be started.
38
39
Practice shows that specification (especially, cycle accurate) is convenient to represent in the following ways.
40
1. by means of block diagrams (see figure 2).
41
42
!figure2.png!
43
*Figure 2. Example of block diagram for single operation*
44
45
Block diagram allows describing reference model behavior with any proximity to cycle accurateness. Figure 2 contains abstract representation of operation IO-WRITE (write via IO channel), starting from one-cycle request by the 20th interface. At the following cycle, device starts to receive data, taking 17, 19, 33 or 37 cycles depending on data length and presence of mask among sent data. This block diagram is insufficient for usage in reference model development and is supplied with _chart of cycle accurate operation description_, which should be described below. Operation finishes after receiving and saving of all data.
46
47
The following notations are used in block diagrams.
48
* Oval symbols mean points of operation start and stop; start point contains mnemonic designation of operation to be executed in this control flow path;
49
* Rectangle symbols mean blocks taking exactly one cycle for execution at current abstraction level. Process of reference model functioning is always described inside of such blocks. Block is allowed to be supplied with information about real execution time, other auxiliary information facilitating binding the scheme to reference model.
50
* Rhombus symbols mean branches in control flow. The symbol should contain condition of branching inside. There should be only one ingoing path and two outgoing paths;
51
* Forking of control flow is represented by bold point with two or more outgoing paths;
52
* Merging of several control flow paths is made also in bold point, meaning synchronization of control flow paths (waiting for the slowest one) to proceed to the outgoing path.
53
54
2. by means of _charts of cycle accurate operation description_ (see table 1).
55 4 Mikhail Chupilko
56
*Table 1. Example of chart of cycle accurate operation description*
57
| Stimulus | Branch 0 | Microoperation 0 | Microoperation 1 | Branch 1 | Branch 2 | Microoperation 2 | Microoperation 3 |
58
| PRE:
59
  1) only one operation of the type may be executed simultaneously 2) val_wr_data_buff_nreg_to_IO(o)=1 | BRANCH:
60
  if val_mask=1 (in stimulus) microoperation 0 is started, else microoperation 1 is started. | PRE:
61
  - | PRE:
62
  - | BRANCH:
63
  if wr64=0 (in stim.), operation is done, else branch 2 is started. | BRANCH:
64
  if val_mask=1 (in stimulus),  microoperation 2 is started, else micro operation 3 is started. | PRE:
65
  - | PRE:
66
  - |
67
| Set stimulus parameters:
68
 val_wr_data_from_IO (i)=1 (strobe)
69
 wr_data_from_IO [15:0](i)=[0-7]
70
 Stimulus takes one cycle. || wr_data_from_IO [15:0] (i) contains mask. It is written into buffer(21) (with capacity of 4 32- or 64-bytes words).
71
 (repeat once). | wr_data_from_IO [15:0] (i) contains 2 data bytes. Data are written into buffer(21).
72
 (repeat 16 times). ||| wr_data_from_IO [15:0] (i) contains mask for the higher part of 64-byte transmission, being written in buffer(21).
73
 (repeat once). | wr_data_from_IO [15:0] (i) contains 2 bytes of the second part of 64-byte transmission. Data are written in buffer(21). (repeat 16 times). | 
74
||| POST:
75
 next cycle after val signal wr_data_buff_nreg_to_IO (o) (being written item in (21)) changes to the number of the following free item or to 8 if buffer (21) is full |    POST:
76
 next cycle after val signal wr_data_buff_nreg_to_IO (o) (being written item in (21)) changes to the number of the following free item or to 8 if buffer (21) is full ||| POST:
77
 - | POST:
78
 - |
79
| INPUT:
80
 val_wr_data_from_IO(i) - strobe
81
 wr_data_from_IO[15:0](i) includes
82
 - [15:3] - reserved;
83
 - [2] A5 – write to the high 32-bytes of 64-byte item flag;
84
 - [1] wr64 – 64-byte transmission flag;
85
 - [0] val_mask – mask flag. | INPUT:
86
 wr_data_from_IO [15:0] - mask | INPUT:
87
 wr_data_from_IO [15:0] - data ||| INPUT:
88
 wr_data_from_IO [15:0] - mask | INPUT:
89 1 Mikhail Chupilko
 wr_data_from_IO [15:0] - data |
90 5 Mikhail Chupilko
91
Such a table represents target system behavior in cycle-accurate manner. Information in the table corresponds to block diagram and used for the following development of cycle-accurate reference model. The table is created for each operation, performed by target system. The column with stimuli comes first, where information about operation preconditions and set input signals should be written. There are four types of the other columns according to block diagrams: microoperation (one cycle of work at current level of abstraction), branching, forking, joining. The table supposes left-to-right execution of operations: the stimulus runs first, and then the following columns run one by one. Each microoperation contains information about its precondition, correspondent actions of reference model, postcondition to be checked after execution, used input and output signals. Each branch item has a condition and numbers of columns for jumps. Each fork and join items are supplied with numbers of input and output columns.
92
93
If cycle accurate reference model is not made in particular case (for example, it is taken from system simulator) or the model is abstract, specification in simple text form is enough. Therefore, if they exist in such a form, any additional representation of specification +is not+ necessary.
94
95
Let us proceed to analysis of requirements in context of usage С++TESK. To make С++TESK''s components _«reference model»_ and _«reference model adapter»_, input and output interfaces should be created. Each being verified unit has both input and output signals, which can be grouped in interfaces by their belonging to certain type of activity: writing or reading. Functionally complete sequence of interface call, leading the unit to some stationary state where the unit can stay infinitely, will be called an operation. Notice, that signals like CLK and RST do not usually belong to any interface. Each operation can use several input and output interfaces. Analyzing the documentation, one should let input interfaces contain *only input signals*, and output interfaces contain *only output signals*.
96
97
Let''s review an example of interface information extraction. Let''s take a short specification for microprocessor data hub unit Databox (DB), describing connection between DB and external unit Memory Access Unit (MAU).
98
99
_Arbiter (13), working with round priority, selects one request from 5 issues of short requests (they may come in parallel) to send into MAU. The pace of transmission is 1 request per 2 cycles in case of 32-byte or 1 request per 4 cycles in case of 64-byte request. The selected short request is stored into DB and is not sent to MAU.  MAU has the following interface (see the following table)._
100
101
*Table 2. Interface to MAU*
102
| Signal | Type | Semantics |
103
| val_data_reqack_to_mau_03 | O | Request validity flag. Request for lower part of 64-byte cache row |
104
| val_data_reqack_to_mau_47 | O | Request validity. Request for higher part of 64-byte cache row |
105
| cop_data_reqack_to_mau[2:0] | O | Operation code:
106
«011» - reset register LDB
107
«100» - send coherent answer
108
«101» - reset register STB
109
«110» - send data from register STB
110
«111» - send data and reset register STB |
111
| source_reg_data_reqack_to_mau[4:0] | O | Number of register LDB/STB |
112
113
According to this documentation fragment, abstract reference model requires input interfaces for all five resources of short requests. More information about interfaces is likely to be found later. Keep on reading: “request is sent to MAU”. MAU is an external to the being tested unit and test system has to control data sent there. To control them, first, they have to be taken; second, restrictions for data should be known. To have these tasks done, component “output interface” being correspondent to implementation output interface is created in reference model. This component is overloaded in reference model adapter (where it is bound to implementation signals) and start solving task of getting signals going from implementation and sending them to controlling component. Let this output interface be named as iface14. It will contain signals val_data_reqack_to_mau_03, val_data_reqack_to_mau_47, cop_data_reqack_to_mau, source_reg_data_reqack_to_mau. Notice, that subdivision of signals into interfaces is only logical in current version of C++TESK. Only selected signals are allowed in correspondent interface adapters but it is not checked. After finding of interfaces, the rest of documentation is ordered by means of block diagrams or table of cycle accurate operation description if cycle accurate model is needed. If cycle accurate model is not needed or it has been developed, one may proceed to the following chapter.
114
115
h2. Development of reference model
116
117
We proceed to development of test oracle being a control component. Having received stimuli from stimulus generator, it sends them to target system, receives its reactions and checks them (see figure 1). This checking requires reference values obtained from reference model. Test oracle can be said to be a «wrapper» of _reference model_. We will return to test oracle later, speaking about reference model now. Being used in C++TESK structure of reference model is represented in figure 3.
118
119
!figure3.png!
120
*Figure 3. Structure of С++TESK reference model*
121
122
Reference model contains the following main parts.
123
* _Input interface models_;
124
* _Functional model of target system_;
125
* _Output interface models_.
126
127
All data inside of model are carried by messages being instances of class _Message_. It is made for the purpose of unification of interfaces between test system components. Input and output interface models are instances of class _Interface_.
128
129
Functional model of system is a part of reference model class but can be written in a separated class. Functional model contains of _control logic_, _data processing_, and _interface commutation models_. First two parts can be implemented in separated classes, but the third part depends on interfaces in reference model class.
130
131
The following sub chapter describes message model development. Typically, at least two types of message are used – for input and output interfaces. After introduction of message model, we will return to reference model development.
132
133
h3. 2.1 Message model
134
135
All necessary macros and classes are located in file *<hw/message.hpp>*.
136
137 6 Mikhail Chupilko
Message model is a class developed by means of macro @MESSAGE(class_name) {}@. Let us place empty constructor and destructor inside of macro''s code block. Then let us turn on copying constructor by adding macro @SUPPORT_CLONE(class_name)@ after those two functions.
138 5 Mikhail Chupilko
139 6 Mikhail Chupilko
Each message contains one or several data fields. These fields do not necessary have the same names as names of DUV wires, but their names are to be convenient for test system developer. In the majority of cases, field names are equivalent to the wire names at certain abstract level, but do not go too far: one “global” field in message for data from all wires is not convenient at all. To include fields into message class, macro @DECLARE_FIELD(name, length)@ is used. The length should not exceed 64 bits. This macro defines a variable in message class and creates get and set functions @message_class_object.get_field_name and message_class_object.set_field_name@ for the variable. Manual initialization of fields is allowed in message constructor as they are available via their names. Although, values do not have to be assigned to the fields manually; this work can be done automatically by macro @RANDOMIZE_MESSAGE(pointer_to_message_class_object)@. Notice, that all explicitly assigned values will be overwritten after macro being called. All fields should be registered in message class constructor by macro @ADD_FIELD(message_class::field_name)@.
140 5 Mikhail Chupilko
141
Header for message models (fifo_msg.h) can look as follows.
142
<pre><code class="cpp">
143
#pragma once
144
#include <hw/message.hpp>
145
namespace cpptesk {
146
namespace fifo {
147
148
MESSAGE(InputData) {
149
public:
150
  InputData();
151
  virtual ~InputData();
152
153
  SUPPORT_CLONE(InputData);
154
  DECLARE_FIELD(data, 8);
155
};
156
157
MESSAGE(OutputData) {
158
public:
159
  OutputData();
160
  OutputData(uint8_t datum);
161
  virtual ~OutputData();
162
163
  SUPPORT_CLONE(OutputData);
164
  DECLARE_FIELD(data, 8);
165
};
166
}}
167
</code></pre>
168
169
Notice, that directive #pragma once is wide distributed but not fully standard way to ask compiler include the header file into library only once (the other way is to use #ifndef, #define, and #endif).
170
171
File with message model implementation (fifo_msg.cpp) can look as follows.
172
173
<pre><code class="cpp">
174
#include <fifo_msg.h>
175
namespace cpptesk {
176
namespace fifo {
177
178
InputData::InputData(void) {
179
  ADD_FIELD(InputData::data);
180
  RANDOMIZE_MESSAGE(*this);
181
}
182
183
InputData::~InputData(void) {}
184
185
OutputData::OutputData(void) {
186
  ADD_FIELD(OutputData::data);
187
}
188
189
OutputData::OutputData(uint8_t output_data) {
190
  ADD_FIELD(OutputData::data);
191
  data = output_data;
192
}
193
194
OutputData::~OutputData(void) {}
195
}}
196
</code></pre>
197
198
h3. 2.2 Reference model
199
200
Necessary macros and classes are located in file <hw/model.hpp>.
201
202 6 Mikhail Chupilko
Model class is created by macro @MODEL(model_class_name) {}@. Minimally necessary methods include constructor and virtual destructor. Selected in previous parts interfaces are defined in model class by macros @DECLARE_INPUT(interface_name)@ for input interfaces of target system and @DECLARE_OUTPUT(interface_name)@ for output interfaces of target system. Each output interface may be supplied with a method returning availability of the interface by means of standard for interface method @interface_name.isReady()@. Let such methods be declared as @virtual bool isInterfaceNameReady() const@. Notice that availability of interfaces depends on running operations using them: if some operation has been started on the interface, it will be busy till operation stops its execution.
203 5 Mikhail Chupilko
204 6 Mikhail Chupilko
Model also contains definition of operations being sequences of registered interface calls. Being, in fact, operations, model methods are declared by macro @DECLARE_STIMULUS(method_name)@ and @DECLARE_REACTION(method_name)@. They are not distinguished, but the first macro is typically used for definition of operations themselves, the second macro is used for operation parts requiring reading of data from output interfaces.
205 5 Mikhail Chupilko
206
Header of reference model class (fifo_model.h) can look as follows.
207
208
<pre><code class="cpp">
209
#include <hw/model.hpp>
210
#include <fifo_msg.h>
211
namespace cpptesk {
212
namespace fifo {
213
214
MODEL(FIFO) {
215
public:
216
  FIFO();
217
  virtual ~FIFO();
218
  DECLARE_INPUT(iface1);
219
  DECLARE_OUTPUT(iface2);
220
  virtual bool isIface1Ready() const;
221
  DECLARE_STIMULUS(push_msg);         // operation “push data”
222
  DECLARE_STIMULUS(pop_msg);          // operation “pop data”
223
  DECLARE_REACTION(get_pop_msg);      // reaction “read output data”
224
  void push_item(int data);           // reference model function “push data”
225
  uint8_t pop_item(void);             // reference model function “pop data”
226
protected:
227
  std::vector<uint8_t> fifo;
228
};
229
}}
230
</code></pre>
231
232 6 Mikhail Chupilko
Now let us speak a few words about implementation of reference model class. Constructor of the class must contain calls of interface constructor with the only parameter being text description of the correspondent interface. Sensible names are recommended as they will appear in diagnostics messages. Input and output interfaces are registered by means of @ADD_INPUT(interface_name)@ and @ADD_OUTPUT(interface_name)@ correspondingly.
233 5 Mikhail Chupilko
234 6 Mikhail Chupilko
Let us proceed to description of functions showing interface availability. They use standard for interface class method isReady and simply return its value: @return interface_name.isReady()@.
235 5 Mikhail Chupilko
236 6 Mikhail Chupilko
Let us now describe operations. Each operation starts from copying of input message into local variable by means of macro @CAST_MESSAGE(input_message_type)@. Then message may be sent to the interface, set during operation start. If it is sent to an input interface, we are speaking about stimulus start, or about reaction start in another case. If we want to send message to an input interface with standard parameters (i.e., without changing of message, of interface name), this sending can be done by macro @START_STIMULUS(start_mode)@. Start modes are the following.
237
*PARALLEL creates different process for sending and processing of sending results; the process is executed in parallel with operation commands written after @START_STIMULUS@;
238 5 Mikhail Chupilko
*SEQUENTIAL stops execution of operation till stimulus execution is finished.
239
240
Being sent message will be serialized or turned into sequence of actions to DUV applied via its input wires. This work is done by serializers in reference model adapter (see next step).
241
242 6 Mikhail Chupilko
Notice: if sending message or used interface are different from those being input parameters of function (what are “those being input parameters” will be clarified later), one should use macro @RECV_STIMULUS(start_mode, interface_name, message_object_name)@. In this case operation has to be started from @START_PROCESS()@, not from @START_STIMULUS(start_mode)@, and to be finished by @STOP_PROCESS()@ instead of @STOP_STIMULUS()@ (see later).
243 5 Mikhail Chupilko
244
Operation description may contain macro CYCLE (), telling to the test system that execution of this operation requires one cycle of delay. Operation is finished by macro STOP_STIMULUS ().
245
246 6 Mikhail Chupilko
Parts of operations reading data from output interfaces are subject to separation into specific methods for definition as “reactions” (@DEFINE_REACTION@). In a reaction method reference model says to the test system that it has certain message to be obtained on a given output interface of DUV by means of macro @SEND_REACTION(start_mode, interface_name, message_object_name)@.
247 5 Mikhail Chupilko
248 6 Mikhail Chupilko
Notice: @SEND_REACTION@ works with only output interfaces!
249 5 Mikhail Chupilko
250 6 Mikhail Chupilko
Standard interface and message object may be obtained by means of macros @GET_IFACE()@ and @GET_MESSAGE()@ (under standard we means those, which are input parameters). Reaction methods (if they are developed) should be called from stimulus method. Best way to call reaction method is to call reaction_method_name (process, interface_name, message_object_name), where process is an implicitly defined operation execution context. To send the context is necessary for joining all operation parts together.
251 5 Mikhail Chupilko
252
Development of DUV functional model (more precisely, control logic part) is not a specific task in test system development by means of C++TESK. This task is done by means of C++ standard library and that is why detailed comments about it will be omitted.
253
254
After all the steps, the following file with model implementation may be obtained (fifo_model.cpp).
255
256
<pre><code class="cpp">
257
#include <fifo_model.h>
258
namespace cpptesk {
259
namespace fifo {
260
261
FIFO::FIFO() {
262
  ADD_INPUT(iface1);
263
  ADD_OUTPUT(iface2);
264
}
265
266
FIFO::~FIFO() {}
267
268
bool FIFO::isIface1Ready() const { return iface1.isReady(); }
269
270
DEFINE_STIMULUS(FIFO::push_msg) {
271
  InputData data = CAST_MESSAGE(InputData);
272
  push_item(data.get_data());
273
  START_STIMULUS(PARALLEL);
274
  CYCLE();
275
  STOP_STIMULUS();
276
}
277
278
DEFINE_STIMULUS(FIFO::pop_msg) {
279
  InputData data = CAST_MESSAGE(InputData);
280
  START_STIMULUS(PARALLEL);
281
  OutputData outdata = OutputData(pop_item());
282
  get_pop_msg(process, iface2, outdata);
283
  STOP_STIMULUS();
284
}
285
DEFINE_REACTION(FIFO::get_pop_msg) {
286
  START_PROCESS();
287
  SEND_REACTION(SEQUENTIAL, GET_IFACE(), GET_MESSAGE());
288
  STOP_PROCESS();
289
}
290
291
void FIFO::push_item(uint8_t data) {
292
  assert(fifo.size() < 16);
293
  fifo.push_back(data);
294
}
295
296
uint8_t FIFO::pop_item(void) {
297
  assert(fifo.size() > 0);
298
  uint8_t data = fifo[0];
299
  fifo.erase(fifo.begin());
300
  return data;
301
}
302
}}
303
</code></pre>
304
305
h2. 3. Auxiliary tool VeriTool
306 1 Mikhail Chupilko
307
Digital devices are developed in hardware description languages (HDLs) like Verilog and VHDL. In this paper we use only Verilog language (http://en.wikipedia.org/wiki/Verilog) as one of the most distributed. To connect design under verification (DUV) with test systems developed in different languages (not Verilog), Verilog standard describes special Verilog procedural interface (VPI).
308
309
To make files of VPI-environment (environment is understood to be a set of functions connecting Verilog-simulator running DUV and test system developed in C/C++ in standard to Verilog way), we will use licensed under GPL tool VeriTool (http://forge.ispras.ru/projects/veritool/files). VeriTool uses Verilog syntax analyzer built in open source Verilog simulator Icarus Verilog (http://sourceforge.net/projects/iverilog/). Installation of both tools can be done both manually and automatically by means of script from C++TESK package.
310
311
h3. 3.1 Command line options of tool VeriTool
312
313
Syntax of VeriTool command line looks as follows.
314
<pre>
315
$ veritool [options] input_files
316
</pre>
317
Supported command line options are represented below.
318 6 Mikhail Chupilko
319
# --module=module sets name of being tested Verilog-module to generate test system components. If this option is absent, the first found module will be processed.
320
# --clk=signal sets name of clock signal (default value is clk). This option is used only if options --all or --testbench are used.
321
# --rst=signal sets name of reset signal (default value is rst). This option is used only if options --all or --testbench are used.
322
# --rstpos sets high active level of reset signal (active level is supposed to be low by default).
323
# --all turns on generation of all test system components including DUV wrapper in Verilog, VPI-adapter, VPI-functions and C-structures with DUV-interface. Usage of this option is equivalent to simultaneous usage of --testbench, --vpi-media, --vpi-systf, and --interface (with default values).
324
# --testbench[=file] enables generation of DUV wrapper in Verilog and can set the resulting file name (default value is testbench.v).
325
# --vpi-media[=file] enables generation of VPI-adapter and can set the resulting file name (default value is vpi_media.c).
326
# --vpi-media-header=file sets the name of header file for VPI-adapter (default value is the name set by --vpi-media, but with extension .h). This option works only if option --vpi-media is set.
327
# --vpi-systf[=file] enables generation of VPI-functions and can set the resulting file name (default value is vpi_systf.c).
328
# --vpi-systf-header=file sets the name of header file for VPI-functions (default value is the name set by --vpi-systf, but with extension .h). This option works only if option --vpi-systf is set.
329
# --interface[=file] enables generation of C-structures with DUV-interface definition and can set the resulting file name (default value is interface.c).
330
# --interface-header=file sets the name of header file for C-structures with DUV-interface (default value is the name set by --interface, but with extension .h). This option works only if option --interface is set.
331
# --destination=dir sets an output directory (default value is current directory).
332
# --version | -v shows tool version.
333
# --help | -h shows information about available options.
334
335
h3. 3.2 Running VeriTool
336
337
To run VeriTool is possible as follows.
338
<pre>
339
$(PATH_TO_VERITOOL)/bin/veritool --clk=clock --rst=reset --rstpos target/src/myfifo.v --vpi-systf=vpi_systf.cpp --vpi-media=vpi_media.cpp --interface=interface.cpp
340
</pre>
341
Notice: names of clk and rst signals should be the exactly same as DUV has. The tool supports only one signals of each semantic in one DUV. Possible names of signals are clock, CLOCK, clk, CLK, reset, RESET, rst, RST etc.
342
343
h2. 4. Development of reference model adapter
344
345
We keep on developing of test oracle. Now we are to make reference model adapter (mediator and adapter are synonyms). Adapter is an heir of reference model, providing following means.
346
* binding with DUV for applying stimuli and receiving reactions;
347
* listening of DUV output interfaces for «unexpected» reactions;
348
* matching of DUV and reference model reactions with detailed diagnostics information.
349
350
Reference model adapter structure is represented in figure 4.
351
352
!figure4.png!
353
*Figure 4. Generalized structure of reference model adapter*
354
355
General components of adapter are the following.
356
* inherited reference model;
357
* _input interface adapters_ (or _serializers_) which transform model stimuli into DUV stimuli by converting abstract messages into sequence of DUV input wire assignments;
358
* _output interface adapters_ (or _deserializers_) which transform DUV reactions into being checked reactions by converting DUV output wire values into abstract messages;
359
* _reaction detectors_ which are necessary for tracking of DUV output activity; they are created for each output interface and check DUV reactions by means of model reactions;
360
* _additional reactions matchers_ (secondary arbiters) which make final decision of correspondence between DUV and model reactions (if relation 1:1 is not satisfied)
361
362
h3. 4.1 Development of adapter class
363
364
All necessary classes and macros for development of adapter are located in <hw/media.hpp>.
365
366
Typically, mediator development requires additional structures describing VPI-interface of DUV. Such structures have been already created  by means of VeriTool (file interface.h).
367
368
Adapter is a heir of reference model class and defined by macro @ADAPTER(adapter_class_name, model_class_name)@. @MEDIA@ is an alias of @ADAPTER@. This class at least contains default constructor, virtual destructor and also maximum timeout of waiting for sent reaction (let us call it @REACTION_TIMEOUT@ being a simple int constant). Also, adapter class contains overloaded functions showing availability of interfaces.
369
370
Macro @DECLARE_PROCESS(serializer_name)@ allows definition of a method processing reference model messages sent to a given model interface into sequence of DUV input wire assignments (the method is called serializer). Serializers are created for each input interface. Macro @DECLARE_PROCESS(deserializer_name)@ allows definition of a method processing DUV messages into message understood by test system. Deserializers send wrapper DUV messages to check against reference model messages. Deserializers are created for each output interface.
371
372
In some cases, reference model may send several messages to the same output interface in short time. It may happen that adapter will not be able to match those model messages and received DUV message. In this case, component ordering model reactions (so called arbiter) defined by  macro DECLARE_ARBITER (ordering_mode, ordering_component_name) is used. There are following ordering modes.
373
* FIFO where model messages are ordered according to the time of their sending,
374
* Priority which orders model messages according to their priority,
375
* Adaptive which match reference model and DUV reactions by their data.
376
377
Output interfaces also support finding unexpected reactions. Reaction is called unexpected if interface adapter couldn''t match this reaction with correspondent reference model reaction. It might happen if model reaction has not been sent or matching method did not recognize correspondence.
378
379
Finding of unexpected messages is done by means of standard listeners defined by macro @DEFINE_BASIC_OUTPUT_LISTENER(listener_name, output_interface_object_name, condition)@, where condition is a predicate for message detection.
380
381
Also, adapter should contain the following auxiliary functions and variables for testing HDL DUV.
382
* @virtual void initialize()@ prepares test system and DUV for verification;
383
* @virtual void finalize()@ stops DUV and might contain finalizing commands for test system;
384
* @inputs_t inputs@ is a structure with the same fields as DUV input signals (generated);
385
* @virtual void setInputs()@ sets DUV inputs signals to values of fields from inputs;
386
* @outputs_t outputs@ is a structure with the same fields as DUV output signals (generated);
387
* @virtual void getOutputs()@ sets fields from outputs to DUV output signal values at this cycle;
388
* @virtual void simulate()@ gives control flow to simulator for modeling one cycle of DUV.
389
390
Macro VERITOOL_ADAPTER used instead of ADAPTER creates all these functions and variables automatically.
391
392
Result of these actions may look as follows (fifo_media.h).
393
394
<pre><code class="cpp">
395
#pragma once
396
#include <hw/veritool/media.hpp>
397
#include <model.h>
398
#include <interface.h>
399
#include <string>
400
401
namespace cpptesk {
402
namespace fifo {
403
404
VERITOOL_ADAPTER(FIFOMediator, FIFO)
405
{
406
public:
407
  FIFOMediator();
408
  virtual ~FIFOMediator();
409
410
  const static int REACTION_TIMEOUT = 100;
411
  virtual bool isIface1Ready() const;
412
  DECLARE_PROCESS(serialize_iface1);
413
  DECLARE_PROCESS(deserialize_iface2);
414
  DEFINE_BASIC_OUTPUT_LISTENER(listen_iface2, iface2, outputs.DO_STROBE);
415
  DECLARE_ARBITER(Priority, matcher_iface2);
416
};
417
}}
418
</code></pre>
419
420
Let us proceed to implementation of reference model adapter. We are to bind input and output model interfaces to their adapters (serializers and deserializers correspondingly) by means of macros @SET_INPUT_ADAPTER(interface_object_name, full_adapter_method_name)@ for input interfaces and @SET_OUTPUT_ADAPTER(interface_object_name, full_adapter_method_name)@ for output interfaces.
421
422
Function @setReactionTimeout(timeout)@ sets maximum time in cycles which model reaction may wait for correspondent DUV reaction. Here we recommend to use REACTION_TIMEOUT defined earlier. If the DUV reaction does not appear in timeout, “missing reaction” error is shown.
423
424
Structures inputs and outputs must be initialized in adapter before their usage by means of functions @clear_inputs(&inputs)@ and @clear_outputs(&outputs)@ generated by VeriTool.
425
426
Macro @SET_ARBITER(output_interface_object_name, ordering_component_name)@ should be used for binding output interface and having been defined message ordering component. Macro @CALL_OUTPUT_LISTENER(full_listener_name, output_interface_name)@ starts listener of unexpected reaction on the current interface.
427
428
To turn on debug information printing to the console, standard reference model class function @debug(debug_level)@ should be called. We will use debug level DEBUG_ERROR.
429
Functions showing whether output interface is free are usually overloaded in reference model adapter but they use functions of super class (of reference model). Implementation of these functions in adapter class may be extended by usage of output signals of DUV if necessary.
430
431
Definition of serializers starts from macro @DEFINE_PROCESS(full_serializer_name)@. In the beginning of serializer implementation, reference message can be copied into local variable. To access reference model is possible by means of macro @CAST_MESSAGE(message_class)@. After it, the serializer itself begins by macro @START()@. Function @iface.capture()@ “captures” this particular input interface. This capturing is only logical and needed for prevent two or more operations from simultaneous capturing of the same input interface. If necessary, interface state can be checked by means of function isReady* before stimulus sending. Then appropriate structure fields used for setting of DUV input signals is assigned with values carried by reference message’s fields. When all the actions have been already applied, the interface should be released by calling @iface.release()@. This calling should be done in advice of one cycle before necessary release of the interface. To make serializer wait for one cycle before following setting DUV input signals is possible by means of macro @CYCLE()@. Command @iface.release()@ is usually followed by macro @STOP()@ meaning finish of stimulus processing. At least one @CYCLE()@ is required to be between @iface.capture()@ and @STOP()@.
432
433
Definition of deserializers starts from macro @DEFINE_PROCESS(full_deserializer_name)@. In the beginning of deserializer implementation, a reference to given by reference model message can be created. This message will contain implementation reaction. Macro @START()@ shows beginning of deserialization process. This macro is followed by condition statement allowing to match received implementation reaction and reference model one. This condition is written inside of macro @WAIT_REACTION(match_condition)@. Valid signals are used very often here, but some reference message fields such as address may be used here. Notice that if several implementation reactions satisfy the match_condition, test system will stop with assertion. Therefore match_condition should be enough detailed to differentiate which one really matches to the given reference message. When correspondent reaction is found, some field values of output structure are written into local message. If necessary, macro @CYCLE()@ can make deserializer wait for one cycle before reading the following data. After all data having been read, macro @NEXT_REACTION()@ is used, asking test system to proceed to the next registered model reaction at this particular interface (if this reaction exists). At last, macro @STOP()@ shows finish of deserialization process.
434
435
Reference model adapter file may look as follows (fifo_media.cpp).
436
437
<pre><code class="cpp">
438
#include <fifo_media.h>
439
#include <sync.h>
440
#include <interface.h>
441
#include <vpi_media.h>
442
#include <iostream>
443
444
namespace cpptesk {
445
namespace fifo {
446
447
FIFOMediator::FIFOMediator() {
448
  SET_DEBUG_LEVEL(DEBUG_INFO, true);
449
  SET_INPUT_ADAPTER(iface1, FIFOMediator::serialize_iface1);
450
  SET_OUTPUT_ADAPTER(iface2, FIFOMediator::deserialize_iface2);
451
452
  SET_REACTION_TIMEOUT(FIFOMediator::REACTION_TIMEOUT);
453
454
  SET_ARBITER(iface2, matcher_iface2);
455
  CALL_OUTPUT_LISTENER(FIFOMediator::listen_iface2, iface2);
456
}
457
458
FIFOMediator::~FIFOMediator() {}
459
460
bool FIFOMediator::isIface1Ready() const {
461
  return FIFO::isIface1Ready();
462
}
463
464
DEFINE_PROCESS(FIFOMediator::serialize_iface1) {
465
  InputData msg = CAST_MESSAGE(InputData);
466
  START_PROCESS();
467
  CAPTURE_IFACE();
468
  inputs.WR_STROBE = 1;
469
  inputs.DI = msg.get_data();
470
  RELEASE_IFACE();
471
  CYCLE();
472
  STOP_PROCESS();
473
}
474
475
DEFINE_PROCESS(FIFOMediator::deserialize_iface2)
476
{
477
  OutputData &msg = CAST_MESSAGE(OutputData);
478
  START_PROCESS();
479
  WAIT_REACTION(outputs.DO_STROBE);
480
  msg.set_data(outputs.DO);
481
  NEXT_REACTION();
482
  STOP_PROCESS();
483
}
484
}}
485
</pre></code>
486
487
h2. 5. Development of test scenario
488
489
_Test scenarios_ are test system integral part. Their main aim is to _describe stimuli_ and _select method of their usage_. Test scenarios are used to configure stimulus generation class objects from library of C++TESK. Stimulus generator configured by test scenario is shown in general test system scheme in figure 1 as “stimulus generator”.
490
491
С++TESK supports two common ways of stimulus generation. The first one is based on _finite state machine traversing_ (this way will be considered in one of the following chapters) and _random stimulus selection_ from the list of registered stimuli. Both ways have both advantages and disadvantages. The first one provide better test coverage, being aimed to covering of all states in FSM. The second way requires less labor costs and applies more variable stimuli from the beginning. The first way as a rule is more difficult in development and test running. Following the second way, it is difficult to catch errors appearing after specific sequence of operations applied.
492
493
Remember that the at previous steps of test system creation reference model, reference model adapter, test coverage collector have been developed. According to figure 1, we are to develop not only stimulus generator but test oracle as well. The latter is an adapter extended by _checking components_ (see figure 5), under which we understand _stimulus precondition_ and _reaction comparators_. Stimulus preconditions are predicates which are satisfied if current reference model state allows to start the particular stimulus. If the precondition is satisfied, stimulus will be started at this cycle, in the other case it will be ignored. Then, according to the test scenario, either new stimulus will be checked, or the time is shifted. Preconditions may be placed inside of test scenario or of reference model used by the scenario. Reaction comparators are automatically created when reference model adapter is created.
494
495
!figure5.png!
496
*Figure 5. Test Oracle*
497
498
So, we have all the means to proceed to test scenario development. Scenarios should contain:
499
* object of reference model adapter class;
500
* _scenario functions_;
501
* (optionally) _current state function_.
502
503
_Scenario functions_ create reference model stimuli and apply them to reference model if precondition is satisfied (the latter may be checked inside of reference model). Current state function is necessary if FSM-based stimulus generator is used.
504
505
As having been said above, C++TESK supports two types of test sequence generation: FSM-based and random one. The first way will be reviewed later. To generate stimuli randomly, only scenario functions are necessary, current state is not calculated. In this case, registration of scenario functions should be done according to the following convention. The first stimulus in the list should be the one shifting time (cycle of stimulation), the other ones should not do it. To set up random stimulus generation is possible by means of the following parameters.
506
* maximum cycle number
507
* strategy of stimulus application (max load, min load, dynamic load and their parameters)
508
509
Usage of random scenario parameters will be considered in detail later. Let us proceed to scenario development.
510
511
All necessary classes and macros are defined in  <ts/scenario.hpp>. Used namespaces are cpptesk::ts, cpptesk::hw, cpptesk::tracer. Scenario class is defined by means of macro @SCENARIO(scenario_class_name) {}@. The following functions are standard for scenario definition.
512
* constructor and virtual destructor;
513
* @virtual bool init(int argc, char ** argv)@ initializes test scenario;
514
* @virtual void finish()@ finishes test scenario;
515
* @std::string get_state() (not used now)@ returns current state;
516
* @bool scenario_function_name(IntAbcCtx& ctx)@ makes reference model stimuli and start them;
517
* object of reference model adapter class.
518
519
Having obtained class is a base class for test scenario classes. It can be inherited to obtain new child scenarios. These scenarios use selectively registered scenario functions defined in base class, use appropriate current state function also defined in base class. Child scenarios usually do not require any methods besides constructor and destructor.
520
521
Declaration of test scenario class might look as follows.
522
523
<pre><code class="cpp">
524
#pragma once
525
526
#include <fifo_media.h>
527
528
#include <ts/scenario.hpp>
529
530
using namespace cpptesk::ts;
531
using namespace cpptesk::hw;
532
using namespace cpptesk::tracer;
533
534
namespace cpptesk {
535
namespace examples {
536
537
SCENARIO(ParentScenario) {
538
public:
539
  virtual bool init(int argc, char ** argv);
540
  virtual void finish();
541
  bool scen_nop(IntAbcCtx& ctx);
542
  bool scen_push(IntAbcCtx& ctx);
543
  bool scen_pop(IntAbcCtx& ctx);
544
  std::string get_current_state();
545
546
  ParentScenario(void);
547
  virtual ~ParentScenario(void);
548
549
protected:
550
  DUTMediator dut;
551
};
552
553
class ChildScenario : public ParentScenario {
554
public:
555
  ParentScenario();
556
  virtual ~ParentScenario();
557
};
558
}}
559
</code></pre>
560
561
After declaration of test scenario class, its methods are to be implemented. Parent class constructor (if it is not used as a scenario itself) should call only constructor of its super class @ScenarioBase()@. Each child scenario calls its parent constructor and also uses the following function.
562
<pre>
563
setup(string_with_scenario_name, &full_init_function_name, &full_finalizing_function_name, &full_current_state_function_name).
564
</pre>
565
Usage of setup function is limited by the requirement of locating all registered functions inside of one class. After calling setup function, scenario methods can be registered by means of macro @ADD_SCENARIO_METHOD(full_scenario_function_name)@.
566
567
Scenario functions are methods of parent scenario class. In the beginning of the function, typical local variable should be declared if they are necessary. Then, macro @IBEGIN@ starts substantial part of scenario function. @IBEGIN@ is followed by macro @IACTION{}@, having inside all the actions of creating reference message and its sending to reference model. The simplest sending looks as follows. First, stimulus precondition is checked and if it is satisfied, application is made by function @reference_model_object.start(&full_reference_model_stimulus_name, interface_name, message, reference_model_class_name::START_REQUEST_IFACE)@. In case of scenario function shifting time, function cycle should be used instead of stimulus call: @reference_model_object.cycle()@.
568
569
Whatever precondition shows, @IACTION@ block should be finished by macro @YIELD(reference_model_object.verdict())@, getting results of test oracle work at current cycle. 
570
Returning to test scenario methods, in the simplest case, method init is usually empty and returns true, method finish asks HDL-simulator to stop by calling @reference_model_object.finalize()@. Current state function get_state takes some values returning by reference model functions, makes a string basing on them and return it. Typically, developing of such a function is not a trivial task.
571
572
Implementation of test scenario methods may look as follows.
573
574
<pre><code class="cpp">
575
#include <fifo_scen.h>
576
577
#include <tracer/tracer.hpp>
578
579
namespace cpptesk {
580
namespace examples {
581
582
bool ParentScenario::init(int argc, char ** argv) {
583
  return true;
584
}
585
586
void ParentScenario::finish() {
587
  dut.finalize();
588
}
589
590
bool ParentScenario::scen_nop(IntAbcCtx& ctx) {
591
  IBEGIN
592
  IACTION {
593
    dut.cycle();
594
    YIELD(dut.verdict());
595
  }
596
  IEND
597
}
598
599
bool ParentScenario::scen_push(IntAbcCtx& ctx) {
600
  IBEGIN
601
  IACTION {
602
    if(dut.isIface1Ready() && !dut.is_full()) {
603
      InputData msg = InputData();
604
      dut.start(&DUT::push_msg, dut.iface1, msg, DUT::START_REQUEST_IFACE);
605
    }
606
    YIELD(dut.verdict());
607
  }
608
  IEND
609
}
610
611
bool ParentScenario::scen_pop(cpptesk::ts::IntAbcCtx& ctx) {
612
  IBEGIN
613
  IACTION {
614
    if(dut.isIface2Ready() && !dut.is_empty()) {
615
      InputData msg = InputData();
616
      dut.start(&FIFO::pop_msg, dut.iface2, msg, FIFO::START_REQUEST_IFACE);
617
    }
618
    YIELD(dut.verdict());
619
  }
620
  IEND
621
}
622
623
std::string ParentScenario::get_state_fifo() {
624
  std::string state;
625
  std::stringstream out;
626
  out << "";
627
  state = out.str();
628
  return state;
629
}
630
631
ParentScenario::ParentScenario(void) : ScenarioBase() {}
632
633
ParentScenario::~ParentScenario(void) {}
634
635
ChildScenario::ChildScenario(void) : ParentScenario() {
636
  setup("Example scenario", &ParentScenario::init, &ParentScenario::finish,
637
      &ParentScenario::get_state_fifo);
638
  ADD_SCENARIO_METHOD(ParentScenario::scen_nop);
639
  ADD_SCENARIO_METHOD(ParentScenario::scen_push);
640
  ADD_SCENARIO_METHOD(ParentScenario::scen_pop);
641
}
642
643
ChildScenario::~ChildScenario(void) { };
644
}}
645
</code></pre>
646
647
Having been developed test scenario should be bound to stimulus generator. It is done by means of _test repository_. Repository contains of test scenario class objects together with information of selected test engine and its parameters. Each row of test repository should contain different objects of test scenario classes while object names are used for identification of running test scenario. To start test with given name, it should be sent to HDL-simulator as a parameter +scen=scenario_name (see file testbench.v generated by VeriTool).
648
649
Necessary macros for test repository are defined in file <utils/testreg.h>. Repository starts from macro @TEST_REGISTRY_BEGIN@, and finishes by macro @TEST_REGISTRY_END@. Each test scenario is associated with test engine by macro @REGISTER_TEST(test_scenario_object, test_engine_type, default_parameters)@. Parameter @test_engine_type@ must be either @engine::fsm@ or @engine::rnd@. The first one means FSM-based generation, the second one means random selection of stimuli. Default_parameters allow to set up stimulus generator. The parameter list might include but not restricted by the following ones.
650
* -uerr=N, N>0 orders to test until error counter being equal to N;
651
* -nt turns off tracing in format UTT;
652
* -nt2 turns off tracing in format UTT2;
653
* --length N, N>0 orders to execute random test scenario exactly N cycles;
654
* --parallel turns on parallel stimuli in random test scenario;
655
* --maxload turns on max stimulus load in random test scenario.
656
657
Written in test repository parameters have less priority then those being sent via ARGV (see file testbench.v generated by VeriTool).
658
659
Developed test repository (fifo_tests.cpp) might look as follows.
660
661
<pre><code class="cpp">
662
#include <utils/testreg.h>
663
#include <fifo_scen.h>
664
665
using namespace cpptesk::examples::fifo;
666
667
ChildScenario scen_rnd;
668
669
TEST_REGISTRY_BEGIN
670
REGISTER_TEST(scen_rnd, engine::rnd, "-uerr=1 --length 10000 --parallel")
671
TEST_REGISTRY_END
672
</class></pre>