OpenASIP 2.2
Loading...
Searching...
No Matches
FUTestbenchGenerator.cc
Go to the documentation of this file.
1/*
2 Copyright (c) 2002-2010 Tampere University.
3
4 This file is part of TTA-Based Codesign Environment (TCE).
5
6 Permission is hereby granted, free of charge, to any person obtaining a
7 copy of this software and associated documentation files (the "Software"),
8 to deal in the Software without restriction, including without limitation
9 the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 and/or sell copies of the Software, and to permit persons to whom the
11 Software is furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 DEALINGS IN THE SOFTWARE.
23 */
24/**
25 * @file FUTestbenchGenerator.cc
26 *
27 * Implementation of FUTestbenchGenerator class.
28 *
29 * @author Pekka Jääskeläinen 2006 (pekka.jaaskelainen-no.spam-tut.fi)
30 * @author Otto Esko 2010 (otto.esko-no.spam-tut.fi)
31 * @note rating: red
32 */
33
34#include <string>
35#include <sstream>
36#include <vector>
37#include <boost/random.hpp>
38#include <boost/nondet_random.hpp>
39#include <ctime>
40#include <stdint.h>
41#include "HDBManager.hh"
42#include "FUEntry.hh"
43#include "TestbenchGenerator.hh"
45#include "FUArchitecture.hh"
46#include "FunctionUnit.hh"
47#include "Machine.hh"
48#include "MachineState.hh"
50#include "MemorySystem.hh"
51#include "OutputPortState.hh"
52#include "InputPortState.hh"
54#include "FUImplementation.hh"
55#include "HWOperation.hh"
56#include "StringTools.hh"
57#include "Conversion.hh"
58
59using std::string;
60using std::vector;
61using std::ifstream;
62using std::ofstream;
63
64#define STIMULUS_PER_OP 10
65#define INDENT " "
66
68 fuEntry_(fu), fuImpl_(NULL), fuArch_(NULL), msm_(NULL), inputPorts_(),
69 outputPorts_(), opcodePort_(), machine_(NULL), memSystem_(NULL) {
70}
71
73
74 if (msm_) {
75 delete(msm_);
76 }
77 if (machine_) {
78 delete(machine_);
79 }
80 if (memSystem_) {
81 delete(memSystem_);
82 }
83}
84
85
86void
99
100/**
101 * Create a machine architecture object model with the tested FU in it
102 */
103void
115
116
117void
119
120 // divide ports into input and output ports
121 for (int i = 0; i < fuArch_->architecture().portCount(); i++) {
122 const std::string portName = fuArch_->architecture().port(i)->name();
123 PortState& simulatedPort =
124 msm_->portState(portName, fuArch_->architecture().name());
125
127 opcodePort_ = portName;
128
129 if (dynamic_cast<OutputPortState*>(&simulatedPort)) {
130 outputPorts_.push_back(portName);
131 } else if (dynamic_cast<InputPortState*>(&simulatedPort)) {
132 inputPorts_.push_back(portName);
133 } else if (&simulatedPort == &NullPortState::instance()) {
134 InvalidData e(__FILE__, __LINE__, "ImplementationTester",
135 "Port not found in state");
136 throw e;
137 } else {
138 InvalidData e(__FILE__, __LINE__, "ImplementationTester",
139 "Port " + portName + " has unknown direction.");
140 throw e;
141 }
142 }
143}
144
145/**
146 * Creates component declaration, connection signals and connects FU component
147 * to testbench
148 */
149void
151
153 << INDENT << "for tested_fu_0 : fu_under_test use entity work.";
154 bindingStream() << fuImpl_->moduleName() << ";" << std::endl;
155
157 << INDENT << "component fu_under_test" << std::endl
158 << INDENT INDENT << "port(" << std::endl;
159
161 << INDENT << "tested_fu_0\t:\tfu_under_test " << std::endl
162 << INDENT INDENT << "port map (" << std::endl;
163
164 for (int i = 0; i < fuImpl_->architecturePortCount(); ++i) {
166 // this might not always work..
167 const int portWidth = fuArch_->architecture().port(i)->width();
168 const bool isInput =
172 << INDENT INDENT << port.name() << "\t: ";
174 << INDENT << "signal " << port.name() << "\t: ";
176 << INDENT INDENT INDENT << port.name() << " => " << port.name();
177
178 if (isInput)
179 declarationStream() << "in";
180 else
181 declarationStream() << "out";
182
184 << " std_logic_vector("
185 << portWidth - 1 << " downto 0);" << std::endl;
186
188 << "std_logic_vector("
189 << portWidth - 1 << " downto 0);" << std::endl;
190
191 if (isInput) {
193 << INDENT INDENT << port.loadPort()
194 << "\t: in std_logic;" << std::endl;
195
197 << INDENT << "signal " << port.loadPort()
198 << "\t: std_logic_vector(1-1 downto 0);" << std::endl;
199
201 << "," << std::endl
202 << INDENT INDENT INDENT << port.loadPort() << " => "
203 << port.loadPort() << "(0)";
204 }
205 if (i < fuImpl_->architecturePortCount() - 1) {
206 instantiationStream() << "," << std::endl;
207 } else {
208 if (fuImpl_->opcodePort() != "") {
210 << INDENT INDENT << fuImpl_->opcodePort() << "\t: "
211 << "in" << " std_logic_vector("
212 << fuImpl_->maxOpcodeWidth() - 1 << " downto 0);"
213 << std::endl;
215 << "," << std::endl
217 << fuImpl_->opcodePort()
218 << " => " << fuImpl_->opcodePort();
220 << INDENT << "signal " << fuImpl_->opcodePort() << "\t: ";
222 << "std_logic_vector("
223 << fuImpl_->maxOpcodeWidth() - 1
224 << " downto 0);" << std::endl;
225 }
226 }
227 }
229 << "," << std::endl
231 << fuImpl_->clkPort() << " => " << fuImpl_->clkPort()
232 << "," << std::endl
234 << fuImpl_->rstPort() << " => " << fuImpl_->rstPort()
235 << "," << std::endl
237 << fuImpl_->glockPort() << " => " << fuImpl_->glockPort()
238 << ");";
239
241 << INDENT INDENT << fuImpl_->glockPort() << "\t: in std_logic;"
242 << std::endl
243 << INDENT INDENT << fuImpl_->rstPort() << "\t: in std_logic;"
244 << std::endl
245 << INDENT INDENT << fuImpl_->clkPort() << "\t: in std_logic);"
246 << std::endl
247 << INDENT << "end component;"
248 << std::endl;
249
251 << INDENT << "signal " << fuImpl_->glockPort() << "\t: std_logic;"
252 << std::endl
253 << INDENT << "signal " << fuImpl_->rstPort() << "\t: std_logic;"
254 << std::endl
255 << INDENT << "signal " << fuImpl_->clkPort() << "\t: std_logic;"
256 << std::endl;
257}
258
259
260/**
261 * Creates input and output data tables
262 *
263 * Creates input and output data tables and control signals for the testbench.
264 * Every operation is tested STIMULUS_PER_OP times and command execution is
265 * pipelined. Only fully pipelined FUs are supported.
266 */
267void
269
271 assert(&simFU != &NullFUState::instance());
272
273 // stimulus for each port in each clock cycle
274 PortDataArray inputStimulus;
275
276 // stimulus for load ports. Notice that all load ports get the same
277 // signal!
278 // TODO: Exploit this when supporting non-fully pipelined operations
279 // (if latency(op N) > latency(op N+1) there needs to be no-load
280 // cycles when switching operations)
281 vector<uint32_t> loadStimulus;
282
283 // operations started in each clock cycle (names)
284 vector<string> startedOperations;
285
286 // expected output for each port in each clock cycle
287 PortDataArray outputs;
288
289 // initialize a random number generator for the stimuli
290 boost::uniform_int<> distribution(INT_MIN, INT_MAX);
291 boost::mt19937 rng;
292 rng.seed(time(NULL));
293 boost::variate_generator<boost::mt19937&, boost::uniform_int<> >
294 randomNumber(rng, distribution);
295
296 const int cyclesToSimulate = STIMULUS_PER_OP;
297 for (int opIndex = 0; opIndex < fuArch_->architecture().operationCount();
298 ++opIndex) {
299 const string operation =
300 fuArch_->architecture().operation(opIndex)->name();
301
302 for (int i = 0; i < cyclesToSimulate; i++) {
303 startedOperations.push_back(operation);
304
305 // generate stimulus for each port
306 for (std::size_t i = 0; i < inputPorts_.size(); ++i) {
307 const string portName = inputPorts_.at(i);
309 inputStimulus,
310 operation, portName, (uint32_t)randomNumber());
311 }
312
313 // load signal stimulus
314 uint32_t loadOnThisCycle = 1;
315 loadStimulus.push_back(loadOnThisCycle);
316
317 readValuesFromOutPorts(outputs);
318 // advance the simulation clock
319 simFU.endClock();
320 simFU.advanceClock();
321 }
322 }
323 // flush the pipeline
324 int lastOpIndex = fuArch_->architecture().operationCount()-1;
325 int pipelineClearCycles =
326 fuArch_->architecture().operation(lastOpIndex)->latency();
327 for (int i = 0; i < pipelineClearCycles; i++) {
328 if (fuArch_->architecture().operationCount() > 1) {
329 // insert dummy values for opcode port
330 const string operation =
331 fuArch_->architecture().operation(lastOpIndex)->name();
332 startedOperations.push_back(operation);
333 }
334 for (std::size_t j = 0; j < inputPorts_.size(); ++j) {
335 const string portName = inputPorts_.at(j);
336 // operation does not matter
337 const string operation =
338 fuArch_->architecture().operation(lastOpIndex)->name();
339 uint32_t inputStim = 0;
341 inputStimulus, operation, portName, inputStim);
342 }
343 // no load on these cycles
344 uint32_t loadStim = 0;
345 loadStimulus.push_back(loadStim);
346
347 readValuesFromOutPorts(outputs);
348 // advance the simulation clock
349 simFU.endClock();
350 simFU.advanceClock();
351 }
353 inputStimulus, loadStimulus, startedOperations, outputs);
354
355 int waitCycles =
356 fuArch_->architecture().operation(startedOperations.at(0))->latency();
357 // TODO: change this when supporting different pipelines
358 int opCount = fuArch_->architecture().operationCount();
359 int latencyOfLastOp =
360 fuArch_->architecture().operation(opCount-1)->latency();
361 int totalCycles =
362 STIMULUS_PER_OP * opCount + latencyOfLastOp;
363 writeTbConstants(totalCycles, waitCycles);
364}
365
366
367/**
368 * Writes the testbench main process code
369 */
370void
372
373 for (std::size_t i = 0; i < inputPorts_.size(); ++i) {
374 const std::string portName = inputPorts_.at(i);
375 const std::string hwDataPortName =
377 const std::string hwLoadPortName =
378 fuImpl_->
379 portImplementationByArchitectureName(portName).loadPort();
381 << INDENT INDENT
382 << hwDataPortName << " <= " << hwDataPortName << "_data("
383 << "current_cycle);" << std::endl
384 << INDENT INDENT << hwLoadPortName << " <= "
385 << hwLoadPortName << "_data(current_cycle);" << std::endl;
386 }
387
388 if (fuArch_->architecture().operationCount() > 1) {
389 tbCodeStream()
390 << INDENT INDENT
391 << fuImpl_->opcodePort()
392 << " <= " << fuImpl_->opcodePort() << "_data(current_cycle); ";
393 }
394
396 << std::endl << std::endl
397 << INDENT INDENT
398 << "if current_cycle >= IGNORE_OUTPUT_COUNT then" << std::endl;
399
400 for (std::size_t i = 0; i < outputPorts_.size(); ++i) {
401 const std::string portName = outputPorts_.at(i);
402 const std::string hwDataPortName =
404 tbCodeStream()
406 << "assert " << hwDataPortName << " = " << hwDataPortName
407 << "_data(current_cycle)" << std::endl
409 << "report lf & \"TCE Assert: Verification failed at cycle \" "
410 << "& integer'image(current_cycle) & \" for output " << i << "\"" << std::endl
411 << INDENT INDENT INDENT INDENT <<"& \" actual: \" "
412 << "& to_hstring(" << hwDataPortName << ")"
413 << std::endl
414 << INDENT INDENT INDENT INDENT << "& "
415 << "\" expected: \" & to_hstring(" << hwDataPortName
416 << "_data(current_cycle)) severity error;"
417 << std::endl << std::endl;
418 }
419
421 << INDENT INDENT
422 << "end if;" << std::endl;
423}
424
425/**
426 * Writes input data to the given port and saves the input value to an array
427 *
428 * @param inputs PortDataArray containing input ports
429 * @param operation Name of the triggered operation
430 * @param portName Name of the port where data is written to
431 * @param stimulus Input data
432 */
433void
435 PortDataArray& inputs,
436 const std::string& operation,
437 const std::string& portName, uint32_t stimulus) {
438
439 string operationString = "";
440 PortState* simulatedPort = NULL;
441 bool isOpcodePort = false;
442 // fetch the virtual opcode setting port for the triggered operation
443 if (portName == opcodePort_) {
444 operationString =
446 std::string(".") + operation);
447 isOpcodePort = true;
448 }
449
450 simulatedPort = &msm_->portState(
451 portName + operationString,
453
454 const int inputWidth = simulatedPort->value().width();
455 SimValue value(inputWidth);
456
457 int wantedBits = inputWidth;
458 if (isShiftOrRotOp(operation) && isOpcodePort
459 && inputWidth > 5) {
460 // log2(32) = 5 bits needed to express max shift
461 wantedBits = 5;
462 }
463 stimulus = truncateStimulus(stimulus, wantedBits);
464
465 inputs[portName].push_back(stimulus);
466 value = stimulus;
467 simulatedPort->setValue(value);
468}
469
470/**
471 * Reads data from output ports and saves the values to an array
472 *
473 * @param outputs PortDataArray containing the output ports
474 */
475void
477
478 for (std::size_t i = 0; i < outputPorts_.size(); ++i) {
479 const string portName = outputPorts_.at(i);
480
481 PortState* simulatedPort = NULL;
482 simulatedPort = &msm_->portState(
483 portName, fuArch_->architecture().name());
484
485 outputs[portName].push_back(
486 simulatedPort->value().intValue());
487 }
488}
489
490/**
491 * Writes input, output and control signal data to output streams
492 *
493 * @param inputStimulus Input port data
494 * @param loadStimulus Load port data
495 * @param operations Triggered operations
496 * @param outputStimulus Output port data
497 */
498void
500 PortDataArray& inputStimulus,
501 std::vector<uint32_t>& loadStimulus,
502 std::vector<std::string>& operations, PortDataArray& outputStimulus) {
503
504 // input array(s)
505 for (PortDataArray::iterator i = inputStimulus.begin();
506 i != inputStimulus.end(); i++) {
509 const string hwPortName = port->name();
510 const int hwPortWidth =
511 fuArch_->architecture().port((*i).first)->width();
512 vector<uint32_t> data = i->second;
513 writeStimulusArray(inputArrayStream(), data, hwPortName, hwPortWidth);
514
515 const string loadPortName = port->loadPort();
516 if (!loadPortName.empty()) {
517 int loadPortWidth = 1;
518 writeStimulusArray(loadArrayStream(), loadStimulus, loadPortName,
519 loadPortWidth);
520 }
521 }
522
523 // opcode arrays
524 // Special case so we can print the operation names to the testbench
525 const int operationsInFU = fuArch_->architecture().operationCount();
526 if (operationsInFU > 1) {
527 const string hwPortName = fuImpl_->opcodePort();
528 const int hwPortWidth = fuImpl_->maxOpcodeWidth();
530 << INDENT INDENT
531 << "type " << hwPortName << "_data_array is array "
532 << "(natural range <>) of" << std::endl << INDENT INDENT INDENT
533 << "std_logic_vector(" << hwPortWidth - 1 << " downto 0);"
534 << std::endl << std::endl
535 << INDENT INDENT
536 << "constant "<< hwPortName << "_data : " << hwPortName
537 << "_data_array :=" << std::endl;
538
539 for (std::size_t i = 0; i < operations.size(); ++i) {
540 uint32_t input = fuImpl_->opcode(operations.at(i));
541 std::string inputAsBinaryLiteral =
542 Conversion::toBinary(input, hwPortWidth);
544 if (i == 0) {
545 opcodeArrayStream() << "(";
546 } else {
547 opcodeArrayStream() << " ";
548 }
549 opcodeArrayStream() << "\"" << inputAsBinaryLiteral << "\"";
550 if (i == operations.size() - 1) {
551 opcodeArrayStream() << ");";
552 } else {
553 opcodeArrayStream() << ",";
554 }
556 << "\t -- @" << i << " = " << input << " ("
557 << operations.at(i) << ")" << std::endl;
558 }
559 }
560
561 // output arrays
562 for (PortDataArray::iterator i = outputStimulus.begin();
563 i != outputStimulus.end(); ++i) {
564 const std::string hwPortName =
566 const int hwPortWidth =
567 fuArch_->architecture().port((*i).first)->width();
568 vector<uint32_t> data = i->second;
569 writeStimulusArray(outputArrayStream(), data, hwPortName,
570 hwPortWidth);
571 }
572}
573
574
575/**
576 * Test if operation is shift or rotation operation
577 *
578 * @param operation Name of the operation
579 * @return Is operation shift or rotate
580 */
581bool
582FUTestbenchGenerator::isShiftOrRotOp(const std::string& operation) const {
583
584 string opName = StringTools::stringToLower(operation);
585
586 if (opName == "shl" || opName == "shr" || opName == "shru") {
587 return true;
588 } else if (opName == "rotl" || opName == "rotr") {
589 return true;
590 }
591 return false;
592}
593
594/**
595 * Truncates shift operand to log2(32) bits
596 *
597 * @param operand Operand to be truncated
598 * @return Truncated operand
599 */
600uint32_t
601FUTestbenchGenerator::truncateStimulus(uint32_t operand, int nBits) const {
602
603 if (nBits < 0) {
604 InvalidData exc(__FILE__, __LINE__, "ImplementationTester",
605 "Negative amount f wanted bits");
606 throw exc;
607 }
608
609 unsigned int dataWidth = 32;
610 uint32_t truncated =
611 (operand << (dataWidth - nBits) >> (dataWidth - nBits));
612 return truncated;
613}
#define assert(condition)
#define STIMULUS_PER_OP
#define INDENT
static bool containsValue(const ContainerType &aContainer, const ElementType &aKey)
static std::string toBinary(unsigned int source, unsigned int stringWidth=0)
virtual void advanceClock()
Definition FUState.cc:152
virtual void endClock()
Definition FUState.cc:122
virtual void generateTestbench(std::ofstream &file)
uint32_t truncateStimulus(uint32_t operand, int nBits) const
void writeInputPortStimulus(PortDataArray &inputs, const std::string &operation, const std::string &portName, uint32_t stimulus)
HDB::FUArchitecture * fuArch_
TTAMachine::Machine * machine_
std::vector< std::string > outputPorts_
bool isShiftOrRotOp(const std::string &operation) const
HDB::FUImplementation * fuImpl_
void createStimulusArrays(PortDataArray &inputStimulus, std::vector< uint32_t > &loadStimulus, std::vector< std::string > &operations, PortDataArray &outputStimulus)
std::vector< std::string > inputPorts_
void readValuesFromOutPorts(PortDataArray &outputs)
FUTestbenchGenerator(HDB::FUEntry *fu)
TTAMachine::FunctionUnit & architecture() const
FUImplementation & implementation() const
Definition FUEntry.cc:86
FUArchitecture & architecture() const
Definition FUEntry.cc:129
FUPortImplementation & architecturePort(int index) const
FUPortImplementation & portImplementationByArchitectureName(const std::string &architectureName) const
std::string opcodePort() const
int opcode(const std::string &operation) const
std::string architecturePort() const
std::string loadPort() const
MachineState * build(const TTAMachine::Machine &machine, MemorySystem &memSys)
PortState & portState(const std::string &portName, const std::string &fuName)
FUState & fuState(const std::string &name)
static NullFUState & instance()
Definition FUState.cc:392
static NullPortState & instance()
Definition PortState.cc:96
virtual void setValue(const SimValue &value)
virtual const SimValue & value() const
int intValue() const
Definition SimValue.cc:895
int width() const
Definition SimValue.cc:103
static std::string stringToLower(const std::string &source)
virtual int width() const
virtual bool isOpcodeSetting() const =0
virtual TCEString name() const
virtual HWOperation * operation(const std::string &name) const
virtual int operationCount() const
virtual BaseFUPort * port(const std::string &name) const
const std::string & name() const
virtual void addFunctionUnit(FunctionUnit &unit)
Definition Machine.cc:202
virtual std::string name() const
Definition Port.cc:141
std::ostringstream & opcodeArrayStream()
std::map< std::string, std::vector< uint32_t > > PortDataArray
std::ostringstream & inputArrayStream()
void writeTbConstants(int totalCycles, int outputIgnoreCycles)
std::ostringstream & tbCodeStream()
std::ostringstream & outputArrayStream()
std::ostringstream & instantiationStream()
std::ostringstream & loadArrayStream()
std::ostringstream & bindingStream()
std::ostringstream & declarationStream()
std::ostringstream & signalStream()
void writeTestbench(std::ofstream &file, HDB::HWBlockImplementation *impl)
virtual void writeStimulusArray(std::ostringstream &stream, std::vector< uint32_t > &dataArray, std::string portName, int portWidth)