Step 7: Inverters¶
Read time: 11 minutes (2859 words)
Our first real part is the Inverter. Formally, this device is a hardware implementation of George Boole’s “not” operation. This device has a single input pin, and a single output pin. All it does inside is read the input signal, flip it (complement) and write that new signal back out on the output pin. How hard can this be to implement? For our first real device, the input will be a single bit (a zero or a one). The output will also be a single bit. Formally, we could handle more bits, and compliment each bit individually, producing an output result. We will do that later. For now, we will stick with this single bit version.
C++ Inheritance¶
To implement this device we need to set up a class that “inherits” everything we defined in the generic component class. Essentially, this new class will have all the capabilities of the component class plus new features we add in in this derived (child) class.
Inheritance is a complex topic in C++, but in our case all we
really need to do is provide a new constructor that sets up our new part, and a
new version of the tick
routine that does the real work we need. We will set
up the required input and output pins in the constructor for this class, so
objects we create later will be ready to run!
Inverter Class Specification¶
Here is our new part header file:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Copyright 2018 Roie R. Black
#pragma once
#include <string>
#include "Component.h"
class Inverter : public Component {
public:
// constructors
explicit Inverter(std::string n);
// mutators
void tick(void);
};
|
The “inheritance” we are doing here can be seen on line 5. The normal class
declaration line has new stuff after the class name. We place a colon, an
access specifier (public
in this case), then the name of the class we are
extending. Of course, we need to include the header file for that generic class
so this name will be recognized by the compiler.
Essentially, that first line in the constructor will pass on the Inverter name to the constructor from the Component class, where that name will be registered. Inheritance lets you add new attributes or methods, or override methods in the base class with new methods in the derived class. We cannot delete things from the base class.
Testing the Inverter¶
Testing the inverter simply involves checking that it does the work needed when
that tick
routine is called. All we need to do is set the input pin to some
value (remember that this is a single bit input, so the only legal values are
zero and one). Then we call the tick
routine and make sure the output
changes to the opposite of the set value.
Here is the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Copyright 2018 Roie R. Black
#include <catch.hpp>
#include "Inverter.h"
TEST_CASE("test inverter constructor", "parts") {
std::string NAME = "CPUtest";
Inverter inv(NAME);
REQUIRE(inv.get_name() == NAME);
}
TEST_CASE( "test_inverter operation", "parts" ) {
std::string name = "INV";
Inverter inv(name);
Pin * inpin = inv.get_in_pin("IN");
Pin * outpin = inv.get_out_pin("OUT");
inpin->set_val(1);
inv.tick();
REQUIRE( outpin->get_val() == 0 );
inpin->set_val(0);
inv.tick();
REQUIRE( outpin->get_val() == 1 );
}
|
Inverter Implementation¶
I suspect you can figure out the implementation code for this device. Since the
hard work has already been taken care of in the generic Component
class,
all we need here is a new constructor that sets up the required pins, and the
new tick
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // Copyright 2018 Roie R. Black
#include "Inverter.h"
#include <iostream>
#include <string>
// constructor
Inverter::Inverter(std::string n):Component(n) {
this->add_in_pin("IN");
this->add_out_pin("OUT");
}
// TICK: perform component processing
void Inverter::tick(void) {
Pin *inpin = this->get_in_pin("IN");
Pin *outpin = this->get_out_pin("OUT");
uint16_t ival, oval;
if (inpin) {
ival = inpin->get_val();
oval = ival == 0 ? 1 : 0;
}
if (outpin) {
outpin->set_val(oval);
}
}
|
Note
Look closely at that single line that “flips” the input bit. This is called a “C++ Conditional Expression”. Basically is has this form:
expr1 ? expr2 : expr3;
the statement returns the result of evaluating expr2 if evaluating
expr1 results in ``true``, otherwise it returns the result on
evaluating expr3. It is just a shortened up if-then-else piece of code.
Running the tests now shows that we have a real, admittedly simple, digital part to play with. Let’s set up a simple circuit and see if we can make something work!