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)
86 if (visited.contains(port)) {
87 const QString mapped = m_varMap.value(port);
88 return mapped.isEmpty() ?
"LOW" : mapped;
93 const QString mapped = m_varMap.value(port);
94 if (!mapped.isEmpty()) {
99 if (elm && elm->wirelessMode() == WirelessMode::Rx && !elm->label().isEmpty()) {
100 auto *txInputPort = m_txInputPorts.value(elm->label(),
nullptr);
102 return otherPortNameImpl(txInputPort, visited);
108 auto *connection = port->
connections().constFirst();
113 auto *otherPort = connection->otherPort(port);
119 if (visited.contains(otherPort)) {
120 const QString mapped = m_varMap.value(otherPort);
121 return mapped.isEmpty() ?
"LOW" : mapped;
125 visited.insert(port);
126 visited.insert(otherPort);
128 const QString result = m_varMap.value(otherPort);
129 if (result.isEmpty()) {
130 return highLow(otherPort->defaultValue());
139 m_hasSequential = hasNativeMemory(m_elements);
141 int requiredInputPins = 0;
142 int requiredOutputPins = 0;
143 for (
auto *elm : m_elements) {
144 const auto type = elm->elementType();
145 if (type == ElementType::InputButton || type == ElementType::InputSwitch) {
147 }
else if (type == ElementType::InputRotary) {
148 requiredInputPins += elm->outputSize();
151 for (
auto *elm : m_elements) {
152 if (elm->elementGroup() == ElementGroup::Output) {
153 requiredOutputPins +=
static_cast<int>(elm->inputs().size());
156 const int totalRequiredPins = requiredInputPins + requiredOutputPins;
157 m_totalRequiredPins = totalRequiredPins;
158 m_selectedBoard = selectBoard(totalRequiredPins);
159 m_availablePins = m_selectedBoard.availablePins;
161 m_stream <<
"// ==================================================================== //" << Qt::endl;
162 m_stream <<
"// ======= This code was generated automatically by wiRedPanda ======== //" << Qt::endl;
163 m_stream <<
"// ==================================================================== //" << Qt::endl;
164 m_stream <<
"//" << Qt::endl;
165 m_stream << QString(
"// Target Board: %1").arg(m_selectedBoard.name) << Qt::endl;
166 m_stream << QString(
"// Pin Usage: %1/%2 pins").arg(totalRequiredPins).arg(m_selectedBoard.maxPins()) << Qt::endl;
167 m_stream <<
"//" << Qt::endl;
170 m_stream <<
"#include <elapsedMillis.h>" << Qt::endl;
173 declareAuxVariables();
174 if (m_hasSequential) {
177 m_stream <<
"bool g_sample = true;" << Qt::endl << Qt::endl;
180 emitComputeLogicFunction();
181 if (m_hasSequential) {
182 emitCommitFlipFlops();
192void ArduinoCodeGen::declareInputs()
195 m_stream <<
"/* ========= Inputs ========== */" << Qt::endl;
197 for (
auto *elm : m_elements) {
198 const auto type = elm->elementType();
200 if ((type == ElementType::InputButton) || (type == ElementType::InputSwitch)) {
202 if (m_availablePins.isEmpty()) {
206 QString varName = elm->objectName() + QString::number(counter);
207 const QString label = elm->label();
209 if (!label.isEmpty()) {
210 varName +=
"_" + label;
213 varName = removeForbiddenChars(varName);
214 if (isArduinoReserved(varName)) {
217 m_stream << QString(
"const int %1 = %2;").arg(varName, m_availablePins.constFirst()) << Qt::endl;
218 auto *outPort = elm->outputPort(0);
220 m_inputMap.append(MappedPin(elm, m_availablePins.constFirst(), varName, outPort, 0));
221 m_varMap[outPort] = varName + QString(
"_val");
223 m_availablePins.removeFirst();
225 }
else if (type == ElementType::InputRotary) {
227 const QString label = elm->label();
228 for (
int i = 0; i < elm->outputSize(); ++i) {
229 if (m_availablePins.isEmpty()) {
232 QString varName = elm->objectName() + QString::number(counter) +
"_pos" + QString::number(i);
233 if (!label.isEmpty()) {
234 varName = elm->objectName() + QString::number(counter) +
"_" + label +
"_pos" + QString::number(i);
236 varName = removeForbiddenChars(varName);
237 if (isArduinoReserved(varName)) {
240 m_stream << QString(
"const int %1 = %2;").arg(varName, m_availablePins.constFirst()) << Qt::endl;
241 auto *outPort = elm->outputPort(i);
243 m_inputMap.append(MappedPin(elm, m_availablePins.constFirst(), varName, outPort, i));
244 m_varMap[outPort] = varName +
"_val";
246 m_availablePins.removeFirst();
252 m_stream << Qt::endl;
255void ArduinoCodeGen::declareOutputs()
258 m_stream <<
"/* ========= Outputs ========== */" << Qt::endl;
259 for (
auto *elm : m_elements) {
260 if (elm->elementGroup() == ElementGroup::Output) {
261 QString label = elm->label();
262 for (
int i = 0; i < elm->inputs().size(); ++i) {
264 if (m_availablePins.isEmpty()) {
268 QString varName = elm->objectName() + QString::number(counter);
269 if (!label.isEmpty()) {
270 varName = QString(
"%1_%2").arg(varName, label);
272 Port *port = elm->inputPort(i);
273 if (port && !port->
name().isEmpty()) {
274 varName = QString(
"%1_%2").arg(varName, port->
name());
276 varName = removeForbiddenChars(varName);
277 if (isArduinoReserved(varName)) {
280 m_stream << QString(
"const int %1 = %2;").arg(varName, m_availablePins.constFirst()) << Qt::endl;
281 m_outputMap.append(MappedPin(elm, m_availablePins.constFirst(), varName, port, i));
282 m_availablePins.removeFirst();
287 m_stream << Qt::endl;
290void ArduinoCodeGen::declareAuxVariablesRec(
const QVector<GraphicElement *> &elements,
const bool isBox,
const QString &icPrefix)
294 for (
auto *elm : elements) {
295 if (elm->elementType() == ElementType::IC) {
296 auto *ic = qobject_cast<IC *>(elm);
304 QString baseVarName = QString(
"aux_%1%2_%3").arg(icPrefix, removeForbiddenChars(ic->label()), QString::number(counter++));
306 for (
int i = 0; i < ic->outputSize(); ++i) {
307 Port *externalPort = ic->outputPort(i);
309 QString portVarName = baseVarName;
310 const QString portName = externalPort->
name();
311 if (!portName.isEmpty()) {
312 portVarName +=
"_" + removeForbiddenChars(portName);
314 portVarName +=
"_" + QString::number(i);
317 portVarName +=
"_out" + QString::number(i);
319 m_varMap[externalPort] = portVarName;
320 if (!m_declaredVariables.contains(portVarName)) {
321 m_stream <<
"bool " << portVarName <<
" = " << highLow(externalPort->
defaultValue()) <<
";" << Qt::endl;
322 m_declaredVariables.append(portVarName);
327 if (!ic->internalElements().isEmpty()) {
328 const QString nestedPrefix = QString(
"%1%2_%3_").arg(icPrefix, removeForbiddenChars(ic->label()), QString::number(counter - 1));
329 declareAuxVariablesRec(ic->internalElements(),
true, nestedPrefix);
331 for (
int i = 0; i < ic->internalInputs().size(); ++i) {
332 Port *internalPort = ic->internalInputs().at(i);
333 if (m_varMap.value(internalPort).isEmpty()) {
334 const QString portVarName = QString(
"aux_ic_input_%1%2_%3_%4").arg(icPrefix, removeForbiddenChars(ic->label()), QString::number(counter - 1), QString::number(i));
335 m_varMap[internalPort] = portVarName;
336 if (!m_declaredVariables.contains(portVarName)) {
337 m_stream <<
"bool " << portVarName <<
" = LOW;" << Qt::endl;
338 m_declaredVariables.append(portVarName);
348 const auto outputs = elm->outputs();
351 if (!icPrefix.isEmpty()) {
352 baseVarName = QString(
"aux_%1%2_%3").arg(icPrefix, removeForbiddenChars(elm->objectName()), QString::number(counter++));
354 baseVarName = QString(
"aux_%1_%2").arg(removeForbiddenChars(elm->objectName()), QString::number(counter++));
357 if (outputs.size() == 1) {
358 Port *port = outputs.constFirst();
360 if (elm->elementType() == ElementType::InputVcc) {
361 m_varMap[port] =
"HIGH";
365 if (elm->elementType() == ElementType::InputGnd) {
366 m_varMap[port] =
"LOW";
370 if (m_varMap.value(port).isEmpty()) {
371 m_varMap[port] = baseVarName;
375 for (
auto *port : outputs) {
376 QString portName = baseVarName + QString(
"_%1").arg(portCounter++);
377 if (!port->
name().isEmpty()) {
378 portName +=
"_" + removeForbiddenChars(port->
name());
380 m_varMap[port] = portName;
384 for (
auto *port : outputs) {
385 const QString varName2 = m_varMap.value(port);
386 if (!m_declaredVariables.contains(varName2)) {
387 m_stream <<
"bool " << varName2 <<
" = " << highLow(port->
defaultValue()) <<
";" << Qt::endl;
388 m_declaredVariables.append(varName2);
392 if (elm->elementGroup() == ElementGroup::Memory) {
393 m_stream <<
"bool " << varName2 <<
"_next = " << highLow(port->
defaultValue()) <<
";" << Qt::endl;
396 switch (elm->elementType()) {
397 case ElementType::Clock: {
399 auto *clk = qobject_cast<Clock *>(elm);
403 m_stream <<
"elapsedMillis " << varName2 <<
"_elapsed = 0;" << Qt::endl;
404 m_stream <<
"int " << varName2 <<
"_interval = " << (std::max)(1,
static_cast<int>(1000.0 / clk->frequency())) <<
";" << Qt::endl;
408 case ElementType::DFlipFlop: {
409 m_stream <<
"bool " << varName2 <<
"_inclk = LOW;" << Qt::endl;
410 m_stream <<
"bool " << varName2 <<
"_last = LOW;" << Qt::endl;
413 case ElementType::TFlipFlop:
414 case ElementType::SRFlipFlop:
415 case ElementType::JKFlipFlop: {
416 m_stream <<
"bool " << varName2 <<
"_inclk = LOW;" << Qt::endl;
419 case ElementType::And:
420 case ElementType::AudioBox:
421 case ElementType::Buzzer:
422 case ElementType::DLatch:
423 case ElementType::Demux:
424 case ElementType::Display14:
425 case ElementType::Display16:
426 case ElementType::Display7:
427 case ElementType::IC:
428 case ElementType::InputButton:
429 case ElementType::InputGnd:
430 case ElementType::InputRotary:
431 case ElementType::InputSwitch:
432 case ElementType::InputVcc:
433 case ElementType::JKLatch:
434 case ElementType::Led:
435 case ElementType::Line:
436 case ElementType::Mux:
437 case ElementType::Nand:
438 case ElementType::Node:
439 case ElementType::Nor:
440 case ElementType::Not:
441 case ElementType::Or:
442 case ElementType::SRLatch:
443 case ElementType::Text:
444 case ElementType::TruthTable:
445 case ElementType::Unknown:
446 case ElementType::Xnor:
447 case ElementType::Xor:
454void ArduinoCodeGen::declareSequentialStateRec(
const QVector<GraphicElement *> &elements,
const bool topLevel)
456 for (
auto *elm : elements) {
457 if (elm->elementType() == ElementType::IC) {
458 if (
auto *ic = qobject_cast<IC *>(elm)) {
459 declareSequentialStateRec(ic->internalElements(),
false);
464 const auto outputs = elm->outputs();
465 if (outputs.isEmpty()) {
469 if (elm->elementGroup() == ElementGroup::Memory) {
470 for (
auto *port : outputs) {
471 const QString v = m_varMap.value(port);
473 m_stream <<
"bool " << v <<
"_next = " << highLow(port->
defaultValue()) <<
";" << Qt::endl;
478 const QString varName = m_varMap.value(outputs.constFirst());
479 if (varName.isEmpty()) {
483 switch (elm->elementType()) {
484 case ElementType::Clock:
487 m_stream <<
"elapsedMillis " << varName <<
"_elapsed = 0;" << Qt::endl;
488 m_stream <<
"int " << varName <<
"_interval = 1000;" << Qt::endl;
491 case ElementType::DFlipFlop:
492 m_stream <<
"bool " << varName <<
"_inclk = LOW;" << Qt::endl;
493 m_stream <<
"bool " << varName <<
"_last = LOW;" << Qt::endl;
495 case ElementType::TFlipFlop:
496 case ElementType::SRFlipFlop:
497 case ElementType::JKFlipFlop:
498 m_stream <<
"bool " << varName <<
"_inclk = LOW;" << Qt::endl;
500 case ElementType::And:
501 case ElementType::AudioBox:
502 case ElementType::Buzzer:
503 case ElementType::DLatch:
504 case ElementType::Demux:
505 case ElementType::Display14:
506 case ElementType::Display16:
507 case ElementType::Display7:
508 case ElementType::IC:
509 case ElementType::InputButton:
510 case ElementType::InputGnd:
511 case ElementType::InputRotary:
512 case ElementType::InputSwitch:
513 case ElementType::InputVcc:
514 case ElementType::JKLatch:
515 case ElementType::Led:
516 case ElementType::Line:
517 case ElementType::Mux:
518 case ElementType::Nand:
519 case ElementType::Node:
520 case ElementType::Nor:
521 case ElementType::Not:
522 case ElementType::Or:
523 case ElementType::SRLatch:
524 case ElementType::Text:
525 case ElementType::TruthTable:
526 case ElementType::Unknown:
527 case ElementType::Xnor:
528 case ElementType::Xor:
534bool ArduinoCodeGen::hasNativeMemory(
const QVector<GraphicElement *> &elements)
536 for (
auto *elm : elements) {
537 if (elm->elementGroup() == ElementGroup::Memory) {
540 if (elm->elementType() == ElementType::IC) {
541 if (
auto *ic = qobject_cast<IC *>(elm)) {
542 if (hasNativeMemory(ic->internalElements())) {
551void ArduinoCodeGen::emitCommitFlipFlopsRec(
const QVector<GraphicElement *> &elements)
553 for (
auto *elm : elements) {
554 if (elm->elementType() == ElementType::IC) {
555 if (
auto *ic = qobject_cast<IC *>(elm)) {
556 emitCommitFlipFlopsRec(ic->internalElements());
560 if (elm->elementGroup() != ElementGroup::Memory) {
563 for (
auto *port : elm->outputs()) {
564 const QString varName = m_varMap.value(port);
565 if (!varName.isEmpty()) {
566 m_stream <<
" " << varName <<
" = " << varName <<
"_next;" << Qt::endl;
572void ArduinoCodeGen::emitCommitFlipFlops()
574 m_stream <<
"void commitFlipFlops() {" << Qt::endl;
575 emitCommitFlipFlopsRec(m_elements);
576 m_stream <<
"}" << Qt::endl << Qt::endl;
579void ArduinoCodeGen::emitTickDriver()
585 if (m_hasSequential) {
586 m_stream <<
" g_sample = true;" << Qt::endl;
588 m_stream <<
" commitFlipFlops();" << Qt::endl;
589 m_stream <<
" g_sample = false;" << Qt::endl;
592 m_stream <<
" computeLogic();" << Qt::endl;
596void ArduinoCodeGen::declareAuxVariables()
598 m_stream <<
"/* ====== Aux. Variables ====== */" << Qt::endl;
600 for (
const auto &pin : std::as_const(m_inputMap)) {
601 const QString valVarName = pin.m_varName +
"_val";
602 if (!m_declaredVariables.contains(valVarName)) {
603 m_stream <<
"bool " << valVarName <<
" = LOW;" << Qt::endl;
604 m_declaredVariables.append(valVarName);
608 declareAuxVariablesRec(m_elements,
false, {});
609 m_stream << Qt::endl;
612void ArduinoCodeGen::emitFlipFlopBlock(
GraphicElement *elm,
const QString &typeName,
const QString &firstOut,
613 const QString &secondOut,
int clockInputIndex,
int presetInputIndex,
614 int clearInputIndex,
const std::function<
void()> &edgeLogic,
615 const std::function<
void()> &stateEpilogue)
617 QString clk = otherPortName(elm->
inputPort(clockInputIndex));
618 QString inclk = firstOut +
"_inclk";
619 QString firstOutNext = firstOut +
"_next";
620 QString secondOutNext = secondOut +
"_next";
622 m_stream << QString(
" //%1 FlipFlop").arg(typeName) << Qt::endl;
627 m_stream << QString(
" if (g_sample) {") << Qt::endl;
628 m_stream << QString(
" if (%1 && !%2) { ").arg(clk, inclk) << Qt::endl;
633 m_stream << QString(
" }") << Qt::endl;
636 QString prst = otherPortName(elm->
inputPort(presetInputIndex));
637 QString clr = otherPortName(elm->
inputPort(clearInputIndex));
638 m_stream << QString(
" if (!%1 || !%2) { ").arg(prst, clr) << Qt::endl;
639 m_stream << QString(
" %1 = !%2; //Preset").arg(firstOutNext, prst) << Qt::endl;
640 m_stream << QString(
" %1 = !%2; //Clear").arg(secondOutNext, clr) << Qt::endl;
641 m_stream << QString(
" }") << Qt::endl;
644 m_stream <<
" " << inclk <<
" = " << clk <<
";" << Qt::endl;
651 m_stream << QString(
" }") << Qt::endl;
652 m_stream << QString(
" //End of %1 FlipFlop").arg(typeName) << Qt::endl;
655void ArduinoCodeGen::setup()
657 m_stream <<
"void setup() {" << Qt::endl;
658 for (
const auto &pin : std::as_const(m_inputMap)) {
659 m_stream <<
" pinMode(" << pin.m_varName <<
", INPUT);" << Qt::endl;
661 for (
const auto &pin : std::as_const(m_outputMap)) {
662 m_stream <<
" pinMode(" << pin.m_varName <<
", OUTPUT);" << Qt::endl;
664 m_stream <<
"}" << Qt::endl
668void ArduinoCodeGen::assignVariablesRec(
const QVector<GraphicElement *> &elements)
670 for (
auto *elm : elements) {
672 auto *ic = qobject_cast<IC *>(elm);
677 for (
int i = 0; i < ic->inputSize(); ++i) {
678 Port *externalPort = ic->inputPort(i);
679 Port *internalPort = ic->internalInputs().at(i);
680 const QString externalValue = otherPortName(externalPort);
681 const QString internalVar = m_varMap.value(internalPort);
682 m_stream <<
" " << internalVar <<
" = " << externalValue <<
";" << Qt::endl;
685 if (!ic->internalElements().isEmpty()) {
686 IC *previousIC = m_currentIC;
695 QSet<GraphicElement *> boundaryInputs;
696 for (
auto *port : ic->internalInputs()) {
698 boundaryInputs.insert(boundaryElement);
702 std::stable_partition(sortedInternal.begin(), sortedInternal.end(),
703 [&boundaryInputs](GraphicElement *e) { return boundaryInputs.contains(e); });
704 assignVariablesRec(sortedInternal);
705 m_currentIC = previousIC;
708 for (
int i = 0; i < ic->outputSize(); ++i) {
709 Port *externalPort = ic->outputPort(i);
710 Port *internalPort = ic->internalOutputs().at(i);
711 const QString internalValue = m_varMap.value(internalPort);
712 const QString externalVar = m_varMap.value(externalPort);
713 m_stream <<
" " << externalVar <<
" = " << internalValue <<
";" << Qt::endl;
728 QString firstOut = m_varMap.value(outputPort0);
730 case ElementType::DFlipFlop: emitDFlipFlop(elm, firstOut);
break;
731 case ElementType::DLatch: emitDLatch(elm, firstOut);
break;
732 case ElementType::JKFlipFlop: emitJKFlipFlop(elm, firstOut);
break;
733 case ElementType::SRFlipFlop: emitSRFlipFlop(elm, firstOut);
break;
734 case ElementType::TFlipFlop: emitTFlipFlop(elm, firstOut);
break;
735 case ElementType::SRLatch: emitSRLatch(elm, firstOut);
break;
736 case ElementType::Mux: emitMux(elm);
break;
737 case ElementType::Demux: emitDemux(elm);
break;
738 case ElementType::TruthTable: emitTruthTable(elm);
break;
739 case ElementType::And:
740 case ElementType::Or:
741 case ElementType::Nand:
742 case ElementType::Nor:
743 case ElementType::Xor:
744 case ElementType::Xnor:
745 case ElementType::Not:
746 case ElementType::Node: assignLogicOperator(elm);
break;
747 case ElementType::AudioBox:
748 case ElementType::Buzzer:
749 case ElementType::Clock:
750 case ElementType::Display14:
751 case ElementType::Display16:
752 case ElementType::Display7:
753 case ElementType::IC:
754 case ElementType::InputButton:
755 case ElementType::InputGnd:
756 case ElementType::InputRotary:
757 case ElementType::InputSwitch:
758 case ElementType::InputVcc:
759 case ElementType::JKLatch:
760 case ElementType::Led:
761 case ElementType::Line:
762 case ElementType::Text:
763 case ElementType::Unknown:
764 throw PANDACEPTION(
"Element type not supported: %1", elm->objectName());
769void ArduinoCodeGen::emitDFlipFlop(
GraphicElement *elm,
const QString &firstOut)
772 if (!outputPort1)
return;
773 QString secondOut = m_varMap.value(outputPort1);
774 QString firstOutNext = firstOut +
"_next";
775 QString secondOutNext = secondOut +
"_next";
776 QString data = otherPortName(elm->
inputPort(0));
777 QString last = firstOut +
"_last";
778 emitFlipFlopBlock(elm,
"D", firstOut, secondOut, 1, 2, 3,
779 [
this, &last, &firstOutNext, &secondOutNext]() {
780 m_stream << QString(
" %1 = %2;").arg(firstOutNext, last) << Qt::endl;
781 m_stream << QString(
" %1 = !%2;").arg(secondOutNext, last) << Qt::endl;
783 [
this, &last, &data]() {
784 m_stream <<
" " << last <<
" = " << data <<
";" << Qt::endl;
788void ArduinoCodeGen::emitDLatch(
GraphicElement *elm,
const QString &firstOut)
791 if (!outputPort1)
return;
792 QString secondOut = m_varMap.value(outputPort1);
793 QString firstOutNext = firstOut +
"_next";
794 QString secondOutNext = secondOut +
"_next";
795 QString data = otherPortName(elm->
inputPort(0));
796 QString clk = otherPortName(elm->
inputPort(1));
797 m_stream << QString(
" //D Latch") << Qt::endl;
798 m_stream << QString(
" if (g_sample) {") << Qt::endl;
799 m_stream << QString(
" if (%1) { ").arg(clk) << Qt::endl;
800 m_stream << QString(
" %1 = %2;").arg(firstOutNext, data) << Qt::endl;
801 m_stream << QString(
" %1 = !%2;").arg(secondOutNext, data) << Qt::endl;
802 m_stream << QString(
" }") << Qt::endl;
803 m_stream << QString(
" }") << Qt::endl;
804 m_stream << QString(
" //End of D Latch") << Qt::endl;
807void ArduinoCodeGen::emitJKFlipFlop(
GraphicElement *elm,
const QString &firstOut)
810 if (!outputPort1)
return;
811 QString secondOut = m_varMap.value(outputPort1);
812 QString firstOutNext = firstOut +
"_next";
813 QString secondOutNext = secondOut +
"_next";
814 QString j = otherPortName(elm->
inputPort(0));
815 QString k = otherPortName(elm->
inputPort(2));
816 emitFlipFlopBlock(elm,
"JK", firstOut, secondOut, 1, 3, 4,
817 [
this, &j, &k, &firstOut, &secondOut, &firstOutNext, &secondOutNext]() {
818 m_stream << QString(
" if (%1 && %2) { ").arg(j, k) << Qt::endl;
819 m_stream << QString(
" bool aux = %1;").arg(firstOut) << Qt::endl;
820 m_stream << QString(
" %1 = %2;").arg(firstOutNext, secondOut) << Qt::endl;
821 m_stream << QString(
" %1 = aux;").arg(secondOutNext) << Qt::endl;
822 m_stream << QString(
" } else if (%1) {").arg(j) << Qt::endl;
823 m_stream << QString(
" %1 = 1;").arg(firstOutNext) << Qt::endl;
824 m_stream << QString(
" %1 = 0;").arg(secondOutNext) << Qt::endl;
825 m_stream << QString(
" } else if (%1) {").arg(k) << Qt::endl;
826 m_stream << QString(
" %1 = 0;").arg(firstOutNext) << Qt::endl;
827 m_stream << QString(
" %1 = 1;").arg(secondOutNext) << Qt::endl;
828 m_stream << QString(
" }") << Qt::endl;
832void ArduinoCodeGen::emitSRFlipFlop(
GraphicElement *elm,
const QString &firstOut)
835 if (!outputPort1)
return;
836 QString secondOut = m_varMap.value(outputPort1);
837 QString firstOutNext = firstOut +
"_next";
838 QString secondOutNext = secondOut +
"_next";
839 QString s = otherPortName(elm->
inputPort(0));
840 QString r = otherPortName(elm->
inputPort(2));
841 emitFlipFlopBlock(elm,
"SR", firstOut, secondOut, 1, 3, 4,
842 [
this, &s, &r, &firstOutNext, &secondOutNext]() {
843 m_stream << QString(
" if (%1 && %2) { ").arg(s, r) << Qt::endl;
844 m_stream << QString(
" %1 = 1;").arg(firstOutNext) << Qt::endl;
845 m_stream << QString(
" %1 = 1;").arg(secondOutNext) << Qt::endl;
846 m_stream << QString(
" } else if (%1 != %2) {").arg(s, r) << Qt::endl;
847 m_stream << QString(
" %1 = %2;").arg(firstOutNext, s) << Qt::endl;
848 m_stream << QString(
" %1 = %2;").arg(secondOutNext, r) << Qt::endl;
849 m_stream << QString(
" }") << Qt::endl;
853void ArduinoCodeGen::emitTFlipFlop(
GraphicElement *elm,
const QString &firstOut)
856 if (!outputPort1)
return;
857 QString secondOut = m_varMap.value(outputPort1);
858 QString firstOutNext = firstOut +
"_next";
859 QString secondOutNext = secondOut +
"_next";
860 QString t = otherPortName(elm->
inputPort(0));
861 emitFlipFlopBlock(elm,
"T", firstOut, secondOut, 1, 2, 3,
862 [
this, &t, &firstOut, &firstOutNext, &secondOutNext]() {
864 m_stream << QString(
" if (%1) { ").arg(t) << Qt::endl;
865 m_stream << QString(
" %1 = !%2;").arg(firstOutNext, firstOut) << Qt::endl;
866 m_stream << QString(
" %1 = %2;").arg(secondOutNext, firstOut) << Qt::endl;
867 m_stream << QString(
" }") << Qt::endl;
871void ArduinoCodeGen::emitSRLatch(
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 s = otherPortName(elm->
inputPort(0));
879 QString r = otherPortName(elm->
inputPort(1));
880 m_stream << QString(
" //SR Latch") << Qt::endl;
881 m_stream << QString(
" if (g_sample) {") << Qt::endl;
882 m_stream << QString(
" if (%1 && %2) { ").arg(s, r) << Qt::endl;
883 m_stream << QString(
" %1 = LOW;").arg(firstOutNext) << Qt::endl;
884 m_stream << QString(
" %1 = LOW;").arg(secondOutNext) << Qt::endl;
885 m_stream << QString(
" } else if (%1) { ").arg(s) << Qt::endl;
886 m_stream << QString(
" %1 = HIGH;").arg(firstOutNext) << Qt::endl;
887 m_stream << QString(
" %1 = LOW;").arg(secondOutNext) << Qt::endl;
888 m_stream << QString(
" } else if (%1) { ").arg(r) << Qt::endl;
889 m_stream << QString(
" %1 = LOW;").arg(firstOutNext) << Qt::endl;
890 m_stream << QString(
" %1 = HIGH;").arg(secondOutNext) << Qt::endl;
891 m_stream << QString(
" }") << Qt::endl;
892 m_stream << QString(
" }") << Qt::endl;
893 m_stream << QString(
" //End of SR Latch") << Qt::endl;
898 const int totalInputs = elm->
inputSize();
899 int numSelectLines = 1;
900 while (numSelectLines < 16 && (1 << numSelectLines) + numSelectLines < totalInputs) {
903 const int numDataInputs = totalInputs - numSelectLines;
904 const QString output = m_varMap.value(elm->
outputPort(0));
905 const QString selectValue = buildSelectExpression(elm, numDataInputs, numSelectLines);
907 m_stream << QString(
" //Multiplexer") << Qt::endl;
908 for (
int i = 0; i < numDataInputs; ++i) {
910 m_stream << QString(
" if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
912 m_stream << QString(
" } else if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
914 m_stream << QString(
" %1 = %2;").arg(output, otherPortName(elm->
inputPort(i))) << Qt::endl;
916 m_stream << QString(
" } else {") << Qt::endl;
917 m_stream << QString(
" %1 = LOW;").arg(output) << Qt::endl;
918 m_stream << QString(
" }") << Qt::endl;
919 m_stream << QString(
" //End of Multiplexer") << Qt::endl;
925 int numSelectLines = 1;
926 while (numSelectLines < 16 && (1 << numSelectLines) < numOutputs) {
929 const QString dataInput = otherPortName(elm->
inputPort(0));
930 const QString selectValue = buildSelectExpression(elm, 1, numSelectLines);
932 m_stream << QString(
" //Demultiplexer") << Qt::endl;
933 for (
int i = 0; i < numOutputs; ++i) {
934 m_stream << QString(
" %1 = LOW;").arg(m_varMap.value(elm->
outputPort(i))) << Qt::endl;
936 for (
int i = 0; i < numOutputs; ++i) {
938 m_stream << QString(
" if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
940 m_stream << QString(
" } else if ((%1) == %2) {").arg(selectValue).arg(i) << Qt::endl;
942 m_stream << QString(
" %1 = %2;").arg(m_varMap.value(elm->
outputPort(i)), dataInput) << Qt::endl;
944 m_stream << QString(
" }") << Qt::endl;
945 m_stream << QString(
" //End of Demultiplexer") << Qt::endl;
950 auto *ttGraphic =
dynamic_cast<TruthTable *
>(elm);
951 if (!ttGraphic)
return;
953 const QBitArray propositions = ttGraphic->key();
955 const int rows = 1 << nInputs;
957 QStringList inputSignalNames;
958 for (
int i = 0; i < nInputs; ++i) {
959 QString signalName = otherPortName(elm->
inputPort(i));
960 if (signalName ==
"LOW") {
962 }
else if (signalName ==
"HIGH") {
964 }
else if (signalName.isEmpty()) {
965 m_stream <<
"// WARNING: Input " << i <<
" of TruthTable '" << elm->objectName() <<
"' appears disconnected. Assuming LOW." << Qt::endl;
968 inputSignalNames << signalName;
971 QString indexCalculation;
972 for (
int i = 0; i < nInputs; ++i) {
974 indexCalculation = inputSignalNames[i];
976 indexCalculation = inputSignalNames[i] +
" + (" + indexCalculation +
" << 1)";
983 for (
int out = 0; out < elm->
outputSize(); ++out) {
984 const QString outputVarName = m_varMap.value(elm->
outputPort(out));
985 if (outputVarName.isEmpty()) {
987 throw PANDACEPTION(
"Output variable not mapped for TruthTable: %1", elm->objectName());
989 m_stream <<
"// TruthTable '" << elm->objectName() <<
"' output " << out <<
" is disconnected — no code emitted." << Qt::endl;
993 m_stream << QString(
" //TruthTable") << Qt::endl;
994 for (
int i = 0; i < rows; ++i) {
996 m_stream << QString(
" if ((%1) == %2) {").arg(indexCalculation).arg(i) << Qt::endl;
998 m_stream << QString(
" } else if ((%1) == %2) {").arg(indexCalculation).arg(i) << Qt::endl;
1000 m_stream << QString(
" %1 = %2;").arg(outputVarName, propositions.testBit(256 * out + i) ?
"HIGH" :
"LOW") << Qt::endl;
1002 m_stream << QString(
" } else {") << Qt::endl;
1003 m_stream << QString(
" %1 = LOW;").arg(outputVarName) << Qt::endl;
1004 m_stream << QString(
" }") << Qt::endl;
1005 m_stream << QString(
" //End TruthTable") << Qt::endl;
1009QVector<ArduinoBoardConfig> ArduinoCodeGen::availableBoards()
const
1011 static const QVector<ArduinoBoardConfig> boards = []() {
1012 QVector<ArduinoBoardConfig> b;
1015 "Arduino UNO R3/R4",
1016 {
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13"},
1017 "Standard Arduino board with 20 I/O pins"
1022 {
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13"},
1023 "Compact Arduino board with 20 I/O pins"
1026 QStringList megaPins;
1027 for (
int i = 0; i <= 15; ++i) {
1028 megaPins.append(QString(
"A%1").arg(i));
1030 for (
int i = 2; i <= 53; ++i) {
1031 megaPins.append(QString::number(i));
1034 "Arduino Mega 2560",
1036 "High I/O count Arduino board with 70 I/O pins"
1041 {
"A0",
"A1",
"A2",
"A3",
"A4",
"A5",
"A6",
"A7",
"A8",
"A9",
"A10",
"A11",
"A12",
"A13",
"A14",
"A15",
"A16",
"A17",
1042 "2",
"4",
"5",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"21",
"22",
"23",
"25",
"26",
"27",
"32",
"33",
"34"},
1043 "WiFi/Bluetooth enabled board with 36 I/O pins"
1053 const auto boards = availableBoards();
1054 for (
const auto &board : boards) {
1055 if (board.maxPins() >= requiredPins) {
1061 return *std::max_element(boards.cbegin(), boards.cend(),
1062 [](
const ArduinoBoardConfig &a,
const ArduinoBoardConfig &b) { return a.maxPins() < b.maxPins(); });
1065void ArduinoCodeGen::throwPinOverflow()
const
1067 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.",
1068 QString::number(m_totalRequiredPins), m_selectedBoard.name, QString::number(m_selectedBoard.maxPins()));
1071QString ArduinoCodeGen::buildSelectExpression(
GraphicElement *elm,
int startIndex,
int numSelectLines)
1073 QString selectValue;
1074 for (
int i = numSelectLines - 1; i >= 0; --i) {
1075 QString selectBit = otherPortName(elm->
inputPort(startIndex + i));
1076 if (selectBit ==
"HIGH") selectBit =
"1";
1077 else if (selectBit ==
"LOW") selectBit =
"0";
1078 if (i == numSelectLines - 1) {
1079 selectValue = selectBit;
1081 selectValue = selectBit +
" + (" + selectValue +
" << 1)";
1089 bool negate =
false;
1090 bool parentheses =
true;
1091 QString logicOperator;
1093 case ElementType::And: {
1094 logicOperator =
"&&";
1097 case ElementType::Or: {
1098 logicOperator =
"||";
1101 case ElementType::Nand: {
1102 logicOperator =
"&&";
1106 case ElementType::Nor: {
1107 logicOperator =
"||";
1111 case ElementType::Xor: {
1112 logicOperator =
"^";
1115 case ElementType::Xnor: {
1116 logicOperator =
"^";
1120 case ElementType::Not: {
1122 parentheses =
false;
1125 case ElementType::Node: {
1126 if (elm->
outputs().size() == 1 && elm->
inputs().size() == 1) {
1129 if (outputPort && inputPort) {
1130 QString varName = m_varMap.value(outputPort);
1131 QString inputValue = otherPortName(inputPort);
1132 if (inputValue.isEmpty() && m_currentIC) {
1133 for (
int i = 0; i < m_currentIC->internalInputs().size(); ++i) {
1134 if (m_currentIC->internalInputs().at(i) == inputPort) {
1135 inputValue = m_varMap.value(inputPort);
1140 m_stream <<
" " << varName <<
" = " << inputValue <<
";" << Qt::endl;
1145 case ElementType::AudioBox:
1146 case ElementType::Buzzer:
1147 case ElementType::Clock:
1148 case ElementType::DFlipFlop:
1149 case ElementType::DLatch:
1150 case ElementType::Demux:
1151 case ElementType::Display14:
1152 case ElementType::Display16:
1153 case ElementType::Display7:
1154 case ElementType::IC:
1155 case ElementType::InputButton:
1156 case ElementType::InputGnd:
1157 case ElementType::InputRotary:
1158 case ElementType::InputSwitch:
1159 case ElementType::InputVcc:
1160 case ElementType::JKFlipFlop:
1161 case ElementType::JKLatch:
1162 case ElementType::Led:
1163 case ElementType::Line:
1164 case ElementType::Mux:
1165 case ElementType::SRFlipFlop:
1166 case ElementType::SRLatch:
1167 case ElementType::TFlipFlop:
1168 case ElementType::Text:
1169 case ElementType::TruthTable:
1170 case ElementType::Unknown:
1173 if (elm->
outputs().size() == 1) {
1175 if (!outputPort)
return;
1176 QString varName = m_varMap.value(outputPort);
1178 if (!inPort)
return;
1182 innerExpr = otherPortName(inPort);
1183 for (
int i = 1; i < elm->
inputs().size(); ++i) {
1184 inPort = elm->
inputs().at(i);
1185 innerExpr +=
" " + logicOperator +
" ";
1186 innerExpr += otherPortName(inPort);
1189 QString finalExpr = innerExpr;
1192 finalExpr =
"!(" + innerExpr +
")";
1194 finalExpr =
"!" + innerExpr;
1198 m_stream <<
" " << varName <<
" = " << finalExpr <<
";" << Qt::endl;
1200 qWarning() <<
"assignLogicOperator: element" << elm->objectName() <<
"has" << elm->
outputs().size() <<
"outputs (expected 1) — skipping";
1204void ArduinoCodeGen::emitComputeLogicFunction()
1206 m_stream <<
"void computeLogic() {" << Qt::endl;
1207 m_stream <<
" // Assigning aux variables. //" << Qt::endl;
1208 assignVariablesRec(m_elements);
1209 m_stream <<
"}" << Qt::endl
1213void ArduinoCodeGen::loop()
1215 m_stream <<
"void loop() {" << Qt::endl;
1216 m_stream <<
" // Reading input data //." << Qt::endl;
1217 for (
const auto &pin : std::as_const(m_inputMap)) {
1218 m_stream << QString(
" %1_val = digitalRead(%1);").arg(pin.m_varName) << Qt::endl;
1220 m_stream << Qt::endl;
1221 m_stream <<
" // Updating clocks. //" << Qt::endl;
1222 for (
auto *elm : m_elements) {
1224 const auto elmOutputs = elm->
outputs();
1225 if (elmOutputs.isEmpty()) {
1228 QString varName = m_varMap.value(elmOutputs.constFirst());
1229 m_stream << QString(
" if (%1_elapsed > %1_interval) {").arg(varName) << Qt::endl;
1230 m_stream << QString(
" %1_elapsed = 0;").arg(varName) << Qt::endl;
1231 m_stream << QString(
" %1 = ! %1;").arg(varName) << Qt::endl;
1232 m_stream << QString(
" }") << Qt::endl;
1235 m_stream << Qt::endl;
1237 m_stream << Qt::endl;
1238 m_stream <<
" // Writing output data. //" << Qt::endl;
1239 for (
const auto &pin : std::as_const(m_outputMap)) {
1240 if (pin.m_elm->elementType() == ElementType::Buzzer) {
1241 auto *buzzer = qobject_cast<Buzzer *>(pin.m_elm);
1242 if (!buzzer)
continue;
1243 const QString inputSignal = otherPortName(buzzer->inputPort(0));
1244 const int frequency =
static_cast<int>(buzzer->frequency());
1245 m_stream << QString(
" if (%1) {").arg(inputSignal) << Qt::endl;
1246 m_stream << QString(
" tone(%1, %2);").arg(pin.m_varName).arg(frequency) << Qt::endl;
1247 m_stream << QString(
" } else {") << Qt::endl;
1248 m_stream << QString(
" noTone(%1);").arg(pin.m_varName) << Qt::endl;
1249 m_stream << QString(
" }") << Qt::endl;
1252 QString varName = otherPortName(pin.m_port);
1253 if (varName.isEmpty()) {
1254 varName = highLow(pin.m_port->defaultValue());
1256 m_stream << QString(
" digitalWrite(%1, %2);").arg(pin.m_varName, varName) << Qt::endl;
1258 m_stream <<
"}" << Qt::endl;
1263 QFile tbFile(tbFileName);
1264 if (!tbFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
1265 throw PANDACEPTION(
"Could not open testbench file: %1", tbFileName);
1268 QIODevice *savedDevice = m_stream.device();
1269 m_stream.setDevice(&tbFile);
1270 m_hasSequential = hasNativeMemory(m_elements);
1273 m_stream <<
"// ============================================================ //" << Qt::endl;
1274 m_stream <<
"// ====== Testbench generated automatically by wiRedPanda ====== //" << Qt::endl;
1275 m_stream <<
"// ============================================================ //" << Qt::endl;
1276 m_stream << Qt::endl;
1277 m_stream <<
"#include <avr/sleep.h>" << Qt::endl;
1278 m_stream <<
"#include <elapsedMillis.h>" << Qt::endl;
1279 m_stream << Qt::endl;
1282 m_stream <<
"/* ========= Inputs ========== */" << Qt::endl;
1283 for (
const auto &pin : std::as_const(m_inputMap)) {
1284 m_stream <<
"const int " << pin.m_varName <<
" = 0;" << Qt::endl;
1286 m_stream << Qt::endl;
1289 m_stream <<
"/* ========= Outputs ========== */" << Qt::endl;
1290 for (
const auto &pin : std::as_const(m_outputMap)) {
1291 m_stream <<
"const int " << pin.m_varName <<
" = 0;" << Qt::endl;
1293 m_stream << Qt::endl;
1296 m_stream <<
"/* ====== Aux. Variables ====== */" << Qt::endl;
1297 for (
const auto &varName : std::as_const(m_declaredVariables)) {
1298 m_stream <<
"bool " << varName <<
" = LOW;" << Qt::endl;
1303 declareSequentialStateRec(m_elements,
true);
1304 if (m_hasSequential) {
1305 m_stream <<
"bool g_sample = true;" << Qt::endl;
1307 m_stream << Qt::endl;
1310 emitComputeLogicFunction();
1311 if (m_hasSequential) {
1312 emitCommitFlipFlops();
1316 QStringList outputVarNames;
1317 for (
const auto &pin : std::as_const(m_outputMap)) {
1318 QString varName = otherPortName(pin.m_port);
1319 if (varName.isEmpty()) {
1320 varName = highLow(pin.m_port->defaultValue());
1322 outputVarNames.append(varName);
1326 const int numInputs =
static_cast<int>(m_inputMap.size());
1327 const int numOutputs =
static_cast<int>(m_outputMap.size());
1328 const int numTests =
static_cast<int>(vectors.size());
1330 m_stream <<
"/* ====== Test Vectors ====== */" << Qt::endl;
1331 m_stream <<
"struct TestVec {" << Qt::endl;
1332 m_stream <<
" bool in[" << (std::max)(1, numInputs) <<
"];" << Qt::endl;
1333 m_stream <<
" bool out[" << (std::max)(1, numOutputs) <<
"];" << Qt::endl;
1334 m_stream <<
"};" << Qt::endl;
1335 m_stream << Qt::endl;
1336 m_stream <<
"const int NUM_TESTS = " << numTests <<
";" << Qt::endl;
1337 m_stream <<
"const TestVec VECTORS[" << (std::max)(1, numTests) <<
"] = {" << Qt::endl;
1338 for (
const auto &v : vectors) {
1340 for (
int j = 0; j < v.inputs.size(); ++j) {
1341 if (j > 0) m_stream <<
", ";
1342 m_stream << (v.inputs[j] ?
"HIGH" :
"LOW");
1345 for (
int k = 0; k < v.outputs.size(); ++k) {
1346 if (k > 0) m_stream <<
", ";
1347 m_stream << (v.outputs[k] ?
"HIGH" :
"LOW");
1349 m_stream <<
"}}," << Qt::endl;
1351 m_stream <<
"};" << Qt::endl;
1352 m_stream << Qt::endl;
1355 m_stream <<
"void setup() {" << Qt::endl;
1356 m_stream <<
" Serial.begin(9600);" << Qt::endl;
1357 m_stream <<
" bool allPassed = true;" << Qt::endl;
1358 m_stream <<
" for (int t = 0; t < NUM_TESTS; t++) {" << Qt::endl;
1359 for (
int j = 0; j < numInputs; ++j) {
1360 m_stream <<
" " << m_inputMap.at(j).m_varName <<
"_val = VECTORS[t].in[" << j <<
"];" << Qt::endl;
1367 if (m_hasSequential) {
1368 m_stream <<
" g_sample = true;" << Qt::endl;
1370 <<
"; s++) { computeLogic(); }" << Qt::endl;
1371 m_stream <<
" commitFlipFlops();" << Qt::endl;
1372 m_stream <<
" g_sample = false;" << Qt::endl;
1374 <<
"; s++) { computeLogic(); }" << Qt::endl;
1377 <<
"; s++) { computeLogic(); }" << Qt::endl;
1379 m_stream <<
" bool pass = true;" << Qt::endl;
1380 for (
int k = 0; k < numOutputs; ++k) {
1381 m_stream <<
" pass = pass && (" << outputVarNames.at(k) <<
" == VECTORS[t].out[" << k <<
"]);" << Qt::endl;
1383 m_stream <<
" if (!pass) {" << Qt::endl;
1384 m_stream <<
" allPassed = false;" << Qt::endl;
1385 m_stream <<
" Serial.print(\"FAIL vector \");" << Qt::endl;
1386 m_stream <<
" Serial.println(t);" << Qt::endl;
1387 m_stream <<
" }" << Qt::endl;
1388 m_stream <<
" }" << Qt::endl;
1389 m_stream <<
" Serial.println(allPassed ? \"ALL PASS\" : \"SOME FAILED\");" << Qt::endl;
1390 m_stream <<
" Serial.flush();" << Qt::endl;
1391 m_stream <<
" set_sleep_mode(SLEEP_MODE_PWR_DOWN);" << Qt::endl;
1392 m_stream <<
" sleep_mode();" << Qt::endl;
1393 m_stream <<
"}" << Qt::endl;
1394 m_stream << Qt::endl;
1396 m_stream <<
"void loop() {}" << Qt::endl;
1398 m_stream.setDevice(savedDevice);
1403 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.