OpenASIP 2.2
Loading...
Searching...
No Matches
FUGen.cc
Go to the documentation of this file.
1/*
2 Copyright (c) 2017-2019 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 FUGen.cc
26 *
27 * @author Lasse Lehtonen 2017 (lasse.lehtonen-no.spam-tut.fi)
28 * @author Kati Tervo 2017-2019 (kati.tervo-no.spam-tuni.fi)
29 * @author Topi Leppänen 2019 (topi.leppanen-no.spam-tuni.fi)
30 */
31
32#include <regex>
33#include "IPXact.hh"
34#include "FUGen.hh"
35#include "ProGeTools.hh"
36#include "ContainerTools.hh"
37#include "ConstantNode.hh"
38#include "FUPort.hh"
39#include "FunctionUnit.hh"
40#include "HDBManager.hh"
41#include "HWOperation.hh"
42#include "NetlistPort.hh"
43#include "Operand.hh"
44#include "Operation.hh"
45#include "OperationIndex.hh"
46#include "OperationNode.hh"
47#include "OperationPool.hh"
48#include "ProGeTypes.hh"
49#include "TerminalNode.hh"
51#include "BinaryOps.hh"
52#include "OperationPimpl.hh"
53#include "Signal.hh"
54#include "SignalGroupTypes.hh"
55#include "NetlistPortGroup.hh"
56#include "MemoryBusInterface.hh"
57
58using namespace HDLGenerator;
59
60bool
61FUGen::hasToken(std::string line, std::string token) {
62 auto regex = std::regex("\\b" + token + "\\b");
63 return std::regex_search(line, regex);
64}
65
66std::string
67FUGen::replaceToken(std::string line, Replace replace) {
68 auto regex = std::regex("\\b" + replace.first + "\\b");
69 return std::regex_replace(line, regex, replace.second);
70}
71
75 return Language::VHDL;
76 } else {
77 return Language::Verilog;
78 }
79}
80
81std::deque<std::string>
82FUGen::readFile(std::string filename) {
83 std::deque<std::string> file;
84 // replace temps in operation implmentations and keep track of
85 // read temps.
86 if (filename == "") {
87 return file;
88 }
89
90 std::ifstream implStream(findAbsolutePath(filename));
91 for (std::string line; std::getline(implStream, line);) {
92 file.emplace_back(StringTools::stringToLower(line));
93 }
94 return file;
95}
96
97/**
98 * Searches for file in TCE folders and makes the path absolute.
99 */
100std::string
101FUGen::findAbsolutePath(std::string file) {
102 if (file.length() > 4 && file.substr(0, 4) == "tce:") {
103 file = file.substr(4);
104 }
105 std::vector<std::string> paths = Environment::hdbPaths();
106 for (auto file : options_.hdbList) {
107 paths.emplace_back(FileSystem::directoryOfPath(file));
108 }
109 return FileSystem::findFileInSearchPaths(paths, file);
110}
111
112std::string
114 if (stage == 0) {
115 return "operation_in";
116 } else {
117 return "operation_" + std::to_string(stage) + "_r";
118 }
119}
120
121std::string
123 if (stage == 0) {
124 return "load_" + triggerPort_ + "_in";
125 } else {
126 return "optrig_" + std::to_string(stage) + "_r";
127 }
128}
129
130std::string
131FUGen::opcodeConstant(std::string operation) {
132 return "op_" + operation + "_c";
133}
134
135std::string
136FUGen::operandSignal(std::string operation, int id) {
137 return operation + "_op" + std::to_string(id);
138}
139
140std::string
142 return "op" + std::to_string(id);
143}
144
145std::string
146FUGen::pipelineName(std::string port, int cycle) {
147 if (cycle == 0) {
148 return "data_" + port;
149 } else {
150 return "data_" + port + "_" + std::to_string(cycle) + "_r";
151 }
152}
153
154std::string
155FUGen::pipelineValid(std::string port, int cycle) {
156 return "data_" + port + "_" + std::to_string(cycle) + "_valid_r";
157}
158
159std::string
161 int id;
162 std::string op = node->referencedOperation().name();
164 if (nodeImplementations_.count(node->nodeID())) {
165 id = nodeImplementations_[node->nodeID()];
166 } else {
167 if (!subOpCount_.count(op)) {
168 subOpCount_[op] = 0;
169 }
170 id = subOpCount_[op];
171 subOpCount_[op] += 1;
172 nodeImplementations_[node->nodeID()] = id;
173 }
174 return "subop_" + op + "_" + std::to_string(id);
175}
176
177std::string
179 int id;
180 std::string op = dag->operation().name();
182 if (dagConstants_.count(node->nodeID())) {
183 id = dagConstants_[node->nodeID()].id;
184 } else {
185 if (!dagConstantCount_.count(op)) {
186 dagConstantCount_[op] = 0;
187 }
188 id = dagConstantCount_[op];
189 dagConstantCount_[op] += 1;
190 dagConstants_[node->nodeID()] = {op, node->value(), id};
191 }
192 return constantName(dagConstants_[node->nodeID()]);
193}
194
195std::string
197 return "dag_" + dagc.operation + "_" + std::to_string(dagc.id) + "_c";
198}
199
200int
202 OperationDAGNode& node, int id, OperationDAG* dag) {
203 auto operation = dynamic_cast<OperationNode*>(&node);
204 auto terminal = dynamic_cast<TerminalNode*>(&node);
205 auto constant = dynamic_cast<ConstantNode*>(&node);
206
207 OperationPool opPool;
208 std::string opName;
209 if (operation) {
210 opName = operation->referencedOperation().name();
211 } else if (terminal) {
212 opName = dag->operation().name();
213 } else if (constant) {
214 return MathTools::requiredBitsSigned(constant->value());
215 } else {
216 assert(false && "Shouldn't be possible to get here.");
217 }
218
219 opName = StringTools::stringToLower(opName);
220 Operation& op = opPool.operation(opName.c_str());
221 return op.operand(id).width();
222}
223
224/**
225 * Creates the header comment for fu.
226 */
227void
229 fu_.appendToHeader("Function Unit: " + fug_.name());
231 fu_.appendToHeader("Operations:");
232
233 if (adfFU_->operationCount() > 1) {
234 size_t maxOpNameLen = 0;
235 for (int i = 0; i < adfFU_->operationCount(); ++i) {
237 maxOpNameLen = std::max(maxOpNameLen, hwop->name().size());
238 }
239 int opDigits =
240 static_cast<int>(std::ceil(std::log10(adfFU_->operationCount())));
241 for (int i = 0; i < adfFU_->operationCount(); ++i) {
243 operations_.emplace_back(hwop->name());
244 }
245 std::sort(operations_.begin(), operations_.end());
246 int opcode = 0;
247 for (auto&& op : operations_) {
249 std::ostringstream comment;
250 comment << boost::format(
251 " %-" + std::to_string(maxOpNameLen) + "s : %" +
252 std::to_string(opDigits) + "s") %
253 op % std::to_string(opcode);
254 fu_.appendToHeader(comment.str());
255 opcode++;
256 }
257
258 } else {
260 operations_.emplace_back(hwop->name());
261 fu_.appendToHeader(" " + hwop->name() + " : 0");
262 }
264}
265
266void
268 std::string file, std::string format, bool isSynthesizable) {
269 file = findAbsolutePath(file);
270 const std::string DS = FileSystem::DIRECTORY_SEPARATOR;
271 std::string dir = options_.outputDirectory + DS;
272 if (isSynthesizable) {
273 dir += format == "VHDL" ? "vhdl" : "verilog";
274 } else {
275 dir += "blackbox" + DS;
276 dir += format == "VHDL simulation" ? "vhdl" : "verilog";
277 }
278 std::string target =
280 if (!FileSystem::fileExists(target)) {
282 FileSystem::copy(file, target);
283 }
284}
285
286/*
287 * Create actual HDL files.
288 */
289void
292 Path dir = Path(options_.outputDirectory) / std::string("vhdl");
293 FileSystem::createDirectory(dir.string());
294 Path file = dir / (fu_.name() + ".vhd");
295 std::ofstream ofs(file);
296 fu_.implement(ofs, Language::VHDL);
297 } else if (options_.language == ProGe::HDL::Verilog) {
298 Path dir = Path(options_.outputDirectory) / std::string("verilog");
299 FileSystem::createDirectory(dir.string());
300 Path file = dir / (fu_.name() + ".v");
301 std::ofstream ofs(file);
302 fu_.implement(ofs, Language::Verilog);
303 }
304
305 // Copy synthesis files and simulation models.
306 for (auto&& opInfo : fug_.operations()) {
307 std::string hdb = opInfo.hdb;
308 hdb = findAbsolutePath(hdb);
311 manager.OperationImplementationByID(opInfo.id);
312 for (auto&& r : opImpl.resources) {
313 for (size_t i = 0; i < r.simFiles.size(); ++i) {
314 copyImplementation(r.simFiles[i], r.simFormats[i], false);
315 }
316 for (size_t i = 0; i < r.synFiles.size(); ++i) {
317 copyImplementation(r.synFiles[i], r.synFormats[i], true);
318 }
319 }
320 }
321}
322
323/*
324 * Create ports that are always there.
325 */
326void
328 std::string resetPort;
329 if (ProGeTools::findInOptionList("active low reset", globalOptions_)) {
330 resetPort = "rstx";
331 } else {
332 resetPort = "rst";
333 }
334
335 fu_ << InPort("clk") << InPort(resetPort) << InPort("glock_in")
336 << OutPort("glockreq_out");
337
338 if (adfFU_->operationCount() > 1) {
339 fu_ << InPort("operation_in", opcodeWidth_, WireType::Vector);
340 }
341
342 // operand ports.
343 for (int i = 0; i < adfFU_->portCount(); ++i) {
344 TTAMachine::FUPort* adfPort =
345 static_cast<TTAMachine::FUPort*>(adfFU_->port(i));
346
347 if (adfPort->isInput()) {
348 fu_ << InPort(
349 "data_" + adfPort->name() + "_in", adfPort->width(),
350 WireType::Vector);
351 fu_ << InPort("load_" + adfPort->name() + "_in");
352 } else {
353 fu_ << OutPort(
354 "data_" + adfPort->name() + "_out", adfPort->width(),
355 WireType::Vector);
356 }
357 }
358
359 if (addressWidth_ > 0) {
360 fu_ << IntegerConstant("addrw_c", addressWidth_);
361 }
362}
363
364void
366 // Check that we can implement the FU.
367 // Cannot implement if FU has multioutput operation that has
368 // different latencies for the outputs.
369 for (auto&& op : operations_) {
371 int hwOpOperands = hwOp->operandCount();
372 int prevLatency = -1;
373 for (int operand = 0; operand < hwOpOperands; ++operand) {
374 TTAMachine::FUPort* fuPort = hwOp->port(operand + 1);
375
376 if (!fuPort->isOutput()) {
377 continue;
378 }
379
380 int latency = hwOp->latency(operand + 1);
381 if (prevLatency == -1) {
382 prevLatency = latency;
383 } else if (prevLatency != latency) {
384 // TODO: probably not true anymore, but needs to be tested
385 throw std::runtime_error(
386 "FUGen cannot implement multioutput operations (" + op +
387 ") which have different latencies for"
388 " its outputs.");
389 }
390 }
391 }
392}
393
394std::vector<FUGen::Replace>
395FUGen::buildReplaces(std::string name) {
396 OperationSchedule& schedule = scheduledOperations_[name];
397 std::string op = schedule.baseOp;
398 std::vector<FUGen::Replace> replaces;
399
400 replaces.emplace_back("glock", "glock_in");
401 replaces.emplace_back("trigger", triggerSignal(schedule.initialCycle));
402
403 for (auto&& operand : schedule.operands) {
404 int id = operand.id;
405 replaces.emplace_back(
406 operandPlaceholder(id), operandSignal(name, id));
407 }
408 for (int id : schedule.results) {
409 replaces.emplace_back(
410 operandPlaceholder(id), operandSignal(name, id));
411 }
412
413 if (!baseOperations_.count(op)) {
414 return replaces;
415 }
416
417 // Resources
418 for (auto&& r : baseOperations_[op].resources) {
419 std::string rName = StringTools::stringToLower(r.name);
420 if (!schedule.resourceOffsets.count(rName)) {
421 assert(false && "Couldn't find resource offset.");
422 }
423 int offset = schedule.resourceOffsets[rName];
424
425 for (int i = 0; i < r.count; ++i) {
426 int portID = i + 1;
427 int resID = i + offset + 1;
428 std::string file = findAbsolutePath(r.ipxact);
430 for (auto&& p : resource.ports) {
431 std::string pName = StringTools::stringToLower(p.name);
432 std::string replace = pName + "_" + std::to_string(portID);
433 std::string with =
434 rName + "_" + std::to_string(resID) + "_" + pName;
435
436 replaces.emplace_back(replace, with);
437 }
438 }
439 }
440
441 // Variables
442 for (auto&& v : baseOperations_[op].variables) {
443 std::string replica;
444 if (v.rename) {
445 replica = name + "_" + v.name;
446 replaces.emplace_back(v.name, replica);
447 } else {
448 bool nameFound = false;
449 for (auto&& old_var : renamedVariables_) {
450 if (old_var.name == v.name) {
451 nameFound = true;
452 }
453 }
454 if (nameFound) {
455 continue;
456 }
457 replica = v.name;
458 }
459 HDB::Variable var = {replica, v.width, v.type, v.rename};
460 renamedVariables_.emplace_back(var);
461 }
462 // Global signals
463 for (auto&& s : baseOperations_[op].globalsignals) {
464 std::string replica;
465 // Not renaming the signals allows them to be directly shared between
466 // ops
467 if (s.rename) {
468 replica = name + "_" + s.name;
469 replaces.emplace_back(s.name, replica);
470 } else {
471 // don't add the same signal multiple times
472 bool nameFound = false;
473 for (auto&& old_var : renamedGlobalSignals_) {
474 if (old_var.name == s.name) {
475 nameFound = true;
476 }
477 }
478 if (nameFound) {
479 continue;
480 }
481 replica = s.name;
482 }
483
484 HDB::Variable var = {replica, s.width, s.type, s.rename};
485 renamedGlobalSignals_.emplace_back(var);
486 }
487
488 return replaces;
489}
490
491void
493 std::string filename, std::string opName, std::deque<std::string>& sink) {
494 // replace temps in operation implmentations and keep track of
495 // read temps.
496 std::ifstream implStream(findAbsolutePath(filename));
497 for (std::string line; std::getline(implStream, line);) {
498 line = StringTools::stringToLower(line);
499 for (auto&& p : replacesPerOp_[opName]) {
500 line = replaceToken(line, p);
501 }
502
503 sink.emplace_back(line);
504 }
505}
506
507/**
508 * (Ugly) heuristics for identifying an LSU data memory port.
509 *
510 * The identification is made by partial match of the port's name.
511 *
512 * @param portName The port name given in HDB.
513 * @return True if the port is LSU data memory port.
514 */
515bool
516FUGen::isLSUDataPort(const std::string& portName) {
517 if (!adfFU_->hasAddressSpace()) {
518 return false;
519 }
520
521 static const std::set<std::string> magicWords{
522 "avalid", "aready", "aaddr", "awren", "astrb",
523 "rvalid", "rready", "rdata", "adata"};
524
525 for (auto magicWord : magicWords) {
526 if (portName.find(magicWord) != std::string::npos) {
527 //Assume the FU is an LSU
528 isLSU_ = true;
529 return true;
530 }
531 }
532
533 return false;
534}
535/**
536 * (Ugly) heuristics for mapping FUGen generated LSU signal types
537 *
538 * @param portName Name of the port
539 * @return SignalType
540 */
542FUGen::inferLSUSignal(const std::string& portName) const {
543 size_t pos = 0;
544 using SigT = ProGe::SignalType;
545 if (((pos = portName.find("avalid")) != std::string::npos)) {
546 return SigT::AVALID;
547 } else if (((pos = portName.find("aready")) != std::string::npos)) {
548 return SigT::AREADY;
549 } else if (((pos = portName.find("aaddr")) != std::string::npos)) {
550 return SigT::AADDR;
551 } else if (((pos = portName.find("awren")) != std::string::npos)) {
552 return SigT::AWREN;
553 } else if (((pos = portName.find("astrb")) != std::string::npos)) {
554 return SigT::ASTRB;
555 } else if (((pos = portName.find("rvalid")) != std::string::npos)) {
556 return SigT::RVALID;
557 } else if (((pos = portName.find("rready")) != std::string::npos)) {
558 return SigT::RREADY;
559 } else if (((pos = portName.find("rdata")) != std::string::npos)) {
560 return SigT::RDATA;
561 } else if (((pos = portName.find("adata")) != std::string::npos)) {
562 return SigT::ADATA;
563 }
564 return SigT::UNDEFINED;
565}
566
567void
569 std::set<std::pair<ProGe::NetlistPort*, ProGe::NetlistPort*>> lsuPorts;
570 Replace replaceAddress = {"addrw_c", std::to_string(addressWidth_)};
571 for (auto&& ei : extIfaces_) {
573 for (auto&& port : info.ports) {
574 std::string width = port.width;
575 if (addressWidth_ > 0) {
576 width = replaceToken(width, replaceAddress);
577 }
579 if (port.direction == "out") {
580 fu_ << OutPort(
581 port.name, width, HDLGenerator::WireType::Vector);
582 extOutputs_.emplace(port.name, port.defaultValue);
584 } else {
585 fu_ << InPort(
586 port.name, width, HDLGenerator::WireType::Vector);
587 extInputs_.emplace(port.name);
589 }
590
591 std::string extName = moduleName_ + "_" + port.name;
592 ProGe::NetlistPort* ext = NULL;
594 port.name, width, ProGe::DataType::BIT_VECTOR, dir,
596 if (isLSUDataPort(extName) && !genIntegrator) {
597 ext = new ProGe::NetlistPort(
598 extName, width, ProGe::DataType::BIT_VECTOR, dir, *core_,
599 inferLSUSignal(extName));
600 lsuPorts.insert(std::make_pair(internal, ext));
601 } else {
602 ext = new ProGe::NetlistPort(
603 extName, width, ProGe::DataType::BIT_VECTOR, dir, *core_);
604 core_->netlist().connect(*ext, *internal);
605 }
606 }
607 }
608 if (lsuPorts.empty()) {
609 return;
610 }
611 // Handle LSU data ports.
612 TCEString asName("");
613 if (adfFU_->hasAddressSpace()) {
614 asName = adfFU_->addressSpace()->name();
615 }
616
617 ProGe::NetlistPortGroup* dmemPortGroup = nullptr;
618 for (auto portPair : lsuPorts) {
619 dmemPortGroup =
620 dmemPortGroup
621 ? dmemPortGroup
624 dmemPortGroup->addPort(*portPair.second);
625 core_->netlist().connect(*portPair.first, *portPair.second);
626 }
627 if (dmemPortGroup != nullptr) {
628 core_->addPortGroup(dmemPortGroup);
629 dmemPortGroup = nullptr;
630 }
631}
632
633void
635 std::map<std::string, int> instantiatedResources;
636 for (auto&& rs : baseOperations_) {
637 for (auto&& r : rs.second.resources) {
638 if (!instantiatedResources.count(r.name)) {
639 instantiatedResources[r.name] = 0;
640 }
641 std::string file = findAbsolutePath(r.ipxact);
642 std::string rName = StringTools::stringToLower(r.name);
643 int rCount = resourceCount_[rName];
644
645 for (int i = instantiatedResources[r.name]; i < rCount; ++i) {
646 auto module = ipxact::parseComponent(file);
647 Module resource(module, i + 1);
648 resource.set_prefix(rName);
649 fu_ << resource;
650 for (auto&& p : module.ports) {
651 std::string wName = StringTools::stringToLower(
652 rName + "_" + std::to_string(i + 1) + "_" + p.name);
653 if (p.vector) {
654 fu_ << Wire(wName, p.width);
655 } else {
656 fu_ << Wire(wName);
657 }
658
659 if (p.direction == "in") {
660 resourceInputs_.emplace_back(wName);
661 } else {
662 resourceOutputs_.emplace_back(wName);
663 }
664 }
665 }
666 instantiatedResources[r.name] = rCount;
667 }
668 }
669}
670
671void
673 Asynchronous operationCp("operations_actual_cp");
674 CodeBlock defaultValues;
675 CodeBlock defaultSnippets;
676 CodeBlock triggeredSnippets;
677
678 for (auto&& d : extOutputs_) {
679 operationCp << DefaultAssign(d.first, d.second);
680 }
681
682 for (std::string signal : resourceInputs_) {
683 // Zero initialize this configuration to avoid simulation warnings
684 if (isLSU_ && minLatency_ < 3) {
685 operationCp << DefaultAssign(signal, "0");
686 } else {
687 operationCp << DefaultAssign(signal, "-");
688 }
689 }
690
691 for (auto&& pair : dagConstants_) {
692 auto spec = pair.second;
693 std::string name = constantName(spec);
694 int width = MathTools::requiredBitsSigned(spec.value);
695 fu_ << BinaryConstant(name, width, spec.value);
696 }
697
698 for (std::string signal : resourceOutputs_) {
699 operationCp.reads(signal);
700 }
701
702 for (std::string signal : extInputs_) {
703 operationCp.reads(signal);
704 }
705
706 std::set<std::string> defaultStatements;
707 for (auto&& pair : scheduledOperations_) {
708 auto schedule = pair.second;
709 std::string name = pair.first;
710
711 for (int id : schedule.results) {
712 std::string source = operandSignal(name, id);
713 // Zero initialize this configuration to avoid simulation warnings
714 if (isLSU_ && minLatency_ < 3) {
715 defaultValues.append(DefaultAssign(source, "0"));
716 } else {
717 defaultValues.append(DefaultAssign(source, "-"));
718 }
719 }
720
721 for (auto&& operand : schedule.operands) {
722 int id = operand.id;
723
724 std::string source = operand.signalName;
725 std::string destination = operandSignal(name, id);
726 if (operand.portWidth > operand.operandWidth) {
727 defaultValues.append(Assign(
728 destination,
729 Splice(source, operand.operandWidth - 1, 0)));
730 } else if (operand.portWidth < operand.operandWidth) {
731 defaultValues.append(Assign(
732 destination,
733 Ext(source, operand.operandWidth, operand.portWidth)));
734 } else {
735 defaultValues.append(Assign(destination, LHSSignal(source)));
736 }
737
738 if (!operand.isOutput) {
739 operationCp.reads(destination);
740 }
741 }
742
743 replacesPerOp_[name] = buildReplaces(name);
744
745 std::string baseOp = schedule.baseOp;
746 auto& initial = baseOperations_[baseOp].initial;
747 if (!initial.empty()) {
748 prepareSnippet(name, initial, defaultSnippets, defaultStatements);
749 }
750 }
751
752 for (int cycle = 0; cycle <= maxLatency_; ++cycle) {
753 Switch opSwitch(LHSSignal(opcodeSignal(cycle)));
754 bool emptySwitch = true;
755
756 for (std::string op : operations_) {
757 CodeBlock onTrigger;
758 bool emptyBlock = true;
759 auto schedule = scheduledOperations_[op];
760
761 std::vector<std::string> schedules = schedule.subOperations;
762 schedules.push_back(op);
763
764 for (std::string subop : schedules) {
765 auto schedule = scheduledOperations_[subop];
766 std::string baseOp = schedule.baseOp;
767 if (schedule.initialCycle == cycle) {
768 auto& impl = baseOperations_[baseOp].implementation;
769 if (!impl.empty()) {
770 std::set<std::string> statements;
771 prepareSnippet(subop, impl, onTrigger, statements);
772 }
773 emptyBlock = false;
774 }
775
776 if (schedule.finalCycle == cycle) {
777 auto& postOp = baseOperations_[baseOp].postOp;
778 if (!postOp.empty()) {
779 std::set<std::string> statements;
780 prepareSnippet(subop, postOp, onTrigger, statements);
781 }
782 emptyBlock = false;
783 }
784 }
785
786 if (emptyBlock) {
787 continue;
788 }
789
790 if (operations_.size() > 1) {
791 Case opCase(opcodeConstant(op));
792 opCase << onTrigger;
793 opSwitch.addCase(opCase);
794 emptySwitch = false;
795 } else {
796 If opIf(
797 Equals(
799 onTrigger);
800 triggeredSnippets.append(opIf);
801 }
802 }
803
804 if (!emptySwitch) {
805 opSwitch.addCase(DefaultCase());
806 If opIf(
808 opSwitch);
809 triggeredSnippets.append(opIf);
810 }
811 }
812
813 if (useGlock_) {
814 operationCp.reads("glock_in");
815 }
816
817 if (useGlockRequest_) {
818 defaultValues.append(Assign("glockreq", BinaryLiteral("0")));
819 }
820
821 for (auto&& v : renamedVariables_) {
822 int w = std::stoi(v.width);
823 if (v.type == "Logic") {
824 operationCp.addVariable(LogicVariable(v.name, w));
825 } else if (v.type == "Unsigned") {
826 operationCp.addVariable(UnsignedVariable(v.name, w));
827 } else {
828 operationCp.addVariable(SignedVariable(v.name, w));
829 }
830 // Zero initialize this configuration to avoid simulation warnings
831 if (isLSU_ && minLatency_ < 3) {
832 operationCp << DefaultAssign(v.name, "0");
833 } else {
834 operationCp << DefaultAssign(v.name, "-");
835 }
836 }
837 for (auto&& s : renamedGlobalSignals_) {
838 int w = std::stoi(s.width);
839 fu_ << Wire(s.name, w); // creates the signal declaration
840 operationCp.reads(s.name); // adds it to sensitivity list
841 // Zero initialize this configuration to avoid simulation warnings
842 if (isLSU_ && minLatency_ < 3) {
843 operationCp << DefaultAssign(s.name, "0");
844 } else {
845 operationCp << DefaultAssign(s.name, "-");
846 }
847 }
848
849 operationCp << defaultValues << defaultSnippets << triggeredSnippets;
850 behaviour_ << operationCp;
851}
852
853void
855 std::string name, std::deque<std::string> statements, CodeBlock& sink,
856 std::set<std::string>& addedStatements) {
857 std::deque<std::string> snippet;
858 std::set<std::string> lines;
859 for (std::string line : statements) {
860 std::string replaced = StringTools::stringToLower(line);
861 for (Replace rep : replacesPerOp_[name]) {
862 replaced = replaceToken(replaced, rep);
863 }
864
865 // Cosmetic improvement: do not repeat statements in the same context
866 if (addedStatements.count(replaced)) {
867 continue;
868 }
869 lines.insert(replaced);
870
871 snippet.push_back(replaced);
872
873 if (hasToken(line, "glockreq")) {
874 useGlockRequest_ = true;
875 }
876 if (hasToken(line, "glock")) {
877 useGlock_ = true;
878 }
879 }
880
881 for (std::string line : lines) {
882 addedStatements.insert(line);
883 }
884
885 sink.append(HDLOperation(name, snippet, selectedLanguage()));
886}
887
888void
890 // Create lock request wires
891 if (useGlockRequest_) {
892 fu_ << Wire("glockreq");
893 behaviour_ << Assign("glockreq_out", LHSSignal("glockreq"));
894 } else {
895 behaviour_ << Assign("glockreq_out", BinaryLiteral("0"));
896 }
897
898 // Finalize and set global options.
899 fu_ << behaviour_;
900 for (auto&& option : globalOptions_) {
901 fu_ << Option(option);
902 }
903}
904
905void
907 OperationPool opPool;
908 std::vector<std::string> dagOperations;
909
910 for (auto&& op : fug_.operations()) {
911 BaseOperation opInfo;
912 opInfo.name = op.operationName;
913 opInfo.latency = op.latency;
914
915 std::string opHDB = findAbsolutePath(op.hdb);
916 HDB::CachedHDBManager& manager =
919 manager.OperationImplementationByID(op.id);
920
921 std::string implFile;
923 opInfo.variables = opImpl.vhdlVariables;
924 opInfo.globalsignals = opImpl.vhdlGlobalSignals;
925 opInfo.implementation = readFile(opImpl.implFileVhdl);
926 opInfo.initial = readFile(opImpl.initialImplFileVhdl);
927 opInfo.postOp = readFile(opImpl.postOpImplFileVhdl);
928 } else {
929 if (opImpl.implFileVerilog == "") {
930 const std::string msg =
931 "Cannot generate operation \"" + opInfo.name +
932 "\" due to missing verilog operation implementation";
933 throw std::runtime_error(msg);
934 }
935 opInfo.variables = opImpl.verilogVariables;
936 opInfo.globalsignals = opImpl.verilogGlobalSignals;
937 opInfo.implementation = readFile(opImpl.implFileVerilog);
938 opInfo.initial = readFile(opImpl.initialImplFileVerilog);
939 opInfo.postOp = readFile(opImpl.postOpImplFileVerilog);
940 }
941 opInfo.resources = opImpl.resources;
942
943 implLatency_[opInfo.name] = op.latency;
944
945 baseOperations_.emplace(opInfo.name, opInfo);
946
947 std::string ifPath = opImpl.absBusDefFile;
948 if (!ifPath.empty()) {
949 extIfaces_.insert(findAbsolutePath(ifPath));
950 }
951 }
952
953 maxLatency_ = 0;
954 minLatency_ = INT_MAX;
955 for (auto&& op : operations_) {
957 int hwOpOperands = hwOp->operandCount();
958 OperationPool opPool;
959
960 for (int operand = 0; operand < hwOpOperands; ++operand) {
961 TTAMachine::FUPort* fuPort = hwOp->port(operand + 1);
962
963 if (fuPort->isOutput()) {
964 std::string portName = fuPort->name();
965 int latency = hwOp->latency(operand + 1);
966
967 auto search = operationCycles_.find(op);
968 if (search != operationCycles_.end()) {
969 operationCycles_[op] = std::max(search->second, latency);
970 } else {
971 operationCycles_[op] = latency;
972 }
973 maxLatency_ = std::max(maxLatency_, latency);
974 minLatency_ = std::min(minLatency_, latency);
975 }
976 }
977
978 if (baseOperations_.find(op) == baseOperations_.end()) {
979 dagOperations.emplace_back(op);
980 }
981 }
982
983 for (auto&& op : dagOperations) {
984 int dagCount;
985 {
986 OperationPool opPool;
987 Operation& osalOp = opPool.operation(op.c_str());
988 dagCount = osalOp.dagCount();
989 }
990
991 if (dagCount < 1) {
992 throw std::runtime_error(op + " has no dags to implement.");
993 }
994
995 bool implementable = false;
996 for (int i = 0; i < dagCount; ++i) {
997 OperationDAG* dagPtr;
998 {
999 OperationPool opPool;
1000 OperationIndex& index = opPool.index();
1001 Operation* osalOp = index.effectiveOperation(op);
1002 dagPtr = &osalOp->dag(i);
1003 }
1004
1006 *dagPtr, fug_.operations(), nullptr)) {
1007 continue;
1008 } else {
1009 implementable = true;
1010 implementapleDAGs_[op] = dagPtr;
1011 implLatency_[op] =
1013 break;
1014 }
1015 }
1016
1017 if (!implementable) {
1018 throw std::runtime_error(op + " has no valid dags to implement.");
1019 }
1020 }
1021}
1022
1025 OperationDAG* dag, OperationDAGEdge* edge, bool isOutput) {
1026 OperationDAGNode& srcNode = dag->tailNode(*edge);
1027 int srcID = edge->srcOperand();
1028 int srcWidth = DAGNodeOperandWidth(srcNode, srcID, dag);
1029
1030 OperationDAGNode& dstNode = dag->headNode(*edge);
1031 int dstID = edge->dstOperand();
1032 int dstWidth = DAGNodeOperandWidth(dstNode, dstID, dag);
1033 auto dstTerminal = dynamic_cast<TerminalNode*>(&dstNode);
1034 if (dstTerminal) {
1035 dstID = dstTerminal->operandIndex();
1036 }
1037
1038 auto operation = dynamic_cast<OperationNode*>(&srcNode);
1039 auto terminal = dynamic_cast<TerminalNode*>(&srcNode);
1040 auto constant = dynamic_cast<ConstantNode*>(&srcNode);
1041
1042 std::string signalName;
1043 if (operation) {
1044 std::string srcOp = subOpName(operation);
1045 srcOp = StringTools::stringToLower(srcOp);
1046 signalName = operandSignal(srcOp, srcID);
1047 } else if (terminal) {
1048 std::string srcOp = dag->operation().name();
1049 srcOp = StringTools::stringToLower(srcOp);
1050 srcID = terminal->operandIndex();
1051 signalName = operandSignal(srcOp, srcID);
1052 } else if (constant) {
1053 signalName = constantName(constant, dag);
1054 } else {
1055 assert(false && "It shouldn't be possible to get here.");
1056 }
1057
1059 dstID, srcWidth, dstWidth, signalName, isOutput};
1060 return conn;
1061}
1062
1063void
1065 std::set<std::string> instantiatedWires;
1066 for (auto&& op : operations_) {
1067 OperationSchedule schedule;
1068 schedule.baseOp = op;
1069
1070 if (frontRegistered_) {
1071 schedule.initialCycle = operationCycles_[op] - implLatency_[op];
1072 schedule.finalCycle = operationCycles_[op];
1073 } else if (middleRegistered_) {
1074 schedule.initialCycle =
1075 operationCycles_[op] - implLatency_[op] - 1;
1076 schedule.finalCycle = schedule.initialCycle + implLatency_[op];
1077 } else { // Back-registered
1078 schedule.initialCycle = 0;
1079 schedule.finalCycle = implLatency_[op];
1080 }
1081 assert(
1082 schedule.initialCycle >= 0 &&
1083 "Failure likely due to mis-selected operation implementation");
1084
1085 OperationPool opPool;
1086 Operation& osalOp = opPool.operation(op.c_str());
1088 for (int i = 1; i <= hwOp->operandCount(); ++i) {
1089 Operand& osalOperand = osalOp.operand(i);
1090 TTAMachine::FUPort* fuPort = hwOp->port(i);
1091 std::string port = fuPort->name();
1092 int width = osalOperand.width();
1093 int accessCycle;
1094 ProGe::Direction dir;
1095
1096 OperandConnection oper = {
1097 i, fuPort->width(), width,
1098 pipelineName(port, schedule.initialCycle), false};
1099 if (osalOperand.isInput()) {
1100 accessCycle = schedule.initialCycle;
1101 schedule.operands.push_back(oper);
1103 } else {
1104 accessCycle = operationCycles_[op] - schedule.finalCycle;
1105 schedule.results.insert(i);
1107 OutputConnection out = {
1108 width, i, schedule.finalCycle, accessCycle, op};
1109 portInputs_.emplace(port, out);
1110 }
1111
1112 assert(
1113 accessCycle >= 0 &&
1114 "Failure likely due to mis-selected operation "
1115 "implementation");
1116
1117 std::string newWire = operandSignal(op, i);
1118 if (!instantiatedWires.count(newWire)) {
1119 fu_ << Wire(newWire, width);
1120 instantiatedWires.insert(newWire);
1121 }
1122
1123 if (pipelineLength_.find(port) == pipelineLength_.end()) {
1124 pipelineLength_[port] = accessCycle;
1125 } else {
1126 pipelineLength_[port] =
1127 std::max(accessCycle, pipelineLength_[port]);
1128 }
1129
1130 if (portDirection_.find(port) == portDirection_.end()) {
1131 portDirection_[port] = dir;
1132 } else {
1133 if (portDirection_[port] != dir) {
1134 throw std::runtime_error(
1135 "Unsupported bidirectional port " + port +
1136 ", aborting.");
1137 }
1138 }
1139 }
1140
1141 if (baseOperations_.find(op) != baseOperations_.end()) {
1142 // No need to arbitrate between suboperations here,
1143 // just use the first resource
1144 for (auto&& res : baseOperations_[op].resources) {
1145 std::string resName = StringTools::stringToLower(res.name);
1146 schedule.resourceOffsets[resName] = 0;
1147 resourceCount_[res.name] =
1148 std::max(resourceCount_[res.name], res.count);
1149 }
1150 } else {
1151 assert(implementapleDAGs_.find(op) != implementapleDAGs_.end());
1152 auto dag = implementapleDAGs_[op];
1153 std::unordered_map<std::string, int> dagResourceCount;
1154
1155 for (int n = 0; n < dag->nodeCount(); ++n) {
1156 OperationSchedule subopSchedule;
1157 OperationDAGNode& node = dag->node(n);
1158
1159 OperationNode* operationNode =
1160 dynamic_cast<OperationNode*>(&node);
1161
1162 if (!operationNode) {
1163 continue;
1164 }
1165
1166 std::string subOp =
1167 operationNode->referencedOperation().name();
1168 subOp = StringTools::stringToLower(subOp);
1169
1170 std::string name = subOpName(operationNode);
1171 schedule.subOperations.push_back(name);
1172 subopSchedule.baseOp = subOp;
1173 // Start counting subopcycles from the upper op's initCycle.
1174 subopSchedule.initialCycle =
1175 schedule.initialCycle +
1177 *dag, node, implLatency_, false);
1178 subopSchedule.finalCycle =
1179 subopSchedule.initialCycle + implLatency_[subOp];
1180
1181 for (auto&& input : dag->inEdges(node)) {
1182 int dstID = input->dstOperand();
1183 int width = DAGNodeOperandWidth(
1184 dag->headNode(*input), dstID, dag);
1185
1186 std::string newWire = operandSignal(name, dstID);
1187 if (!instantiatedWires.count(newWire)) {
1188 fu_ << Wire(newWire, width);
1189 instantiatedWires.insert(newWire);
1190 }
1191
1192 subopSchedule.operands.push_back(
1193 subOpConnection(dag, input, false));
1194 }
1195
1196 for (auto&& output : dag->outEdges(node)) {
1197 int srcID = output->srcOperand();
1198 int width = DAGNodeOperandWidth(
1199 dag->tailNode(*output), srcID, dag);
1200
1201 std::string newWire = operandSignal(name, srcID);
1202 if (!instantiatedWires.count(newWire)) {
1203 fu_ << Wire(newWire, width);
1204 instantiatedWires.insert(newWire);
1205 }
1206
1207 subopSchedule.results.insert(srcID);
1208
1209 auto terminal =
1210 dynamic_cast<TerminalNode*>(&dag->headNode(*output));
1211
1212 if (terminal) {
1213 schedule.operands.push_back(
1214 subOpConnection(dag, output, true));
1215 }
1216 }
1217
1218 // TODO: make sure same resource doesn't get used by different
1219 // operations on _different_ pipeline cycles
1220 for (auto&& r : baseOperations_[subOp].resources) {
1221 std::string resName = StringTools::stringToLower(r.name);
1222 if (!dagResourceCount.count(resName)) {
1223 dagResourceCount[resName] = 0;
1224 }
1225 subopSchedule.resourceOffsets[resName] =
1226 dagResourceCount[resName];
1227 dagResourceCount[resName] += r.count;
1228 }
1229
1230 scheduledOperations_[name] = subopSchedule;
1231 }
1232
1233 for (auto&& d : dagResourceCount) {
1234 resourceCount_[d.first] =
1235 std::max(resourceCount_[d.first], d.second);
1236 }
1237 }
1238 scheduledOperations_[op] = schedule;
1239 }
1240}
1241
1242void
1244 std::vector<std::string> inOperands;
1245 std::unordered_set<std::string> registeredInOperands;
1246 std::unordered_map<std::string, std::string> currentName;
1247 std::unordered_map<std::string, int> portWidth;
1248
1249 for (int i = 0; i < adfFU_->portCount(); ++i) {
1250 TTAMachine::FUPort* adfPort =
1251 static_cast<TTAMachine::FUPort*>(adfFU_->port(i));
1252 portWidth[adfPort->name()] = adfPort->width();
1253
1254 if (adfPort->isInput()) {
1255 currentName[adfPort->name()] = "data_" + adfPort->name() + "_in";
1256
1257 if (adfPort->isTriggering()) {
1258 triggerPort_ = adfPort->name();
1259 }
1260
1261 inOperands.emplace_back(adfPort->name());
1262 if (!adfPort->noRegister()) {
1263 registeredInOperands.emplace(adfPort->name());
1264 }
1265
1266 std::string name = pipelineName(adfPort->name(), 0);
1267 fu_ << Wire(name, adfPort->width());
1268 } else {
1269 currentName[adfPort->name()] = "data_" + adfPort->name() + "_out";
1270 }
1271 }
1272
1274 for (auto&& p : inOperands) {
1275 std::string newName = "data_" + p + "_gated";
1276 auto gate = LHSSignal(currentName[p]) &
1277 Sext("load_" + p + "_in", portWidth[p], 1);
1278 behaviour_ << Assign(newName, gate);
1279 currentName[p] = newName;
1280 fu_ << Wire(newName, portWidth[p]);
1281 }
1282 }
1283
1284 for (auto&& p : inOperands) {
1285 std::string dataPort = currentName[p];
1286 std::string output = pipelineName(p, 0);
1287 if (p != triggerPort_ &&
1288 registeredInOperands.find(p) != registeredInOperands.end()) {
1289 std::string loadPort = "load_" + p + "_in";
1290 std::string shadowReg = "shadow_" + p + "_r";
1291
1292 fu_ << Register(shadowReg, portWidth[p], ResetOption::Optional);
1293
1294 auto noLock = Equals(LHSSignal("glock_in"), BinaryLiteral("0"));
1295 auto portLoad = Equals(LHSSignal(loadPort), BinaryLiteral("1"));
1296 If iffi(
1297 noLock && portLoad, Assign(shadowReg, LHSSignal(dataPort)));
1298
1299 behaviour_ << (Synchronous("shadow_" + p + "_sp") << iffi);
1300
1301 auto triggerLoad = Equals(
1302 LHSSignal("load_" + triggerPort_ + "_in"),
1303 BinaryLiteral("1"));
1304 If cpIf(
1305 triggerLoad && portLoad,
1306 Assign(output, LHSSignal("data_" + p + "_in")));
1307 cpIf.elseClause(Assign(output, LHSSignal(shadowReg)));
1308
1309 behaviour_ << (Asynchronous("shadow_" + p + "_cp") << cpIf);
1310 } else {
1311 behaviour_ << Assign(output, LHSSignal(dataPort));
1312 }
1313 }
1314}
1315
1316void
1318 CodeBlock pipelineAssignments;
1319 CodeBlock firstStage;
1320
1321 // Pipeline for opcode and trigger
1322 // TODO: This might generate some unnecessary registers for some FUs,
1323 // but these should be removed by the synthesis tool
1324 for (int i = 0; i < maxLatency_; ++i) {
1325 if (operations_.size() > 1) {
1326 std::string prevOpcode = opcodeSignal(i);
1327 std::string nextOpcode = opcodeSignal(i + 1);
1328 addRegisterIfMissing(nextOpcode, opcodeWidth_, WireType::Vector);
1329 if (i == 0) {
1330 firstStage.append(Assign(nextOpcode, LHSSignal(prevOpcode)));
1331 } else {
1332 pipelineAssignments.append(
1333 Assign(nextOpcode, LHSSignal(prevOpcode)));
1334 }
1335 }
1336 std::string prevTrigger = triggerSignal(i);
1337 std::string nextTrigger = triggerSignal(i + 1);
1338 addRegisterIfMissing(nextTrigger, 1);
1339 pipelineAssignments.append(
1340 Assign(nextTrigger, LHSSignal(prevTrigger)));
1341 }
1342
1343 // Pipelines for operand data
1344 for (int i = 0; i < adfFU_->portCount(); ++i) {
1345 auto port = adfFU_->port(i);
1346 int length = pipelineLength_[port->name()];
1347 int width = port->width();
1348 if (portDirection_[port->name()] != ProGe::Direction::IN) {
1349 continue;
1350 }
1351
1352 for (int i = 0; i < length; ++i) {
1353 std::string prevReg = pipelineName(port->name(), i);
1354 std::string nextReg = pipelineName(port->name(), i + 1);
1355 addRegisterIfMissing(nextReg, width, WireType::Vector);
1356 if (i == 0) {
1357 firstStage.append(Assign(nextReg, LHSSignal(prevReg)));
1358 } else {
1359 pipelineAssignments.append(
1360 Assign(nextReg, LHSSignal(prevReg)));
1361 }
1362 }
1363 }
1364
1365 If operationTrigger(
1366 Equals(LHSSignal(triggerSignal(0)), BinaryLiteral("1")), firstStage);
1367 pipelineAssignments.append(operationTrigger);
1368 If glock_low(
1369 Equals(LHSSignal("glock_in"), BinaryLiteral("0")),
1370 pipelineAssignments);
1371 Synchronous pipeline("input_pipeline_sp");
1372 pipeline << glock_low;
1373 behaviour_ << pipeline;
1374}
1375
1376void
1377FUGen::addRegisterIfMissing(std::string name, int width, WireType wt) {
1379 fu_ << Register(name, width, wt, ResetOption::Optional);
1380 registers_.emplace_back(name);
1381 }
1382}
1383
1384void
1386 CodeBlock outputPipeline;
1387 CodeBlock lastStage;
1388
1389 for (int i = 0; i < adfFU_->portCount(); ++i) {
1390 auto port = adfFU_->port(i);
1391 int length = pipelineLength_[port->name()];
1392 int width = port->width();
1393 if (portDirection_[port->name()] != ProGe::Direction::OUT) {
1394 continue;
1395 }
1396
1397 auto inputs = portInputs_.equal_range(port->name());
1398 for (int cycle = length; cycle >= 0; --cycle) {
1399 bool cycleActive = false;
1400
1401 std::string nextReg = pipelineName(port->name(), cycle);
1402 std::string prevReg = pipelineName(port->name(), cycle + 1);
1403 std::string valid = pipelineValid(port->name(), cycle);
1404
1405 if (cycle == 0) {
1406 fu_ << Wire(nextReg, width, WireType::Vector);
1407 } else {
1408 outputPipeline.append(Assign(valid, BinaryLiteral("1")));
1409 addRegisterIfMissing(nextReg, width, WireType::Vector);
1410 addRegisterIfMissing(valid, 1);
1411 }
1412
1413 // TODO: build a mux if all operations come from the same cycle
1414 If validOperations(BinaryLiteral("1"), DefaultAssign("dummy"));
1415 for (auto it = inputs.first; it != inputs.second; ++it) {
1416 auto connection = it->second;
1417 if (connection.pipelineStage != cycle) {
1418 continue;
1419 }
1420
1421 Equals triggered(
1422 LHSSignal(triggerSignal(connection.sourceCycle)),
1423 BinaryLiteral("1"));
1424 LogicalAnd active(
1425 Equals(
1426 LHSSignal(opcodeSignal(connection.sourceCycle)),
1427 LHSSignal(opcodeConstant(connection.operation))),
1428 triggered);
1429
1430 Ext source(
1431 operandSignal(connection.operation, connection.operandID),
1432 width, connection.operandWidth);
1433
1434 if (cycleActive) {
1435 if (operations_.size() == 1) {
1436 validOperations.elseIfClause(
1437 triggered, Assign(nextReg, source));
1438 } else {
1439 validOperations.elseIfClause(
1440 active, Assign(nextReg, source));
1441 }
1442 } else {
1443 if (operations_.size() == 1) {
1444 validOperations =
1445 If(triggered, Assign(nextReg, source));
1446 } else {
1447 validOperations = If(active, Assign(nextReg, source));
1448 }
1449 }
1450 cycleActive = true;
1451 }
1452
1453 bool skip_last_assign = false;
1454 // No need for this on first cycle
1455 if (cycle != length) {
1456 std::string prevValid =
1457 pipelineValid(port->name(), cycle + 1);
1458
1459 Equals isValid(LHSSignal(prevValid), BinaryLiteral("1"));
1460 if (cycleActive) {
1461 validOperations.elseIfClause(
1462 isValid, Assign(nextReg, LHSSignal(prevReg)));
1463 } else {
1464 if (cycle == 0) {
1465 skip_last_assign = true;
1466 lastStage.append(Assign(nextReg, LHSSignal(prevReg)));
1467 } else {
1468 validOperations =
1469 If(isValid, Assign(nextReg, LHSSignal(prevReg)));
1470 }
1471 }
1472 cycleActive = true;
1473 }
1474
1475 if (cycle == 0) {
1476 std::string outReg = nextReg + "_r";
1477 addRegisterIfMissing(outReg, width, WireType::Vector);
1478 outputPipeline.append(Assign(outReg, LHSSignal(nextReg)));
1479
1480 Assign finalStep(nextReg, LHSSignal(outReg));
1481 if (!skip_last_assign) {
1482 if (cycleActive) {
1483 validOperations.elseClause(finalStep);
1484 lastStage.append(validOperations);
1485 } else {
1486 lastStage.append(finalStep);
1487 }
1488 }
1489 } else {
1490 validOperations.elseClause(Assign(valid, BinaryLiteral("0")));
1491 outputPipeline.append(validOperations);
1492 }
1493 }
1494 lastStage.append(Assign(
1495 "data_" + port->name() + "_out",
1496 LHSSignal(pipelineName(port->name(), 0))));
1497 }
1498
1499 Synchronous sync("output_pipeline_sp");
1500 sync << If(
1501 Equals(LHSSignal("glock_in"), BinaryLiteral("0")), outputPipeline);
1502 behaviour_ << sync;
1503
1504 Asynchronous async("output_pipeline_cp");
1505 async << lastStage;
1506 behaviour_ << async;
1507}
1508
1509/**
1510 *
1511 * Generate all FUGen FUs.
1512 *
1513 */
1514void
1516 const ProGeOptions& options, std::vector<std::string> globalOptions,
1517 const std::vector<IDF::FUGenerated>& generatetFUs,
1519 // Generate FU innards.
1520 for (auto fug : generatetFUs) {
1521 FUGen fugen(options, globalOptions, fug, machine, core);
1522
1523 // Some repetition to have named FUs override an "all"
1524 // e.g. --fu-front-register=all --fu-back-register=lsu
1526 fug.name(), options.fuFrontRegistered, false)) {
1527 fugen.frontRegistered_ = true;
1529 fug.name(), options.fuMiddleRegistered, false)) {
1530 fugen.middleRegistered_ = true;
1532 fug.name(), options.fuBackRegistered, false)) {
1533 fugen.backRegistered_ = true;
1535 fug.name(), options.fuFrontRegistered)) {
1536 fugen.frontRegistered_ = true;
1538 fug.name(), options.fuMiddleRegistered)) {
1539 fugen.middleRegistered_ = true;
1540 } else { // Default to back-register
1541 fugen.backRegistered_ = true;
1542 }
1543
1544 fugen.createFUHeaderComment();
1545 fugen.checkForValidity();
1546
1547 fugen.parseOperations();
1548 fugen.scheduleOperations();
1549 fugen.createMandatoryPorts();
1550 fugen.createExternalInterfaces(!options.integratorName.empty());
1552
1553 fugen.createShadowRegisters();
1554 fugen.createPortPipeline();
1555 fugen.buildOperations();
1556 fugen.createOutputPipeline();
1557
1558 fugen.finalizeHDL();
1560 }
1561}
#define assert(condition)
TTAMachine::Machine * machine
the architecture definition of the estimated processor
#define DS
static MachInfoCmdLineOptions options
Definition MachInfo.cc:46
virtual Node & headNode(const Edge &edge) const
virtual Node & tailNode(const Edge &edge) const
virtual long value() const
static bool containsValue(const ContainerType &aContainer, const ElementType &aKey)
static std::vector< std::string > hdbPaths(bool libraryPathsOnly=false)
Definition FUGen.hh:58
void parseOperations()
Definition FUGen.cc:906
ProGe::Signal inferLSUSignal(const std::string &portName) const
Definition FUGen.cc:542
void createShadowRegisters()
Definition FUGen.cc:1243
std::string opcodeSignal(int stage)
Definition FUGen.cc:113
void finalizeHDL()
Definition FUGen.cc:889
std::unordered_map< std::string, BaseOperation > baseOperations_
Definition FUGen.hh:220
void scheduleOperations()
Definition FUGen.cc:1064
void readImplementation(std::string filename, std::string opName, std::deque< std::string > &sink)
Definition FUGen.cc:492
void createPortPipeline()
Definition FUGen.cc:1317
std::unordered_map< std::string, int > pipelineLength_
Definition FUGen.hh:233
std::pair< std::string, std::string > Replace
Definition FUGen.hh:98
void createExternalInterfaces(bool genIntegrator)
Definition FUGen.cc:568
std::vector< Replace > buildReplaces(std::string opName)
Definition FUGen.cc:395
IDF::FUGenerated & fug_
Definition FUGen.hh:207
std::string pipelineValid(std::string port, int cycle)
Definition FUGen.cc:155
std::string operandPlaceholder(int id)
Definition FUGen.cc:141
std::vector< std::string > resourceInputs_
Definition FUGen.hh:241
std::vector< HDB::Variable > renamedGlobalSignals_
Definition FUGen.hh:249
std::vector< std::string > globalOptions_
Definition FUGen.hh:205
void createOperationResources()
Definition FUGen.cc:634
int DAGNodeOperandWidth(OperationDAGNode &node, int id, OperationDAG *dag)
Definition FUGen.cc:201
int maxLatency_
Definition FUGen.hh:201
void addRegisterIfMissing(std::string name, int width, HDLGenerator::WireType wt=HDLGenerator::WireType::Auto)
Definition FUGen.cc:1377
std::string pipelineName(std::string port, int cycle)
Definition FUGen.cc:146
int opcodeWidth_
Definition FUGen.hh:214
std::string replaceToken(std::string line, Replace replace)
Definition FUGen.cc:67
std::unordered_map< std::string, OperationDAG * > implementapleDAGs_
Definition FUGen.hh:225
bool isLSU_
Definition FUGen.hh:261
HDLGenerator::Module fu_
Definition FUGen.hh:210
ProGe::NetlistBlock * core_
Definition FUGen.hh:208
void copyImplementation(std::string file, std::string format, bool isSynthesizable)
Definition FUGen.cc:267
std::deque< std::string > readFile(std::string filename)
Definition FUGen.cc:82
std::unordered_set< std::string > extInputs_
Definition FUGen.hh:246
std::unordered_map< std::string, int > operationCycles_
Definition FUGen.hh:223
OperandConnection subOpConnection(OperationDAG *dag, OperationDAGEdge *edge, bool isOutput)
Definition FUGen.cc:1024
std::string constantName(ConstantNode *node, OperationDAG *dag)
Definition FUGen.cc:178
std::unordered_multimap< std::string, OutputConnection > portInputs_
Definition FUGen.hh:235
bool hasToken(std::string line, std::string token)
Definition FUGen.cc:61
bool useGlock_
Definition FUGen.hh:255
std::string opcodeConstant(std::string operation)
Definition FUGen.cc:131
HDLGenerator::Behaviour behaviour_
Definition FUGen.hh:256
std::unordered_map< std::string, int > subOpCount_
Definition FUGen.hh:226
std::unordered_map< std::string, int > implLatency_
Definition FUGen.hh:224
bool middleRegistered_
Definition FUGen.hh:258
std::unordered_map< std::string, int > resourceCount_
Definition FUGen.hh:230
void prepareSnippet(std::string name, std::deque< std::string > statements, HDLGenerator::CodeBlock &sink, std::set< std::string > &addedStatements)
Definition FUGen.cc:854
const ProGeOptions & options_
Definition FUGen.hh:204
int minLatency_
Definition FUGen.hh:202
int addressWidth_
Definition FUGen.hh:260
std::string operandSignal(std::string operation, int id)
Definition FUGen.cc:136
std::unordered_map< std::string, std::vector< Replace > > replacesPerOp_
Definition FUGen.hh:222
std::unordered_map< std::string, ProGe::Direction > portDirection_
Definition FUGen.hh:234
void createMandatoryPorts()
Definition FUGen.cc:327
std::string triggerSignal(int stage)
Definition FUGen.cc:122
std::string triggerPort_
Definition FUGen.hh:250
bool isLSUDataPort(const std::string &portName)
Definition FUGen.cc:516
HDLGenerator::Language selectedLanguage()
Definition FUGen.cc:73
std::vector< std::string > resourceOutputs_
Definition FUGen.hh:242
TTAMachine::FunctionUnit * adfFU_
Definition FUGen.hh:211
void buildOperations()
Definition FUGen.cc:672
std::string moduleName_
Definition FUGen.hh:216
std::set< std::pair< std::string, std::string > > extOutputs_
Definition FUGen.hh:245
std::string subOpName(OperationNode *node)
Definition FUGen.cc:160
bool backRegistered_
Definition FUGen.hh:259
std::string findAbsolutePath(std::string file)
Definition FUGen.cc:101
std::vector< std::string > operations_
Definition FUGen.hh:213
bool useGlockRequest_
Definition FUGen.hh:254
void createFUHeaderComment()
Definition FUGen.cc:228
std::unordered_map< std::string, int > dagConstantCount_
Definition FUGen.hh:227
std::vector< std::string > registers_
Definition FUGen.hh:252
std::unordered_set< std::string > extIfaces_
Definition FUGen.hh:244
void checkForValidity()
Definition FUGen.cc:365
std::unordered_map< int, DAGConstant > dagConstants_
Definition FUGen.hh:239
std::unordered_map< std::string, OperationSchedule > scheduledOperations_
Definition FUGen.hh:221
ProGe::NetlistBlock * netlistBlock_
Definition FUGen.hh:217
static void implement(const ProGeOptions &options, std::vector< std::string > globalOptions, const std::vector< IDF::FUGenerated > &generatetFUs, const TTAMachine::Machine &machine, ProGe::NetlistBlock *core)
Definition FUGen.cc:1515
void createImplementationFiles()
Definition FUGen.cc:290
std::unordered_map< int, int > nodeImplementations_
Definition FUGen.hh:238
bool frontRegistered_
Definition FUGen.hh:257
void createOutputPipeline()
Definition FUGen.cc:1385
std::vector< HDB::Variable > renamedVariables_
Definition FUGen.hh:248
static bool createDirectory(const std::string &path)
static const std::string DIRECTORY_SEPARATOR
static std::string fileOfPath(const std::string pathName)
static void copy(const std::string &source, const std::string &target)
static std::string directoryOfPath(const std::string fileName)
Definition FileSystem.cc:79
static std::string findFileInSearchPaths(const std::vector< std::string > &searchPaths, const std::string &file)
static bool fileExists(const std::string fileName)
int nodeID() const
static CachedHDBManager & instance(const std::string &hdbFile)
OperationImplementation OperationImplementationByID(RowID id) const
virtual void reads(const std::string &var) override
const std::string & name() const noexcept
void elseIfClause(LHSValue cls, SS ifBlock)
void elseClause(SS elseBlock)
void implement(std::ostream &stream, Language lang, int level=0)
void appendToHeader(const std::string &line)
void set_prefix(std::string prefix)
void addCase(Case rhs)
std::vector< Info > & operations()
std::string name() const
static int requiredBitsSigned(SLongWord number)
virtual bool isInput() const
Definition Operand.cc:145
virtual int width() const
Definition Operand.cc:318
const class OperationPimpl & operation() const
Operation * effectiveOperation(const TCEString &name)
Operation & referencedOperation() const
TCEString name() const
OperationIndex & index()
Operation & operation(const char *name)
virtual OperationDAG & dag(int index) const
Definition Operation.cc:148
virtual TCEString name() const
Definition Operation.cc:93
virtual int dagCount() const
Definition Operation.cc:134
virtual Operand & operand(int id) const
Definition Operation.cc:541
void addPortGroup(NetlistPortGroup *portGroup)
virtual const Netlist & netlist() const
void addPort(NetlistPort &port)
bool connect(const NetlistPort &port1, const NetlistPort &port2, int port1FirstBit, int port2FirstBit, int width=1)
Definition Netlist.cc:83
static std::string stringToLower(const std::string &source)
virtual int width() const
virtual TCEString name() const
virtual bool isTriggering() const
Definition FUPort.cc:182
bool noRegister() const
Definition FUPort.cc:469
virtual AddressSpace * addressSpace() const
virtual HWOperation * operation(const std::string &name) const
virtual int operationCount() const
virtual bool hasAddressSpace() const
virtual BaseFUPort * port(const std::string &name) const
virtual FUPort * port(int operand) const
const std::string & name() const
virtual bool isInput() const
Definition Port.cc:298
virtual bool isOutput() const
Definition Port.cc:308
virtual std::string name() const
Definition Port.cc:141
virtual int portCount() const
Definition Unit.cc:135
virtual int operandIndex() const
bool findInOptionList(const std::string &option, std::vector< std::string > list, bool enableAll=true)
int maxLatencyToNode(const OperationDAG &dag, OperationDAGNode &node, const std::unordered_map< std::string, int > &maxOpLatency, bool allowDifference=true)
int dagLatency(const OperationDAG &dag, const std::unordered_map< std::string, int > &maxOpLatency)
bool canGenerateFromDAG(const OperationDAG &dag, const std::vector< IDF::FUGenerated::Info > infos, std::vector< IDF::FUGenerated::Info > *subops)
@ BIT_VECTOR
Several bits.
Definition ProGeTypes.hh:48
@ BYTEMASKED_SRAM_PORT
Signal group type for one port SRAM having read and write capability and bitmask for writing with sep...
Direction
Direction of the port.
Definition ProGeTypes.hh:52
@ OUT
Output port.
Definition ProGeTypes.hh:54
@ IN
Input port.
Definition ProGeTypes.hh:53
@ Verilog
Verilog.
Definition ProGeTypes.hh:42
@ VHDL
VHDL.
Definition ProGeTypes.hh:41
BusInfo parseBus(std::string file)
Definition IPXact.cc:39
ModuleInfo parseComponent(std::string file)
Definition IPXact.cc:86
std::vector< HDB::OperationImplementationResource > resources
Definition FUGen.hh:132
std::deque< std::string > implementation
Definition FUGen.hh:133
std::vector< HDB::Variable > globalsignals
Definition FUGen.hh:131
std::deque< std::string > postOp
Definition FUGen.hh:135
std::vector< HDB::Variable > variables
Definition FUGen.hh:130
std::deque< std::string > initial
Definition FUGen.hh:134
std::string name
Definition FUGen.hh:128
std::string operation
Definition FUGen.hh:139
std::vector< std::string > subOperations
Definition FUGen.hh:124
std::set< int > results
Definition FUGen.hh:122
std::vector< OperandConnection > operands
Definition FUGen.hh:121
std::map< std::string, int > resourceOffsets
Definition FUGen.hh:123
std::vector< Variable > verilogVariables
std::vector< Variable > vhdlGlobalSignals
std::vector< Variable > verilogGlobalSignals
std::vector< OperationImplementationResource > resources
std::vector< Variable > vhdlVariables
std::vector< std::string > fuIcGateList
ProGe::HDL language
std::string outputDirectory
std::vector< std::string > hdbList
std::vector< Port > ports
Definition IPXact.hh:61
std::vector< Port > ports
Definition IPXact.hh:56