Jlm
Loading...
Searching...
No Matches
RhlsToFirrtlConverter.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 Magnus Sjalander <work@sjalander.com> and
3 * David Metz <david.c.metz@ntnu.no>
4 * See COPYING for terms of redistribution.
5 */
6
10#include <jlm/util/strfmt.hpp>
11
12#include <llvm/ADT/SmallPtrSet.h>
13
14#include <mlir/IR/OwningOpRef.h>
15
16namespace jlm::hls
17{
18
19// Handles nodes with 2 inputs and 1 output
20circt::firrtl::FModuleOp
22{
23 // Only handles nodes with a single output
24 if (node->noutputs() != 1)
25 {
26 throw std::logic_error(node->DebugString() + " has more than 1 output");
27 }
28
29 // Create the module and its input/output ports
30 auto module = nodeToModule(node);
31 auto body = module.getBodyBlock();
32
33 ::llvm::SmallVector<mlir::Value> inBundles;
34
35 // Get input signals
36 for (size_t i = 0; i < node->ninputs(); i++)
37 {
38 // Get the input bundle
39 auto bundle = GetInPort(module, i);
40 // Get the data signal from the bundle
41 GetSubfield(body, bundle, "data");
42 inBundles.push_back(bundle);
43 }
44
45 // Get the output bundle
46 auto outBundle = GetOutPort(module, 0);
47 // Get the data signal from the bundle
48 auto outData = GetSubfield(body, outBundle, "data");
49
51 {
52 auto input0 = GetSubfield(body, inBundles[0], "data");
53 auto input1 = GetSubfield(body, inBundles[1], "data");
54 auto op = AddAddOp(body, input0, input1);
55 // Connect the op to the output data
56 // We drop the carry bit
58 }
60 {
61 auto input0 = GetSubfield(body, inBundles[0], "data");
62 auto input1 = GetSubfield(body, inBundles[1], "data");
63 auto op = AddSubOp(body, input0, input1);
64 // Connect the op to the output data
65 // We drop the carry bit
67 }
69 {
70 auto input0 = GetSubfield(body, inBundles[0], "data");
71 auto input1 = GetSubfield(body, inBundles[1], "data");
72 auto op = AddAndOp(body, input0, input1);
73 // Connect the op to the output data
75 }
77 {
78 auto input0 = GetSubfield(body, inBundles[0], "data");
79 auto input1 = GetSubfield(body, inBundles[1], "data");
80 auto op = AddXorOp(body, input0, input1);
81 // Connect the op to the output data
83 }
85 {
86 auto input0 = GetSubfield(body, inBundles[0], "data");
87 auto input1 = GetSubfield(body, inBundles[1], "data");
88 auto op = AddOrOp(body, input0, input1);
89 // Connect the op to the output data
91 }
92 else if (auto bitmulOp = dynamic_cast<const llvm::IntegerMulOperation *>(&(node->GetOperation())))
93 {
94 auto input0 = GetSubfield(body, inBundles[0], "data");
95 auto input1 = GetSubfield(body, inBundles[1], "data");
96 auto op = AddMulOp(body, input0, input1);
97 // Connect the op to the output data
98 // Multiplication results are double the input width, so we drop the upper half of the result
99 Connect(body, outData, DropMSBs(body, op, bitmulOp->Type().nbits()));
100 }
102 {
103 auto input0 = GetSubfield(body, inBundles[0], "data");
104 auto input1 = GetSubfield(body, inBundles[1], "data");
108 auto uIntOp = AddAsUIntOp(body, divOp);
109 // Connect the op to the output data
111 }
113 {
114 auto input0 = GetSubfield(body, inBundles[0], "data");
115 auto input1 = GetSubfield(body, inBundles[1], "data");
116 auto op = AddDShrOp(body, input0, input1);
117 // Connect the op to the output data
119 }
121 {
122 auto input0 = GetSubfield(body, inBundles[0], "data");
123 auto input1 = GetSubfield(body, inBundles[1], "data");
126 auto uIntOp = AddAsUIntOp(body, shrOp);
127 // Connect the op to the output data
129 }
131 {
132 auto input0 = GetSubfield(body, inBundles[0], "data");
133 auto input1 = GetSubfield(body, inBundles[1], "data");
134 auto bitsOp = AddBitsOp(body, input1, 7, 0);
135 auto op = AddDShlOp(body, input0, bitsOp);
136 int outSize = JlmSize(node->output(0)->Type().get());
137 auto slice = AddBitsOp(body, op, outSize - 1, 0);
138 // Connect the op to the output data
139 Connect(body, outData, slice);
140 }
142 {
143 auto input0 = GetSubfield(body, inBundles[0], "data");
144 auto input1 = GetSubfield(body, inBundles[1], "data");
148 auto uIntOp = AddAsUIntOp(body, remOp);
150 }
152 {
153 auto input0 = GetSubfield(body, inBundles[0], "data");
154 auto input1 = GetSubfield(body, inBundles[1], "data");
155 auto op = AddEqOp(body, input0, input1);
156 // Connect the op to the output data
158 }
160 {
161 auto input0 = GetSubfield(body, inBundles[0], "data");
162 auto input1 = GetSubfield(body, inBundles[1], "data");
163 auto op = AddNeqOp(body, input0, input1);
164 // Connect the op to the output data
166 }
168 {
169 auto input0 = GetSubfield(body, inBundles[0], "data");
170 auto input1 = GetSubfield(body, inBundles[1], "data");
173 auto op = AddGtOp(body, sIntOp0, sIntOp1);
174 // Connect the op to the output data
176 }
178 {
179 auto input0 = GetSubfield(body, inBundles[0], "data");
180 auto input1 = GetSubfield(body, inBundles[1], "data");
181 auto op = AddLtOp(body, input0, input1);
182 // Connect the op to the output data
184 }
186 {
187 auto input0 = GetSubfield(body, inBundles[0], "data");
188 auto input1 = GetSubfield(body, inBundles[1], "data");
189 auto op = AddLeqOp(body, input0, input1);
190 // Connect the op to the output data
192 }
194 {
195 auto input0 = GetSubfield(body, inBundles[0], "data");
196 auto input1 = GetSubfield(body, inBundles[1], "data");
197 auto op = AddGtOp(body, input0, input1);
198 // Connect the op to the output data
200 }
202 {
203 auto input0 = GetSubfield(body, inBundles[0], "data");
204 auto input1 = GetSubfield(body, inBundles[1], "data");
207 auto op = AddGeqOp(body, sIntOp0, sIntOp1);
208 // Connect the op to the output data
210 }
212 {
213 auto input0 = GetSubfield(body, inBundles[0], "data");
214 auto input1 = GetSubfield(body, inBundles[1], "data");
215 auto op = AddGeqOp(body, input0, input1);
216 // Connect the op to the output data
218 }
220 {
221 auto input0 = GetSubfield(body, inBundles[0], "data");
222 auto input1 = GetSubfield(body, inBundles[1], "data");
225 auto op = AddLeqOp(body, sIntOp0, sIntOp1);
226 // Connect the op to the output data
228 }
230 {
231 auto input0 = GetSubfield(body, inBundles[0], "data");
232 auto input1 = GetSubfield(body, inBundles[1], "data");
233 auto sInt0 = AddAsSIntOp(body, input0);
234 auto sInt1 = AddAsSIntOp(body, input1);
235 auto op = AddLtOp(body, sInt0, sInt1);
237 }
238 else if (dynamic_cast<const llvm::ZExtOperation *>(&(node->GetOperation())))
239 {
240 auto input0 = GetSubfield(body, inBundles[0], "data");
242 }
244 {
245 auto inData = GetSubfield(body, inBundles[0], "data");
246 int outSize = JlmSize(node->output(0)->Type().get());
248 }
249 else if (dynamic_cast<const llvm::LambdaExitMemoryStateMergeOperation *>(&(node->GetOperation())))
250 {
251 auto inData = GetSubfield(body, inBundles[0], "data");
253 }
254 else if (dynamic_cast<const llvm::MemoryStateMergeOperation *>(&(node->GetOperation())))
255 {
256 auto inData = GetSubfield(body, inBundles[0], "data");
258 }
259 else if (auto op = dynamic_cast<const llvm::SExtOperation *>(&(node->GetOperation())))
260 {
261 auto input0 = GetSubfield(body, inBundles[0], "data");
262 auto sintOp = AddAsSIntOp(body, input0);
263 auto padOp = AddPadOp(body, sintOp, op->ndstbits());
264 auto uintOp = AddAsUIntOp(body, padOp);
266 }
267 else if (auto op = dynamic_cast<const llvm::IntegerConstantOperation *>(&(node->GetOperation())))
268 {
269 auto & value = op->Representation();
270 auto size = value.nbits();
271 // Create a constant of UInt<size>(value) and connect to output data
272 auto constant = GetConstant(body, size, value.to_uint());
273 Connect(body, outData, constant);
274 }
275 else if (
276 auto op = dynamic_cast<const jlm::rvsdg::ControlConstantOperation *>(&(node->GetOperation())))
277 {
278 auto value = op->value().alternative();
279 auto size = ceil(log2(op->value().nalternatives()));
280 auto constant = GetConstant(body, size, value);
281 Connect(body, outData, constant);
282 }
283 else if (dynamic_cast<const llvm::BitCastOperation *>(&(node->GetOperation())))
284 {
285 auto input0 = GetSubfield(body, inBundles[0], "data");
287 }
288 else if (dynamic_cast<const llvm::IntToPtrOperation *>(&(node->GetOperation())))
289 {
290 auto input0 = GetSubfield(body, inBundles[0], "data");
292 }
293 else if (auto op = dynamic_cast<const jlm::rvsdg::MatchOperation *>(&(node->GetOperation())))
294 {
295 auto inData = GetSubfield(body, inBundles[0], "data");
296 auto outData = GetSubfield(body, outBundle, "data");
297 int inSize = JlmSize(node->input(0)->Type().get());
298 int outSize = JlmSize(node->output(0)->Type().get());
299 if (IsIdentityMapping(*op))
300 {
301 if (inSize == outSize)
302 {
304 }
305 else
306 {
308 }
309 }
310 else
311 {
312 auto size = op->nbits();
313 mlir::Value result = GetConstant(body, size, op->default_alternative());
314 for (auto it = op->begin(); it != op->end(); it++)
315 {
316 auto comparison = AddEqOp(body, inData, GetConstant(body, size, it->first));
317 auto value = GetConstant(body, size, it->second);
318 result = AddMuxOp(body, comparison, value, result);
319 }
320 if ((unsigned long)outSize != size)
321 {
322 result = AddBitsOp(body, result, outSize - 1, 0);
323 }
324 Connect(body, outData, result);
325 }
326 }
327 else if (auto op = dynamic_cast<const llvm::GetElementPtrOperation *>(&(node->GetOperation())))
328 {
329 // Start of with base pointer
330 auto input0 = GetSubfield(body, inBundles[0], "data");
331 mlir::Value result = AddCvtOp(body, input0);
332
333 // TODO: support structs
334 const rvsdg::Type * pointeeType = op->getPointeeType().get();
335 for (size_t i = 1; i < node->ninputs(); i++)
336 {
337 int bits = JlmSize(pointeeType);
338 if (dynamic_cast<const rvsdg::BitType *>(pointeeType)
339 || dynamic_cast<const llvm::FloatingPointType *>(pointeeType))
340 {
341 pointeeType = nullptr;
342 }
343 else if (auto arrayType = dynamic_cast<const llvm::ArrayType *>(pointeeType))
344 {
345 pointeeType = &arrayType->element_type();
346 }
347 else if (auto vectorType = dynamic_cast<const llvm::VectorType *>(pointeeType))
348 {
349 pointeeType = vectorType->Type().get();
350 }
351 else
352 {
353 throw std::logic_error(pointeeType->debug_string() + " pointer not implemented!");
354 }
355 // GEP inputs are signed
356 auto input = GetSubfield(body, inBundles[i], "data");
357 auto asSInt = AddAsSIntOp(body, input);
358 int bytes = bits / 8;
360 auto cvtOp = AddCvtOp(body, constantOp);
361 auto offset = AddMulOp(body, asSInt, cvtOp);
362 result = AddAddOp(body, result, offset);
363 }
364 auto asUInt = AddAsUIntOp(body, result);
366 }
367 else if (auto op = dynamic_cast<const llvm::ExtractElementOperation *>(&(node->GetOperation())))
368 {
369 // Start of with base pointer
370 auto input0 = GetSubfield(body, inBundles[0], "data");
371 auto input1 = GetSubfield(body, inBundles[1], "data");
372 auto vt = dynamic_cast<const llvm::VectorType *>(op->argument(0).get());
373 auto vec = Builder_->create<circt::firrtl::WireOp>(
374 Builder_->getUnknownLoc(),
375 circt::firrtl::FVectorType::get(GetFirrtlType(vt->Type().get()), vt->size()),
376 "vec");
377 auto elementBits = JlmSize(vt->Type().get());
378 body->push_back(vec);
379 for (size_t i = 0; i < vt->size(); ++i)
380 {
381 auto subindexOp = Builder_->create<circt::firrtl::SubindexOp>(
382 Builder_->getUnknownLoc(),
383 vec.getResult(),
384 i);
385 body->push_back(subindexOp);
386 Connect(
387 body,
389 AddBitsOp(body, input0, elementBits * (i + 1) - 1, elementBits * i));
390 }
391 auto subaccessOp = Builder_->create<circt::firrtl::SubaccessOp>(
392 Builder_->getUnknownLoc(),
393 vec.getResult(),
394 input1);
395 body->push_back(subaccessOp);
397 }
398 else if (dynamic_cast<const llvm::UndefValueOperation *>(&(node->GetOperation())))
399 {
401 }
402 else if (auto op = dynamic_cast<const MuxOperation *>(&(node->GetOperation())))
403 {
404 JLM_ASSERT(op->discarding);
405 auto select = GetSubfield(body, inBundles[0], "data");
407 for (size_t i = 1; i < node->ninputs(); i++)
408 {
409 auto data = GetSubfield(body, inBundles[i], "data");
410 auto constant = GetConstant(body, JlmSize(node->input(0)->Type().get()), i - 1);
411 auto eqOp = AddEqOp(body, select, constant);
412 auto whenOp = AddWhenOp(body, eqOp, false);
413 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
414 Connect(thenBody, outData, data);
415 }
416 }
417 else
418 {
419 // Destroy the module to avoid leaking it on exception
420 module.erase();
421 throw std::logic_error("Simple node " + node->DebugString() + " not implemented!");
422 }
423
424 // Generate the output valid signal
425 auto oneBitValue = GetConstant(body, 1, 1);
426 mlir::Value prevAnd = oneBitValue;
427 for (size_t i = 0; i < node->ninputs(); i++)
428 {
429 auto bundle = inBundles[i];
431 }
432 // Connect the valide signal to the output bundle
433 auto outValid = GetSubfield(body, outBundle, "valid");
435
436 // Generate the ready signal
437 auto outReady = GetSubfield(body, outBundle, "ready");
439 // Connect it to the ready signal of the two input bundles
440 for (size_t i = 0; i < node->ninputs(); i++)
441 {
442 auto bundle = inBundles[i];
443 auto ready = GetSubfield(body, bundle, "ready");
445 }
446
447 return module;
448}
449
450circt::firrtl::FModuleOp
452{
453 // Create the module and its input/output ports
454 auto module = nodeToModule(node);
455 auto body = module.getBodyBlock();
456
457 // Create a constant of UInt<1>(1)
458 auto intType = GetIntType(1);
459 auto constant = Builder_->create<circt::firrtl::ConstantOp>(
460 Builder_->getUnknownLoc(),
461 intType,
462 ::llvm::APInt(1, 1));
463 body->push_back(constant);
464
465 // Get the input bundle
466 auto bundle = GetInPort(module, 0);
467 // Get the ready signal from the bundle (first signal in the bundle)
468 auto ready = GetSubfield(body, bundle, "ready");
469 // Connect the constant to the ready signal
470 Connect(body, ready, constant);
471
472 return module;
473}
474
475circt::firrtl::FModuleOp
477{
478 // Create the module and its input/output ports
479 auto module = nodeToModule(node);
480 auto body = module.getBodyBlock();
481
482 auto clock = GetClockSignal(module);
483
484 // Input signals
485 auto predBundle = GetInPort(module, 0);
486 auto predReady = GetSubfield(body, predBundle, "ready");
487 auto predValid = GetSubfield(body, predBundle, "valid");
488 auto predData = GetSubfield(body, predBundle, "data");
489
490 auto inBundle = GetInPort(module, 1);
491 auto inReady = GetSubfield(body, inBundle, "ready");
492 auto inValid = GetSubfield(body, inBundle, "valid");
493 auto inData = GetSubfield(body, inBundle, "data");
494
495 // Output signals
496 auto outBundle = GetOutPort(module, 0);
497 auto outReady = GetSubfield(body, outBundle, "ready");
498 auto outValid = GetSubfield(body, outBundle, "valid");
499 auto outData = GetSubfield(body, outBundle, "data");
500
501 auto dataReg = Builder_->create<circt::firrtl::RegOp>(
502 Builder_->getUnknownLoc(),
503 GetIntType(node->input(1)->Type().get()),
504 clock,
505 Builder_->getStringAttr("data_reg"));
506 body->push_back(dataReg);
507 // predicate 0 updates register, passes through and consumes input
508 // we always start with predicate 0 due to pred_buf
509 // predicate 1 uses data in register
511 Connect(
512 body,
513 inReady,
515
517 Connect(body, outData, dataReg.getResult());
520 AddWhenOp(body, dataPassThrough, false).getThenBodyBuilder().getBlock();
522
524 auto inFireBody = AddWhenOp(body, inFire, false).getThenBodyBuilder().getBlock();
525 Connect(inFireBody, dataReg.getResult(), inData);
526
527 return module;
528}
529
530circt::firrtl::FModuleOp
532{
533 auto op = dynamic_cast<const jlm::hls::ForkOperation *>(&node->GetOperation());
534 bool isConstant = op->IsConstant();
535 // Create the module and its input/output ports
536 auto module = nodeToModule(node);
537 auto body = module.getBodyBlock();
538
539 // Input signals
540 auto inBundle = GetInPort(module, 0);
541 auto inReady = GetSubfield(body, inBundle, "ready");
542 auto inValid = GetSubfield(body, inBundle, "valid");
543 auto inData = GetSubfield(body, inBundle, "data");
544
545 auto oneBitValue = GetConstant(body, 1, 1);
546 auto zeroBitValue = GetConstant(body, 1, 0);
547
548 //
549 // Output registers
550 //
551 if (isConstant)
552 {
554 for (size_t i = 0; i < node->noutputs(); ++i)
555 {
556 // Get the bundle
557 auto port = GetOutPort(module, i);
558 auto portValid = GetSubfield(body, port, "valid");
559 auto portData = GetSubfield(body, port, "data");
562 }
563 }
564 else
565 {
566 auto clock = GetClockSignal(module);
567 auto reset = GetResetSignal(module);
568 ::llvm::SmallVector<circt::firrtl::RegResetOp> firedRegs;
569 ::llvm::SmallVector<circt::firrtl::AndPrimOp> whenConditions;
570 // outputs can only fire if input is valid. This should not be necessary, unless other
571 // components misbehave
572 mlir::Value allFired = inValid;
573 for (size_t i = 0; i < node->noutputs(); ++i)
574 {
575 std::string validName("out");
576 validName.append(std::to_string(i));
577 validName.append("_fired_reg");
578 auto firedReg = Builder_->create<circt::firrtl::RegResetOp>(
579 Builder_->getUnknownLoc(),
580 GetIntType(1),
581 clock,
582 reset,
584 Builder_->getStringAttr(validName));
585 body->push_back(firedReg);
586 firedRegs.push_back(firedReg);
587
588 // Get the bundle
589 auto port = GetOutPort(module, i);
590 auto portReady = GetSubfield(body, port, "ready");
591 auto portValid = GetSubfield(body, port, "valid");
592 auto portData = GetSubfield(body, port, "data");
593
594 auto notFiredReg = AddNotOp(body, firedReg.getResult());
595 auto andOp = AddAndOp(body, inValid, notFiredReg.getResult());
598
599 auto orOp = AddOrOp(body, portReady, firedReg.getResult());
601
602 // Conditions needed for the when statements
604 }
605 allFired = AddNodeOp(body, allFired, "all_fired").getResult();
607
608 // When statement
610 auto whenOp = AddWhenOp(body, condition, true);
611 // getThenBlock() cause an error during commpilation
612 // So we first get the builder and then its associated body
613 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
614 // Then region
615 for (size_t i = 0; i < node->noutputs(); i++)
616 {
618 auto nestedBody = nestedWhen.getThenBodyBuilder().getBlock();
620 }
621 // Else region
622 auto elseBody = whenOp.getElseBodyBuilder().getBlock();
623 for (size_t i = 0; i < node->noutputs(); i++)
624 {
626 }
627 }
628
629 return module;
630}
631
632circt::firrtl::FModuleOp
634{
635 // Create the module and its input/output ports
636 auto module = nodeToModule(node);
637 auto body = module.getBodyBlock();
638
639 //
640 // Output registers
641 //
642 auto clock = GetClockSignal(module);
643 auto reset = GetResetSignal(module);
644 ::llvm::SmallVector<circt::firrtl::RegResetOp> firedRegs;
645 ::llvm::SmallVector<circt::firrtl::AndPrimOp> whenConditions;
646 auto oneBitValue = GetConstant(body, 1, 1);
647 auto zeroBitValue = GetConstant(body, 1, 0);
648 mlir::Value allInsValid = oneBitValue;
649 for (size_t i = 0; i < node->ninputs(); ++i)
650 {
651 auto inBundle = GetInPort(module, i);
652 // auto inReady = GetSubfield(body, inBundle, "ready");
653 auto inValid = GetSubfield(body, inBundle, "valid");
654 // auto inData = GetSubfield(body, inBundle, "data");
656 }
657 allInsValid = AddNodeOp(body, allInsValid, "all_ins_valid").getResult();
658 mlir::Value allFired = oneBitValue;
659 for (size_t i = 0; i < node->noutputs(); ++i)
660 {
661 std::string validName("out");
662 validName.append(std::to_string(i));
663 validName.append("_fired_reg");
664 auto firedReg = Builder_->create<circt::firrtl::RegResetOp>(
665 Builder_->getUnknownLoc(),
666 GetIntType(1),
667 clock,
668 reset,
670 Builder_->getStringAttr(validName));
671 body->push_back(firedReg);
672 firedRegs.push_back(firedReg);
673
674 // Get the bundle
675 auto out = GetOutPort(module, i);
676 auto outReady = GetSubfield(body, out, "ready");
677 auto outValid = GetSubfield(body, out, "valid");
678 auto outData = GetSubfield(body, out, "data");
679 auto in = GetInPort(module, i);
680 auto inData = GetSubfield(body, in, "data");
681
682 auto notFiredReg = AddNotOp(body, firedReg.getResult());
686
687 auto orOp = AddOrOp(body, AddAndOp(body, outValid, outReady), firedReg.getResult());
689
690 // Conditions needed for the when statements
692 }
693 allFired = AddNodeOp(body, allFired, "all_fired").getResult();
694 for (size_t i = 0; i < node->ninputs(); ++i)
695 {
696 auto in = GetInPort(module, i);
697 auto inReady = GetSubfield(body, in, "ready");
699 }
700
701 // When statement
703 auto whenOp = AddWhenOp(body, condition, true);
704 // getThenBlock() cause an error during commpilation
705 // So we first get the builder and then its associated body
706 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
707 // Then region
708 for (size_t i = 0; i < node->noutputs(); i++)
709 {
711 auto nestedBody = nestedWhen.getThenBodyBuilder().getBlock();
713 }
714 // Else region
715 auto elseBody = whenOp.getElseBodyBuilder().getBlock();
716 for (size_t i = 0; i < node->noutputs(); i++)
717 {
719 }
720
721 return module;
722}
723
724circt::firrtl::FModuleOp
726{
727 // Create the module and its input/output ports
728 auto module = nodeToModule(node, false);
729 auto body = module.getBodyBlock();
730
731 auto zeroBitValue = GetConstant(body, 1, 0);
732 auto oneBitValue = GetConstant(body, 1, 1);
733
734 for (size_t i = 0; i < node->noutputs(); ++i)
735 {
736 auto outBundle = GetOutPort(module, i);
737 auto outValid = GetSubfield(body, outBundle, "valid");
738 auto outData = GetSubfield(body, outBundle, "data");
741 }
742 for (size_t j = 0; j < node->ninputs(); ++j)
743 {
744 mlir::BlockArgument memRes = GetInPort(module, j);
745 auto memResValid = GetSubfield(body, memRes, "valid");
746 auto memResReady = GetSubfield(body, memRes, "ready");
747 auto memResBundle = GetSubfield(body, memRes, "data");
748 auto memResId = GetSubfield(body, memResBundle, "id");
749 auto memResData = GetSubfield(body, memResBundle, "data");
750 auto portWidth =
751 memResData->getResult(0).getType().cast<circt::firrtl::IntType>().getWidth().value();
752
753 auto elseBody = body;
754 for (size_t i = 0; i < node->noutputs(); ++i)
755 {
756 bool isStore = node->output(i)->Type()->Kind() == rvsdg::TypeKind::State;
757 auto outBundle = GetOutPort(module, i);
758 auto outValid = GetSubfield(elseBody, outBundle, "valid");
759 auto outReady = GetSubfield(elseBody, outBundle, "ready");
760 auto outData = GetSubfield(elseBody, outBundle, "data");
761 auto condition =
763 auto whenOp = AddWhenOp(elseBody, condition, true);
764 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
767 // don't connect data for stores
768 if (!isStore)
769 {
770 int nbits = JlmSize(node->output(i)->Type().get());
771 if (nbits == portWidth)
772 {
774 }
775 else
776 {
778 }
779 }
780 elseBody = whenOp.getElseBodyBuilder().getBlock();
781 }
782
783 // Connect to ready for other ids - for example stores
785 // Assert we don't get a response to the same ID on several in ports - if this shows up we need
786 // taken logic for outputs
787 for (size_t i = 0; i < j; ++i)
788 {
789 mlir::BlockArgument memRes2 = GetInPort(module, i);
790 auto memResValid2 = GetSubfield(body, memRes2, "valid");
791 auto memResBundle2 = GetSubfield(body, memRes2, "data");
793 auto id_assert = Builder_->create<circt::firrtl::AssertOp>(
794 Builder_->getUnknownLoc(),
795 GetClockSignal(module),
796 AddNotOp(
797 body,
798 AddAndOp(
799 body,
802 AddNotOp(body, GetResetSignal(module)),
803 "overlapping reponse id",
804 mlir::ValueRange(),
805 "response_id_assert_" + std::to_string(j) + "_" + std::to_string(i));
806 body->push_back(id_assert);
807 }
808 }
809
810 return module;
811}
812
813circt::firrtl::FModuleOp
815{
816 // Create the module and its input/output ports
817 auto module = nodeToModule(node, false);
818 auto body = module.getBodyBlock();
819 auto op = dynamic_cast<const MemoryRequestOperation *>(&node->GetOperation());
820
821 auto loadTypes = op->GetLoadTypes();
822 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadAddrReadys;
823 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadAddrValids;
824 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadAddrDatas;
825 ::llvm::SmallVector<mlir::Value> loadIds;
826
827 auto storeTypes = op->GetStoreTypes();
828 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeAddrReadys;
829 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeAddrValids;
830 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeAddrDatas;
831 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeDataReadys;
832 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeDataValids;
833 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeDataDatas;
834 ::llvm::SmallVector<mlir::Value> storeIds;
835 // The ports for loads come first and consist only of addresses.
836 // Stores have both addresses and data
837 size_t id = 0;
838 for (size_t i = 0; i < op->get_nloads(); ++i)
839 {
840 auto bundle = GetInPort(module, i);
841 loadAddrReadys.push_back(GetSubfield(body, bundle, "ready"));
842 loadAddrValids.push_back(GetSubfield(body, bundle, "valid"));
843 loadAddrDatas.push_back(GetSubfield(body, bundle, "data"));
844 loadIds.push_back(GetConstant(body, 8, id));
845 id++;
846 }
847 for (size_t i = op->get_nloads(); i < node->ninputs(); ++i)
848 {
849 // Store
850 auto addrBundle = GetInPort(module, i);
851 storeAddrReadys.push_back(GetSubfield(body, addrBundle, "ready"));
852 storeAddrValids.push_back(GetSubfield(body, addrBundle, "valid"));
853 storeAddrDatas.push_back(GetSubfield(body, addrBundle, "data"));
854 i++;
855 auto dataBundle = GetInPort(module, i);
856 storeDataReadys.push_back(GetSubfield(body, dataBundle, "ready"));
857 storeDataValids.push_back(GetSubfield(body, dataBundle, "valid"));
858 storeDataDatas.push_back(GetSubfield(body, dataBundle, "data"));
859 storeIds.push_back(GetConstant(body, 8, id));
860 id++;
861 }
862
863 auto zeroBitValue = GetConstant(body, 1, 0);
864 auto oneBitValue = GetConstant(body, 1, 1);
865 ::llvm::SmallVector<mlir::Value> loadGranted(loadTypes->size(), zeroBitValue);
866 ::llvm::SmallVector<mlir::Value> storeGranted(storeTypes->size(), zeroBitValue);
867 for (size_t j = 0; j < node->noutputs(); ++j)
868 {
869 auto reqType = util::assertedCast<const BundleType>(node->output(j)->Type().get());
870 auto hasWrite = reqType->elements_.size() == 5;
871 mlir::BlockArgument memReq = GetOutPort(module, j);
872 mlir::Value memReqData;
873 mlir::Value memReqWrite;
874 auto memReqReady = GetSubfield(body, memReq, "ready");
875 auto memReqValid = GetSubfield(body, memReq, "valid");
876 auto memReqBundle = GetSubfield(body, memReq, "data");
877 auto memReqAddr = GetSubfield(body, memReqBundle, "addr");
878 auto memReqSize = GetSubfield(body, memReqBundle, "size");
879 auto memReqId = GetSubfield(body, memReqBundle, "id");
880 if (hasWrite)
881 {
884 }
885 // Default request connection
888 mlir::Value previousGranted = zeroBitValue;
889 for (size_t i = 0; i < loadTypes->size(); ++i)
890 {
891 if (j == 0)
892 {
894 }
897 auto whenOp = AddWhenOp(body, grant, false);
898 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
903 // No data or write
904 auto loadType = loadTypes->at(i).get();
906 int log2Bytes = log2(bitWidth / 8);
908 if (hasWrite)
909 {
911 }
912 // Update for next iteration
915 }
916 // Stores
917 for (size_t i = 0; hasWrite && i < storeTypes->size(); ++i)
918 {
919 if (j == 0)
920 {
923 }
927 auto whenOp = AddWhenOp(body, grant, false);
928 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
934 // TODO: pad
935 // auto portWidth =
936 // memReqData.getType().cast<circt::firrtl::IntType>().getWidth().value();
938 // No data or write
939 auto storeType = storeTypes->at(i).get();
941 int log2Bytes = log2(bitWidth / 8);
944 // Update for next iteration
947 }
948 }
949
950 return module;
951}
952
953circt::firrtl::FModuleOp
955{
957
958 // Create the module and its input/output ports
959 auto module = nodeToModule(node, false);
960 auto body = module.getBodyBlock();
961
962 // Input signals
963 auto inBundleAddr = GetInPort(module, 0);
964 auto inReadyAddr = GetSubfield(body, inBundleAddr, "ready");
965 auto inValidAddr = GetSubfield(body, inBundleAddr, "valid");
966 auto inDataAddr = GetSubfield(body, inBundleAddr, "data");
967
968 ::llvm::SmallVector<circt::firrtl::SubfieldOp> inReadyStates;
969 ::llvm::SmallVector<circt::firrtl::SubfieldOp> inValidStates;
970 ::llvm::SmallVector<circt::firrtl::SubfieldOp> inDataStates;
971 for (size_t i = 1; i < node->ninputs() - 1; ++i)
972 {
973 auto bundle = GetInPort(module, i);
974 inReadyStates.push_back(GetSubfield(body, bundle, "ready"));
975 inValidStates.push_back(GetSubfield(body, bundle, "valid"));
976 inDataStates.push_back(GetSubfield(body, bundle, "data"));
977 }
978
979 auto inBundleMemData = GetInPort(module, node->ninputs() - 1);
983
984 // Output signals
985 auto outBundleData = GetOutPort(module, 0);
986 auto outReadyData = GetSubfield(body, outBundleData, "ready");
987 auto outValidData = GetSubfield(body, outBundleData, "valid");
988 auto outDataData = GetSubfield(body, outBundleData, "data");
989
990 ::llvm::SmallVector<circt::firrtl::SubfieldOp> outReadyStates;
991 ::llvm::SmallVector<circt::firrtl::SubfieldOp> outValidStates;
992 ::llvm::SmallVector<circt::firrtl::SubfieldOp> outDataStates;
993 for (size_t i = 1; i < node->noutputs() - 1; ++i)
994 {
995 auto bundle = GetOutPort(module, i);
996 outReadyStates.push_back(GetSubfield(body, bundle, "ready"));
997 outValidStates.push_back(GetSubfield(body, bundle, "valid"));
998 outDataStates.push_back(GetSubfield(body, bundle, "data"));
999 }
1000
1001 auto outBundleMemAddr = GetOutPort(module, node->noutputs() - 1);
1005
1006 auto clock = GetClockSignal(module);
1007 auto reset = GetResetSignal(module);
1008 auto zeroBitValue = GetConstant(body, 1, 0);
1009 auto oneBitValue = GetConstant(body, 1, 1);
1010
1011 // Registers
1012 ::llvm::SmallVector<circt::firrtl::RegResetOp> oValidRegs;
1013 ::llvm::SmallVector<circt::firrtl::RegResetOp> oDataRegs;
1014 for (size_t i = 0; i < node->noutputs() - 1; i++)
1015 {
1016 std::string validName("o");
1017 validName.append(std::to_string(i));
1018 validName.append("_valid_reg");
1019 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
1020 Builder_->getUnknownLoc(),
1021 GetIntType(1),
1022 clock,
1023 reset,
1025 Builder_->getStringAttr(validName));
1026 body->push_back(validReg);
1027 oValidRegs.push_back(validReg);
1028
1029 auto zeroValue = GetConstant(body, JlmSize(node->output(i)->Type().get()), 0);
1030 std::string dataName("o");
1031 dataName.append(std::to_string(i));
1032 dataName.append("_data_reg");
1033 auto dataReg = Builder_->create<circt::firrtl::RegResetOp>(
1034 Builder_->getUnknownLoc(),
1035 GetIntType(node->output(i)->Type().get()),
1036 clock,
1037 reset,
1038 zeroValue,
1039 Builder_->getStringAttr(dataName));
1040 body->push_back(dataReg);
1041 oDataRegs.push_back(dataReg);
1042 }
1043 auto sentReg = Builder_->create<circt::firrtl::RegResetOp>(
1044 Builder_->getUnknownLoc(),
1045 GetIntType(1),
1046 clock,
1047 reset,
1049 Builder_->getStringAttr("sent_reg"));
1050 body->push_back(sentReg);
1051
1052 // mlir::Value canRequest = AddOrOp(body, AddNotOp(body, sentReg), AddAndOp(body,
1053 // inValidMemData, outReadyData));
1054 mlir::Value canRequest = AddNotOp(body, sentReg.getResult());
1056 for (auto vld : inValidStates)
1057 {
1059 }
1060 // canRequest = AddAndOp(body, canRequest, AddOrOp(body, AddNotOp(body, oValidRegs[0]),
1061 // outReadyData));
1063 for (size_t i = 1; i < oValidRegs.size(); i++)
1064 {
1065 // canRequest = AddAndOp(body, canRequest, AddOrOp(body, AddNotOp(body, oValidRegs[i]),
1066 // outReadyStates[i-1]));
1068 }
1069
1070 // Block until all inputs and no outputs are valid
1073
1076
1077 for (size_t i = 1; i < node->noutputs() - 1; ++i)
1078 {
1081 auto andOp2 = AddAndOp(body, outReadyStates[i - 1], outValidStates[i - 1]);
1082 Connect(
1083 // When o1 fires
1086 zeroBitValue);
1087 }
1088
1089 // mem_res fire
1090 auto whenResFireOp = AddWhenOp(body, AddAndOp(body, sentReg.getResult(), inValidMemData), false);
1091 auto whenResFireBody = whenResFireOp.getThenBodyBuilder().getBlock();
1097
1098 // mem_req fire
1100 auto whenReqFireBody = whenReqFireOp.getThenBodyBuilder().getBlock();
1102 for (size_t i = 1; i < node->noutputs() - 1; ++i)
1103 {
1106 }
1107
1108 // Handshaking
1110 for (size_t i = 1; i < node->ninputs() - 1; ++i)
1111 {
1113 }
1114 Connect(body, inReadyMemData, sentReg.getResult());
1115
1117 Connect(
1118 // When o0 fires
1120 oValidRegs[0].getResult(),
1121 zeroBitValue);
1122
1123 return module;
1124}
1125
1126circt::firrtl::FModuleOp
1128{
1130
1131 // Create the module and its input/output ports
1132 auto module = nodeToModule(node, false);
1133 auto body = module.getBodyBlock();
1134
1135 // Input signals
1136 auto inBundleAddr = GetInPort(module, 0);
1137 auto inReadyAddr = GetSubfield(body, inBundleAddr, "ready");
1138 auto inValidAddr = GetSubfield(body, inBundleAddr, "valid");
1139 auto inDataAddr = GetSubfield(body, inBundleAddr, "data");
1140
1141 auto inBundleMemData = GetInPort(module, node->ninputs() - 1);
1145
1146 // Output signals
1147 auto outBundleData = GetOutPort(module, 0);
1148 auto outReadyData = GetSubfield(body, outBundleData, "ready");
1149 auto outValidData = GetSubfield(body, outBundleData, "valid");
1150 auto outDataData = GetSubfield(body, outBundleData, "data");
1151
1152 auto outBundleMemAddr = GetOutPort(module, node->noutputs() - 1);
1156
1157 // Block until all inputs and no outputs are valid
1160
1161 // Handshaking
1164
1168
1169 return module;
1170}
1171
1172circt::firrtl::FModuleOp
1174{
1175 auto lmem_op = util::assertedCast<const LocalMemoryOperation>(&node->GetOperation());
1180
1181 // Create the module and its input/output ports - we use a non-standard way here
1182 // Generate a vector with all inputs and outputs of the module
1183 ::llvm::SmallVector<circt::firrtl::PortInfo> ports;
1184 // Clock and reset ports
1187 // Input bundle port
1188 // virtual in/outputs based on request/reponse ports
1189 for (size_t i = 1; i < req_node->ninputs(); ++i)
1190 {
1191 std::string name("i");
1192 name.append(std::to_string(i - 1));
1194 &ports,
1195 circt::firrtl::Direction::In,
1196 name,
1197 GetFirrtlType(req_node->input(i)->Type().get()));
1198 }
1199 for (size_t i = 0; i < res_node->noutputs(); ++i)
1200 {
1201 std::string name("o");
1202 name.append(std::to_string(i));
1204 &ports,
1205 circt::firrtl::Direction::Out,
1206 name,
1207 GetFirrtlType(res_node->output(i)->Type().get()));
1208 }
1209
1210 // Creat a name for the module
1211 auto nodeName = GetModuleName(node);
1212 mlir::StringAttr name = Builder_->getStringAttr(nodeName);
1213 // Create the module
1214 auto module = Builder_->create<circt::firrtl::FModuleOp>(
1215 Builder_->getUnknownLoc(),
1216 name,
1217 circt::firrtl::ConventionAttr::get(
1218 Builder_->getContext(),
1219 circt::firrtl::Convention::Internal),
1220 ports);
1221
1222 auto body = module.getBodyBlock();
1223
1224 size_t loads = rvsdg::TryGetOwnerNode<rvsdg::Node>(*node->output(0)->Users().begin())->noutputs();
1225
1226 // Input signals
1227 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadAddrReadys;
1228 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadAddrValids;
1229 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadAddrDatas;
1230
1231 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeAddrReadys;
1232 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeAddrValids;
1233 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeAddrDatas;
1234 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeDataReadys;
1235 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeDataValids;
1236 ::llvm::SmallVector<circt::firrtl::SubfieldOp> storeDataDatas;
1237 // the ports for loads come first and consist only of addresses. Stores have both addresses and
1238 // data
1239 for (size_t i = 1; i < req_node->ninputs(); ++i)
1240 {
1241 if (i - 1 < loads)
1242 {
1243 // Load
1244 JLM_ASSERT(storeAddrReadys.empty()); // no stores yet
1245 auto bundle = GetInPort(module, i - 1);
1246 loadAddrReadys.push_back(GetSubfield(body, bundle, "ready"));
1247 loadAddrValids.push_back(GetSubfield(body, bundle, "valid"));
1248 loadAddrDatas.push_back(GetSubfield(body, bundle, "data"));
1249 }
1250 else
1251 {
1252 // Store
1253 auto addrBundle = GetInPort(module, i - 1);
1254 storeAddrReadys.push_back(GetSubfield(body, addrBundle, "ready"));
1255 storeAddrValids.push_back(GetSubfield(body, addrBundle, "valid"));
1256 storeAddrDatas.push_back(GetSubfield(body, addrBundle, "data"));
1257 i++;
1258 auto dataBundle = GetInPort(module, i - 1);
1259 storeDataReadys.push_back(GetSubfield(body, dataBundle, "ready"));
1260 storeDataValids.push_back(GetSubfield(body, dataBundle, "valid"));
1261 storeDataDatas.push_back(GetSubfield(body, dataBundle, "data"));
1262 }
1263 }
1264
1265 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadDataReadys;
1266 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadDataValids;
1267 ::llvm::SmallVector<circt::firrtl::SubfieldOp> loadDataDatas;
1268 for (size_t i = 0; i < res_node->noutputs(); ++i)
1269 {
1270 auto bundle = GetOutPort(module, i);
1271 loadDataReadys.push_back(GetSubfield(body, bundle, "ready"));
1272 loadDataValids.push_back(GetSubfield(body, bundle, "valid"));
1273 loadDataDatas.push_back(GetSubfield(body, bundle, "data"));
1274 }
1275
1276 auto clock = GetClockSignal(module);
1277 auto reset = GetResetSignal(module);
1278 auto zeroBitValue = GetConstant(body, 1, 0);
1279 auto oneBitValue = GetConstant(body, 1, 1);
1280
1281 // memory
1282 auto arraytype = std::dynamic_pointer_cast<const llvm::ArrayType>(lmem_op->result(0));
1283 size_t depth = arraytype->nelements();
1284 auto dataType = GetFirrtlType(&arraytype->element_type());
1285 ::llvm::SmallVector<mlir::Type> memTypes;
1286 ::llvm::SmallVector<mlir::Attribute> memNames;
1287 memTypes.push_back(circt::firrtl::MemOp::getTypeForPort(
1288 depth,
1289 dataType,
1290 circt::firrtl::MemOp::PortKind::ReadWrite));
1291 memNames.push_back(Builder_->getStringAttr("rw0"));
1292 // memTypes.push_back(circt::firrtl::MemOp::getTypeForPort(depth, dataType,
1293 // circt::firrtl::MemOp::PortKind::ReadWrite));
1294 // memNames.push_back(Builder_->getStringAttr("rw1"));
1295 // TODO: figure out why writeLatency is wrong here
1296 auto memory = Builder_->create<circt::firrtl::MemOp>(
1297 Builder_->getUnknownLoc(),
1298 memTypes,
1299 2,
1300 1,
1301 depth,
1302 circt::firrtl::RUWAttr::New,
1303 memNames,
1304 "mem");
1305 body->push_back(memory);
1306 auto rw0 = memory.getPortNamed("rw0");
1307 Connect(body, GetSubfield(body, rw0, "clk"), clock);
1308 auto rw0_wmode = GetSubfield(body, rw0, "wmode");
1311 auto rw0_addr = GetSubfield(body, rw0, "addr");
1312 auto rw0_rdata = GetSubfield(body, rw0, "rdata");
1313 auto rw0_wdata = GetSubfield(body, rw0, "wdata");
1314 Connect(body, rw0_wdata, GetConstant(body, JlmSize(&arraytype->element_type()), 0));
1315 // auto rw1 = memory.getPortNamed("rw1");
1316 // Connect(body, GetSubfield(body, rw1, "clk"), clock);
1317 int addrwidth = ceil(log2(depth));
1318
1319 // do stores first, because they pass state edges on directly; having loads first might create a
1320 // combinatorial cycle
1321 for (size_t i = 0; i < storeDataReadys.size(); ++i)
1322 {
1325 }
1326 ::llvm::SmallVector<circt::firrtl::RegResetOp> loadValidRegs;
1327 for (size_t i = 0; i < loadAddrReadys.size(); ++i)
1328 {
1329 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
1330 Builder_->getUnknownLoc(),
1331 GetIntType(1),
1332 clock,
1333 reset,
1335 Builder_->getStringAttr("load_valid_" + std::to_string(i)));
1336 body->push_back(validReg);
1337 loadValidRegs.push_back(validReg);
1338 Connect(body, validReg.getResult(), zeroBitValue);
1339 Connect(body, loadDataValids[i], validReg.getResult());
1342 }
1343 // mlir::Value assigned = zeroBitValue;
1344 mlir::Block * elsewhen = body;
1345 for (size_t i = 0; i < storeDataReadys.size(); ++i)
1346 {
1347 auto whenReqFireOp =
1349 auto whenReqFireBody = whenReqFireOp.getThenBodyBuilder().getBlock();
1354 Connect(
1356 rw0_addr,
1358 elsewhen = whenReqFireOp.getElseBodyBuilder().getBlock();
1359 }
1360 for (size_t i = 0; i < loadAddrReadys.size(); ++i)
1361 {
1363 auto whenReqFireBody = whenReqFireOp.getThenBodyBuilder().getBlock();
1366 Connect(
1368 rw0_addr,
1371 elsewhen = whenReqFireOp.getElseBodyBuilder().getBlock();
1372 }
1375
1376 return module;
1377}
1378
1379circt::firrtl::FModuleOp
1381{
1383
1384 // Create the module and its input/output ports
1385 auto module = nodeToModule(node, false);
1386 auto body = module.getBodyBlock();
1387
1388 // Input signals
1389 auto inBundleAddr = GetInPort(module, 0);
1390 auto inReadyAddr = GetSubfield(body, inBundleAddr, "ready");
1391 auto inValidAddr = GetSubfield(body, inBundleAddr, "valid");
1392 auto inDataAddr = GetSubfield(body, inBundleAddr, "data");
1393
1394 auto inBundleData = GetInPort(module, 1);
1395 auto inReadyData = GetSubfield(body, inBundleData, "ready");
1396 auto inValidData = GetSubfield(body, inBundleData, "valid");
1397 auto inDataData = GetSubfield(body, inBundleData, "data");
1398
1399 ::llvm::SmallVector<circt::firrtl::SubfieldOp> inReadyStates;
1400 ::llvm::SmallVector<circt::firrtl::SubfieldOp> inValidStates;
1401 ::llvm::SmallVector<circt::firrtl::SubfieldOp> inDataStates;
1402 for (size_t i = 2; i < node->ninputs() - 1; ++i)
1403 {
1404 auto bundle = GetInPort(module, i);
1405 inReadyStates.push_back(GetSubfield(body, bundle, "ready"));
1406 inValidStates.push_back(GetSubfield(body, bundle, "valid"));
1407 inDataStates.push_back(GetSubfield(body, bundle, "data"));
1408 }
1409
1410 auto inBundleResp = GetInPort(module, node->ninputs() - 1);
1411 auto inReadyResp = GetSubfield(body, inBundleResp, "ready");
1412 auto inValidResp = GetSubfield(body, inBundleResp, "valid");
1413
1414 ::llvm::SmallVector<circt::firrtl::SubfieldOp> outReadyStates;
1415 ::llvm::SmallVector<circt::firrtl::SubfieldOp> outValidStates;
1416 ::llvm::SmallVector<circt::firrtl::SubfieldOp> outDataStates;
1417 for (size_t i = 0; i < node->noutputs() - 2; ++i)
1418 {
1419 auto bundle = GetOutPort(module, i);
1420 outReadyStates.push_back(GetSubfield(body, bundle, "ready"));
1421 outValidStates.push_back(GetSubfield(body, bundle, "valid"));
1422 outDataStates.push_back(GetSubfield(body, bundle, "data"));
1423 }
1424
1425 auto outBundleMemAddr = GetOutPort(module, node->noutputs() - 2);
1429
1430 // Output signals
1431 auto outBundleMemData = GetOutPort(module, node->noutputs() - 1);
1434
1435 auto oneBitValue = GetConstant(body, 1, 1);
1436
1437 mlir::Value canRequest = inValidAddr;
1439 for (auto vld : inValidStates)
1440 {
1442 }
1443 // TODO: for now just assume that there is always room for state edges
1444 // for (size_t i = 0; i < oValidRegs.size(); ++i)
1445 // {
1446 // // register is empty or being drained
1447 // // canRequest = AddAndOp(body, canRequest, AddOrOp(body, AddNotOp(body,
1448 // oValidRegs[i]),
1449 // // outReadyStates[i]));
1450 // canRequest = AddAndOp(body, canRequest, AddNotOp(body, oValidRegs[i].getResult()));
1451 // }
1452
1453 // Block until all inputs and no outputs are valid
1458
1459 mlir::Value outStatesReady = oneBitValue;
1460 for (size_t i = 0; i < node->noutputs() - 2; ++i)
1461 {
1465 }
1467
1468 // Handshaking
1470 // TODO: check readyness seperately?
1472 for (size_t i = 2; i < node->ninputs() - 1; ++i)
1473 {
1475 }
1476 return module;
1477}
1478
1479circt::firrtl::FModuleOp
1481{
1482 // Create the module and its input/output ports
1483 auto module = nodeToModule(node, true);
1484 auto body = module.getBodyBlock();
1485
1486 // Check if it's a load or store GetOperation
1487 bool store = dynamic_cast<const llvm::StoreNonVolatileOperation *>(&(node->GetOperation()));
1488
1489 InitializeMemReq(module);
1490 // Input signals
1491 auto inBundle0 = GetInPort(module, 0);
1492 auto inReady0 = GetSubfield(body, inBundle0, "ready");
1493 auto inValid0 = GetSubfield(body, inBundle0, "valid");
1494 auto inData0 = GetSubfield(body, inBundle0, "data");
1495
1496 auto inBundle1 = GetInPort(module, 1);
1497 auto inReady1 = GetSubfield(body, inBundle1, "ready");
1498 auto inValid1 = GetSubfield(body, inBundle1, "valid");
1499 auto inData1 = GetSubfield(body, inBundle1, "data");
1500
1501 // Stores also have a data input that needs to be handled
1502 // The input is not used by loads but code below reference
1503 // these variables so we need to define them
1504 mlir::BlockArgument inBundle2 = NULL;
1505 circt::firrtl::SubfieldOp inReady2 = NULL;
1506 circt::firrtl::SubfieldOp inValid2 = NULL;
1507 circt::firrtl::SubfieldOp inData2 = NULL;
1508 if (store)
1509 {
1510 inBundle2 = GetInPort(module, 2);
1511 inReady2 = GetSubfield(body, inBundle2, "ready");
1512 inValid2 = GetSubfield(body, inBundle2, "valid");
1513 inData2 = GetSubfield(body, inBundle2, "data");
1514 }
1515
1516 // Output signals
1517 auto outBundle0 = GetOutPort(module, 0);
1518 auto outReady0 = GetSubfield(body, outBundle0, "ready");
1519 auto outValid0 = GetSubfield(body, outBundle0, "valid");
1520 auto outData0 = GetSubfield(body, outBundle0, "data");
1521
1522 // Mem signals
1523 mlir::BlockArgument memReq = GetPort(module, "mem_req");
1524 mlir::BlockArgument memRes = GetPort(module, "mem_res");
1525
1526 auto memReqReady = GetSubfield(body, memReq, "ready");
1527 auto memReqValid = GetSubfield(body, memReq, "valid");
1528 auto memReqAddr = GetSubfield(body, memReq, "addr");
1529 auto memReqData = GetSubfield(body, memReq, "data");
1530 auto memReqWrite = GetSubfield(body, memReq, "write");
1531 auto memReqWidth = GetSubfield(body, memReq, "width");
1532
1533 auto memResValid = GetSubfield(body, memRes, "valid");
1534 auto memResData = GetSubfield(body, memRes, "data");
1535
1536 auto clock = GetClockSignal(module);
1537 auto reset = GetResetSignal(module);
1538 auto zeroBitValue = GetConstant(body, 1, 0);
1539 auto oneBitValue = GetConstant(body, 1, 1);
1540
1541 // Registers
1542 ::llvm::SmallVector<circt::firrtl::RegResetOp> oValidRegs;
1543 ::llvm::SmallVector<circt::firrtl::RegResetOp> oDataRegs;
1544 for (size_t i = 0; i < node->noutputs(); i++)
1545 {
1546 std::string validName("o");
1547 validName.append(std::to_string(i));
1548 validName.append("_valid_reg");
1549 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
1550 Builder_->getUnknownLoc(),
1551 GetIntType(1),
1552 clock,
1553 reset,
1555 Builder_->getStringAttr(validName));
1556 body->push_back(validReg);
1557 oValidRegs.push_back(validReg);
1558
1559 auto zeroValue = GetConstant(body, JlmSize(node->output(i)->Type().get()), 0);
1560 std::string dataName("o");
1561 dataName.append(std::to_string(i));
1562 dataName.append("_data_reg");
1563 auto dataReg = Builder_->create<circt::firrtl::RegResetOp>(
1564 Builder_->getUnknownLoc(),
1565 GetIntType(node->output(i)->Type().get()),
1566 clock,
1567 reset,
1568 zeroValue,
1569 Builder_->getStringAttr(dataName));
1570 body->push_back(dataReg);
1571 oDataRegs.push_back(dataReg);
1572 }
1573 auto sentReg = Builder_->create<circt::firrtl::RegResetOp>(
1574 Builder_->getUnknownLoc(),
1575 GetIntType(1),
1576 clock,
1577 reset,
1579 Builder_->getStringAttr("sent_reg"));
1580 body->push_back(sentReg);
1581
1582 mlir::Value canRequest = AddNotOp(body, sentReg.getResult());
1585 if (store)
1586 {
1588 }
1589 for (size_t i = 0; i < node->noutputs(); i++)
1590 {
1592 }
1593
1594 // Block until all inputs and no outputs are valid
1597
1598 int bitWidth = 0;
1599 if (store)
1600 {
1603 bitWidth = std::dynamic_pointer_cast<const rvsdg::BitType>(node->input(1)->Type())->nbits();
1604 }
1605 else
1606 {
1608 auto invalid = GetInvalid(body, 32);
1610 if (auto bitType = std::dynamic_pointer_cast<const rvsdg::BitType>(node->output(0)->Type()))
1611 {
1612 bitWidth = bitType->nbits();
1613 }
1614 else if (rvsdg::is<llvm::PointerType>(node->output(0)->Type()))
1615 {
1617 }
1618 else
1619 {
1620 throw util::Error("unknown width for mem request");
1621 }
1622 }
1623
1624 int log2Bytes = log2(bitWidth / 8);
1626
1627 // mem_req fire
1628 auto whenReqFireOp = AddWhenOp(body, memReqReady, false);
1629 auto whenReqFireBody = whenReqFireOp.getThenBodyBuilder().getBlock();
1631 if (store)
1632 {
1635 }
1636 else
1637 {
1640 }
1641
1642 // mem_res fire
1643 auto whenResFireOp = AddWhenOp(body, AddAndOp(body, sentReg.getResult(), memResValid), false);
1644 auto whenResFireBody = whenResFireOp.getThenBodyBuilder().getBlock();
1646 if (!store)
1647 {
1649 if (bitWidth != 64)
1650 {
1653 }
1654 else
1655 {
1657 }
1658 }
1659
1660 // Handshaking
1663 if (store)
1664 {
1666 }
1667
1671 Connect(
1672 // When o0 fires
1674 oValidRegs[0].getResult(),
1675 zeroBitValue);
1676 if (!store)
1677 {
1678 auto outBundle1 = GetOutPort(module, 1);
1679 auto outReady1 = GetSubfield(body, outBundle1, "ready");
1680 auto outValid1 = GetSubfield(body, outBundle1, "valid");
1681 auto outData1 = GetSubfield(body, outBundle1, "data");
1682
1686 Connect(
1687 // When o1 fires
1689 oValidRegs[1].getResult(),
1690 zeroBitValue);
1691 }
1692
1693 return module;
1694}
1695
1696circt::firrtl::FModuleOp
1698{
1699 // Create the module and its input/output ports
1700 auto module = nodeToModule(node);
1701 auto body = module.getBodyBlock();
1702
1703 // Input signals
1704 auto inBundle0 = GetInPort(module, 0);
1705 auto inReady0 = GetSubfield(body, inBundle0, "ready");
1706 auto inValid0 = GetSubfield(body, inBundle0, "valid");
1707 // auto inData0 = GetSubfield(body, inBundle0, "data");
1708 auto inBundle1 = GetInPort(module, 1);
1709 auto inReady1 = GetSubfield(body, inBundle1, "ready");
1710 auto inValid1 = GetSubfield(body, inBundle1, "valid");
1711 auto inData1 = GetSubfield(body, inBundle1, "data");
1712 // Output signals
1713 auto outBundle = GetOutPort(module, 0);
1714 auto outReady = GetSubfield(body, outBundle, "ready");
1715 auto outValid = GetSubfield(body, outBundle, "valid");
1716 auto outData = GetSubfield(body, outBundle, "data");
1717
1721
1726
1727 return module;
1728}
1729
1730circt::firrtl::FModuleOp
1732{
1733 // Create the module and its input/output ports
1734 auto module = nodeToModule(node);
1735 auto body = module.getBodyBlock();
1736
1737 auto clock = GetClockSignal(module);
1738 auto reset = GetResetSignal(module);
1739
1740 // Input signals
1741 auto inBundle = GetInPort(module, 0);
1742 auto inReady = GetSubfield(body, inBundle, "ready");
1743 auto inValid = GetSubfield(body, inBundle, "valid");
1744 auto inData = GetSubfield(body, inBundle, "data");
1745 // Output signals
1746 auto outBundle = GetOutPort(module, 0);
1749 auto pn = dynamic_cast<const PrintOperation *>(&node->GetOperation());
1750 auto formatString = "print node " + std::to_string(pn->id()) + ": %x\n";
1751 auto name = "print_node_" + std::to_string(pn->id());
1752 auto printValue = AddPadOp(body, inData, 64);
1753 ::llvm::SmallVector<mlir::Value> operands;
1754 operands.push_back(printValue);
1755 body->push_back(Builder_->create<circt::firrtl::PrintFOp>(
1756 Builder_->getUnknownLoc(),
1757 clock,
1758 trigger,
1760 operands,
1761 name));
1762 return module;
1763}
1764
1765circt::firrtl::FModuleOp
1767{
1768 // Create the module and its input/output ports
1769 auto module = nodeToModule(node);
1770 auto body = module.getBodyBlock();
1771
1772 auto clock = GetClockSignal(module);
1773 auto reset = GetResetSignal(module);
1774 auto zeroBitValue = GetConstant(body, 1, 0);
1775 auto oneBitValue = GetConstant(body, 1, 1);
1776
1777 std::string validName("buf_valid_reg");
1778 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
1779 Builder_->getUnknownLoc(),
1780 GetIntType(1),
1781 clock,
1782 reset,
1784 Builder_->getStringAttr(validName));
1785 body->push_back(validReg);
1786
1787 std::string dataName("buf_data_reg");
1788 auto dataReg = Builder_->create<circt::firrtl::RegResetOp>(
1789 Builder_->getUnknownLoc(),
1790 GetIntType(node->input(0)->Type().get()),
1791 clock,
1792 reset,
1794 Builder_->getStringAttr(dataName));
1795 body->push_back(dataReg);
1796
1797 auto inBundle = GetInPort(module, 0);
1798 auto inReady = GetSubfield(body, inBundle, "ready");
1799 auto inValid = GetSubfield(body, inBundle, "valid");
1800 auto inData = GetSubfield(body, inBundle, "data");
1801
1802 auto outBundle = GetOutPort(module, 0);
1803 auto outReady = GetSubfield(body, outBundle, "ready");
1804 auto outValid = GetSubfield(body, outBundle, "valid");
1805 auto outData = GetSubfield(body, outBundle, "data");
1806
1807 auto orOp = AddOrOp(body, validReg.getResult(), inValid);
1809 auto muxOp = AddMuxOp(body, validReg.getResult(), dataReg.getResult(), inData);
1811 auto notOp = AddNotOp(body, validReg.getResult());
1813
1814 // When
1816 auto whenOp = AddWhenOp(body, condition, false);
1817 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
1818 Connect(thenBody, validReg.getResult(), oneBitValue);
1819 Connect(thenBody, dataReg.getResult(), inData);
1820
1821 // When
1823 whenOp = AddWhenOp(body, condition, false);
1824 thenBody = whenOp.getThenBodyBuilder().getBlock();
1825 Connect(thenBody, validReg.getResult(), zeroBitValue);
1826
1827 return module;
1828}
1829
1830circt::firrtl::FModuleOp
1832{
1833 // Create the module and its input/output ports
1834 auto module = nodeToModule(node);
1835 auto body = module.getBodyBlock();
1836
1837 auto op = dynamic_cast<const BufferOperation *>(&(node->GetOperation()));
1838 auto capacity = op->Capacity();
1839
1840 auto clock = GetClockSignal(module);
1841 auto reset = GetResetSignal(module);
1842 auto zeroBitValue = GetConstant(body, 1, 0);
1843 auto zeroValue = GetConstant(body, JlmSize(node->input(0)->Type().get()), 0);
1844 auto oneBitValue = GetConstant(body, 1, 1);
1845
1846 // Registers
1847 ::llvm::SmallVector<circt::firrtl::RegResetOp> validRegs;
1848 ::llvm::SmallVector<circt::firrtl::RegResetOp> dataRegs;
1849 for (size_t i = 0; i <= capacity; i++)
1850 {
1851 std::string validName("buf");
1852 validName.append(std::to_string(i));
1853 validName.append("_valid_reg");
1854 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
1855 Builder_->getUnknownLoc(),
1856 GetIntType(1),
1857 clock,
1858 reset,
1860 Builder_->getStringAttr(validName));
1861 body->push_back(validReg);
1862 validRegs.push_back(validReg);
1863
1864 std::string dataName("buf");
1865 dataName.append(std::to_string(i));
1866 dataName.append("_data_reg");
1867 auto dataReg = Builder_->create<circt::firrtl::RegResetOp>(
1868 Builder_->getUnknownLoc(),
1869 GetIntType(node->input(0)->Type().get()),
1870 clock,
1871 reset,
1872 zeroValue,
1873 Builder_->getStringAttr(dataName));
1874 body->push_back(dataReg);
1875 dataRegs.push_back(dataReg);
1876 }
1877 // FIXME
1878 // Resource waste as the registers will constantly be set to zero
1879 // This simplifies the code below but might waste resources unless
1880 // the tools are clever anough to replace it with a constant
1882 Connect(body, dataRegs[capacity].getResult(), zeroValue);
1883
1884 // Add wires
1885 ::llvm::SmallVector<circt::firrtl::WireOp> shiftWires;
1886 ::llvm::SmallVector<circt::firrtl::WireOp> consumedWires;
1887 for (size_t i = 0; i <= capacity; i++)
1888 {
1889 std::string shiftName("shift_out");
1890 shiftName.append(std::to_string(i));
1891 shiftWires.push_back(AddWireOp(body, shiftName, 1));
1892 std::string consumedName("in_consumed");
1893 consumedName.append(std::to_string(i));
1894 consumedWires.push_back(AddWireOp(body, consumedName, 1));
1895 }
1896
1897 auto inBundle = GetInPort(module, 0);
1898 auto inReady = GetSubfield(body, inBundle, "ready");
1899 auto inValid = GetSubfield(body, inBundle, "valid");
1900 auto inData = GetSubfield(body, inBundle, "data");
1901
1902 auto outBundle = GetOutPort(module, 0);
1903 auto outReady = GetSubfield(body, outBundle, "ready");
1904 auto outValid = GetSubfield(body, outBundle, "valid");
1905 auto outData = GetSubfield(body, outBundle, "data");
1906
1907 // Connect out to buf0
1912 if (op->IsPassThrough())
1913 {
1914 auto notOp = AddNotOp(body, validRegs[0].getResult());
1917 auto whenOp = AddWhenOp(body, notOp, false);
1918 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
1921 }
1922 else
1923 {
1925 }
1926
1927 // The buffer is ready if the last one is empty
1928 auto notOp = AddNotOp(body, validRegs[capacity - 1].getResult());
1930
1932 for (size_t i = 0; i < capacity; ++i)
1933 {
1936
1937 // When valid reg
1938 auto whenOp = AddWhenOp(body, shiftWires[i].getResult(), false);
1939 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
1941
1942 // When will be empty
1943 auto notOp = AddNotOp(body, validRegs[i].getResult());
1945 whenOp = AddWhenOp(body, condition, false);
1946 thenBody = whenOp.getThenBodyBuilder().getBlock();
1947 // Create the condition needed in nested when
1950
1951 // Nested when valid reg
1952 whenOp = AddWhenOp(thenBody, validRegs[i + 1].getResult(), true);
1953 thenBody = whenOp.getThenBodyBuilder().getBlock();
1957
1958 // Nested else in available
1959 auto elseBody = whenOp.getElseBodyBuilder().getBlock();
1961 thenBody = nestedWhen.getThenBodyBuilder().getBlock();
1965 }
1966
1967 return module;
1968}
1969
1970circt::firrtl::FModuleOp
1972{
1973 // Create the module and its input/output ports
1974 auto module = nodeToModule(node);
1975 auto body = module.getBodyBlock();
1976
1977 auto op = dynamic_cast<const hls::AddressQueueOperation *>(&(node->GetOperation()));
1978 auto capacity = op->capacity;
1979
1980 auto clock = GetClockSignal(module);
1981 auto reset = GetResetSignal(module);
1982 auto zeroBitValue = GetConstant(body, 1, 0);
1983 auto zeroValue = GetConstant(body, JlmSize(node->input(0)->Type().get()), 0);
1984 auto oneBitValue = GetConstant(body, 1, 1);
1985
1986 // Registers
1987 ::llvm::SmallVector<circt::firrtl::RegResetOp> validRegs;
1988 ::llvm::SmallVector<circt::firrtl::RegResetOp> dataRegs;
1989 for (size_t i = 0; i <= capacity; i++)
1990 {
1991 std::string validName("buf");
1992 validName.append(std::to_string(i));
1993 validName.append("_valid_reg");
1994 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
1995 Builder_->getUnknownLoc(),
1996 GetIntType(1),
1997 clock,
1998 reset,
2000 Builder_->getStringAttr(validName));
2001 body->push_back(validReg);
2002 validRegs.push_back(validReg);
2003
2004 std::string dataName("buf");
2005 dataName.append(std::to_string(i));
2006 dataName.append("_data_reg");
2007 auto dataReg = Builder_->create<circt::firrtl::RegResetOp>(
2008 Builder_->getUnknownLoc(),
2009 GetIntType(node->input(0)->Type().get()),
2010 clock,
2011 reset,
2012 zeroValue,
2013 Builder_->getStringAttr(dataName));
2014 body->push_back(dataReg);
2015 dataRegs.push_back(dataReg);
2016 }
2017 // FIXME
2018 // Resource waste as the registers will constantly be set to zero
2019 // This simplifies the code below but might waste resources unless
2020 // the tools are clever anough to replace it with a constant
2022 Connect(body, dataRegs[capacity].getResult(), zeroValue);
2023
2024 // Add wires
2025 ::llvm::SmallVector<circt::firrtl::WireOp> shiftWires;
2026 ::llvm::SmallVector<circt::firrtl::WireOp> consumedWires;
2027 for (size_t i = 0; i <= capacity; i++)
2028 {
2029 std::string shiftName("shift_out");
2030 shiftName.append(std::to_string(i));
2031 shiftWires.push_back(AddWireOp(body, shiftName, 1));
2032 std::string consumedName("in_consumed");
2033 consumedName.append(std::to_string(i));
2034 consumedWires.push_back(AddWireOp(body, consumedName, 1));
2035 }
2036
2037 auto checkBundle = GetInPort(module, 0);
2038 auto checkReady = GetSubfield(body, checkBundle, "ready");
2039 auto checkValid = GetSubfield(body, checkBundle, "valid");
2040 auto checkData = GetSubfield(body, checkBundle, "data");
2041
2042 auto enqBundle = GetInPort(module, 1);
2043 auto enqReady = GetSubfield(body, enqBundle, "ready");
2044 auto enqValid = GetSubfield(body, enqBundle, "valid");
2045 auto enqData = GetSubfield(body, enqBundle, "data");
2046
2047 auto deqBundle = GetInPort(module, 2);
2048 auto deqReady = GetSubfield(body, deqBundle, "ready");
2049 auto deqValid = GetSubfield(body, deqBundle, "valid");
2050
2051 auto outBundle = GetOutPort(module, 0);
2052 auto outReady = GetSubfield(body, outBundle, "ready");
2053 auto outValid = GetSubfield(body, outBundle, "valid");
2054 auto outData = GetSubfield(body, outBundle, "data");
2055
2056 // Connect out to addr
2057 auto addr_in_queue_wire = AddWireOp(body, "addr_in_queue", 1);
2063
2065 // deq fire
2067 // if (op->pass_through) {
2068 // auto notOp = AddNotOp(body, validRegs[0]);
2069 // andOp = AddAndOp(body, notOp, outReady);
2070 // Connect(body, consumedWires[0], andOp);
2071 // auto whenOp = AddWhenOp(body, notOp, false);
2072 // auto thenBody = whenOp.getThenBodyBuilder().getBlock();
2073 // Connect(thenBody, outData, inData);
2074 // Connect(thenBody, outValid, inValid);
2075 // } else {
2077 // }
2078
2079 // The buffer is ready if the last one is empty
2080 auto notOp = AddNotOp(body, validRegs[capacity - 1].getResult());
2082
2084 mlir::Value addr_in_queue = zeroBitValue;
2085 for (size_t i = 0; i < capacity; ++i)
2086 {
2089
2090 // When valid reg
2091 auto whenOp = AddWhenOp(body, shiftWires[i].getResult(), false);
2092 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
2094
2095 // When will be empty
2096 auto notOp = AddNotOp(body, validRegs[i].getResult());
2098 whenOp = AddWhenOp(body, condition, false);
2099 thenBody = whenOp.getThenBodyBuilder().getBlock();
2100 // Create the condition needed in nested when
2103
2104 // Nested when valid reg
2105 whenOp = AddWhenOp(thenBody, validRegs[i + 1].getResult(), true);
2106 thenBody = whenOp.getThenBodyBuilder().getBlock();
2110
2111 // Nested else in available
2112 auto elseBody = whenOp.getElseBodyBuilder().getBlock();
2114 thenBody = nestedWhen.getThenBodyBuilder().getBlock();
2118
2120 body,
2122 AddAndOp(
2123 body,
2126 }
2127 if (op->combinatorial)
2128 {
2129 // may not be the same as addr enqueued in same cycle
2132 }
2134
2135 return module;
2136}
2137
2138circt::firrtl::FModuleOp
2140{
2141 // Create the module and its input/output ports
2142 auto module = nodeToModule(node);
2143 auto body = module.getBodyBlock();
2144
2145 auto zeroBitValue = GetConstant(body, 1, 0);
2146
2147 auto inputs = node->ninputs();
2148 auto outBundle = GetOutPort(module, 0);
2149 auto outReady = GetSubfield(body, outBundle, "ready");
2150 // Out valid
2151 auto outValid = GetSubfield(body, outBundle, "valid");
2153 // Out data
2154 auto invalid = GetInvalid(body, JlmSize(node->output(0)->Type().get()));
2155 auto outData = GetSubfield(body, outBundle, "data");
2157 // Input ready 0
2158 auto inBundle0 = GetInPort(module, 0);
2159 auto inReady0 = GetSubfield(body, inBundle0, "ready");
2160 auto inValid0 = GetSubfield(body, inBundle0, "valid");
2161 auto inData0 = GetSubfield(body, inBundle0, "data");
2162
2163 // Add discard registers
2164 auto clock = GetClockSignal(module);
2165 auto reset = GetResetSignal(module);
2166
2167 int ctr_bits = 4;
2168 auto ctr_zero = GetConstant(body, ctr_bits, 0);
2169 auto ctr_one = GetConstant(body, ctr_bits, 1);
2170 auto ctr_max = GetConstant(body, ctr_bits, (1 << ctr_bits) - 1);
2171
2172 ::llvm::SmallVector<mlir::Value> discard_queueds;
2173 ::llvm::SmallVector<circt::firrtl::WireOp> discardWires;
2174 mlir::Value any_discard_full = GetConstant(body, 1, 0);
2175 // each input has a counter that tracks how many tokens to discard
2176 // the discardWires are used to increase these counters
2177 for (size_t i = 1; i < inputs; i++)
2178 {
2179 auto inBundle = GetInPort(module, i);
2180 auto inReady = GetSubfield(body, inBundle, "ready");
2181 auto inValid = GetSubfield(body, inBundle, "valid");
2182
2183 std::string regName("i");
2184 regName.append(std::to_string(i));
2185 regName.append("_discard_ctr");
2186 auto discard_ctr_reg = Builder_->create<circt::firrtl::RegResetOp>(
2187 Builder_->getUnknownLoc(),
2189 clock,
2190 reset,
2191 ctr_zero,
2192 Builder_->getStringAttr(regName));
2193 body->push_back(discard_ctr_reg);
2194
2195 std::string wireName("i");
2196 wireName.append(std::to_string(i));
2197 wireName.append("_discard");
2198 auto discard_wire = AddWireOp(body, wireName, 1);
2199 discardWires.push_back(discard_wire);
2200 Connect(body, discard_wire.getResult(), zeroBitValue);
2201 auto discard_queued = AddNeqOp(body, discard_ctr_reg.getResult(), ctr_zero);
2203 auto discard_full = AddEqOp(body, discard_ctr_reg.getResult(), ctr_max);
2205 auto fire = AddAndOp(body, inReady, inValid);
2207 auto whenOp = AddWhenOp(
2208 body,
2209 AddAndOp(
2210 body,
2212 AddNotOp(body, discard_wire.getResult())),
2213 true);
2214 // This connect was a partial connect and is likely to not work
2215 Connect(
2216 &whenOp.getThenBlock(),
2217 discard_ctr_reg.getResult(),
2218 DropMSBs(
2219 &whenOp.getThenBlock(),
2220 AddSubOp(&whenOp.getThenBlock(), discard_ctr_reg.getResult(), ctr_one),
2221 1));
2222 auto elseWhenOp = AddWhenOp(
2223 &whenOp.getElseBlock(),
2224 AddAndOp(
2225 &whenOp.getElseBlock(),
2226 discard_wire.getResult(),
2227 AddNotOp(&whenOp.getElseBlock(), fire)),
2228 false);
2229 // This connect was a partial connect and is likely to not work
2230 Connect(
2231 &elseWhenOp.getThenBlock(),
2232 discard_ctr_reg.getResult(),
2233 DropMSBs(
2234 &elseWhenOp.getThenBlock(),
2235 AddAddOp(&elseWhenOp.getThenBlock(), discard_ctr_reg.getResult(), ctr_one),
2236 1));
2237 }
2238
2241
2242 auto matchBlock =
2244 .getThenBlock();
2245 for (size_t i = 1; i < inputs; i++)
2246 {
2247 auto inBundle = GetInPort(module, i);
2248 auto inReady = GetSubfield(matchBlock, inBundle, "ready");
2249 auto inValid = GetSubfield(matchBlock, inBundle, "valid");
2250 auto inData = GetSubfield(matchBlock, inBundle, "data");
2251
2252 auto whenBlock = &AddWhenOp(
2253 matchBlock,
2254 AddAndOp(
2255 matchBlock,
2258 false)
2259 .getThenBlock();
2263 for (size_t j = 1; j < inputs; j++)
2264 {
2265 if (i == j)
2266 {
2267 continue;
2268 }
2270 }
2271 }
2272
2273 return module;
2274}
2275
2276circt::firrtl::FModuleOp
2278{
2279 // Create the module and its input/output ports
2280 auto module = nodeToModule(node);
2281 auto body = module.getBodyBlock();
2282
2283 auto inputs = node->ninputs();
2284 auto outBundle = GetOutPort(module, 0);
2285 auto outReady = GetSubfield(body, outBundle, "ready");
2286 // Out valid
2287 auto outValid = GetSubfield(body, outBundle, "valid");
2288 auto zeroBitValue = GetConstant(body, 1, 0);
2290 // Out data
2291 auto invalid = GetInvalid(body, JlmSize(node->output(0)->Type().get()));
2292 auto outData = GetSubfield(body, outBundle, "data");
2294
2295 auto inBundle0 = GetInPort(module, 0);
2296 auto inReady0 = GetSubfield(body, inBundle0, "ready");
2297 auto inValid0 = GetSubfield(body, inBundle0, "valid");
2299 auto inData0 = GetSubfield(body, inBundle0, "data");
2300
2301 // We have already handled the first input (i.e., i == 0)
2302 for (size_t i = 1; i < inputs; i++)
2303 {
2304 auto inBundle = GetInPort(module, i);
2305 auto inReady = GetSubfield(body, inBundle, "ready");
2306 auto inValid = GetSubfield(body, inBundle, "valid");
2307 auto inData = GetSubfield(body, inBundle, "data");
2309 auto constant = GetConstant(body, JlmSize(node->input(0)->Type().get()), i - 1);
2310 auto eqOp = AddEqOp(body, inData0, constant);
2311 auto andOp = AddAndOp(body, inValid0, eqOp);
2312 auto whenOp = AddWhenOp(body, andOp, false);
2313 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
2319 }
2320 return module;
2321}
2322
2323circt::firrtl::FModuleOp
2325{
2326 // Create the module and its input/output ports
2327 auto module = nodeToModule(node);
2328 auto body = module.getBodyBlock();
2329
2330 auto zeroBitValue = GetConstant(body, 1, 0);
2331
2332 auto inBundle0 = GetInPort(module, 0);
2333 auto inReady0 = GetSubfield(body, inBundle0, "ready");
2334 auto inValid0 = GetSubfield(body, inBundle0, "valid");
2335 auto inData0 = GetSubfield(body, inBundle0, "data");
2336
2337 auto inBundle1 = GetInPort(module, 1);
2338 auto inReady1 = GetSubfield(body, inBundle1, "ready");
2339 auto inValid1 = GetSubfield(body, inBundle1, "valid");
2340 auto inData1 = GetSubfield(body, inBundle1, "data");
2341
2344
2345 auto invalid = GetInvalid(body, 1);
2346 for (size_t i = 0; i < node->noutputs(); i++)
2347 {
2348 auto outBundle = GetOutPort(module, i);
2349 auto outReady = GetSubfield(body, outBundle, "ready");
2350 auto outValid = GetSubfield(body, outBundle, "valid");
2351 auto outData = GetSubfield(body, outBundle, "data");
2354
2355 auto constant = GetConstant(body, JlmSize(node->input(0)->Type().get()), i);
2356 auto eqOp = AddEqOp(body, inData0, constant);
2358 auto whenOp = AddWhenOp(body, condition, false);
2359 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
2365 }
2366
2367 return module;
2368}
2369
2370circt::firrtl::FModuleLike
2372{
2373 if (dynamic_cast<const hls::SinkOperation *>(&(node->GetOperation())))
2374 {
2375 return MlirGenSink(node);
2376 }
2377 else if (dynamic_cast<const ForkOperation *>(&(node->GetOperation())))
2378 {
2379 return MlirGenFork(node);
2380 }
2382 {
2383 return MlirGenLoopConstBuffer(node);
2384 // } else if (dynamic_cast<const jlm::LoadOperation *>(&(node->GetOperation()))) {
2385 // return MlirGenMem(node);
2386 // } else if (dynamic_cast<const jlm::StoreOperation *>(&(node->GetOperati()))) {
2387 // return MlirGenMem(node);
2388 }
2389 else if (dynamic_cast<const LoadOperation *>(&(node->GetOperation())))
2390 {
2391 return MlirGenHlsLoad(node);
2392 }
2393 else if (dynamic_cast<const hls::DecoupledLoadOperation *>(&(node->GetOperation())))
2394 {
2395 return MlirGenExtModule(node);
2396 }
2397 else if (dynamic_cast<const hls::StoreOperation *>(&(node->GetOperation())))
2398 {
2399 return MlirGenHlsStore(node);
2400 }
2401 else if (dynamic_cast<const hls::LocalLoadOperation *>(&(node->GetOperation())))
2402 {
2403 // same as normal load for now, but with index instead of address
2404 return MlirGenHlsLoad(node);
2405 }
2407 {
2408 // same as normal store for now, but with index instead of address
2409 return MlirGenHlsStore(node);
2410 }
2411 else if (dynamic_cast<const hls::LocalMemoryOperation *>(&(node->GetOperation())))
2412 {
2413 return MlirGenHlsLocalMem(node);
2414 }
2416 {
2417 return MlirGenHlsMemResp(node);
2418 }
2419 else if (rvsdg::is<MemoryRequestOperation>(node))
2420 {
2421 return MlirGenHlsMemReq(node);
2422 }
2424 {
2425 return MlirGenPredicationBuffer(node);
2426 }
2427 else if (auto b = dynamic_cast<const BufferOperation *>(&node->GetOperation()))
2428 {
2429 JLM_ASSERT(b->Capacity());
2430 return MlirGenExtModule(node);
2431 }
2432 else if (dynamic_cast<const hls::BranchOperation *>(&(node->GetOperation())))
2433 {
2434 return MlirGenBranch(node);
2435 }
2436 else if (rvsdg::is<TriggerOperation>(node))
2437 {
2438 return MlirGenTrigger(node);
2439 }
2440 else if (rvsdg::is<StateGateOperation>(node))
2441 {
2442 return MlirGenStateGate(node);
2443 }
2444 else if (dynamic_cast<const PrintOperation *>(&(node->GetOperation())))
2445 {
2446 return MlirGenPrint(node);
2447 }
2448 else if (dynamic_cast<const AddressQueueOperation *>(&(node->GetOperation())))
2449 {
2450 return MlirGenAddrQueue(node);
2451 }
2452 else if (auto o = dynamic_cast<const MuxOperation *>(&(node->GetOperation())))
2453 {
2454 if (o->discarding)
2455 {
2456 return MlirGenSimpleNode(node);
2457 }
2458 else
2459 {
2460 return MlirGenNDMux(node);
2461 }
2462 }
2463 bool is_float = false;
2464 for (size_t i = 0; i < node->ninputs(); ++i)
2465 {
2467 }
2468 for (size_t i = 0; i < node->noutputs(); ++i)
2469 {
2471 }
2472 if (is_float)
2473 {
2474 return MlirGenExtModule(node);
2475 }
2476 return MlirGenSimpleNode(node);
2477}
2478
2479circt::firrtl::FModuleOp
2481{
2482 // Create the module and its input/output ports
2483 auto module = nodeToModule(loopNode);
2484 auto body = module.getBodyBlock();
2485
2486 auto srModule = MlirGen(loopNode->subregion(), circuitBody);
2487 // Instantiate the region
2488 auto instance =
2489 Builder_->create<circt::firrtl::InstanceOp>(Builder_->getUnknownLoc(), srModule, "sr");
2490 body->push_back(instance);
2491 // Connect the Clock
2492 auto clock = GetClockSignal(module);
2494 // Connect the Reset
2495 auto reset = GetResetSignal(module);
2496 Connect(body, GetInstancePort(instance, "reset"), reset);
2497 JLM_ASSERT(instance.getNumResults() == module.getNumPorts());
2498
2499 const size_t clockAndResetOffset = 2;
2500 for (size_t i = 0; i < loopNode->ninputs(); ++i)
2501 {
2502 auto arg = loopNode->input(i)->arguments.begin().ptr();
2503 auto sourcePort = body->getArgument(i + clockAndResetOffset);
2505 }
2506 for (size_t i = 0; i < loopNode->noutputs(); ++i)
2507 {
2508 auto res = loopNode->output(i)->results.begin().ptr();
2509 auto sinkPort = body->getArgument(i + loopNode->ninputs() + clockAndResetOffset);
2511 }
2512 return module;
2513}
2514
2515circt::firrtl::BitsPrimOp
2516RhlsToFirrtlConverter::DropMSBs(mlir::Block * body, mlir::Value value, int amount)
2517{
2518 auto type = value.getType().cast<circt::firrtl::UIntType>();
2519 auto width = type.getWidth();
2520 auto result = AddBitsOp(body, value, width.value() - 1 - amount, 0);
2521 return result;
2522}
2523
2524// Trace the argument back to the "node" generating the value
2525// Returns the output of a node or the argument of a region that has
2526// been instantiated as a module
2529{
2530 // Check if the argument is part of a LoopNode
2531 auto region = arg->region();
2532 auto node = region->node();
2533 if (dynamic_cast<LoopNode *>(node))
2534 {
2535 if (auto ba = dynamic_cast<BackEdgeArgument *>(arg))
2536 {
2537 return ba->result()->origin();
2538 }
2539 else
2540 {
2541 // Check if the argument is connected to an input,
2542 // i.e., if the argument exits the region
2543 JLM_ASSERT(arg->input() != nullptr);
2544 // Check if we are in a nested region and directly
2545 // connected to the outer regions argument
2546 auto origin = arg->input()->origin();
2547 if (auto o = dynamic_cast<rvsdg::RegionArgument *>(origin))
2548 {
2549 // Need to find the source of the outer regions argument
2550 return TraceArgument(o);
2551 }
2552 else if (auto o = dynamic_cast<rvsdg::StructuralOutput *>(origin))
2553 {
2554 // Check if we the input of one LoopNode is connected to the output of another
2555 // StructuralNode, i.e., if the input is connected to the output of another LoopNode
2556 return TraceStructuralOutput(o);
2557 }
2558 // Else we have reached the source
2559 return origin;
2560 }
2561 }
2562 // Reached the argument of a structural node that is not a LoopNode
2563 return arg;
2564}
2565
2566circt::firrtl::FModuleLike
2568{
2569 // Generate a vector with all inputs and outputs of the module
2570 ::llvm::SmallVector<circt::firrtl::PortInfo> ports;
2571
2572 // Clock and reset ports
2575 // Argument ports
2576 for (size_t i = 0; i < subRegion->narguments(); ++i)
2577 {
2578 if (!dynamic_cast<BackEdgeArgument *>(subRegion->argument(i)))
2579 {
2581 &ports,
2582 circt::firrtl::Direction::In,
2583 get_port_name(subRegion->argument(i)),
2584 GetFirrtlType(subRegion->argument(i)->Type().get()));
2585 }
2586 }
2587 // Result ports
2588 for (size_t i = 0; i < subRegion->nresults(); ++i)
2589 {
2590 if (!dynamic_cast<BackEdgeResult *>(subRegion->result(i)))
2591 {
2593 &ports,
2594 circt::firrtl::Direction::Out,
2595 get_port_name(subRegion->result(i)),
2596 GetFirrtlType(subRegion->result(i)->Type().get()));
2597 }
2598 }
2599
2600 // Create a name for the module
2601 auto moduleName = Builder_->getStringAttr("subregion_mod_" + util::strfmt(subRegion));
2602 // Now when we have all the port information we can create the module
2603 auto module = Builder_->create<circt::firrtl::FModuleOp>(
2604 Builder_->getUnknownLoc(),
2605 moduleName,
2606 circt::firrtl::ConventionAttr::get(
2607 Builder_->getContext(),
2608 circt::firrtl::Convention::Internal),
2609 ports);
2610 // Insert module into circuit body immediately so it is owned by the circuit (and cleaned up
2611 // by OwningOpRef<CircuitOp> if an exception is thrown during further processing)
2612 circuitBody->push_back(module);
2613 // Get the body of the module such that we can add contents to the module
2614 auto body = module.getBodyBlock();
2615
2616 const size_t clockAndResetOffset = 2;
2617
2618 std::unordered_map<rvsdg::Output *, mlir::Value> output_map;
2619 // Arguments
2620 for (size_t i = 0; i < subRegion->narguments(); ++i)
2621 {
2622 if (dynamic_cast<BackEdgeArgument *>(subRegion->argument(i)))
2623 {
2624 auto bundleType = GetBundleType(GetFirrtlType(subRegion->argument(i)->Type().get()));
2625 auto op = Builder_->create<circt::firrtl::WireOp>(
2626 Builder_->getUnknownLoc(),
2627 bundleType,
2628 get_port_name(subRegion->argument(i)));
2629 body->push_back(op);
2630 output_map[subRegion->argument(i)] = op.getResult();
2631 }
2632 else
2633 {
2634 auto ix = i;
2635 // handle indices of lambdas, that have no inputs and loops, that have backedges
2637 {
2638 ix = subRegion->argument(i)->input()->index();
2639 }
2640 auto sourcePort = body->getArgument(ix + clockAndResetOffset);
2641 output_map[subRegion->argument(i)] = sourcePort;
2642 }
2643 }
2644
2645 auto clock = body->getArgument(0);
2646 auto reset = body->getArgument(1);
2647 // create nod instances and connect their inputs
2648 for (const auto node : rvsdg::TopDownTraverser(subRegion))
2649 {
2650 auto instance = AddInstanceOp(circuitBody, node);
2651 body->push_back(instance);
2652 // Connect clock and reset to the instance
2653 Connect(body, instance->getResult(0), clock);
2654 Connect(body, instance->getResult(1), reset);
2655 // connect inputs
2656 for (size_t i = 0; i < node->ninputs(); ++i)
2657 {
2658 auto sourcePort = output_map[node->input(i)->origin()];
2659 auto sinkPort = instance->getResult(i + clockAndResetOffset);
2661 }
2662 // map outputs
2663 for (size_t i = 0; i < node->noutputs(); ++i)
2664 {
2665 auto outputPort = instance->getResult(i + node->ninputs() + clockAndResetOffset);
2666 output_map[node->output(i)] = outputPort;
2667 }
2668 }
2669
2670 for (size_t i = 0; i < subRegion->nresults(); ++i)
2671 {
2672 mlir::Value resultSink;
2673 if (auto ber = dynamic_cast<BackEdgeResult *>(subRegion->result(i)))
2674 {
2675 auto bundleType = GetBundleType(GetFirrtlType(subRegion->result(i)->Type().get()));
2676 auto op = Builder_->create<circt::firrtl::WireOp>(
2677 Builder_->getUnknownLoc(),
2678 bundleType,
2679 get_port_name(subRegion->result(i)));
2680 body->push_back(op);
2681 resultSink = op.getResult();
2682 // connect backedge to its argument
2683 Connect(body, output_map[ber->argument()], resultSink);
2684 }
2685 else
2686 {
2687 auto ix = i;
2688 // handle indices of lambdas, that have no outputs and loops, that have backedges
2690 {
2691 ix = subRegion->result(i)->output()->index();
2692 }
2693 resultSink = body->getArgument(ix + module.getNumInputPorts());
2694 }
2695 Connect(body, resultSink, output_map[subRegion->result(i)->origin()]);
2696 }
2697 return module;
2698}
2699
2700// Trace a structural output back to the "node" generating the value
2701// Returns the output of the node
2704{
2705 auto node = output->node();
2706
2707 // We are only expecting LoopNode to have a structural output
2708 if (!dynamic_cast<LoopNode *>(node))
2709 {
2710 throw std::logic_error("Expected a hls::LoopNode but found: " + node->DebugString());
2711 }
2712 JLM_ASSERT(output->results.size() == 1);
2713 auto origin = output->results.begin().ptr()->origin();
2714 if (auto o = dynamic_cast<rvsdg::StructuralOutput *>(origin))
2715 {
2716 // Need to trace the output of the nested structural node
2717 return TraceStructuralOutput(o);
2718 }
2719
2721 {
2722 // Found the source node
2723 return origin;
2724 }
2725 else if (dynamic_cast<rvsdg::RegionArgument *>(origin))
2726 {
2727 throw std::logic_error("Encountered pass through argument - should be eliminated");
2728 }
2729 else
2730 {
2731 throw std::logic_error("Encountered an unexpected output type");
2732 }
2733}
2734
2735// Emit a circuit
2736circt::firrtl::CircuitOp
2738{
2739 // Use OwningOpRef to ensure proper cleanup if an exception is thrown during generation
2740 mlir::OwningOpRef<circt::firrtl::CircuitOp> circuitRef;
2741
2742 // Ensure consistent naming across runs
2743 create_node_names(lambdaNode->subregion());
2744 // The same name is used for the circuit and main module
2745 auto moduleName = Builder_->getStringAttr(
2746 dynamic_cast<llvm::LlvmLambdaOperation &>(lambdaNode->GetOperation()).name() + "_lambda_mod");
2747 // Create the top level FIRRTL circuit
2748 circuitRef = mlir::OwningOpRef<circt::firrtl::CircuitOp>(
2749 Builder_->create<circt::firrtl::CircuitOp>(Builder_->getUnknownLoc(), moduleName));
2750 // The body will be populated with a list of modules
2751 auto circuitBody = circuitRef->getBodyBlock();
2752
2753 // Get the region of the function
2754 auto subRegion = lambdaNode->subregion();
2755
2756 //
2757 // Add ports
2758 //
2759 // Generate a vector with all inputs and outputs of the module
2760 ::llvm::SmallVector<circt::firrtl::PortInfo> ports;
2761
2762 // Clock and reset ports
2765
2768
2769 // Input bundle
2770 using BundleElement = circt::firrtl::BundleType::BundleElement;
2771 ::llvm::SmallVector<BundleElement> inputElements;
2772 inputElements.push_back(GetReadyElement());
2773 inputElements.push_back(GetValidElement());
2774
2775 for (size_t i = 0; i < reg_args.size(); ++i)
2776 {
2777 // don't generate ports for state edges
2778 if (reg_args[i]->Type()->Kind() == rvsdg::TypeKind::State)
2779 continue;
2780 std::string portName("data_");
2781 portName.append(std::to_string(i));
2782 inputElements.push_back(BundleElement(
2783 Builder_->getStringAttr(portName),
2784 false,
2785 GetIntType(reg_args[i]->Type().get())));
2786 }
2787 auto inputType = circt::firrtl::BundleType::get(Builder_->getContext(), inputElements);
2788 struct circt::firrtl::PortInfo iBundle = {
2789 Builder_->getStringAttr("i"), inputType, circt::firrtl::Direction::In, {},
2790 Builder_->getUnknownLoc(),
2791 };
2792 ports.push_back(iBundle);
2793
2794 // Output bundle
2795 ::llvm::SmallVector<BundleElement> outputElements;
2796 outputElements.push_back(GetReadyElement());
2797 outputElements.push_back(GetValidElement());
2798 for (size_t i = 0; i < reg_results.size(); ++i)
2799 {
2800 // don't generate ports for state edges
2801 if (reg_results[i]->Type()->Kind() == rvsdg::TypeKind::State)
2802 continue;
2803 std::string portName("data_");
2804 portName.append(std::to_string(i));
2805 outputElements.push_back(BundleElement(
2806 Builder_->getStringAttr(portName),
2807 false,
2808 GetIntType(reg_results[i]->Type().get())));
2809 }
2810 auto outputType = circt::firrtl::BundleType::get(Builder_->getContext(), outputElements);
2811 struct circt::firrtl::PortInfo oBundle = {
2812 Builder_->getStringAttr("o"), outputType, circt::firrtl::Direction::Out, {},
2813 Builder_->getUnknownLoc(),
2814 };
2815 ports.push_back(oBundle);
2816
2817 // Memory ports
2820 JLM_ASSERT(mem_resps.size() == mem_reqs.size());
2821 for (size_t i = 0; i < mem_reqs.size(); ++i)
2822 {
2823 ::llvm::SmallVector<BundleElement> memElements;
2824
2825 ::llvm::SmallVector<BundleElement> reqElements;
2826 reqElements.push_back(GetReadyElement());
2827 reqElements.push_back(GetValidElement());
2828 reqElements.push_back(BundleElement(
2829 Builder_->getStringAttr("data"),
2830 false,
2831 GetFirrtlType(mem_reqs[i]->Type().get())));
2832 auto reqType = circt::firrtl::BundleType::get(Builder_->getContext(), reqElements);
2833 memElements.push_back(BundleElement(Builder_->getStringAttr("req"), false, reqType));
2834
2835 ::llvm::SmallVector<BundleElement> resElements;
2836 resElements.push_back(GetReadyElement());
2837 resElements.push_back(GetValidElement());
2838 resElements.push_back(BundleElement(
2839 Builder_->getStringAttr("data"),
2840 false,
2841 GetFirrtlType(mem_resps[i]->Type().get())));
2842 auto resType = circt::firrtl::BundleType::get(Builder_->getContext(), resElements);
2843 memElements.push_back(BundleElement(Builder_->getStringAttr("res"), true, resType));
2844
2845 auto memType = circt::firrtl::BundleType::get(Builder_->getContext(), memElements);
2846 struct circt::firrtl::PortInfo memBundle = {
2847 Builder_->getStringAttr("mem_" + std::to_string(i)),
2848 memType,
2849 circt::firrtl::Direction::Out,
2850 {},
2851 Builder_->getUnknownLoc(),
2852 };
2853 ports.push_back(memBundle);
2854 }
2855
2856 // Now when we have all the port information we can create the module
2857 // The same name is used for the circuit and main module
2858 auto module = Builder_->create<circt::firrtl::FModuleOp>(
2859 Builder_->getUnknownLoc(),
2860 moduleName,
2861 circt::firrtl::ConventionAttr::get(
2862 Builder_->getContext(),
2863 circt::firrtl::Convention::Internal),
2864 ports);
2865 // Insert module into circuit body immediately so it is owned by the circuit (and cleaned up
2866 // by OwningOpRef<CircuitOp> if an exception is thrown during further processing)
2867 circuitBody->push_back(module);
2868 // Get the body of the module such that we can add contents to the module
2869 auto body = module.getBodyBlock();
2870
2871 // Create a module of the region
2873 // Instantiate the region
2874 auto instance =
2875 Builder_->create<circt::firrtl::InstanceOp>(Builder_->getUnknownLoc(), srModule, "sr");
2876 body->push_back(instance);
2877 // Connect the Clock
2878 auto clock = GetClockSignal(module);
2880 // Connect the Reset
2881 auto reset = GetResetSignal(module);
2882 Connect(body, GetInstancePort(instance, "reset"), reset);
2883
2884 //
2885 // Add registers to the module
2886 //
2887 // Reset when low (0 == false) 1-bit
2888 auto zeroBitValue = GetConstant(body, 1, 0);
2889
2890 // Input registers
2891 ::llvm::SmallVector<circt::firrtl::RegResetOp> inputValidRegs;
2892 ::llvm::SmallVector<circt::firrtl::RegResetOp> inputDataRegs;
2893 for (size_t i = 0; i < reg_args.size(); ++i)
2894 {
2895 std::string validName("i");
2896 validName.append(std::to_string(i));
2897 validName.append("_valid_reg");
2898 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
2899 Builder_->getUnknownLoc(),
2900 GetIntType(1),
2901 clock,
2902 reset,
2904 Builder_->getStringAttr(validName));
2905 body->push_back(validReg);
2906 inputValidRegs.push_back(validReg);
2907
2908 std::string dataName("i");
2909 dataName.append(std::to_string(i));
2910 dataName.append("_data_reg");
2911 auto dataReg = Builder_->create<circt::firrtl::RegResetOp>(
2912 Builder_->getUnknownLoc(),
2913 GetIntType(reg_args[i]->Type().get()),
2914 clock,
2915 reset,
2917 Builder_->getStringAttr(dataName));
2918 body->push_back(dataReg);
2919 inputDataRegs.push_back(dataReg);
2920
2921 auto port = GetInstancePort(instance, "a" + std::to_string(reg_args[i]->index()));
2922 auto portValid = GetSubfield(body, port, "valid");
2923 Connect(body, portValid, validReg.getResult());
2924 auto portData = GetSubfield(body, port, "data");
2925 Connect(body, portData, dataReg.getResult());
2926
2927 // When statement
2928 auto portReady = GetSubfield(body, port, "ready");
2930 auto whenOp = AddWhenOp(body, whenCondition, false);
2931
2932 // getThenBlock() cause an error during commpilation
2933 // So we first get the builder and then its associated body
2934 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
2935 Connect(thenBody, validReg.getResult(), zeroBitValue);
2936 }
2937
2938 // Output registers
2939
2940 // Need to know the number of inputs so we can calculate the
2941 // correct index for outputs
2942 ::llvm::SmallVector<circt::firrtl::RegResetOp> outputValidRegs;
2943 ::llvm::SmallVector<circt::firrtl::RegResetOp> outputDataRegs;
2944
2945 auto oneBitValue = GetConstant(body, 1, 1);
2946 for (size_t i = 0; i < reg_results.size(); ++i)
2947 {
2948 std::string validName("o");
2949 validName.append(std::to_string(i));
2950 validName.append("_valid_reg");
2951 auto validReg = Builder_->create<circt::firrtl::RegResetOp>(
2952 Builder_->getUnknownLoc(),
2953 GetIntType(1),
2954 clock,
2955 reset,
2957 Builder_->getStringAttr(validName));
2958 body->push_back(validReg);
2959 outputValidRegs.push_back(validReg);
2960
2961 std::string dataName("o");
2962 dataName.append(std::to_string(i));
2963 dataName.append("_data_reg");
2964 auto dataReg = Builder_->create<circt::firrtl::RegResetOp>(
2965 Builder_->getUnknownLoc(),
2966 GetIntType(reg_results[i]->Type().get()),
2967 clock,
2968 reset,
2970 Builder_->getStringAttr(dataName));
2971 body->push_back(dataReg);
2972 outputDataRegs.push_back(dataReg);
2973
2974 // Get the bundle
2975 auto port = GetInstancePort(instance, "r" + std::to_string(reg_results[i]->index()));
2976
2977 auto portReady = GetSubfield(body, port, "ready");
2978 auto notValidReg = Builder_->create<circt::firrtl::NotPrimOp>(
2979 Builder_->getUnknownLoc(),
2980 circt::firrtl::IntType::get(Builder_->getContext(), false, 1),
2981 validReg.getResult());
2982 body->push_back(notValidReg);
2984
2985 // When statement
2986 auto portValid = GetSubfield(body, port, "valid");
2987 auto portData = GetSubfield(body, port, "data");
2989 auto whenOp = AddWhenOp(body, whenCondition, false);
2990
2991 // getThenBlock() cause an error during commpilation
2992 // So we first get the builder and then its associated body
2993 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
2994 Connect(thenBody, validReg.getResult(), oneBitValue);
2995 Connect(thenBody, dataReg.getResult(), portData);
2996 }
2997
2998 // Create the ready signal for the input bundle
2999 mlir::Value prevAnd = oneBitValue;
3000 for (size_t i = 0; i < inputValidRegs.size(); i++)
3001 {
3002 auto notReg = Builder_->create<circt::firrtl::NotPrimOp>(
3003 Builder_->getUnknownLoc(),
3004 circt::firrtl::IntType::get(Builder_->getContext(), false, 1),
3005 inputValidRegs[i].getResult());
3006 body->push_back(notReg);
3007 auto andOp = AddAndOp(body, notReg, prevAnd);
3008 prevAnd = andOp;
3009 }
3010 auto inBundle = GetPort(module, "i");
3011 auto inReady = GetSubfield(body, inBundle, "ready");
3013
3014 // Create the valid signal for the output bundle
3016 for (size_t i = 0; i < outputValidRegs.size(); i++)
3017 {
3019 prevAnd = andOp;
3020 }
3021 auto outBundle = GetPort(module, "o");
3022 auto outValid = GetSubfield(body, outBundle, "valid");
3024
3025 // Connect output data signals
3026 for (size_t i = 0; i < outputDataRegs.size(); i++)
3027 {
3028 // don't generate ports for state edges
3029 if (reg_results[i]->Type()->Kind() == rvsdg::TypeKind::State)
3030 continue;
3031 auto outData = GetSubfield(body, outBundle, "data_" + std::to_string(i));
3033 }
3034
3035 if (inputValidRegs.size())
3036 { // avoid generating invalid firrtl for return of just a constant
3037 // Input when statement
3038 auto inValid = GetSubfield(body, inBundle, "valid");
3040 auto whenOp = AddWhenOp(body, whenCondition, false);
3041
3042 // getThenBlock() cause an error during commpilation
3043 // So we first get the builder and then its associated body
3044 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
3045 for (size_t i = 0; i < inputValidRegs.size(); i++)
3046 {
3048 // don't generate ports for state edges
3049 if (reg_args[i]->Type()->Kind() == rvsdg::TypeKind::State)
3050 continue;
3051 auto inData = GetSubfield(thenBody, inBundle, "data_" + std::to_string(i));
3053 }
3054 }
3055
3056 // Output when statement
3057 auto outReady = GetSubfield(body, outBundle, "ready");
3059 auto whenOp = AddWhenOp(body, whenCondition, false);
3060 // getThenBlock() cause an error during commpilation
3061 // So we first get the builder and then its associated body
3062 auto thenBody = whenOp.getThenBodyBuilder().getBlock();
3063 for (size_t i = 0; i < outputValidRegs.size(); i++)
3064 {
3066 }
3067
3068 // Connect the memory ports
3069 for (size_t i = 0; i < mem_reqs.size(); ++i)
3070 {
3071 auto mem_port = GetPort(module, "mem_" + std::to_string(i));
3072 auto mem_req = GetSubfield(body, mem_port, "req");
3073 auto mem_res = GetSubfield(body, mem_port, "res");
3074 auto inst_req = GetInstancePort(instance, "r" + std::to_string(mem_reqs[i]->index()));
3075 auto inst_res = GetInstancePort(instance, "a" + std::to_string(mem_resps[i]->index()));
3078 }
3079
3080 return circuitRef.release();
3081}
3082
3083/*
3084 Helper functions
3085*/
3086
3087// Returns a PortInfo of ClockType
3088void
3089RhlsToFirrtlConverter::AddClockPort(::llvm::SmallVector<circt::firrtl::PortInfo> * ports)
3090{
3091 struct circt::firrtl::PortInfo port = {
3092 Builder_->getStringAttr("clk"), circt::firrtl::ClockType::get(Builder_->getContext()),
3093 circt::firrtl::Direction::In, {},
3094 Builder_->getUnknownLoc(),
3095 };
3096 ports->push_back(port);
3097}
3098
3099// Returns a PortInfo of unsigned IntType with width of 1
3100void
3101RhlsToFirrtlConverter::AddResetPort(::llvm::SmallVector<circt::firrtl::PortInfo> * ports)
3102{
3103 struct circt::firrtl::PortInfo port = {
3104 Builder_->getStringAttr("reset"), circt::firrtl::IntType::get(Builder_->getContext(), false, 1),
3105 circt::firrtl::Direction::In, {},
3106 Builder_->getUnknownLoc(),
3107 };
3108 ports->push_back(port);
3109}
3110
3111void
3112RhlsToFirrtlConverter::AddMemReqPort(::llvm::SmallVector<circt::firrtl::PortInfo> * ports)
3113{
3114 using BundleElement = circt::firrtl::BundleType::BundleElement;
3115
3116 ::llvm::SmallVector<BundleElement> memReqElements;
3117 memReqElements.push_back(GetReadyElement());
3118 memReqElements.push_back(GetValidElement());
3119 memReqElements.push_back(BundleElement(
3120 Builder_->getStringAttr("addr"),
3121 false,
3122 circt::firrtl::IntType::get(Builder_->getContext(), false, GetPointerSizeInBits())));
3123 memReqElements.push_back(BundleElement(
3124 Builder_->getStringAttr("data"),
3125 false,
3126 circt::firrtl::IntType::get(Builder_->getContext(), false, 64)));
3127 memReqElements.push_back(BundleElement(
3128 Builder_->getStringAttr("write"),
3129 false,
3130 circt::firrtl::IntType::get(Builder_->getContext(), false, 1)));
3131 memReqElements.push_back(BundleElement(
3132 Builder_->getStringAttr("width"),
3133 false,
3134 circt::firrtl::IntType::get(Builder_->getContext(), false, 3)));
3135
3136 auto memType = circt::firrtl::BundleType::get(Builder_->getContext(), memReqElements);
3137 struct circt::firrtl::PortInfo memBundle = {
3138 Builder_->getStringAttr("mem_req"), memType, circt::firrtl::Direction::Out, {},
3139 Builder_->getUnknownLoc(),
3140 };
3141 ports->push_back(memBundle);
3142}
3143
3144void
3145RhlsToFirrtlConverter::AddMemResPort(::llvm::SmallVector<circt::firrtl::PortInfo> * ports)
3146{
3147 using BundleElement = circt::firrtl::BundleType::BundleElement;
3148
3149 ::llvm::SmallVector<BundleElement> memResElements;
3150 memResElements.push_back(GetValidElement());
3151 memResElements.push_back(BundleElement(
3152 Builder_->getStringAttr("data"),
3153 false,
3154 circt::firrtl::IntType::get(Builder_->getContext(), false, 64)));
3155
3156 auto memResType = circt::firrtl::BundleType::get(Builder_->getContext(), memResElements);
3157 struct circt::firrtl::PortInfo memResBundle = {
3158 Builder_->getStringAttr("mem_res"), memResType, circt::firrtl::Direction::In, {},
3159 Builder_->getUnknownLoc(),
3160 };
3161 ports->push_back(memResBundle);
3162}
3163
3164void
3166 ::llvm::SmallVector<circt::firrtl::PortInfo> * ports,
3167 circt::firrtl::Direction direction,
3168 std::string name,
3169 circt::firrtl::FIRRTLBaseType type)
3170{
3171 auto bundleType = GetBundleType(type);
3172 struct circt::firrtl::PortInfo bundle = {
3173 Builder_->getStringAttr(name), bundleType, direction, {}, Builder_->getUnknownLoc(),
3174 };
3175 ports->push_back(bundle);
3176}
3177
3178circt::firrtl::BundleType
3179RhlsToFirrtlConverter::GetBundleType(const circt::firrtl::FIRRTLBaseType & type)
3180{
3181 using BundleElement = circt::firrtl::BundleType::BundleElement;
3182
3183 ::llvm::SmallVector<BundleElement> elements;
3184 elements.push_back(this->GetReadyElement());
3185 elements.push_back(this->GetValidElement());
3186 elements.push_back(BundleElement(this->Builder_->getStringAttr("data"), false, type));
3187
3188 return circt::firrtl::BundleType::get(this->Builder_->getContext(), elements);
3189}
3190
3191circt::firrtl::SubfieldOp
3192RhlsToFirrtlConverter::GetSubfield(mlir::Block * body, mlir::Value value, int index)
3193{
3194 auto subfield =
3195 Builder_->create<circt::firrtl::SubfieldOp>(Builder_->getUnknownLoc(), value, index);
3196 body->push_back(subfield);
3197 return subfield;
3198}
3199
3200circt::firrtl::SubfieldOp
3202 mlir::Block * body,
3203 mlir::Value value,
3204 ::llvm::StringRef fieldName)
3205{
3206 auto subfield =
3207 Builder_->create<circt::firrtl::SubfieldOp>(Builder_->getUnknownLoc(), value, fieldName);
3208 body->push_back(subfield);
3209 return subfield;
3210}
3211
3212mlir::BlockArgument
3213RhlsToFirrtlConverter::GetPort(circt::firrtl::FModuleOp & module, std::string portName)
3214{
3215 for (size_t i = 0; i < module.getNumPorts(); ++i)
3216 {
3217 if (module.getPortName(i) == portName)
3218 {
3219 return module.getArgument(i);
3220 }
3221 }
3222 llvm_unreachable("port not found");
3223}
3224
3225mlir::OpResult
3226RhlsToFirrtlConverter::GetInstancePort(circt::firrtl::InstanceOp & instance, std::string portName)
3227{
3228 for (size_t i = 0; i < instance.getNumResults(); ++i)
3229 {
3230 // std::cout << instance.getPortName(i).str() << std::endl;
3231 if (instance.getPortName(i) == portName)
3232 {
3233 return instance->getResult(i);
3234 }
3235 }
3236 llvm_unreachable("port not found");
3237}
3238
3239mlir::BlockArgument
3240RhlsToFirrtlConverter::GetInPort(circt::firrtl::FModuleOp & module, size_t portNr)
3241{
3242 return GetPort(module, "i" + std::to_string(portNr));
3243}
3244
3245mlir::BlockArgument
3246RhlsToFirrtlConverter::GetOutPort(circt::firrtl::FModuleOp & module, size_t portNr)
3247{
3248 return GetPort(module, "o" + std::to_string(portNr));
3249}
3250
3251void
3252RhlsToFirrtlConverter::Connect(mlir::Block * body, mlir::Value sink, mlir::Value source)
3253{
3254 body->push_back(
3255 Builder_->create<circt::firrtl::ConnectOp>(Builder_->getUnknownLoc(), sink, source));
3256}
3257
3258circt::firrtl::BitsPrimOp
3259RhlsToFirrtlConverter::AddBitsOp(mlir::Block * body, mlir::Value value, int high, int low)
3260{
3261 auto intType = Builder_->getIntegerType(32);
3262 auto op = Builder_->create<circt::firrtl::BitsPrimOp>(
3263 Builder_->getUnknownLoc(),
3264 value,
3265 Builder_->getIntegerAttr(intType, high),
3266 Builder_->getIntegerAttr(intType, low));
3267 body->push_back(op);
3268 return op;
3269}
3270
3271circt::firrtl::AndPrimOp
3272RhlsToFirrtlConverter::AddAndOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3273{
3274 auto op = Builder_->create<circt::firrtl::AndPrimOp>(Builder_->getUnknownLoc(), first, second);
3275 body->push_back(op);
3276 return op;
3277}
3278
3279circt::firrtl::NodeOp
3280RhlsToFirrtlConverter::AddNodeOp(mlir::Block * body, mlir::Value value, std::string name)
3281{
3282 auto op = Builder_->create<circt::firrtl::NodeOp>(Builder_->getUnknownLoc(), value, name);
3283 body->push_back(op);
3284 return op;
3285}
3286
3287circt::firrtl::XorPrimOp
3288RhlsToFirrtlConverter::AddXorOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3289{
3290 auto op = Builder_->create<circt::firrtl::XorPrimOp>(Builder_->getUnknownLoc(), first, second);
3291 body->push_back(op);
3292 return op;
3293}
3294
3295circt::firrtl::OrPrimOp
3296RhlsToFirrtlConverter::AddOrOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3297{
3298 auto op = Builder_->create<circt::firrtl::OrPrimOp>(Builder_->getUnknownLoc(), first, second);
3299 body->push_back(op);
3300 return op;
3301}
3302
3303circt::firrtl::NotPrimOp
3304RhlsToFirrtlConverter::AddNotOp(mlir::Block * body, mlir::Value first)
3305{
3306 auto op = Builder_->create<circt::firrtl::NotPrimOp>(Builder_->getUnknownLoc(), first);
3307 body->push_back(op);
3308 return op;
3309}
3310
3311circt::firrtl::AddPrimOp
3312RhlsToFirrtlConverter::AddAddOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3313{
3314 auto op = Builder_->create<circt::firrtl::AddPrimOp>(Builder_->getUnknownLoc(), first, second);
3315 body->push_back(op);
3316 return op;
3317}
3318
3319circt::firrtl::SubPrimOp
3320RhlsToFirrtlConverter::AddSubOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3321{
3322 auto op = Builder_->create<circt::firrtl::SubPrimOp>(Builder_->getUnknownLoc(), first, second);
3323 body->push_back(op);
3324 return op;
3325}
3326
3327circt::firrtl::MulPrimOp
3328RhlsToFirrtlConverter::AddMulOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3329{
3330 auto op = Builder_->create<circt::firrtl::MulPrimOp>(Builder_->getUnknownLoc(), first, second);
3331 body->push_back(op);
3332 return op;
3333}
3334
3335circt::firrtl::DivPrimOp
3336RhlsToFirrtlConverter::AddDivOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3337{
3338 auto op = Builder_->create<circt::firrtl::DivPrimOp>(Builder_->getUnknownLoc(), first, second);
3339 body->push_back(op);
3340 return op;
3341}
3342
3343circt::firrtl::DShrPrimOp
3344RhlsToFirrtlConverter::AddDShrOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3345{
3346 auto op = Builder_->create<circt::firrtl::DShrPrimOp>(Builder_->getUnknownLoc(), first, second);
3347 body->push_back(op);
3348 return op;
3349}
3350
3351circt::firrtl::DShlPrimOp
3352RhlsToFirrtlConverter::AddDShlOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3353{
3354 auto op = Builder_->create<circt::firrtl::DShlPrimOp>(Builder_->getUnknownLoc(), first, second);
3355 body->push_back(op);
3356 return op;
3357}
3358
3359circt::firrtl::RemPrimOp
3360RhlsToFirrtlConverter::AddRemOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3361{
3362 auto op = Builder_->create<circt::firrtl::RemPrimOp>(Builder_->getUnknownLoc(), first, second);
3363 body->push_back(op);
3364 return op;
3365}
3366
3367circt::firrtl::EQPrimOp
3368RhlsToFirrtlConverter::AddEqOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3369{
3370 auto op = Builder_->create<circt::firrtl::EQPrimOp>(Builder_->getUnknownLoc(), first, second);
3371 body->push_back(op);
3372 return op;
3373}
3374
3375circt::firrtl::NEQPrimOp
3376RhlsToFirrtlConverter::AddNeqOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3377{
3378 auto op = Builder_->create<circt::firrtl::NEQPrimOp>(Builder_->getUnknownLoc(), first, second);
3379 body->push_back(op);
3380 return op;
3381}
3382
3383circt::firrtl::GTPrimOp
3384RhlsToFirrtlConverter::AddGtOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3385{
3386 auto op = Builder_->create<circt::firrtl::GTPrimOp>(Builder_->getUnknownLoc(), first, second);
3387 body->push_back(op);
3388 return op;
3389}
3390
3391circt::firrtl::GEQPrimOp
3392RhlsToFirrtlConverter::AddGeqOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3393{
3394 auto op = Builder_->create<circt::firrtl::GEQPrimOp>(Builder_->getUnknownLoc(), first, second);
3395 body->push_back(op);
3396 return op;
3397}
3398
3399circt::firrtl::LTPrimOp
3400RhlsToFirrtlConverter::AddLtOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3401{
3402 auto op = Builder_->create<circt::firrtl::LTPrimOp>(Builder_->getUnknownLoc(), first, second);
3403 body->push_back(op);
3404 return op;
3405}
3406
3407circt::firrtl::LEQPrimOp
3408RhlsToFirrtlConverter::AddLeqOp(mlir::Block * body, mlir::Value first, mlir::Value second)
3409{
3410 auto op = Builder_->create<circt::firrtl::LEQPrimOp>(Builder_->getUnknownLoc(), first, second);
3411 body->push_back(op);
3412 return op;
3413}
3414
3415circt::firrtl::MuxPrimOp
3417 mlir::Block * body,
3418 mlir::Value select,
3419 mlir::Value high,
3420 mlir::Value low)
3421{
3422 auto op =
3423 Builder_->create<circt::firrtl::MuxPrimOp>(Builder_->getUnknownLoc(), select, high, low);
3424 body->push_back(op);
3425 return op;
3426}
3427
3428circt::firrtl::AsSIntPrimOp
3429RhlsToFirrtlConverter::AddAsSIntOp(mlir::Block * body, mlir::Value value)
3430{
3431 auto op = Builder_->create<circt::firrtl::AsSIntPrimOp>(Builder_->getUnknownLoc(), value);
3432 body->push_back(op);
3433 return op;
3434}
3435
3436circt::firrtl::AsUIntPrimOp
3437RhlsToFirrtlConverter::AddAsUIntOp(mlir::Block * body, mlir::Value value)
3438{
3439 auto op = Builder_->create<circt::firrtl::AsUIntPrimOp>(Builder_->getUnknownLoc(), value);
3440 body->push_back(op);
3441 return op;
3442}
3443
3444circt::firrtl::PadPrimOp
3445RhlsToFirrtlConverter::AddPadOp(mlir::Block * body, mlir::Value value, int amount)
3446{
3447 auto op = Builder_->create<circt::firrtl::PadPrimOp>(Builder_->getUnknownLoc(), value, amount);
3448 body->push_back(op);
3449 return op;
3450}
3451
3452circt::firrtl::CvtPrimOp
3453RhlsToFirrtlConverter::AddCvtOp(mlir::Block * body, mlir::Value value)
3454{
3455 auto op = Builder_->create<circt::firrtl::CvtPrimOp>(Builder_->getUnknownLoc(), value);
3456 body->push_back(op);
3457 return op;
3458}
3459
3460circt::firrtl::WireOp
3461RhlsToFirrtlConverter::AddWireOp(mlir::Block * body, std::string name, int size)
3462{
3463 auto op =
3464 Builder_->create<circt::firrtl::WireOp>(Builder_->getUnknownLoc(), GetIntType(size), name);
3465 body->push_back(op);
3466 return op;
3467}
3468
3469circt::firrtl::WhenOp
3471{
3472 auto op =
3473 Builder_->create<circt::firrtl::WhenOp>(Builder_->getUnknownLoc(), condition, elseStatement);
3474 body->push_back(op);
3475 return op;
3476}
3477
3478void
3480 mlir::Value value,
3481 ::llvm::SmallPtrSet<mlir::Value, 16> & forbiddenDependencies,
3482 ::llvm::SmallPtrSet<mlir::Value, 16> & visited)
3483{
3484 if (visited.contains(value))
3485 {
3486 return;
3487 }
3488 visited.insert(value);
3489 if (forbiddenDependencies.contains(value))
3490 {
3491 throw util::Error("forbidden dependency detected");
3492 }
3493 auto op = value.getDefiningOp();
3494 // don't check anything for registers - connects don't count since they don't form combinatorial
3495 // circuits
3496 if (mlir::dyn_cast<circt::firrtl::RegResetOp>(op))
3497 {
3498 return;
3499 }
3500 else if (mlir::dyn_cast<circt::firrtl::RegOp>(op))
3501 {
3502 return;
3503 }
3504 // check uses because of connects
3505 for (auto & use : value.getUses())
3506 {
3507 auto * user = use.getOwner();
3508 if (auto connectOp = mlir::dyn_cast<circt::firrtl::ConnectOp>(user))
3509 {
3510 if (connectOp.getDest() == value)
3511 {
3513 }
3514 }
3515 else
3516 {
3517 }
3518 }
3519 // stop at port level
3520 if (mlir::dyn_cast<circt::firrtl::SubfieldOp>(op))
3521 {
3522 return;
3523 }
3524 JLM_ASSERT(op->getNumResults() == 1);
3525 for (size_t i = 0; i < op->getNumOperands(); ++i)
3526 {
3527 check_may_not_depend_on(op->getOperand(i), forbiddenDependencies, visited);
3528 }
3529}
3530
3531void
3533 ::llvm::SmallVector<mlir::Value> & oReadys,
3534 ::llvm::SmallVector<mlir::Value> & oValids)
3535{
3536 ::llvm::SmallPtrSet<mlir::Value, 16> forbiddenDependencies(oReadys.begin(), oReadys.end());
3537 for (auto oValid : oValids)
3538 {
3539 ::llvm::SmallPtrSet<mlir::Value, 16> visited;
3541 }
3542}
3543
3544void
3545RhlsToFirrtlConverter::check_module(circt::firrtl::FModuleOp & module)
3546{
3547 // check if module/node obeys ready/valid semantics at the circuit level
3548
3549 // compile time: ovalid and odata may not depend on oready
3550 ::llvm::SmallVector<mlir::Value> oReadys;
3551 ::llvm::SmallVector<mlir::Value> oValids;
3552 ::llvm::SmallVector<mlir::Value> oDatas;
3553 for (size_t i = 0; i < module.getNumPorts(); ++i)
3554 {
3555 auto portName = module.getPortName(i);
3556 auto port = module.getArgument(i);
3557 if (portName.starts_with("o"))
3558 {
3559 // out port
3560 for (auto & use : port.getUses())
3561 {
3562 auto * user = use.getOwner();
3563 if (auto subfieldOp = mlir::dyn_cast<circt::firrtl::SubfieldOp>(user))
3564 {
3565 auto subfieldName =
3566 subfieldOp.getInput().getType().cast<circt::firrtl::BundleType>().getElementName(
3567 subfieldOp.getFieldIndex());
3568 if (subfieldName == "ready")
3569 {
3570 oReadys.push_back(subfieldOp);
3571 }
3572 else if (subfieldName == "valid")
3573 {
3574 oValids.push_back(subfieldOp);
3575 }
3576 else if (subfieldName == "data")
3577 {
3578 oDatas.push_back(subfieldOp);
3579 }
3580 }
3581 else
3582 {
3583 user->print(::llvm::outs());
3584 llvm_unreachable("unexpected GetOperation");
3585 }
3586 }
3587 }
3588 }
3591
3592#ifdef FIRRTL_RUNTIME_ASSERTIONS
3593 // run time: valid/ready may not go down without firing once they are up - insert assertions
3594 auto body = &module.getBody().back();
3595 auto clock = GetClockSignal(module);
3596 auto reset = GetResetSignal(module);
3597 auto zeroBitValue = GetConstant(body, 1, 0);
3598 for (size_t i = 0; i < module.getNumPorts(); ++i)
3599 {
3600 auto portName = module.getPortName(i);
3601 auto port = module.getArgument(i);
3602 if (portName.starts_with("o") || portName.starts_with("i"))
3603 {
3604 auto ready = GetSubfield(body, port, "ready");
3605 auto valid = GetSubfield(body, port, "valid");
3606 auto data = GetSubfield(body, port, "data");
3607 if (data.getResult().getType().dyn_cast<circt::firrtl::BundleType>())
3608 {
3609 // skip memory ports
3610 continue;
3611 }
3612 auto fire = AddAndOp(body, ready, valid);
3613 auto prev_ready_reg = Builder_->create<circt::firrtl::RegResetOp>(
3614 Builder_->getUnknownLoc(),
3615 GetIntType(1),
3616 clock,
3617 reset,
3619 std::string(portName) + "_prev_ready_reg");
3620 body->push_back(prev_ready_reg);
3621 auto prev_valid_reg = Builder_->create<circt::firrtl::RegResetOp>(
3622 Builder_->getUnknownLoc(),
3623 GetIntType(1),
3624 clock,
3625 reset,
3627 std::string(portName) + "_prev_valid_reg");
3628 body->push_back(prev_valid_reg);
3629 auto prev_data_reg = Builder_->create<circt::firrtl::RegOp>(
3630 Builder_->getUnknownLoc(),
3631 data.getResult().getType(),
3632 clock,
3633 std::string(portName) + "_prev_data_reg");
3634 body->push_back(prev_data_reg);
3635 Connect(body, prev_ready_reg.getResult(), ready);
3636 Connect(body, prev_valid_reg.getResult(), valid);
3637 Connect(body, prev_data_reg.getResult(), data);
3638 auto fireBody = &AddWhenOp(body, fire, false).getThenBlock();
3641
3642 auto valid_assert = Builder_->create<circt::firrtl::AssertOp>(
3643 Builder_->getUnknownLoc(),
3644 clock,
3646 AddNotOp(body, reset),
3647 std::string(portName) + "_valid went down without firing",
3648 mlir::ValueRange(),
3649 std::string(portName) + "_valid_assert");
3650 body->push_back(valid_assert);
3651
3652 auto ready_assert = Builder_->create<circt::firrtl::AssertOp>(
3653 Builder_->getUnknownLoc(),
3654 clock,
3656 AddNotOp(body, reset),
3657 std::string(portName) + "_ready went down without firing",
3658 mlir::ValueRange(),
3659 std::string(portName) + "_ready_assert");
3660 body->push_back(ready_assert);
3661
3662 auto data_assert = Builder_->create<circt::firrtl::AssertOp>(
3663 Builder_->getUnknownLoc(),
3664 clock,
3665 AddNotOp(
3666 body,
3667 AddAndOp(
3668 body,
3669 prev_valid_reg.getResult(),
3670 AddNeqOp(body, prev_data_reg.getResult(), data))),
3671 AddNotOp(body, reset),
3672 std::string(portName) + "_data changed without firing",
3673 mlir::ValueRange(),
3674 std::string(portName) + "_data_assert");
3675 body->push_back(data_assert);
3676 }
3677 }
3678#endif // FIRRTL_RUNTIME_ASSERTIONS
3679}
3680
3681circt::firrtl::InstanceOp
3683{
3684 auto name = GetModuleName(node);
3685 // Check if the module has already been instantiated else we need to generate it
3686 if (auto sn = dynamic_cast<rvsdg::SimpleNode *>(node))
3687 {
3688 if (!modules[name])
3689 {
3690 auto module = MlirGen(sn);
3691 if (circt::isa<circt::firrtl::FModuleOp>(module))
3692 check_module(circt::cast<circt::firrtl::FModuleOp>(module));
3693 modules[name] = module;
3694 circuitBody->push_back(module);
3695 }
3696 }
3697 else
3698 {
3699 auto ln = dynamic_cast<LoopNode *>(node);
3700 JLM_ASSERT(ln);
3701 auto module = MlirGen(ln, circuitBody);
3702 modules[name] = module;
3703 circuitBody->push_back(module);
3704 }
3705 // We increment a counter for each node that is instantiated
3706 // to assure the name is unique while still being relatively
3707 // easy to read (which helps when debugging).
3708 auto node_name = get_node_name(node);
3709 return Builder_->create<circt::firrtl::InstanceOp>(
3710 Builder_->getUnknownLoc(),
3711 modules[name],
3712 node_name);
3713}
3714
3715circt::firrtl::ConstantOp
3716RhlsToFirrtlConverter::GetConstant(mlir::Block * body, int size, int value)
3717{
3718 auto intType = GetIntType(size);
3719 auto constant = Builder_->create<circt::firrtl::ConstantOp>(
3720 Builder_->getUnknownLoc(),
3721 intType,
3722 ::llvm::APInt(size, value));
3723 body->push_back(constant);
3724 return constant;
3725}
3726
3727circt::firrtl::InvalidValueOp
3729{
3730
3731 auto invalid =
3732 Builder_->create<circt::firrtl::InvalidValueOp>(Builder_->getUnknownLoc(), GetIntType(size));
3733 body->push_back(invalid);
3734 return invalid;
3735}
3736
3737void
3738RhlsToFirrtlConverter::ConnectInvalid(mlir::Block * body, mlir::Value value)
3739{
3740
3741 auto invalid =
3742 Builder_->create<circt::firrtl::InvalidValueOp>(Builder_->getUnknownLoc(), value.getType());
3743 body->push_back(invalid);
3744 return Connect(body, value, invalid);
3745}
3746
3747// Get the clock signal in the module
3748mlir::BlockArgument
3749RhlsToFirrtlConverter::GetClockSignal(circt::firrtl::FModuleOp module)
3750{
3751 auto clock = module.getArgument(0);
3752 auto ctype = clock.getType().cast<circt::firrtl::FIRRTLType>();
3753 if (!ctype.isa<circt::firrtl::ClockType>())
3754 {
3755 JLM_ASSERT("Not a ClockType");
3756 }
3757 return clock;
3758}
3759
3760// Get the reset signal in the module
3761mlir::BlockArgument
3762RhlsToFirrtlConverter::GetResetSignal(circt::firrtl::FModuleOp module)
3763{
3764 auto reset = module.getArgument(1);
3765 auto rtype = reset.getType().cast<circt::firrtl::FIRRTLType>();
3766 if (!rtype.isa<circt::firrtl::ResetType>())
3767 {
3768 JLM_ASSERT("Not a ResetType");
3769 }
3770 return reset;
3771}
3772
3773circt::firrtl::BundleType::BundleElement
3775{
3776 using BundleElement = circt::firrtl::BundleType::BundleElement;
3777
3778 return BundleElement(
3779 Builder_->getStringAttr("ready"),
3780 true,
3781 circt::firrtl::IntType::get(Builder_->getContext(), false, 1));
3782}
3783
3784circt::firrtl::BundleType::BundleElement
3786{
3787 using BundleElement = circt::firrtl::BundleType::BundleElement;
3788
3789 return BundleElement(
3790 Builder_->getStringAttr("valid"),
3791 false,
3792 circt::firrtl::IntType::get(Builder_->getContext(), false, 1));
3793}
3794
3795void
3796RhlsToFirrtlConverter::InitializeMemReq(circt::firrtl::FModuleOp module)
3797{
3798 mlir::BlockArgument mem = GetPort(module, "mem_req");
3799 mlir::Block * body = module.getBodyBlock();
3800
3801 auto zeroBitValue = GetConstant(body, 1, 0);
3802 auto invalid1 = GetInvalid(body, 1);
3803 auto invalid3 = GetInvalid(body, 3);
3805 auto invalid64 = GetInvalid(body, 64);
3806
3807 auto memValid = GetSubfield(body, mem, "valid");
3808 auto memAddr = GetSubfield(body, mem, "addr");
3809 auto memData = GetSubfield(body, mem, "data");
3810 auto memWrite = GetSubfield(body, mem, "write");
3811 auto memWidth = GetSubfield(body, mem, "width");
3812
3818}
3819
3820// Takes a jlm::rvsdg::Node and creates a firrtl module with an input
3821// bundle for each node input and output bundle for each node output
3822// Returns a circt::firrtl::FModuleOp with an empty body
3823circt::firrtl::FModuleOp
3825{
3826 // Generate a vector with all inputs and outputs of the module
3827 ::llvm::SmallVector<circt::firrtl::PortInfo> ports;
3828
3829 // Clock and reset ports
3832 // Input bundle port
3833 for (size_t i = 0; i < node->ninputs(); ++i)
3834 {
3835 std::string name("i");
3836 name.append(std::to_string(i));
3838 &ports,
3839 circt::firrtl::Direction::In,
3840 name,
3841 GetFirrtlType(node->input(i)->Type().get()));
3842 }
3843 for (size_t i = 0; i < node->noutputs(); ++i)
3844 {
3845 std::string name("o");
3846 name.append(std::to_string(i));
3848 &ports,
3849 circt::firrtl::Direction::Out,
3850 name,
3851 GetFirrtlType(node->output(i)->Type().get()));
3852 }
3853
3854 if (mem)
3855 {
3858 }
3859
3860 // Creat a name for the module
3861 auto nodeName = GetModuleName(node);
3862 mlir::StringAttr name = Builder_->getStringAttr(nodeName);
3863 // Create the module
3864 return Builder_->create<circt::firrtl::FModuleOp>(
3865 Builder_->getUnknownLoc(),
3866 name,
3867 circt::firrtl::ConventionAttr::get(
3868 Builder_->getContext(),
3869 circt::firrtl::Convention::Internal),
3870 ports);
3871}
3872
3873//
3874// HLS only works with wires so all types are represented as unsigned integers
3875//
3876
3877// Returns IntType of the specified width
3878circt::firrtl::IntType
3880{
3881 return circt::firrtl::IntType::get(Builder_->getContext(), false, size);
3882}
3883
3884// Return unsigned IntType with the bit width specified by the
3885// jlm::rvsdg::type. The extend argument extends the width of the IntType,
3886// which is useful for, e.g., additions where the result has to be 1
3887// larger than the operands to accommodate for the carry.
3888circt::firrtl::IntType
3890{
3891 return circt::firrtl::IntType::get(Builder_->getContext(), false, JlmSize(type) + extend);
3892}
3893
3894circt::firrtl::FIRRTLBaseType
3896{
3897 if (auto bt = dynamic_cast<const BundleType *>(type))
3898 {
3899 using BundleElement = circt::firrtl::BundleType::BundleElement;
3900 ::llvm::SmallVector<BundleElement> elements;
3901 for (size_t i = 0; i < bt->elements_.size(); ++i)
3902 {
3903 auto t = &bt->elements_.at(i);
3904 elements.push_back(
3905 BundleElement(Builder_->getStringAttr(t->first), false, GetFirrtlType(t->second.get())));
3906 }
3907 return circt::firrtl::BundleType::get(Builder_->getContext(), elements);
3908 }
3909 else
3910 {
3911 return GetIntType(type);
3912 }
3913}
3914
3915std::string
3917{
3918
3919 std::string append = "";
3920 for (size_t i = 0; i < node->ninputs(); ++i)
3921 {
3922 append.append("_I");
3923 append.append(std::to_string(JlmSize(node->input(i)->Type().get())));
3924 append.append("W");
3925 }
3926 for (size_t i = 0; i < node->noutputs(); ++i)
3927 {
3928 append.append("_O");
3929 append.append(std::to_string(JlmSize(node->output(i)->Type().get())));
3930 append.append("W");
3931 }
3932 if (auto op = dynamic_cast<const llvm::GetElementPtrOperation *>(&node->GetOperation()))
3933 {
3934 const rvsdg::Type * pointeeType = op->getPointeeType().get();
3935 for (size_t i = 1; i < node->ninputs(); i++)
3936 {
3937 int bits = JlmSize(pointeeType);
3938 if (dynamic_cast<const jlm::rvsdg::BitType *>(pointeeType)
3939 || dynamic_cast<const llvm::FloatingPointType *>(pointeeType))
3940 {
3941 pointeeType = nullptr;
3942 }
3943 else if (auto arrayType = dynamic_cast<const llvm::ArrayType *>(pointeeType))
3944 {
3945 pointeeType = &arrayType->element_type();
3946 }
3947 else if (auto vectorType = dynamic_cast<const llvm::VectorType *>(pointeeType))
3948 {
3949 pointeeType = vectorType->Type().get();
3950 }
3951 else
3952 {
3953 throw std::logic_error(pointeeType->debug_string() + " pointer not implemented!");
3954 }
3955 int bytes = bits / 8;
3956 append.append("_");
3957 append.append(std::to_string(bytes));
3958 }
3959 }
3960 if (auto op = dynamic_cast<const MemoryRequestOperation *>(&node->GetOperation()))
3961 {
3962 auto loadTypes = op->GetLoadTypes();
3963 for (size_t i = 0; i < loadTypes->size(); i++)
3964 {
3965 auto loadType = loadTypes->at(i).get();
3966 int bitWidth = JlmSize(loadType);
3967 append.append("_");
3968 append.append(std::to_string(bitWidth));
3969 }
3970 }
3971 if (auto op = dynamic_cast<const LocalMemoryOperation *>(&node->GetOperation()))
3972 {
3973 append.append("_S");
3974 append.append(std::to_string(
3975 std::dynamic_pointer_cast<const llvm::ArrayType>(op->result(0))->nelements()));
3976 append.append("_L");
3977 size_t loads =
3978 rvsdg::TryGetOwnerNode<rvsdg::Node>(*node->output(0)->Users().begin())->noutputs();
3979 append.append(std::to_string(loads));
3980 append.append("_S");
3981 size_t stores =
3982 (rvsdg::TryGetOwnerNode<rvsdg::Node>(*node->output(1)->Users().begin())->ninputs() - 1
3983 - loads)
3984 / 2;
3985 append.append(std::to_string(stores));
3986 }
3987 if (dynamic_cast<const LoopOperation *>(&node->GetOperation()))
3988 {
3989 append.append("_");
3990 append.append(util::strfmt(node));
3991 }
3992 auto name = jlm::util::strfmt("op_", node->DebugString() + append);
3993 // Remove characters that are not valid in firrtl module names
3994 std::replace_if(name.begin(), name.end(), isForbiddenChar, '_');
3995 return name;
3996}
3997
3998bool
4000{
4001 for (const auto & pair : op)
4002 {
4003 if (pair.first != pair.second)
4004 return false;
4005 }
4006
4007 return true;
4008}
4009
4010// Used for debugging a module by wrapping it in a circuit and writing it to a file
4011// Node is simply a convenience for generating the circuit name
4012void
4014 const circt::firrtl::FModuleOp fModuleOp,
4015 const rvsdg::Node * node)
4016{
4017 if (!fModuleOp)
4018 return;
4019
4020 auto name = GetModuleName(node);
4021 auto moduleName = Builder_->getStringAttr(name);
4022
4023 // Adde the fModuleOp to a circuit
4024 auto circuit = Builder_->create<circt::firrtl::CircuitOp>(Builder_->getUnknownLoc(), moduleName);
4025 auto body = circuit.getBodyBlock();
4026 body->push_back(fModuleOp);
4027
4029}
4030
4031// Verifies the circuit and writes the FIRRTL to a file
4032void
4033RhlsToFirrtlConverter::WriteCircuitToFile(const circt::firrtl::CircuitOp circuit, std::string name)
4034{
4035 // Add the circuit to a top module
4036 auto module = mlir::ModuleOp::create(Builder_->getUnknownLoc());
4037 module.push_back(circuit);
4038
4039 // Verify the module
4040 if (failed(mlir::verify(module)))
4041 {
4042 module.emitError("module verification error");
4043 throw std::logic_error("Verification of firrtl failed");
4044 }
4045 // Print the FIRRTL IR
4046 module.print(::llvm::outs());
4047
4048 // Write the module to file
4049 std::string fileName = name + extension();
4050 std::error_code EC;
4051 ::llvm::raw_fd_ostream output(fileName, EC);
4052 size_t targetLineLength = 100;
4053 auto status = circt::firrtl::exportFIRFile(module, output, targetLineLength, DefaultFIRVersion_);
4054
4055 if (status.failed())
4056 {
4057 throw util::Error("Exporting of FIRRTL failed");
4058 }
4059
4060 output.close();
4061 std::cout << "\nWritten firrtl to " << fileName << "\n";
4062}
4063
4064std::string
4065RhlsToFirrtlConverter::toString(const circt::firrtl::CircuitOp circuit)
4066{
4067 // Add the circuit to a top module
4068 auto module = mlir::ModuleOp::create(Builder_->getUnknownLoc());
4069 module.push_back(circuit);
4070
4071 // Verify the module
4072 if (failed(mlir::verify(module)))
4073 {
4074 module.emitError("module verification error");
4075 module.print(::llvm::outs());
4076 throw std::logic_error("Verification of firrtl failed");
4077 }
4078
4079 // Export FIRRTL to string
4080 std::string outputString;
4081 ::llvm::raw_string_ostream output(outputString);
4082
4083 size_t targetLineLength = 100;
4084 auto status = circt::firrtl::exportFIRFile(module, output, targetLineLength, DefaultFIRVersion_);
4085 if (status.failed())
4086 throw std::logic_error("Exporting of firrtl failed");
4087
4088 return outputString;
4089}
4090
4091circt::firrtl::FExtModuleOp
4093{
4094 // Generate a vector with all inputs and outputs of the module
4095 ::llvm::SmallVector<circt::firrtl::PortInfo> ports;
4096
4097 // Clock and reset ports
4100 // Input bundle port
4101 for (size_t i = 0; i < node->ninputs(); ++i)
4102 {
4103 std::string name("i");
4104 name.append(std::to_string(i));
4106 &ports,
4107 circt::firrtl::Direction::In,
4108 name,
4109 GetFirrtlType(node->input(i)->Type().get()));
4110 }
4111 for (size_t i = 0; i < node->noutputs(); ++i)
4112 {
4113 std::string name("o");
4114 name.append(std::to_string(i));
4116 &ports,
4117 circt::firrtl::Direction::Out,
4118 name,
4119 GetFirrtlType(node->output(i)->Type().get()));
4120 }
4121
4122 // Creat a name for the module
4123 auto nodeName = GetModuleName(node);
4124 mlir::StringAttr name = Builder_->getStringAttr(nodeName);
4125 // Create the module
4126 return Builder_->create<circt::firrtl::FExtModuleOp>(
4127 Builder_->getUnknownLoc(),
4128 name,
4129 circt::firrtl::ConventionAttr::get(
4130 Builder_->getContext(),
4131 circt::firrtl::Convention::Internal),
4132 ports);
4133}
4134} // namespace jlm::hls
static const auto vt
Definition PullTests.cpp:16
std::vector< rvsdg::RegionResult * > get_mem_reqs(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:93
std::vector< rvsdg::RegionArgument * > get_reg_args(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:112
static std::string get_port_name(jlm::rvsdg::Input *port)
Definition base-hls.cpp:62
void create_node_names(rvsdg::Region *r)
Definition base-hls.cpp:116
static int JlmSize(const jlm::rvsdg::Type *type)
Definition base-hls.cpp:110
std::unordered_map< jlm::rvsdg::Output *, std::string > output_map
Definition base-hls.hpp:45
std::string get_node_name(const rvsdg::Node *node)
Definition base-hls.cpp:28
std::vector< rvsdg::RegionResult * > get_reg_results(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:130
std::vector< rvsdg::RegionArgument * > get_mem_resps(const rvsdg::LambdaNode &lambda)
Definition base-hls.hpp:75
std::size_t Capacity() const noexcept
Definition hls.hpp:406
bool IsConstant() const noexcept
Definition hls.hpp:189
const std::vector< std::shared_ptr< const rvsdg::Type > > * GetLoadTypes() const
Definition hls.hpp:1357
circt::firrtl::InstanceOp AddInstanceOp(mlir::Block *circuitBody, jlm::rvsdg::Node *node)
circt::firrtl::GEQPrimOp AddGeqOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::FModuleOp MlirGenBuffer(const jlm::rvsdg::SimpleNode *node)
mlir::BlockArgument GetPort(circt::firrtl::FModuleOp &module, std::string portName)
circt::firrtl::FModuleOp MlirGenHlsMemReq(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::EQPrimOp AddEqOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::LTPrimOp AddLtOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::BitsPrimOp AddBitsOp(mlir::Block *body, mlir::Value value, int high, int low)
void check_module(circt::firrtl::FModuleOp &module)
circt::firrtl::XorPrimOp AddXorOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::FModuleOp MlirGenNDMux(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::BundleType GetBundleType(const circt::firrtl::FIRRTLBaseType &type)
circt::firrtl::BitsPrimOp DropMSBs(mlir::Block *body, mlir::Value value, int amount)
circt::firrtl::WireOp AddWireOp(mlir::Block *body, std::string name, int size)
void AddMemResPort(::llvm::SmallVector< circt::firrtl::PortInfo > *ports)
circt::firrtl::RemPrimOp AddRemOp(mlir::Block *body, mlir::Value first, mlir::Value second)
std::unordered_map< std::string, circt::firrtl::FModuleLike > modules
circt::firrtl::FModuleOp MlirGenFork(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::FModuleOp nodeToModule(const jlm::rvsdg::Node *node, bool mem=false)
void WriteModuleToFile(const circt::firrtl::FModuleOp fModuleOp, const rvsdg::Node *node)
rvsdg::Output * TraceStructuralOutput(rvsdg::StructuralOutput *out)
circt::firrtl::NodeOp AddNodeOp(mlir::Block *body, mlir::Value value, std::string name)
circt::firrtl::FModuleOp MlirGenTrigger(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::DShrPrimOp AddDShrOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::NEQPrimOp AddNeqOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::FModuleOp MlirGenPrint(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::FModuleOp MlirGenHlsMemResp(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::BundleType::BundleElement GetReadyElement()
circt::firrtl::BundleType::BundleElement GetValidElement()
void AddResetPort(::llvm::SmallVector< circt::firrtl::PortInfo > *ports)
circt::firrtl::FModuleOp MlirGenStateGate(const jlm::rvsdg::SimpleNode *node)
const circt::firrtl::FIRVersion DefaultFIRVersion_
void InitializeMemReq(circt::firrtl::FModuleOp module)
circt::firrtl::FModuleOp MlirGenSink(const jlm::rvsdg::SimpleNode *node)
void Connect(mlir::Block *body, mlir::Value sink, mlir::Value source)
circt::firrtl::FModuleOp MlirGenHlsStore(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::FIRRTLBaseType GetFirrtlType(const jlm::rvsdg::Type *type)
void AddMemReqPort(::llvm::SmallVector< circt::firrtl::PortInfo > *ports)
circt::firrtl::DShlPrimOp AddDShlOp(mlir::Block *body, mlir::Value first, mlir::Value second)
void WriteCircuitToFile(const circt::firrtl::CircuitOp circuit, std::string name)
bool IsIdentityMapping(const rvsdg::MatchOperation &op)
circt::firrtl::CircuitOp MlirGen(const rvsdg::LambdaNode *lamdaNode)
circt::firrtl::FModuleOp MlirGenSimpleNode(const jlm::rvsdg::SimpleNode *node)
mlir::BlockArgument GetResetSignal(circt::firrtl::FModuleOp module)
std::string toString(const circt::firrtl::CircuitOp circuit)
circt::firrtl::ConstantOp GetConstant(mlir::Block *body, int size, int value)
circt::firrtl::InvalidValueOp GetInvalid(mlir::Block *body, int size)
mlir::BlockArgument GetOutPort(circt::firrtl::FModuleOp &module, size_t portNr)
circt::firrtl::FModuleOp MlirGenBranch(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::SubPrimOp AddSubOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::PadPrimOp AddPadOp(mlir::Block *body, mlir::Value value, int amount)
circt::firrtl::MuxPrimOp AddMuxOp(mlir::Block *body, mlir::Value select, mlir::Value high, mlir::Value low)
circt::firrtl::WhenOp AddWhenOp(mlir::Block *body, mlir::Value condition, bool elseStatment)
circt::firrtl::AsUIntPrimOp AddAsUIntOp(mlir::Block *body, mlir::Value value)
circt::firrtl::FModuleOp MlirGenPredicationBuffer(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::NotPrimOp AddNotOp(mlir::Block *body, mlir::Value first)
circt::firrtl::FModuleOp MlirGenAddrQueue(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::GTPrimOp AddGtOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::FExtModuleOp MlirGenExtModule(const jlm::rvsdg::SimpleNode *node)
void AddClockPort(::llvm::SmallVector< circt::firrtl::PortInfo > *ports)
circt::firrtl::FModuleOp MlirGenHlsDLoad(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::FModuleOp MlirGenLoopConstBuffer(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::OrPrimOp AddOrOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::IntType GetIntType(int size)
circt::firrtl::MulPrimOp AddMulOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::LEQPrimOp AddLeqOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::FModuleOp MlirGenHlsLocalMem(const jlm::rvsdg::SimpleNode *node)
std::string GetModuleName(const rvsdg::Node *node)
jlm::rvsdg::Output * TraceArgument(rvsdg::RegionArgument *arg)
std::unique_ptr<::mlir::OpBuilder > Builder_
circt::firrtl::AsSIntPrimOp AddAsSIntOp(mlir::Block *body, mlir::Value value)
circt::firrtl::FModuleOp MlirGenDMux(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::DivPrimOp AddDivOp(mlir::Block *body, mlir::Value first, mlir::Value second)
circt::firrtl::FModuleOp MlirGenHlsLoad(const jlm::rvsdg::SimpleNode *node)
circt::firrtl::AddPrimOp AddAddOp(mlir::Block *body, mlir::Value first, mlir::Value second)
mlir::BlockArgument GetInPort(circt::firrtl::FModuleOp &module, size_t portNr)
circt::firrtl::CvtPrimOp AddCvtOp(mlir::Block *body, mlir::Value value)
circt::firrtl::SubfieldOp GetSubfield(mlir::Block *body, mlir::Value value, int index)
mlir::OpResult GetInstancePort(circt::firrtl::InstanceOp &instance, std::string portName)
mlir::BlockArgument GetClockSignal(circt::firrtl::FModuleOp module)
circt::firrtl::AndPrimOp AddAndOp(mlir::Block *body, mlir::Value first, mlir::Value second)
void ConnectInvalid(mlir::Block *body, mlir::Value value)
circt::firrtl::FModuleOp MlirGenMem(const jlm::rvsdg::SimpleNode *node)
void AddBundlePort(::llvm::SmallVector< circt::firrtl::PortInfo > *ports, circt::firrtl::Direction direction, std::string name, circt::firrtl::FIRRTLBaseType type)
const std::string & name() const noexcept
Definition lambda.hpp:42
UndefValueOperation class.
Output * origin() const noexcept
Definition node.hpp:58
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:67
NodeInput * input(size_t index) const noexcept
Definition node.hpp:615
NodeOutput * output(size_t index) const noexcept
Definition node.hpp:650
virtual std::string DebugString() const =0
virtual const Operation & GetOperation() const noexcept=0
size_t ninputs() const noexcept
Definition node.hpp:609
size_t noutputs() const noexcept
Definition node.hpp:644
rvsdg::Region * region() const noexcept
Definition node.cpp:151
UsersRange Users()
Definition node.hpp:354
const std::shared_ptr< const rvsdg::Type > & Type() const noexcept
Definition node.hpp:366
Represents the argument of a region.
Definition region.hpp:41
StructuralInput * input() const noexcept
Definition region.hpp:69
Represent acyclic RVSDG subgraphs.
Definition region.hpp:213
rvsdg::StructuralNode * node() const noexcept
Definition region.hpp:301
const SimpleOperation & GetOperation() const noexcept override
std::string DebugString() const override
NodeInput * input(size_t index) const noexcept
NodeOutput * output(size_t index) const noexcept
StructuralNode * node() const noexcept
constexpr Type() noexcept
Definition type.hpp:46
virtual std::string debug_string() const =0
size_type size() const noexcept
Iterator begin() noexcept
#define JLM_ASSERT(x)
Definition common.hpp:16
void check_may_not_depend_on(mlir::Value value, ::llvm::SmallPtrSet< mlir::Value, 16 > &forbiddenDependencies, ::llvm::SmallPtrSet< mlir::Value, 16 > &visited)
void check_oValids(::llvm::SmallVector< mlir::Value > &oReadys, ::llvm::SmallVector< mlir::Value > &oValids)
bool isForbiddenChar(char c)
Definition base-hls.cpp:16
size_t GetPointerSizeInBits()
Definition hls.cpp:396
static std::vector< jlm::rvsdg::Output * > operands(const Node *node)
Definition node.hpp:1049
@ State
Designate a state type.
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872
detail::TopDownTraverserGeneric< false > TopDownTraverser
Traverser for visiting every node in a region in a top down order.
static std::string strfmt(Args... args)
Definition strfmt.hpp:35