Skip to content

Commit

Permalink
added testbench for adder tree and fixed the bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
marph91 committed Mar 20, 2021
1 parent 8ad56a7 commit ac27930
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 8 deletions.
134 changes: 134 additions & 0 deletions sim/test_adder_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from dataclasses import dataclass
from math import ceil, log2
import pathlib
from random import randint
from typing import List

from bitstring import Bits
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import Timer
from cocotb_test.simulator import run
import pytest

from test_utils.cocotb_helpers import ImageMonitor, Tick
from test_utils.general import get_files, record_waveform


def to_fixedint(number: int, bitwidth: int, is_unsigned: bool = True):
"""Convert signed int to fixed int."""
if is_unsigned:
number_dict = {"uint": number, "length": bitwidth}
else:
number_dict = {"int": number, "length": bitwidth}
return int(Bits(**number_dict).bin, 2)


def from_fixedint(number: int, bitwidth: int, is_unsigned: bool = True):
"""Convert fixed int to signed int."""
number_bin = bin(number)[2:].zfill(bitwidth)
if is_unsigned:
return Bits(bin=number_bin).uint
return Bits(bin=number_bin).int


def concatenate_integers(integer_list: List[int], bitwidth=1) -> int:
concatenated_integer = 0
for value in integer_list:
if value > 2 ** bitwidth:
raise ValueError(f"Value {value} exeeds range.")
concatenated_integer = (concatenated_integer << bitwidth) + value
return concatenated_integer


@cocotb.test()
async def run_test(dut):
input_count = int(dut.C_INPUT_COUNT.value.integer)
input_bitwidth = int(dut.C_INPUT_BITWIDTH.value.integer)
is_unsigned = bool(dut.C_UNSIGNED.value.integer)
output_bitwidth = int(dut.C_OUTPUT_BITWIDTH.value.integer)

@dataclass
class Testcase:
input_data: List[int]

@property
def input_data_fixedint(self) -> int:
input_data_fixedint = [
to_fixedint(datum, input_bitwidth, is_unsigned)
for datum in self.input_data
]
return concatenate_integers(input_data_fixedint, bitwidth=input_bitwidth)

@property
def output_data(self) -> int:
return sum(self.input_data)

input_range = (
(0, 2 ** input_bitwidth - 1)
if is_unsigned
else (-(2 ** (input_bitwidth - 1)), 2 ** (input_bitwidth - 1) - 1)
)
cases = (
# minimum values
Testcase([input_range[0]] * input_count),
# maximum values
Testcase([input_range[1]] * input_count),
# random values
Testcase([randint(*input_range) for _ in range(input_count)]),
)

# initialize the test
clock_period = 10 # ns
tick = Tick(clock_period=clock_period)
cocotb.fork(Clock(dut.isl_clk, clock_period, units="ns").start())
output_mon = ImageMonitor(
"output",
dut.oslv_data,
dut.osl_valid,
dut.isl_clk,
1,
output_bitwidth,
)
dut.isl_valid <= 0
await tick.wait()

for case in cases:
dut.isl_valid <= 1
dut.islv_data <= case.input_data_fixedint
await tick.wait()
dut.isl_valid <= 0

await tick.wait_multiple(ceil(log2(input_count)) + 1) # number of stages
assert dut.osl_valid.value.integer == 0

for case, output in zip(cases, output_mon.output):
output_int = from_fixedint(output, output_bitwidth, is_unsigned)
assert output_int == case.output_data, f"{output_int} /= {case.output_data}"


@pytest.mark.parametrize("is_unsigned", (0, 1))
@pytest.mark.parametrize("input_bitwidth", (1, 4, 8))
def test_adder_tree(record_waveform, is_unsigned, input_bitwidth):
generics = {
"C_INPUT_COUNT": randint(1, 16),
"C_INPUT_BITWIDTH": input_bitwidth,
"C_UNSIGNED": is_unsigned,
}
generics["C_OUTPUT_BITWIDTH"] = generics["C_INPUT_BITWIDTH"] + ceil(
log2(generics["C_INPUT_COUNT"])
)
run(
vhdl_sources=[
pathlib.Path(__file__).parent.absolute()
/ ".."
/ "src"
/ "util"
/ "adder_tree.vhd"
],
toplevel="adder_tree",
module="test_adder_tree",
compile_args=["--work=util", "--std=08"],
parameters=generics,
sim_args=["--wave=adder_tree.ghw"] if record_waveform else None,
)
16 changes: 8 additions & 8 deletions src/util/adder_tree.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ library ieee;

entity adder_tree is
generic (
C_INPUT_COUNT : integer;
C_INPUT_BITWIDTH : integer;
C_INPUT_COUNT : integer := 4;
C_INPUT_BITWIDTH : integer := 8;
C_UNSIGNED : integer range 0 to 1 := 1;
C_OUTPUT_BITWIDTH : integer
C_OUTPUT_BITWIDTH : integer := 8
);
port (
isl_clk : in std_logic;
Expand Down Expand Up @@ -40,24 +40,24 @@ architecture rtl of adder_tree is

function convert_input (input_vector : std_logic_vector) return t_sums is
variable v_sum_init : t_sums(0 to C_INPUTS_FIRST_STAGE - 1);
variable v_input_datum : std_logic_vector(C_OUTPUT_BITWIDTH - 1 downto 0);
variable v_input_datum : std_logic_vector(C_INPUT_BITWIDTH - 1 downto 0);
begin
v_sum_init := (others => (others => '0'));

-- Pad with zeros to widen from input bitwidth to output bitwidth.
assert C_OUTPUT_BITWIDTH >= C_INPUT_BITWIDTH;
assert C_OUTPUT_BITWIDTH >= C_INPUT_BITWIDTH + C_STAGES; -- Input gets extended by 1 bit at each stage.
v_input_datum := (others => '0');

for i in 0 to C_INPUT_COUNT - 1 loop

-- TODO: use get_slice
v_input_datum(C_INPUT_BITWIDTH - 1 downto 0) := input_vector((i + 1) * C_INPUT_BITWIDTH - 1 downto i * C_INPUT_BITWIDTH);
v_input_datum := input_vector((i + 1) * C_INPUT_BITWIDTH - 1 downto i * C_INPUT_BITWIDTH);

if (C_UNSIGNED = 1) then
-- Pad a zero (sign) bit in case of unsigned input.
v_sum_init(i) := signed('0' & v_input_datum);
v_sum_init(i) := resize(signed('0' & v_input_datum), v_sum_init(0));
else
v_sum_init(i) := signed(v_input_datum);
v_sum_init(i) := resize(signed(v_input_datum), v_sum_init(0));
end if;

end loop;
Expand Down

0 comments on commit ac27930

Please sign in to comment.