Project

General

Profile

C++TESK Getting Started » History » Version 9

Alexander Kamkin, 05/22/2014 10:49 AM

1 1 Mikhail Chupilko
h1. C++TESK Getting Started
2
3 9 Alexander Kamkin
{{toc}}
4
5 1 Mikhail Chupilko
h2. Introduction
6
7 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_.
8 1 Mikhail Chupilko
9 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).
10 1 Mikhail Chupilko
11 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.
12 1 Mikhail Chupilko
13 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_.
14 1 Mikhail Chupilko
15 3 Mikhail Chupilko
Common scheme of test system is represented in figure 1.
16
17
!figure1.png!
18
*Figure1. Generalized structure of test system*
19
20
Let us shortly describe elements represented at the common scheme.
21
# _Stimulus generator_ is a component making test sequence (stimuli sequence). It is adjusted by test scenarios;
22
# _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;
23
# _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;
24
# _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);
25
# _Test report generator_ is a component making reports (test traces) with information of traversed transitions, reached coverage, found errors, etc.
26
27
28
The following tasks will be overviewed below.
29
* Analysis of documentation for development of C++TESK test systems;
30
* Development of test oracles including reference models and their adapters;
31
* Definition of test coverage;
32
* Setting up stimulus generator;
33
* Verification itself.
34
35 5 Mikhail Chupilko
h2. 1. Documentation analysis for development of test systems
36 3 Mikhail Chupilko
37
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.
38
39
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.
40
41
Practice shows that specification (especially, cycle accurate) is convenient to represent in the following ways.
42
1. by means of block diagrams (see figure 2).
43
44
!figure2.png!
45
*Figure 2. Example of block diagram for single operation*
46
47
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.
48
49
The following notations are used in block diagrams.
50
* Oval symbols mean points of operation start and stop; start point contains mnemonic designation of operation to be executed in this control flow path;
51
* 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.
52
* 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;
53
* Forking of control flow is represented by bold point with two or more outgoing paths;
54
* 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.
55
56
2. by means of _charts of cycle accurate operation description_ (see table 1).
57 4 Mikhail Chupilko
58
*Table 1. Example of chart of cycle accurate operation description*
59
| Stimulus | Branch 0 | Microoperation 0 | Microoperation 1 | Branch 1 | Branch 2 | Microoperation 2 | Microoperation 3 |
60
| PRE:
61
  1) only one operation of the type may be executed simultaneously 2) val_wr_data_buff_nreg_to_IO(o)=1 | BRANCH:
62
  if val_mask=1 (in stimulus) microoperation 0 is started, else microoperation 1 is started. | PRE:
63
  - | PRE:
64
  - | BRANCH:
65
  if wr64=0 (in stim.), operation is done, else branch 2 is started. | BRANCH:
66
  if val_mask=1 (in stimulus),  microoperation 2 is started, else micro operation 3 is started. | PRE:
67
  - | PRE:
68
  - |
69
| Set stimulus parameters:
70
 val_wr_data_from_IO (i)=1 (strobe)
71
 wr_data_from_IO [15:0](i)=[0-7]
72
 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).
73
 (repeat once). | wr_data_from_IO [15:0] (i) contains 2 data bytes. Data are written into buffer(21).
74
 (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).
75
 (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). | 
76
||| POST:
77
 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:
78
 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:
79
 - | POST:
80
 - |
81
| INPUT:
82
 val_wr_data_from_IO(i) - strobe
83
 wr_data_from_IO[15:0](i) includes
84
 - [15:3] - reserved;
85
 - [2] A5 – write to the high 32-bytes of 64-byte item flag;
86
 - [1] wr64 – 64-byte transmission flag;
87
 - [0] val_mask – mask flag. | INPUT:
88
 wr_data_from_IO [15:0] - mask | INPUT:
89
 wr_data_from_IO [15:0] - data ||| INPUT:
90
 wr_data_from_IO [15:0] - mask | INPUT:
91 1 Mikhail Chupilko
 wr_data_from_IO [15:0] - data |
92 5 Mikhail Chupilko
93
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.
94
95
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.
96
97
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*.
98
99
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).
100
101
_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)._
102
103
*Table 2. Interface to MAU*
104
| Signal | Type | Semantics |
105
| val_data_reqack_to_mau_03 | O | Request validity flag. Request for lower part of 64-byte cache row |
106
| val_data_reqack_to_mau_47 | O | Request validity. Request for higher part of 64-byte cache row |
107
| cop_data_reqack_to_mau[2:0] | O | Operation code:
108
«011» - reset register LDB
109
«100» - send coherent answer
110
«101» - reset register STB
111
«110» - send data from register STB
112
«111» - send data and reset register STB |
113
| source_reg_data_reqack_to_mau[4:0] | O | Number of register LDB/STB |
114
115
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.
116
117
h2. Development of reference model
118
119
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.
120
121
!figure3.png!
122
*Figure 3. Structure of С++TESK reference model*
123
124
Reference model contains the following main parts.
125
* _Input interface models_;
126
* _Functional model of target system_;
127
* _Output interface models_.
128
129
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_.
130
131
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.
132
133
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.
134
135
h3. 2.1 Message model
136
137
All necessary macros and classes are located in file *<hw/message.hpp>*.
138
139 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.
140 5 Mikhail Chupilko
141 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)@.
142 5 Mikhail Chupilko
143
Header for message models (fifo_msg.h) can look as follows.
144
<pre><code class="cpp">
145
#pragma once
146
#include <hw/message.hpp>
147
namespace cpptesk {
148
namespace fifo {
149
150
MESSAGE(InputData) {
151
public:
152
  InputData();
153
  virtual ~InputData();
154
155
  SUPPORT_CLONE(InputData);
156
  DECLARE_FIELD(data, 8);
157
};
158
159
MESSAGE(OutputData) {
160
public:
161
  OutputData();
162
  OutputData(uint8_t datum);
163
  virtual ~OutputData();
164
165
  SUPPORT_CLONE(OutputData);
166
  DECLARE_FIELD(data, 8);
167
};
168
}}
169
</code></pre>
170
171
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).
172
173
File with message model implementation (fifo_msg.cpp) can look as follows.
174
175
<pre><code class="cpp">
176
#include <fifo_msg.h>
177
namespace cpptesk {
178
namespace fifo {
179
180
InputData::InputData(void) {
181
  ADD_FIELD(InputData::data);
182
  RANDOMIZE_MESSAGE(*this);
183
}
184
185
InputData::~InputData(void) {}
186
187
OutputData::OutputData(void) {
188
  ADD_FIELD(OutputData::data);
189
}
190
191
OutputData::OutputData(uint8_t output_data) {
192
  ADD_FIELD(OutputData::data);
193
  data = output_data;
194
}
195
196
OutputData::~OutputData(void) {}
197
}}
198
</code></pre>
199
200
h3. 2.2 Reference model
201
202
Necessary macros and classes are located in file <hw/model.hpp>.
203
204 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.
205 5 Mikhail Chupilko
206 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.
207 5 Mikhail Chupilko
208
Header of reference model class (fifo_model.h) can look as follows.
209
210
<pre><code class="cpp">
211
#include <hw/model.hpp>
212
#include <fifo_msg.h>
213
namespace cpptesk {
214
namespace fifo {
215
216
MODEL(FIFO) {
217
public:
218
  FIFO();
219
  virtual ~FIFO();
220
  DECLARE_INPUT(iface1);
221
  DECLARE_OUTPUT(iface2);
222
  virtual bool isIface1Ready() const;
223
  DECLARE_STIMULUS(push_msg);         // operation “push data”
224
  DECLARE_STIMULUS(pop_msg);          // operation “pop data”
225
  DECLARE_REACTION(get_pop_msg);      // reaction “read output data”
226
  void push_item(int data);           // reference model function “push data”
227
  uint8_t pop_item(void);             // reference model function “pop data”
228
protected:
229
  std::vector<uint8_t> fifo;
230
};
231
}}
232
</code></pre>
233
234 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.
235 5 Mikhail Chupilko
236 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()@.
237 5 Mikhail Chupilko
238 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.
239
*PARALLEL creates different process for sending and processing of sending results; the process is executed in parallel with operation commands written after @START_STIMULUS@;
240 5 Mikhail Chupilko
*SEQUENTIAL stops execution of operation till stimulus execution is finished.
241
242
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).
243
244 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).
245 5 Mikhail Chupilko
246
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 ().
247
248 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)@.
249 5 Mikhail Chupilko
250 6 Mikhail Chupilko
Notice: @SEND_REACTION@ works with only output interfaces!
251 5 Mikhail Chupilko
252 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.
253 5 Mikhail Chupilko
254
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.
255
256
After all the steps, the following file with model implementation may be obtained (fifo_model.cpp).
257
258
<pre><code class="cpp">
259
#include <fifo_model.h>
260
namespace cpptesk {
261
namespace fifo {
262
263
FIFO::FIFO() {
264
  ADD_INPUT(iface1);
265
  ADD_OUTPUT(iface2);
266
}
267
268
FIFO::~FIFO() {}
269
270
bool FIFO::isIface1Ready() const { return iface1.isReady(); }
271
272
DEFINE_STIMULUS(FIFO::push_msg) {
273
  InputData data = CAST_MESSAGE(InputData);
274
  push_item(data.get_data());
275
  START_STIMULUS(PARALLEL);
276
  CYCLE();
277
  STOP_STIMULUS();
278
}
279
280
DEFINE_STIMULUS(FIFO::pop_msg) {
281
  InputData data = CAST_MESSAGE(InputData);
282
  START_STIMULUS(PARALLEL);
283
  OutputData outdata = OutputData(pop_item());
284
  get_pop_msg(process, iface2, outdata);
285
  STOP_STIMULUS();
286
}
287
DEFINE_REACTION(FIFO::get_pop_msg) {
288
  START_PROCESS();
289
  SEND_REACTION(SEQUENTIAL, GET_IFACE(), GET_MESSAGE());
290
  STOP_PROCESS();
291
}
292
293
void FIFO::push_item(uint8_t data) {
294
  assert(fifo.size() < 16);
295
  fifo.push_back(data);
296
}
297
298
uint8_t FIFO::pop_item(void) {
299
  assert(fifo.size() > 0);
300
  uint8_t data = fifo[0];
301
  fifo.erase(fifo.begin());
302
  return data;
303
}
304
}}
305
</code></pre>
306
307
h2. 3. Auxiliary tool VeriTool
308 1 Mikhail Chupilko
309
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).
310
311
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.
312
313
h3. 3.1 Command line options of tool VeriTool
314
315
Syntax of VeriTool command line looks as follows.
316
<pre>
317
$ veritool [options] input_files
318
</pre>
319
Supported command line options are represented below.
320 6 Mikhail Chupilko
321
# --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.
322
# --clk=signal sets name of clock signal (default value is clk). This option is used only if options --all or --testbench are used.
323
# --rst=signal sets name of reset signal (default value is rst). This option is used only if options --all or --testbench are used.
324
# --rstpos sets high active level of reset signal (active level is supposed to be low by default).
325
# --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).
326
# --testbench[=file] enables generation of DUV wrapper in Verilog and can set the resulting file name (default value is testbench.v).
327
# --vpi-media[=file] enables generation of VPI-adapter and can set the resulting file name (default value is vpi_media.c).
328
# --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.
329
# --vpi-systf[=file] enables generation of VPI-functions and can set the resulting file name (default value is vpi_systf.c).
330
# --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.
331
# --interface[=file] enables generation of C-structures with DUV-interface definition and can set the resulting file name (default value is interface.c).
332
# --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.
333
# --destination=dir sets an output directory (default value is current directory).
334
# --version | -v shows tool version.
335
# --help | -h shows information about available options.
336
337
h3. 3.2 Running VeriTool
338
339
To run VeriTool is possible as follows.
340
<pre>
341
$(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
342
</pre>
343
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.
344
345
h2. 4. Development of reference model adapter
346
347
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.
348
* binding with DUV for applying stimuli and receiving reactions;
349
* listening of DUV output interfaces for «unexpected» reactions;
350
* matching of DUV and reference model reactions with detailed diagnostics information.
351
352
Reference model adapter structure is represented in figure 4.
353
354
!figure4.png!
355
*Figure 4. Generalized structure of reference model adapter*
356
357
General components of adapter are the following.
358
* inherited reference model;
359
* _input interface adapters_ (or _serializers_) which transform model stimuli into DUV stimuli by converting abstract messages into sequence of DUV input wire assignments;
360
* _output interface adapters_ (or _deserializers_) which transform DUV reactions into being checked reactions by converting DUV output wire values into abstract messages;
361
* _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;
362
* _additional reactions matchers_ (secondary arbiters) which make final decision of correspondence between DUV and model reactions (if relation 1:1 is not satisfied)
363
364
h3. 4.1 Development of adapter class
365
366
All necessary classes and macros for development of adapter are located in <hw/media.hpp>.
367
368
Typically, mediator development requires additional structures describing VPI-interface of DUV. Such structures have been already created  by means of VeriTool (file interface.h).
369
370
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.
371
372
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.
373
374
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.
375
* FIFO where model messages are ordered according to the time of their sending,
376
* Priority which orders model messages according to their priority,
377
* Adaptive which match reference model and DUV reactions by their data.
378
379
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.
380
381
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.
382
383
Also, adapter should contain the following auxiliary functions and variables for testing HDL DUV.
384
* @virtual void initialize()@ prepares test system and DUV for verification;
385
* @virtual void finalize()@ stops DUV and might contain finalizing commands for test system;
386
* @inputs_t inputs@ is a structure with the same fields as DUV input signals (generated);
387
* @virtual void setInputs()@ sets DUV inputs signals to values of fields from inputs;
388
* @outputs_t outputs@ is a structure with the same fields as DUV output signals (generated);
389
* @virtual void getOutputs()@ sets fields from outputs to DUV output signal values at this cycle;
390
* @virtual void simulate()@ gives control flow to simulator for modeling one cycle of DUV.
391
392
Macro VERITOOL_ADAPTER used instead of ADAPTER creates all these functions and variables automatically.
393
394
Result of these actions may look as follows (fifo_media.h).
395
396
<pre><code class="cpp">
397
#pragma once
398
#include <hw/veritool/media.hpp>
399
#include <model.h>
400
#include <interface.h>
401
#include <string>
402
403
namespace cpptesk {
404
namespace fifo {
405
406
VERITOOL_ADAPTER(FIFOMediator, FIFO)
407
{
408
public:
409
  FIFOMediator();
410
  virtual ~FIFOMediator();
411
412
  const static int REACTION_TIMEOUT = 100;
413
  virtual bool isIface1Ready() const;
414
  DECLARE_PROCESS(serialize_iface1);
415
  DECLARE_PROCESS(deserialize_iface2);
416
  DEFINE_BASIC_OUTPUT_LISTENER(listen_iface2, iface2, outputs.DO_STROBE);
417
  DECLARE_ARBITER(Priority, matcher_iface2);
418
};
419
}}
420
</code></pre>
421
422
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.
423
424
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.
425
426
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.
427
428
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.
429
430
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.
431
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.
432
433
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()@.
434
435
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.
436
437
Reference model adapter file may look as follows (fifo_media.cpp).
438
439
<pre><code class="cpp">
440
#include <fifo_media.h>
441
#include <sync.h>
442
#include <interface.h>
443
#include <vpi_media.h>
444
#include <iostream>
445
446
namespace cpptesk {
447
namespace fifo {
448
449
FIFOMediator::FIFOMediator() {
450
  SET_DEBUG_LEVEL(DEBUG_INFO, true);
451
  SET_INPUT_ADAPTER(iface1, FIFOMediator::serialize_iface1);
452
  SET_OUTPUT_ADAPTER(iface2, FIFOMediator::deserialize_iface2);
453
454 7 Mikhail Chupilko
  SET_REACTION_TIMEOUT(FIFOMediator::REACTION_TIMEOUT);
455 6 Mikhail Chupilko
456
  SET_ARBITER(iface2, matcher_iface2);
457
  CALL_OUTPUT_LISTENER(FIFOMediator::listen_iface2, iface2);
458
}
459
460
FIFOMediator::~FIFOMediator() {}
461
462
bool FIFOMediator::isIface1Ready() const {
463 7 Mikhail Chupilko
  return FIFO::isIface1Ready();
464 6 Mikhail Chupilko
}
465
466
DEFINE_PROCESS(FIFOMediator::serialize_iface1) {
467
  InputData msg = CAST_MESSAGE(InputData);
468
  START_PROCESS();
469
  CAPTURE_IFACE();
470
  inputs.WR_STROBE = 1;
471
  inputs.DI = msg.get_data();
472
  RELEASE_IFACE();
473
  CYCLE();
474
  STOP_PROCESS();
475
}
476
477
DEFINE_PROCESS(FIFOMediator::deserialize_iface2)
478
{
479
  OutputData &msg = CAST_MESSAGE(OutputData);
480
  START_PROCESS();
481
  WAIT_REACTION(outputs.DO_STROBE);
482
  msg.set_data(outputs.DO);
483
  NEXT_REACTION();
484
  STOP_PROCESS();
485
}
486
}}
487 8 Mikhail Chupilko
</code></pre>
488 6 Mikhail Chupilko
489
h2. 5. Development of test scenario
490
491
_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”.
492
493
С++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.
494
495
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.
496
497
!figure5.png!
498
*Figure 5. Test Oracle*
499
500
So, we have all the means to proceed to test scenario development. Scenarios should contain:
501
* object of reference model adapter class;
502
* _scenario functions_;
503
* (optionally) _current state function_.
504
505
_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.
506
507
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.
508
* maximum cycle number
509
* strategy of stimulus application (max load, min load, dynamic load and their parameters)
510
511
Usage of random scenario parameters will be considered in detail later. Let us proceed to scenario development.
512
513
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.
514
* constructor and virtual destructor;
515
* @virtual bool init(int argc, char ** argv)@ initializes test scenario;
516
* @virtual void finish()@ finishes test scenario;
517
* @std::string get_state() (not used now)@ returns current state;
518
* @bool scenario_function_name(IntAbcCtx& ctx)@ makes reference model stimuli and start them;
519
* object of reference model adapter class.
520
521
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.
522
523
Declaration of test scenario class might look as follows.
524
525
<pre><code class="cpp">
526
#pragma once
527
528
#include <fifo_media.h>
529
530
#include <ts/scenario.hpp>
531
532
using namespace cpptesk::ts;
533
using namespace cpptesk::hw;
534
using namespace cpptesk::tracer;
535
536
namespace cpptesk {
537
namespace examples {
538
539
SCENARIO(ParentScenario) {
540
public:
541
  virtual bool init(int argc, char ** argv);
542
  virtual void finish();
543
  bool scen_nop(IntAbcCtx& ctx);
544
  bool scen_push(IntAbcCtx& ctx);
545
  bool scen_pop(IntAbcCtx& ctx);
546
  std::string get_current_state();
547
548
  ParentScenario(void);
549
  virtual ~ParentScenario(void);
550
551
protected:
552
  DUTMediator dut;
553
};
554
555
class ChildScenario : public ParentScenario {
556
public:
557
  ParentScenario();
558
  virtual ~ParentScenario();
559
};
560
}}
561
</code></pre>
562
563
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.
564
<pre>
565
setup(string_with_scenario_name, &full_init_function_name, &full_finalizing_function_name, &full_current_state_function_name).
566
</pre>
567
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)@.
568
569
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()@.
570
571
Whatever precondition shows, @IACTION@ block should be finished by macro @YIELD(reference_model_object.verdict())@, getting results of test oracle work at current cycle. 
572
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.
573
574
Implementation of test scenario methods may look as follows.
575
576
<pre><code class="cpp">
577
#include <fifo_scen.h>
578
579
#include <tracer/tracer.hpp>
580
581
namespace cpptesk {
582
namespace examples {
583
584
bool ParentScenario::init(int argc, char ** argv) {
585 7 Mikhail Chupilko
  return true;
586 6 Mikhail Chupilko
}
587
588
void ParentScenario::finish() {
589 7 Mikhail Chupilko
  dut.finalize();
590 6 Mikhail Chupilko
}
591
592
bool ParentScenario::scen_nop(IntAbcCtx& ctx) {
593
  IBEGIN
594
  IACTION {
595
    dut.cycle();
596
    YIELD(dut.verdict());
597
  }
598
  IEND
599
}
600
601
bool ParentScenario::scen_push(IntAbcCtx& ctx) {
602
  IBEGIN
603
  IACTION {
604
    if(dut.isIface1Ready() && !dut.is_full()) {
605
      InputData msg = InputData();
606
      dut.start(&DUT::push_msg, dut.iface1, msg, DUT::START_REQUEST_IFACE);
607
    }
608
    YIELD(dut.verdict());
609
  }
610
  IEND
611
}
612
613
bool ParentScenario::scen_pop(cpptesk::ts::IntAbcCtx& ctx) {
614
  IBEGIN
615
  IACTION {
616
    if(dut.isIface2Ready() && !dut.is_empty()) {
617
      InputData msg = InputData();
618
      dut.start(&FIFO::pop_msg, dut.iface2, msg, FIFO::START_REQUEST_IFACE);
619
    }
620
    YIELD(dut.verdict());
621
  }
622
  IEND
623
}
624
625
std::string ParentScenario::get_state_fifo() {
626
  std::string state;
627
  std::stringstream out;
628
  out << "";
629
  state = out.str();
630
  return state;
631
}
632
633
ParentScenario::ParentScenario(void) : ScenarioBase() {}
634
635
ParentScenario::~ParentScenario(void) {}
636
637
ChildScenario::ChildScenario(void) : ParentScenario() {
638
  setup("Example scenario", &ParentScenario::init, &ParentScenario::finish,
639
      &ParentScenario::get_state_fifo);
640
  ADD_SCENARIO_METHOD(ParentScenario::scen_nop);
641
  ADD_SCENARIO_METHOD(ParentScenario::scen_push);
642
  ADD_SCENARIO_METHOD(ParentScenario::scen_pop);
643
}
644
645
ChildScenario::~ChildScenario(void) { };
646
}}
647
</code></pre>
648
649
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).
650
651
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.
652
* -uerr=N, N>0 orders to test until error counter being equal to N;
653
* -nt turns off tracing in format UTT;
654
* -nt2 turns off tracing in format UTT2;
655
* --length N, N>0 orders to execute random test scenario exactly N cycles;
656
* --parallel turns on parallel stimuli in random test scenario;
657
* --maxload turns on max stimulus load in random test scenario.
658
659
Written in test repository parameters have less priority then those being sent via ARGV (see file testbench.v generated by VeriTool).
660
661
Developed test repository (fifo_tests.cpp) might look as follows.
662
663
<pre><code class="cpp">
664
#include <utils/testreg.h>
665
#include <fifo_scen.h>
666
667
using namespace cpptesk::examples::fifo;
668
669
ChildScenario scen_rnd;
670 1 Mikhail Chupilko
671
TEST_REGISTRY_BEGIN
672
REGISTER_TEST(scen_rnd, engine::rnd, "-uerr=1 --length 10000 --parallel")
673
TEST_REGISTRY_END
674 7 Mikhail Chupilko
</code></pre>
675
676
h2. 6. Development of functional coverage
677
678
Additional possibility helping to improve test process is _function coverage_. Under coverage usually some value measured in absolute or relative numbers is meant. Test coverage is gathered for evaluation of test completeness: the more numbers are, the better DUV is checked.
679
680
There are several approaches to gather the coverage. In the introduction to С++TESK we will speak only about functional coverage showing number of checked functional properties selected by an engineer.
681
682
At the moment С++TESK supports only manually selected functional coverage structures.  Tracing of their covering is made automatically. Resulted coverage is printed as a report.
683
684
Verification engineer should analyze documentation to the DUV to find situations appearing in functional model which must happen during test process. It is useful to ask DUV developers about their vision of test cases. Then engineer creates functional coverage structure for selected situations. This work is rather creative and its results depend on the qualification of engineer.
685
686
It should be noticed, that C++TESK does not still allow to describe dynamic situations where some action happens after another, in n cycles, etc. Such possibilities will be implemented later.
687
688
All necessary macro and classes are declared in <ts/coverage.hpp> and <tracer/tracer.hpp>.
689
690
Coverage class might be a part of reference model class or it may be a different class. In the second case it has to contain a reference to reference model object, constructor and virtual destructor. In both cases object of class CoverageTracker should be declared. This object will keep registered test coverage structures, print reached test coverage in trace of UTT2 format.
691
692
Coverage can be set by macro @DEFINE_ENUMERATED_COVERAGE(coverage_id, string_form, ((coverage_element_id, coverage_element_string_form), (...,...), ...))@. It declares an object of class Coverage with coverage_id name. Coverage_element_id is used for identification of all subcomponents of being defined coverage structure. All string representations are used in report generation. The tool also allows to copy coverage structures, product coverage structures with or without excluded tuples. Macros @DEFINE_ALIAS_COVERAGE(coverage_id, string_form, initial_coverage_id), DEFINE_COMPOSED_COVERAGE(coverage_id, string_form, initial_coverage_A_id,initial_coverage_B_id),DEFINE_COMPOSED_COVERAGE_EXCLUDING(coverage_id, string_form, initial_coverage_А_id, initial_coverage_B_id, {excluded_element_from_A_id, excluded_element_ from_B_id}, ...)@ are used for these purposes.
693
694
To let coverage information be collected, current state of reference model should be read and used for increasing of an appropriate coverage structure item. The increased values are to be sent into object of CoverageTracker class by means of operator <<. It is easy to do by means of two functions. The first one returns object of Coverage class, selected according to the used reference model parameter. The second function receives value returned by the first function and sends it to the object of CoverageTracker type by means of operator @<<@.
695
696
Header file for coverage class (fifo_coverage.h) may look as follows.
697
698
<pre><code class="cpp">
699
#pragma once
700
#include <hw/model.hpp>
701
#include <ts/coverage.hpp>
702
#include <tracer/tracer.hpp>
703
704
#include <fifo_model.h>
705
706
using namespace cpptesk::ts;
707
using namespace cpptesk::hw;
708
using namespace cpptesk::tracer;
709
using namespace cpptesk::tracer::coverage;
710
711
namespace cpptesk {
712
namespace fifo {
713
714
class DUT;
715
class DUTCoverage {
716
public:
717
  DUTCoverage(DUT &dut): dut(dut) {}
718
  virtual ~DUTCoverage() {};
719
  CoverageTracker coverageTracker;
720
  DEFINE_ENUMERATED_COVERAGE(DUT_FULLNESS, "DUT fullness", ((I0, "0 (free)"), (I1, "1"), (I2, "2"), (I3, "3"), (I4, "4 (full)")));
721
  DUT_FULLNESS select_coverage_element_DUT_FULLNESS(void) const;
722
  void update_coverageTracker_DUT_FULLNESS(void);
723
protected:
724
  FIFO &fifo;
725
};
726
}}
727
</code></pre>
728
729
Implementation of this class (fifo_coverage.cpp) may look as follows.
730
731
<pre><code class="cpp">
732
#include <fifo_coverage.h>
733
734
namespace cpptesk {
735
namespace fifo {
736
737
DUTCoverage::DUT_FULLNESS DUTCoverage::select_coverage_element_DUT_FULLNESS(void) const {
738
  switch(dut.dut_fullness()) {
739
    case 0:  return DUTCoverage::DUT_FULLNESS::I0;
740
    case 1:  return DUTCoverage::DUT_FULLNESS::I1;
741
    case 2:  return DUTCoverage::DUT_FULLNESS::I2;
742
    case 3:  return DUTCoverage::DUT_FULLNESS::I3;
743
    case 4:  return DUTCoverage::DUT_FULLNESS::I4;
744
    default: assert(false);
745
  }
746
}
747
748
void DUTCoverage::update_coverageTracker_DUT_FULLNESS(void) {
749
  coverageTracker << DUTCoverage::select_coverage_element_DUT_FULLNESS();
750
}
751
}}
752
</code></pre>
753
754
If coverage means are created in a separated class, object of the class should be added to reference model class to let binding the reference to the reference model inside of coverage class. To update coverage information kept in coverage class, the second function of the two above is called from time to time.  Such “times” might be cycle shifts, certain operation starts, i.e. all events changing reference model state where functional coverage might change and should be evaluated.
755
756
Reached coverage is traced into UTT2 format. To analyze trace, special report generator is used which can be started by command @$CPPTESK_HOME/bin/reportgen.sh@ trace_name.utt2 from the directory with trace (trace name should not contain spaces). The report generator creates directory Reports with reports about testing to be read by usual Internet browser.
757
758
h2. 7. Development of FSM-based scenarios
759
760
_Addition to chapter 5._
761
762
To generate test actions, FSM-based test engine can also be used. It implies dynamic creation of tight connected finite state machine and its exploration by certain non-extra state algorithm. The FSM is defined by current state function and list of stimuli (scenario functions in this context) with their preconditions. Test is supposed to be finished, when all the stimuli are applied in each state, and no error has been found.
763
764
Each scenario function is supplied by iteration context (see signature of scenario functions: @IntAbcCtx&@ ctx is the context), keeping iterator values. Iterators are constructions for iterative assignment of input parameters of started operations. They let consider stimuli started from the same scenario function but with different iteration variable values to be different stimuli. It is necessary for applying of all input parameter combinations of all stimuli.
765
Iteration variables are declared in the beginning of scenario functions: @int& variable_name = IVAR(a)@. Macro @IVAR()@ should have inside one letter of Latin alphabet (from a till z). Values of these variables will be kept into mentioned above iteration context.
766
767
Let us remind that after initialization of all local variables the pair of macros @IBEGIN-IEND@ is written. Inside of the pair @IBEGIN-IEND@ nested into each other cycles by iteration variables may be written, and the last one should have macro IACTION{} inside. Cycle by iteration variable is a usual cycle “for”: @for(variable_name = 0; variable_name < 2; variable_name++)@. Usage of iteration variable allows to define a set of stimuli being different in this parameter, variable_name. Notice, that iterators are not always needed. In the simplest examples like FIFO, iterators are not necessary but can be added artificially to increase number of FSM states. Let us use iteration variable into scenario function nop to increase the number of arcs issuing from states.
768
769
<pre><code class="cpp">
770
bool ParentScenario::scen_nop(IntAbcCtx& ctx) {
771
  int &pseudo_iteration = IVAR(a);
772
  IBEGIN
773
  for(i = 0; i < 10; i++)
774
    IACTION {
775
      dut.cycle();
776
      YIELD(dut.verdict());
777
    }
778
  IEND
779
}
780
</code></pre>
781
782
Current state function may be the following one.
783
784
<pre><code class="cpp">
785
std::string ParentScenario::get_state_fifo() {
786
  std::string state;
787
  std::stringstream out;
788
  out << dut.fullness() << ","<< dut.isIface1Ready() << dut.isIface2Ready();
789
  state = out.str(); 
790
  return state;
791
}
792
</code></pre>
793
794
To start FSM-based stimulus generation, test engine engine::fsm should be used:
795
796
<pre><code class="cpp">
797
#include <utils/testreg.h>
798
#include <fifo_scen.h>
799
800
using namespace cpptesk::examples::fifo;
801
802
ChildScenario scen_fsm;
803
804
TEST_REGISTRY_BEGIN
805
REGISTER_TEST(scen_fsm, engine::fsm, "-uerr=1 -nt -nt2")
806
TEST_REGISTRY_END
807
</code></pre>