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!

Leave a Reply

Your email address will not be published. Required fields are marked *