7#include <QRegularExpression>
25 return QStringLiteral(
"embedded:") + ic->
blobName();
27 QString key = QFileInfo(ic->
file()).canonicalFilePath();
28 return key.isEmpty() ? ic->
file() : key;
33 , m_elements(elements)
35 if (!m_file.open(QIODevice::WriteOnly | QIODevice::Text)) {
39 throw PANDACEPTION(
"Could not open file for writing: %1", fileName);
41 m_stream.setDevice(&m_file);
44 m_availablePins = QStringList{
61 QFileInfo info(fileName);
62 m_fileName = info.completeBaseName();
65QString SystemVerilogCodeGen::highLow(
const Status val)
67 return (val == Status::Active) ?
"1'b1" :
"1'b0";
71QString SystemVerilogCodeGen::removeForbiddenChars(
const QString &input)
77bool SystemVerilogCodeGen::isSimpleIdentifier(
const QString &expr)
79 if (expr.isEmpty())
return false;
80 static QRegularExpression re(
"^[a-zA-Z_][a-zA-Z0-9_]*$");
81 return re.match(expr).hasMatch();
86QString SystemVerilogCodeGen::ensureSimpleSignal(
const QString &expr)
88 if (expr.isEmpty() || expr ==
"1'b0" || expr ==
"1'b1")
return expr;
89 if (isSimpleIdentifier(expr))
return expr;
92 QString wireName = QString(
"aux_async_%1").arg(m_globalCounter++);
93 m_stream <<
" wire " << wireName <<
" = " << expr <<
";" << Qt::endl;
97QString SystemVerilogCodeGen::otherPortName(
Port *port)
100 return otherPortNameImpl(port, visited);
103QString SystemVerilogCodeGen::otherPortNameImpl(
Port *port, QSet<Port *> &visited)
105 if (!port)
return "1'b0";
108 if (visited.contains(port)) {
109 QString mapped = m_varMap.value(port);
110 return mapped.isEmpty() ?
"1'b0" : mapped;
114 QString mapped = m_varMap.value(port);
115 if (!mapped.isEmpty())
return mapped;
118 if (elm && elm->wirelessMode() == WirelessMode::Rx && !elm->label().isEmpty()) {
119 auto *txInputPort = m_txInputPorts.value(elm->label(),
nullptr);
121 return otherPortNameImpl(txInputPort, visited);
127 auto *otherPort = port->
connections().constFirst()->otherPort(port);
131 if (visited.contains(otherPort)) {
132 QString mapped = m_varMap.value(otherPort);
133 return mapped.isEmpty() ?
"1'b0" : mapped;
137 visited.insert(port);
139 auto *elm = otherPort->graphicElement();
141 QString mapped = m_varMap.value(otherPort);
142 return mapped.isEmpty() ? highLow(port->
defaultValue()) : mapped;
148 QString mapped = m_varMap.value(otherPort);
149 if (!mapped.isEmpty()) {
153 if (elm->elementType() == ElementType::And ||
154 elm->elementType() == ElementType::Or ||
155 elm->elementType() == ElementType::Nand ||
156 elm->elementType() == ElementType::Nor ||
157 elm->elementType() == ElementType::Xor ||
158 elm->elementType() == ElementType::Xnor ||
159 elm->elementType() == ElementType::Not ||
160 elm->elementType() == ElementType::Node) {
162 return generateLogicExpressionImpl(elm, visited);
171 static const QSet<QString> reserved = {
172 "always",
"and",
"assign",
"automatic",
"begin",
"buf",
"bufif0",
"bufif1",
173 "case",
"casex",
"casez",
"cell",
"cmos",
"config",
"deassign",
"default",
174 "defparam",
"design",
"disable",
"edge",
"else",
"end",
"endcase",
175 "endconfig",
"endfunction",
"endgenerate",
"endmodule",
"endprimitive",
176 "endspecify",
"endtable",
"endtask",
"event",
"for",
"force",
"forever",
177 "fork",
"function",
"generate",
"genvar",
"highz0",
"highz1",
"if",
178 "ifnone",
"incdir",
"include",
"initial",
"inout",
"input",
179 "instance",
"integer",
"join",
"large",
"liblist",
"library",
"localparam",
180 "macromodule",
"medium",
"module",
"nand",
"negedge",
"nmos",
"nor",
181 "noshowcancelled",
"not",
"notif0",
"notif1",
"or",
"output",
"parameter",
182 "pmos",
"posedge",
"primitive",
"pull0",
"pull1",
"pulldown",
"pullup",
183 "pulsestyle_onevent",
"pulsestyle_ondetect",
"rcmos",
"real",
"realtime",
184 "reg",
"release",
"repeat",
"rnmos",
"rpmos",
"rtran",
"rtranif0",
185 "rtranif1",
"scalared",
"showcancelled",
"signed",
"small",
"specify",
186 "specparam",
"strong0",
"strong1",
"supply0",
"supply1",
"table",
"task",
187 "time",
"tran",
"tranif0",
"tranif1",
"tri",
"tri0",
"tri1",
"triand",
188 "trior",
"trireg",
"unsigned",
"use",
"uwire",
"vectored",
"wait",
189 "wand",
"weak0",
"weak1",
"while",
"wire",
"wor",
"xnor",
"xor"
191 return reserved.contains(name);
195void SystemVerilogCodeGen::collectICTypes(
const QVector<GraphicElement *> &elements)
197 for (
auto *elm : elements) {
198 if (elm->elementType() != ElementType::IC) {
202 auto *ic = qobject_cast<IC *>(elm);
208 if (m_icModules.contains(key)) {
217 QString baseName = ic->
isEmbedded() ? ic->blobName() : QFileInfo(ic->file()).baseName();
224 QSet<QString> usedNames;
225 for (
int i = 0; i < ic->inputSize(); ++i) {
226 QString portLabel = ic->inputPort(i)->name();
228 if (portName.isEmpty() || portName ==
"_unnamed") {
229 portName = QString(
"in_%1").arg(i);
233 portName =
"p_" + portName;
236 QString original = portName;
238 while (usedNames.contains(portName)) {
239 portName = QString(
"%1_%2").arg(original).arg(suffix++);
241 usedNames.insert(portName);
246 for (
int i = 0; i < ic->outputSize(); ++i) {
247 QString portLabel = ic->outputPort(i)->name();
249 if (portName.isEmpty() || portName ==
"_unnamed") {
250 portName = QString(
"out_%1").arg(i);
254 portName =
"p_" + portName;
256 QString original = portName;
258 while (usedNames.contains(portName)) {
259 portName = QString(
"%1_%2").arg(original).arg(suffix++);
261 usedNames.insert(portName);
265 m_icModules.insert(key, info);
268 collectICTypes(ic->internalElements());
278QSet<GraphicElement *> SystemVerilogCodeGen::findFeedbackElements(
const QVector<GraphicElement *> &elements)
280 const QSet<GraphicElement *> elementSet(elements.cbegin(), elements.cend());
282 auto isGate = [](GraphicElement *e) {
283 switch (e->elementType()) {
284 case ElementType::And:
case ElementType::Or:
case ElementType::Nand:
285 case ElementType::Nor:
case ElementType::Xor:
case ElementType::Xnor:
286 case ElementType::Not:
case ElementType::Node:
294 auto successors = [&](GraphicElement *elm) {
295 QVector<GraphicElement *> succ;
296 for (
auto *outPort : elm->outputs()) {
297 const auto conns = outPort->connections();
298 for (
auto *conn : conns) {
300 Port *other = conn->otherPort(outPort);
301 if (!other)
continue;
303 if (h && elementSet.contains(h) && isGate(h)) {
311 QSet<GraphicElement *> feedback;
312 for (
auto *start : elements) {
313 if (!isGate(start))
continue;
315 QSet<GraphicElement *> seen;
316 QVector<GraphicElement *> stack = successors(start);
317 while (!stack.isEmpty()) {
318 GraphicElement *n = stack.takeLast();
320 feedback.insert(start);
323 if (seen.contains(n))
continue;
325 stack += successors(n);
332void SystemVerilogCodeGen::generateICModules()
334 if (m_icModules.isEmpty()) {
339 QStringList sortedKeys = m_icModules.keys();
340 std::sort(sortedKeys.begin(), sortedKeys.end());
342 bool progress =
true;
345 for (
const QString &key : std::as_const(sortedKeys)) {
346 ICModuleInfo &info = m_icModules[key];
352 bool allDepsReady =
true;
354 if (elm->elementType() != ElementType::IC) {
357 auto *nestedIC = qobject_cast<IC *>(elm);
358 if (!nestedIC)
continue;
361 if (!m_icModules.value(nestedKey).generated) {
362 allDepsReady =
false;
368 generateSingleICModule(info);
377void SystemVerilogCodeGen::generateSingleICModule(
ICModuleInfo &info)
380 QHash<Port *, QString> savedVarMap = m_varMap;
381 QHash<IC *, QString> savedInstanceNames = m_instanceNames;
382 int savedCounter = m_globalCounter;
385 m_instanceNames.clear();
387 m_generatingICModule =
true;
399 m_stream <<
"module " << info.
moduleName <<
" (" << Qt::endl;
402 QStringList portDecls;
403 for (
int i = 0; i < info.
inputNames.size(); ++i) {
404 portDecls << QString(
" input %1").arg(info.
inputNames[i]);
406 for (
int i = 0; i < info.
outputNames.size(); ++i) {
407 portDecls << QString(
" output %1").arg(info.
outputNames[i]);
409 m_stream << portDecls.join(
",\n") << Qt::endl;
410 m_stream <<
");" << Qt::endl;
415 QSet<GraphicElement *> boundaryNodes;
423 m_varMap[nodeElm->outputPort(0)] = info.
inputNames[i];
424 boundaryNodes.insert(nodeElm);
434 m_varMap[nodeElm->outputPort(0)] = info.
outputNames[i];
435 boundaryNodes.insert(nodeElm);
439 QVector<GraphicElement *> internalElements;
441 if (!boundaryNodes.contains(elm)) {
442 internalElements.append(elm);
448 m_feedbackElements = findFeedbackElements(internalElements);
449 const bool hasFeedback = !m_feedbackElements.isEmpty();
452 m_stream << Qt::endl;
454 m_stream <<
"/* verilator lint_off UNOPTFLAT */ // intentional latch feedback" << Qt::endl;
456 declareAuxVariablesRec(internalElements);
457 m_stream << Qt::endl;
460 m_stream <<
"// Internal logic" << Qt::endl;
461 assignVariablesRec(internalElements);
464 m_stream << Qt::endl;
472 QString value = otherPortName(nodeElm->inputPort(0));
473 m_stream <<
"assign " << info.
outputNames[i] <<
" = " << value <<
";" << Qt::endl;
477 m_stream <<
"/* verilator lint_on UNOPTFLAT */" << Qt::endl;
479 m_feedbackElements.clear();
481 m_stream <<
"endmodule" << Qt::endl;
482 m_stream << Qt::endl;
485 m_varMap = savedVarMap;
486 m_instanceNames = savedInstanceNames;
487 m_globalCounter = savedCounter;
488 m_generatingICModule =
false;
495 m_stream <<
"// ==================================================================== //" << Qt::endl;
496 m_stream <<
"// ======= This code was generated automatically by wiRedPanda ======== //" << Qt::endl;
497 m_stream <<
"// ==================================================================== //" << Qt::endl;
502 collectICTypes(m_elements);
507 QSet<QString> usedModuleNames;
508 usedModuleNames.insert(m_fileName);
509 QStringList collisionKeys = m_icModules.keys();
510 std::sort(collisionKeys.begin(), collisionKeys.end());
511 for (
const QString &key : std::as_const(collisionKeys)) {
512 QString name = m_icModules[key].moduleName;
513 while (usedModuleNames.contains(name)) {
516 m_icModules[key].moduleName = name;
517 usedModuleNames.insert(name);
523 m_stream <<
"module " << m_fileName <<
" (" << Qt::endl;
527 declareAuxVariables();
534void SystemVerilogCodeGen::declareInputs()
539 int totalOutputs = 0;
540 int currentOutput = 0;
541 for (
auto *elm : m_elements) {
542 const auto type = elm->elementType();
543 if (elm->elementGroup() == ElementGroup::Output) {
544 totalOutputs = INT_MAX;
546 }
else if ((type == ElementType::InputButton) || (type == ElementType::InputSwitch) || (type == ElementType::Clock) || (type == ElementType::InputRotary)) {
547 totalOutputs +=
static_cast<int>(elm->outputs().size());
551 m_stream <<
"/* ========= Inputs ========== */" << Qt::endl;
553 for (
auto *elm : m_elements) {
554 const auto type = elm->elementType();
556 if ((type == ElementType::InputButton) || (type == ElementType::InputSwitch) || (type == ElementType::Clock) || (type == ElementType::InputRotary)) {
557 QString baseName = elm->objectName() + QString::number(counter);
558 const QString label = elm->label();
560 if (!label.isEmpty()) {
561 baseName +=
"_" + label;
565 baseName = removeForbiddenChars(baseName);
571 for (
int port = 0; port < elm->outputSize(); ++port) {
572 QString varName = (elm->outputSize() > 1) ? QString(
"%1_%2").arg(baseName).arg(port) : baseName;
575 if (currentOutput < totalOutputs) {
576 m_stream << QString(
"input %1,").arg(varName) << Qt::endl;
578 m_stream << QString(
"input %1").arg(varName) << Qt::endl;
581 m_inputMap.append(MappedPinSystemVerilog(elm,
"", varName, elm->outputPort(port), port));
583 m_varMap[elm->outputPort(port)] = varName;
589 m_stream << Qt::endl;
592void SystemVerilogCodeGen::declareOutputs()
596 int totalOutputs = 0;
597 int currentOutput = 0;
598 for (
auto *elm : m_elements) {
599 if (elm->elementGroup() == ElementGroup::Output) {
600 totalOutputs +=
static_cast<int>(elm->inputs().size());
607 m_stream <<
"/* ========= Outputs ========== */" << Qt::endl;
608 for (
auto *elm : m_elements) {
609 if (elm->elementGroup() == ElementGroup::Output) {
610 QString label = elm->label();
611 for (
int i = 0; i < elm->inputs().size(); ++i) {
613 QString varName = elm->objectName() + QString::number(counter);
614 if (!label.isEmpty()) {
615 varName = QString(
"%1_%2").arg(varName, label);
617 Port *port = elm->inputPort(i);
618 if (!port->
name().isEmpty()) {
619 varName = QString(
"%1_%2").arg(varName, port->
name());
622 varName = removeForbiddenChars(varName);
623 if (currentOutput < totalOutputs) {
624 m_stream << QString(
"output %1,").arg(varName) << Qt::endl;
626 m_stream << QString(
"output %1").arg(varName) << Qt::endl;
628 m_outputMap.append(MappedPinSystemVerilog(elm,
"", varName, port, i));
633 m_stream <<
");" << Qt::endl;
636void SystemVerilogCodeGen::declareAuxVariablesRec(
const QVector<GraphicElement *> &elements)
638 for (
auto *elm : elements) {
639 if (elm->elementType() == ElementType::IC) {
640 auto *ic = qobject_cast<IC *>(elm);
645 const ICModuleInfo &info = m_icModules.value(key);
648 QString instanceName = QString(
"%1_inst_%2").arg(info.
moduleName).arg(m_globalCounter++);
649 m_instanceNames[ic] = instanceName;
655 QString wireName = QString(
"w_%1_%2").arg(instanceName, info.
outputNames.value(i, QString(
"out_%1").arg(i)));
657 m_stream <<
"wire " << wireName <<
";" << Qt::endl;
664 const auto type = elm->elementType();
665 if ((type == ElementType::InputButton ||
666 type == ElementType::InputSwitch ||
667 type == ElementType::Clock ||
668 type == ElementType::InputRotary) &&
669 !m_varMap.value(elm->outputPort()).isEmpty()) {
676 if (!m_generatingICModule &&
677 (type == ElementType::And ||
678 type == ElementType::Or ||
679 type == ElementType::Nand ||
680 type == ElementType::Nor ||
681 type == ElementType::Xor ||
682 type == ElementType::Xnor ||
683 type == ElementType::Not ||
684 type == ElementType::Node)) {
688 QString varName = QString(
"aux_%1_%2").arg(removeForbiddenChars(
CodeGenUtils::stripAccents(elm->objectName()))).arg(m_globalCounter++);
689 const auto outputs = elm->outputs();
692 QSet<Port *> preMapped;
694 if (outputs.size() == 1) {
695 Port *port = outputs.constFirst();
697 if (elm->elementType() == ElementType::InputVcc) {
698 m_varMap[port] =
"1'b1";
702 if (elm->elementType() == ElementType::InputGnd) {
703 m_varMap[port] =
"1'b0";
707 if (m_varMap.value(port).isEmpty()) {
708 m_varMap[port] = varName;
710 preMapped.insert(port);
715 for (
auto *port : outputs) {
716 if (!m_varMap.value(port).isEmpty()) {
717 preMapped.insert(port);
722 QString portName = varName;
723 portName.append(QString(
"_%1").arg(portCounter++));
725 if (!port->
name().isEmpty()) {
729 m_varMap[port] = portName;
733 for (
auto *port : outputs) {
735 if (preMapped.contains(port)) {
739 QString varName2 = m_varMap.value(port);
741 switch (elm->elementType()) {
743 case ElementType::DLatch:
744 case ElementType::SRLatch:
745 case ElementType::SRFlipFlop:
746 case ElementType::DFlipFlop:
747 case ElementType::TFlipFlop:
748 case ElementType::JKFlipFlop: {
749 m_stream <<
"reg " << varName2 << QString(
" = 1'b%1;").arg(aux) << Qt::endl;
754 case ElementType::TruthTable:
755 if (!outputs.isEmpty()) {
756 Port *outputPort = outputs.constFirst();
757 QString ttVarName = QString(
"%1_output").arg(removeForbiddenChars(elm->objectName()));
758 m_varMap[outputPort] = ttVarName;
759 m_stream << QString(
"reg ") << ttVarName <<
";" << Qt::endl;
765 case ElementType::Mux:
766 case ElementType::Demux: {
768 m_stream <<
"reg " << varName2 <<
" = 1'b0;" << Qt::endl;
772 case ElementType::And:
773 case ElementType::AudioBox:
774 case ElementType::Buzzer:
775 case ElementType::Clock:
776 case ElementType::Display14:
777 case ElementType::Display16:
778 case ElementType::Display7:
779 case ElementType::IC:
780 case ElementType::InputButton:
781 case ElementType::InputGnd:
782 case ElementType::InputRotary:
783 case ElementType::InputSwitch:
784 case ElementType::InputVcc:
785 case ElementType::JKLatch:
786 case ElementType::Led:
787 case ElementType::Line:
788 case ElementType::Nand:
789 case ElementType::Node:
790 case ElementType::Nor:
791 case ElementType::Not:
792 case ElementType::Or:
793 case ElementType::Text:
794 case ElementType::Unknown:
795 case ElementType::Xnor:
796 case ElementType::Xor:
797 if (m_feedbackElements.contains(elm)) {
801 m_stream <<
"reg " << varName2 <<
" = " << highLow(port->
status()) <<
";" << Qt::endl;
803 m_stream <<
"wire " << varName2 <<
";" << Qt::endl;
812void SystemVerilogCodeGen::declareAuxVariables()
814 m_stream <<
"/* ====== Aux. Variables ====== */" << Qt::endl;
815 declareAuxVariablesRec(m_elements);
816 m_stream << Qt::endl;
823void SystemVerilogCodeGen::emitSequentialBlock(
824 const QString &typeName,
825 const QString &clk,
const QString &rawPrst,
const QString &rawClr,
826 const std::function<
void()> &emitPresetBody,
827 const std::function<
void()> &emitClearBody,
828 const std::function<
void()> &emitNormalBody)
830 bool hasPrst = (rawPrst !=
"1'b1" && rawPrst !=
"1'b0");
831 bool hasClr = (rawClr !=
"1'b1" && rawClr !=
"1'b0");
835 QString prst = hasPrst ? ensureSimpleSignal(rawPrst) : rawPrst;
836 QString clr = hasClr ? ensureSimpleSignal(rawClr) : rawClr;
838 m_stream <<
" //" << typeName << Qt::endl;
841 if (hasPrst && hasClr) {
842 m_stream <<
" always @(posedge " << clk <<
" or negedge " << prst <<
" or negedge " << clr <<
")" << Qt::endl;
843 }
else if (hasPrst) {
844 m_stream <<
" always @(posedge " << clk <<
" or negedge " << prst <<
")" << Qt::endl;
846 m_stream <<
" always @(posedge " << clk <<
" or negedge " << clr <<
")" << Qt::endl;
848 m_stream <<
" always @(posedge " << clk <<
")" << Qt::endl;
851 m_stream <<
" begin" << Qt::endl;
853 if (!hasPrst && !hasClr) {
857 bool needsElse =
false;
860 m_stream <<
" if (~" << prst <<
")" << Qt::endl;
861 m_stream <<
" begin" << Qt::endl;
863 m_stream <<
" end" << Qt::endl;
869 m_stream <<
" else if (~" << clr <<
")" << Qt::endl;
871 m_stream <<
" if (~" << clr <<
")" << Qt::endl;
873 m_stream <<
" begin" << Qt::endl;
875 m_stream <<
" end" << Qt::endl;
878 m_stream <<
" else" << Qt::endl;
879 m_stream <<
" begin" << Qt::endl;
881 m_stream <<
" end" << Qt::endl;
884 m_stream <<
" end" << Qt::endl;
885 m_stream <<
" //End of " << typeName << Qt::endl;
888void SystemVerilogCodeGen::assignVariablesRec(
const QVector<GraphicElement *> &elements)
890 for (
auto *elm : elements) {
891 if (elm->elementType() == ElementType::IC) {
892 auto *ic = qobject_cast<IC *>(elm);
897 const ICModuleInfo &info = m_icModules.value(key);
898 QString instanceName = m_instanceNames.value(ic);
901 m_stream << info.
moduleName <<
" " << instanceName <<
" (" << Qt::endl;
904 for (
int i = 0; i < ic->
inputSize(); ++i) {
905 QString inputValue = otherPortName(ic->
inputPort(i));
906 m_stream <<
" ." << info.
inputNames.value(i, QString(
"in_%1").arg(i))
907 <<
"(" << inputValue <<
")";
908 if (i < ic->inputSize() - 1 || ic->
outputSize() > 0) {
911 m_stream << Qt::endl;
916 QString outputWire = m_varMap.value(ic->
outputPort(i));
917 m_stream <<
" ." << info.
outputNames.value(i, QString(
"out_%1").arg(i))
918 <<
"(" << outputWire <<
")";
919 if (i < ic->outputSize() - 1) {
922 m_stream << Qt::endl;
925 m_stream <<
");" << Qt::endl;
928 if (elm->inputs().isEmpty() || elm->outputs().isEmpty()) {
934 if (elm->elementType() == ElementType::And ||
935 elm->elementType() == ElementType::Or ||
936 elm->elementType() == ElementType::Nand ||
937 elm->elementType() == ElementType::Nor ||
938 elm->elementType() == ElementType::Xor ||
939 elm->elementType() == ElementType::Xnor ||
940 elm->elementType() == ElementType::Not ||
941 elm->elementType() == ElementType::Node) {
943 QString expr = generateLogicExpression(elm);
944 for (
auto *port : elm->outputs()) {
945 QString existingVar = m_varMap.value(port);
946 if (!existingVar.isEmpty() && m_generatingICModule) {
947 const bool isFeedback = m_feedbackElements.contains(elm);
952 m_stream <<
"always @(*) " << existingVar <<
" = " << expr <<
";" << Qt::endl;
955 m_stream <<
"assign " << existingVar <<
" = " << expr <<
";" << Qt::endl;
959 m_varMap[port] = expr;
965 QString firstOut = m_varMap.value(elm->outputPort(0));
966 switch (elm->elementType()) {
967 case ElementType::DLatch: {
968 QString secondOut = m_varMap.value(elm->outputPort(1));
969 QString data = otherPortName(elm->inputPort(0));
970 QString enable = otherPortName(elm->inputPort(1));
971 m_stream << QString(
" //D Latch") << Qt::endl;
972 m_stream << QString(
" always @(*)") << Qt::endl;
973 m_stream << QString(
" begin") << Qt::endl;
974 m_stream << QString(
" if (%1)").arg(enable) << Qt::endl;
975 m_stream << QString(
" begin") << Qt::endl;
977 m_stream << QString(
" %1 = %2;").arg(firstOut, data) << Qt::endl;
978 QString dataBar = data.startsWith(
"~") ? data.mid(1) : (
"~" + data);
979 m_stream << QString(
" %1 = %2;").arg(secondOut, dataBar) << Qt::endl;
980 m_stream << QString(
" end") << Qt::endl;
981 m_stream << QString(
" end") << Qt::endl;
982 m_stream << QString(
" //End of D Latch") << Qt::endl;
986 case ElementType::SRLatch: {
987 QString secondOut = m_varMap.value(elm->outputPort(1));
988 QString s = otherPortName(elm->inputPort(0));
989 QString r = otherPortName(elm->inputPort(1));
991 m_stream << QString(
" //SR Latch") << Qt::endl;
992 m_stream << QString(
" always @(*)") << Qt::endl;
993 m_stream << QString(
" begin") << Qt::endl;
994 m_stream << QString(
" if (%1 && %2)").arg(s, r) << Qt::endl;
995 m_stream << QString(
" begin") << Qt::endl;
996 m_stream << QString(
" %1 = 1'b0;").arg(firstOut) << Qt::endl;
997 m_stream << QString(
" %1 = 1'b0;").arg(secondOut) << Qt::endl;
998 m_stream << QString(
" end") << Qt::endl;
999 m_stream << QString(
" else if (%1 != %2)").arg(s, r) << Qt::endl;
1000 m_stream << QString(
" begin") << Qt::endl;
1001 m_stream << QString(
" %1 = %2;").arg(firstOut, s) << Qt::endl;
1002 m_stream << QString(
" %1 = %2;").arg(secondOut, r) << Qt::endl;
1003 m_stream << QString(
" end") << Qt::endl;
1004 m_stream << QString(
" end") << Qt::endl;
1005 m_stream << QString(
" //End of SR Latch") << Qt::endl;
1013 case ElementType::SRFlipFlop: {
1014 QString secondOut = m_varMap.value(elm->outputPort(1));
1015 QString s = otherPortName(elm->inputPort(0));
1016 QString clk = otherPortName(elm->inputPort(1));
1017 QString r = otherPortName(elm->inputPort(2));
1018 QString prst = otherPortName(elm->inputPort(3));
1019 QString clr = otherPortName(elm->inputPort(4));
1021 emitSequentialBlock(
"SR FlipFlop", clk, prst, clr,
1023 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1024 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1027 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1028 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1031 m_stream <<
" if (" << s <<
" && ~" << r <<
")" << Qt::endl;
1032 m_stream <<
" begin" << Qt::endl;
1033 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1034 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1035 m_stream <<
" end" << Qt::endl;
1036 m_stream <<
" else if (~" << s <<
" && " << r <<
")" << Qt::endl;
1037 m_stream <<
" begin" << Qt::endl;
1038 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1039 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1040 m_stream <<
" end" << Qt::endl;
1046 case ElementType::DFlipFlop: {
1047 QString secondOut = m_varMap.value(elm->outputPort(1));
1048 QString data = otherPortName(elm->inputPort(0));
1049 QString clk = otherPortName(elm->inputPort(1));
1050 QString prst = otherPortName(elm->inputPort(2));
1051 QString clr = otherPortName(elm->inputPort(3));
1053 emitSequentialBlock(
"D FlipFlop", clk, prst, clr,
1055 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1056 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1059 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1060 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1063 m_stream <<
" " << firstOut <<
" <= " << data <<
";" << Qt::endl;
1064 QString dataBar = data.startsWith(
"~") ? data.mid(1) : (
"~" + data);
1065 m_stream <<
" " << secondOut <<
" <= " << dataBar <<
";" << Qt::endl;
1071 case ElementType::JKFlipFlop: {
1072 QString secondOut = m_varMap.value(elm->outputPort(1));
1073 QString j = otherPortName(elm->inputPort(0));
1074 QString clk = otherPortName(elm->inputPort(1));
1075 QString k = otherPortName(elm->inputPort(2));
1076 QString prst = otherPortName(elm->inputPort(3));
1077 QString clr = otherPortName(elm->inputPort(4));
1079 emitSequentialBlock(
"JK FlipFlop", clk, prst, clr,
1081 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1082 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1085 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1086 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1089 m_stream <<
" if (" << j <<
" && " << k <<
")" << Qt::endl;
1090 m_stream <<
" begin" << Qt::endl;
1091 m_stream <<
" " << firstOut <<
" <= " << secondOut <<
";" << Qt::endl;
1092 m_stream <<
" " << secondOut <<
" <= " << firstOut <<
";" << Qt::endl;
1093 m_stream <<
" end" << Qt::endl;
1094 m_stream <<
" else if (" << j <<
" && ~" << k <<
")" << Qt::endl;
1095 m_stream <<
" begin" << Qt::endl;
1096 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1097 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1098 m_stream <<
" end" << Qt::endl;
1099 m_stream <<
" else if (~" << j <<
" && " << k <<
")" << Qt::endl;
1100 m_stream <<
" begin" << Qt::endl;
1101 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1102 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1103 m_stream <<
" end" << Qt::endl;
1109 case ElementType::TFlipFlop: {
1110 QString secondOut = m_varMap.value(elm->outputPort(1));
1111 QString t = otherPortName(elm->inputPort(0));
1112 QString clk = otherPortName(elm->inputPort(1));
1113 QString prst = otherPortName(elm->inputPort(2));
1114 QString clr = otherPortName(elm->inputPort(3));
1116 emitSequentialBlock(
"T FlipFlop", clk, prst, clr,
1118 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1119 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1122 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1123 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1126 m_stream <<
" if (" << t <<
")" << Qt::endl;
1127 m_stream <<
" begin" << Qt::endl;
1128 m_stream <<
" " << firstOut <<
" <= " << secondOut <<
";" << Qt::endl;
1129 m_stream <<
" " << secondOut <<
" <= " << firstOut <<
";" << Qt::endl;
1130 m_stream <<
" end" << Qt::endl;
1136 case ElementType::Mux: {
1139 int totalInputs = elm->inputSize();
1140 int numSelectLines = 1;
1141 while ((1 << numSelectLines) + numSelectLines < totalInputs) {
1144 int numDataInputs = totalInputs - numSelectLines;
1146 QString output = m_varMap.value(elm->outputPort(0));
1147 m_stream << QString(
" //Multiplexer") << Qt::endl;
1148 m_stream << QString(
" always @(*)") << Qt::endl;
1149 m_stream << QString(
" begin") << Qt::endl;
1150 m_stream << QString(
" case({");
1153 for (
int i = numSelectLines - 1; i >= 0; --i) {
1154 m_stream << otherPortName(elm->inputPort(numDataInputs + i));
1155 if (i > 0) m_stream <<
", ";
1157 m_stream <<
"})" << Qt::endl;
1160 for (
int i = 0; i < numDataInputs; ++i) {
1161 m_stream << QString(
" %1'd%2: %3 = %4;")
1162 .arg(numSelectLines)
1165 .arg(otherPortName(elm->inputPort(i))) << Qt::endl;
1167 m_stream << QString(
" default: %1 = 1'b0;").arg(output) << Qt::endl;
1168 m_stream << QString(
" endcase") << Qt::endl;
1169 m_stream << QString(
" end") << Qt::endl;
1170 m_stream << QString(
" //End of Multiplexer") << Qt::endl;
1174 case ElementType::Demux: {
1176 int numOutputs = elm->outputSize();
1177 int numSelectLines = 1;
1178 while ((1 << numSelectLines) < numOutputs) {
1182 QString dataInput = otherPortName(elm->inputPort(0));
1183 m_stream << QString(
" //Demultiplexer") << Qt::endl;
1184 m_stream << QString(
" always @(*)") << Qt::endl;
1185 m_stream << QString(
" begin") << Qt::endl;
1188 for (
int i = 0; i < numOutputs; ++i) {
1189 m_stream << QString(
" %1 = 1'b0;").arg(m_varMap.value(elm->outputPort(i))) << Qt::endl;
1193 m_stream << QString(
" case({");
1194 for (
int i = numSelectLines - 1; i >= 0; --i) {
1195 m_stream << otherPortName(elm->inputPort(1 + i));
1196 if (i > 0) m_stream <<
", ";
1198 m_stream <<
"})" << Qt::endl;
1201 for (
int i = 0; i < numOutputs; ++i) {
1202 m_stream << QString(
" %1'd%2: %3 = %4;")
1203 .arg(numSelectLines)
1205 .arg(m_varMap.value(elm->outputPort(i)))
1206 .arg(dataInput) << Qt::endl;
1208 m_stream << QString(
" endcase") << Qt::endl;
1209 m_stream << QString(
" end") << Qt::endl;
1210 m_stream << QString(
" //End of Demultiplexer") << Qt::endl;
1214 case ElementType::TruthTable: {
1215 auto *ttGraphic =
dynamic_cast<TruthTable *
>(elm);
1216 if (!ttGraphic)
break;
1218 QBitArray propositions = ttGraphic->key();
1219 const int nInputs = elm->inputSize();
1220 const int rows = 1 << nInputs;
1223 QStringList inputSignalNames;
1224 for (
int i = 0; i < nInputs; ++i) {
1225 Port *ttInputPort = elm->inputPort(i);
1226 QString signalName = otherPortName(ttInputPort);
1228 if (signalName ==
"LOW") {
1230 }
else if (signalName ==
"HIGH") {
1232 }
else if (signalName.isEmpty()) {
1233 m_stream <<
"// WARNING: Input " << i <<
" of TruthTable '" << elm->objectName() <<
"' appears disconnected. Assuming LOW." << Qt::endl;
1236 inputSignalNames << signalName;
1240 QStringList bitExpressions;
1241 bitExpressions <<
"{";
1242 for (
int i = 0; i < nInputs; ++i) {
1243 if (i < nInputs - 1) {
1244 bitExpressions << QString(
"%1, ").arg(inputSignalNames[i]);
1246 bitExpressions << QString(
"%1}").arg(inputSignalNames[i]);
1250 QString indexCalculation = bitExpressions.join(
"");
1255 for (
int out = 0; out < elm->outputSize(); ++out) {
1256 QString outputVarName = m_varMap.value(elm->outputPort(out));
1257 if (outputVarName.isEmpty()) {
1259 throw PANDACEPTION(
"Output variable not mapped for TruthTable: %1", elm->objectName());
1261 m_stream <<
"// TruthTable '" << elm->objectName() <<
"' output " << out <<
" is disconnected — no code emitted." << Qt::endl;
1265 m_stream << QString(
" //TruthTable") << Qt::endl;
1266 m_stream << QString(
" always @(*)") << Qt::endl;
1267 m_stream << QString(
" begin") << Qt::endl;
1268 m_stream << QString(
" case(%1)").arg(indexCalculation) << Qt::endl;
1270 for (
int i = 0; i < rows; ++i) {
1271 m_stream << QString(
" %1'b").arg(nInputs) << QString::number(i, 2).rightJustified(nInputs,
'0') <<
": " << outputVarName <<
" = 1'b" << (propositions.testBit(256 * out + i) ?
"1" :
"0") <<
";" << Qt::endl;
1274 m_stream << QString(
" default: %1 = 1'b0;").arg(outputVarName) << Qt::endl;
1275 m_stream << QString(
" endcase") << Qt::endl;
1276 m_stream << QString(
" end") << Qt::endl;
1277 m_stream << QString(
" //End TruthTable") << Qt::endl;
1283 case ElementType::And:
1284 case ElementType::AudioBox:
1285 case ElementType::Buzzer:
1286 case ElementType::Clock:
1287 case ElementType::Display14:
1288 case ElementType::Display16:
1289 case ElementType::Display7:
1290 case ElementType::IC:
1291 case ElementType::InputButton:
1292 case ElementType::InputGnd:
1293 case ElementType::InputRotary:
1294 case ElementType::InputSwitch:
1295 case ElementType::InputVcc:
1296 case ElementType::JKLatch:
1297 case ElementType::Led:
1298 case ElementType::Line:
1299 case ElementType::Nand:
1300 case ElementType::Node:
1301 case ElementType::Nor:
1302 case ElementType::Not:
1303 case ElementType::Or:
1304 case ElementType::Text:
1305 case ElementType::Unknown:
1306 case ElementType::Xnor:
1307 case ElementType::Xor:
1308 throw PANDACEPTION(
"Element type not supported: %1", elm->objectName());
1314QString SystemVerilogCodeGen::generateLogicExpression(
GraphicElement *elm)
1316 QSet<Port *> visited;
1317 return generateLogicExpressionImpl(elm, visited);
1321QString SystemVerilogCodeGen::generateLogicExpressionImpl(
GraphicElement *elm, QSet<Port *> &visited)
1323 bool negate =
false;
1324 QString logicOperator;
1327 case ElementType::And: logicOperator =
"&";
break;
1328 case ElementType::Or: logicOperator =
"|";
break;
1329 case ElementType::Nand: logicOperator =
"&"; negate =
true;
break;
1330 case ElementType::Nor: logicOperator =
"|"; negate =
true;
break;
1331 case ElementType::Xor: logicOperator =
"^";
break;
1332 case ElementType::Xnor: logicOperator =
"^"; negate =
true;
break;
1333 case ElementType::Not: {
1334 QString inner = otherPortNameImpl(elm->
inputPort(0), visited);
1336 if (inner.startsWith(
"~")) {
1337 return inner.mid(1);
1341 case ElementType::Node:
return otherPortNameImpl(elm->
inputPort(0), visited);
1342 case ElementType::AudioBox:
1343 case ElementType::Buzzer:
1344 case ElementType::Clock:
1345 case ElementType::DFlipFlop:
1346 case ElementType::DLatch:
1347 case ElementType::Demux:
1348 case ElementType::Display14:
1349 case ElementType::Display16:
1350 case ElementType::Display7:
1351 case ElementType::IC:
1352 case ElementType::InputButton:
1353 case ElementType::InputGnd:
1354 case ElementType::InputRotary:
1355 case ElementType::InputSwitch:
1356 case ElementType::InputVcc:
1357 case ElementType::JKFlipFlop:
1358 case ElementType::JKLatch:
1359 case ElementType::Led:
1360 case ElementType::Line:
1361 case ElementType::Mux:
1362 case ElementType::SRFlipFlop:
1363 case ElementType::SRLatch:
1364 case ElementType::TFlipFlop:
1365 case ElementType::Text:
1366 case ElementType::TruthTable:
1367 case ElementType::Unknown:
1372 if (elm->
inputs().size() == 1) {
1373 expr = otherPortNameImpl(elm->
inputPort(0), visited);
1377 for (
int i = 0; i < elm->
inputs().size(); ++i) {
1378 if (i > 0) expr +=
" " + logicOperator +
" ";
1379 expr += otherPortNameImpl(elm->
inputPort(i), visited);
1386 if (expr.startsWith(
"~")) {
1396void SystemVerilogCodeGen::loop()
1400 m_stream <<
"\n// Assigning aux variables. //" << Qt::endl;
1401 assignVariablesRec(m_elements);
1403 m_stream <<
"\n// Writing output data. //" << Qt::endl;
1404 for (
const auto &pin : std::as_const(m_outputMap)) {
1405 QString expr = otherPortName(pin.m_port);
1406 if (expr.isEmpty()) {
1407 expr = highLow(pin.m_port->defaultValue());
1409 m_stream << QString(
"assign %1 = %2;").arg(pin.m_varName, expr) << Qt::endl;
1411 m_stream <<
"endmodule" << Qt::endl;
Graphic element for the real-time clock input.
Shared string utilities used by all code generators.
Common logging utilities, the Pandaception error type, and helper macros.
#define PANDACEPTION(msg,...)
Connection: a wire that connects an output port to an input port in the circuit scene.
Abstract base class for all graphical circuit elements.
Integrated Circuit (IC) graphic element that encapsulates a sub-circuit file.
Port classes: Port (base), InputPort, and OutputPort.
Main circuit editing scene with undo/redo and user interaction.
static bool isSystemVerilogReserved(const QString &name)
static QString icModuleKey(const IC *ic)
SystemVerilog code generator: translates a circuit into a synthesisable module.
Graphic element for a user-programmable truth table.
Abstract base class for all graphical circuit elements in wiRedPanda.
ElementType elementType() const
Returns the type identifier for this element.
int inputSize() const
Returns the current number of input ports.
QString label() const
Returns the user-visible label text for this element.
InputPort * inputPort(const int index=0) const
Returns the input port at index (default 0).
int outputSize() const
Returns the current number of output ports.
OutputPort * outputPort(const int index=0) const
Returns the output port at index (default 0).
const QVector< InputPort * > & inputs() const
Returns a const reference to the vector of all input ports.
Graphic element representing an Integrated Circuit (sub-circuit) box.
const QVector< Port * > & internalInputs() const
const QVector< Port * > & internalOutputs() const
bool isEmbedded() const override
Returns true if this element is an embedded IC (not file-backed). Base returns false.
const QString & blobName() const override
Returns the blob name for embedded ICs, empty if file-backed.
const QVector< GraphicElement * > & internalElements() const
const QString & file() const
Abstract base class for circuit element ports (connection endpoints).
GraphicElement * graphicElement()
Returns the graphic element that owns this port.
Status status() const
Returns the current logical status (Active/Inactive/Unknown/Error).
Status defaultValue() const
Returns the default status applied when the port is unconnected.
const QList< Connection * > & connections() const
Returns the list of wires attached to this port.
QString name() const
Returns the port's label text.
static QHash< QString, InputPort * > wirelessTxInputPorts(const QVector< GraphicElement * > &elements)
Returns a map from wireless channel label to the Tx node's input port.
SystemVerilogCodeGen(const QString &fileName, const QVector< GraphicElement * > &elements)
Constructs the code generator for the given output fileName and circuit elements.
void generate()
Generates the SystemVerilog output file for the circuit.
QString removeForbiddenChars(const QString &input, const bool stripFirst=false)
Converts input into a legal language identifier.
QString sanitizeComment(const QString &input)
Makes input safe to embed in a single-line "//" comment.
QString stripAccents(const QString &input)
Strips Unicode diacritic marks (accents) from input using NFC → NFD decomposition.
Metadata for a single IC module during SystemVerilog hierarchical generation.
QString sourceFile
Original .panda file path (or blob name).
QString moduleName
Generated module identifier.
QVector< QString > inputNames
Port names for the module's inputs.
QVector< QString > outputNames
Port names for the module's outputs.
IC * prototypeIC
Representative IC element (used to inspect sub-circuit).
bool generated
True once the module body has been emitted.