BF instruction set:¶

> Increment data pointer by one (move to the "right") < Decrement data pointer by one (move to the "left") + Increment byte at data pointer - Decrement byte at data pointer , Read one byte of input into byte at data pointer . Output byte at data pointer [ if byte at data pointer is zero, jump to instruction beyond matching ] * ] if byte at data pointers is nonzero, jump to instruction after matching [ * * Brackets can be nested, and it is illegal for an open- or close-bracket to be used without it's match.

Note that any non-command character in the program is simply ignored (a noop).

Also note that we are not concerned (yet) with verifying that the input program is sane - this is assumed to be the case.

The BF Machine¶

The BF spec/description only calls for a minimal set of resources:

memory of some form to hold the program

an instruction pointer into the program memory

an array of at least (only?) 30,000 data cells (bytes)

a data pointer into the array of data memory

two byte streams, one each for input and output

Program execution begins with the first ("leftmost") instruction in the program (instruction pointer is initialized to zero) and proceeds sequentially to the right until the last instruction has been executed (except for cases where the instruction being executed results in a different movement of the instruction pointer, ie: the looping instructions).

Our Proposed Design¶

Our hardware design will consist of a slightly more complex setup compared to the theoretical BF machine model, out of necessity (we're trying to design for actual hardware here, not just a theoretical soft model). Since MyHDL allows us to mix and match regular/high-level Python with lower level mechanisms which would mirror actual hardware components... we could just pipe the BF input through a function that maps it's instructions directly to normal/high-level python code and be done with it.

But that would defeat the whole purpose of this excercise, which is to simulate actual hardware that will be implement-able on a real world FPGA.

As such, we're going to stick to something called Register Transfer Level mechanisms in our simulation - MyHDL constructs which map more or less directly to low-level hardware abstractions at the level of basic logic and signals that will be present in a real digital circuit.

Sequential vs. Combinatorial Logic¶

Quick primer on the difference: combinatorial logic elements have output which depends solely on their inputs. Sequential logic elements, in contrast, have outputs which depend on their inputs and prior state. Ie: sequential logic elements have/use memory/storage.

Example Sequential Operations:

Incrementing or Decrementing a value (depends on prior value)

Looping (depends on location of begin/end of loop, nesting, etc)

Writing data to memory (depends on value of a data pointer (address) already being set/stable)

Example Combinatorial Operations:

Reading instructions/data onto a bus (output to bus is linked directly to input address)

MyHDL provides both - the @always_comb decorator is used to produce combinatorial elements, and @always_seq is used for sequential (usage/etc for both will be explained later).

We're going to model a very simple system where both data and instruction buses are loaded immediately (ie: in a combinatorial fashion) whenever their respective pointers change - if you increment the data pointer, the data byte on the bus is automatically updated, etc.

We'll have a simple symmetrical clock signal to drive the sequential elements, which will be: the writing of any data to memory (if necessary) at the end of a clock cycle, the execution of the current instruction at the beginning of a clock cycle, and the advancement of the instruction pointer at the end of a cycle.

We'll need a set of registers to hold various state inside the processor. In the interest of keeping most of the background detail in one place, I've listed them all here with brief descriptions; each will be further explained later as we come across them.

The following registers are all boolean in nature (flags):

CLK Our clock signal. SE "Scan Enable" - signifies that we're performing a search through the instructions to find matching brackets for the purpose of looping. SD "Scan Direction" - indicates whether we're moving forwards or backwards when advancing the instruction pointer (IP). WE "Write Enable" - indicates when the current byte on the data bus (DBUS) should be written to memory at DP.

We will also need the following single-byte registers:

IP Instruction pointer DP Data pointer SA Scan accumulator - keeps track of how many open/close brackets we've seen while scanning for a match.

And these single-byte buses:

IBUS Holds the current instruction. DBUS Holds the current data byte.