Simulator Basics

Read time: 35 minutes (8935 words)

One of the major course objectives is for you to build a simple simulator for a real computer. While I would love to build a real computer in this class, using one of those FPGA boards, that would be asking a bit much. Instead, we are going to build as much of a simple simulator as we can.

No, we will not simulate anything as complex as a Pentium. We can, however, write a simple simulation program, using C++ tools, that mimics what a real machine does. And, mimics it so well we can actually run some code on it!

Hey, this is the kind of programming I really like, it is fun! Maybe not as cool as blowing up space aliens, but cool enough! You will learn a few new and useful programming tricks in this simulation as well!

Here is one of my FPGA boards:

../_images/basys-fpga.png

Building a Computer

What we need to do is find a way to write a program where we can assemble a bunch of fairly simple simulated components and interconnect those components with something that simulates wires.

A real computer is constructed in exactly this way. Each component part just sits there, with no idea that it is part of a computer. There will be a set of wires attached to each component allowing signals to travel from component to component.

Hey, we can do that in code! Simulation does not sound too hard!

Components

We will view each component as a simple “Black Box”. That means that what happens inside is hidden. We can envision a box inside of which some kind of magic happens. We do not know, and really do not care how it does its magic. We provide a set of pathways we can use to deliver input signals to this component. We will also provide another set of output pathways that can carry output signals away from the component. We can use those output signals however we wish. That component does not know, and does not care, where the input signals came from, nor does it care where its output signals go. It just sits there waiting for signals it can process.

By the way, the transformation work takes some amount of time to complete. In our last lecture, we decided to deal with time by waking up a specific moments and checking our system to see what state it is in.

Since we are not allowed to look inside of the components, I guess that leaves us with only wires to examine. That is exactly how I learned how all this computer stuff works. I built a real computer out of a few fairly simple parts, connected them together with wires, and hooked up test equipment to see what ws happening on those wires!

This is a shot of a “wire-wrapped” board, sort of like the mess I was building back when home computers were very new:

../_images/wire-wrap.png

Today, building test circuits is much simpler. As we shall see, much of the design work is done in software!

If this was a course in electronics, we might actually build our own computer. That is not as far-fetched as you might think.

Modeling the Internal Work

Each component reacts to incoming signals in some way. More than likely, it generates a set of output signals using just those input signals, which leave the component and head off to some other component in the system.

C++ was designed to help us model real things from our human world. We call those things objects. Here we will call them components. We will simulate each basic part we need for our computer using a single C++ class. The C++ class allows us to describe the component, then manufacture a bunch of those components (objects) on demand, as we need them.

By now, you know how to set up a C++ class. You also know how to build one or more objects using that class. We will assembly our simulated computer using a set of class definitions, and build as many components as we need.

Simple, huh?

Well, there is one small wrinkle. These component objects will not be calling other component objects to get things done. That simply is not how a real computer works.

How Components Work

As we indicated earlier, each component in real computer just sits there watching its input signals. As they change, it reacts to them, and generates some new set of output signals. The internal work needed to create that output signal set takes some time to complete.

All of these signals travel from place to place over wires. The output signal from one component becomes the input signal for some other component.

What we have to do is control all of this action, and make the system of components do our bidding. Basically, we need to teach this collection of parts to do the “dance”!

We just need to figure out how to make a simulated signal trigger action in a simulated component. Then we have to teach our components how to do a few simple transformations on those input signals to produce the resulting output signals we want.

Add all of this up, and we hope to get a computer!

Note

There are many ways to build a simulation. I am going to show you a way that behaves as much like a real computer as I can come up with. It is not too hard to understand, but you will be using a scary (for beginners) C++ construct called a pointer

Don’t panic, we will be doing very little with these pointers!)

Wires

In our Let’s Dance lecture, and the game we played in class, you got a first look at how electricity “flows” over a wire. At any point in time, the wire carries some voltage. That voltage is a measure of how many free electrons are in the wire. As we saw in class, it takes some time for these electrons to move around, but we can get away with ignoring that, as long as we keep our simulated wires short. (What does that even mean? We will get to that!)

Our view of a wire can be very simple. It is just a place that holds a single voltage at any given point in time. That value can change over time, but the voltage exists at every physical point in the wire. If we put a signal at one end of the wire, it is effectively available at the other end of the wire instantaneously!. We know that is not true, but the time involved is so short, we can ignore it.

That greatly simplifies out simulation of a wire. It becomes nothing more than a simple variable. (Well, a variable wrapped up in a class, so we can work with that variable - er – attribute! We will place a value in that variable, representing the signal we want to move.

Wires do nothing to the signal placed on them. They do not modify that signal in any way. Their purpose is simply to let that signal travel from one place in our collection of components to another place.

Watching Wires

Wires do play a very important role in a real electronic system, and in our simulated system. We are not allowed to look inside those “Black Box” components in a real computer, so we really should not look inside our simulated components either. To watch what is happening in our simulation as time progresses, we can watch what happens on the wires. In fact, this is how real electronics engineers work.

There is a neat gadget called an oscilloscope that can be attached to any wire in a system. As the system runs (over time) the oscilloscope`` will display the voltage on that wire. The display you can get might look like this:

../_images/digital-oscilloscope.jpg

Since we seem to be “simulating” everything else in out simulated computer, we will just build a simulated oscilloscope as well. That will require a bit of graphics work, and I am going to simplify that by using Python. I will provide the basic code for that!

We are just about ready to start our code. We need to summarize a few key concepts!

Connecting Wires to Components

Here is where those ugly pointers come in. We will “attach” a component to a wire using pointers. The component will need to maintain an internal pointer variable we can use to attach each wire. When the component action is triggered, the component will use the input pointer to reach out to the attached wire, whose address we store in that pointer variable``. It will then “read” the value of the current signal on that wire. When it is finished generating the output signals it will use the output pointer variables to “reach out” and place an output signal on the associated wire. We need to make sure that every component gets a chance to do part of the “dance”.

Writing the Simulator

We have enough of an idea about what we have to do, to start writing our simulator. As we create our code, always remember that one component cannot ever “call” another component. All objects just sit there until some event triggers them into action.

Huh?

Your component class may have a few variables, but they will be private. (They really should be, anyway!) The outside world cannot see them. There may be methods as well. Almost every one of those will also be private. The component can do some internal work, but the trigger event that starts the action will not be just any other another component “calling” a method. Instead, we will create one special component in our simulated system, whose purpose is to activate every other component in our simulated system. We will call that special component the clock, and it will call one method every component provides. Just for run, we will name that method tick.

Are you ready to see some code. That will be the next lecture in this series.