wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
ICSimulation.cpp
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <algorithm>
7
8#include "App/Element/IC.h"
10#include "App/Wiring/Port.h"
11
13{
14 ic.m_sortedInternalElements.clear();
15 ic.m_boundaryInputElements.clear();
16 ic.m_internalHasFeedback = false;
17
18 if (ic.m_internalElements.isEmpty()) {
19 return;
20 }
21
22 // Initialize simulation vectors on all internal elements
23 for (auto *elm : std::as_const(ic.m_internalElements)) {
24 elm->initSimulationVectors(elm->inputSize(), elm->outputSize());
25 }
26
27 // Build connection graph and wire wireless Tx→Rx
28 Simulation::buildConnectionGraph(ic.m_internalElements);
29 Simulation::connectWirelessElements(ic.m_internalElements);
30
31 // Initialize nested ICs recursively
32 for (auto *elm : std::as_const(ic.m_internalElements)) {
33 if (elm->elementType() == ElementType::IC) {
34 ICSimulation::initialize(*static_cast<IC *>(elm));
35 }
36 }
37
38 // Record boundary input elements (driven externally, should not run updateLogic)
39 for (auto *port : std::as_const(ic.m_internalInputs)) {
40 if (auto *elm = port->graphicElement()) {
41 ic.m_boundaryInputElements.insert(elm);
42 }
43 }
44
45 // Topological sort with feedback detection using shared helpers
46 const auto txMap = Simulation::buildTxMap(ic.m_internalElements);
47 const auto successors = Simulation::buildSuccessorGraph(ic.m_internalElements, txMap);
48 const auto result = Simulation::topologicalSort(ic.m_internalElements, successors);
49 ic.m_internalHasFeedback = !result.feedbackNodes.isEmpty();
50
51 // Remove boundary input elements so the hot loop in updateLogic()
52 // doesn't need a per-element QSet lookup on every simulation tick.
53 ic.m_sortedInternalElements = result.sorted;
54 ic.m_sortedInternalElements.erase(
55 std::remove_if(ic.m_sortedInternalElements.begin(), ic.m_sortedInternalElements.end(),
56 [&ic](auto *elm) { return ic.m_boundaryInputElements.contains(elm); }),
57 ic.m_sortedInternalElements.end());
58}
59
60void ICSimulation::pushInputsToBoundary(IC &ic)
61{
62 for (int i = 0; i < ic.inputSize() && i < ic.m_internalInputs.size(); ++i) {
63 auto *boundaryElement = ic.m_internalInputs.at(i)->graphicElement();
64 if (boundaryElement) {
65 boundaryElement->setOutputValue(0, ic.simInputs().at(i));
66 }
67 }
68}
69
70void ICSimulation::pullOutputsFromBoundary(IC &ic)
71{
72 for (int i = 0; i < ic.outputSize() && i < ic.m_internalOutputs.size(); ++i) {
73 auto *boundaryElement = ic.m_internalOutputs.at(i)->graphicElement();
74 if (boundaryElement) {
75 ic.setOutputValue(i, boundaryElement->outputValue(0));
76 }
77 }
78}
79
81{
82 if (ic.m_sortedInternalElements.isEmpty()) {
83 return;
84 }
85
86 // Permissive mode so ICs can propagate Unknown through their internal elements.
88 return;
89 }
90
91 pushInputsToBoundary(ic);
92
93 // Run internal elements in topological order.
94 // Boundary input nodes are already excluded from m_sortedInternalElements.
95 if (ic.m_internalHasFeedback) {
96 Simulation::iterativeSettle(ic.m_sortedInternalElements);
97 } else {
98 for (auto *element : std::as_const(ic.m_sortedInternalElements)) {
99 if (element) {
100 element->updateLogic();
101 }
102 }
103 }
104
105 pullOutputsFromBoundary(ic);
106}
107
109{
110 if (ic.m_sortedInternalElements.isEmpty()) {
111 return;
112 }
113
114 if (!ic.simUpdateInputsAllowUnknown()) {
115 return;
116 }
117
118 pushInputsToBoundary(ic);
119
120 // Re-propagate just-committed internal sequential state through the IC's
121 // combinational logic, WITHOUT re-running the sequential elements (that
122 // would corrupt their edge state / re-clock them). Memory-group internals
123 // keep the value they committed this tick; everything else recomputes.
124 // Most cycles run through a sequential element (register/counter cells), so
125 // removing them leaves an acyclic graph that settles in one topological
126 // sweep; only genuine combinational feedback (gate-built latches) needs more.
127 // Iterate to a fixed point, bounded, instead of a fixed pass count.
128 const int maxPasses = ic.m_internalHasFeedback ? Simulation::kMaxSettleIterations : 1;
129 for (int pass = 0; pass < maxPasses; ++pass) {
130 bool changed = false;
131 for (auto *element : std::as_const(ic.m_sortedInternalElements)) {
132 if (element && element->elementGroup() != ElementGroup::Memory) {
133 element->clearOutputChanged();
134 element->resettleCombinational();
135 changed = changed || element->outputChanged();
136 }
137 }
138 if (!changed) {
139 break;
140 }
141 }
142
143 pullOutputsFromBoundary(ic);
144}
ICSimulation: builds and runs an IC's internal simulation graph.
Integrated Circuit (IC) graphic element that encapsulates a sub-circuit file.
Port classes: Port (base), InputPort, and OutputPort.
Synchronous cycle-based simulation engine with event-driven clock support.
const QVector< Status > & simInputs() const
Read-only view of the cached simulation input values.
int inputSize() const
Returns the current number of input ports.
bool simUpdateInputsAllowUnknown()
Like simUpdateInputs(), but allows Unknown/Error values through.
void setOutputValue(const int index, const Status value)
Sets simulation output port index to value.
int outputSize() const
Returns the current number of output ports.
static void update(IC &ic)
Simulates ic's internal circuit and propagates results to its boundary.
static void resettle(IC &ic)
static void initialize(IC &ic)
Builds the internal simulation graph (connection graph + topological sort) for ic.
Graphic element representing an Integrated Circuit (sub-circuit) box.
Definition IC.h:31
static SortResult topologicalSort(const QVector< GraphicElement * > &elements, const QHash< GraphicElement *, QVector< GraphicElement * > > &successors)
Topologically sorts elements using the successor graph, detects feedback loops.
static bool iterativeSettle(const QVector< GraphicElement * > &elements, int maxIterations=kMaxSettleIterations)
static QHash< QString, GraphicElement * > buildTxMap(const QVector< GraphicElement * > &elements)
Builds a label→element map for wireless Tx nodes. First Tx per label wins.
static void buildConnectionGraph(const QVector< GraphicElement * > &elements)
static void connectWirelessElements(const QVector< GraphicElement * > &elements)
static QHash< GraphicElement *, QVector< GraphicElement * > > buildSuccessorGraph(const QVector< GraphicElement * > &elements, const QHash< QString, GraphicElement * > &txMap)
Builds a successor adjacency list from connection graph + wireless Tx→Rx edges.
static constexpr int kMaxSettleIterations
Definition Simulation.h:46