OpenASIP 2.2
Loading...
Searching...
No Matches
CompiledSimCodeGenerator.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 CompiledSimCodeGenerator.cc
26 *
27 * Definition of CompiledSimCodeGenerator class.
28 *
29 * @author Viljami Korhonen 2007 (viljami.korhonen-no.spam-tut.fi)
30 * @author Pekka Jääskeläinen 2009-2010 (pekka.jaaskelainen-no.spam-tut.fi)
31 * @note rating: red
32 */
33
34#include <iostream>
35#include <fstream>
36#include <string>
37#include <sstream>
38#include <vector>
39#include <cstdlib>
40
41#include "SimulatorFrontend.hh"
44#include "Machine.hh"
45#include "FunctionUnit.hh"
46#include "FUPort.hh"
47#include "PipelineElement.hh"
48#include "Program.hh"
49#include "Procedure.hh"
50#include "Instruction.hh"
52#include "NullInstruction.hh"
53#include "NullRegisterFile.hh"
54#include "Move.hh"
55#include "Guard.hh"
56#include "MoveGuard.hh"
57#include "Terminal.hh"
58#include "TerminalImmediate.hh"
59#include "TerminalFUPort.hh"
60#include "Immediate.hh"
61#include "Operation.hh"
62#include "OperationContext.hh"
64#include "OperationPool.hh"
65#include "HWOperation.hh"
66#include "ControlUnit.hh"
68#include "FileSystem.hh"
69#include "CodeLabel.hh"
70#include "DataLabel.hh"
71#include "ControlFlowGraph.hh"
72#include "tce_config.h"
73#include "BaseType.hh"
74#include "MachineInfo.hh"
76#include "CompiledSimMove.hh"
79#include "MapTools.hh"
80#include "TCEString.hh"
81
82using namespace TTAMachine;
83using namespace TTAProgram;
84using std::string;
85using std::fstream;
86using std::endl;
87using std::vector;
88
89namespace {
90
91enum class AccessMode : unsigned char { read, write };
92enum class ExtensionMode : unsigned char { sign, zero };
93enum class MAUOrder : unsigned char { littleEndian, bigEndian };
94
95struct MemoryOperationDescription {
96 AccessMode accessMode;
97 unsigned mauCount;
98 ExtensionMode extensionMode; // relevant only for load ops
99 MAUOrder mauOrder;
100
101};
102
103// Since DirectAccessMemory's advanceClock() method is empty and is not
104// called, memory operations with latency more than one are not supported
105// in compiled simulation.
106// However, these operations are exception and uses other methods for
107// simulating latency.
108std::map<TCEString, MemoryOperationDescription, TCEString::ICLess>
109supportedMemoryOps{
110 {"ldq",
111 {AccessMode::read, 1, ExtensionMode::sign, MAUOrder::bigEndian}},
112 {"ldqu",
113 {AccessMode::read, 1, ExtensionMode::zero, MAUOrder::bigEndian}},
114 {"ldh",
115 {AccessMode::read, 2, ExtensionMode::sign, MAUOrder::bigEndian}},
116 {"ldhu",
117 {AccessMode::read, 2, ExtensionMode::zero, MAUOrder::bigEndian}},
118 {"ldw",
119 {AccessMode::read, 4, ExtensionMode::sign, MAUOrder::bigEndian}},
120 {"ld8",
121 {AccessMode::read, 1, ExtensionMode::sign, MAUOrder::littleEndian}},
122 {"ldu8",
123 {AccessMode::read, 1, ExtensionMode::zero, MAUOrder::littleEndian}},
124 {"ld16",
125 {AccessMode::read, 2, ExtensionMode::sign, MAUOrder::littleEndian}},
126 {"ldu16",
127 {AccessMode::read, 2, ExtensionMode::zero, MAUOrder::littleEndian}},
128 {"ld32",
129 {AccessMode::read, 4, ExtensionMode::sign, MAUOrder::littleEndian}},
130
131 {"st8",
132 {AccessMode::write, 1, ExtensionMode::zero, MAUOrder::littleEndian}},
133 {"st16",
134 {AccessMode::write, 2, ExtensionMode::zero, MAUOrder::littleEndian}},
135 {"st32",
136 {AccessMode::write, 4, ExtensionMode::zero, MAUOrder::littleEndian}},
137 {"stq",
138 {AccessMode::write, 1, ExtensionMode::zero, MAUOrder::bigEndian}},
139 {"sth",
140 {AccessMode::write, 2, ExtensionMode::zero, MAUOrder::bigEndian}},
141 {"stw",
142 {AccessMode::write, 4, ExtensionMode::zero, MAUOrder::bigEndian}}
143};
144
145} // anonymous namespace.
146
147
148/**
149 * The constructor
150 *
151 * Gets the settings for compiled simulation code generation.
152 *
153 * @param machine The machine to run the simulation on
154 * @param program The simulated program
155 * @param controller Compiled Simulation controller
156 * @param fuResourceConflictDetection is the conflict detection on?
157 * @param handleCycleEnd should we let frontend handle each cycle end
158 * @param basicBlockPerFile Should we generate only one BB per code file?
159 */
163 const TTASimulationController& controller,
164 bool fuResourceConflictDetection,
165 bool handleCycleEnd,
166 bool dynamicCompilation,
167 bool basicBlockPerFile,
168 bool functionPerFile,
169 const TCEString& globalSymbolSuffix) :
170 machine_(machine), program_(program), simController_(controller),
171 gcu_(*machine.controlUnit()),
172 handleCycleEnd_(handleCycleEnd),
173 dynamicCompilation_(dynamicCompilation),
174 basicBlockPerFile_(basicBlockPerFile),
175 functionPerFile_(functionPerFile),
176 instructionNumber_(0), instructionCounter_(0),
177 moveCounter_(0),
178 isProcedureBegin_(true), currentProcedure_(0),
179 lastInstructionOfBB_(0),
180 className_(TCEString("CompiledSimulationEngine_") + globalSymbolSuffix),
181 os_(NULL), symbolGen_(globalSymbolSuffix),
182 conflictDetectionGenerator_(
183 machine_, symbolGen_, fuResourceConflictDetection),
184 needGuardPipeline_(false), globalSymbolSuffix_(globalSymbolSuffix) {
185
186 // this should result in roughly 100K-400K .cpp files
188 // roughly 300-600 c++ lines per simulation function
190
191// Create the guardpipeline.
192
194 needGuardPipeline_ = true;
195
196 int ggLatency = machine.controlUnit()->globalGuardLatency();
197
200
201 for (int i = 0; i < busNav.count(); i++) {
202 const TTAMachine::Bus* bus = busNav.item(i);
203 for (int j = 0; j < bus->guardCount(); j++) {
204 Guard* guard = bus->guard(j);
205 RegisterGuard* rg = dynamic_cast<RegisterGuard*>(guard);
206 if (rg != NULL) {
207 const RegisterFile* rf = rg->registerFile();
208 int rgLat = rg->registerFile()->guardLatency();
209 string symbolName =
211 guardPipeline_[symbolName] = ggLatency + rgLat + 1;
212 }
213 }
214 }
215 }
216}
217
218/**
219 * The destructor
220 */
223
224/**
225 * Generates compiled simulation code to the given directory.
226 *
227 * @param directory Directory where all the code is to be generated at
228 * @exception IOException if the directory could not be opened for writing
229 */
230void
232 targetDirectory_ = directory;
233 headerFile_ = "CompiledSimulationEngine.hh";
234 mainFile_ = "CompiledSimulationEngine.cc";
235
239}
240
241/**
242 * Generates a Makefile for compiling the simulation engine.
243 */
244void
246
249
250 std::ofstream makefile(currentFileName_.c_str());
251 std::vector<std::string> includePaths = Environment::includeDirPaths();
252 std::string includes;
253 for (std::vector<std::string>::iterator it = includePaths.begin();
254 it != includePaths.end(); ++it) {
255 includes += "-I" + *it + " ";
256 }
257
258 makefile
259 << "sources = $(wildcard *.cpp)" << endl
260 << "objects = $(patsubst %.cpp,%.o,$(sources))" << endl
261 << "dobjects = $(patsubst %.cpp,%.so,$(sources))" << endl
262 << "includes = " << includes << endl
263 << "soflags = " << CompiledSimCompiler::COMPILED_SIM_SO_FLAGS << endl
264
265 // use because ccache doesn't like changing directory paths
266 // (in a preprocessor comment)
267 << "cppflags = " << CompiledSimCompiler::COMPILED_SIM_CPP_FLAGS << endl
268 << endl
269
270 << "all: CompiledSimulationEngine.so" << endl << endl
271
272 << "CompiledSimulationEngine.so: CompiledSimulationEngine.hh.gch "
273 << "$(dobjects) CompiledSimulationEngine.cc" << endl
274 << "\t#@echo Compiling CompiledSimulationEngine.so" << endl
275 << "\t$(CC) $(cppflags) -O0 $(includes) CompiledSimulationEngine.cc "
276 << "-c -o CompiledSimulationEngine.o" << endl
277 << "\t$(CC) $(soflags) CompiledSimulationEngine.o -o CompiledSimulationEngine.so"
278 << endl << endl
279 << "$(dobjects): %.so: %.cpp CompiledSimulationEngine.hh.gch" << endl
280
281 // compile and link phases separately to allow distributed compilation
282 // thru distcc
283 << "\t$(CC) -c $(cppflags) $(opt_flags) $(includes) $< -o $@.o" << endl
284 << "\t$(CC) $(soflags) $(opt_flags) -lgcc $@.o -o $@" << endl
285 << "\t@rm -f $@.so.o" << endl
286 << endl
287
288 // use precompiled headers for more speed
289 << "CompiledSimulationEngine.hh.gch:" << endl
290 << "\t$(CC) $(cppflags) $(opt_flags) $(includes) "
291 << "-xc++-header CompiledSimulationEngine.hh" << endl
292 << endl
293
294 << "clean:" << endl
295 << "\t@rm -f $(dobjects) CompiledSimulationEngine.so CompiledSimulationEngine.hh.gch" << endl;
296
297 makefile.close();
298 currentFileName_.clear();
299}
300
301/**
302 * Returns a list of the created .cpp filenames
303 *
304 * @return a list of the created .cpp filenames
305 */
310
311/**
312 * Returns a list of basic blocks of the program
313 *
314 * @return a list of basic blocks of the program
315 */
318 if (bbStarts_.empty()) {
320 }
321 return bbEnds_;
322}
323
324/**
325 * Returns a struct of basic blocks and their corresponding code files
326 *
327 * @return a struct of basic blocks and their corresponding code files
328 */
333
334/**
335 * Creates a header file to be used for all the .cpp files and the main
336 * source file which includes the constructor and the main simulation loop.
337 *
338 * Initializes all variables required by the simulation
339 *
340 * Sets up new SimValue variables for all the FUs, registers,
341 * FU pipeline stages and so on. Variables are created as member variables.
342 *
343 */
344void
346
347 // Open a new file for the header
348 const string DS = FileSystem::DIRECTORY_SEPARATOR;
350 currentFile_.open(currentFileName_.c_str(), fstream::out);
351 os_ = &currentFile_;
352
354
355 // Generate includes
356 *os_ << "// " << className_ << " Generated automatically by ttasim" << endl
357 << "#ifndef _AUTO_GENERATED_COMPILED_SIMULATION_H_" << endl
358 << "#define _AUTO_GENERATED_COMPILED_SIMULATION_H_" << endl
359 << "#include \"SimValue.hh\"" << endl
360 << "#include \"DirectAccessMemory.hh\"" << endl
361 << "#include \"OSAL.hh\"" << endl
362 << "#include \"Operation.hh\"" << endl
363 << "#include \"OperationPool.hh\"" << endl
364 << "#include \"OperationContext.hh\"" << endl
365 << "#include \"CompiledSimulation.hh\"" << endl
366 << "#include \"BaseType.hh\"" << endl
367 << "#include \"MathTools.hh\"" << endl
369 << endl;
370
371 // Open up class declaration and define some extra member variables
372 *os_ << "class " << className_ << ";" << endl << endl;
373 *os_ << "class " << className_ << " : public CompiledSimulation {" << endl
374 << "public:" << endl;
375
376 // Declare all FUs
378 for (int i = 0; i < fus.count(); ++i) {
379 const FunctionUnit& fu = *fus.item(i);
380
381 // FU Ports
382 for (int j = 0; j < fu.operationPortCount(); ++j) {
383 const FUPort& port = *fu.operationPort(j);
385 }
386
387 // Conflict detectors
390 }
391
392 // Operation contexts
393 *os_ << "\t" << "OperationContext "
394 << symbolGen_.operationContextSymbol(fu) << ";" << endl;
395
396 // Address spaces
397 if (fu.addressSpace() != NULL) {
398 *os_ << "\t" << "DirectAccessMemory& "
399 << symbolGen_.DAMemorySymbol(*fus.item(i)) << ";"
400 << endl;
401 }
402
403 // All operations of the FU
404 for (int j = 0; j < fu.operationCount(); ++j) {
405 string opName = fu.operation(j)->name();
406 *os_ << "\t" << "Operation& "
407 << symbolGen_.operationSymbol(opName, fu) << ";" << endl;
408
409 usedOperations_.insert(
410 std::make_pair(opName, symbolGen_.operationSymbol(opName, fu)));
411 }
412
413 // FU output results
414 std::vector<Port*> outPorts = fuOutputPorts(fu);
415 for (size_t j = 0; j < outPorts.size(); ++j) {
416 *os_ << "\t" << "FUResultType "
417 << symbolGen_.FUResultSymbol(*outPorts.at(j))
418 << ";" << endl;
419 }
420 }
421 *os_ << endl;
422
423 // GCU
425 if (gcu) {
426 for (int i = 0; i < gcu->specialRegisterPortCount(); ++i) {
429 }
430 for (int i = 0; i < gcu->operationPortCount(); ++i) {
431 FUPort& port = *gcu->operationPort(i);
433 }
434 }
435
436 // Declare all IUs
439 for (int i = 0; i < ius.count(); ++i) {
440 const ImmediateUnit& iu = *ius.item(i);
441 for (int j = 0; j < iu.numberOfRegisters(); ++j) {
443 iu.width());
444 }
445 }
446
447 // Register files
450 for (int i = 0; i < rfs.count(); ++i) {
451 const RegisterFile& rf = *rfs.item(i);
452 for (int j = 0; j < rf.numberOfRegisters(); ++j) {
454 rf.width());
455 }
456 }
457
458 // Buses
460 for (int i = 0; i < buses.count(); ++i) {
461 const Bus& bus = *buses.item(i);
463 }
464
465 // guard pipeline
466 if (needGuardPipeline_) {
468 }
469
472 *os_ << ";" << endl;
473
475
476 // Generate dummy destructor
477 *os_ << "EXPORT virtual ~" << className_ << "() { }" << endl << endl;
478 *os_ << "EXPORT virtual void simulateCycle();" << endl << endl << "}; // end class" << endl;
479
480 *os_ << "extern \"C\" EXPORT void updateFUOutputs_"
481 << globalSymbolSuffix_ << "(" << className_ << "&);" << endl;
482
483 *os_ << endl << endl << "#endif // include once" << endl << endl;
484
485 // header written
486 currentFile_.close();
487 currentFileName_.clear();
488 os_ = NULL;
489
491}
492
493/**
494 * Generates the parameter list for the constructor.
495 */
496void
498 *os_ << className_
499 << "(const TTAMachine::Machine& machine," << endl
500 << "InstructionAddress entryAddress," << endl
501 << "InstructionAddress lastInstruction," << endl
502 << "SimulatorFrontend& frontend," << endl
503 << "CompiledSimController& controller," << endl
504 << "MemorySystem& memorySystem," << endl
505 << "bool dynamicCompilation,"
506 << "ProcedureBBRelations& procedureBBRelations)";
507}
508
509/**
510 * Generates code for the class constructor in the main .cpp file.
511 */
512void
514
515 const string DS = FileSystem::DIRECTORY_SEPARATOR;
516 currentFile_.open((targetDirectory_ + DS + mainFile_).c_str(), fstream::out);
517 createdFiles_.insert(mainFile_);
518 os_ = &currentFile_;
519
521
522 *os_ << "#include \"" << headerFile_ << "\"" << endl << endl;
523
524 // generate forward declarations for simulate functions
525 for (AddressMap::const_iterator it = bbStarts_.begin();
526 it != bbStarts_.end(); ++it) {
527 *os_ << "\t" << "extern \"C\" EXPORT void "
528 << symbolGen_.basicBlockSymbol(it->first)
529 << "(void*);" << endl;
530 }
531
532 *os_ << "EXPORT " << className_ << "::";
533
535
536 *os_ << " : " << endl
537 << "CompiledSimulation(machine, entryAddress, lastInstruction, "
538 << "frontend, controller, memorySystem, dynamicCompilation, "
539 << "procedureBBRelations),"
540 << endl;
541
544
547 for (int i = 0; i < fus.count(); i++) {
548 const FunctionUnit& fu = *fus.item(i);
549 std::string context = symbolGen_.operationContextSymbol(fu);
550
551 *os_ << "\t" << context << ".setCycleCountVariable(cycleCount_);"
552 << endl;
553 // Create a state for each operation
554 for (int j = 0; j < fu.operationCount(); ++j) {
555 std::string operation = symbolGen_.operationSymbol(
556 fu.operation(j)->name(), fu);
557
558 *os_ << "\t" << operation << ".createState(" << context << ");"
559 << endl;
560 }
561
562 // Set a Memory for the context
563 if (fu.addressSpace() != NULL) {
564 *os_ << "\t" << context << ".setMemory(&"
565 << symbolGen_.DAMemorySymbol(fu) << ");" << endl;
566 }
567 }
568
570
572 << "}" << endl << endl;
573
574 // generate simulateCycle() method
575 *os_ << "// Simulation code:" << endl
576 << "EXPORT void " << className_ << "::simulateCycle() {"
577 << endl << endl;
578
579 // Create a jump dispatcher for accessing each basic block start
580 *os_ << "\t// jump dispatcher" << endl
581 << "\tjumpTargetFunc_ = getSimulateFunction(jumpTarget_);" << endl
582 << "\t(jumpTargetFunc_)(this);" << endl << endl;
583
585 << "}" << endl << endl;
586
589}
590
591/**
592 * Generates the simulateCycle() function that is called outside the .so file
593 *
594 * The generated function simulates one basic block at a time by calling
595 * the according basic block methods.
596 */
597void
599
602
603 // generate code for all procedures
604 for (int i = 0; i < program_.procedureCount(); ++i) {
606 }
607
608 // Close the last file
609 currentFile_.close();
610 currentFileName_.clear();
611 os_ = NULL;
612}
613
614/**
615 * Finds all basic blocks of the program and stores them in two std::maps
616 *
617 * Big basic blocks are split to smaller ones to reduce the engine
618 * compilation memory consumption.
619 */
620void
622 for (int i = 0; i < program_.procedureCount(); ++i) {
624 for (int i = 0; i < cfg.nodeCount(); ++i) {
625 BasicBlockNode& node = cfg.node(i);
626 if (!node.isNormalBB()) continue;
627
628 InstructionAddress blockStart = node.originalStartAddress();
629 const InstructionAddress end = node.originalEndAddress();
630
631 if (end - blockStart > maxInstructionsPerSimulationFunction_) {
632 // split the real basic block to smaller ones in case
633 // there are too many instructions (huge basic blocks might
634 // explode the engine compiler memory consumption)
635 InstructionAddress blockEnd = blockStart;
636#if 0
638 << "splitting a bb " << blockStart << " to " << end
639 << " with size " << end - blockStart << std::endl;
640#endif
641 for (; blockStart < end; blockStart = blockEnd + 1) {
642 blockEnd =
643 std::min(
645 end);
646
647 // we must ensure that the branch and its delay slots
648 // end up in the same block due to the way guard
649 // reads are handled as local variables in the simulation
650 // func
651 if (blockEnd + machine_.controlUnit()->delaySlots() >= end) {
652 blockEnd = end;
653 }
654
655 bbEnds_[blockEnd] = blockStart;
656 bbStarts_[blockStart] = blockEnd;
657#if 0
659 << "small block: " << blockStart << " to "
660 << blockEnd << std::endl;
661#endif
662 }
663 // the "leftover" block:
664
665
666 blockStart = blockEnd + 1;
667 if (blockStart <= end) {
668#if 0
670 << "leftover block: " << blockStart << " to "
671 << end << std::endl;
672#endif
673 bbEnds_[end] = blockStart;
674 bbStarts_[blockStart] = end;
675 }
676 } else {
677 bbEnds_[end] = blockStart;
678 bbStarts_[blockStart] = end;
679 }
680 }
681 }
682}
683
684/**
685 * Generates code for each instruction in a procedure
686 *
687 * @param procedure the procedure to generate code from
688 * @exception InstanceNotFound if the first instruction wasn't found
689 */
690void
692 currentProcedure_ = &procedure;
693 isProcedureBegin_ = true;
694 for (int i = 0; i < procedure.instructionCount(); i++) {
695 Instruction& instruction = procedure.instructionAtIndex(i);
696 generateInstruction(instruction);
699 isProcedureBegin_ = false;
700 }
701}
702
703/**
704 * Generates shutdown code for the given instruction address
705 *
706 * @param address Address location of the instruction. Will be saved in the PC
707 *
708 */
709void
711 *os_ << "/* PROGRAM EXIT */" << endl
712 << "engine.programCounter_ = " << address << ";" << endl
713 << "engine.isFinished_ = true; return;" << endl;
714}
715
716/**
717 * Generates a function that is used to update all FU outputs
718 * from the "result buffer".
719 */
720void
722 // use C-style functions only! (C++ name mangling complicates stuff)
723 *os_ << "/* Updates all FU outputs to correct the visible machine state */" << endl
724 << "extern \"C\" EXPORT void updateFUOutputs_"
725 << globalSymbolSuffix_ << "("
726 << className_ << "& engine) {" << endl;
727
729 for (int i = 0; i < fus.count(); ++i) {
730 const FunctionUnit& fu = *fus.item(i);
731 std::vector<Port*> outPorts = fuOutputPorts(fu);
732 for (size_t j = 0; j < outPorts.size(); ++j) {
733 symbolGen_.enablePrefix("engine.");
734 *os_ << "\t" << generateFUResultRead(
735 symbolGen_.portSymbol(*outPorts.at(j)),
736 symbolGen_.FUResultSymbol(*outPorts.at(j)));
737 }
738 }
739
740 *os_
741 << "}" << endl
742 << endl;
743}
744
745/**
746 * Generates code for a C function that returns an instance of the compiled sim
747 */
748void
750 // use C-style functions only! (C++ name mangling complicates stuff)
751 *os_ << "/* Class getter function */" << endl
752 << "extern \"C\" EXPORT CompiledSimulation* "
753 << "getSimulation_" << globalSymbolSuffix_ << "(" << endl
754 << "\tconst TTAMachine::Machine& machine," << endl
755 << "\tInstructionAddress entryAddress," << endl
756 << "\tInstructionAddress lastInstruction," << endl
757 << "\tSimulatorFrontend& frontend," << endl
758 << "\tCompiledSimController& controller," << endl
759 << "\tMemorySystem& memorySystem," << endl
760 << "\tbool dynamicCompilation," << endl
761 << "\tProcedureBBRelations& procedureBBRelations) {" << endl
762 << "\treturn new " << className_
763 << "(machine, entryAddress, lastInstruction, frontend, controller, "
764 << "memorySystem, dynamicCompilation, procedureBBRelations); "
765 << endl << "}" << endl << endl; // 2x end-of-line in the end of file
766}
767
768/**
769 * Generates code for halting the simulation
770 *
771 * @param message Reason for the halt
772 * @return generated code for halting the simulation
773 */
774string
776 return std::string("\thaltSimulation(__FILE__, __LINE__, __FUNCTION__, \""
777 + message + "\");\n");
778}
779
780/**
781 * Generates code for advancing clocks of various items per cycle
782 */
784 *os_ << endl << "void inline advanceClocks() {" << endl;
785 if (needGuardPipeline_) {
787 }
789
790 *os_ << endl << "}" << endl;
791}
792
793/**
794 * Updates the declared symbols list after the program code is generated.
795 *
796 * Generates constructor calls for each declared symbol.
797 */
798void
800
802
803 // Constructor calls for SimValues
804 for (SimValueSymbolDeclarations::iterator it = declaredSymbols_.begin();
805 it != declaredSymbols_.end(); ++it) {
806 *os_ << "\t";
807 if (it != declaredSymbols_.begin()) {
808 *os_ << ",";
809 }
810 *os_ << it->first << "(" << Conversion::toString(it->second)
811 << ")" << endl;
812 }
813 if (!declaredSymbols_.empty()) {
814 *os_ << endl;
815 }
816
817 // Operations
818 for (OperationSymbolDeclarations::iterator it = usedOperations_.begin();
819 it != usedOperations_.end(); ++it) {
820 *os_ << "\t," << it->second
821 << "(operationPool_.operation(\"" << it->first << "\"))" << endl;
822 }
823 if (!usedOperations_.empty()) {
824 *os_ << endl;
825 }
826
827 // FU result symbols
829 for (int i = 0; i < fus.count(); ++i) {
830 const FunctionUnit& fu = *fus.item(i);
831 std::vector<Port*> outPorts = fuOutputPorts(fu);
832 for (size_t j = 0; j < outPorts.size(); ++j) {
833 *os_ << "\t," << symbolGen_.FUResultSymbol(*outPorts.at(j))
834 << "(" << Conversion::toString(fu.maxLatency()+1) << ")";
835 *os_ << endl;
836 }
837 }
838 if (fus.count() > 0) {
839 *os_ << endl;
840 }
841 // Conflict detectors
843
844 // DirectAccessMemories
845 for (int i = 0; i < fus.count(); i++) {
846 const FunctionUnit & fu = *fus.item(i);
847 if (fu.addressSpace() != NULL) {
848 *os_ << "\t," << symbolGen_.DAMemorySymbol(fu)
849 << "(FUMemory(\"" << fu.name() << "\"))" << endl;
850 }
851 }
852 *os_ << " {" << endl;
853}
854
855/**
856 * Updates the Symbols map of the CompiledSimulation class
857 */
858void
860 for (SimValueSymbolDeclarations::const_iterator it =
861 declaredSymbols_.begin(); it != declaredSymbols_.end(); ++it) {
862 string symbolName = it->first;
863 *os_ << "\t" << "addSymbol(\"" << symbolName << "\", "
864 << symbolName << ");" << endl;
865 }
866}
867
868/**
869 * Generates declarations for all the symbols in the declared symbols -list
870 *
871 * (SimValues, bool guards...)
872 */
873void
875
877
878 for (SimValueSymbolDeclarations::const_iterator it =
879 declaredSymbols_.begin(); it != declaredSymbols_.end(); ++it) {
880 *os_ << "\t" << "SimValue " << it->first << ";" << endl;
881 }
882 *os_ << endl;
883}
884
885/**
886 * Generates code that updates the jump table and finds out all the basic blocks
887 */
888void
890 *os_ << "\t" << "resizeJumpTable(lastInstruction_ + 1);" << endl;
891
892 // If static simulation, set all jump targets in the constructor.
893 if (!dynamicCompilation_) {
894 for (AddressMap::const_iterator it = bbStarts_.begin();
895 it != bbStarts_.end(); ++it) {
896 *os_ << "\t" << "setJumpTargetFunction(" << it->first << ", &"
897 << symbolGen_.basicBlockSymbol(it->first) << ");" << endl;
898 }
899 }
900}
901
902/**
903 * Adds a new declared symbol to the map
904 *
905 * @param name name of the symbol
906 * @param width SimValue width
907 */
908void
909CompiledSimCodeGenerator::addDeclaredSymbol(const string& name, int width) {
910 declaredSymbols_[name] = width;
911}
912
913
914/**
915 * Generates code for a jump operation
916 *
917 * @param op A jump or call operation
918 * @return A std::string containing generated code for the jump call
919 */
920string
922 assert(op.name() == "call" || op.name() == "jump");
923
924 std::stringstream ss;
925 ss << "engine.jumpTarget_ = "
926 << symbolGen_.portSymbol(*op.port(1)) << ".sIntWordValue();";
927 if (op.name() == "call") { // save return address
928 ss << symbolGen_.returnAddressSymbol(gcu_) << " = "
929 << instructionNumber_ + gcu_.delaySlots() + 1 << "u;" << endl;
930 }
931 return ss.str();
932}
933
934/**
935 * Generates code for a triggered operation.
936 *
937 * @param op The triggered operation
938 * @return A std::string containing generated code for the operation call
939 */
940string
942 std::stringstream ss;
943
944 ss << endl << "/* Operation: " << op.name() << ", latency: "
945 << op.latency() << " */" << endl;
946
947 if (operationPool_.operation(op.name().c_str()).dagCount() <= 0) {
949 } else {
950 /// @todo maybe use IsCall & IsControlFlowOperation.
951 if (op.name() != "jump" && op.name() != "call") {
952 ss << "#define context "
954 << endl
955 << "#define opPool_ engine.operationPool_" << endl
956 << generateTriggerCode(op) << endl
957 << "#undef context" << endl
958 << "#undef opPool_" << endl
959 << endl;
960 } else { // simulate a jump
961 ss << handleJump(op);
962 }
963 }
964 return ss.str();
965}
966
967/**
968 * Generates code for a triggered operation that has no DAG available
969 *
970 * @param op The triggered operation
971 * @return A std::string containing generated code for the operation call
972 */
973string
975 const TTAMachine::HWOperation& op) {
976 std::stringstream ss;
977
978 std::vector<string> operandSymbols;
979 string operandTable = symbolGen_.generateTempVariable();
980
981 // Generate symbols for each operand, and temporaries for output operands
982 for (int i = 1; op.port(i) != NULL; ++i) {
983 if (op.port(i)->isOutput()) {
984 string outputSymbol = symbolGen_.generateTempVariable();
985 operandSymbols.push_back(outputSymbol);
986 ss << "SimValue " << outputSymbol
987 << "(" << op.port(i)->width() << ");" << endl;
988 } else { // input port
989 string inputSymbol = symbolGen_.portSymbol(*op.port(i));
990 operandSymbols.push_back(inputSymbol);
991 }
992 }
993
994 // Add operand symbols to the operand table
995 ss << "SimValue* " << operandTable << "[] = {";
996 for (size_t i = 0; i < operandSymbols.size(); ++i) {
997 ss << "&" << operandSymbols.at(i);
998 if (i < operandSymbols.size() - 1) {
999 ss << ",";
1000 }
1001 ss << " ";
1002 }
1003 ss << "};";
1004
1005 // call simulateTrigger with the previously generated operand table
1006 /// @todo maybe use IsCall & IsControlFlowOperation.
1007 if (op.name() != "jump" && op.name() != "call") {
1008 ss << symbolGen_.operationSymbol(op.name(), *op.parentUnit())
1009 << ".simulateTrigger(" << operandTable << ", "
1011 << "); ";
1012
1013 // add output values as delayed assignments
1014 for (int i = 1; op.port(i) != NULL; ++i) {
1015 if (op.port(i)->isOutput()) {
1016 ss << generateAddFUResult(*op.port(i), operandSymbols.at(i - 1),
1017 op.latency());
1018 }
1019 }
1020 } else { // simulate a jump
1021 ss << handleJump(op);
1022 }
1023
1024 return ss.str();
1025}
1026
1027/**
1028 * Generates code for reading a guard value before an instruction
1029 * with moves guarded with the value is simulated.
1030 *
1031 * @param move guarded move
1032 * @return a std::string containing generated code for the guard check
1033 */
1034string
1036 const TTAProgram::Move& move) {
1037
1038 std::stringstream ss;
1039 string guardSymbolName;
1040
1041 const TTAMachine::Guard& guard = move.guard().guard();
1042
1043 const TTAMachine::RegisterGuard* rg =
1044 dynamic_cast<const RegisterGuard*>(&guard);
1045 // Find out the guard type
1046 if (rg != NULL) {
1047 const RegisterGuard& rg = dynamic_cast<const RegisterGuard&>(guard);
1048 const RegisterFile& rf = *rg.registerFile();
1049 guardSymbolName = symbolGen_.registerSymbol(rf, rg.registerIndex());
1050 } else if (dynamic_cast<const PortGuard*>(&guard) != NULL) {
1051 const PortGuard& pg = dynamic_cast<const PortGuard&>(guard);
1052 guardSymbolName = symbolGen_.portSymbol(*pg.port());
1053 } else {
1054 ss << endl << "#error unknown guard type!" << endl;
1055 }
1056
1057 lastGuardBool_ = "";
1058
1059 // Make sure to create only one bool per guard read and store the symbol
1060 if (usedGuardSymbols_.find(guardSymbolName) == usedGuardSymbols_.end()) {
1061
1062 // read from the guard pipeline? then use the pipeline ar directly
1063 if (needGuardPipeline_ && rg != NULL) {
1064 std::string guardSym = guardPipelineTopSymbol(*rg);
1065 lastGuardBool_ = usedGuardSymbols_[guardSymbolName] = guardSym;
1066 } else {
1068 usedGuardSymbols_[guardSymbolName] = lastGuardBool_;
1069
1070 // red from the register.
1071 ss << "const bool " << lastGuardBool_ <<
1072 " = !(MathTools::fastZeroExtendTo("
1073 << guardSymbolName << ".uIntWordValue(), " <<
1074 guardSymbolName << ".width()) == 0u);";
1075 }
1076 } else {
1077 lastGuardBool_ = usedGuardSymbols_[guardSymbolName];
1078 }
1079 return ss.str();
1080}
1081
1082/**
1083 * Generates the condition simulation code for a guarded move.
1084 *
1085 * @param move guarded move
1086 * @return a std::string containing generated code for the guard check
1087 */
1088string
1090 const TTAProgram::Move& move) {
1091
1092 std::stringstream ss;
1093 string guardSymbolName;
1094
1095 const TTAMachine::Guard& guard = move.guard().guard();
1096
1097 // Find out the guard type
1098 if (dynamic_cast<const RegisterGuard*>(&guard) != NULL) {
1099 const RegisterGuard& rg = dynamic_cast<const RegisterGuard&>(guard);
1100 const RegisterFile& rf = *rg.registerFile();
1101 guardSymbolName = symbolGen_.registerSymbol(rf, rg.registerIndex());
1102 } else if (dynamic_cast<const PortGuard*>(&guard) != NULL) {
1103 const PortGuard& pg = dynamic_cast<const PortGuard&>(guard);
1104 guardSymbolName = symbolGen_.portSymbol(*pg.port());
1105 } else {
1106 ss << endl << "#error unknown guard type!" << endl;
1107 }
1108
1109 lastGuardBool_ = "";
1110
1111 // Make sure to create only one bool per guard read and store the symbol
1112 if (usedGuardSymbols_.find(guardSymbolName) == usedGuardSymbols_.end()) {
1114 usedGuardSymbols_[guardSymbolName] = lastGuardBool_;
1115 } else {
1116 lastGuardBool_ = usedGuardSymbols_[guardSymbolName];
1117 }
1118
1119 // Handle inverted guards
1120 ss << endl << "if (";
1121 if (guard.isInverted()) {
1123 }
1124 ss << lastGuardBool_ << ") { ";
1125
1126 return ss.str();
1127}
1128
1129
1130/**
1131 * Generates simulation code of the given instruction
1132 *
1133 * @param instruction instruction to generate the code from
1134 */
1135void
1137
1138 InstructionAddress address = instruction.address().location();
1139 usedGuardSymbols_.clear();
1140
1141 // Are we at the start of a new basic block?
1142 if (bbStarts_.find(address) != bbStarts_.end()) {
1143 // Should we start with a new file?
1147
1148 if (currentFile_.is_open()) {
1149 currentFile_.close();
1150 }
1151
1152 // Generate a new file to begin to work with
1153 const string DS = FileSystem::DIRECTORY_SEPARATOR;
1155 + symbolGen_.basicBlockSymbol(address) + ".cpp";
1156 currentFile_.open(currentFileName_.c_str(), fstream::out);
1158 os_ = &currentFile_;
1159 *os_ << "// " << className_
1160 << " Generated automatically by ttasim" << endl
1161 << "#include \"" << headerFile_ << "\"" << endl << endl;
1162 }
1163
1164 lastInstructionOfBB_ = bbStarts_.find(address)->second;
1166
1167 // Save basic block<->procedure related information
1168 InstructionAddress procedureStart = currentProcedure_->
1169 startAddress().location();
1170 procedureBBRelations_.procedureStart[address] = procedureStart;
1172 procedureBBRelations_.basicBlockStarts.insert(std::make_pair(
1173 procedureStart, address));
1174
1175 // Start a new C++ function for the basic block
1176 if (isProcedureBegin_) {
1177 *os_ << "/* Procedure " << currentProcedure_->name()
1178 << " */" << endl;
1179 }
1180 *os_ << endl << "extern \"C\" EXPORT void "
1181 << symbolGen_.basicBlockSymbol(address)
1182 << "(void* eng) {" << endl;
1183 *os_ << className_ << "& engine = *(" << className_ << "*)eng;" << endl;
1184 symbolGen_.enablePrefix("engine.");
1185 lastFUWrites_.clear();
1186
1187 // initialize jump target to next BB.
1188 int bbEndAddr = -1;
1189 for (int addr = address; bbEndAddr == -1; addr++) {
1190 if (AssocTools::containsKey(bbEnds_, addr)) {
1191 bbEndAddr = addr;
1192 }
1193 }
1194
1195 *os_ << "/* First instruction of BB - initialize address of next BB"
1196 << " */" << endl;
1197 *os_ << "engine.jumpTarget_ = " << bbEndAddr + 1 << ";" << endl;
1198 }
1199
1200 *os_ << endl << "/* Instruction " << instructionNumber_ << " */" << endl;
1201
1202 // Advance clocks of the conflict detectors
1205 *os_ << "engine.advanceClocks();" << endl;
1206 }
1207
1208 // Do immediate assignments per instruction for FU ports
1209 for (int i = 0; i < instruction.immediateCount(); ++i) {
1210 const Immediate& immediate = instruction.immediate(i);
1211
1212 if (!immediate.destination().isFUPort()) {
1213 continue;
1214 }
1215
1217 int value = immediate.value().value().unsignedValue();
1218 *os_ << " = " << value << "u;";
1219 }
1220
1221 // Get FU Results if there are any ready
1222 std::set<std::string> gotResults; // used to get FU results only once/instr.
1223 DelayedAssignments::iterator it =
1225 while (it != delayedFUResultWrites_.end()) {
1226 *os_ << "engine.clearFUResults(" << it->second.fuResultSymbol << ");" << endl;
1227 *os_ << it->second.targetSymbol << " = " << it->second.sourceSymbol
1228 << ";" << std::endl;
1229 gotResults.insert(it->second.targetSymbol);
1230 delayedFUResultWrites_.erase(it);
1232 }
1233
1234 bool endGuardBracket = false;
1235
1236 // Do moves
1237 std::vector<CompiledSimMove> lateMoves; // moves with buses
1238
1239 // generate the possible guard value reads before the
1240 // actual moves
1241 for (int i = 0; i < instruction.moveCount(); ++i) {
1242 const Move& move = instruction.move(i);
1243 if (!move.isUnconditional()) {
1244 *os_ << generateGuardRead(move) << std::endl;
1245 }
1246 }
1247
1248 for (int i = 0; i < instruction.moveCount(); ++i, moveCounter_++) {
1249 const Move& move = instruction.move(i);
1250 string moveSource = symbolGen_.moveOperandSymbol(
1251 move.source(), move);
1252 string moveDestination = symbolGen_.moveOperandSymbol(
1253 move.destination(), move);
1254
1255 lastGuardBool_.clear();
1256
1257 if (move.source().isFUPort() && gotResults.find(moveSource) == gotResults.end() &&
1258 dynamic_cast<const ControlUnit*>(&move.source().functionUnit()) == NULL) {
1259 *os_
1261 moveSource, symbolGen_.FUResultSymbol(move.source().port()))
1262 << endl;
1263 gotResults.insert(moveSource);
1264 }
1265
1266 if (!move.isUnconditional()) { // has a guard?
1267 *os_ << generateGuardCondition(move);
1268 endGuardBracket = true;
1269 }
1270
1271 // increase move count if the move is guarded or it is an exit point
1272 if (!move.isUnconditional() || exitPoints_.find(
1273 instruction.address().location()) != exitPoints_.end()) {
1274 *os_ << " ++engine.moveExecCounts_[" << moveCounter_ << "]; ";
1275 }
1276
1277 // Find all moves that depend on others i.e. those moves that have to
1278 // be done last.
1279 bool dependingMove = false;
1280 for (int j = instruction.moveCount() - 1; j > 0 && i != j; --j) {
1281 if (moveDestination == symbolGen_.moveOperandSymbol(
1282 instruction.move(j).source(), move)) {
1283 dependingMove = true;
1284 CompiledSimMove lateMove(move, lastGuardBool_, symbolGen_);
1285 lateMoves.push_back(lateMove);
1286 *os_ << lateMove.copyToBusCode();
1287 }
1288 }
1289
1290 // Assign the values directly instead of through
1291 // the SimValue assignment in case:
1292 if ((move.source().isGPR() || move.source().isFUPort()
1293 || move.source().isImmediateRegister()) &&
1294 (move.source().port().width() <= static_cast<int>(sizeof(UIntWord)*8)) &&
1295 move.source().port().width() == move.destination().port().width()) {
1296 if (move.source().isImmediateRegister() && move.source().immediateUnit().signExtends()) {
1297 moveSource += ".sIntWordValue()";
1298 } else {
1299 moveSource += ".uIntWordValue()";
1300 }
1301 }
1302
1303 if (!dependingMove) {
1304 *os_ << moveDestination << " = " << moveSource << "; ";
1305 if (needGuardPipeline_) {
1306 handleRegisterWrite(moveDestination, *os_);
1307 }
1308 }
1309
1310 if (endGuardBracket) {
1311 *os_ << "}";
1312 endGuardBracket = false;
1313 }
1314 }
1315
1316 endGuardBracket = false;
1317
1318 // Do moves with triggers, except stores.
1319 for (int i = 0; i < instruction.moveCount(); ++i) {
1320 const Move& move = instruction.move(i);
1321 if (!move.isTriggering()) {
1322 continue;
1323 }
1324
1325 const TerminalFUPort& tfup =
1326 static_cast<const TerminalFUPort&>(move.destination());
1327 const HWOperation& hwOperation = *tfup.hwOperation();
1328
1329 if (isStoreOperation(hwOperation.name())) {
1330 continue;
1331 }
1332
1333 string moveSource = symbolGen_.moveOperandSymbol(
1334 move.source(), move);
1335 string moveDestination = symbolGen_.moveOperandSymbol(
1336 move.destination(), move);
1337
1338 if (!move.isUnconditional()) { // has a guard?
1339 *os_ << generateGuardCondition(move);
1340 endGuardBracket = true;
1341 }
1342
1343 if (move.source().isFUPort() && gotResults.find(moveSource) ==
1344 gotResults.end() && dynamic_cast<const ControlUnit*>(
1345 &move.source().functionUnit()) == NULL) {
1346 *os_
1348 moveDestination,
1350 << endl;
1351
1352 gotResults.insert(
1353 symbolGen_.moveOperandSymbol(move.source(), move));
1354 }
1355
1356 *os_ << handleOperation(hwOperation)
1358
1359 if (endGuardBracket) {
1360 *os_ << "}" << endl;
1361 endGuardBracket = false;
1362 }
1363 } // end for
1364
1365
1366
1367
1368 // Do moves with store triggers.
1369 for (int i = 0; i < instruction.moveCount(); ++i) {
1370 const Move& move = instruction.move(i);
1371 if (!move.isTriggering()) {
1372 continue;
1373 }
1374
1375 const TerminalFUPort& tfup =
1376 static_cast<const TerminalFUPort&>(move.destination());
1377 const HWOperation& hwOperation = *tfup.hwOperation();
1378
1379 if (!isStoreOperation(hwOperation.name())) {
1380 continue;
1381 }
1382
1383 string moveSource = symbolGen_.moveOperandSymbol(
1384 move.source(), move);
1385 string moveDestination = symbolGen_.moveOperandSymbol(
1386 move.destination(), move);
1387
1388 if (!move.isUnconditional()) { // has a guard?
1389 *os_ << generateGuardCondition(move);
1390 endGuardBracket = true;
1391 }
1392
1393 if (move.source().isFUPort() && gotResults.find(moveSource) ==
1394 gotResults.end() && dynamic_cast<const ControlUnit*>(
1395 &move.source().functionUnit()) == NULL) {
1396 *os_
1398 moveDestination,
1400 << endl;
1401
1402 gotResults.insert(
1403 symbolGen_.moveOperandSymbol(move.source(), move));
1404 }
1405
1406 *os_ << handleOperation(hwOperation)
1408
1409 if (endGuardBracket) {
1410 *os_ << "}" << endl;
1411 endGuardBracket = false;
1412 }
1413 } // end for
1414
1415
1416
1417 // Do immediate assignments for everything else
1418 for (int i = 0; i < instruction.immediateCount(); ++i) {
1419 const Immediate& immediate = instruction.immediate(i);
1420
1421 if (immediate.destination().isFUPort()) {
1422 continue;
1423 }
1424
1426 if (immediate.destination().immediateUnit().signExtends()) {
1427 int value = immediate.value().value().intValue();
1428 *os_ << " = SIntWord("<< value << ");";
1429 } else {
1430 unsigned int value = immediate.value().value().unsignedValue();
1431 *os_ << " = " << value << "u;";
1432 }
1433 }
1434
1435 // Do bus moves
1436 for (std::vector<CompiledSimMove>::const_iterator it = lateMoves.begin();
1437 it != lateMoves.end(); ++it) {
1438 *os_ << it->copyFromBusCode();
1439 if (needGuardPipeline_) {
1440 if (it->destination().isGPR()) {
1442 symbolGen_.registerSymbol(it->destination()), *os_);
1443 }
1444 }
1445 }
1446
1447 // No operation?
1448 if (instruction.moveCount() == 0 && instruction.immediateCount() == 0) {
1449 *os_ << "/* NOP */" << endl;
1450 }
1451
1452 // Let frontend handle cycle end?
1453 if (handleCycleEnd_) {
1454 *os_ << "engine.cycleEnd();" << endl;
1455 }
1456
1457 *os_ << "engine.cycleCount_++;" << endl;
1458
1459 AddressMap::iterator bbEnd = bbEnds_.find(address);
1460
1461 // Increase basic block execution count
1462 if (bbEnd != bbEnds_.end()) {
1463 InstructionAddress bbStart =
1464 bbEnds_.lower_bound(instructionNumber_)->second;
1465 *os_ << "++engine.bbExecCounts_[" << bbStart << "];" << endl;
1466 }
1467
1468 // generate exit code if this is a return instruction
1469 if (exitPoints_.find(address) != exitPoints_.end()) {
1470 generateShutdownCode(address);
1471 }
1472
1473 // Create code for a possible exit after the basic block
1474 if (bbEnd != bbEnds_.end()) {
1475 *os_
1476 << "if (engine.cycleCount_ >= engine.cyclesToSimulate_) {" << endl
1477 << "\t" << "engine.stopRequested_ = true;" << endl
1478 << "\t" << "updateFUOutputs_" << globalSymbolSuffix_
1479 << "(engine);" << endl
1480 << "}" << endl;
1481
1482 *os_ << "{ engine.programCounter_ = engine.jumpTarget_; "
1483 << "engine.lastExecutedInstruction_ = " << address
1484 << "; return; }" << endl;
1485 // Generate shutdown code after the last instruction
1486 if (address == program_.lastInstruction().address().location()) {
1487 generateShutdownCode(address);
1488 }
1489
1490 *os_ << endl << "} /* end function */" << endl << endl;
1491 }
1492}
1493
1494/**
1495 * Generates code for calling triggered operations
1496 *
1497 * @param op The operation to be triggered
1498 * @return A string containing the generated C++ code
1499 */
1500string
1502 const TTAMachine::HWOperation& op) {
1503
1504 vector<string> operands;
1505 string source = "EXEC_OPERATION(";
1506
1507 // grab all operands and initialize them to the operand table
1508 for (int i = 1; op.port(i) != NULL; ++i) {
1509 if (op.port(i)->isInput()) {
1510 operands.push_back(symbolGen_.portSymbol(*op.port(i)));
1511 } else {
1512 operands.push_back(std::string("outputvalue")+Conversion::toString(i));
1513 }
1514 }
1515
1516 if (isStoreOperation(op.name())) {
1517 return generateStoreTrigger(op);
1518 } else if (isLoadOperation(op.name())) {
1519 return generateLoadTrigger(op);
1520 }
1521 OperationDAG* dag = &operationPool_.operation(op.name().c_str()).dag(0);
1522
1523 std::string simCode =
1525
1526 for (int i = 1, tmp = 1; op.port(i) != NULL; ++i) {
1527 if (op.port(i)->isOutput()) {
1528
1529 std::string outValueStr;
1530 // add output values as delayed assignments
1531 std::string outputStr = std::string("outputvalue") + Conversion::toString(i);
1532 size_t ovLen = outputStr.length() + 3;
1533 while (simCode.find(outputStr + " = ") != string::npos) {
1534 std::size_t begin = simCode.find(outputStr+" = ");
1535 std::size_t end = simCode.find(";", begin);
1536 outValueStr = simCode.substr(begin+ovLen,(end-begin-ovLen));
1537 simCode.erase(begin, end-begin);
1538 }
1539
1540 if (outValueStr.empty()) {
1541 std::string msg = "Machine has bound outport not used by op: ";
1542 msg += op.name();
1543 msg += " port index: ";
1544 msg += Conversion::toString(i);
1545 throw IllegalMachine(__FILE__,__LINE__,__func__,msg);
1546 }
1547
1548 // generate new unique name for it
1549 string tempVariable = symbolGen_.generateTempVariable();
1550
1551 if (simCode.find(outValueStr) == string::npos) {
1552 continue;
1553 }
1554
1555 while(simCode.find(outValueStr) != string::npos) {
1556 string::iterator it = simCode.begin() + simCode.find(outValueStr);
1557 simCode.replace(it, it + outValueStr.length(), tempVariable);
1558 }
1559
1560 simCode.append("\n" + generateAddFUResult(*op.port(i),
1561 tempVariable, op.latency()));
1562 tmp++;
1563 } // end isOutput
1564 } // end for
1565
1566 // fix the names of the tmp[n] values to unique ones so there can be
1567 // multiple in same BB.
1568 while (simCode.find("SimValue tmp") != string::npos) {
1569 std::size_t begin = simCode.find("SimValue tmp");
1570 std::size_t end = simCode.find(";", begin);
1571 std::string tmpName = simCode.substr(begin+9,(end-begin-9));
1572 string tempVariable = symbolGen_.generateTempVariable();
1573
1574 while(simCode.find(tmpName) != string::npos) {
1575 string::iterator it = simCode.begin() + simCode.find(tmpName);
1576 simCode.replace(it,it + tmpName.length(), tempVariable);
1577 }
1578 }
1579
1580 return simCode;
1581}
1582
1583/**
1584 * Generates a faster version of specific store triggers
1585 * @param o The store operation (either stw, sth or stq)
1586 */
1587string
1589 const TTAMachine::HWOperation& op) {
1590 string address = symbolGen_.portSymbol(*op.port(1)) + ".uIntWordValue()";
1591 string dataToWrite = symbolGen_.portSymbol(*op.port(2)) + ".uIntWordValue()";
1592 string memory = symbolGen_.DAMemorySymbol(op.parentUnit()->name());
1593 string method;
1594
1595 const MemoryOperationDescription& memOpDesc = supportedMemoryOps.at(
1596 op.name());
1597 method = "fastWrite";
1598 if (memOpDesc.mauCount > 1) {
1599 method += std::to_string(memOpDesc.mauCount) + "MAUs";
1600 if (memOpDesc.mauOrder == MAUOrder::littleEndian) {
1601 method += "LE";
1602 } else { // big-endian
1603 method += "BE";
1604 }
1605 } else {
1606 method += "MAU";
1607 }
1608
1609 return memory + "." + method + "(" + address + ", " + dataToWrite + ");";
1610}
1611
1612/**
1613 * Generates a faster version of specific load triggers
1614 * @param op The load operation (either ldw, ldq, ldh, ldhu, or ldqu)
1615 */
1616string
1618 const TTAMachine::HWOperation& op) {
1619 const FunctionUnit& fu = *op.parentUnit();
1620 std::stringstream ss;
1621 string address = symbolGen_.portSymbol(*op.port(1)) + ".uLongWordValue()";
1622 string memory = symbolGen_.DAMemorySymbol(op.parentUnit()->name());
1623 string MAUSize = Conversion::toString(fu.addressSpace()->width());
1624 string method;
1625 string extensionMode = "SIGN_EXTEND";
1626 string resultSignExtend;
1627 string temp = symbolGen_.generateTempVariable();
1628
1629 const MemoryOperationDescription& memOpDesc = supportedMemoryOps.at(
1630 op.name());
1631 method = "fastRead";
1632 if (memOpDesc.mauCount > 1) {
1633 method += std::to_string(memOpDesc.mauCount) + "MAUs";
1634 if (memOpDesc.mauOrder == MAUOrder::littleEndian) {
1635 method += "LE";
1636 } else { // big-endian
1637 method += "BE";
1638 }
1639 } else {
1640 method += "MAU";
1641 }
1642
1643 if (memOpDesc.extensionMode == ExtensionMode::sign) {
1644 extensionMode = "SIGN_EXTEND";
1645 } else {
1646 extensionMode = "ZERO_EXTEND";
1647 }
1648
1649 resultSignExtend = temp + " = " + extensionMode
1650 + "(" + temp + ", (" + MAUSize + "*"
1651 + std::to_string(memOpDesc.mauCount) +"));";
1652
1653 ss << "ULongWord " << temp << "; " << memory + "." << method << "("
1654 << address << ", " << temp << "); ";
1655
1656 ss << resultSignExtend << " ";
1657
1658 ss << generateAddFUResult(*op.port(2), temp, op.latency());
1659
1660 return ss.str();
1661}
1662
1663/**
1664 * Generates code for adding a result to FU's output port
1665 *
1666 * Handles static latency simulation in case it is possible, otherwise
1667 * reverts back to older dynamic model.
1668 *
1669 * The case this function tracks is the following:
1670 *
1671 * 1) Instruction address > basic block start + latency
1672 * 2) Trigger not inside a guard
1673 * 3) Result must be ready in the same basic block
1674 * 4) No overlapping writes of any kind
1675 *
1676 * @param resultPort FU result port to set the value to
1677 * @param value value to set as a result
1678 * @param latency latency of the operation
1679 * @return the generated code for putting the results.
1680 */
1681std::string
1683 const TTAMachine::FUPort& resultPort,
1684 const std::string& value,
1685 int latency) {
1686
1687 std::stringstream ss;
1688 const FunctionUnit& fu = *resultPort.parentUnit();
1689 const int writeTime = instructionNumber_ + latency;
1690
1691 AddressMap::iterator bbEnd = bbEnds_.lower_bound(instructionNumber_);
1692
1693 const bool resultInSameBasicBlock = bbEnd->first ==
1694 bbEnds_.lower_bound(writeTime)->first;
1695
1696 const int bbStart = bbEnd->second;
1697 bool staticSimulationPossible = false;
1698 const std::string destination = symbolGen_.portSymbol(resultPort);
1699
1700 int lastWrite = 0;
1701 if (lastFUWrites_.find(destination) != lastFUWrites_.end()) {
1702 lastWrite = lastFUWrites_[destination];
1703 }
1704
1705 // If no more pending results are coming outside or inside the basic block,
1706 // no guard of any kind and result will be ready in the same basic block,
1707 // then static latency simulation can be done.
1708 if ((writeTime >= bbStart + fu.maxLatency())
1709 && lastGuardBool_.empty() && resultInSameBasicBlock
1710 && (writeTime > lastWrite)) {
1711 staticSimulationPossible = true;
1712
1713 for (int i = instructionNumber_ + 1; i < writeTime
1714 && staticSimulationPossible; ++i) {
1715
1717 for (int j = 0; j < instr.moveCount(); ++j) {
1718 if ((instr.move(j).isTriggering() && fu.name()
1719 == instr.move(j).destination().functionUnit().name()) ||
1720 (instr.move(j).source().isGPR() &&
1721 instr.move(j).source().port().name()==resultPort.name())) {
1722 staticSimulationPossible = false;
1723 break;
1724 }
1725 }
1726 }
1727 }
1728
1729 if (staticSimulationPossible) { // Add a new delayed assignment
1730 DelayedAssignment assignment = { value, destination,
1731 symbolGen_.FUResultSymbol(resultPort) };
1732 delayedFUResultWrites_.insert(std::make_pair(writeTime, assignment));
1733 } else { // revert to old dynamic FU result model
1734 ss << "engine.addFUResult(" << symbolGen_.FUResultSymbol(resultPort)
1735 << ", " << "engine.cycleCount_, " << value << ", " << latency << ");";
1736 if (writeTime > lastWrite) {
1737 lastFUWrites_[destination] = writeTime;
1738 }
1739 }
1740
1741 return ss.str();
1742}
1743
1744/**
1745 * Generates code for reading FU results from result symbol to result port
1746 *
1747 * @param destination destination port symbol
1748 * @param resultSymbol results symbol
1749 * @return generated code for getting the result
1750 */
1751std::string
1753 const std::string& destination,
1754 const std::string& resultSymbol) {
1755 std::stringstream ss;
1756
1757 ss << "engine.FUResult(" << destination << ", " << resultSymbol
1758 << ", engine.cycleCount_);" << endl;
1759
1760 return ss.str();
1761}
1762
1763/**
1764 * Returns the maximum possible latency from the FUs & GCU
1765 *
1766 * @return the maximum possible latency from the FUs & GCU
1767 */
1768int
1772 for (int i = 0; i < fus.count(); ++i) {
1773 if (maxLatency < fus.item(i)->maxLatency()) {
1774 maxLatency = fus.item(i)->maxLatency();
1775 }
1776 }
1777 return maxLatency;
1778}
1779
1780/**
1781 * Returns the output ports of a function unit
1782 *
1783 * @param fu The function unit
1784 *
1785 * @return A vector of output ports of a function unit
1786 */
1787std::vector<TTAMachine::Port*>
1789 const TTAMachine::FunctionUnit& fu) const {
1790 std::vector<TTAMachine::Port*> ports;
1791 for (int i = 0; i < fu.portCount(); ++i) {
1792 if (fu.port(i)->isOutput()) {
1793 ports.push_back(fu.port(i));
1794 }
1795 }
1796 return ports;
1797}
1798
1800 std::ostream& stream) {
1801 for (GuardPipeline::iterator i = guardPipeline_.begin();
1802 i != guardPipeline_.end(); i++) {
1803 const std::string& regName = i->first;
1804 for (int j = i->second -1 ; j > 0; j--) {
1805 stream << "guard_pipeline_" << regName << "_" << j << " = "
1806 << "guard_pipeline_" << regName << "_" << j -1
1807 << ";" << std::endl;
1808 }
1809 }
1810}
1811
1813 std::ostream& stream) {
1814 for (GuardPipeline::iterator i = guardPipeline_.begin();
1815 i != guardPipeline_.end(); i++) {
1816 const std::string& regName = i->first;
1817 for (int j = 0; j < i->second ; j++) {
1818 stream << "bool guard_pipeline_" + regName << "_" << j <<
1819 ";" << std::endl;
1820 }
1821 }
1822}
1823
1825 const TTAMachine::RegisterGuard& rg) {
1826 const RegisterFile* rf = rg.registerFile();
1827 std::string regName = "RF_" + rf->name() + "_" +
1829 GuardPipeline::iterator i = guardPipeline_.find(regName);
1830 assert(i!= guardPipeline_.end());
1831 return "engine.guard_pipeline_RF_" +
1833 "_" + Conversion::toString(i->second -1);
1834}
1835
1836// if register found from guard registe list. put the value if the just written
1837// register into the guard pipeline.
1839 const std::string& regSymbolName, std::ostream& stream) {
1840 std::string tmpString;
1841 std::string tmpString2;
1842
1843 const string tmpRef1 = regSymbolName;
1844
1845 // drop ".engine" from beginning
1846 // if found, copy to tmp and take ref to tmp. not found, ref to original
1847 const string& tmpRef2 = (tmpRef1.find("engine.") == 0) ?
1848 tmpString2 = tmpRef1.substr(7) :
1849 tmpRef1;
1850
1851 GuardPipeline::iterator i = guardPipeline_.find(tmpRef2);
1852 if ( i != guardPipeline_.end()) {
1853 stream << "engine.guard_pipeline_" << tmpRef2 << "_0 "
1854 << " = !(MathTools::fastZeroExtendTo("
1855 << tmpRef1 << ".uIntWordValue(), "
1856 << tmpRef1 << ".width()) == 0u);" << std::endl;
1857 return true;
1858 }
1859 return false;
1860}
1861
1862
1863/**
1864 * Returns list of all supported memory accessing operations for compiled
1865 * simulation.
1866 */
1869 TCETools::CIStringSet result;
1870 for (auto& pair : supportedMemoryOps) {
1871 result.insert(pair.first);
1872 }
1873 return result;
1874}
1875
1876
1877bool
1879 auto it = supportedMemoryOps.find(opName);
1880 if (it == supportedMemoryOps.end()) return false;
1881
1882 return (it->second.accessMode == AccessMode::write);
1883}
1884
1885bool
1887 auto it = supportedMemoryOps.find(opName);
1888 if (it == supportedMemoryOps.end()) return false;
1889
1890 return (it->second.accessMode == AccessMode::read);
1891}
1892
#define __func__
#define assert(condition)
Word UIntWord
Definition BaseType.hh:144
UInt32 InstructionAddress
Definition BaseType.hh:175
TTAMachine::Machine * machine
the architecture definition of the estimated processor
find Finds info of the inner loops in the program
find Finds info of the inner loops in the false
#define DS
static std::ostream & logStream()
static bool containsKey(const ContainerType &aContainer, const KeyType &aKey)
bool isNormalBB() const
InstructionAddress originalEndAddress() const
InstructionAddress originalStartAddress() const
int nodeCount() const
Node & node(const int index) const
std::string generateStoreTrigger(const TTAMachine::HWOperation &op)
std::string generateFUResultRead(const std::string &destination, const std::string &resultSymbol)
std::string currentFileName_
Name of the current file being processed.
bool basicBlockPerFile_
Should the generator generate only one basic block per code file.
std::string generateGuardRead(const TTAProgram::Move &move)
std::string generateHaltCode(const std::string &message="")
std::string handleJump(const TTAMachine::HWOperation &op)
std::set< InstructionAddress > exitPoints_
Program exit point addresses.
void generateInstruction(const TTAProgram::Instruction &instruction)
std::string handleOperationWithoutDag(const TTAMachine::HWOperation &op)
std::map< InstructionAddress, InstructionAddress > AddressMap
A type for storing address-to-address combinations.
std::string generateTriggerCode(const TTAMachine::HWOperation &op)
std::string handleOperation(const TTAMachine::HWOperation &op)
bool functionPerFile_
Should the generator start with a new file after function end.
const TTAProgram::Program & program_
The simulated program.
DelayedAssignments delayedFUResultWrites_
Delayed FU Result assignments.
const TTAProgram::Procedure * currentProcedure_
Pointer to the current Procedure being processed.
std::string lastGuardBool_
name of the last used guard variable
std::vector< TTAMachine::Port * > fuOutputPorts(const TTAMachine::FunctionUnit &fu) const
const TTAMachine::ControlUnit & gcu_
GCU.
std::set< std::string > StringSet
A type for std::string sets.
std::string generateGuardCondition(const TTAProgram::Move &move)
const TTASimulationController & simController_
The simulator frontend.
bool handleCycleEnd_
Should we let frontend handle each cycle end.
std::string generateAddFUResult(const TTAMachine::FUPort &resultPort, const std::string &value, int latency)
ConflictDetectionCodeGenerator conflictDetectionGenerator_
Conflict detection code generator.
static TCETools::CIStringSet supportedMemoryOperations()
std::map< std::string, std::string > usedGuardSymbols_
Temporary list of the used guard bool symbols per instruction.
void generateShutdownCode(InstructionAddress address)
const TTAMachine::Machine & machine_
The machine used for simulation.
bool dynamicCompilation_
Is this a dynamic compiled simulation?
virtual AddressMap basicBlocks() const
InstructionAddress lastInstructionOfBB_
last instruction of the current basic block
std::fstream currentFile_
Current file being processed.
static bool isLoadOperation(const std::string &opName)
unsigned maxInstructionsPerSimulationFunction_
Max for each simulation function.
std::string guardPipelineTopSymbol(const TTAMachine::RegisterGuard &guard)
std::string className_
Name of the class to be created.
void generateGuardPipelineAdvance(std::ostream &stream)
std::string mainFile_
Main source filename. This includes the constructor and the simulateCycle().
int instructionNumber_
Absolute instruction # being processed.
AddressMap bbEnds_
The basic block map referred by end of the block as a key.
bool handleRegisterWrite(const std::string &regSymbolName, std::ostream &stream)
std::string generateLoadTrigger(const TTAMachine::HWOperation &op)
std::string headerFile_
Header filename.
int instructionCounter_
Istruction counter for checking how many instructions to put per file.
static bool isStoreOperation(const std::string &opName)
std::string targetDirectory_
Directory where to write the source files of the engine.
void generateGuardPipelineVariables(std::ostream &stream)
OperationPool operationPool_
The operation pool.
virtual void generateToDirectory(const std::string &dirName)
virtual ProcedureBBRelations procedureBBRelations() const
void generateProcedureCode(const TTAProgram::Procedure &procedure)
OperationSymbolDeclarations usedOperations_
A list of used operations.
StringSet createdFiles_
A list of the code files created during the process.
SimValueSymbolDeclarations declaredSymbols_
A list of all symbols that are declared after the program code is ready.
unsigned maxInstructionsPerFile_
Maximum number of instructions per engine source code file, computed from the instruction width (bus ...
CompiledSimSymbolGenerator symbolGen_
The symbol generator.
int moveCounter_
How many moves have we been through with?
ProcedureBBRelations procedureBBRelations_
Basic blocks relations to procedures and vice versa.
FUResultWrites lastFUWrites_
Last known FU result writes.
CompiledSimCodeGenerator(const TTAMachine::Machine &machine, const TTAProgram::Program &program, const TTASimulationController &controller, bool fuResourceConflictDetection, bool handleCycleEnd, bool dynamicCompilation, bool basicBlockPerFile=false, bool functionPerFile=true, const TCEString &globalSymbolPrefix="")
StringSet declaredFunctions_
A set of all the declared functions.
void addDeclaredSymbol(const std::string &name, int width)
bool isProcedureBegin_
Are we at the beginning of a new procedure?
std::ostream * os_
Current output stream i.e. the above file.
virtual StringSet createdFiles() const
AddressMap bbStarts_
The basic block map referred by start of the block as a key.
static const char * COMPILED_SIM_SO_FLAGS
flags used when compiling .so files
static const char * COMPILED_SIM_CPP_FLAGS
cpp flags used for compiled simulation
std::string copyToBusCode() const
std::string basicBlockSymbol(InstructionAddress startAddress) const
std::string DAMemorySymbol(const TTAMachine::FunctionUnit &fu) const
std::string returnAddressSymbol(const TTAMachine::ControlUnit &gcu) const
std::string operationSymbol(const std::string &operationName, const TTAMachine::FunctionUnit &fu) const
std::string operationContextSymbol(const TTAMachine::FunctionUnit &fu) const
std::string busSymbol(const TTAMachine::Bus &bus) const
void enablePrefix(const std::string &prefix)
std::string portSymbol(const TTAMachine::Port &port) const
std::string moveOperandSymbol(const TTAProgram::Terminal &terminal, const TTAProgram::Move &move) const
std::string registerSymbol(const TTAProgram::Terminal &terminal) const
std::string FUResultSymbol(const TTAMachine::Port &port) const
std::string immediateRegisterSymbol(const TTAProgram::Terminal &terminal) const
std::string symbolDeclaration(const TTAMachine::FunctionUnit &fu)
std::string detectConflicts(const TTAMachine::HWOperation &op)
static std::string toString(const T &source)
static TCEString registerName(const TTAMachine::RegisterFile &rf, int index, char delim='.')
static std::vector< std::string > includeDirPaths()
static const std::string DIRECTORY_SEPARATOR
static int longestGuardLatency(const TTAMachine::Machine &mach)
static std::string createSimulationCode(const OperationDAG &dag, std::vector< std::string > *varReplacements=NULL)
Operation & operation(const char *name)
virtual OperationDAG & dag(int index) const
Definition Operation.cc:148
virtual int dagCount() const
Definition Operation.cc:134
int intValue() const
Definition SimValue.cc:895
unsigned int unsignedValue() const
Definition SimValue.cc:919
virtual int width() const
FunctionUnit * parentUnit() const
Definition BaseFUPort.cc:96
virtual int width() const
virtual int numberOfRegisters() const
virtual int width() const
int width() const
Definition Bus.cc:149
Guard * guard(int index) const
Definition Bus.cc:456
int guardCount() const
Definition Bus.cc:441
virtual TCEString name() const
SpecialRegisterPort * specialRegisterPort(int index) const
int globalGuardLatency() const
int specialRegisterPortCount() const
virtual AddressSpace * addressSpace() const
virtual HWOperation * operation(const std::string &name) const
virtual int operationCount() const
virtual int maxLatency() const
virtual FUPort * operationPort(const std::string &name) const
virtual int operationPortCount() const
virtual BaseFUPort * port(const std::string &name) const
virtual bool isInverted() const
virtual FUPort * port(int operand) const
const std::string & name() const
FunctionUnit * parentUnit() const
ComponentType * item(int index) const
virtual RegisterFileNavigator registerFileNavigator() const
Definition Machine.cc:450
virtual FunctionUnitNavigator functionUnitNavigator() const
Definition Machine.cc:380
virtual ImmediateUnitNavigator immediateUnitNavigator() const
Definition Machine.cc:416
virtual BusNavigator busNavigator() const
Definition Machine.cc:356
virtual ControlUnit * controlUnit() const
Definition Machine.cc:345
FUPort * port() const
virtual bool isInput() const
Definition Port.cc:298
virtual bool isOutput() const
Definition Port.cc:308
virtual int width() const =0
virtual std::string name() const
Definition Port.cc:141
virtual int guardLatency() const
const RegisterFile * registerFile() const
virtual int portCount() const
Definition Unit.cc:135
InstructionAddress location() const
virtual int instructionCount() const
virtual Instruction & instructionAtIndex(int index) const
TerminalImmediate & value() const
Definition Immediate.cc:103
const Terminal & destination() const
Definition Immediate.cc:92
Move & move(int i) const
Address address() const
Immediate & immediate(int i) const
const TTAMachine::Guard & guard() const
Definition MoveGuard.cc:86
MoveGuard & guard() const
Definition Move.cc:345
bool isUnconditional() const
Definition Move.cc:154
Terminal & source() const
Definition Move.cc:302
bool isTriggering() const
Definition Move.cc:284
Terminal & destination() const
Definition Move.cc:323
TCEString name() const
Definition Procedure.hh:66
Procedure & procedure(int index) const
Definition Program.cc:622
Instruction & lastInstruction() const
Definition Program.cc:463
Instruction & instructionAt(InstructionAddress address) const
Definition Program.cc:374
int procedureCount() const
Definition Program.cc:610
virtual const TTAMachine::HWOperation * hwOperation() const
virtual SimValue value() const
virtual const TTAMachine::FunctionUnit & functionUnit() const
Definition Terminal.cc:251
virtual bool isGPR() const
Definition Terminal.cc:107
virtual bool isImmediateRegister() const
Definition Terminal.cc:97
virtual const TTAMachine::Port & port() const
Definition Terminal.cc:378
virtual const TTAMachine::ImmediateUnit & immediateUnit() const
Definition Terminal.cc:240
virtual bool isFUPort() const
Definition Terminal.cc:118
virtual std::set< InstructionAddress > findProgramExitPoints(const TTAProgram::Program &program, const TTAMachine::Machine &machine) const
std::set< TCEString, CaseInsensitiveCmp > CIStringSet
A struct for tracking basic blocks and their relation to their procedures.
std::map< InstructionAddress, std::string > basicBlockFiles
Basic block starts and their corresponding .cpp files.
std::map< InstructionAddress, InstructionAddress > procedureStart
Procedure start per basic block starts.
BasicBlockStarts basicBlockStarts
All basic block start addresses per procedure start.