OpenASIP 2.2
Loading...
Searching...
No Matches
ProGeTestBenchGenerator.cc
Go to the documentation of this file.
1/*
2 Copyright (c) 2002-2011 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 ProGeTestBenchGenerator.cc
26 *
27 * Implementation of ProGeTestBenchGenerator class.
28 *
29 * @author Esa Määttä 2007 (esa.maatta-no.spam-tut.fi)
30 * @author Pekka Jääskeläinen 2011
31 * @note rating: red
32 */
33
34#include <iostream>
35#include <fstream>
36#include <string>
37#include <map>
38#include <vector>
39#include <list>
40#include <cmath> // for log
41
42#include "CompilerWarnings.hh"
43IGNORE_CLANG_WARNING("-Wkeyword-macro")
44#include <boost/regex.hpp>
46
47#include "ProGeTypes.hh"
50#include "FileSystem.hh"
51#include "Conversion.hh"
52#include "Machine.hh"
53
54#include "HDBManager.hh"
55#include "HWOperation.hh"
56#include "FUPort.hh"
57#include "HDBRegistry.hh"
58#include "FUEntry.hh"
59#include "FUArchitecture.hh"
60#include "FUImplementation.hh"
64#include "FUExternalPort.hh"
65#include "Exception.hh"
66#include "StringTools.hh"
67
68
69using namespace IDF;
70using namespace HDB;
71using namespace TTAMachine;
72using namespace ProGe;
73
74using std::string;
75using std::endl;
76
77using namespace TTAMachine;
78
79const string ADDR_WIDTH = "addrw";
80
81/**
82 * The constructor.
83 *
84 */
88
89
90/**
91 * The destructor.
92 */
96
97/**
98 * Generates testbench to given destination directory.
99 *
100 * At the moment multiple address spaces for FUs are not supported. Only one
101 * data memory and instruction memory are usable. There is no code for
102 * generating new memory components, this is a todo item for later.
103 * There is also additional limit that at most two function units can use the
104 * one address space. Most of the TODO comments mark the places where code
105 * needs to be changed to accomodate the removal of these limitations.
106 *
107 * @param mach Machine for which test bench is generated
108 * @param implementation Implementation of the machine
109 * @param dstDirectory Destination directory where test bench is generated
110 * @param progeOutDir Processor generators output directory
111 *
112 * @exception IOException Problems in handling files.
113 * @exception OutOfRange Too many FUs using same address space or same FU uses
114 * more than one address space.
115 * @exception InvalidName
116 * @exception InvalidData FU was not found from HDB.
117 */
118void
120 const ProGe::HDL language, const TTAMachine::Machine& mach,
122 const std::string& dstDirectory, const std::string& progeOutDir,
123 const std::string& entityStr) {
124 entityStr_ = entityStr;
125 language_ = language;
126 // map to store FUs that use address spaces
127 std::map<string, std::vector<FunctionUnit*> > ASFUs;
128
129 // for now limit the number of function units that use certain address
130 // space
131 const unsigned int FUSPERAS = 2;
132 // get all address spaces and function units that use them
133 // every function unit can use different address space
135 for (int i = 0; i < FUNav.count(); ++i) {
136 FunctionUnit* FU = FUNav.item(i);
137
138 if (FU->hasAddressSpace()) {
139 AddressSpace* AS = FU->addressSpace();
140
141 std::map<string, std::vector<FunctionUnit*> >::iterator it
142 = ASFUs.find(AS->name());
143 if (it != ASFUs.end()) {
144 // the number of FUs that can use same address space is
145 // limited for now
146 if (it->second.size() < FUSPERAS) {
147 it->second.push_back(FU);
148 } else {
149 OutOfRange error(
150 __FILE__, __LINE__, __func__,
151 "More than two FUs use same address space.\n");
152 throw error;
153 }
154 } else {
155 std::vector<FunctionUnit*> FUList;
156 FUList.push_back(FU);
157 ASFUs.insert(make_pair(AS->name(), FUList));
158 }
159 }
160 }
161
162 if (ASFUs.size() > 1) {
163 // only one address space for now
164 string eMsg = "More than one address space used by FUs.";
165 OutOfRange error(__FILE__, __LINE__, __func__, eMsg);
166 throw error;
167 }
168
169 // copy test bench files used as a template
170 copyTestBenchFiles(dstDirectory);
171
172 // for every address space a memory component should be created to
173 // the test bench vhdl files
174 // TODO: for now it's quite unclear how memory components should be
175 // created in vhdl and is there some clean and dynamic way to do
176 // it. So for now there is no support for multiple address spaces.
177 // get this name from address space name when creating dynamically memory
178 // units
179 const string MEMORYNAME = "dmem";
180 std::map<string, std::vector<FunctionUnit*> >::iterator it
181 = ASFUs.begin();
182 std::map<string, std::vector<FunctionUnit*> >::iterator it_secLast
183 = (ASFUs.empty()) ? ASFUs.end() : --(ASFUs.end());
184 string LSUMap = "";
185 while (it != ASFUs.end()) {
186 // go through all FUs that have been mapped to address spaces
187 HDB::FUImplementation* fuImplementation = NULL;
188 for (unsigned int i = 0; i < it->second.size(); ++i) {
189 FunctionUnit* FU = it->second.at(i);
190
191 // get FUs implementation from hdb
194
195 FUEntry* entry = NULL;
196 try {
198 impl.hdbFile());
199 entry = manager.fuByEntryID(impl.id());
200 } catch (const KeyNotFound& e) {
201 throw InvalidData(__FILE__, __LINE__, __func__,
202 e.errorMessage());
203 }
204
205 // external ports can be found from implementation (hdb)
206 fuImplementation = &entry->implementation();
207 for (int j = 0, count = fuImplementation->externalPortCount();
208 j < count; j++) {
209
210 FUExternalPort& ep = fuImplementation->externalPort(j);
211 LSUMap.append(getSignalMapping(FU->name(), ep.name(),
212 (ep.widthFormula() == "1"), MEMORYNAME,
213 ((i > 0) ? "b" : "a"))); // only two FUs for same AS
214
215 if (it == it_secLast && (i+1) == it->second.size() &&
216 (j + 1) == count) {
217 // last mapping
218 LSUMap.append(");\n");
219 } else {
220 LSUMap.append(",\n");
221 }
222 }
223
224 }
225
226 // get data width and address width of memory unit that fu is
227 // connected to, only once for every address space
228 string dataWidth;
229 string addrWidth;
230 string dmemImageFilename("dmem_");
231 if (ASFUs.size() == 1) {
232 dmemImageFilename +=
233 ASFUs.begin()->second.at(0)->addressSpace()->name();
234 dmemImageFilename += "_";
235 }
236 dmemImageFilename += "init.img";
237 for (int p = 0, count = fuImplementation->parameterCount();
238 p < count; ++p) {
239
240 FUImplementation::Parameter param = fuImplementation->parameter(p);
241 if (string::npos != param.name.find("dataw")
242 && param.type == "integer") {
243
244 if (param.value.length() < 1) {
245 // if width isn't stored read it from port
246 FunctionUnit* FU = it->second.at(0);
247 TTAMachine::HWOperation* hwop = FU->operation(0);
248 dataWidth = Conversion::toString(hwop->port(2)->width());
249 } else {
250 dataWidth = param.value;
251 }
252 } else if (string::npos != param.name.find(ADDR_WIDTH)
253 && param.type == "integer") {
254 // calculate the internal address width from the address space
255 FunctionUnit* FU = it->second.at(0);
256 AddressSpace* AS = FU->addressSpace();
257 string internalAddrWidth = Conversion::toString(
258 static_cast<int>(ceil(log(AS->end()) / log(2))));
259
260 // locate the external address port and its width formula
261 string widthFormula;
262 for (int i = 0; i < fuImplementation->externalPortCount();
263 i++) {
264 FUExternalPort& ep = fuImplementation->externalPort(i);
265 if (ep.widthFormula().find(ADDR_WIDTH) != string::npos) {
266 widthFormula = ep.widthFormula();
267 break;
268 }
269 }
270
271 if (widthFormula.empty()) {
272 addrWidth = internalAddrWidth;
273 } else {
275 widthFormula, ADDR_WIDTH, internalAddrWidth);
276 }
277 }
278 }
279 // TODO: don't create whole file here just add memory widths and
280 // the init file entry
282 dstDirectory, dmemImageFilename, dataWidth, addrWidth);
283 ++it;
284 }
285
286 if (ASFUs.empty()) {
287 createTBConstFile(dstDirectory);
288 }
289
290 // the beginning of the core FU (load store unit) mappings
291 string LSUMapConst;
292 if (language == VHDL) {
293 LSUMapConst =
294 "port map (\n"
295 "clk => clk,\n"
296 "rstx => rst_x,\n"
297 "busy => '0',\n"
298 "imem_en_x => imem_en_x,\n"
299 "imem_addr => imem_addr,\n"
300 "imem_data => imem_data,\n"
301 "locked => locked";
302 // Add external debugger ports, if needed
303 if (implementation.icDecoderParameterValue("debugger") ==
304 "external") {
305 LSUMapConst.append(",\n"
306 "db_pc_start => (others => '0'),\n"
307 "db_tta_nreset => '1',\n"
308 "db_lockrq => '0'");
309 }
310 } else {
311 LSUMapConst=
312 ".clk (clk),\n"
313 ".rstx (rst_x),\n"
314 ".busy (1'b0),\n"
315 ".imem_en_x (imem_en_x),\n"
316 ".imem_addr (imem_addr),\n"
317 ".imem_data (imem_data)";
318 }
319
320 if (LSUMap.length() < 1) {
321 LSUMapConst.append(");\n");
322 } else {
323 LSUMapConst.append(",\n");
324 // append generated mappings
325 LSUMapConst.append(LSUMap);
326 }
327
328 // read toplevel.vhdl from proge output dir for proc_arch.vhdl
329 string toplevel = progeOutDir + FileSystem::DIRECTORY_SEPARATOR +
330 ((language == VHDL) ? "vhdl" : "verilog") +
332 ((language == VHDL) ? ".vhdl" : ".v");
333
334 createProcArchVhdl(dstDirectory, toplevel, LSUMapConst);
335}
336
337/**
338 * Creates a new proc_arch vhdl file from a template file.
339 *
340 * Writes a new toplevel and core signal mapping.
341 *
342 * @param dstDirectory Directory where new proc_arch file is created.
343 * @param topLevelVhdl Toplevel vhdl file where toplevel is read for writing
344 * to the proc_arch file.
345 * @param signalMappings Core signal mappings as a string to be writen to
346 * the proc_arch file.
347 *
348 * @exception IOException Problems in handling files.
349 */
350void
352 const std::string& dstDirectory, const std::string& topLevelVhdl,
353 const std::string& signalMappings) {
354 if (!FileSystem::fileIsReadable(topLevelVhdl)) {
355 string eMsg = "File was not readable: " + topLevelVhdl;
356 IOException error(__FILE__, __LINE__, __func__, eMsg);
357 throw error;
358 }
359
360 string startRE,endRE;
361 if(language_==VHDL){
362 startRE = std::string(".*entity.") + entityStr_ + ".is.*";
363 endRE = std::string(".*end.") + entityStr_ + ";.*";
364 } else {
365 startRE = std::string(".*module.") + entityStr_;
366 endRE = std::string(".*endmodule.*");
367 }
368
369 string block = "";
370 bool ok =
372 topLevelVhdl, startRE, endRE, block, false);
373
374 if (!ok || block == "")
375 throw IOException(__FILE__, __LINE__, __func__,
376 TCEString(
377 "Could not read processor entity from ") +
378 topLevelVhdl);
379
380
381 TCEString sourceFile =
384 ((language_==VHDL)?"proc_arch.vhdl.tmpl":"proc_arch.v.tmpl");
385
386 // change proc_arch.vhdl
387 string procArch = dstDirectory + FileSystem::DIRECTORY_SEPARATOR +
388 ((language_==VHDL)?"proc_arch.vhdl":"proc_arch.v");
389
392 inst.instantiateTemplateFile(sourceFile, procArch);
393
394 // check if readable and writable
395 if(!FileSystem::fileIsReadable(procArch) ||
396 !FileSystem::fileIsWritable(procArch)) {
397
398 string eMsg = "File was not readable: " + procArch;
399 IOException error(__FILE__, __LINE__, __func__, eMsg);
400 throw error;
401 }
402
403 if(language_==VHDL){
404 startRE = std::string(".*component.") + entityStr_ + ".*";
405 endRE = std::string(".*end.component;.*");
406 if (!FileSystem::appendReplaceFile(procArch, startRE, block, endRE,
407 false)) {
408
409 string eMsg = "Could not write toplevel to: " + procArch;
410 IOException error(__FILE__, __LINE__, __func__, eMsg);
411 throw error;
412 }
413 }
414
415 if(language_==VHDL){
416 startRE = std::string(".*core.:.") + entityStr_ + ".*";
417 endRE = ".*datamem.:.synch_dualport_sram.*";
418 }else{
419 startRE = entityStr_ + std::string(".*core.*");
420 endRE = ".*synch_dualport_sram.*";
421 }
422 if (!FileSystem::appendReplaceFile(procArch, startRE, signalMappings,
423 endRE, false)) {
424
425 string eMsg = "Could not write core to: " + procArch;
426 IOException error(__FILE__, __LINE__, __func__, eMsg);
427 throw error;
428 }
429}
430
431/**
432 * Creates core signal mapping for FUs external port -> data memory.
433 *
434 * Maps signals according to some defined naming "standard".
435 *
436 * @param fuName Function unit name.
437 * @param epName External port name.
438 * @param widthIsOne True if port width is one, false otherwise.
439 * @param memoryName Memory name.
440 * @param memoryLine Memory port that is used (for dual port memory a or b).
441 *
442 * @return Signal mapping as a string.
443 * @exception InvalidName External port name was invalid (not matching).
444 */
445std::string
447 const std::string& fuName, const std::string& epName, bool widthIsOne,
448 const std::string& memoryName, const std::string& memoryLine) {
449 const string sep = "_"; // separator between signal name elements
450
451 // create fu signal name
452 string fuSignalName("fu" + sep);
453 if(language_==VHDL)
454 fuSignalName.append(fuName + sep + epName + ((widthIsOne) ? "(0)" : ""));
455 else
456 fuSignalName.append(fuName + sep + epName);
457
458 // create memory signal name
459 string memSignalName(memoryName + sep);
460
461 if (epName == "data_in") {
462 memSignalName.append("q" + sep + memoryLine);
463 } else
464 if (epName == "data_out") {
465 memSignalName.append("d" + sep + memoryLine);
466 } else
467 if (epName == "addr") {
468 memSignalName.append(epName + sep + memoryLine);
469 } else
470 if (epName == "mem_en_x") {
471 memSignalName.append("en" + sep + memoryLine + sep + "x");
472 } else
473 if (epName == "wr_en_x") {
474 memSignalName.append("wr" + sep + memoryLine + sep + "x");
475 } else
476 if (epName == "wr_mask_x") {
477 memSignalName.append("bit" + sep + "wr" + sep + memoryLine + sep
478 + "x");
479 } else {
480 string eMsg = "External port name didn't match any: " + epName;
481 InvalidName error(__FILE__, __LINE__, __func__, eMsg);
482 throw error;
483 }
484
485 if(language_==VHDL)
486 return string(fuSignalName + " => " + memSignalName);
487
488 return string("."+fuSignalName + "(" + memSignalName + ")");
489}
490
491/**
492 * Creates test bench constants package vhdl file.
493 *
494 * @param dstDirectory Directory where the file is created.
495 * @param dataWidth Memory data width.
496 * @param addrWidth Memory address width.
497 */
498void
500 std::string dstDirectory, const std::string& dmemImage,
501 const string& dataWidth, const string& addrWidth) {
502 string dstFile = dstDirectory + FileSystem::DIRECTORY_SEPARATOR +
503 ((language_ == VHDL) ? "testbench_constants_pkg.vhdl"
504 : "testbench_constants_pkg.vh");
505
506 createFile(dstFile);
507
508 std::ofstream stream(dstFile.c_str(), std::ofstream::out);
509 if(language_==VHDL){
510 stream << "package testbench_constants is" << endl
511 << "-- width of the data memory" << endl
512 << "constant DMEMDATAWIDTH : positive := "
513 << ((dataWidth.empty()) ? "1" : dataWidth) << ";" << endl
514
515 << "-- address width of the data memory" << endl
516 << "constant DMEMADDRWIDTH : positive := "
517 << ((addrWidth.empty()) ? "1" : addrWidth) << ";" << endl
518
519 << "-- simulation run time" << endl
520 << "constant RUNTIME : time := 5234 * 10 ns;" << endl
521
522 << "-- memory init files" << endl
523 << "constant DMEM_INIT_FILE : string := "
524 << ((dataWidth.empty())
525 ? "\"\";"
527 dmemImage + "\";")
528 << endl
529
530 << "constant IMEM_INIT_FILE : string := "
532 "imem_init.img\";"
533 << endl
534 << "end testbench_constants;" << endl;
535 } else {
536 stream << "// width of the data memory" << endl
537 << "parameter DMEMDATAWIDTH = "
538 << ((dataWidth.empty()) ? "1" : dataWidth) << "," << endl
539
540 << "// address width of the data memory" << endl
541 << "parameter DMEMADDRWIDTH = "
542 << ((addrWidth.empty()) ? "1" : addrWidth) << "," << endl
543
544 << "// simulation run time" << endl
545 << "parameter RUNTIME = `SIMTIME,// ns" << endl
546
547 << "// memory init files" << endl
548 << "parameter DMEM_INIT_FILE = "
549 << ((dataWidth.empty())
550 ? "\"\","
552 dmemImage + "\",")
553 << endl
554
555 << "parameter IMEM_INIT_FILE = "
556 << ((addrWidth.empty())
557 ? "\"\""
559 "imem_init.img\"")
560 << endl;
561 }
562 stream.close();
563}
564
565/**
566 * Copies general testbench files to given destination directory.
567 *
568 * @param dstDirectory Destination directory for test bench files.
569 */
570void
571ProGeTestBenchGenerator::copyTestBenchFiles(const std::string& dstDirectory) {
572 // create destination directory for the testbench
573 if(!FileSystem::createDirectory(dstDirectory) &&
574 !FileSystem::fileIsDirectory(dstDirectory)) {
575 return;
576 }
577
578 const std::string DS = FileSystem::DIRECTORY_SEPARATOR;
579 // copy testbench base files to dstDirectory
580 string sourceDir = Environment::dataDirPath("ProGe");
581 sourceDir = sourceDir + DS + "tb";
582 std::list<string> foundSourceFiles;
583
584 string vhdlRegex = ((language_==VHDL)?".*\\.vhdl$":".*\\.v*$");
585 FileSystem::findFromDirectory(vhdlRegex, sourceDir, foundSourceFiles);
586 std::list<string>::iterator it = foundSourceFiles.begin();
587 while (it != foundSourceFiles.end()) {
588 FileSystem::copy(*it, dstDirectory);
589 it++;
590 }
591
594
596 sourceDir + DS +
597 ((language_==VHDL)?
598 "legacy_testbench.vhdl.tmpl":"legacy_testbench.v.tmpl"),
599 dstDirectory + DS +
600 ((language_==VHDL)?"testbench.vhdl":"testbench.v"));
601
602 if(language_==VHDL)
604 sourceDir + DS + "proc_ent.vhdl.tmpl",
605 dstDirectory + DS + "proc_ent.vhdl");
606
607}
608
609/**
610 * Creates a file.
611 *
612 * @param Name of the script file to be created.
613 * @exception IOException Couldn't create the file.
614 */
615void
616ProGeTestBenchGenerator::createFile(const std::string& fileName) {
618 bool isCreated = FileSystem::createFile(fileName);
619 if (!isCreated) {
620 string errorMsg = "Unable to create file " + fileName;
621 throw IOException(__FILE__, __LINE__, __func__, errorMsg);
622 }
623}
#define __func__
#define POP_CLANG_DIAGS
#define IGNORE_CLANG_WARNING(X)
IDF::MachineImplementation * implementation
the implementation definition of the estimated processor
const string FU
#define DS
const string ADDR_WIDTH
static std::string toString(const T &source)
static std::string dataDirPath(const std::string &prog)
std::string errorMessage() const
Definition Exception.cc:123
static bool createFile(const std::string &file)
static bool readBlockFromFile(const std::string &sourceFile, const std::string &blockStartRE, const std::string &blockEndRE, std::string &readBlock, const bool includeMatchingLines=true)
static bool appendReplaceFile(const std::string &targetFile, const std::string &ARStartRE, const std::string &writeToFile, const std::string &AREndRE="", const bool discardBlockBorder="true")
static bool fileIsReadable(const std::string fileName)
static bool createDirectory(const std::string &path)
static bool removeFileOrDirectory(const std::string &path)
static const std::string DIRECTORY_SEPARATOR
static bool findFromDirectory(const std::string &regex, const std::string &directory, STLCONT &found)
static void copy(const std::string &source, const std::string &target)
static bool fileIsDirectory(const std::string fileName)
static bool fileIsWritable(const std::string fileName)
std::string widthFormula() const
std::string name() const
FUImplementation & implementation() const
Definition FUEntry.cc:86
Parameter parameter(int index) const
FUExternalPort & externalPort(int index) const
FUEntry * fuByEntryID(RowID id) const
static HDBRegistry & instance()
CachedHDBManager & hdb(const std::string fileName)
void instantiateTemplateFile(const std::string &templateFile, const std::string &dstFile)
void setEntityString(const TCEString &entityStr)
FUImplementationLocation & fuImplementation(const std::string &fu) const
std::string icDecoderParameterValue(const std::string &name) const
std::string getSignalMapping(const std::string &fuName, const std::string &epName, bool widthIsOne, const std::string &memoryName, const std::string &memoryLine)
void copyTestBenchFiles(const std::string &dstDirectory)
void createTBConstFile(std::string dstDirectory, const std::string &dmemImage="dmem_init.img", const std::string &dataWidth="", const std::string &addrWidth="")
void createProcArchVhdl(const std::string &dstDirectory, const std::string &topLevelVhdl, const std::string &signalMappings)
void generate(const ProGe::HDL language, const TTAMachine::Machine &mach, const IDF::MachineImplementation &implementation, const std::string &dstDirectory, const std::string &progeOutDir, const std::string &entityStr="tta0")
void createFile(const std::string &fileName)
static std::string replaceAllOccurrences(const std::string &source, const std::string &occurrence, const std::string &newString)
virtual ULongWord end() const
virtual int width() const
virtual TCEString name() const
virtual FUPort * port(int operand) const
ComponentType * item(int index) const
virtual FunctionUnitNavigator functionUnitNavigator() const
Definition Machine.cc:380
Definition FUGen.hh:54
HDL
HDLs supported by ProGe.
Definition ProGeTypes.hh:40
@ VHDL
VHDL.
Definition ProGeTypes.hh:41
std::string value
Value of the parameter.
Definition HDBTypes.hh:49
std::string type
Type of the parameter.
Definition HDBTypes.hh:48
std::string name
Name of the parameter.
Definition HDBTypes.hh:47