Beginning Logic Design – Part 12

Hello and welcome to Part 12 of my Beginning Logic Design series! In the last post I implemented the LOAD and STORE sets of operations. In this round I will start to implement branching operations that allow the code to take different paths through a program.

Branching Out

One of the most important things a CPU needs is the ability to branch, or jump, to different program code based on some conditions. Imagine an if/else statement in most common programming languages. You have some condition that you evaluate, and based on that outcome you perform one set of operations or another.

The first step I want to take towards implementing this is adding my CPU flags that will represent the conditions that can be considered.

// CPU flags
logic zero;
logic sign;
logic overflow;
logic carry;

I’ll also modify my CPU’s RESET state to set these all to 0 on reset.

RESET: begin
  state <= FETCH;
  program_counter <= 'h8000;
  stack <= 0;
  read <= 0;
  write <= 0;
  address_bus <= 0;
  write_data <= 0;
  zero <= 0;
  sign <= 0;
  overflow <= 0;
  carry <= 0;
end

Next, I want to check to make sure the instructions I have defined so far are setting these flags as I’d like them to. Right now the only commands that should be modifying these flags are the LOAD commands, if the number loaded is 0, then the zero flag should be set. If the number loaded could be interpreted  as a negative number (it’s highest bit is 1), the sign flag should get set.

This is easily implement by adding this to the final cycle of each load command, right near where the register loaded is being set.

if (data_bus == 0)
  zero <= 1;
else
  zero <= 0;
sign <= data_bus[7];

I’ll write a test program to load A with 0, then load B it with ff (-1).

c0 00
c1 ff

In simulation, it looks good!

Now for the BRANCH operations themselves! For now I have 10 Branch operations I’d like to define:

0 - Halt
1 - Jump
2 - Branch if zero set
3 - Branch if zero unset
4 - Branch if sign set
5 - Branch if sign unset
6 - Branch if overflow set
7 - Branch if overflow unset
8 - Branch if carry set
9 - Branch if carry unset

These will be fairly quick to implement, as all of them are quite similar. First I will setup my overall case statement structure.

BRANCH: begin
  case (instruction[3:0])
    // Halt
    0: begin
    end
    // Jump
    1: begin
    end
    // Branch zero set
    2: begin
    end
    // Branch zero clear
    3: begin
    end
    // Branch sign set
    4: begin
    end
    // Branch sign clear
    5: begin
    end
    // Branch overflow set
    6: begin
    end
    // Branch overflow clear
    7: begin
    end
    // Branch carry set
    8: begin
    end
    // Branch carry clear
    9: begin
    end
  endcase
end

The HALT operation is dead simple, just change the CPU state to HALT

// Halt
0: begin
  state <= HALT;
end

I’ll give this operation a test shortly, first I want to implement my JUMP operation. The implementation of that begins pretty similarly to the other operations that look for a memory address, there will be 3 cycles. The first address byte will be requested; on the second cycle the first byte read and the second byte requested; on the last cycle the reading will stop and the program_counter will be set to its new value.

// Jump
1: begin
  case (cycle)
    0: begin
      read <= 1;
      address_bus <= program_counter + 1;
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end

Now, as a test, I’ll extend my last program that set the flags to include a JUMP call, after that instruction I’ll pad a few bytes with FF and at 8010 I’ll have my HALT instruction.

c0 00
c1 ff
e1 80 10
ff ff ff ff ff ff ff ff ff
e0

Testing it in the simulator it works like a charm!



Conditional Branches

The conditional branches are fairly simple to implement, I just took my JUMP implementation and added a condition on the flag during the first cycle. If the condition is not met, we can modify the program counter to start the fetch of the next instruction

// Branch zero set
2: begin
  case (cycle)
    0: begin
      if (zero) begin
        read <= 1;
        address_bus <= program_counter + 1;
      end else begin
        program_counter += 3;
        state <= FETCH;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end

This is basically the same for the Branch zero clear operation, the only change is a flipping of the if/else statements.

// Branch zero clear
3: begin
  case (cycle)
    0: begin
      if (zero) begin
        program_counter += 3;
        state <= FETCH;
      end else begin
        read <= 1;
        address_bus <= program_counter + 1;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end

The remaining operations are a very slight derivation of these two, some copy-pasta will do the trick and the only change is what flag is being looked at in the if condition.

// Branch sign set
4: begin
  case (cycle)
    0: begin
      if (sign) begin
        read <= 1;
        address_bus <= program_counter + 1;
      end else begin
        program_counter += 3;
        state <= FETCH;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end
// Branch sign clear
5: begin
  case (cycle)
    0: begin
      if (sign) begin
        program_counter += 3;
        state <= FETCH;
      end else begin
        read <= 1;
        address_bus <= program_counter + 1;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end
// Branch overflow set
6: begin
  case (cycle)
    0: begin
      if (overflow) begin
        read <= 1;
        address_bus <= program_counter + 1;
      end else begin
        program_counter += 3;
        state <= FETCH;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end
// Branch overflow clear
7: begin
  case (cycle)
    0: begin
      if (overflow) begin
        program_counter += 3;
        state <= FETCH;
      end else begin
        read <= 1;
        address_bus <= program_counter + 1;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end
// Branch carry set
8: begin
  case (cycle)
    0: begin
      if (carry) begin
        read <= 1;
        address_bus <= program_counter + 1;
      end else begin
        program_counter += 3;
        state <= FETCH;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end
// Branch carry clear
9: begin
  case (cycle)
    0: begin
      if (carry) begin
        program_counter += 3;
        state <= FETCH;
      end else begin
        read <= 1;
        address_bus <= program_counter + 1;
      end
    end
    1: begin
      address_bus <= program_counter + 2;
      x <= data_bus;
    end
    2: begin
      read <= 0;
      program_counter <= {x,data_bus};
      state <= FETCH;
    end
  endcase
end

That’ll do it for the basic branching operations! In the next post I will begin the implementation of  the operations that will utilize the ALU. As always, I welcome your feedback and questions in the comments. Keep tinkering!

Beginning Logic Design – Part 11

Hello and welcome to Part 11 of my Beginning Logic Design series. In this episode, I will continue implementing the CPU I planned and stared in the previous post.

The first CPU operations I’d like to have working are going to be my LOAD and STORE type instructions, as these provide the basic reading and writing operations to interact with my system bus. This design will not be very efficient nor the most clever implementation, but it will work!



The LOAD Instruction

I want each of my registers to have the same LOAD capabilities, in contrast to the 6502 instruction set which has 8 types of LOAD instructions for A and 5 for X and Y.

I have 5 types of load instruction in mind:

  • Load register with next byte in program code (Immediate Load)
  • Load register using next two bytes of program code as a memory address. (Memory Load)
  • Load register using next byte as the upper 4 bits of a memory address, and the A register as the lower 4 bits (A indexed load)
  • Load register using next byte as the upper 4 bits of a memory address, and the B register as the lower 4 bits (B indexed load)
  • Load register using next byte as the upper 4 bits of a memory address, and the C register as the lower 4 bits (C indexed load)

With my 3 registers and these 5 different types of load instructions, this consumes 15 of the 16 possible LOAD instructions.

0 - Immediate Load A
1 - Immediate Load B
2 - Immediate Load C
3 - Memory Load A
4 - Memory Load B
5 - Nemory Load C
6 - A Index Load A
7 - A Index Load B
8 - A Index Load C
9 - B Index Load A
a - B Index Load B
b - B Index Load C
c - C Index Load A
d - C Index Load B
e - C Index Load C
f - undefined

Immediate Load

With a rough plan, I’m ready to start implementing! My first goal is to just get the Immediate Load A instruction to work. I’ll write a small program that should load A with 00 then load it with 42.

c0 00
c0 42

Now I’ll start building out what will end up being a huge tree of case statements implementing my various operations. This isn’t the most elegant way to organize the code, but it’s simple and it will work for a start.

PERFORM: begin
  case (op_type)
    LOAD: begin
      case (instruction[3:0])
        0: begin
          if (!read) begin
            read <= 1;
            address_bus <= program_counter + 1;
          end else begin
            read <= 0;
            a <= data_bus;
            program_counter += 2;
            state <= FETCH;
          end
        end
      endcase
    end
  endcase
end

Similar to the fetch state this will be a two cycle operation. First the CPU starts a memory read request for the next byte in program code, on the next cycle the result is stored into the program counter. The program_counter then gets incremented to the next address after the opcode and its parameter.

Testing this bit of code in simulation verifies it works as intended!

From here we can use our copy-pasta skills to do the same for the immediate load operations for the B and C registers.

LOAD: begin
  case (instruction[3:0])
    0: begin
      if (!read) begin
        read <= 1;
        address_bus <= program_counter + 1;
      end else begin
        read <= 0;
        a <= data_bus;
        program_counter += 2;
        state <= FETCH;
      end
    end
    1: begin
      if (!read) begin
        read <= 1;
        address_bus <= program_counter + 1;
      end else begin
        read <= 0;
        b <= data_bus;
        program_counter += 2;
        state <= FETCH;
      end
    end
    2: begin
      if (!read) begin
        read <= 1;
        address_bus <= program_counter + 1;
      end else begin
        read <= 0;
        c <= data_bus;
        program_counter += 2;
        state <= FETCH;
      end
    end
  endcase
end

I’ll write a new program to test this out:

c0 aa
c1 bb
c2 cc

Stepping through these opcodes, I should end up with the A register set to aa, B to bb and C to cc.

Woohoo! These load commands work and were not too difficult to implement. At this point I’m feeling pretty excited about my first CPU design.

Memory Load

For my next trick, I will implement my memory load operations. These will fetch a memory address after the current instruction and set the register to the number at that location. This operation is going to take more than two CPU cycles. With this in mind, I’m going to add a new internal variable logic [1:0] cycle; to track each CPU cycle. In my FETCH, state, I will set cycle to 0 as I transition to the PERFORM state so that all instructions can use this same variable. After the main case statement within PERFORM, I’ll add cycle++; to increment cycle every clock cycle.

Next, before I implement my memory load operations, I’ll modify the immediate load implementation to follow this model for consistency.

LOAD: begin
  case (instruction[3:0])
    0: begin
      case (cycle)
        0: begin
          read <= 1;
          address_bus <= program_counter + 1;
        end
        1: begin
          read <= 0;
          a <= data_bus;
          program_counter += 2;
          state <= FETCH;
        end
      endcase
    end
  ...

Now for the memory load! It will start off identical to the immediate load by reading the next byte in code.

// Memory load A
3: begin
  case (cycle)
    0: begin
      read <= 1;
      address_bus <= program_counter + 1;
    end
  endcase
end

On the next cycle I’ll have the most significant address byte returned via the data bus and I’ll need another register to store it. I’ll add logic [7:0] x; near my other CPU internal registers and request the next byte.

1: begin
  x <= data_bus;
  address_bus <= program_counter + 2;
end

On the 3rd cycle, I’ll have the lower address byte and can concatenate it with the x register to read that memory address.

2: begin
  address_bus <= {x,data_bus};
end

Finally, on the last cycle of the operation, I will have the value from the specified memory location on the data_bus. I can store that value, increment the program_counter by the total length of the instruction, clear the read signal and transition back into FETCH.

3: begin
  program_counter += 3;
  read <= 0;
  a <= data_bus;
  state <= FETCH;
end

Now to test it! I’ll extend my previous program to include this new operation. It’ll load the first byte of the program into A.

Alright! It does successfully pull the memory address and uses it to load the value at that address into the register. With some more copy paste I can replicate this for the B and C registers.

Offset Memory Load

With the basic memory load operation figured out, the offset memory load is a small modification. I only need to read the most significant address byte then I can concatenate that with the appropriate register to read the desired offset address.

// A offset load A
6: begin
  case (cycle)
    0: begin
      read <= 1;
      address_bus <= program_counter + 1;
    end
    1: begin
      address_bus <= {data_bus, a};
    end
    2: begin
      program_counter += 2;
      read <= 0;
      a <= data_bus;
      state <= FETCH;
    end
  endcase
end

As before I can duplicate this for the various permutations of the load command. I validated this in the simulator as well and it looks to work just as intended.

Store Operations

The STORE operations are nearly identical to the load operations, though there are not Immediate Store instructions. Because the operations are so similar I will actually even use the same numbers for the lower 4 operation bits to indicate the types of operation.

0 - undefined
1 - undefined
2 - undefined
3 - Memory Store A
4 - Memory Store B
5 - Nemory Store C
6 - A Index Store A
7 - A Index Store B
8 - A Index Store C
9 - B Index Store A
a - B Index Store B
b - B Index Store C
c - C Index Store A
d - C Index Store B
e - C Index Store C
f - undefined

I’ll first implement the Memory Store A operation. It starts off pretty similar to the load, as it needs to begin by reading the memory address from the program code.

0: begin
  read <= 1;
  address_bus <= program_counter + 1;
end
1: begin
  x <= data_bus;
  address_bus <= program_counter + 2;
end

On the next cycle, I’ll have the full address and can stop reading to start writing A to the data_bus. On the last cycle I’ll increment the program counter and return to FETCH to grab the next bit of code.

2: begin
  address_bus <= {x,data_bus};
  read <= 0;
  write <= 1;
  write_data <= a;
end
3: begin
  program_counter += 3;
  write <= 0;
  state <= FETCH;
end

Easy enough! I’ll extend my last program to end with an operation to write A to the first byte of my RAM.

c0 02
c6 80
d3 00 00

Via simulation I can confirm it’s stashing the A register into the first byte of RAM.

As before we can use this as the basis for the memory store calls for the B and C registers.

Offset Store Operations

As the offset load was a small variation on memory load, the same will be true for offset store. With some small modifications to the regular memory store call, the offset store is easily implemented.

// A offset store A
6: begin
  case (cycle)
    0: begin
      read <= 1;
      address_bus <= program_counter + 1;
    end
    1: begin
      address_bus <= {data_bus, a};
      read <= 0;
      write <= 1;
      write_data <= a;
    end
    2: begin
      program_counter += 2;
      write <= 0;
      state <= FETCH;
    end
  endcase
end

 

With the STORE and LOAD operations implemented I will call it a wrap for this post. As always I would love any feedback or questions you may have. Keep tinkering!

Save

Save

Beginning Logic Design – Part 10

Hello and welcome to part 10 of my Beginning Logic Design series!

There’s been a lot of ground covered so far. Through these posts I’ve implemented an ALU, a system bus, a RAM module, a ROM module. I explored state machines in the last post to build a design that can implement a more complex processing flow by breaking a process into individual operations.

All of this has laid out significant foundation to implement the final design of the series. A basic CPU and computer system!

Over the next few posts I will cover my planning, design and testing for this CPU. The planning will be very light, and this will certainly be an inefficient design in many ways… but I’ll keep changing it until it works!



System Design

I’m starting this design by planning my system architecture, how my largest building blocks will communicate.

For this design I’ve decided to give my ROM the full upper half of my address space, 32KB of ROM. I decided to split the lower half between 16KB of RAM and 16KB of I/O space. I felt this was a good balance of the address space and it’ll be easy to implement.

My system architecture

CPU Design and Instruction Set

There are many decisions to make in designing a CPU and computer system. For this design I’m going to build an 8-bit CPU with a 16-bit system bus to stick with my previous posts.

It will have 4 general purpose registers to use with the various instructions, they will be referred to as A, B and C.

Internally there will also be a PC (program counter) register to keep track of the memory address of the current instruction. An 8-bit stack register, S, will be held for stack related operations. There will be an instruction register to hold the current CPU instruction. It will also have the same status flags as my ALU had, zero, sign, overflow and carry.

The next major piece I have in mind is the instruction set architecture (ISA) itself. The ISA describes my machine code language that the processor will support. I’ve stirred over my ISA design and feel pretty certain that I will end up implementing some operations I don’t need while not thinking of others that would be extremely useful. I accept this and will push forward with a less elegant design to learn the lessons that could be applied to a future one.

My instruction format is split between 4 bits for the instruction family, and 4 bits to be interpreted different by the instruction.

Here are the instruction families I’m initially running with.

0:  ADD
1:  SUBTRACT
2:  INCREMENT
3:  DECREMENT
4:  BIT_AND
5:  BIT_OR
6:  BIT_XOR
7:  BIT_NOT
8:  SHIFT_LEFT
9:  SHIFT_RIGHT
10: ROTATE_LEFT
11: ROTATE_RIGHT
12: LOAD
13: STORE
14: BRANCH
15: EXTRA

The first 12 operations are intentionally identical to the ALU operations, the latter 4 are for loading and storing register values, program code branching and other miscellaneous processor operations.

The CPU will also have a state machine to manage its flow of operation.

CPU state flow

I think that’s about enough planning for now, I want to start laying down the foundations so I can begin the actual designing.

Ground Work

I’m going to use my go-to Makefile to organize my building and testing.

Then I will setup my top module to include my CPU and system bus components.

`timescale 1ns / 1ps

module top ();
  logic clock;
  logic reset;

  // System Bus
  logic slave_clock;
  logic read;
  logic write;
  logic [15:0] address_bus;
  wire logic [7:0] data_bus;

  assign slave_clock = ~clock;

  cpu processor (
    reset,
    clock,
    read,
    write,
    address_bus,
    data_bus
  );

  ram memory (
    slave_clock,
    read,
    write,
    address_bus,
    data_bus
  );

  io devices (
    slave_clock,
    read,
    write,
    address_bus,
    data_bus
  );

  rom storage (
    slave_clock,
    read,
    write,
    address_bus,
    data_bus
  );

  initial begin
    clock = 0;
    reset = 1;
    #2 reset = 0;
  end

  always begin
    #1 clock = ~clock;
  end

endmodule

Then I’ll start my cpu module with a few of the planned features. I’ll include my cpu_state enumeration, the various registers/flags and I’ll add support for system bus write operations.

`timescale 1ns / 1ps

typedef enum logic [1:0] {
  RESET,
  FETCH,
  PERFORM,
  HALT
} cpu_state;

module cpu (
  input logic reset,
  input logic clock,
  output logic read,
  output logic write,
  output logic [15:0] address_bus,
  inout logic [7:0] data_bus
);
  // CPU internals
  cpu_state state;
  logic [15:0] program_counter;
  logic [7:0] stack;
  logic [7:0] instruction;

  // General purpose registers
  logic [7:0] a;
  logic [7:0] b;
  logic [7:0] c;

  // System Bus support
  logic [7:0] write_data;
  assign data_bus = write ? write_data : 'bZ;

endmodule

I’ll next pull in my RAM module from post 8. I’ll modify it slightly to look at the first two address bits and to contain 16KB of memory instead of 32KB.

`timescale 1ns / 1ps

module ram (
  input logic clock,
  input logic read,
  input logic write,
  input logic [15:0] address_bus,
  inout logic [7:0] data_bus
);

  assign data_bus = (read && address_bus[15:14] == 0) ? memory[address_bus[13:0]] : 'bZ;

  logic [7:0] memory [0:(1<<14)-1];

  always_ff @ (posedge clock) begin
    if (address_bus[15:14] == 0 && write) begin
      memory[address_bus[13:0]] <= data_bus;
    end
  end


endmodule

I’ll also modify the writer module from post 8 to listen to the appropriate addresses and will change the output format to show both the address and the data.

`timescale 1ns / 1ps

module io (
  input logic clock,
  input logic read,
  input logic write,
  input logic [15:0] address_bus,
  inout logic [7:0] data_bus
);

  always_ff @ (posedge clock) begin
    if (address_bus[15:14] == 1 && write) begin
      $display("%h: %h (%c)", address_bus, data_bus, data_bus);
    end
  end


endmodule

The ROM module can be directly pulled in from the previous post.

`timescale 1ns / 1ps

module rom (
  input logic clock,
  input logic read,
  input logic write,
  input logic [15:0] address_bus,
  inout logic [7:0] data_bus
);

  assign data_bus = (read && address_bus[15] == 1) ? memory[address_bus[14:0]] : 'bZ;

  logic [7:0] memory [0:(1<<15)-1];

  initial begin
    $readmemh("rom.hex", memory);
  end


endmodule

With these base module in place, I attempt a build to catch the various syntax errors I’ve made along the way to ensure the code I’m sharing actually works 🙂

Processor Functionality

With the foundation set, we can start building! The first part I want to implement is the case statement to handle the various states and the reset logic.

always_ff @ (posedge clock or posedge reset) begin
  if (reset) begin
    state <= RESET;
  end else begin
    case (state)
      RESET: begin
        state <= FETCH;
        program_counter <= 'h8000;
        stack <= 0;
        read <= 0;
        write <= 0;
        address_bus <= 0;
        write_data <= 0;
      end
      FETCH: begin
        state <= PERFORM;
      end
      PERFORM: begin
        state <= HALT;
      end
      HALT: begin
        $finish();
      end
    endcase
  end
end

For now the processing loop just progresses one step at a time until it halts, via simulation I can verify all the important things are cleared, and I can see what’s left unset as well.

Next up, I want to start fetching instructions from ROM similar to how I fetched data from ROM in the previous post. I have the program_counter register as my instruction pointer. The fetch will have 2 cycles, a read request, and the read itself. Since an operation will eventually transition back to FETCH and this state never writes, I’ll ensure write  is set low here as well.

FETCH: begin
  write <= 0;
  if (!read) begin
    read <= 1;
    address_bus <= program_counter;
  end else begin
    read <= 0;
    instruction <= data_bus;
    state <= PERFORM;
  end
end

So this should fetch our first instruction, I’ll test it! Here’s my first ROM (in hex format):

de ad be ef

It’s a total garbage program 🙂 but it’s just here to make sure our instruction register becomes set to de, the first byte. In simulation it does work just fine, as FETCH transitions to PERFORM my instruction register becomes de!

Now, to more easily identify what this de instruction is, I’m going to add another enumeration for my operation family.

typedef enum logic [3:0] {
  CPU_ADD,
  CPU_SUBTRACT,
  CPU_INCREMENT,
  CPU_DECREMENT,
  CPU_AND,
  CPU_OR,
  CPU_XOR,
  CPU_NOR,
  CPU_SHIFT_LEFT,
  CPU_SHIFT_RIGHT,
  CPU_ROTATE_LEFT,
  CPU_ROTATE_RIGHT,
  LOAD,
  STORE,
  BRANCH,
  EXTRA
} instruction_type;

Then, in my CPU internals, I’ll add an instance of this type. My intention is that this will refer to the 4 most significant bits of my instruction register. I’ll add instruction_type op_type; to my CPU internal variables.

Then, I’ll use $cast(); to map the upper 4 bits of the 8-bit logic type to my instruction_type variable. If I did not cast this, Vivado would be unhappy with me. I’ll put this bit of code in my FETCH state.

FETCH: begin
  write <= 0;
  if (!read) begin
    read <= 1;
    address_bus <= program_counter;
  end else begin
    read <= 0;
    instruction <= data_bus;
    $cast(op_type, data_bus[7:4]);
    state <= PERFORM;
  end
end

Now I’ll check this in simulation.

Huzzah! I have fetched the instruction and can identify it’s family. Here’s my cpu module at this point:

`timescale 1ns / 1ps

typedef enum logic [1:0] {
  RESET,
  FETCH,
  PERFORM,
  HALT
} cpu_state;

typedef enum logic [3:0] {
  CPU_ADD,
  CPU_SUBTRACT,
  CPU_INCREMENT,
  CPU_DECREMENT,
  CPU_AND,
  CPU_OR,
  CPU_XOR,
  CPU_NOR,
  CPU_SHIFT_LEFT,
  CPU_SHIFT_RIGHT,
  CPU_ROTATE_LEFT,
  CPU_ROTATE_RIGHT,
  LOAD,
  STORE,
  BRANCH,
  EXTRA
} instruction_type;

module cpu (
  input logic reset,
  input logic clock,
  output logic read,
  output logic write,
  output logic [15:0] address_bus,
  inout logic [7:0] data_bus
);
  // CPU internals
  cpu_state state;
  logic [15:0] program_counter;
  logic [7:0] stack;
  logic [7:0] instruction;
  instruction_type op_type;

  // General purpose registers
  logic [7:0] a;
  logic [7:0] b;
  logic [7:0] c;

  // System Bus support
  logic [7:0] write_data;
  assign data_bus = write ? write_data : 'bZ;

  always_ff @ (posedge clock or posedge reset) begin
    if (reset) begin
      state <= RESET;
    end else begin
      case (state)
        RESET: begin
          state <= FETCH;
          program_counter <= 'h8000;
          stack <= 0;
          read <= 0;
          write <= 0;
          address_bus <= 0;
          write_data <= 0;
        end
        FETCH: begin
          write <= 0;
          if (!read) begin
            read <= 1;
            address_bus <= program_counter;
          end else begin
            read <= 0;
            instruction <= data_bus;
            $cast(op_type, data_bus[7:4]);
            state <= PERFORM;
          end
        end
        PERFORM: begin
          state <= HALT;
        end
        HALT: begin
          $finish();
        end
      endcase
    end
  end

endmodule

With a significant start to the organization and flow to this CPU, I will call that a wrap for this post. In the next post I will begin implementing some of the planned instructions. As always, I welcome your feedback and questions in the comments. Keep tinkering!