Search code examples
c++binary-operators

C++ Simulation of digital logic, implementing friend binary operators


Introduction

I am working on an experiment to simulate some digital logic. (Logic gates.)

I have an abstract base class component from which a class andgate inherits. A class port also inherits from component. Class port has a member; PORTSTATE state.

PORTSTATE is an enum class, with values including HIGH, LOW, HIGH_IMPEDANCE, UNDEFINED, etc. (This is just background information.)

The simulation works by altering the state of an input port to some logic gate. The gate is connected (using pointers) to a class Net which in turn has pointers to all the connected logic on that network. This allows all the gates downstream of one which has its state changed to be altered as required. Consider the state of a logic gate changing due to a change on the input, using the network to which its output is attached, any gates further down the sequence can be updated accordingly.

My question is about how this process is to be done using friend functions and binary operators such as operator&.

Example of Problem

Consider an And gate, with 1 output port and 2 input ports. The ports are members of the class representing the gate, and they have some information attached to them such as their direction, as well as their state.

A function is called to compute the output of the gate from the inputs. This function is a member function of the gate class. The And gate has a version of this function, for example.

Initially, I implemented the function the following way.

void Update()
{
    this->output_port.SetState(this->input_port_a & this->input_port_b);
}

The importance being that the operator& is invoked with the arguments of the 2 input gates; input_port_a and input_port_b as arguments.

In the port class I then implemented this operator.

friend const port& operator&(const port& left, const port& right)
{
    if((left.state == PORTSTATE::HIGH) && (right.state == PORTSTATE::HIGH))
    {
        return PORTSTATE::HIGH;
    }
    else if( // These are the 3 options for definitive low
            ((left.portstate == PORTSTATE::LOW) && (right.portstate == PORTSTATE::HIGH))
         || ((left.portstate == PORTSTATE::LOW) && (right.portstate == PORTSTATE::LOW))
         || ((left.portstate == PORTSTATE::HIGH) && (right.portstate == PORTSTATE::LOW))
            )
    {
        return PORTSTATE::LOW;
    }
    else
    {
        return PORTSTATE::UNDEFINED; // Anything else is not defined
    }
}

But I have a serious problem with this. You cannot "AND" 2 ports together. A port is an abstract concept, a method of keeping track of what current is flowing (or equivalently what voltage is applied) to which inputs or outputs. A more physical interpretation is that the port is a short length of wire with some current flowing through it. The current is either "HIGH" or "LOW", but there is certainly no way to "AND" these 2 objects together. I think most engineers and physicists would probably look at this piece of code in horror because of this point.

So the "obvious" fix is to place the operator& inside the And gate as a friendly function.

However those who are awake will realize that this is also a bad* idea, because it would then only be possible to & 2 instances of an And gate together, and this certainly doesn't make sense, nor does it compute the correct result! (Can it even compute a sensible result? Unlikely.)

*(completely wrong)

Consider;

andgate and_gate_1, and_gate_2;
// Setup the inputs, outputs, connections, networks
// When stepping through the simulation, we end up with something like this:
and_gate_1.Update();

// Which calls a function like this:
const andgate& operator&(const andgate& left, const andgate& right)
{
    // How would you even implement this? It makes no sense...
}

So yes, this is the problem, and I can't think of a solution. Although in some sense conceptual, it does seem like a seriously flawed idea to use the first approach and be able to "AND" two bits of wire together.

Possible - But Not Very Possible - "Solution"

The only thing I can think of is to dispense with the operators altogether and program everything manually inside the And gate's Update() function... But this kind of avoids the nice things about C++, which is that you can write operators for such things.

Note that if there was no such thing as PORTSTATE and if in replacement bool was used, then there would be no issue, as 2 boolean types can simply be &'ed together. However, this would alter Update() to something like this, which is also not very nice. (Note member access is public.)

void Update()
{

    return (input_a.state) & (input)b.state); // input_* is of type port, state is a bool
}

Arguably better is to protect the data: (But it "looks" worse due to the function call, perhaps?)

void Update()
{

    return (input_a.state()) & (input)b.state()); // input_* is of type port
                            // state() is a member function returning a bool
}

This also introduces the problem of there only existing 2 states. There is nothing which can handle undefined states or high impedance states, etc.

So I am stuck, and out of ideas.


Solution

  • Overload the operator& for two PORTSTATE. While eliminating the problem with UNDEFINED that will make your last form working.

    PORTSTATE operator&(const PORTSTATE& a, const PORTSTATE& b)
    {
        ...
    }
    
    void Update()
    {
        return input_a.state() & input_b.state(); // input_* is of type port
                                // state() is a member function returning a bool
    }
    

    For me, that's the most right way because of fact that AND operation operates with states only.

    Calling a member function is not confusing and is widely used. First, that makes it explicit that port has state which you're AND'ing (that is more logically). Second, it protects state field from external modification.