# Beginning Logic Design – Part 13

Hello and welcome to Part 13 of my Beginning Logic Design series! In the last post I implemented my branch instructions. For this round, I want to implement my ALU operations.

# ALU Instructions and Arguments

For my ALU, I want to follow a slightly different pattern for my arguments. In the instructions implemented so far the lower 4 bits of the instruction represented a certain operation within the instruction family. For the ALU operations I’d like to use these 4 bits to instead represent the operands of the instruction.

With the 4  bits available, I’ll use 2 bits to encode each operand with the following representations:

1. 00 - A register
2. 01 - B register
3. 10 - C register
4. 11 - Unused

So the overall format (in binary) of these ALU instructions will be `iiiiaabb`. Where `i` represents the instruction, `a` the first encoded operand and `b` for the second.

For all of the ALU instructions, I will use the second operand to indicate where the result will be stored. The instructions `ADD`, `SUBTRACT`, `BIT_AND`, `BIT_OR` and `BIT_XOR` all use two operands, so the second operand is used in the instruction and is where the result is stored. For the remaining operations `INCREMENT`, `DECREMENT`, `BIT_NOT`, `SHIFT_LEFT`, `SHIFT_RIGHT`, `ROTATE_LEFT` and `ROTATE_RIGHT` the first operand is used in the operation and the second is where the result is to be stored.

# Wiring up the ALU

The first thing I’ll need to build instructions for the ALU, will be to actually include it in the processor!

First, near the top of my `cpu.sv` file I’ll include my ALU package.

1. import ALU::*;

Next, inside my `cpu` module, just under the other internal declarations, I’ll add signals to interface with my ALU and the ALU instance itself.

1. // ALU signals and module
2. logic alu_clock;
3. opcode alu_operation;
4. logic [7:0] alu_a;
5. logic [7:0] alu_b;
6. logic alu_carry_in;
7. logic [7:0] alu_y;
8. logic alu_zero;
9. logic alu_sign;
10. logic alu_carry_out;
11. logic alu_overflow;
12. assign alu_clock = !clock;
13. alu cpu_alu (
14. alu_clock,
15. alu_operation,
16. alu_a,
17. alu_b,
18. alu_carry_in,
19. alu_y,
20. alu_zero,
21. alu_sign,
22. alu_carry_out,
23. alu_overflow
24. );

I’ve set my `alu_clock` to follow an inverted clock similar to how the system bus operates.

Next, within my `FETCH`  CPU state, I’ll add another `\$cast()` call to set my `alu_operation` to be upper four bits of my current instruction, just like I have for my `op_type` since I mapped the same values for CPU and ALU operations. There are some possible edge cases where the CPU operation will map to a number that has no meaning to the ALU, so we’ll add a sanity check to make sure it’s within the supported range.

1. if (data_bus[7:4] < 15)
2. \$cast(alu_operation, data_bus[7:4]);

That’ll get the basics in place for the ALU.

# Implementing ALU operations

The first instruction I want to get setup is the `ADD` instruction.

In the first cycle of `ADD`, I’ll set my ALU variables to match the registers specified in the instruction as well as passing in our current `carry` flag:

2. case(cycle)
3. 0: begin
4. case(instruction[3:2])
5. 0: alu_a <= a;
6. 1: alu_a <= b;
7. 2: alu_a <= c;
8. endcase
9. case(instruction[1:0])
10. 0: alu_b <= a;
11. 1: alu_b <= b;
12. 2: alu_b <= c;
13. endcase
14. alu_carry_in <= carry;
15. end
16. endcase
17. end

On the next cycle our ALU will have presented its results so we can, in a similar fashion, store the result and set the modified flags.

1. 1: begin
2. case(instruction[1:0])
3. 0: a <= alu_y;
4. 1: b <= alu_y;
5. 2: c <= alu_y;
6. endcase
7. carry <= alu_carry_out;
8. zero <= alu_zero;
9. sign <= alu_sign;
10. overflow <= alu_overflow;
11. program_counter++;
12. state <= FETCH;
13. end

Getting the `ADD` to work was just that easy, but better yet this pattern also works for `SUBTRACT`! We can just let both operations follow this same `case` statement.

1. CPU_ADD, CPU_SUBTRACT: begin
2. case(cycle)
3. 0: begin
4. case(instruction[3:2])
5. 0: alu_a <= a;
6. 1: alu_a <= b;
7. 2: alu_a <= c;
8. endcase
9. case(instruction[1:0])
10. 0: alu_b <= a;
11. 1: alu_b <= b;
12. 2: alu_b <= c;
13. endcase
14. alu_carry_in <= carry;
15. end
16. 1: begin
17. case(instruction[1:0])
18. 0: a <= alu_y;
19. 1: b <= alu_y;
20. 2: c <= alu_y;
21. endcase
22. carry <= alu_carry_out;
23. zero <= alu_zero;
24. sign <= alu_sign;
25. overflow <= alu_overflow;
26. program_counter++;
27. state <= FETCH;
28. end
29. endcase
30. end

It almost supports `SHIFT_RIGHT`, `ROTATE_LEFT` and `ROTATE_RIGHT` too, as these operations should also set most of these same flags. The issue is that `ADD` and `SUBTRACT` affect the `overflow` flag, so I’ll use my powers of copy-pasta to separate those into a case that doesn’t set `overflow`, but is otherwise identical.

1. CPU_SHIFT_RIGHT, CPU_ROTATE_LEFT, CPU_ROTATE_RIGHT: begin
2. case(cycle)
3. 0: begin
4. case(instruction[3:2])
5. 0: alu_a <= a;
6. 1: alu_a <= b;
7. 2: alu_a <= c;
8. endcase
9. case(instruction[1:0])
10. 0: alu_b <= a;
11. 1: alu_b <= b;
12. 2: alu_b <= c;
13. endcase
14. alu_carry_in <= carry;
15. end
16. 1: begin
17. case(instruction[1:0])
18. 0: a <= alu_y;
19. 1: b <= alu_y;
20. 2: c <= alu_y;
21. endcase
22. carry <= alu_carry_out;
23. zero <= alu_zero;
24. sign <= alu_sign;
25. program_counter++;
26. state <= FETCH;
27. end
28. endcase
29. end

That’s 5 of the 12 operations already. The last 7 can also be bundled into the same case statement, the only difference for them is they don’t care what the `carry` flag is set to, and they only affect the `sign` and `zero` flags.

1. CPU_INCREMENT, CPU_DECREMENT, CPU_AND, CPU_OR, CPU_XOR, CPU_NOR, CPU_SHIFT_LEFT: begin
2. case(cycle)
3. 0: begin
4. case(instruction[3:2])
5. 0: alu_a <= a;
6. 1: alu_a <= b;
7. 2: alu_a <= c;
8. endcase
9. case(instruction[1:0])
10. 0: alu_b <= a;
11. 1: alu_b <= b;
12. 2: alu_b <= c;
13. endcase
14. end
15. 1: begin
16. case(instruction[1:0])
17. 0: a <= alu_y;
18. 1: b <= alu_y;
19. 2: c <= alu_y;
20. endcase
21. zero <= alu_zero;
22. sign <= alu_sign;
23. program_counter++;
24. state <= FETCH;
25. end
26. endcase
27. end

Huzzah! We can now utilize our ALU operations via our CPU program code. In the next post I will add some operations to my CPU to include stack functionality and operations that can be used to call subroutines. As always, I welcome your feedback and questions in the comments. Keep tinkering!

Posted on Categories Digital Logic Design