Beginning Logic Design – Part 7

Hello and welcome to Part 7 of my Beginning Logic Design series. In this post I will continue to add functionality to the ALU I’ve been working on in the last couple posts.

Using Vivado Via Command Line

This ALU project is making decent progress and I’d like to start managing the code with Git and make my work flow a bit faster. When I talk to engineers that work with FPGAs for a living, it seems like most of them do not use the project flow that I’ve been working with. They favor the command line or batch mode approach.

I’ll first copy my alu.sv and top.sv to a new directory so that those files are all I have. To build and test on the command line there are 3 commands I’ll use, xvlog to parse my SystemVerilog, xelab to elaborate the design, and xsim to run the simulation.

xvlog --sv alu.sv top.sv
xelab top
xsim -R top

Armed with these commands, I can more easily write my code in whichever editor I choose!

I’ll use this Makefile to give me an easy way to test and clean up the built files and logs.

XVLOG_FILES=xvlog.log xvlog.pb xsim.dir
XELAB_FILES=webtalk*.log webtalk*.jou xelab.log xelab.pb .Xil/
XSIM_FILES=xsim*.log xsim*.jou

all:
  @echo "Parsing HDL"
  @xvlog --sv *.sv > build.log
  @echo "Elaborating design"
  @xelab top >> build.log
  @echo "Starting simulation"
  @xsim -R top

clean:
  rm -rf $(XVLOG_FILES) $(XELAB_FILES) $(XSIM_FILES) build.log



Implementing Subtraction

Subtraction in binary is nearly identical to addition. In the case of subtraction, carry is looked at as a borrow value. Building a subtraction operation in SystemVerilog is the same as addition, but with - instead of +.

// Subtract operation internals
logic [7:0] subtract;
logic [6:0] subtract_lower_bits;
logic subtract_borrow_6;
logic subtract_borrow_7;

...

assign {subtract_borrow_6, subtract_lower_bits} = a[6:0] - b[6:0] - carry_in,
    {subtract_borrow_7, subtract} = a - b - carry_in;

...

        SUBTRACT: begin
            y <= subtract;
            carry_out <= subtract_borrow_7;
            sign <= subtract[7];
            overflow <= subtract_borrow_7 ^ subtract_borrow_6;
            if (subtract == 0)
                zero <= 1;
            else
                zero <= 0;
        end

As with the addition, I’ll make some tests that validate I’m getting the results I expect from the operation.

// Test 100 - 80
operation = SUBTRACT;
a = 100;
b = 80;
#2 assert(y == 20) else $error("Subtract assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");
assert(carry_out == 0) else $error("Carry assertion failed.");
assert(overflow == 0) else $error("Overflow assertion failed.");

// Test 80 - 100
a = 80;
b = 100;
#2 assert(y == 236) else $error("Subtract assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 1) else $error("Sign assertion failed.");
assert(carry_out == 1) else $error("Carry assertion failed.");
assert(overflow == 0) else $error("Overflow assertion failed.");

// Test 80 - 176
a = 80;
b = 176;
#2 assert(y == 160) else $error("Subtract assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 1) else $error("Sign assertion failed.");
assert(carry_out == 1) else $error("Carry assertion failed.");
assert(overflow == 1) else $error("Overflow assertion failed.");

// Test 208 - 112
a = 208;
b = 112;
#2 assert(y == 96) else $error("Subtract assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");
assert(carry_out == 0) else $error("Carry assertion failed.");
assert(overflow == 1) else $error("Overflow assertion failed.");

// Test 208 - 208
a = 208;
b = 208;
#2 assert(y == 0) else $error("Subtract assertion failed.");
assert(zero == 1) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");
assert(carry_out == 0) else $error("Carry assertion failed.");
assert(overflow == 0) else $error("Overflow assertion failed.");

Implementing Remaining Operations

Addition and Subtraction are the trickiest operations to implement of the operations planned for this ALU. With two instructions down there are 10 to go,

  • 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

Increment & Decrement

Increment and decrement only affect the sign and zero flags, and adds or subtracts 1 from a single operand. To implement them I’ll use assign again to give me a reference to an incremented and decremented value.

// Increment/Decrement
logic [7:0] incremented;
logic [7:0] decremented;


assign incremented = a + 1,
  decremented = a - 1;

Then in my case statement on operation, I’ll add the INCREMENT and DECREMENT cases.

INCREMENT: begin
  y <= incremented;
  sign <= incremented[7];
  if (incremented == 0) 
    zero <= 1;
  else
    zero <= 0;
end
DECREMENT: begin
  y <= decremented;
  sign <= decremented[7];
  if (decremented == 0) 
    zero <= 1;
  else
    zero <= 0;
end

Then I’ll add some more tests to top to verify it.

// Test 0++
operation = INCREMENT;
a = 0;
#2 assert(y == 1) else $error("Increment assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test 200++
a = 200;
#2 assert(y == 201) else $error("Increment assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 1) else $error("Sign assertion failed.");

// Test 255++
a = 255;
#2 assert(y == 0) else $error("Increment assertion failed.");
assert(zero == 1) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test 0--
operation = DECREMENT;
a = 0;
#2 assert(y == 255) else $error("Decrement assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 1) else $error("Sign assertion failed.");

// Test 200--
a = 200;
#2 assert(y == 199) else $error("Decrement assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 1) else $error("Sign assertion failed.");

// Test 1--
a = 1;
#2 assert(y == 0) else $error("Decrement assertion failed.");
assert(zero == 1) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign a

Logic Operations

The logic operations AND, OR, XOR, and NOT are the easiest operations to implement. Very similar to what I did for increment and decrement, I’ll name some new variables and keep them assigned to the results for each operation. Then they are added to the case statement.

// Logic operations
logic [7:0] and_result;
logic [7:0] or_result;
logic [7:0] xor_result;
logic [7:0] not_result;

// Logic assignments
assign and_result = a & b,
  or_result = a | b,
  xor_result = a ^ b,
  not_result = ~a;

...

    BIT_AND: begin
      y <= and_result;
      sign <= and_result[7];
      if (and_result == 0)
        zero <= 1;
      else
        zero <= 0;
    end
    BIT_OR: begin
      y <= or_result;
      sign <= or_result[7];
      if (or_result == 0)
        zero <= 1;
      else
        zero <= 0;
    end
    BIT_XOR: begin
      y <= xor_result;
      sign <= xor_result[7];
      if (xor_result == 0)
        zero <= 1;
      else
        zero <= 0;
    end
    BIT_NOT: begin
      y <= not_result;
      sign <= not_result[7];
      if (not_result == 0)
        zero <= 1;
      else
        zero <= 0;
    end

Then some tests to verify.

// Test 12 & 10
operation = BIT_AND;
a = 12;
b = 10;
#2 assert(y == 8) else $error("AND assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test 13 | 15
operation = BIT_OR;
a = 13;
b = 15;
#2 assert(y == 15) else $error("OR assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test 13 ^ 15
operation = BIT_XOR;
a = 13;
b = 15;
#2 assert(y == 2) else $error("XOR assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test 13 & 15
operation = BIT_NOT;
a = 13;
#2 assert(y == 242) else $error("NOT assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 1) else $error("Sign assertion failed.");

Shifting Operations

The last few operations to add are the shifters. Rotate left and rotate right shift all the bits left or right by one position. In the case of a right shift, the leftmost bit will change to the carry in bit, and the rightmost bit will become the new carry out. In the arithmetic and logical shift operations the bit being pushed in will be 0 regardless of the carry in value.

// Shifting operations
logic [7:0] rotated_left;
logic [7:0] rotated_right;
logic [7:0] shifted_left;
logic [7:0] shifted_right;

// Shifting assignments
assign rotated_left = {a[6:0], carry_in},
  rotated_right = {carry_in, a[7:1]},
  shifted_left = {a[6:0], 1'b0},
  shifted_right = {1'b0, a[7:1]};

...

    ROTATE_LEFT: begin
      y <= rotated_left;
      carry_out <= a[7];
      sign <= rotated_left[7];
      if (rotated_left == 0)
        zero <= 1;
      else
        zero <= 0;
    end
    ROTATE_RIGHT: begin
      y <= rotated_right;
      carry_out <= a[0];
      sign <= rotated_right[7];
      if (rotated_right == 0)
        zero <= 1;
      else
        zero <= 0;
    end
    SHIFT_LEFT: begin
      y <= shifted_left;
      carry_out <= a[7];
      sign <= shifted_left[7];
      if (shifted_left == 0)
        zero <= 1;
      else
        zero <= 0;
    end
    SHIFT_RIGHT: begin
      y <= shifted_right;
      carry_out <= a[0];
      sign <= shifted_right[7];
      if (shifted_right == 0)
        zero <= 1;
      else
        zero <= 0;

For the shift assignments I use 1'b0 to specify a 1 bit binary with the value 0 in the concatenation. As always some tests to validate we’re getting the right behavior.

// Test shift left
operation = SHIFT_LEFT;
carry_in = 1;
a = 136;
#2 assert(y == 16) else $error("Shift left assertion failed.");
assert(carry_out == 1) else $error("carry assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test shift right
operation = SHIFT_RIGHT;
a = 8;
#2 assert(y == 4) else $error("Shift right assertion failed.");
assert(carry_out == 0) else $error("carry assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test rotate left
operation = ROTATE_LEFT;
a = 8;
#2 assert(y == 17) else $error("Rotate left assertion failed.");
assert(carry_out == 0) else $error("carry assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 0) else $error("Sign assertion failed.");

// Test rotate right
operation = ROTATE_RIGHT;
a = 9;
#2 assert(y == 132) else $error("Rotate right assertion failed.");
assert(carry_out == 1) else $error("carry assertion failed.");
assert(zero == 0) else $error("Zero assertion failed.");
assert(sign == 1) else $error("Sign assertion failed.");

It Works!

There it is! An ALU implementation that can handle 12 different 8-bit operations. It took 3 posts to get here, but this is a decently large design. Here’s the final alu.sv file

`timescale 1ns / 1ps

package ALU;
  typedef enum logic [3:0] {
    ADD,
    SUBTRACT,
    INCREMENT,
    DECREMENT,
    BIT_AND,
    BIT_OR,
    BIT_XOR,
    BIT_NOT,
    SHIFT_LEFT,
    SHIFT_RIGHT,
    ROTATE_LEFT,
    ROTATE_RIGHT
  } opcode;
endpackage

import ALU::*;

module alu (
  input logic clock,
  input opcode operation,
  input logic [7:0] a,
  input logic [7:0] b,
  input logic carry_in,
  output logic [7:0] y,
  output logic zero,
  output logic sign,
  output logic carry_out,
  output logic overflow
);

  // Add operation internals
  logic [7:0] add;
  logic [6:0] add_lower_bits;
  logic add_carry_6;
  logic add_carry_7;

  // Subtract operation internals
  logic [7:0] subtract;
  logic [6:0] subtract_lower_bits;
  logic subtract_borrow_6;
  logic subtract_borrow_7;

  // Increment/Decrement
  logic [7:0] incremented;
  logic [7:0] decremented;

  // Logic operations
  logic [7:0] and_result;
  logic [7:0] or_result;
  logic [7:0] xor_result;
  logic [7:0] not_result;

  // Shifting operations
  logic [7:0] rotated_left;
  logic [7:0] rotated_right;
  logic [7:0] shifted_left;
  logic [7:0] shifted_right;

  // Add assignments
  assign {add_carry_6, add_lower_bits} = a[6:0] + b[6:0] + carry_in,
    {add_carry_7, add} = a + b + carry_in;

  // Subtract assignments
  assign {subtract_borrow_6, subtract_lower_bits} = a[6:0] - b[6:0] - carry_in,
    {subtract_borrow_7, subtract} = a - b - carry_in;

  // Increment/decrement assignments
  assign incremented = a + 1,
    decremented = a - 1;

  // Logic assignments
  assign and_result = a & b,
    or_result = a | b,
    xor_result = a ^ b,
    not_result = ~a;

  // Shifting assignments
  assign rotated_left = {a[6:0], carry_in},
    rotated_right = {carry_in, a[7:1]},
    shifted_left = {a[6:0], 1'b0},
    shifted_right = {1'b0, a[7:1]};

  always_ff @ (posedge clock) begin
    case (operation)
      ADD: begin
        y <= add;
        carry_out <= add_carry_7;
        sign <= add[7];
        overflow <= add_carry_7 ^ add_carry_6;
        if (add == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      SUBTRACT: begin
        y <= subtract;
        carry_out <= subtract_borrow_7;
        sign <= subtract[7];
        overflow <= subtract_borrow_7 ^ subtract_borrow_6;
        if (subtract == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      INCREMENT: begin
        y <= incremented;
        sign <= incremented[7];
        if (incremented == 0) 
          zero <= 1;
        else
          zero <= 0;
      end
      DECREMENT: begin
        y <= decremented;
        sign <= decremented[7];
        if (decremented == 0) 
          zero <= 1;
        else
          zero <= 0;
      end
      BIT_AND: begin
        y <= and_result;
        sign <= and_result[7];
        if (and_result == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      BIT_OR: begin
        y <= or_result;
        sign <= or_result[7];
        if (or_result == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      BIT_XOR: begin
        y <= xor_result;
        sign <= xor_result[7];
        if (xor_result == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      BIT_NOT: begin
        y <= not_result;
        sign <= not_result[7];
        if (not_result == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      ROTATE_LEFT: begin
        y <= rotated_left;
        carry_out <= a[7];
        sign <= rotated_left[7];
        if (rotated_left == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      ROTATE_RIGHT: begin
        y <= rotated_right;
        carry_out <= a[0];
        sign <= rotated_right[7];
        if (rotated_right == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      SHIFT_LEFT: begin
        y <= shifted_left;
        carry_out <= a[7];
        sign <= shifted_left[7];
        if (shifted_left == 0)
          zero <= 1;
        else
          zero <= 0;
      end
      SHIFT_RIGHT: begin
        y <= shifted_right;
        carry_out <= a[0];
        sign <= shifted_right[7];
        if (shifted_right == 0)
          zero <= 1;
        else
          zero <= 0;
      end
    endcase
  end

  endmodule

And the final top.sv

`timescale 1ns / 1ps

import ALU::*;

module top ();
  logic clock;
  opcode operation;
  logic [7:0] a;
  logic [7:0] b;
  logic carry_in;
  logic [7:0] y;
  logic zero;
  logic sign;
  logic carry_out;
  logic overflow;

  alu myALU (
    clock,
    operation,
    a,
    b,
    carry_in,
    y,
    zero,
    sign,
    carry_out,
    overflow
  );

  initial begin
    clock = 0;
    operation = ADD;
    carry_in = 0;

    // Test 208 + 144
    a = 208;
    b = 144;
    #2 assert(y == 96) else $error("Sum assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");
    assert(carry_out == 1) else $error("Carry assertion failed.");
    assert(overflow == 1) else $error("Overflow assertion failed.");

    // Test 208 + 48 (signed -48 + 48)
    a = 208;
    b = 48;
    #2 assert(y == 0) else $error("Sum assertion failed.");
    assert(zero == 1) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");
    assert(carry_out == 1) else $error("Carry assertion failed.");
    assert(overflow == 0) else $error("Overflow assertion failed.");

    // Test 80 + 80
    a = 80;
    b = 80;
    #2 assert(y == 160) else $error("Sum assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");
    assert(carry_out == 0) else $error("Carry assertion failed.");
    assert(overflow == 1) else $error("Overflow assertion failed.");

    // Test 100 - 80
    operation = SUBTRACT;
    a = 100;
    b = 80;
    #2 assert(y == 20) else $error("Subtract assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");
    assert(carry_out == 0) else $error("Carry assertion failed.");
    assert(overflow == 0) else $error("Overflow assertion failed.");

    // Test 80 - 100
    a = 80;
    b = 100;
    #2 assert(y == 236) else $error("Subtract assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");
    assert(carry_out == 1) else $error("Carry assertion failed.");
    assert(overflow == 0) else $error("Overflow assertion failed.");

    // Test 80 - 176
    a = 80;
    b = 176;
    #2 assert(y == 160) else $error("Subtract assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");
    assert(carry_out == 1) else $error("Carry assertion failed.");
    assert(overflow == 1) else $error("Overflow assertion failed.");

    // Test 208 - 112
    a = 208;
    b = 112;
    #2 assert(y == 96) else $error("Subtract assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");
    assert(carry_out == 0) else $error("Carry assertion failed.");
    assert(overflow == 1) else $error("Overflow assertion failed.");

    // Test 208 - 208
    a = 208;
    b = 208;
    #2 assert(y == 0) else $error("Subtract assertion failed.");
    assert(zero == 1) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");
    assert(carry_out == 0) else $error("Carry assertion failed.");
    assert(overflow == 0) else $error("Overflow assertion failed.");

    // Test 0++
    operation = INCREMENT;
    a = 0;
    #2 assert(y == 1) else $error("Increment assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test 200++
    a = 200;
    #2 assert(y == 201) else $error("Increment assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");

    // Test 255++
    a = 255;
    #2 assert(y == 0) else $error("Increment assertion failed.");
    assert(zero == 1) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test 0--
    operation = DECREMENT;
    a = 0;
    #2 assert(y == 255) else $error("Decrement assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");

    // Test 200--
    a = 200;
    #2 assert(y == 199) else $error("Decrement assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");

    // Test 1--
    a = 1;
    #2 assert(y == 0) else $error("Decrement assertion failed.");
    assert(zero == 1) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test 12 & 10
    operation = BIT_AND;
    a = 12;
    b = 10;
    #2 assert(y == 8) else $error("AND assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test 13 | 15
    operation = BIT_OR;
    a = 13;
    b = 15;
    #2 assert(y == 15) else $error("OR assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test 13 ^ 15
    operation = BIT_XOR;
    a = 13;
    b = 15;
    #2 assert(y == 2) else $error("XOR assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test 13 & 15
    operation = BIT_NOT;
    a = 13;
    #2 assert(y == 242) else $error("NOT assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");

    // Test shift left
    operation = SHIFT_LEFT;
    carry_in = 1;
    a = 136;
    #2 assert(y == 16) else $error("Shift left assertion failed.");
    assert(carry_out == 1) else $error("carry assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test shift right
    operation = SHIFT_RIGHT;
    a = 8;
    #2 assert(y == 4) else $error("Shift right assertion failed.");
    assert(carry_out == 0) else $error("carry assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test rotate left
    operation = ROTATE_LEFT;
    a = 8;
    #2 assert(y == 17) else $error("Rotate left assertion failed.");
    assert(carry_out == 0) else $error("carry assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 0) else $error("Sign assertion failed.");

    // Test rotate right
    operation = ROTATE_RIGHT;
    a = 9;
    #2 assert(y == 132) else $error("Rotate right assertion failed.");
    assert(carry_out == 1) else $error("carry assertion failed.");
    assert(zero == 0) else $error("Zero assertion failed.");
    assert(sign == 1) else $error("Sign assertion failed.");

    #1 $finish();
  end

  always begin
    #1 clock = ~clock;
  end

endmodule

With the ALU working I’ll call it a wrap for this post. If you have any questions or feedback be sure to leave a comment. Keep tinkering!

Leave a Reply

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