Building a Simulator

Read time: 39 minutes (9946 words)

We are now in a position to begin serious work on a simulator. I am going to model this simulator on the smallest microcontroller I happen to own now, the Atmel attiny85. We will not be able to build a complete simulator for this tiny chip, but we will use it for inspiration. The project you will work on will consist of several steps and the lecture notes will guide you through them.

Warning

You might be tempted to just copy-and-paste example code to complete this work. Do not do that. All copy-and-paste teaches you is how to copy-and-paste. I am keeping these steps small so you understand what we are doing. Nothing bets running example code after you study it a bit. YMMY!

We Need to “Git” More Power

So far, we have been using Git in a very basic way. We learned a simple “mantra” and we develop the way we have always developed. (OK, the command line is probably different!) It almost seems like Git simply adds work for us to do, in the form of additional commands to learn.

That is true, but if you look at how you are developing now, you are using in a much safer workflow. GitHub is acting like your project backup system. If you ever make a huge mistake on your project, you can throw the entire thing away, and start over, by cloning the previous version that is safely stored on GitHub. Or, you can sit down in front of a completely different machine, “clone” your project from your GitHub account, and continue work on your project (as long as your development tools are installed!)

All of these new capabilities are good, but we have not tapped the real power of Git, the ability to move backward in time to previous versions. We will explore that capability as we work through this project.

Let’s begin our new simulator project:

Step 1: Project Setup

I am assuming you have accepted the group project assignment (cpusim) from a previous homework. This is your first exposure to working in a team, and every member of a team should clone the group repository onto their individual development workstations.

Note

The best way to work in this team is to get together and decide what part of the project each team member will be responsible for. That way, two team members will not end up working on the same file, which can result in “conflicts” when you try to push your work to GitHub.

We start off by building a set of new project directories with the project repository.

Warning

One team member should do this, then push the results to GitHub. All other team members will then ned to run “git pull” to see these changes in their working copy of the project.

Pay attention to the directory name we are going to use here!

$ cd ~/cosc2325     (assumes all class projects are in this directory)
$ cd cpusim-teamname
$ mkdir src
$ mkdir lib
$ mkdir include
$ mkdir build
$ mkdir tests
$ mkdir docs

Add Dummy Files to each Directory

One minor issue we must deal with when using Git and Github is the simple fact that directories with no content will not be “pushed”. That means the team members will not see these directories until something is in each one. For now, just create a single (empty) file named dummy in each of the folders shown above. You should delete these files after you get real files in each directory.

Add a Project README file

Every project should have a README file of some sort. Some of you are letting GitHub set things up with a README.md file. That extension indicates that the README is written in something called MarkDown, a common markup notation used on many wiki systems. I prefer to use reStructuredText, so I always use .rst as the extension.

In this particular project, add your team name, and list the team member names. Use the notation showed in previous examples of project README.rst files.

Write Some Code

The src directory is where we will place all code files associated with the human interface to our simulator. For now, that means we will place our main.cpp file here.

We will start off by using the classic “baby step” approach and add a variant of our “Hello World” application.

Note

This should always the first step in beginning a new project! You should not sit down to a marathon typing (or copy-paste) session and mash together a bunch of code!

Here is our new code:

src/main.cpp
1
2
3
4
5
6
#include <iostream>

int main(void) {
    std::cout << "attiny85sim (v0.1.0)" << std::endl;
}

Make Sure It Runs

That was enough typing to check our work.

Let’s run this simple example!

$ g++ -o attiny85sim src/main.cpp
$ ./attiny85sim
attiny85sim (v0.1.0)

Too Much Typing!

That was way too many characters to type. Even with the easy access to the command history in the terminal, we still have to type in those long commands at least once.

Since programmer’s are lazy (and creative), surely someone addressed that problem.

Stuart Feldman did just that, way back in 1976 (see Make History)!

Adding a Makefile

The tool that Stuart developed is known as Make, and the command you will learn to love is simply make!

Make is installed as part of the standard developer’s tool set on Mac/Linux, and can be added to any Windows system. Let.s verify that we have it installed:

Note

I write these notes on my Macbook, so what you see reflects my system:

$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for i386-apple-darwin11.3.0

The version of Make we will be using is the Gnu Make system. This is not the only such tool around, Microsoft has its own (they think better) version called nmake, which comes as part of Visual Studio. Since this tool is locked into the Windows world, I seldom use it!

Makefiles

Make uses a single text file to control what it does. It is vital that you understand that Make is not a tool that really knows much about programming. Instead, Make looks at the relationships among a set of files, and issues commands for you, based on changes in those files.

Huh?

Rather than get deeply into Make here, I will build up your understanding of this powerful tool by showing it in action, with simple examples. We will get more sophisticated in our use of Make as we proceed.

The one file we need to build so that Make can take control of our build process is called Makefile (or makefile).

Warning

Makefile does not have an extension, and it is a simple text file. There is one HUGE “gotcha” in writing these, though. The <TAB> character must be used in certain places. Watch for that!

Here is a first Makefile for our project:

Makefile
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
.PHONY: all
all:
	g++ -o attiny85sim src/main.cpp

.PHONY: run
run:    
	./attiny85sim

.PHONY: clean
clean:
	rm -f attiny85sim

Place this file at the top of your project repository, next to the src directory.

Those lines that begin with .PHONY: tell Make that it should not look for a file with that name in the project directory (where this Makefile is located). Actually, it will ignore such a file if it is there. More on that later!

The line below each of those “PHONY” lines is called a target. By default, the first such target will mark the place where Make begins looking for commands to run. You can have many targets in a makefile. Make will not run all of them, just those needed. This is what makes (!) it so powerful.

Note

Target lines start with a name followed by a colon. The name can be the name of a file that Make is to build, or (if marked as .PHONY:) it might just be the name of some set of commends you want Make to run for you. There will be many such lines in a typical Makefile. In our first example, all target lines are marked as .PHONY.

The actual command (or commands) we want Make to run for use begin on the next indented line. What you see there is something you could type, but do not want to!

Warning

Look closely at the indented line after a target name. The first character in that line absolutely MUST be a <TAB>! You can check that by moving the cursor with the arrow keys. If it hops to the beginning of the like when you move left, you are fine. If it steps by single spaces, Make will complain. In Vim I use <Ctrl-V><TAB> to insert a tab, since my editor configuration normally expands tabs to four spaces..

You can ask Make to run several commands in a row, by listing each one on a separate line, indenting each one with that evil <TAB> character.

Try this:

$ make
g++ -o attiny85sim src/main.cpp
$ make run
./attiny85sim
attiny85sim (v0.1.0)

Much better! We simply typed “make” and Make ran the command we specified for us. If we want to run a different target listed in our Makefile, we add the target name after “make”.

Warning

Be careful when adding commands to a Makefile. The commands can get very complex. If you ever want to see what Make will do, without actually running the commands, add -n after the command:

$ make clean -n
rm -f attiny85sim

If you check, attiny85sim is still present, since the command was not run by Make.

That last target is designed to eliminate anything we can rebuild later from the project directory. I always run that command before committing work with Git.

Commit this version

Time to get this code on GitHub!

Before we do that, we need to add one important file to this project:

Create this file at the top of your class project folder (or check it if it is already there):

.gitignore
# Executables
attiny85sim

# C/C++
*.o
*.a

# Python
*.py[cod]
__pycache__

# Vim
*~
*.swp

# Mac
.DS_Store

We do not want to add the attiny85sim executable file, since it is something we can build from the project source code.

Warning

Originally, I just added the executable name to the .gitignore file. Unfortunately, the executable file name happened to be the same as the project folder name I was using (not really that uncommon). Git happily ignored the entire project folder. Not Good! The solution was to specify the executable name more completely. This is shown in the above example.

Now, do the standard “mantra”:

$ git add .
$ git commit -m "completed version 0.1.0"
$ git push origin master

Tag this Version

Git creates a very ugly “name” for each commit you create. Formally, it is called a “hash code”. This is a 40 character hexadecimal code string that uniquely identifies a particular version of your project. (The odds on two projects having the same hash code as too small to even think about!)

Here is an example, from my test simulator project:

$ git log --pretty=oneline
9bd42d2e7e2f523badbb4437c255d7954c603540 basic Hello, World app added

The problem with these ugly names is that it is impossible for humans to remember them. We can just use the first few characters from those names and Git will figure out what name we mean, but even that is not much help.

What we want to do is attach a more “human” name to a version. We can do that simply by asking Git to tag our commit.

Do this:

$ git tag v0.1.0

That places a special marker on the project at this point in time. We now have a name we can remember: v0.1.0. See Project Versioning for more information on version numbers).

There is one problem with tags in Git. They are not automatically included when you push changes up to GitHub. To get your tags on GitHub, which you only need to do after creating a new tag, is run this command:

$ git push origin --tags

You can get a list of your current tags using this command:

$ git tag
v0.1.0

Reviewing this Step

This is not much of a start, but it is a good start! We have also learned a few new development tricks! The real simulator code comes next!