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)
72 return (val == Status::Active) ?
"1'b1" :
"1'b0";
75QString SystemVerilogCodeGen::fourState(
const Status val)
83 case Status::Active:
return "1'b1";
84 case Status::Inactive:
return "1'b0";
85 default:
return "1'bx";
90QString SystemVerilogCodeGen::removeForbiddenChars(
const QString &input)
96bool SystemVerilogCodeGen::isSimpleIdentifier(
const QString &expr)
98 if (expr.isEmpty())
return false;
99 static QRegularExpression re(
"^[a-zA-Z_][a-zA-Z0-9_]*$");
100 return re.match(expr).hasMatch();
105QString SystemVerilogCodeGen::ensureSimpleSignal(
const QString &expr)
107 if (expr.isEmpty() || expr ==
"1'b0" || expr ==
"1'b1")
return expr;
108 if (isSimpleIdentifier(expr))
return expr;
111 QString wireName = QString(
"aux_async_%1").arg(m_globalCounter++);
112 m_stream <<
" wire " << wireName <<
" = " << expr <<
";" << Qt::endl;
116QString SystemVerilogCodeGen::otherPortName(
Port *port)
118 QSet<Port *> visited;
119 return otherPortNameImpl(port, visited);
122QString SystemVerilogCodeGen::otherPortNameImpl(
Port *port, QSet<Port *> &visited)
124 if (!port)
return "1'b0";
127 if (visited.contains(port)) {
128 QString mapped = m_varMap.value(port);
129 return mapped.isEmpty() ?
"1'b0" : mapped;
133 QString mapped = m_varMap.value(port);
134 if (!mapped.isEmpty())
return mapped;
137 if (elm && elm->wirelessMode() == WirelessMode::Rx && !elm->label().isEmpty()) {
138 auto *txInputPort = m_txInputPorts.value(elm->label(),
nullptr);
140 return otherPortNameImpl(txInputPort, visited);
146 auto *otherPort = port->
connections().constFirst()->otherPort(port);
157 if (visited.contains(otherPort)) {
158 QString mapped = m_varMap.value(otherPort);
159 return mapped.isEmpty() ?
"1'b0" : mapped;
163 visited.insert(port);
165 auto *elm = otherPort->graphicElement();
170 QString mapped = m_varMap.value(otherPort);
171 return mapped.isEmpty() ? fourState(port->
defaultValue()) : mapped;
177 QString mapped = m_varMap.value(otherPort);
178 if (!mapped.isEmpty()) {
182 if (elm->elementType() == ElementType::And ||
183 elm->elementType() == ElementType::Or ||
184 elm->elementType() == ElementType::Nand ||
185 elm->elementType() == ElementType::Nor ||
186 elm->elementType() == ElementType::Xor ||
187 elm->elementType() == ElementType::Xnor ||
188 elm->elementType() == ElementType::Not ||
189 elm->elementType() == ElementType::Node) {
191 return generateLogicExpressionImpl(elm, visited);
205 static const QSet<QString> reserved = {
206 "always",
"and",
"assign",
"automatic",
"begin",
"buf",
"bufif0",
"bufif1",
207 "case",
"casex",
"casez",
"cell",
"cmos",
"config",
"deassign",
"default",
208 "defparam",
"design",
"disable",
"edge",
"else",
"end",
"endcase",
209 "endconfig",
"endfunction",
"endgenerate",
"endmodule",
"endprimitive",
210 "endspecify",
"endtable",
"endtask",
"event",
"for",
"force",
"forever",
211 "fork",
"function",
"generate",
"genvar",
"highz0",
"highz1",
"if",
212 "ifnone",
"incdir",
"include",
"initial",
"inout",
"input",
213 "instance",
"integer",
"join",
"large",
"liblist",
"library",
"localparam",
214 "macromodule",
"medium",
"module",
"nand",
"negedge",
"nmos",
"nor",
215 "noshowcancelled",
"not",
"notif0",
"notif1",
"or",
"output",
"parameter",
216 "pmos",
"posedge",
"primitive",
"pull0",
"pull1",
"pulldown",
"pullup",
217 "pulsestyle_onevent",
"pulsestyle_ondetect",
"rcmos",
"real",
"realtime",
218 "reg",
"release",
"repeat",
"rnmos",
"rpmos",
"rtran",
"rtranif0",
219 "rtranif1",
"scalared",
"showcancelled",
"signed",
"small",
"specify",
220 "specparam",
"strong0",
"strong1",
"supply0",
"supply1",
"table",
"task",
221 "time",
"tran",
"tranif0",
"tranif1",
"tri",
"tri0",
"tri1",
"triand",
222 "trior",
"trireg",
"unsigned",
"use",
"uwire",
"vectored",
"wait",
223 "wand",
"weak0",
"weak1",
"while",
"wire",
"wor",
"xnor",
"xor"
225 return reserved.contains(name);
229void SystemVerilogCodeGen::collectICTypes(
const QVector<GraphicElement *> &elements)
231 for (
auto *elm : elements) {
232 if (elm->elementType() != ElementType::IC) {
236 auto *ic = qobject_cast<IC *>(elm);
242 if (m_icModules.contains(key)) {
251 QString baseName = ic->
isEmbedded() ? ic->blobName() : QFileInfo(ic->file()).baseName();
258 QSet<QString> usedNames;
259 for (
int i = 0; i < ic->inputSize(); ++i) {
260 QString portLabel = ic->inputPort(i)->name();
267 if (portName.isEmpty() || portName ==
"_unnamed") {
268 portName = QString(
"in_%1").arg(i);
272 portName =
"p_" + portName;
275 QString original = portName;
277 while (usedNames.contains(portName)) {
278 portName = QString(
"%1_%2").arg(original).arg(suffix++);
280 usedNames.insert(portName);
285 for (
int i = 0; i < ic->outputSize(); ++i) {
286 QString portLabel = ic->outputPort(i)->name();
289 if (portName.isEmpty() || portName ==
"_unnamed") {
290 portName = QString(
"out_%1").arg(i);
294 portName =
"p_" + portName;
296 QString original = portName;
298 while (usedNames.contains(portName)) {
299 portName = QString(
"%1_%2").arg(original).arg(suffix++);
301 usedNames.insert(portName);
305 m_icModules.insert(key, info);
308 collectICTypes(ic->internalElements());
318QSet<GraphicElement *> SystemVerilogCodeGen::findFeedbackElements(
const QVector<GraphicElement *> &elements)
320 const QSet<GraphicElement *> elementSet(elements.cbegin(), elements.cend());
322 auto isGate = [](GraphicElement *e) {
323 switch (e->elementType()) {
324 case ElementType::And:
case ElementType::Or:
case ElementType::Nand:
325 case ElementType::Nor:
case ElementType::Xor:
case ElementType::Xnor:
326 case ElementType::Not:
case ElementType::Node:
334 auto successors = [&](GraphicElement *elm) {
335 QVector<GraphicElement *> succ;
336 for (
auto *outPort : elm->outputs()) {
337 const auto conns = outPort->connections();
338 for (
auto *conn : conns) {
340 Port *other = conn->otherPort(outPort);
341 if (!other)
continue;
343 if (h && elementSet.contains(h) && isGate(h)) {
351 QSet<GraphicElement *> feedback;
352 for (
auto *start : elements) {
353 if (!isGate(start))
continue;
355 QSet<GraphicElement *> seen;
356 QVector<GraphicElement *> stack = successors(start);
357 while (!stack.isEmpty()) {
358 GraphicElement *n = stack.takeLast();
360 feedback.insert(start);
363 if (seen.contains(n))
continue;
365 stack += successors(n);
372void SystemVerilogCodeGen::generateICModules()
374 if (m_icModules.isEmpty()) {
379 QStringList sortedKeys = m_icModules.keys();
380 std::sort(sortedKeys.begin(), sortedKeys.end());
382 bool progress =
true;
385 for (
const QString &key : std::as_const(sortedKeys)) {
386 ICModuleInfo &info = m_icModules[key];
392 bool allDepsReady =
true;
394 if (elm->elementType() != ElementType::IC) {
397 auto *nestedIC = qobject_cast<IC *>(elm);
398 if (!nestedIC)
continue;
401 if (!m_icModules.value(nestedKey).generated) {
402 allDepsReady =
false;
408 generateSingleICModule(info);
417void SystemVerilogCodeGen::generateSingleICModule(
ICModuleInfo &info)
420 QHash<Port *, QString> savedVarMap = m_varMap;
421 QHash<IC *, QString> savedInstanceNames = m_instanceNames;
422 int savedCounter = m_globalCounter;
425 m_instanceNames.clear();
427 m_generatingICModule =
true;
439 m_stream <<
"module " << info.
moduleName <<
" (" << Qt::endl;
442 QStringList portDecls;
443 for (
int i = 0; i < info.
inputNames.size(); ++i) {
444 portDecls << QString(
" input %1").arg(info.
inputNames[i]);
446 for (
int i = 0; i < info.
outputNames.size(); ++i) {
447 portDecls << QString(
" output %1").arg(info.
outputNames[i]);
449 m_stream << portDecls.join(
",\n") << Qt::endl;
450 m_stream <<
");" << Qt::endl;
455 QSet<GraphicElement *> boundaryNodes;
466 m_varMap[nodeElm->outputPort(0)] = info.
inputNames[i];
467 boundaryNodes.insert(nodeElm);
478 m_varMap[nodeElm->outputPort(0)] = info.
outputNames[i];
479 boundaryNodes.insert(nodeElm);
483 QVector<GraphicElement *> internalElements;
485 if (!boundaryNodes.contains(elm)) {
486 internalElements.append(elm);
492 m_feedbackElements = findFeedbackElements(internalElements);
493 const bool hasFeedback = !m_feedbackElements.isEmpty();
496 m_stream << Qt::endl;
498 m_stream <<
"/* verilator lint_off UNOPTFLAT */ // intentional latch feedback" << Qt::endl;
500 declareAuxVariablesRec(internalElements);
501 m_stream << Qt::endl;
504 m_stream <<
"// Internal logic" << Qt::endl;
505 assignVariablesRec(internalElements);
508 m_stream << Qt::endl;
517 QString value = otherPortName(nodeElm->inputPort(0));
518 m_stream <<
"assign " << info.
outputNames[i] <<
" = " << value <<
";" << Qt::endl;
522 m_stream <<
"/* verilator lint_on UNOPTFLAT */" << Qt::endl;
524 m_feedbackElements.clear();
526 m_stream <<
"endmodule" << Qt::endl;
527 m_stream << Qt::endl;
530 m_varMap = savedVarMap;
531 m_instanceNames = savedInstanceNames;
532 m_globalCounter = savedCounter;
533 m_generatingICModule =
false;
540 m_stream <<
"// ==================================================================== //" << Qt::endl;
541 m_stream <<
"// ======= This code was generated automatically by wiRedPanda ======== //" << Qt::endl;
542 m_stream <<
"// ==================================================================== //" << Qt::endl;
547 collectICTypes(m_elements);
552 QSet<QString> usedModuleNames;
553 usedModuleNames.insert(m_fileName);
554 QStringList collisionKeys = m_icModules.keys();
555 std::sort(collisionKeys.begin(), collisionKeys.end());
556 for (
const QString &key : std::as_const(collisionKeys)) {
557 QString name = m_icModules[key].moduleName;
558 while (usedModuleNames.contains(name)) {
561 m_icModules[key].moduleName = name;
562 usedModuleNames.insert(name);
576 m_feedbackElements = findFeedbackElements(m_elements);
579 m_stream <<
"module " << m_fileName <<
" (" << Qt::endl;
583 declareAuxVariables();
590void SystemVerilogCodeGen::declareInputs()
595 int totalOutputs = 0;
596 int currentOutput = 0;
597 for (
auto *elm : m_elements) {
598 const auto type = elm->elementType();
599 if (elm->elementGroup() == ElementGroup::Output) {
600 totalOutputs = INT_MAX;
602 }
else if ((type == ElementType::InputButton) || (type == ElementType::InputSwitch) || (type == ElementType::Clock) || (type == ElementType::InputRotary)) {
603 totalOutputs +=
static_cast<int>(elm->outputs().size());
607 m_stream <<
"/* ========= Inputs ========== */" << Qt::endl;
609 for (
auto *elm : m_elements) {
610 const auto type = elm->elementType();
612 if ((type == ElementType::InputButton) || (type == ElementType::InputSwitch) || (type == ElementType::Clock) || (type == ElementType::InputRotary)) {
613 QString baseName = elm->objectName() + QString::number(counter);
614 const QString label = elm->label();
616 if (!label.isEmpty()) {
617 baseName +=
"_" + label;
621 baseName = removeForbiddenChars(baseName);
627 for (
int port = 0; port < elm->outputSize(); ++port) {
628 QString varName = (elm->outputSize() > 1) ? QString(
"%1_%2").arg(baseName).arg(port) : baseName;
631 if (currentOutput < totalOutputs) {
632 m_stream << QString(
"input %1,").arg(varName) << Qt::endl;
634 m_stream << QString(
"input %1").arg(varName) << Qt::endl;
637 m_inputMap.append(MappedPinSystemVerilog(elm,
"", varName, elm->outputPort(port), port));
639 m_varMap[elm->outputPort(port)] = varName;
645 m_stream << Qt::endl;
648void SystemVerilogCodeGen::declareOutputs()
652 int totalOutputs = 0;
653 int currentOutput = 0;
654 for (
auto *elm : m_elements) {
655 if (elm->elementGroup() == ElementGroup::Output) {
656 totalOutputs +=
static_cast<int>(elm->inputs().size());
663 m_stream <<
"/* ========= Outputs ========== */" << Qt::endl;
664 for (
auto *elm : m_elements) {
665 if (elm->elementGroup() == ElementGroup::Output) {
666 QString label = elm->label();
667 for (
int i = 0; i < elm->inputs().size(); ++i) {
669 QString varName = elm->objectName() + QString::number(counter);
670 if (!label.isEmpty()) {
671 varName = QString(
"%1_%2").arg(varName, label);
673 Port *port = elm->inputPort(i);
674 if (!port->
name().isEmpty()) {
675 varName = QString(
"%1_%2").arg(varName, port->
name());
678 varName = removeForbiddenChars(varName);
679 if (currentOutput < totalOutputs) {
680 m_stream << QString(
"output %1,").arg(varName) << Qt::endl;
682 m_stream << QString(
"output %1").arg(varName) << Qt::endl;
684 m_outputMap.append(MappedPinSystemVerilog(elm,
"", varName, port, i));
689 m_stream <<
");" << Qt::endl;
692void SystemVerilogCodeGen::declareAuxVariablesRec(
const QVector<GraphicElement *> &elements)
694 for (
auto *elm : elements) {
695 if (elm->elementType() == ElementType::IC) {
696 auto *ic = qobject_cast<IC *>(elm);
701 const ICModuleInfo &info = m_icModules.value(key);
704 QString instanceName = QString(
"%1_inst_%2").arg(info.
moduleName).arg(m_globalCounter++);
705 m_instanceNames[ic] = instanceName;
711 QString wireName = QString(
"w_%1_%2").arg(instanceName, info.
outputNames.value(i, QString(
"out_%1").arg(i)));
713 m_stream <<
"wire " << wireName <<
";" << Qt::endl;
720 const auto type = elm->elementType();
721 if ((type == ElementType::InputButton ||
722 type == ElementType::InputSwitch ||
723 type == ElementType::Clock ||
724 type == ElementType::InputRotary) &&
725 !m_varMap.value(elm->outputPort()).isEmpty()) {
740 if (!m_generatingICModule && !m_feedbackElements.contains(elm) &&
741 (type == ElementType::And ||
742 type == ElementType::Or ||
743 type == ElementType::Nand ||
744 type == ElementType::Nor ||
745 type == ElementType::Xor ||
746 type == ElementType::Xnor ||
747 type == ElementType::Not ||
748 type == ElementType::Node)) {
752 QString varName = QString(
"aux_%1_%2").arg(removeForbiddenChars(
CodeGenUtils::stripAccents(elm->objectName()))).arg(m_globalCounter++);
753 const auto outputs = elm->outputs();
756 QSet<Port *> preMapped;
758 if (outputs.size() == 1) {
759 Port *port = outputs.constFirst();
761 if (elm->elementType() == ElementType::InputVcc) {
762 m_varMap[port] =
"1'b1";
766 if (elm->elementType() == ElementType::InputGnd) {
767 m_varMap[port] =
"1'b0";
771 if (m_varMap.value(port).isEmpty()) {
772 m_varMap[port] = varName;
781 preMapped.insert(port);
786 for (
auto *port : outputs) {
788 if (!m_varMap.value(port).isEmpty()) {
789 preMapped.insert(port);
794 QString portName = varName;
795 portName.append(QString(
"_%1").arg(portCounter++));
797 if (!port->
name().isEmpty()) {
801 m_varMap[port] = portName;
805 for (
auto *port : outputs) {
808 if (preMapped.contains(port)) {
812 QString varName2 = m_varMap.value(port);
814 switch (elm->elementType()) {
816 case ElementType::DLatch:
817 case ElementType::SRLatch:
818 case ElementType::SRFlipFlop:
819 case ElementType::DFlipFlop:
820 case ElementType::TFlipFlop:
821 case ElementType::JKFlipFlop: {
822 m_stream <<
"reg " << varName2 << QString(
" = 1'b%1;").arg(aux) << Qt::endl;
827 case ElementType::TruthTable:
828 if (!outputs.isEmpty()) {
829 Port *outputPort = outputs.constFirst();
830 QString ttVarName = QString(
"%1_output").arg(removeForbiddenChars(elm->objectName()));
831 m_varMap[outputPort] = ttVarName;
832 m_stream << QString(
"reg ") << ttVarName <<
";" << Qt::endl;
838 case ElementType::Mux:
839 case ElementType::Demux: {
841 m_stream <<
"reg " << varName2 <<
" = 1'b0;" << Qt::endl;
845 case ElementType::And:
846 case ElementType::AudioBox:
847 case ElementType::Buzzer:
848 case ElementType::Clock:
849 case ElementType::Display14:
850 case ElementType::Display16:
851 case ElementType::Display7:
852 case ElementType::IC:
853 case ElementType::InputButton:
854 case ElementType::InputGnd:
855 case ElementType::InputRotary:
856 case ElementType::InputSwitch:
857 case ElementType::InputVcc:
858 case ElementType::JKLatch:
859 case ElementType::Led:
860 case ElementType::Line:
861 case ElementType::Nand:
862 case ElementType::Node:
863 case ElementType::Nor:
864 case ElementType::Not:
865 case ElementType::Or:
866 case ElementType::Text:
867 case ElementType::Unknown:
868 case ElementType::Xnor:
869 case ElementType::Xor:
870 if (m_feedbackElements.contains(elm)) {
874 m_stream <<
"reg " << varName2 <<
" = " << highLow(port->
status()) <<
";" << Qt::endl;
876 m_stream <<
"wire " << varName2 <<
";" << Qt::endl;
885void SystemVerilogCodeGen::declareAuxVariables()
887 m_stream <<
"/* ====== Aux. Variables ====== */" << Qt::endl;
888 declareAuxVariablesRec(m_elements);
889 m_stream << Qt::endl;
896void SystemVerilogCodeGen::emitSequentialBlock(
897 const QString &typeName,
898 const QString &clk,
const QString &rawPrst,
const QString &rawClr,
899 const std::function<
void()> &emitPresetBody,
900 const std::function<
void()> &emitClearBody,
901 const std::function<
void()> &emitNormalBody)
903 bool hasPrst = (rawPrst !=
"1'b1" && rawPrst !=
"1'b0");
904 bool hasClr = (rawClr !=
"1'b1" && rawClr !=
"1'b0");
908 QString prst = hasPrst ? ensureSimpleSignal(rawPrst) : rawPrst;
909 QString clr = hasClr ? ensureSimpleSignal(rawClr) : rawClr;
911 m_stream <<
" //" << typeName << Qt::endl;
914 if (hasPrst && hasClr) {
915 m_stream <<
" always @(posedge " << clk <<
" or negedge " << prst <<
" or negedge " << clr <<
")" << Qt::endl;
916 }
else if (hasPrst) {
917 m_stream <<
" always @(posedge " << clk <<
" or negedge " << prst <<
")" << Qt::endl;
919 m_stream <<
" always @(posedge " << clk <<
" or negedge " << clr <<
")" << Qt::endl;
921 m_stream <<
" always @(posedge " << clk <<
")" << Qt::endl;
924 m_stream <<
" begin" << Qt::endl;
926 if (!hasPrst && !hasClr) {
930 bool needsElse =
false;
933 m_stream <<
" if (~" << prst <<
")" << Qt::endl;
934 m_stream <<
" begin" << Qt::endl;
936 m_stream <<
" end" << Qt::endl;
942 m_stream <<
" else if (~" << clr <<
")" << Qt::endl;
944 m_stream <<
" if (~" << clr <<
")" << Qt::endl;
946 m_stream <<
" begin" << Qt::endl;
948 m_stream <<
" end" << Qt::endl;
951 m_stream <<
" else" << Qt::endl;
952 m_stream <<
" begin" << Qt::endl;
954 m_stream <<
" end" << Qt::endl;
957 m_stream <<
" end" << Qt::endl;
958 m_stream <<
" //End of " << typeName << Qt::endl;
961void SystemVerilogCodeGen::assignVariablesRec(
const QVector<GraphicElement *> &elements)
963 for (
auto *elm : elements) {
964 if (elm->elementType() == ElementType::IC) {
965 auto *ic = qobject_cast<IC *>(elm);
970 const ICModuleInfo &info = m_icModules.value(key);
971 QString instanceName = m_instanceNames.value(ic);
974 m_stream << info.
moduleName <<
" " << instanceName <<
" (" << Qt::endl;
977 for (
int i = 0; i < ic->
inputSize(); ++i) {
978 QString inputValue = otherPortName(ic->
inputPort(i));
979 m_stream <<
" ." << info.
inputNames.value(i, QString(
"in_%1").arg(i))
980 <<
"(" << inputValue <<
")";
981 if (i < ic->inputSize() - 1 || ic->
outputSize() > 0) {
984 m_stream << Qt::endl;
989 QString outputWire = m_varMap.value(ic->
outputPort(i));
990 m_stream <<
" ." << info.
outputNames.value(i, QString(
"out_%1").arg(i))
991 <<
"(" << outputWire <<
")";
992 if (i < ic->outputSize() - 1) {
995 m_stream << Qt::endl;
998 m_stream <<
");" << Qt::endl;
1001 if (elm->inputs().isEmpty() || elm->outputs().isEmpty()) {
1007 if (elm->elementType() == ElementType::And ||
1008 elm->elementType() == ElementType::Or ||
1009 elm->elementType() == ElementType::Nand ||
1010 elm->elementType() == ElementType::Nor ||
1011 elm->elementType() == ElementType::Xor ||
1012 elm->elementType() == ElementType::Xnor ||
1013 elm->elementType() == ElementType::Not ||
1014 elm->elementType() == ElementType::Node) {
1016 QString expr = generateLogicExpression(elm);
1017 const bool isFeedback = m_feedbackElements.contains(elm);
1018 for (
auto *port : elm->outputs()) {
1019 QString existingVar = m_varMap.value(port);
1026 if (!existingVar.isEmpty() && (m_generatingICModule || isFeedback)) {
1031 m_stream <<
"always @(*) " << existingVar <<
" = " << expr <<
";" << Qt::endl;
1034 m_stream <<
"assign " << existingVar <<
" = " << expr <<
";" << Qt::endl;
1038 m_varMap[port] = expr;
1044 QString firstOut = m_varMap.value(elm->outputPort(0));
1045 switch (elm->elementType()) {
1046 case ElementType::DLatch: {
1047 QString secondOut = m_varMap.value(elm->outputPort(1));
1048 QString data = otherPortName(elm->inputPort(0));
1049 QString enable = otherPortName(elm->inputPort(1));
1050 m_stream << QString(
" //D Latch") << Qt::endl;
1051 m_stream << QString(
" always @(*)") << Qt::endl;
1052 m_stream << QString(
" begin") << Qt::endl;
1053 m_stream << QString(
" if (%1)").arg(enable) << Qt::endl;
1054 m_stream << QString(
" begin") << Qt::endl;
1056 m_stream << QString(
" %1 = %2;").arg(firstOut, data) << Qt::endl;
1057 QString dataBar = data.startsWith(
"~") ? data.mid(1) : (
"~" + data);
1058 m_stream << QString(
" %1 = %2;").arg(secondOut, dataBar) << Qt::endl;
1059 m_stream << QString(
" end") << Qt::endl;
1060 m_stream << QString(
" end") << Qt::endl;
1061 m_stream << QString(
" //End of D Latch") << Qt::endl;
1065 case ElementType::SRLatch: {
1066 QString secondOut = m_varMap.value(elm->outputPort(1));
1067 QString s = otherPortName(elm->inputPort(0));
1068 QString r = otherPortName(elm->inputPort(1));
1070 m_stream << QString(
" //SR Latch") << Qt::endl;
1071 m_stream << QString(
" always @(*)") << Qt::endl;
1072 m_stream << QString(
" begin") << Qt::endl;
1073 m_stream << QString(
" if (%1 && %2)").arg(s, r) << Qt::endl;
1074 m_stream << QString(
" begin") << Qt::endl;
1075 m_stream << QString(
" %1 = 1'b0;").arg(firstOut) << Qt::endl;
1076 m_stream << QString(
" %1 = 1'b0;").arg(secondOut) << Qt::endl;
1077 m_stream << QString(
" end") << Qt::endl;
1078 m_stream << QString(
" else if (%1 != %2)").arg(s, r) << Qt::endl;
1079 m_stream << QString(
" begin") << Qt::endl;
1080 m_stream << QString(
" %1 = %2;").arg(firstOut, s) << Qt::endl;
1081 m_stream << QString(
" %1 = %2;").arg(secondOut, r) << Qt::endl;
1082 m_stream << QString(
" end") << Qt::endl;
1083 m_stream << QString(
" end") << Qt::endl;
1084 m_stream << QString(
" //End of SR Latch") << Qt::endl;
1092 case ElementType::SRFlipFlop: {
1093 QString secondOut = m_varMap.value(elm->outputPort(1));
1094 QString s = otherPortName(elm->inputPort(0));
1095 QString clk = otherPortName(elm->inputPort(1));
1096 QString r = otherPortName(elm->inputPort(2));
1097 QString prst = otherPortName(elm->inputPort(3));
1098 QString clr = otherPortName(elm->inputPort(4));
1100 emitSequentialBlock(
"SR FlipFlop", clk, prst, clr,
1102 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1103 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1106 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1107 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1110 m_stream <<
" if (" << s <<
" && ~" << r <<
")" << Qt::endl;
1111 m_stream <<
" begin" << Qt::endl;
1112 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1113 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1114 m_stream <<
" end" << Qt::endl;
1115 m_stream <<
" else if (~" << s <<
" && " << r <<
")" << Qt::endl;
1116 m_stream <<
" begin" << Qt::endl;
1117 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1118 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1119 m_stream <<
" end" << Qt::endl;
1125 case ElementType::DFlipFlop: {
1126 QString secondOut = m_varMap.value(elm->outputPort(1));
1127 QString data = otherPortName(elm->inputPort(0));
1128 QString clk = otherPortName(elm->inputPort(1));
1129 QString prst = otherPortName(elm->inputPort(2));
1130 QString clr = otherPortName(elm->inputPort(3));
1132 emitSequentialBlock(
"D FlipFlop", clk, prst, clr,
1134 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1135 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1138 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1139 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1142 m_stream <<
" " << firstOut <<
" <= " << data <<
";" << Qt::endl;
1143 QString dataBar = data.startsWith(
"~") ? data.mid(1) : (
"~" + data);
1144 m_stream <<
" " << secondOut <<
" <= " << dataBar <<
";" << Qt::endl;
1150 case ElementType::JKFlipFlop: {
1151 QString secondOut = m_varMap.value(elm->outputPort(1));
1152 QString j = otherPortName(elm->inputPort(0));
1153 QString clk = otherPortName(elm->inputPort(1));
1154 QString k = otherPortName(elm->inputPort(2));
1155 QString prst = otherPortName(elm->inputPort(3));
1156 QString clr = otherPortName(elm->inputPort(4));
1158 emitSequentialBlock(
"JK FlipFlop", clk, prst, clr,
1160 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1161 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1164 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1165 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1168 m_stream <<
" if (" << j <<
" && " << k <<
")" << Qt::endl;
1169 m_stream <<
" begin" << Qt::endl;
1170 m_stream <<
" " << firstOut <<
" <= " << secondOut <<
";" << Qt::endl;
1171 m_stream <<
" " << secondOut <<
" <= " << firstOut <<
";" << Qt::endl;
1172 m_stream <<
" end" << Qt::endl;
1173 m_stream <<
" else if (" << j <<
" && ~" << k <<
")" << Qt::endl;
1174 m_stream <<
" begin" << Qt::endl;
1175 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1176 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1177 m_stream <<
" end" << Qt::endl;
1178 m_stream <<
" else if (~" << j <<
" && " << k <<
")" << Qt::endl;
1179 m_stream <<
" begin" << Qt::endl;
1180 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1181 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1182 m_stream <<
" end" << Qt::endl;
1188 case ElementType::TFlipFlop: {
1189 QString secondOut = m_varMap.value(elm->outputPort(1));
1190 QString t = otherPortName(elm->inputPort(0));
1191 QString clk = otherPortName(elm->inputPort(1));
1192 QString prst = otherPortName(elm->inputPort(2));
1193 QString clr = otherPortName(elm->inputPort(3));
1195 emitSequentialBlock(
"T FlipFlop", clk, prst, clr,
1197 m_stream <<
" " << firstOut <<
" <= 1'b1;" << Qt::endl;
1198 m_stream <<
" " << secondOut <<
" <= 1'b0;" << Qt::endl;
1201 m_stream <<
" " << firstOut <<
" <= 1'b0;" << Qt::endl;
1202 m_stream <<
" " << secondOut <<
" <= 1'b1;" << Qt::endl;
1205 m_stream <<
" if (" << t <<
")" << Qt::endl;
1206 m_stream <<
" begin" << Qt::endl;
1207 m_stream <<
" " << firstOut <<
" <= " << secondOut <<
";" << Qt::endl;
1208 m_stream <<
" " << secondOut <<
" <= " << firstOut <<
";" << Qt::endl;
1209 m_stream <<
" end" << Qt::endl;
1215 case ElementType::Mux: {
1218 int totalInputs = elm->inputSize();
1219 int numSelectLines = 1;
1220 while ((1 << numSelectLines) + numSelectLines < totalInputs) {
1223 int numDataInputs = totalInputs - numSelectLines;
1225 QString output = m_varMap.value(elm->outputPort(0));
1226 m_stream << QString(
" //Multiplexer") << Qt::endl;
1227 m_stream << QString(
" always @(*)") << Qt::endl;
1228 m_stream << QString(
" begin") << Qt::endl;
1229 m_stream << QString(
" case({");
1232 for (
int i = numSelectLines - 1; i >= 0; --i) {
1233 m_stream << otherPortName(elm->inputPort(numDataInputs + i));
1234 if (i > 0) m_stream <<
", ";
1236 m_stream <<
"})" << Qt::endl;
1239 for (
int i = 0; i < numDataInputs; ++i) {
1240 m_stream << QString(
" %1'd%2: %3 = %4;").arg(numSelectLines).arg(i).arg(output).arg(otherPortName(elm->inputPort(i))) << Qt::endl;
1242 m_stream << QString(
" default: %1 = 1'b0;").arg(output) << Qt::endl;
1243 m_stream << QString(
" endcase") << Qt::endl;
1244 m_stream << QString(
" end") << Qt::endl;
1245 m_stream << QString(
" //End of Multiplexer") << Qt::endl;
1249 case ElementType::Demux: {
1251 int numOutputs = elm->outputSize();
1252 int numSelectLines = 1;
1253 while ((1 << numSelectLines) < numOutputs) {
1257 QString dataInput = otherPortName(elm->inputPort(0));
1258 m_stream << QString(
" //Demultiplexer") << Qt::endl;
1259 m_stream << QString(
" always @(*)") << Qt::endl;
1260 m_stream << QString(
" begin") << Qt::endl;
1263 for (
int i = 0; i < numOutputs; ++i) {
1264 m_stream << QString(
" %1 = 1'b0;").arg(m_varMap.value(elm->outputPort(i))) << Qt::endl;
1268 m_stream << QString(
" case({");
1269 for (
int i = numSelectLines - 1; i >= 0; --i) {
1270 m_stream << otherPortName(elm->inputPort(1 + i));
1271 if (i > 0) m_stream <<
", ";
1273 m_stream <<
"})" << Qt::endl;
1276 for (
int i = 0; i < numOutputs; ++i) {
1277 m_stream << QString(
" %1'd%2: %3 = %4;").arg(numSelectLines).arg(i).arg(m_varMap.value(elm->outputPort(i))).arg(dataInput) << Qt::endl;
1279 m_stream << QString(
" endcase") << Qt::endl;
1280 m_stream << QString(
" end") << Qt::endl;
1281 m_stream << QString(
" //End of Demultiplexer") << Qt::endl;
1285 case ElementType::TruthTable: {
1286 auto *ttGraphic =
dynamic_cast<TruthTable *
>(elm);
1287 if (!ttGraphic)
break;
1289 QBitArray propositions = ttGraphic->key();
1290 const int nInputs = elm->inputSize();
1291 const int rows = 1 << nInputs;
1294 QStringList inputSignalNames;
1295 for (
int i = 0; i < nInputs; ++i) {
1296 Port *ttInputPort = elm->inputPort(i);
1297 QString signalName = otherPortName(ttInputPort);
1304 if (signalName ==
"LOW") {
1306 }
else if (signalName ==
"HIGH") {
1308 }
else if (signalName.isEmpty()) {
1309 m_stream <<
"// WARNING: Input " << i <<
" of TruthTable '" << elm->objectName() <<
"' appears disconnected. Assuming LOW." << Qt::endl;
1312 inputSignalNames << signalName;
1316 QStringList bitExpressions;
1317 bitExpressions <<
"{";
1318 for (
int i = 0; i < nInputs; ++i) {
1319 if (i < nInputs - 1) {
1320 bitExpressions << QString(
"%1, ").arg(inputSignalNames[i]);
1322 bitExpressions << QString(
"%1}").arg(inputSignalNames[i]);
1326 QString indexCalculation = bitExpressions.join(
"");
1331 for (
int out = 0; out < elm->outputSize(); ++out) {
1332 QString outputVarName = m_varMap.value(elm->outputPort(out));
1337 if (outputVarName.isEmpty()) {
1339 throw PANDACEPTION(
"Output variable not mapped for TruthTable: %1", elm->objectName());
1341 m_stream <<
"// TruthTable '" << elm->objectName() <<
"' output " << out <<
" is disconnected — no code emitted." << Qt::endl;
1345 m_stream << QString(
" //TruthTable") << Qt::endl;
1346 m_stream << QString(
" always @(*)") << Qt::endl;
1347 m_stream << QString(
" begin") << Qt::endl;
1348 m_stream << QString(
" case(%1)").arg(indexCalculation) << Qt::endl;
1350 for (
int i = 0; i < rows; ++i) {
1351 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;
1354 m_stream << QString(
" default: %1 = 1'b0;").arg(outputVarName) << Qt::endl;
1355 m_stream << QString(
" endcase") << Qt::endl;
1356 m_stream << QString(
" end") << Qt::endl;
1357 m_stream << QString(
" //End TruthTable") << Qt::endl;
1372 case ElementType::And:
1373 case ElementType::AudioBox:
1374 case ElementType::Buzzer:
1375 case ElementType::Clock:
1376 case ElementType::Display14:
1377 case ElementType::Display16:
1378 case ElementType::Display7:
1379 case ElementType::IC:
1380 case ElementType::InputButton:
1381 case ElementType::InputGnd:
1382 case ElementType::InputRotary:
1383 case ElementType::InputSwitch:
1384 case ElementType::InputVcc:
1385 case ElementType::JKLatch:
1386 case ElementType::Led:
1387 case ElementType::Line:
1388 case ElementType::Nand:
1389 case ElementType::Node:
1390 case ElementType::Nor:
1391 case ElementType::Not:
1392 case ElementType::Or:
1393 case ElementType::Text:
1394 case ElementType::Unknown:
1395 case ElementType::Xnor:
1396 case ElementType::Xor:
1397 throw PANDACEPTION(
"Element type not supported: %1", elm->objectName());
1404QString SystemVerilogCodeGen::generateLogicExpression(
GraphicElement *elm)
1406 QSet<Port *> visited;
1407 return generateLogicExpressionImpl(elm, visited);
1411QString SystemVerilogCodeGen::generateLogicExpressionImpl(
GraphicElement *elm, QSet<Port *> &visited)
1413 bool negate =
false;
1414 QString logicOperator;
1417 case ElementType::And: logicOperator =
"&";
break;
1418 case ElementType::Or: logicOperator =
"|";
break;
1419 case ElementType::Nand: logicOperator =
"&"; negate =
true;
break;
1420 case ElementType::Nor: logicOperator =
"|"; negate =
true;
break;
1421 case ElementType::Xor: logicOperator =
"^";
break;
1422 case ElementType::Xnor: logicOperator =
"^"; negate =
true;
break;
1423 case ElementType::Not: {
1424 QString inner = otherPortNameImpl(elm->
inputPort(0), visited);
1426 if (inner.startsWith(
"~")) {
1427 return inner.mid(1);
1431 case ElementType::Node:
return otherPortNameImpl(elm->
inputPort(0), visited);
1440 case ElementType::AudioBox:
1441 case ElementType::Buzzer:
1442 case ElementType::Clock:
1443 case ElementType::DFlipFlop:
1444 case ElementType::DLatch:
1445 case ElementType::Demux:
1446 case ElementType::Display14:
1447 case ElementType::Display16:
1448 case ElementType::Display7:
1449 case ElementType::IC:
1450 case ElementType::InputButton:
1451 case ElementType::InputGnd:
1452 case ElementType::InputRotary:
1453 case ElementType::InputSwitch:
1454 case ElementType::InputVcc:
1455 case ElementType::JKFlipFlop:
1456 case ElementType::JKLatch:
1457 case ElementType::Led:
1458 case ElementType::Line:
1459 case ElementType::Mux:
1460 case ElementType::SRFlipFlop:
1461 case ElementType::SRLatch:
1462 case ElementType::TFlipFlop:
1463 case ElementType::Text:
1464 case ElementType::TruthTable:
1465 case ElementType::Unknown:
1474 if (elm->
inputs().size() == 1) {
1475 expr = otherPortNameImpl(elm->
inputPort(0), visited);
1479 for (
int i = 0; i < elm->
inputs().size(); ++i) {
1480 if (i > 0) expr +=
" " + logicOperator +
" ";
1481 expr += otherPortNameImpl(elm->
inputPort(i), visited);
1491 if (expr.startsWith(
"~")) {
1501void SystemVerilogCodeGen::loop()
1505 m_stream <<
"\n// Assigning aux variables. //" << Qt::endl;
1506 assignVariablesRec(m_elements);
1508 m_stream <<
"\n// Writing output data. //" << Qt::endl;
1509 for (
const auto &pin : std::as_const(m_outputMap)) {
1510 QString expr = otherPortName(pin.m_port);
1513 if (expr.isEmpty()) {
1514 expr = fourState(pin.m_port->defaultValue());
1516 m_stream << QString(
"assign %1 = %2;").arg(pin.m_varName, expr) << Qt::endl;
1518 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.