Skip to content

Instantly share code, notes, and snippets.

@multiplemonomials
Last active September 17, 2019 00:41
Show Gist options
  • Save multiplemonomials/f3f8821c465dcd88a7d894f49d45b747 to your computer and use it in GitHub Desktop.
Save multiplemonomials/f3f8821c465dcd88a7d894f49d45b747 to your computer and use it in GitHub Desktop.
Test suite for ALU logic equations for EE 457 Lab 3
import numpy as np
NUM_BITS = 4
def bitwise_not(num:int, width=None):
"""Python doesn't have bitwise not so we have to improvise using XOR"""
if width is None:
width = num.bit_length() + 1
# bitmask for all bits with a value
bitmask = (1 << width) - 1
return num ^ bitmask
def get_bit(num:int, bit:int):
""" Get the Nth bit of a number, as a 1 or 0 integer value."""
return (num & (1 << bit)) >> bit
def convert_to_signed(num:int, num_bits=NUM_BITS):
""" Converts a binary number into its integer value as if it was a two's complement number with NUM_BITS bits.
Python's integers have variable width so logic like this is needed for proper evaluation."""
if get_bit(num, num_bits - 1):
return -(bitwise_not(num, num_bits) + 1)
else:
# signed representation is not negative
return num
def binary_add(A:int, B:int, negate_B = False):
""" Mimics the function of a 4-bit ALU. Adds the numbers and attempts to detect overflow."""
# TODO: handle negate_B
if negate_B:
B_after_inv = ???
carry_in_0 = ???
else:
B_after_inv = B
carry_in_0 = 0
# carry out for each bit
carry_out = 0
# result
result = 0
for bit in range(0, NUM_BITS):
A_bit = get_bit(A, bit)
B_bit = get_bit(B_after_inv, bit)
if bit == 0:
carry_in = carry_in_0
else:
carry_in = get_bit(carry_out, bit - 1)
# TODO: Add the bits here.
# Hint: these equations are given in Redekopp's EE 109 slides in Unit 11
result_bit = ???
carry_out_bit = ???
result |= result_bit << bit
carry_out |= carry_out_bit << bit
# These values will be useful in the overflow calculations
carry_out_msb = get_bit(carry_out, NUM_BITS - 1)
A_msb = get_bit(A, NUM_BITS - 1)
B_msb = get_bit(B, NUM_BITS - 1)
B_after_inv_msb = get_bit(B_after_inv, NUM_BITS - 1)
result_msb = get_bit(result, NUM_BITS - 1)
# TODO: detect signed overflow
signed_overflow = ???
return result, carry_out_msb == 1, signed_overflow
for num1 in range(0, 16):
for num2 in range(0, 16):
# test unsigned addition
alu_result, carry_out, signed_overflow = binary_add(num1, num2)
uint_result = num1 + num2
expected_carry_out = uint_result > 0b1111
if (not carry_out and alu_result != uint_result) or (carry_out and alu_result + (1 << NUM_BITS) != uint_result):
print(f"UNSIGNED ADDITION WRONG! Num1: {num1:04b} ({num1}), Num2: {num2:04b} ({num2}), ALU result: {alu_result} [Cout = {carry_out}], correct result: {uint_result} [Cout = {expected_carry_out}]")
# test signed addition
# get the numbers interpreted as signed numbers
signed_num1 = convert_to_signed(num1)
signed_num2 = convert_to_signed(num2)
int_result = signed_num1 + signed_num2
signed_alu_result = convert_to_signed(alu_result)
# per the textbook section 3.2, overflow occurs during addition when two positive numbers yield a negative number, or
# when two negative numbers yield a positive number
should_have_signed_overflow = ((signed_num1 >= 0 and signed_num2 >= 0 and signed_alu_result < 0) or
(signed_num1 < 0 and signed_num2 < 0 and signed_alu_result >= 0))
if should_have_signed_overflow and not signed_overflow:
print(f"SIGNED ADDITION FAILED TO DETECT OVERFLOW! Num1: {np.binary_repr(signed_num1, width=NUM_BITS)} ({signed_num1}), Num2: {np.binary_repr(signed_num2, width=NUM_BITS)} ({signed_num2}), ALU result: {signed_alu_result}, correct result: {int_result}")
elif signed_alu_result != int_result and not signed_overflow:
print(f"SIGNED ADDITION WRONG! Num1: {np.binary_repr(signed_num1, width=NUM_BITS)} ({signed_num1}), Num2: {np.binary_repr(signed_num2, width=NUM_BITS)} ({signed_num2}), ALU result: {signed_alu_result}, correct result: {int_result}")
elif signed_overflow and not should_have_signed_overflow:
print(f"SIGNED ADDITION INCORRECTLY DETECTED OVERFLOW! Num1: {np.binary_repr(signed_num1, width=NUM_BITS)} ({signed_num1}), Num2: {np.binary_repr(signed_num2, width=NUM_BITS)} ({signed_num2}), ALU result: {signed_alu_result}, correct result: {int_result}")
for num1 in range(0, 16):
for num2 in range(0, 16):
# test unsigned subtraction
alu_result, carry_out, signed_overflow = binary_add(num1, num2, negate_B=True)
signed_alu_result = convert_to_signed(alu_result)
uint_result = num1 - num2
should_have_unsigned_overflow = uint_result < 0
expected_carry_out = uint_result >= 0
if (uint_result & ((1 << NUM_BITS)- 1)) == alu_result and (expected_carry_out == (get_bit(uint_result, NUM_BITS) == 1)):
print(f"UNSIGNED SUBTRACTION WRONG! Num1: {num1:04b} ({num1}), Num2: {num2:04b} ({num2}), ALU result: {alu_result} [Cout = {carry_out}], correct result: {uint_result} [Cout = {expected_carry_out}]")
# get the numbers interpreted as signed numbers
signed_num1 = convert_to_signed(num1)
signed_num2 = convert_to_signed(num2)
int_result = signed_num1 - signed_num2
# from textbook section 3.2
should_have_signed_overflow = ((signed_num1 >= 0 and signed_num2 < 0 and signed_alu_result < 0) or
(signed_num1 < 0 and signed_num2 >= 0 and signed_alu_result >= 0))
if should_have_signed_overflow and not signed_overflow:
print(f"SIGNED SUBTRACTION FAILED TO DETECT OVERFLOW! Num1: {np.binary_repr(signed_num1, width=NUM_BITS)} ({signed_num1}), Num2: {np.binary_repr(signed_num2, width=NUM_BITS)} ({signed_num2}), ALU result: {signed_alu_result}, correct result: {int_result}")
elif signed_alu_result != int_result and not signed_overflow:
print(f"SIGNED SUBTRACTION WRONG! Num1: {np.binary_repr(signed_num1, width=NUM_BITS)} ({signed_num1}), Num2: {np.binary_repr(signed_num2, width=NUM_BITS)} ({signed_num2}), ALU result: {signed_alu_result}, correct result: {int_result}.")
elif signed_overflow and not should_have_signed_overflow:
print(f"SIGNED SUBTRACTION INCORRECTLY DETECTED OVERFLOW! Num1: {np.binary_repr(signed_num1, width=NUM_BITS)} ({signed_num1}), Num2: {np.binary_repr(signed_num2, width=NUM_BITS)} ({signed_num2}), ALU result: {signed_alu_result}, correct result: {int_result}")
print("All tests finished.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment