Skip to content

Commit

Permalink
Merge pull request #18 from Capital-Asterisk/0.1.3-maybe
Browse files Browse the repository at this point in the history
A bunch of improvements and stuff for 0.2.0
  • Loading branch information
Capital-Asterisk authored May 22, 2024
2 parents 243f3c8 + fd0d6b9 commit c965a6f
Show file tree
Hide file tree
Showing 17 changed files with 761 additions and 410 deletions.
2 changes: 1 addition & 1 deletion examples/circuits/circuit_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ ElementId gate_combinatinal(CombinationalGates::GateDesc desc, std::initializer_
void populate_pub_sub(Elements const& elements, Nodes &rNodes)
{
std::vector<int> nodeSubCount(rNodes.m_nodeIds.capacity(), 0); // can we reach 1 million subscribers?
for (ElementId elem : elements.m_ids.bitview().zeros())
for (ElementId elem : elements.m_ids)
{
auto nodes = rNodes.m_elemConnect[elem];
// skip first, port 0 is the publisher
Expand Down
88 changes: 47 additions & 41 deletions examples/circuits/circuits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#pragma once

#include <longeron/containers/intarray_multimap.hpp>
#include <longeron/id_management/id_set_stl.hpp>
#include <longeron/id_management/keyed_vec_stl.hpp>
#include <longeron/id_management/registry_stl.hpp>

#include <algorithm>
Expand All @@ -13,31 +15,37 @@
namespace circuits
{

using ElementId = uint32_t;
using ElemLocalId = uint32_t;
using ElemTypeId = uint8_t;
using NodeId = uint32_t;

// Enum classes used as integer "strong typedef" and can't be implicitly casted to each other,
// preventing logic errors and cognative load from accidentally mixing them up.
enum class ElemLocalId : std::uint32_t { };
enum class ElemTypeId : std::uint8_t { };

// lgrn::IntArrayMultiMap does not (yet?) support strongly typedefed ID types
using ElementId = std::uint32_t;
using NodeId = std::uint32_t;


/**
* @brief Keeps track of which circuit elements of a certain type exists
*/
struct PerElemType
{
lgrn::IdRegistryStl<ElemLocalId> m_localIds;
std::vector<ElementId> m_localToElem;
lgrn::IdRegistryStl<ElemLocalId> m_localIds;
lgrn::KeyedVec<ElemLocalId, ElementId> m_localToElem;
};

/**
* @brief Keeps track of which circuit elements exist and what type they are
*/
struct Elements
{
lgrn::IdRegistryStl<ElementId> m_ids;
lgrn::IdRegistryStl<ElementId> m_ids;

std::vector<ElemTypeId> m_elemTypes;
std::vector<ElemLocalId> m_elemToLocal;
lgrn::KeyedVec<ElementId, ElemTypeId> m_elemTypes;
lgrn::KeyedVec<ElementId, ElemLocalId> m_elemToLocal;

std::vector<PerElemType> m_perType;
lgrn::KeyedVec<ElemTypeId, PerElemType> m_perType;
};

/**
Expand All @@ -49,6 +57,7 @@ struct ElementPair
ElemTypeId m_type;
};


/**
* @brief Connects cirucit elements together as Nodes
*/
Expand All @@ -64,7 +73,7 @@ struct Nodes
// Node-to-Element connections
// Each node can have multiple subscribers, but only one publisher
Subscribers_t m_nodeSubscribers;
std::vector<ElementId> m_nodePublisher;
lgrn::KeyedVec<NodeId, ElementId> m_nodePublisher;

// Corresponding Element-to-Node connections
// [ElementId][PortId] -> NodeId
Expand All @@ -80,7 +89,7 @@ struct Nodes
template <typename VALUE_T>
struct NodeValues
{
std::vector<VALUE_T> m_nodeValues;
lgrn::KeyedVec<NodeId, VALUE_T> m_nodeValues;
};

//-----------------------------------------------------------------------------
Expand All @@ -102,33 +111,31 @@ struct CombinationalGates
bool m_invert;
};

std::vector<GateDesc> m_localGates;
lgrn::KeyedVec<ElemLocalId, GateDesc> m_localGates;
};

//-----------------------------------------------------------------------------

// Updating

using BitVector_t = lgrn::BitView< std::vector<uint64_t> >;

constexpr std::size_t const gc_bitVecIntSize = 64;

struct UpdateElem
{
BitVector_t m_localDirty;
lgrn::IdSetStl<ElemLocalId> m_localDirty;
};

using UpdateElemTypes_t = std::vector<UpdateElem>;
using UpdateElemTypes_t = lgrn::KeyedVec<ElemTypeId, UpdateElem>;

template <typename VALUE_T>
struct UpdateNodes
{
BitVector_t m_nodeDirty;
std::vector<VALUE_T> m_nodeNewValues;
lgrn::IdSetStl<NodeId> m_nodeDirty;
lgrn::KeyedVec<NodeId, VALUE_T> m_nodeNewValues;

void assign(NodeId node, VALUE_T&& value)
{
m_nodeDirty.set(node);
m_nodeDirty.insert(node);
m_nodeNewValues[node] = std::forward<VALUE_T>(value);
}
};
Expand All @@ -147,26 +154,26 @@ struct UpdateNodes
*/
template <typename RANGE_T>
bool update_combinational(
RANGE_T&& toUpdate,
ElementId const* pLocalToElem,
Nodes::Connections_t const& elemConnect,
ELogic const* pNodeValues,
CombinationalGates const& gates,
UpdateNodes<ELogic>& rUpdNodes) noexcept
RANGE_T&& toUpdate,
lgrn::KeyedVec<ElemLocalId, ElementId> const &localToElem,
Nodes::Connections_t const &elemConnect,
lgrn::KeyedVec<NodeId, ELogic> &nodeValues,
CombinationalGates const &gates,
UpdateNodes<ELogic> &rUpdNodes) noexcept
{
using Op = CombinationalGates::Op;

auto const is_logic_high = [pNodeValues] (NodeId in) noexcept -> bool
auto const is_logic_high = [&nodeValues] (NodeId in) noexcept -> bool
{
return pNodeValues[in] == ELogic::High;
return nodeValues[in] == ELogic::High;
};

bool nodeUpdated = false;

for (ElemLocalId local : toUpdate)
{
ElementId const elem = pLocalToElem[local];
CombinationalGates::GateDesc const& desc = gates.m_localGates[local];
ElementId const elem = localToElem[local];
CombinationalGates::GateDesc const &desc = gates.m_localGates[local];

auto connectedNodes = elemConnect[elem];
auto inFirst = connectedNodes.begin() + 1;
Expand Down Expand Up @@ -197,10 +204,10 @@ bool update_combinational(
NodeId out = *connectedNodes.begin();

// Request to write changes to node if value is changed
if (pNodeValues[out] != outLogic)
if (nodeValues[out] != outLogic)
{
nodeUpdated = true;
rUpdNodes.m_nodeDirty.set(out);
rUpdNodes.m_nodeDirty.insert(out);
rUpdNodes.m_nodeNewValues[out] = outLogic;
}
}
Expand All @@ -222,25 +229,24 @@ bool update_combinational(
*/
template <typename VALUE_T, typename RANGE_T>
bool update_nodes(
RANGE_T&& toUpdate,
Nodes::Subscribers_t const& nodeSubs,
Elements const& elements,
VALUE_T const* pNewValues,
VALUE_T* pValues,
UpdateElemTypes_t& rUpdElem)
RANGE_T &&toUpdate,
Nodes::Subscribers_t const &nodeSubs,
lgrn::KeyedVec<NodeId, VALUE_T> const &newValues,
lgrn::KeyedVec<NodeId, VALUE_T> &values,
UpdateElemTypes_t &rUpdElem)
{
bool elemNotified = false;

for (uint32_t node : toUpdate)
for (NodeId node : toUpdate)
{
// Apply node value changes
pValues[node] = pNewValues[node];
values[node] = newValues[node];

// Notify subscribed elements
for (ElementPair subElem : nodeSubs[node])
{
elemNotified = true;
rUpdElem[subElem.m_type].m_localDirty.set(subElem.m_id);
rUpdElem[subElem.m_type].m_localDirty.insert(subElem.m_id);
}
}

Expand Down
43 changes: 20 additions & 23 deletions examples/circuits/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
#include <iostream>
#include <vector>

#include <optional>

using namespace circuits;

using lgrn::id_null;
Expand Down Expand Up @@ -72,15 +70,15 @@ struct UserCircuit
{
UpdateElemTypes_t out;
out.resize(m_maxTypes);
out[gc_elemGate].m_localDirty.ints().resize(m_elements.m_perType[gc_elemGate].m_localIds.vec().capacity());
out[gc_elemGate].m_localDirty.resize(m_elements.m_perType[gc_elemGate].m_localIds.capacity());

// Initially set all to dirty get to valid state
// middle parts of a circuit may be unresponsive otherwise
for (uint32_t elem : m_elements.m_ids.bitview().zeros())
for (ElementId elem : m_elements.m_ids)
{
ElemTypeId const type = m_elements.m_elemTypes[elem];
ElemTypeId const type = m_elements.m_elemTypes[elem];
ElemLocalId const local = m_elements.m_elemToLocal[elem];
out[type].m_localDirty.set(local);
out[type].m_localDirty.insert(local);
}

return out;
Expand All @@ -89,7 +87,7 @@ struct UserCircuit
UpdateNodes<ELogic> setup_logic_updater()
{
UpdateNodes<ELogic> out;
out.m_nodeDirty.ints().resize(m_maxNodes / gc_bitVecIntSize + 1);
out.m_nodeDirty.resize(m_maxNodes);
out.m_nodeNewValues.resize(m_maxNodes);

return out;
Expand Down Expand Up @@ -133,22 +131,21 @@ static int step_until_stable(
while (elemNotified && steps < maxSteps)
{
update_nodes(
rUpdLogic.m_nodeDirty.ones(),
rUpdLogic.m_nodeDirty,
rCircuit.m_logicNodes.m_nodeSubscribers,
rCircuit.m_elements,
rUpdLogic.m_nodeNewValues.data(),
rCircuit.m_logicValues.m_nodeValues.data(),
rUpdLogic.m_nodeNewValues,
rCircuit.m_logicValues.m_nodeValues,
rUpdElems);
rUpdLogic.m_nodeDirty.reset();
rUpdLogic.m_nodeDirty.clear();

elemNotified = update_combinational(
rUpdElems[gc_elemGate].m_localDirty.ones(),
rCircuit.m_elements.m_perType[gc_elemGate].m_localToElem.data(),
rUpdElems[gc_elemGate].m_localDirty,
rCircuit.m_elements.m_perType[gc_elemGate].m_localToElem,
rCircuit.m_logicNodes.m_elemConnect,
rCircuit.m_logicValues.m_nodeValues.data(),
rCircuit.m_logicValues.m_nodeValues,
rCircuit.m_gates,
rUpdLogic);
rUpdElems[gc_elemGate].m_localDirty.reset();
rUpdElems[gc_elemGate].m_localDirty.clear();

steps ++;
}
Expand Down Expand Up @@ -244,7 +241,7 @@ static void test_manual_build()

// Connect 'ports' of XOR gate. Adds a Element->Node mapping
// For basic gates, first (0) is the output, the rest are inputs
circuit.m_logicNodes.m_elemConnect.emplace(gc_elemGate, {out, A, B});
circuit.m_logicNodes.m_elemConnect.emplace(xorElem, {out, A, B});

// Connect publishers and subscribers. Adds Node->Element mapping
circuit.m_logicNodes.m_nodePublisher[out] = xorElem;
Expand All @@ -253,7 +250,7 @@ static void test_manual_build()

// Now everything is set, circuit can run!

UpdateElemTypes_t updElems = circuit.setup_element_updater();
UpdateElemTypes_t updElems = circuit.setup_element_updater();
UpdateNodes<ELogic> updLogic = circuit.setup_logic_updater();

auto const& outVal = circuit.m_logicValues.m_nodeValues[out];
Expand Down Expand Up @@ -308,7 +305,7 @@ static void test_xor_nand()

circuit.build_end();

UpdateElemTypes_t updElems = circuit.setup_element_updater();
UpdateElemTypes_t updElems = circuit.setup_element_updater();
UpdateNodes<ELogic> updLogic = circuit.setup_logic_updater();

auto const& outVal = circuit.m_logicValues.m_nodeValues[out];
Expand Down Expand Up @@ -361,9 +358,9 @@ static void test_sr_latch()
// initialize to get to valid state
for (uint32_t elem : circuit.m_elements.m_ids.bitview().zeros())
{
ElemTypeId const type = circuit.m_elements.m_elemTypes[elem];
ElemTypeId const type = circuit.m_elements.m_elemTypes[elem];
ElemLocalId const local = circuit.m_elements.m_elemToLocal[elem];
updElems[type].m_localDirty.set(local);
updElems[type].m_localDirty.insert(local);
}

std::cout << "NAND SR latch:\n";
Expand Down Expand Up @@ -417,9 +414,9 @@ static void test_edge_detect()
// initialize to get to valid state
for (uint32_t elem : circuit.m_elements.m_ids.bitview().zeros())
{
ElemTypeId const type = circuit.m_elements.m_elemTypes[elem];
ElemTypeId const type = circuit.m_elements.m_elemTypes[elem];
ElemLocalId const local = circuit.m_elements.m_elemToLocal[elem];
updElems[type].m_localDirty.set(local);
updElems[type].m_localDirty.insert(local);
}

std::cout << "Edge Detector:\n";
Expand Down
Loading

0 comments on commit c965a6f

Please sign in to comment.