Tuesday, April 15, 2025

Entry 29: T-states and control signals

Introduction

In the previous entry, we looked at instructions and the instruction register. The current goal is to build a processor that can run the following program:

This program adds 5 and 3 and displays the result on LEDs. Also, we decided that the processor should support the following instructions:

In order to support these instructions, we need the following components (as a starting point):

  • See entry 26 and 27 for the program counter
  • See entry 21 for the memory system
  • See entry 28 for the instruction register

"A, B, OUT and ALU" (see entry 23):

Here is a summary of all the control pins we need to implement the 8 instructions above (in parenthesis, action when pin is high):

  • Program counter:
    • COUNT (increase count)
    • PCL (loads from the data bus)
    • PCW (writes to the address bus)
  • Memory system:
    • MARL (loads MAR from the address bus)
    • MDRL (loads MDR from the data bus)
    • RAML (loads data stored in MDR at address stored in MAR into RAM)
    • RAMW (writes data to data bus and address bus)
  • Instruction register
    • IRL (loads from the data bus)
  • A, B, OUT and ALU
    • RAL (loads register A from the data bus)
    • RBL (loads register B from the data bus)
    • ROL (loads register OUT from the data bus and activates LEDs)
    • RAW (writes A to the data bus)
    • ALUW (writes ALU to the data bus)

The next step is to build an "instruction decoder" so that the processor can "run the program" by itself. 

I didn't really follow the first answer by Grok on "instruction decoding" but the concept "T-state" was mentioned. So, let's start there.

T-states

"Is the instruction decoder related to "T states"? Grok:

Your question about whether the instruction decoder is related to T-states is spot-on, and it ties directly into how instructions are executed in a system like yours. Let’s break it all down, clarify the role of the instruction decoder, explain T-states, and design a decoder for your eight instructions (NOP, LDA, ADD, STA, JMP, HLT, MOV B, A, MOV OUT, A).

Instruction Set (Proposed): (Removed, see table above)


Instruction Format:

  • NOP, ADD, HLT, MOV B,A, MOV OUT,A: Single 4-bit opcode (4 bits total).
  • LDA, STA, JMP: Opcode + 4-bit address (8 bits, two fetches).

What Are T-States?

  • Definition: A T-state (Timing state) is one clock cycle in a processor’s operation, representing the smallest unit of time for executing part of an instruction. In the 8085, each instruction takes multiple T-states (e.g., LDA 2034H = 13 T-states), split across fetch, decode, and execute phases.
  • Purpose: T-states break down instruction execution into steps:
    • Fetch opcode from RAM.
    • Fetch operands (e.g., address for LDA).
    • Decode opcode.
    • Execute (e.g., load, add, store).

Instruction Decoder and T-States

  • Relation: The instruction decoder is intimately tied to T-states because it generates control signals that determine what happens in each T-state (e.g., clocking IR, enabling ALU, writing RAM). Each instruction requires a specific sequence of T-states, and the decoder ensures the right signals are active at the right time.

My summary

This is my summary, after feedback from Grok. It is a long summary, but this is the "gist" of the processor.
  • Each instruction requires a number of T-states to complete. For example, an instruction needing 4 T-states takes four clock cycles to execute.
  • T-states start with T-state 1, followed by T-state 2, and so on. After the final T-state, the processor returns to T-state 1 to fetch the next instruction.
  • In each T-state, relevant control pins (or, as they are also called, control signals) are set to perform specific actions, remaining stable throughout the T-state and changing at the start of the next.
  • A T-state begins on the clock’s falling edge (high-to-low from the 555 timer) and lasts until the next falling edge, ensuring that the control pins remain stabilized on the clock's rising edge (low-to-high). In this example, instruction 1 has 4 T-states and instruction 2 has 3 T-states:
  • For example, to move Register A’s contents to Register B in one T-state, the decoder enables A’s "Write" pin (RAW) and B’s "Load" pin (RBL) on the clock’s falling edge. A's data immediately goes on the data bus. However, B latches the data first on the following clock’s rising edge. This gives register A plenty of time to put the data onto the bus, ensuring a valid transfer. 
  • T-state 1 is identical for all instructions. The Instruction Register (IR) doesn’t yet hold the opcode. Control signals are set to transfer the Program Counter (PC) value to the Memory Address Register (MAR). The decoder sets PCW = 1 (PC writes to the address bus), and MARL = 1 (MAR loads from the address bus).
  • T-state 2 is also universal. Here, the opcode is moved from RAM to the IR, and the PC is incremented. RAM already has the correct address, due to T-state 1. Therefore, in T-state 2, the decoder sets RAMW = 1 (RAM writes data to both buses), IRL = 1 (IR loads from the data bus), and COUNT = 1 (PC increments its count).
  • T-state 3 and beyond depend on the opcode in the IR. For NOP, T-state 3 is idle, and the processor returns to T-state 1 for the next instruction’s fetch.
  • For MOV B,A, T-state 3 sets RAW = 1 and RBL = 1 as discussed in the example above. No further action is needed, so it returns to T-state 1 for the next instruction.
  • Thus, for all eight instructions—NOP, LDA, ADD, STA, JMP, HLT, MOV B, A, MOV OUT, A—we define how the decoder sets control signals in T-state 3 onward, while T-states 1 and 2 are uniformly handled.

Here is a complete list of control signals for all instructions in all T-states:

T-state 1: move the program count to MAR. T-state 2: move the opcode to IR and increase PC.

For example, consider STA. Since the PC was incremented in T-state 2, it is now pointing at the address at which to store the A register. So we move this address to MAR in T-state 3. In T-state 4, the decoder sets RAML = 1 and RAM will immediately load the data found in MDR into the correct address (the address in MAR). This is not the correct data, but that is OK. It also sets RAW = 1 so that the A register goes on the data bus. Since is sets MDRL = 1, MDR will load the correct data on the rising clock edge. RAM will then immediately write this (correct data) into the correct address. COUNT = 1 so the the PC increments, it will now point at the next opcode.

The next question is then, how do we build an instruction decoder which, depending on T-state and instruction, sets the correct control signals? Grok:

You can use ROM to store control signals for the instruction decoder, based on the current T-state and opcode. It aligns perfectly with how many real-world microprogrammed CPUs implement their control units. By using a ROM to map T-states and opcodes to control signals, you can centralize the instruction decoder’s logic, making it easier to modify or expand your 4-bit processor. In essence, ROM is a lookup table. Let’s explore why this works, design the ROM-based decoder, calculate its size, and define the control signals.

Next, let's figure out how ROM differs from RAM and then design the instruction decoder. Hopefully we can then make the processor run programs, like the one above.. in the next entry.




Entry 28: Instruction register

Instructions 

"Can you tell me about instructions, instruction register and instruction decoder?". Groks (curated) response:

Your setup is incredibly impressive—a fully functional 4-bit... (describing what we have so far). Adding instructions, an instruction register (IR), and an instruction decoder is the next logical step to turn your system into a basic processor capable of executing a program. Let’s dive into what these components are, how they work, and how you can integrate them into your setup to start running simple instructions.


What Are Instructions?

  • Definition: Instructions are the basic commands a processor executes, encoded as binary patterns stored in memory (your RAM). Each instruction tells the system what to do, like load data, add numbers, or jump to a new address.
  • In Your System: Instructions are 4-bit values (since your data bus is 4 bits), stored in the 16x4 RAM, addressed by the 4-bit PC (0000 to 1111).
  • Purpose: Define a program—e.g., load Register A, add to B, store result—executed sequentially or with jumps.
  • Example Instructions (4-bit):
    • 0001: Load A from RAM.
    • 0010: Add A and B, store in A.
    • 0011: Store A to RAM.

Instruction Format

  • 4 Bits: Limited to 16 unique instructions (0000 to 1111).
  • Encoding: Each 4-bit code maps to an operation (opcode). Some instructions might include a 4-bit address (requiring a second fetch).
  • Simple Set (Proposed):
    • 0000: NOP (no operation).
    • 0001: LDA addr (load A from RAM[addr]).
    • 0010: ADD (A = A + B).
    • 0011: STA addr (store A to RAM[addr]).
    • 0100: JMP addr (set PC to addr).
    • 0101: HLT (halts the program)
    • 0110: MOV B, A (move A to B)
    • 0111: MOV OUT, A  (move A to OUT)
    • More later as needed.

Example Program

Store in RAM:

  • 0000: 0001 (LDA 1000)
  • 0001: 1000 (address 1000)
  • 0010: 0110 (MOV B, A)
  • 0011: 0001 (LDA 1001)
  • 0100: 1001 (address 1001)
  • 0101: 0010 (ADD)
  • 0110: 0111 (MOV B, OUT)
  • 0111: 0101 (HLT)
  • 1000: 0101 (data: 5)
  • 1001: 0011 (data: 3)

My summary

  • An instruction tells the processor what to do. For example, "LDA 6" means "Load the register A with whatever number is found at address 6" and "ADD" means "Add the numbers in register A and B and put the sum in register A".
  • It is up to us to define which instructions the processor must understand, but the processor’s hardware (decoder, ALU, etc.) must support these instructions. 
  • Some instructions "stand by themselves" (they are "single-word instructions"). These are "NOP", "ADD", "HLT", MOV B, A, and MOV OUT, A. 
  • Some instructions have an "operand". For example, "LDA" by itself would be pointless. Therefore, it has an operand, which is a four bit address from which to get the number to put in A. STA and JMP also have an address as an operand. 
  • NOP, ADD, HLT, LDA, STA, JMP, MOV B, A, and MOV OUT, A are called opcodes. Each opcode is represented by a four bit binary number. NOP by 0000, LDA by 0001, ADD by 0010, and so on.
  • Instructions that stand by themselves occupy one address in RAM. The data at this address is simply the four bit binary representation of the opcode.
  • Instructions with an operand occupy two addresses in RAM. The first one is the opcode and the second one is the address.
  • This means that a number stored in RAM at a given address can mean many things. It can be an opcode, it can be an operand (address), and it can be data.

I did confirm this summary with Grok and the summary includes comments made by Grok. Here is a summary of the eight instructions that Grok suggests we start with:

Let's have a closer look at the example program, repeated here with some more details:

This program will add 5 and 3 and display the result, in binary, using 4 LEDs. When we start the "computer", PC is zero and the processor will execute the first instruction, "LDA 8", loading register A with the number 5. It will then move on to the next instruction, "MOV B,A". It keeps on going until it gets to address 7 and "HLT". Note that addresses 0 to 7 is used for code while addresses 8 and 9 is used for data.

All of this is so far "in theory". What we have so far cannot decode instruction. Let's move on to the instruction register.

Instruction register

What Is an Instruction Register (IR)?

  • Definition: The IR is a register that temporarily holds the current instruction (opcode) fetched from RAM during execution.
  • Purpose:
    • Stores the 4-bit instruction after it’s read from RAM (via the data bus).
    • Feeds the instruction decoder to generate control signals.
  • In Your System: A 4-bit register to hold the opcode, clocked when fetching from RAM.

How It Works

  • PC outputs address (e.g., 0001) to RAM via address bus.
  • RAM outputs instruction (e.g., 0010 = ADD) to data bus.
  • IR clocks, latching 0010 from data bus.
  • IR Q0–Q3 stay stable, driving the decoder.

What Is an Instruction Decoder?

(I removed this for now. For me, this was too difficult to understand.)

-end-

Adding an instruction register

Let's add an instruction register (IR). This is "just a regular register", see entry 15. But it has a special purpose: it will hold the next opcode. This setup is sufficient to experiment with IR:

We enter the program using the DIPs:

  • Addr: Data
  • 0000: 0001 
  • 0001: 1000 
  • 0010: 0110 
  • 0011: 0001 
  • 0100: 1001 
  • 0101: 0010 
  • 0110: 0111 
  • 0111: 0101 
  • 1000: 0101 
  • 1001: 0011 

Set AS and DS to ON to enter programming mode. Set address = 0000 and data = 0001 and click on "Load RAM", move on to address 1, and so on, until the program / data has been stored in RAM. Now, there is a way of cheating if we use ACEL. We can define 

    byte data[16] = {1,8,6,1,9,2,7,5,5,3,0,0,0,0,0,0};

and then we initialize the memory system with this data:

    auto* memorySys = new SixteenByFourMemorySystem(data);

But this is indeed cheating. If we were using real ICs, this would not be possible. We would have to program using the DIPs.

Try this out at Wokwi, project "Instruction Register":


This project is very similar to "Program counter and memory system", see entry 17. The only difference is that the OUT register is replaced by an instruction register. Try the following steps:

We want to move the opcodes to the IR
  1. Move the content of PC to MAR (address = 0 in RAM)
  2. Move the content of RAM at address 0 (the number 1) to the IR. IR now holds 0001 or "LDA"
  3. Increment PC twice
  4. Move the content of PC to MAR (address = 2 in RAM)
  5. Move the content of RAM at address 2 (number 6) to the IR. IR now holds 0110 or "MOV B,A"
  6. Increment PC
  7. Move the content of PC to MAR (address = 3 in RAM)
  8. Move the content of RAM at address 3 (the number 1) to the IR. IR now holds 0001 or "LDA"
  9. Continue until IR holds 0101 or "HLT"


    Click Print. Observe RAM and that correct data has been programmed.
    Move the first instruction:
      Click and hold PCW, MARL (press 3 and 5 on keyboard)
      Click Clock to latch
      Click and hold RAMW, IRL (press 7 and 8 on keyboard)
      Click Clock to latch
      Observe LEDs: 0001 means LDA
    Move the second instruction:
      Click and hold COUNT (4)
      Click Clock to latch twice
      Click and hold PCW, MARL (press 3 and 5 on keyboard)
      Click Clock to latch
      Click and hold RAMW, IRL (press 7 and 8 on keyboard)
      Click Clock to latch
      Observe LEDs: 0110 means MOV B,A
    Move the remaining instructions to the IR.
    Increment count by 1 or 2 depending on the opcode

The next step is to "decode" the instructions. This process is related to concept "T-state"... in the next entry.