# Beginning Logic Design – Part 6

Hello and welcome to Part 6 of my Beginning Logic Design series. In the last post I laid out the start of an ALU design. In this round I will be completing the ADD operation including support for the various status flags and build some tests to validate it.

As a quick reference, here’s the description I laid out in the previous post.

The ALU I’ll be designing in this post will be influenced by the classic 6502 processor that’s at the heart of the NES, Apple II and many other early computer systems. This ALU with support 12 operations:

• Arithmetic Operations
• Add with Carry
• Subtract with Carry
• Increment
• Decrement
• Logic Operations
• AND
• OR
• XOR
• NOT/Negate (not in 6502, but I want my ALU to have it!)
• Shifting Operations
• Arithmetic Shift Left
• Logical Shift Right
• Rotate Left
• Rotate Right

This ALU will also have a few status signals, on the input side it will just have `carry in`, on the output side we’ll have `carry out`, `zero`, `sign` and `overflow`. I’ll dive a bit more into each of these operations and how the interact with status signals as we implement them.

To complete out the `ADD` operation as specified, it needs to factor in a `carry` input signal and output the other status signals as well.

Following the general structure of what the 6502 architecture does, I’ll reference this 6502 opcode document for which flags are modified by which operations, and this 6502 processor document as a reference to the meanings for each flag. I also found this article on the overflow flag  helpful to wrap my head around its meaning and purpose.

To understand what all these flags mean, some familiarity with representing signed numbers and two’s complement is needed to understand the semantics of these flags, and it also helps to understand how subtraction is implemented in digital machines. I suggest this Computerphile video for an overview on unsigned, signed magnitude and twos complement binary representation.

Here’s how each flag should be responding in our addition circuit:

• `zero` should be 1 if the result of the addition is zero
• `sign` should be 1 if the the result could be interpreted as a negative number (in signed addition)
• `carry` should be 1 if the addition results in an unsigned number that is wider than 8 bits
• `overflow` should be 1 if the addition resulted in a number that is outside of the range supported by an 8 bit signed number: `sum < -128` or `sum > 127`

To get started on this, the first thing I’ll do is add the input and output ports for these signals to my ALU. I’ll also use `assign` to set them all to `0` for now.

1. module alu (
2. input logic clock,
3. input opcode operation,
4. input logic [7:0] a,
5. input logic [7:0] b,
6. input logic carry_in,
7. output logic [7:0] y,
8. output logic zero,
9. output logic sign,
10. output logic carry_out,
11. output logic overflow
12. );
13. assign zero = 0,
14. sign = 0,
15. carry_out = 0,
16. overflow = 0;

Then I’ll also update my `top` module to include variables to route these in and out of the `alu`.

1. module top ();
2. logic clock;
3. opcode operation;
4. logic [7:0] a;
5. logic [7:0] b;
6. logic carry_in;
7. logic [7:0] y;
8. logic zero;
9. logic sign;
10. logic carry_out;
11. logic overflow;
12. alu myALU (
13. clock,
14. operation,
15. a,
16. b,
17. carry_in,
18. y,
19. zero,
20. sign,
21. carry_out,
22. overflow
23. );
24. ...

I’ll also add `carry_in = 0;` in my `initial` block within `top` to set that signal to `0` at the start.

# Test Driven Development

Many software developers are supporters of test driven development, but in the hardware world the concept of writing your tests before your implementation this is the norm.

It can be incredibly easy to add a flaw to our designs. The `ADD` operation here supports signed and unsigned addition. Interestingly enough the only difference between signed and unsigned addition is what the number means to you, the potential user of the ALU. If we do the operation `80 + 80`, and intended that be unsigned addition result would be `160`, but we intended signed addition it would erroneously be `-96` because the number went beyond the maximum number a signed 8-bit number can support (127).

If we look at this in hexadecimal it is `0x50 + 0x50 = 0xA0` in either case, but the hexadecimal number `0xA0` has a different value if looked at as a signed (`160`) or unsigned (`-96`) number. We use the the `carry_out` status flag to let us know when our addition exceeds what can be accurately represented in our unsigned integer range `[0, 255]`. Similarly, we use the `overflow` status flag to know when a signed addition goes beyond what can accurately be represented in our signed integer range `[-128, 127]`.

Implementing an adder that let’s us see all of this can be tricky, so let’s identify some of the special cases we need to be able to handle. The first round of tests I will write will use the table from this article on the 6502 overflow flag as a base.

I’ll now start writing some tests for my ALU. For this, I’ll use `assert` statements to check if the outputs are matching what I’d expect. Here’s the top module modified to include a test that validates a case where both `overflow` and `carry` should be set.

1. initial begin
2. clock = 0;
3. // Test 208 + 144
4. a = 208;
5. b = 144;
6. carry_in = 0;
7. operation = ADD;
8. #2 assert(y == 96);
9. assert(zero == 0);
10. assert(sign == 0);
11. assert(carry_out == 1);
12. assert(overflow == 1);
13. #1 \$finish();
14. end

I added `\$finish();` at the end to stop simulation there. Now I’ll fire off the simulator and see how it goes. In the simulator window there’s no indication of an error, but in the Tcl Console at the bottom I can see some output that indicates some assertions failed.

1. Error: Assertion violation
2. Time: 2 ns Iteration: 0 Process: /top/Initial30_1 File: /home/kwilke/suchprogramming/my-alu/my-alu.srcs/sources_1/new/top.sv
3. Error: Assertion violation
4. Time: 2 ns Iteration: 0 Process: /top/Initial30_1 File: /home/kwilke/suchprogramming/my-alu/my-alu.srcs/sources_1/new/top.sv
5. \$finish called at time : 3 ns : File "/home/kwilke/suchprogramming/my-alu/my-alu.srcs/sources_1/new/top.sv" Line 44

The error message shows when the assertion failed, but no information is given as to why. To make the error more informational I can add an `else` statement followed by a `\$error()` call to have the simulator spit out a more descriptive error.

1. #2 assert(y == 96) else \$error("Sum assertion failed.");
2. assert(zero == 0) else \$error("Zero assertion failed.");
3. assert(sign == 0) else \$error("Sign assertion failed.");
4. assert(carry_out == 1) else \$error("Carry assertion failed.");
5. assert(overflow == 1) else \$error("Overflow assertion failed.");

Now when I run the simulation, I can more clearly see what has failed.

1. Error: Carry assertion failed.
2. Time: 2 ns Iteration: 0 Process: /top/Initial30_1 File: /home/kwilke/suchprogramming/my-alu/my-alu.srcs/sources_1/new/top.sv
3. Error: Overflow assertion failed.
4. Time: 2 ns Iteration: 0 Process: /top/Initial30_1 File: /home/kwilke/suchprogramming/my-alu/my-alu.srcs/sources_1/new/top.sv
5. \$finish called at time : 3 ns : File "/home/kwilke/suchprogramming/my-alu/my-alu.srcs/sources_1/new/top.sv" Line 44

# Implementing The Flags

Finding the `zero` flag is easy, we just need to see if our output is zero.

The `sign` flag is also easy, we just need to check if the most significant output bit is `1`.

The `carry` flag we can get by looking the result of our addition as 9 bits and looking at the most significant bit there.

The `overflow` flag is the trickiest, but the easiest way to get that is to take the `carry` of the 6th (counting from 0) bit’s sum and XOR that with the final `carry` output.

If I use the `y <= a + b;` syntax I have now, I can’t access any of the carry values. I need access to the last two carry values to set all of our flags properly. I also want to start factoring the `carry in` signal.

To break these needs up into easily accessed chunks, I’ll declare a few more internal variables.

1. logic [7:0] add;
2. logic [6:0] add_lower_bits;

Next, I’ll define some combinational circuits with `assign`. I’ll first write an assignment that adds the lower bits and provides me my 6th carry bit.

1. assign {add_carry_6, add_lower_bits} = a[6:0] + b[6:0] + carry_in,
2. {add_carry_7, add} = a + b + carry_in;

The curly brackets here combine bits together. In the first statement the addition on the right results 8 bits of output and I use the brackets on the left to put the highest bit in `add_carry_6` and the remaining 7 bits in `add_lower_bits`. In this design I don’t use the lower bits but I set the size so I can easily get to that internal carry. I’ll use a similar assignment for the final carry and sum.

With easy access to all of the addition results I need, I can modify my `ADD` operation to use them to set my output and flags.

2. y <= add;
3. carry_out <= add_carry_7;
4. sign <= add;
6. if (add == 0)
7. zero <= 1;
8. else
9. zero <= 0;
10. end

Now I’ll need to remove my earlier `assign` block that was setting all my output flags to `0`, and I can run the simulation again! It works! I’ll write a few more tests to validate some of the other operations but it should be good to go now! Here’s how my files have ended up.

`top.sv`

1. `timescale 1ns / 1ps
2. import ALU::*;
3. module top ();
4. logic clock;
5. opcode operation;
6. logic [7:0] a;
7. logic [7:0] b;
8. logic carry_in;
9. logic [7:0] y;
10. logic zero;
11. logic sign;
12. logic carry_out;
13. logic overflow;
14. alu myALU (
15. clock,
16. operation,
17. a,
18. b,
19. carry_in,
20. y,
21. zero,
22. sign,
23. carry_out,
24. overflow
25. );
26. initial begin
27. clock = 0;
28. operation = ADD;
29. // Test 208 + 144
30. a = 208;
31. b = 144;
32. carry_in = 0;
33. #2 assert(y == 96) else \$error("Sum assertion failed.");
34. assert(zero == 0) else \$error("Zero assertion failed.");
35. assert(sign == 0) else \$error("Sign assertion failed.");
36. assert(carry_out == 1) else \$error("Carry assertion failed.");
37. assert(overflow == 1) else \$error("Overflow assertion failed.");
38. // Test 208 + 48 (signed -48 + 48)
39. a = 208;
40. b = 48;
41. carry_in = 0;
42. #2 assert(y == 0) else \$error("Sum assertion failed.");
43. assert(zero == 1) else \$error("Zero assertion failed.");
44. assert(sign == 0) else \$error("Sign assertion failed.");
45. assert(carry_out == 1) else \$error("Carry assertion failed.");
46. assert(overflow == 0) else \$error("Overflow assertion failed.");
47. // Test 80 + 80
48. a = 80;
49. b = 80;
50. carry_in = 0;
51. #2 assert(y == 160) else \$error("Sum assertion failed.");
52. assert(zero == 0) else \$error("Zero assertion failed.");
53. assert(sign == 1) else \$error("Sign assertion failed.");
54. assert(carry_out == 0) else \$error("Carry assertion failed.");
55. assert(overflow == 1) else \$error("Overflow assertion failed.");
56. #1 \$finish();
57. end
58. always begin
59. #1 clock = ~clock;
60. end
61. endmodule

`alu.sv`

1. `timescale 1ns / 1ps
2. package ALU;
3. typedef enum logic [3:0] {
5. SUBTRACT,
6. INCREMENT,
7. DECREMENT,
8. BIT_AND,
9. BIT_OR,
10. BIT_XOR,
11. BIT_NOT,
12. SHIFT_LEFT,
13. SHIFT_RIGHT,
14. ROTATE_LEFT,
15. ROTATE_RIGHT
16. } opcode;
17. endpackage
18. import ALU::*;
19. module alu (
20. input logic clock,
21. input opcode operation,
22. input logic [7:0] a,
23. input logic [7:0] b,
24. input logic carry_in,
25. output logic [7:0] y,
26. output logic zero,
27. output logic sign,
28. output logic carry_out,
29. output logic overflow
30. );
31. logic [7:0] add;
32. logic [6:0] add_lower_bits;
35. assign {add_carry_6, add_lower_bits} = a[6:0] + b[6:0] + carry_in,
36. {add_carry_7, add} = a + b + carry_in;
37. always_ff @ (posedge clock) begin
38. case (operation)
40. y <= add;
41. carry_out <= add_carry_7;
42. sign <= add;
With our `ADD` operation fully functional that will finish this post. In the next post I will continue adding instructions and tests to this ALU to keep it moving closer to the model I outlined. If you have any feedback or questions please leave a comment. Keep tinkering!