Introduction

Towards programmability

After Round 4: Sequential logic, state and feedback we know how to maintain state and how to evolve it using sequential logic. In particular, we have a preliminary 16-bit computer architecture consisting of a processor data path interfaced with a memory unit with \(2^{16} = 65536\) words of memory.

We have also agreed on conventions on how to configure the data path with 16-bit (and in some cases 32-bit) binary instructions. As soon as we have an instruction toggled in, we can evolve the state by triggering the clock.

In short, after Round 4: Sequential logic, state and feedback we have what is in principle an extremely versatile machine, but in practice we are still short of programmability because we are forced to toggle in the instructions manually, one instruction at a time.

Storing the program in memory

But what if we stored the instructions in memory?

Say, suppose the first 5 words of memory contained the following binary data (decoded for convenience on the right):

00000: 0000000000011010 mov $0, [imm]
00001: 0011000000111001 [imm: 12345]
00002: 0000000001011010 mov $1, [imm]
00003: 0101101110100000 [imm: 23456]
00004: 0001000010000110 add $2, $0, $1

Automating execution

And suppose the processor was able to automatically load instructions from memory and execute the instructions, one by one?

If you think about it carefully enough, we already have all the building blocks available to implement automatic loading of instructions from memory. We just need to modify and augment our processor design somewhat. So let us follow this intuition and reflect in more detail on what we already have and what we need to implement.

We already have a memory interface unit that loads from memory and stores to memory. The 16-bit words that we load from memory are just bits, which may be user data or contain an instruction. The loaded data can be fed to the instruction decoder just as well as a toggled-in instruction, so it would appear that relatively little implementation work is required to upgrade the design. Assuming of course that the machine is able to load the next instruction, and the next instruction, and so forth.

What we need to implement is the logic that controls the execution of the program, and in particular the combinational logic that determines from which memory address to load the next instruction. This address is then input to the memory interface unit and a memory read is issued to memory. When the instruction (the data that was read from memory) is available, we proceed to execute the instruction by giving the instruction to the instruction decoder unit. The current processor state then determines the memory address of the next instruction to be executed. This will all take place automatically, in sequential logic, as fast as the clock ticks.

This is the gist of a programmable computer (or more accurately, a stored-program computer).

A program is a sequence of instructions in memory. The program is executed by the processor, one instruction at a time, so that the address of the next instruction to be executed is determined by the current processor state and combinational logic.

Resolving the mystery of programmability – a roadmap for this round

Our goal for this round is to complete the armlet architecture, and then of course to program and play with the machine to gain confidence that it really is programmable, as per our initial motivation to resolve the mystery of the programmable computer, as a machine.

This is achieved in the two first sections: The armlet processor and Programming the armlet.

After our careful development and study of the armlet, we will in fact discover that this study helps us in understanding how the Scala programming language itself gains its power to control the hardware. In particular, we will walk through the process how programs written in Scala get automatically compiled into machine code executed by the physical processor in From Scala to hardware (*).

Finally, we will be able to conclude the module with Mystery resolved?.