9#include <QRegularExpression>
27 , m_elements(elements)
29 if (!m_file.open(QIODevice::WriteOnly | QIODevice::Text)) {
30 throw PANDACEPTION(
"Could not open file for writing: %1", fileName);
32 m_stream.setDevice(&m_file);
35QString ArduinoCodeGen::highLow(
const Status val)
37 return (val == Status::Active) ?
"HIGH" :
"LOW";
40QString ArduinoCodeGen::removeForbiddenChars(
const QString &input)
45bool ArduinoCodeGen::isArduinoReserved(
const QString &name)
47 static const QSet<QString> reserved = {
49 "alignas",
"alignof",
"and",
"and_eq",
"asm",
"atomic_cancel",
"atomic_commit",
50 "atomic_noexcept",
"auto",
"bitand",
"bitor",
"bool",
"break",
"case",
"catch",
51 "char",
"char8_t",
"char16_t",
"char32_t",
"class",
"compl",
"concept",
52 "const",
"consteval",
"constexpr",
"constinit",
"const_cast",
"continue",
53 "co_await",
"co_return",
"co_yield",
"decltype",
"default",
"delete",
"do",
54 "double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
55 "false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
56 "mutable",
"namespace",
"new",
"noexcept",
"not",
"not_eq",
"nullptr",
"or",
57 "or_eq",
"private",
"protected",
"public",
"reflexpr",
"register",
58 "reinterpret_cast",
"requires",
"return",
"short",
"signed",
"sizeof",
59 "static",
"static_assert",
"static_cast",
"struct",
"switch",
"synchronized",
60 "template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
61 "typeid",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
62 "volatile",
"wchar_t",
"while",
"xor",
"xor_eq",
64 "high",
"low",
"input",
"output",
"input_pullup",
"input_pulldown",
65 "led_builtin",
"a0",
"a1",
"a2",
"a3",
"a4",
"a5",
"a6",
"a7",
66 "pinmode",
"digitalwrite",
"digitalread",
"analogread",
"analogwrite",
67 "analogreadresolution",
"analogwriteresolution",
"serial",
68 "tone",
"notone",
"delay",
"millis",
"micros",
"setup",
"loop"
70 return reserved.contains(name);
73QString ArduinoCodeGen::otherPortName(
Port *port)
76 return otherPortNameImpl(port, visited);
79QString ArduinoCodeGen::otherPortNameImpl(
Port *port, QSet<Port *> &visited)
90 if (visited.contains(port)) {
91 const QString mapped = m_varMap.value(port);
92 return mapped.isEmpty() ?
"LOW" : mapped;
97 const QString mapped = m_varMap.value(port);
98 if (!mapped.isEmpty()) {
103 if (elm && elm->wirelessMode() == WirelessMode::Rx && !elm->label().isEmpty()) {
104 auto *txInputPort = m_txInputPorts.value(elm->label(),
nullptr);
106 return otherPortNameImpl(txInputPort, visited);
112 auto *connection = port->
connections().constFirst();
117 auto *otherPort = connection->otherPort(port);
124 if (visited.contains(otherPort)) {
125 const QString mapped = m_varMap.value(otherPort);
126 return mapped.isEmpty() ?
"LOW" : mapped;
130 visited.insert(port);
131 visited.insert(otherPort);
133 const QString result = m_varMap.value(otherPort);
134 if (result.isEmpty()) {
138 return highLow(otherPort->defaultValue());
147 m_hasSequential = hasNativeMemory(m_elements);
149 int requiredInputPins = 0;
150 int requiredOutputPins = 0;
151 for (
auto *elm : m_elements) {
152 const auto type = elm->elementType();
153 if (type == ElementType::InputButton || type == ElementType::InputSwitch) {
155 }
else if (type == ElementType::InputRotary) {
156 requiredInputPins += elm->outputSize();
159 for (
auto *elm : m_elements) {
160 if (elm->elementGroup() == ElementGroup::Output) {
161 requiredOutputPins +=
static_cast<int>(elm->inputs().size());
164 const int totalRequiredPins = requiredInputPins + requiredOutputPins;
165 m_totalRequiredPins = totalRequiredPins;
166 m_selectedBoard = selectBoard(totalRequiredPins);
167 m_availablePins = m_selectedBoard.availablePins;
169 m_stream <<
"// ==================================================================== //" << Qt::endl;
170 m_stream <<
"// ======= This code was generated automatically by wiRedPanda ======== //" << Qt::endl;
171 m_stream <<
"// ==================================================================== //" << Qt::endl;
172 m_stream <<
"//" << Qt::endl;
173 m_stream << QString(
"// Target Board: %1").arg(m_selectedBoard.name) << Qt::endl;
174 m_stream << QString(
"// Pin Usage: %1/%2 pins").arg(totalRequiredPins).arg(m_selectedBoard.maxPins()) << Qt::endl;
175 m_stream <<
"//" << Qt::endl;
178 m_stream <<
"#include <elapsedMillis.h>" << Qt::endl;
181 declareAuxVariables();
182 if (m_hasSequential) {
185 m_stream <<
"bool g_sample = true;" << Qt::endl << Qt::endl;
188 emitComputeLogicFunction();
189 if (m_hasSequential) {
190 emitCommitFlipFlops();
200void ArduinoCodeGen::declareInputs()
203 m_stream <<
"/* ========= Inputs ========== */" << Qt::endl;
205 for (
auto *elm : m_elements) {
206 const auto type = elm->elementType();
208 if ((type == ElementType::InputButton) || (type == ElementType::InputSwitch)) {
210 if (m_availablePins.isEmpty()) {
214 QString varName = elm->objectName() + QString::number(counter);
215 const QString label = elm->label();
217 if (!label.isEmpty()) {
218 varName +=
"_" + label;
221 varName = removeForbiddenChars(varName);
226 if (isArduinoReserved(varName)) {
229 m_stream << QString(
"const int %1 = %2;").arg(varName, m_availablePins.constFirst()) << Qt::endl;
230 auto *outPort = elm->outputPort(0);
232 m_inputMap.append(MappedPin(elm, m_availablePins.constFirst(), varName, outPort, 0));
233 m_varMap[outPort] = varName + QString(
"_val");
235 m_availablePins.removeFirst();
237 }
else if (type == ElementType::InputRotary) {
239 const QString label = elm->label();
240 for (
int i = 0; i < elm->outputSize(); ++i) {
241 if (m_availablePins.isEmpty()) {
244 QString varName = elm->objectName() + QString::number(counter) +
"_pos" + QString::number(i);
245 if (!label.isEmpty()) {
246 varName = elm->objectName() + QString::number(counter) +
"_" + label +
"_pos" + QString::number(i);
248 varName = removeForbiddenChars(varName);
249 if (isArduinoReserved(varName)) {
252 m_stream << QString(
"const int %1 = %2;").arg(varName, m_availablePins.constFirst()) << Qt::endl;
253 auto *outPort = elm->outputPort(i);
255 m_inputMap.append(MappedPin(elm, m_availablePins.constFirst(), varName, outPort, i));
256 m_varMap[outPort] = varName +
"_val";
258 m_availablePins.removeFirst();
264 m_stream << Qt::endl;
267void ArduinoCodeGen::declareOutputs()
270 m_stream <<
"/* ========= Outputs ========== */" << Qt::endl;
271 for (
auto *elm : m_elements) {
272 if (elm->elementGroup() == ElementGroup::Output) {
273 QString label = elm->label();
274 for (
int i = 0; i < elm->inputs().size(); ++i) {
276 if (m_availablePins.isEmpty()) {
280 QString varName = elm->objectName() + QString::number(counter);
281 if (!label.isEmpty()) {
282 varName = QString(
"%1_%2").arg(varName, label);
284 Port *port = elm->inputPort(i);
285 if (port && !port->
name().isEmpty()) {
286 varName = QString(
"%1_%2").arg(varName, port->
name());
288 varName = removeForbiddenChars(varName);
289 if (isArduinoReserved(varName)) {
292 m_stream << QString(
"const int %1 = %2;").arg(varName, m_availablePins.constFirst()) << Qt::endl;
293 m_outputMap.append(MappedPin(elm, m_availablePins.constFirst(), varName, port, i));
294 m_availablePins.removeFirst();
299 m_stream << Qt::endl;
302void ArduinoCodeGen::declareAuxVariablesRec(
const QVector<GraphicElement *> &elements,
const bool isBox,
const QString &icPrefix)
306 for (
auto *elm : elements) {
307 if (elm->elementType() == ElementType::IC) {
308 auto *ic = qobject_cast<IC *>(elm);
316 QString baseVarName = QString(
"aux_%1%2_%3").arg(icPrefix, removeForbiddenChars(ic->label()), QString::number(counter++));
318 for (
int i = 0; i < ic->outputSize(); ++i) {
319 Port *externalPort = ic->outputPort(i);
321 QString portVarName = baseVarName;
322 const QString portName = externalPort->
name();
323 if (!portName.isEmpty()) {
324 portVarName +=
"_" + removeForbiddenChars(portName);
326 portVarName +=
"_" + QString::number(i);
329 portVarName +=
"_out" + QString::number(i);
331 m_varMap[externalPort] = portVarName;
332 if (!m_declaredVariables.contains(portVarName)) {
333 m_stream <<
"bool " << portVarName <<
" = " << highLow(externalPort->
defaultValue()) <<
";" << Qt::endl;
334 m_declaredVariables.append(portVarName);
339 if (!ic->internalElements().isEmpty()) {
340 const QString nestedPrefix = QString(
"%1%2_%3_").arg(icPrefix, removeForbiddenChars(ic->label()), QString::number(counter - 1));
341 declareAuxVariablesRec(ic->internalElements(),
true, nestedPrefix);
343 for (
int i = 0; i < ic->internalInputs().size(); ++i) {
344 Port *internalPort = ic->internalInputs().at(i);
345 if (m_varMap.value(internalPort).isEmpty()) {
346 const QString portVarName = QString(
"aux_ic_input_%1%2_%3_%4").arg(icPrefix, removeForbiddenChars(ic->label()), QString::number(counter - 1), QString::number(i));
347 m_varMap[internalPort] = portVarName;
348 if (!m_declaredVariables.contains(portVarName)) {
349 m_stream <<
"bool " << portVarName <<
" = LOW;" << Qt::endl;
350 m_declaredVariables.append(portVarName);
360 const auto outputs = elm->outputs();
363 if (!icPrefix.isEmpty()) {
364 baseVarName = QString(
"aux_%1%2_%3").arg(icPrefix, removeForbiddenChars(elm->objectName()), QString::number(counter++));
366 baseVarName = QString(
"aux_%1_%2").arg(removeForbiddenChars(elm->objectName()), QString::number(counter++));
369 if (outputs.size() == 1) {
370 Port *port = outputs.constFirst();
372 if (elm->elementType() == ElementType::InputVcc) {
373 m_varMap[port] =
"HIGH";
377 if (elm->elementType() == ElementType::InputGnd) {
378 m_varMap[port] =
"LOW";
382 if (m_varMap.value(port).isEmpty()) {
383 m_varMap[port] = baseVarName;
387 for (
auto *port : outputs) {
388 QString portName = baseVarName + QString(
"_%1").arg(portCounter++);
389 if (!port->
name().isEmpty()) {
390 portName +=
"_" + removeForbiddenChars(port->
name());
392 m_varMap[port] = portName;
396 for (
auto *port : outputs) {
397 const QString varName2 = m_varMap.value(port);
398 if (!m_declaredVariables.contains(varName2)) {
399 m_stream <<
"bool " << varName2 <<
" = " << highLow(port->
defaultValue()) <<
";" << Qt::endl;
400 m_declaredVariables.append(varName2);
404 if (elm->elementGroup() == ElementGroup::Memory) {
405 m_stream <<
"bool " << varName2 <<
"_next = " << highLow(port->
defaultValue()) <<
";" << Qt::endl;
408 switch (elm->elementType()) {
409 case ElementType::Clock: {
411 auto *clk = qobject_cast<Clock *>(elm);
415 m_stream <<
"elapsedMillis " << varName2 <<
"_elapsed = 0;" << Qt::endl;
416 m_stream <<
"int " << varName2 <<
"_interval = " << (std::max)(1,
static_cast<int>(1000.0 / clk->frequency())) <<
";" << Qt::endl;
420 case ElementType::DFlipFlop: {
421 m_stream <<
"bool " << varName2 <<
"_inclk = LOW;" << Qt::endl;
422 m_stream <<
"bool " << varName2 <<
"_last = LOW;" << Qt::endl;
425 case ElementType::TFlipFlop:
426 case ElementType::SRFlipFlop:
427 case ElementType::JKFlipFlop: {
428 m_stream <<
"bool " << varName2 <<
"_inclk = LOW;" << Qt::endl;
431 case ElementType::And:
432 case ElementType::AudioBox:
433 case ElementType::Buzzer:
434 case ElementType::DLatch:
435 case ElementType::Demux:
436 case ElementType::Display14:
437 case ElementType::Display16:
438 case ElementType::Display7:
439 case ElementType::IC:
440 case ElementType::InputButton:
441 case ElementType::InputGnd:
442 case ElementType::InputRotary:
443 case ElementType::InputSwitch:
444 case ElementType::InputVcc:
445 case ElementType::JKLatch:
446 case ElementType::Led:
447 case ElementType::Line:
448 case ElementType::Mux:
449 case ElementType::Nand:
450 case ElementType::Node:
451 case ElementType::Nor:
452 case ElementType::Not:
453 case ElementType::Or:
454 case ElementType::SRLatch:
455 case ElementType::Text:
456 case ElementType::TruthTable:
457 case ElementType::Unknown:
458 case ElementType::Xnor:
459 case ElementType::Xor:
466void ArduinoCodeGen::declareSequentialStateRec(
const QVector<GraphicElement *> &elements,
const bool topLevel)
468 for (
auto *elm : elements) {
469 if (elm->elementType() == ElementType::IC) {
470 if (
auto *ic = qobject_cast<IC *>(elm)) {
471 declareSequentialStateRec(ic->internalElements(),
false);
476 const auto outputs = elm->outputs();
477 if (outputs.isEmpty()) {
481 if (elm->elementGroup() == ElementGroup::Memory) {
482 for (
auto *port : outputs) {
483 const QString v = m_varMap.value(port);
485 m_stream <<
"bool " << v <<
"_next = " << highLow(port->
defaultValue()) <<
";" << Qt::endl;
490 const QString varName = m_varMap.value(outputs.constFirst());
491 if (varName.isEmpty()) {
498 switch (elm->elementType()) {
499 case ElementType::Clock:
502 m_stream <<
"elapsedMillis " << varName <<
"_elapsed = 0;" << Qt::endl;
503 m_stream <<
"int " << varName <<
"_interval = 1000;" << Qt::endl;
506 case ElementType::DFlipFlop:
507 m_stream <<
"bool " << varName <<
"_inclk = LOW;" << Qt::endl;
508 m_stream <<
"bool " << varName <<
"_last = LOW;" << Qt::endl;
510 case ElementType::TFlipFlop:
511 case ElementType::SRFlipFlop:
512 case ElementType::JKFlipFlop:
513 m_stream <<
"bool " << varName <<
"_inclk = LOW;" << Qt::endl;
515 case ElementType::And:
516 case ElementType::AudioBox:
517 case ElementType::Buzzer:
518 case ElementType::DLatch:
519 case ElementType::Demux:
520 case ElementType::Display14:
521 case ElementType::Display16:
522 case ElementType::Display7:
523 case ElementType::IC:
524 case ElementType::InputButton:
525 case ElementType::InputGnd:
526 case ElementType::InputRotary:
527 case ElementType::InputSwitch:
528 case ElementType::InputVcc:
529 case ElementType::JKLatch:
530 case ElementType::Led:
531 case ElementType::Line:
532 case ElementType::Mux:
533 case ElementType::Nand:
534 case ElementType::Node:
535 case ElementType::Nor:
536 case ElementType::Not:
537 case ElementType::Or:
538 case ElementType::SRLatch:
539 case ElementType::Text:
540 case ElementType::TruthTable:
541 case ElementType::Unknown:
542 case ElementType::Xnor:
543 case ElementType::Xor:
549bool ArduinoCodeGen::hasNativeMemory(
const QVector<GraphicElement *> &elements)
551 for (
auto *elm : elements) {
552 if (elm->elementGroup() == ElementGroup::Memory) {
555 if (elm->elementType() == ElementType::IC) {
556 if (
auto *ic = qobject_cast<IC *>(elm)) {
557 if (hasNativeMemory(ic->internalElements())) {
566void ArduinoCodeGen::emitCommitFlipFlopsRec(
const QVector<GraphicElement *> &elements)
568 for (
auto *elm : elements) {
569 if (elm->elementType() == ElementType::IC) {
570 if (
auto *ic = qobject_cast<IC *>(elm)) {
571 emitCommitFlipFlopsRec(ic->internalElements());
575 if (elm->elementGroup() != ElementGroup::Memory) {
578 for (
auto *port : elm->outputs()) {
579 const QString varName = m_varMap.value(port);
580 if (!varName.isEmpty()) {
581 m_stream <<
" " << varName <<
" = " << varName <<
"_next;" << Qt::endl;
587void ArduinoCodeGen::emitCommitFlipFlops()
589 m_stream <<
"void commitFlipFlops() {" << Qt::endl;
590 emitCommitFlipFlopsRec(m_elements);
591 m_stream <<
"}" << Qt::endl << Qt::endl;
594void ArduinoCodeGen::emitTickDriver()
600 if (m_hasSequential) {
601 m_stream <<
" g_sample = true;" << Qt::endl;
603 m_stream <<
" commitFlipFlops();" << Qt::endl;
604 m_stream <<
" g_sample = false;" << Qt::endl;
607 m_stream <<
" computeLogic();" << Qt::endl;
611void ArduinoCodeGen::declareAuxVariables()
613 m_stream <<
"/* ====== Aux. Variables ====== */" << Qt::endl;
615 for (
const auto &pin : std::as_const(m_inputMap)) {
616 const QString valVarName = pin.m_varName +
"_val";
617 if (!m_declaredVariables.contains(valVarName)) {
618 m_stream <<
"bool " << valVarName <<
" = LOW;" << Qt::endl;
619 m_declaredVariables.append(valVarName);
623 declareAuxVariablesRec(m_elements,
false, {});
624 m_stream << Qt::endl;
627void ArduinoCodeGen::emitFlipFlopBlock(
GraphicElement *elm,
const QString &typeName,
const QString &firstOut,
628 const QString &secondOut,
int clockInputIndex,
int presetInputIndex,
629 int clearInputIndex,
const std::function<
void()> &edgeLogic,
630 const std::function<
void()> &stateEpilogue)
632 QString clk = otherPortName(elm->
inputPort(clockInputIndex));
633 QString inclk = firstOut +
"_inclk";
634 QString firstOutNext = firstOut +
"_next";
635 QString secondOutNext = secondOut +
"_next";
637 m_stream << QString(
" //%1 FlipFlop").arg(typeName) << Qt::endl;
642 m_stream << QString(
" if (g_sample) {") << Qt::endl;
643 m_stream << QString(
" if (%1 && !%2) { ").arg(clk, inclk) << Qt::endl;
648 m_stream << QString(
" }") << Qt::endl;
651 QString prst = otherPortName(elm->
inputPort(presetInputIndex));
652 QString clr = otherPortName(elm->
inputPort(clearInputIndex));
653 m_stream << QString(
" if (!%1 || !%2) { ").arg(prst, clr) << Qt::endl;
654 m_stream << QString(
" %1 = !%2; //Preset").arg(firstOutNext, prst) << Qt::endl;
655 m_stream << QString(
" %1 = !%2; //Clear").arg(secondOutNext, clr) << Qt::endl;
656 m_stream << QString(
" }") << Qt::endl;
659 m_stream <<
" " << inclk <<
" = " << clk <<
";" << Qt::endl;
666 m_stream << QString(
" }") << Qt::endl;
667 m_stream << QString(
" //End of %1 FlipFlop").arg(typeName) << Qt::endl;
670void ArduinoCodeGen::setup()
672 m_stream <<
"void setup() {" << Qt::endl;
673 for (
const auto &pin : std::as_const(m_inputMap)) {
674 m_stream <<
" pinMode(" << pin.m_varName <<
", INPUT);" << Qt::endl;
676 for (
const auto &pin : std::as_const(m_outputMap)) {
677 m_stream <<
" pinMode(" << pin.m_varName <<
", OUTPUT);" << Qt::endl;
679 m_stream <<
"}" << Qt::endl
683void ArduinoCodeGen::assignVariablesRec(
const QVector<GraphicElement *> &elements)
685 for (
auto *elm : elements) {
687 auto *ic = qobject_cast<IC *>(elm);
692 for (
int i = 0; i < ic->inputSize(); ++i) {
693 Port *externalPort = ic->inputPort(i);
694 Port *internalPort = ic->internalInputs().at(i);
695 const QString externalValue = otherPortName(externalPort);
696 const QString internalVar = m_varMap.value(internalPort);
697 m_stream <<
" " << internalVar <<
" = " << externalValue <<
";" << Qt::endl;
700 if (!ic->internalElements().isEmpty()) {
701 IC *previousIC = m_currentIC;
710 QSet<GraphicElement *> boundaryInputs;
711 for (
auto *port : ic->internalInputs()) {
713 boundaryInputs.insert(boundaryElement);
717 std::stable_partition(sortedInternal.begin(), sortedInternal.end(),
718 [&boundaryInputs](GraphicElement *e) { return boundaryInputs.contains(e); });
719 assignVariablesRec(sortedInternal);
720 m_currentIC = previousIC;
723 for (
int i = 0; i < ic->outputSize(); ++i) {
724 Port *externalPort = ic->outputPort(i);
725 Port *internalPort = ic->internalOutputs().at(i);
726 const QString internalValue = m_varMap.value(internalPort);
727 const QString externalVar = m_varMap.value(externalPort);
728 m_stream <<
" " << externalVar <<
" = " << internalValue <<
";" << Qt::endl;
743 QString firstOut = m_varMap.value(outputPort0);
745 case ElementType::DFlipFlop: emitDFlipFlop(elm, firstOut);
break;
746 case ElementType::DLatch: emitDLatch(elm, firstOut);
break;
747 case ElementType::JKFlipFlop: emitJKFlipFlop(elm, firstOut);
break;
748 case ElementType::SRFlipFlop: emitSRFlipFlop(elm, firstOut);
break;
749 case ElementType::TFlipFlop: emitTFlipFlop(elm, firstOut);
break;
750 case ElementType::SRLatch: emitSRLatch(elm, firstOut);
break;
751 case ElementType::Mux: emitMux(elm);
break;
752 case ElementType::Demux: emitDemux(elm);
break;
753 case ElementType::TruthTable: emitTruthTable(elm);
break;
754 case ElementType::And:
755 case ElementType::Or:
756 case ElementType::Nand:
757 case ElementType::Nor:
758 case ElementType::Xor:
759 case ElementType::Xnor:
760 case ElementType::Not:
761 case ElementType::Node: assignLogicOperator(elm);
break;
765 case ElementType::AudioBox:
766 case ElementType::Buzzer:
767 case ElementType::Clock:
768 case ElementType::Display14:
769 case ElementType::Display16:
770 case ElementType::Display7:
771 case ElementType::IC:
772 case ElementType::InputButton:
773 case ElementType::InputGnd:
774 case ElementType::InputRotary:
775 case ElementType::InputSwitch:
776 case ElementType::InputVcc:
777 case ElementType::JKLatch:
778 case ElementType::Led:
779 case ElementType::Line:
780 case ElementType::Text:
781 case ElementType::Unknown:
782 throw PANDACEPTION(
"Element type not supported: %1", elm->objectName());
787void ArduinoCodeGen::emitDFlipFlop(
GraphicElement *elm,
const QString &firstOut)
790 if (!outputPort1)
return;
791 QString secondOut = m_varMap.value(outputPort1);
792 QString firstOutNext = firstOut +
"_next";
793 QString secondOutNext = secondOut +
"_next";
794 QString data = otherPortName(elm->
inputPort(0));
795 QString last = firstOut +
"_last";
796 emitFlipFlopBlock(elm,
"D", firstOut, secondOut, 1, 2, 3,
797 [
this, &last, &firstOutNext, &secondOutNext]() {
798 m_stream << QString(
" %1 = %2;").arg(firstOutNext, last) << Qt::endl;
799 m_stream << QString(
" %1 = !%2;").arg(secondOutNext, last) << Qt::endl;
801 [
this, &last, &data]() {
802 m_stream <<
" " << last <<
" = " << data <<
";" << Qt::endl;
806void ArduinoCodeGen::emitDLatch(
GraphicElement *elm,
const QString &firstOut)
809 if (!outputPort1)
return;
810 QString secondOut = m_varMap.value(outputPort1);
811 QString firstOutNext = firstOut +
"_next";
812 QString secondOutNext = secondOut +
"_next";
813 QString data = otherPortName(elm->
inputPort(0));
814 QString clk = otherPortName(elm->
inputPort(1));
815 m_stream << QString(
" //D Latch") << Qt::endl;
816 m_stream << QString(
" if (g_sample) {") << Qt::endl;
817 m_stream << QString(
" if (%1) { ").arg(clk) << Qt::endl;
818 m_stream << QString(
" %1 = %2;").arg(firstOutNext, data) << Qt::endl;
819 m_stream << QString(
" %1 = !%2;").arg(secondOutNext, data) << Qt::endl;
820 m_stream << QString(
" }") << Qt::endl;
821 m_stream << QString(
" }") << Qt::endl;
822 m_stream << QString(
" //End of D Latch") << Qt::endl;
825void ArduinoCodeGen::emitJKFlipFlop(
GraphicElement *elm,
const QString &firstOut)
828 if (!outputPort1)
return;
829 QString secondOut = m_varMap.value(outputPort1);
830 QString firstOutNext = firstOut +
"_next";
831 QString secondOutNext = secondOut +
"_next";
832 QString j = otherPortName(elm->
inputPort(0));
833 QString k = otherPortName(elm->
inputPort(2));
834 emitFlipFlopBlock(elm,
"JK", firstOut, secondOut, 1, 3, 4,
835 [
this, &j, &k, &firstOut, &secondOut, &firstOutNext, &secondOutNext]() {
836 m_stream << QString(
" if (%1 && %2) { ").arg(j, k) << Qt::endl;
837 m_stream << QString(
" bool aux = %1;").arg(firstOut) << Qt::endl;
838 m_stream << QString(
" %1 = %2;").arg(firstOutNext, secondOut) << Qt::endl;
839 m_stream << QString(
" %1 = aux;").arg(secondOutNext) << Qt::endl;
840 m_stream << QString(
" } else if (%1) {").arg(j) << Qt::endl;
841 m_stream << QString(
" %1 = 1;").arg(firstOutNext) << Qt::endl;
842 m_stream << QString(
" %1 = 0;").arg(secondOutNext) << Qt::endl;
843 m_stream << QString(
" } else if (%1) {").arg(k) << Qt::endl;
844 m_stream << QString(
" %1 = 0;").arg(firstOutNext) << Qt::endl;
845 m_stream << QString(
" %1 = 1;").arg(secondOutNext) << Qt::endl;
846 m_stream << QString(
" }") << Qt::endl;
850void ArduinoCodeGen::emitSRFlipFlop(
GraphicElement *elm,
const QString &firstOut)
853 if (!outputPort1)
return;
854 QString secondOut = m_varMap.value(outputPort1);
855 QString firstOutNext = firstOut +
"_next";
856 QString secondOutNext = secondOut +
"_next";
857 QString s = otherPortName(elm->
inputPort(0));
858 QString r = otherPortName(elm->
inputPort(2));
859 emitFlipFlopBlock(elm,
"SR", firstOut, secondOut, 1, 3, 4,
860 [
this, &s, &r, &firstOutNext, &secondOutNext]() {
861 m_stream << QString(
" if (%1 && %2) { ").arg(s, r) << Qt::endl;
862 m_stream << QString(
" %1 = 1;").arg(firstOutNext) << Qt::endl;
863 m_stream << QString(
" %1 = 1;").arg(secondOutNext) << Qt::endl;
864 m_stream << QString(
" } else if (%1 != %2) {").arg(s, r) << Qt::endl;
865 m_stream << QString(
" %1 = %2;").arg(firstOutNext, s) << Qt::endl;
866 m_stream << QString(
" %1 = %2;").arg(secondOutNext, r) << Qt::endl;
867 m_stream << QString(
" }") << Qt::endl;
871void ArduinoCodeGen::emitTFlipFlop(
GraphicElement *elm,
const QString &firstOut)
874 if (!outputPort1)
return;
875 QString secondOut = m_varMap.value(outputPort1);
876 QString firstOutNext = firstOut +
"_next";
877 QString secondOutNext = secondOut +
"_next";
878 QString t = otherPortName(elm->
inputPort(0));
879 emitFlipFlopBlock(elm,
"T", firstOut, secondOut, 1, 2, 3,
880 [
this, &t, &firstOut, &firstOutNext, &secondOutNext]() {
882 m_stream << QString(
" if (%1) { ").arg(t) << Qt::endl;
883 m_stream << QString(
" %1 = !%2;").arg(firstOutNext, firstOut) << Qt::endl;
884 m_stream << QString(
" %1 = %2;").arg(secondOutNext, firstOut) << Qt::endl;
885 m_stream << QString(
" }") << Qt::endl;
889void ArduinoCodeGen::emitSRLatch(
GraphicElement *elm,
const QString &firstOut)
892 if (!outputPort1)
return;
893 QString secondOut = m_varMap.value(outputPort1);
894 QString firstOutNext = firstOut +
"_next";
895 QString secondOutNext = secondOut +
"_next";
896 QString s = otherPortName(elm->
inputPort(0));
897 QString r = otherPortName(elm->
inputPort(1));
898 m_stream << QString(
" //SR Latch") << Qt::endl;
899 m_stream << QString(
" if (g_sample) {") << Qt::endl;
900 m_stream << QString(
" if (%1 && %2) { ").arg(s, r) << Qt::endl;
901 m_stream << QString(
" %1 = LOW;").arg(firstOutNext) << Qt::endl;
902 m_stream << QString(
" %1 = LOW;").arg(secondOutNext) << Qt::endl;
903 m_stream << QString(
" } else if (%1) { ").arg(s) << Qt::endl;
904 m_stream << QString(
" %1 = HIGH;").arg(firstOutNext) << Qt::endl;
905 m_stream << QString(
" %1 = LOW;").arg(secondOutNext) << Qt::endl;
906 m_stream << QString(
" } else if (%1) { ").arg(r) << Qt::endl;
907 m_stream << QString(
" %1 = LOW;").arg(firstOutNext) << Qt::endl;
908 m_stream << QString(
" %1 = HIGH;").arg(secondOutNext) << Qt::endl;
909 m_stream << QString(
" }") << Qt::endl;
910 m_stream << QString(
" }") << Qt::endl;
911 m_stream << QString(
" //End of SR Latch") << Qt::endl;
916 const int totalInputs = elm->
inputSize();
917 int numSelectLines = 1;
918 while (numSelectLines < 16 && (1 << numSelectLines) + numSelectLines < totalInputs) {
921 const int numDataInputs = totalInputs - numSelectLines;
922 const QString output = m_varMap.value(elm->
outputPort(0));
923 const QString selectValue = buildSelectExpression(elm, numDataInputs, numSelectLines);
925 m_stream << QString(
" //Multiplexer") << Qt::endl;
926 for (
int i = 0; i < numDataInputs; ++i) {
928 m_stream << QString(
" if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
930 m_stream << QString(
" } else if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
932 m_stream << QString(
" %1 = %2;").arg(output, otherPortName(elm->
inputPort(i))) << Qt::endl;
934 m_stream << QString(
" } else {") << Qt::endl;
935 m_stream << QString(
" %1 = LOW;").arg(output) << Qt::endl;
936 m_stream << QString(
" }") << Qt::endl;
937 m_stream << QString(
" //End of Multiplexer") << Qt::endl;
943 int numSelectLines = 1;
944 while (numSelectLines < 16 && (1 << numSelectLines) < numOutputs) {
947 const QString dataInput = otherPortName(elm->
inputPort(0));
948 const QString selectValue = buildSelectExpression(elm, 1, numSelectLines);
950 m_stream << QString(
" //Demultiplexer") << Qt::endl;
951 for (
int i = 0; i < numOutputs; ++i) {
952 m_stream << QString(
" %1 = LOW;").arg(m_varMap.value(elm->
outputPort(i))) << Qt::endl;
954 for (
int i = 0; i < numOutputs; ++i) {
956 m_stream << QString(
" if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
958 m_stream << QString(
" } else if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
960 m_stream << QString(
" %1 = %2;").arg(m_varMap.value(elm->
outputPort(i)), dataInput) << Qt::endl;
962 m_stream << QString(
" }") << Qt::endl;
963 m_stream << QString(
" //End of Demultiplexer") << Qt::endl;
968 auto *ttGraphic =
dynamic_cast<TruthTable *
>(elm);
969 if (!ttGraphic)
return;
971 const QBitArray propositions = ttGraphic->key();
973 const int rows = 1 << nInputs;
975 QStringList inputSignalNames;
976 for (
int i = 0; i < nInputs; ++i) {
977 QString signalName = otherPortName(elm->
inputPort(i));
978 if (signalName ==
"LOW") {
980 }
else if (signalName ==
"HIGH") {
982 }
else if (signalName.isEmpty()) {
986 m_stream <<
"// WARNING: Input " << i <<
" of TruthTable '" << elm->objectName() <<
"' appears disconnected. Assuming LOW." << Qt::endl;
989 inputSignalNames << signalName;
992 QString indexCalculation;
993 for (
int i = 0; i < nInputs; ++i) {
995 indexCalculation = inputSignalNames[i];
997 indexCalculation = inputSignalNames[i] +
" + (" + indexCalculation +
" << 1)";
1004 for (
int out = 0; out < elm->
outputSize(); ++out) {
1005 const QString outputVarName = m_varMap.value(elm->
outputPort(out));
1009 if (outputVarName.isEmpty()) {
1011 throw PANDACEPTION(
"Output variable not mapped for TruthTable: %1", elm->objectName());
1013 m_stream <<
"// TruthTable '" << elm->objectName() <<
"' output " << out <<
" is disconnected — no code emitted." << Qt::endl;
1017 m_stream << QString(
" //TruthTable") << Qt::endl;
1018 for (
int i = 0; i < rows; ++i) {
1020 m_stream << QString(
" if ((%1) == %2) {").arg(indexCalculation).arg(i) << Qt::endl;
1022 m_stream << QString(
" } else if ((%1) == %2) {").arg(indexCalculation).arg(i) << Qt::endl;
1024 m_stream << QString(
" %1 = %2;").arg(outputVarName, propositions.testBit(256 * out + i) ?
"HIGH" :
"LOW") << Qt::endl;
1026 m_stream << QString(
" } else {") << Qt::endl;
1027 m_stream << QString(
" %1 = LOW;").arg(outputVarName) << Qt::endl;
1028 m_stream << QString(
" }") << Qt::endl;
1029 m_stream << QString(
" //End TruthTable") << Qt::endl;
1033QVector<ArduinoBoardConfig> ArduinoCodeGen::availableBoards()
const
1035 static const QVector<ArduinoBoardConfig> boards = []() {
1036 QVector<ArduinoBoardConfig> b;
1039 "Arduino UNO R3/R4",
1040 {
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13"},
1041 "Standard Arduino board with 20 I/O pins"
1046 {
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13"},
1047 "Compact Arduino board with 20 I/O pins"
1050 QStringList megaPins;
1051 for (
int i = 0; i <= 15; ++i) {
1052 megaPins.append(QString(
"A%1").arg(i));
1054 for (
int i = 2; i <= 53; ++i) {
1055 megaPins.append(QString::number(i));
1058 "Arduino Mega 2560",
1060 "High I/O count Arduino board with 70 I/O pins"
1065 {
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"A6",
"A7",
"A8",
"A9",
"A10",
"A11",
"A12",
"A13",
"A14",
"A15",
"A16",
"A17",
1066 "2",
"4",
"5",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"21",
"22",
"23",
"25",
"26",
"27",
"32",
"33",
"34"},
1067 "WiFi/Bluetooth enabled board with 36 I/O pins"
1077 const auto boards = availableBoards();
1078 for (
const auto &board : boards) {
1079 if (board.maxPins() >= requiredPins) {
1085 return *std::max_element(boards.cbegin(), boards.cend(),
1086 [](
const ArduinoBoardConfig &a,
const ArduinoBoardConfig &b) { return a.maxPins() < b.maxPins(); });
1089void ArduinoCodeGen::throwPinOverflow()
const
1091 throw PANDACEPTION(
"This circuit needs %1 I/O pins, but the largest supported board (%2) provides only %3. Reduce the number of inputs and outputs, or split the circuit.",
1092 QString::number(m_totalRequiredPins), m_selectedBoard.name, QString::number(m_selectedBoard.maxPins()));
1095QString ArduinoCodeGen::buildSelectExpression(
GraphicElement *elm,
int startIndex,
int numSelectLines)
1097 QString selectValue;
1098 for (
int i = numSelectLines - 1; i >= 0; --i) {
1099 QString selectBit = otherPortName(elm->
inputPort(startIndex + i));
1100 if (selectBit ==
"HIGH") selectBit =
"1";
1101 else if (selectBit ==
"LOW") selectBit =
"0";
1102 if (i == numSelectLines - 1) {
1103 selectValue = selectBit;
1105 selectValue = selectBit +
" + (" + selectValue +
" << 1)";
1113 bool negate =
false;
1114 bool parentheses =
true;
1115 QString logicOperator;
1117 case ElementType::And: {
1118 logicOperator =
"&&";
1121 case ElementType::Or: {
1122 logicOperator =
"||";
1125 case ElementType::Nand: {
1126 logicOperator =
"&&";
1130 case ElementType::Nor: {
1131 logicOperator =
"||";
1135 case ElementType::Xor: {
1136 logicOperator =
"^";
1139 case ElementType::Xnor: {
1140 logicOperator =
"^";
1144 case ElementType::Not: {
1146 parentheses =
false;
1149 case ElementType::Node: {
1150 if (elm->
outputs().size() == 1 && elm->
inputs().size() == 1) {
1153 if (outputPort && inputPort) {
1154 QString varName = m_varMap.value(outputPort);
1155 QString inputValue = otherPortName(inputPort);
1159 if (inputValue.isEmpty() && m_currentIC) {
1160 for (
int i = 0; i < m_currentIC->internalInputs().size(); ++i) {
1161 if (m_currentIC->internalInputs().at(i) == inputPort) {
1162 inputValue = m_varMap.value(inputPort);
1167 m_stream <<
" " << varName <<
" = " << inputValue <<
";" << Qt::endl;
1178 case ElementType::AudioBox:
1179 case ElementType::Buzzer:
1180 case ElementType::Clock:
1181 case ElementType::DFlipFlop:
1182 case ElementType::DLatch:
1183 case ElementType::Demux:
1184 case ElementType::Display14:
1185 case ElementType::Display16:
1186 case ElementType::Display7:
1187 case ElementType::IC:
1188 case ElementType::InputButton:
1189 case ElementType::InputGnd:
1190 case ElementType::InputRotary:
1191 case ElementType::InputSwitch:
1192 case ElementType::InputVcc:
1193 case ElementType::JKFlipFlop:
1194 case ElementType::JKLatch:
1195 case ElementType::Led:
1196 case ElementType::Line:
1197 case ElementType::Mux:
1198 case ElementType::SRFlipFlop:
1199 case ElementType::SRLatch:
1200 case ElementType::TFlipFlop:
1201 case ElementType::Text:
1202 case ElementType::TruthTable:
1203 case ElementType::Unknown:
1206 if (elm->
outputs().size() == 1) {
1208 if (!outputPort)
return;
1209 QString varName = m_varMap.value(outputPort);
1211 if (!inPort)
return;
1215 innerExpr = otherPortName(inPort);
1216 for (
int i = 1; i < elm->
inputs().size(); ++i) {
1217 inPort = elm->
inputs().at(i);
1218 innerExpr +=
" " + logicOperator +
" ";
1219 innerExpr += otherPortName(inPort);
1222 QString finalExpr = innerExpr;
1225 finalExpr =
"!(" + innerExpr +
")";
1227 finalExpr =
"!" + innerExpr;
1231 m_stream <<
" " << varName <<
" = " << finalExpr <<
";" << Qt::endl;
1235 qWarning() <<
"assignLogicOperator: element" << elm->objectName() <<
"has" << elm->
outputs().size() <<
"outputs (expected 1) — skipping";
1239void ArduinoCodeGen::emitComputeLogicFunction()
1241 m_stream <<
"void computeLogic() {" << Qt::endl;
1242 m_stream <<
" // Assigning aux variables. //" << Qt::endl;
1243 assignVariablesRec(m_elements);
1244 m_stream <<
"}" << Qt::endl
1248void ArduinoCodeGen::loop()
1250 m_stream <<
"void loop() {" << Qt::endl;
1251 m_stream <<
" // Reading input data //." << Qt::endl;
1252 for (
const auto &pin : std::as_const(m_inputMap)) {
1253 m_stream << QString(
" %1_val = digitalRead(%1);").arg(pin.m_varName) << Qt::endl;
1255 m_stream << Qt::endl;
1256 m_stream <<
" // Updating clocks. //" << Qt::endl;
1257 for (
auto *elm : m_elements) {
1259 const auto elmOutputs = elm->
outputs();
1260 if (elmOutputs.isEmpty()) {
1263 QString varName = m_varMap.value(elmOutputs.constFirst());
1264 m_stream << QString(
" if (%1_elapsed > %1_interval) {").arg(varName) << Qt::endl;
1265 m_stream << QString(
" %1_elapsed = 0;").arg(varName) << Qt::endl;
1266 m_stream << QString(
" %1 = ! %1;").arg(varName) << Qt::endl;
1267 m_stream << QString(
" }") << Qt::endl;
1270 m_stream << Qt::endl;
1272 m_stream << Qt::endl;
1273 m_stream <<
" // Writing output data. //" << Qt::endl;
1274 for (
const auto &pin : std::as_const(m_outputMap)) {
1275 if (pin.m_elm->elementType() == ElementType::Buzzer) {
1276 auto *buzzer = qobject_cast<Buzzer *>(pin.m_elm);
1277 if (!buzzer)
continue;
1278 const QString inputSignal = otherPortName(buzzer->inputPort(0));
1279 const int frequency =
static_cast<int>(buzzer->frequency());
1280 m_stream << QString(
" if (%1) {").arg(inputSignal) << Qt::endl;
1281 m_stream << QString(
" tone(%1, %2);").arg(pin.m_varName).arg(frequency) << Qt::endl;
1282 m_stream << QString(
" } else {") << Qt::endl;
1283 m_stream << QString(
" noTone(%1);").arg(pin.m_varName) << Qt::endl;
1284 m_stream << QString(
" }") << Qt::endl;
1287 QString varName = otherPortName(pin.m_port);
1288 if (varName.isEmpty()) {
1292 varName = highLow(pin.m_port->defaultValue());
1294 m_stream << QString(
" digitalWrite(%1, %2);").arg(pin.m_varName, varName) << Qt::endl;
1296 m_stream <<
"}" << Qt::endl;
1301 QFile tbFile(tbFileName);
1302 if (!tbFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1303 throw PANDACEPTION(
"Could not open testbench file: %1", tbFileName);
1306 QIODevice *savedDevice = m_stream.device();
1307 m_stream.setDevice(&tbFile);
1308 m_hasSequential = hasNativeMemory(m_elements);
1311 m_stream <<
"// ============================================================ //" << Qt::endl;
1312 m_stream <<
"// ====== Testbench generated automatically by wiRedPanda ====== //" << Qt::endl;
1313 m_stream <<
"// ============================================================ //" << Qt::endl;
1314 m_stream << Qt::endl;
1315 m_stream <<
"#include <avr/sleep.h>" << Qt::endl;
1316 m_stream <<
"#include <elapsedMillis.h>" << Qt::endl;
1317 m_stream << Qt::endl;
1320 m_stream <<
"/* ========= Inputs ========== */" << Qt::endl;
1321 for (
const auto &pin : std::as_const(m_inputMap)) {
1322 m_stream <<
"const int " << pin.m_varName <<
" = 0;" << Qt::endl;
1324 m_stream << Qt::endl;
1327 m_stream <<
"/* ========= Outputs ========== */" << Qt::endl;
1328 for (
const auto &pin : std::as_const(m_outputMap)) {
1329 m_stream <<
"const int " << pin.m_varName <<
" = 0;" << Qt::endl;
1331 m_stream << Qt::endl;
1334 m_stream <<
"/* ====== Aux. Variables ====== */" << Qt::endl;
1335 for (
const auto &varName : std::as_const(m_declaredVariables)) {
1336 m_stream <<
"bool " << varName <<
" = LOW;" << Qt::endl;
1341 declareSequentialStateRec(m_elements,
true);
1342 if (m_hasSequential) {
1343 m_stream <<
"bool g_sample = true;" << Qt::endl;
1345 m_stream << Qt::endl;
1348 emitComputeLogicFunction();
1349 if (m_hasSequential) {
1350 emitCommitFlipFlops();
1354 QStringList outputVarNames;
1355 for (
const auto &pin : std::as_const(m_outputMap)) {
1356 QString varName = otherPortName(pin.m_port);
1357 if (varName.isEmpty()) {
1358 varName = highLow(pin.m_port->defaultValue());
1360 outputVarNames.append(varName);
1364 const int numInputs =
static_cast<int>(m_inputMap.size());
1365 const int numOutputs =
static_cast<int>(m_outputMap.size());
1366 const int numTests =
static_cast<int>(vectors.size());
1368 m_stream <<
"/* ====== Test Vectors ====== */" << Qt::endl;
1369 m_stream <<
"struct TestVec {" << Qt::endl;
1370 m_stream <<
" bool in[" << (std::max)(1, numInputs) <<
"];" << Qt::endl;
1371 m_stream <<
" bool out[" << (std::max)(1, numOutputs) <<
"];" << Qt::endl;
1372 m_stream <<
"};" << Qt::endl;
1373 m_stream << Qt::endl;
1374 m_stream <<
"const int NUM_TESTS = " << numTests <<
";" << Qt::endl;
1375 m_stream <<
"const TestVec VECTORS[" << (std::max)(1, numTests) <<
"] = {" << Qt::endl;
1376 for (
const auto &v : vectors) {
1378 for (
int j = 0; j < v.inputs.size(); ++j) {
1379 if (j > 0) m_stream <<
", ";
1380 m_stream << (v.inputs[j] ?
"HIGH" :
"LOW");
1383 for (
int k = 0; k < v.outputs.size(); ++k) {
1384 if (k > 0) m_stream <<
", ";
1385 m_stream << (v.outputs[k] ?
"HIGH" :
"LOW");
1387 m_stream <<
"}}," << Qt::endl;
1389 m_stream <<
"};" << Qt::endl;
1390 m_stream << Qt::endl;
1393 m_stream <<
"void setup() {" << Qt::endl;
1394 m_stream <<
" Serial.begin(9600);" << Qt::endl;
1395 m_stream <<
" bool allPassed = true;" << Qt::endl;
1396 m_stream <<
" for (int t = 0; t < NUM_TESTS; t++) {" << Qt::endl;
1397 for (
int j = 0; j < numInputs; ++j) {
1398 m_stream <<
" " << m_inputMap.at(j).m_varName <<
"_val = VECTORS[t].in[" << j <<
"];" << Qt::endl;
1405 if (m_hasSequential) {
1406 m_stream <<
" g_sample = true;" << Qt::endl;
1408 <<
"; s++) { computeLogic(); }" << Qt::endl;
1409 m_stream <<
" commitFlipFlops();" << Qt::endl;
1410 m_stream <<
" g_sample = false;" << Qt::endl;
1412 <<
"; s++) { computeLogic(); }" << Qt::endl;
1415 <<
"; s++) { computeLogic(); }" << Qt::endl;
1417 m_stream <<
" bool pass = true;" << Qt::endl;
1418 for (
int k = 0; k < numOutputs; ++k) {
1419 m_stream <<
" pass = pass && (" << outputVarNames.at(k) <<
" == VECTORS[t].out[" << k <<
"]);" << Qt::endl;
1421 m_stream <<
" if (!pass) {" << Qt::endl;
1422 m_stream <<
" allPassed = false;" << Qt::endl;
1423 m_stream <<
" Serial.print(\"FAIL vector \");" << Qt::endl;
1424 m_stream <<
" Serial.println(t);" << Qt::endl;
1425 m_stream <<
" }" << Qt::endl;
1426 m_stream <<
" }" << Qt::endl;
1427 m_stream <<
" Serial.println(allPassed ? \"ALL PASS\" : \"SOME FAILED\");" << Qt::endl;
1428 m_stream <<
" Serial.flush();" << Qt::endl;
1429 m_stream <<
" set_sleep_mode(SLEEP_MODE_PWR_DOWN);" << Qt::endl;
1430 m_stream <<
" sleep_mode();" << Qt::endl;
1431 m_stream <<
"}" << Qt::endl;
1432 m_stream << Qt::endl;
1434 m_stream <<
"void loop() {}" << Qt::endl;
1436 m_stream.setDevice(savedDevice);
1441 m_stream.setDevice(savedDevice);
Arduino sketch code generator: translates a circuit into an uploadable .ino file.
Graphic element for the buzzer tone output.
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.
Synchronous cycle-based simulation engine with event-driven clock support.
Graphic element for a user-programmable truth table.
void generate()
Generates the Arduino sketch and writes it to the output file.
void generateTestbench(const QString &tbFileName, const QVector< TestVector > &vectors)
ArduinoCodeGen(const QString &fileName, const QVector< GraphicElement * > &elements)
Constructs the code generator targeting fileName.
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.
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< OutputPort * > & outputs() const
Returns a const reference to the vector of all output ports.
const QVector< InputPort * > & inputs() const
Returns a const reference to the vector of all input ports.
Abstract base class for circuit element ports (connection endpoints).
GraphicElement * graphicElement()
Returns the graphic element that owns this port.
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.
static QVector< GraphicElement * > sortByTopology(QVector< GraphicElement * > elements)
Returns elements sorted in topological dependency order (inputs first).
static constexpr int kMaxSettleIterations
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.
Describes an Arduino board's available GPIO pins.