diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 57c9b695..bfd9a609 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,12 +22,12 @@ repos: - id: no-commit-to-branch stages: [ commit, manual ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.1.1 + rev: v1.5.1 hooks: - id: mypy stages: [ manual ] - repo: https://github.com/asottile/dead - rev: v1.5.0 + rev: v1.5.2 hooks: - id: dead stages: [ manual ] @@ -36,13 +36,14 @@ repos: hooks: - id: isort stages: [ commit, manual ] + args: ["--profile=black"] - repo: https://github.com/psf/black rev: 23.1.0 hooks: - id: black stages: [ commit, manual ] - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook - rev: v9.4.0 + rev: v9.5.0 hooks: - id: commitlint stages: [commit-msg, manual] diff --git a/elasticai/creator/nn/fixed_point/mac/_hw_test_integ.py b/elasticai/creator/nn/fixed_point/mac/_hw_test_integ.py index c61f7d07..af625e31 100644 --- a/elasticai/creator/nn/fixed_point/mac/_hw_test_integ.py +++ b/elasticai/creator/nn/fixed_point/mac/_hw_test_integ.py @@ -1,14 +1,15 @@ import glob import torch -from fixed_point.mac._signal_number_converter import SignalNumberConverter -from fixed_point.mac.sw_function import MacLayer -from fixed_point.mac.testbench import TestBench from elasticai.creator.file_generation.on_disk_path import OnDiskPath from elasticai.creator.file_generation.template import InProjectTemplate from elasticai.creator.vhdl.test_bench_runner import TestBenchRunner +from ._signal_number_converter import SignalNumberConverter +from .sw_function import MacLayer +from .testbench import TestBench + """ Notes: - The software layer knows the correct result for an input diff --git a/elasticai/creator/nn/fixed_point/mac/_signal_number_converter.py b/elasticai/creator/nn/fixed_point/mac/_signal_number_converter.py index 08633a57..7fc3cc23 100644 --- a/elasticai/creator/nn/fixed_point/mac/_signal_number_converter.py +++ b/elasticai/creator/nn/fixed_point/mac/_signal_number_converter.py @@ -1,11 +1,7 @@ from functools import partial from typing import Iterable -from fixed_point.mac.number_conversion import ( - bits_to_rational, - integer_to_bits, - rational_to_bits, -) +from .number_conversion import bits_to_rational, integer_to_bits, rational_to_bits class SignalNumberConverter: diff --git a/elasticai/creator/nn/fixed_point/mac/_signal_number_converter_test.py b/elasticai/creator/nn/fixed_point/mac/_signal_number_converter_test.py index 85a3e714..d4a4e6c0 100644 --- a/elasticai/creator/nn/fixed_point/mac/_signal_number_converter_test.py +++ b/elasticai/creator/nn/fixed_point/mac/_signal_number_converter_test.py @@ -1,6 +1,6 @@ import math -from fixed_point.mac._signal_number_converter import SignalNumberConverter +from ._signal_number_converter import SignalNumberConverter def test_reset_two_times_without_input(): diff --git a/elasticai/creator/nn/fixed_point/mac/design.py b/elasticai/creator/nn/fixed_point/mac/design.py index f20fab65..04e6b95d 100644 --- a/elasticai/creator/nn/fixed_point/mac/design.py +++ b/elasticai/creator/nn/fixed_point/mac/design.py @@ -1,6 +1,7 @@ -from creator.file_generation.savable import Path, Savable -from creator.file_generation.template import InProjectTemplate -from fixed_point.mac._signal_number_converter import SignalNumberConverter +from elasticai.creator.file_generation.savable import Path, Savable +from elasticai.creator.file_generation.template import InProjectTemplate + +from ._signal_number_converter import SignalNumberConverter class MacDesign(Savable): diff --git a/elasticai/creator/nn/fixed_point/mac/number_conversion_test.py b/elasticai/creator/nn/fixed_point/mac/number_conversion_test.py index 5bd2bdfe..0e07e1c8 100644 --- a/elasticai/creator/nn/fixed_point/mac/number_conversion_test.py +++ b/elasticai/creator/nn/fixed_point/mac/number_conversion_test.py @@ -1,4 +1,4 @@ -from fixed_point.mac.number_conversion import ( +from .number_conversion import ( bits_to_rational, integer_to_bits, max_rational, diff --git a/elasticai/creator/nn/fixed_point/mac/sw_function.py b/elasticai/creator/nn/fixed_point/mac/sw_function.py index 8db6fe45..320aeb92 100644 --- a/elasticai/creator/nn/fixed_point/mac/sw_function.py +++ b/elasticai/creator/nn/fixed_point/mac/sw_function.py @@ -1,7 +1,8 @@ -from creator.file_generation.savable import Savable -from fixed_point._math_operations import MathOperations -from fixed_point._two_complement_fixed_point_config import FixedPointConfig -from fixed_point.mac.design import MacDesign +from elasticai.creator.file_generation.savable import Savable + +from .._math_operations import MathOperations +from .._two_complement_fixed_point_config import FixedPointConfig +from .design import MacDesign class MacLayer: diff --git a/elasticai/creator/nn/fixed_point/mac/testbench.py b/elasticai/creator/nn/fixed_point/mac/testbench.py index bafe4874..efd5ec7f 100644 --- a/elasticai/creator/nn/fixed_point/mac/testbench.py +++ b/elasticai/creator/nn/fixed_point/mac/testbench.py @@ -1,6 +1,7 @@ -from creator.file_generation.savable import Path -from creator.file_generation.template import InProjectTemplate -from fixed_point.mac._signal_number_converter import SignalNumberConverter +from elasticai.creator.file_generation.savable import Path +from elasticai.creator.file_generation.template import InProjectTemplate + +from ._signal_number_converter import SignalNumberConverter class InputsFile: @@ -21,6 +22,17 @@ def parameters(self): class TestBench: + """ + The testbench aims to provide an interface between hw and sw engineer for + generating hw/sw tests for a hw/sw module pair. + The use case is as follows: + - Given a translatable software module and some input data `X`, we + - call the software module with the provided data and record the outputs + - generate the hw design (unit under test UUT) for the sw equivalent + - generate a testbench, that feeds the same input data `X` to the UUT and writes the output data to a file or stdout + - compare the output observed by the testbench to the output of the sw module + """ + def __init__(self, total_bits, frac_bits, inputs, name): self._number_converter = SignalNumberConverter( total_bits=total_bits, frac_bits=frac_bits @@ -36,6 +48,6 @@ def save_to(self, destination: Path): test_bench = InProjectTemplate( package="elasticai.creator.nn.fixed_point.mac", file_name="testbench.tpl.vhd", - parameters={"input_file": f"inputs.csv", "output_file": f"outputs.csv"}, + parameters={}, ) destination.create_subpath(self._name).as_file(".vhd").write(test_bench) diff --git a/elasticai/creator/nn/fixed_point/mac/testbench.tpl.vhd b/elasticai/creator/nn/fixed_point/mac/testbench.tpl.vhd index e0d66251..7524647a 100644 --- a/elasticai/creator/nn/fixed_point/mac/testbench.tpl.vhd +++ b/elasticai/creator/nn/fixed_point/mac/testbench.tpl.vhd @@ -1,125 +1,72 @@ ----------------------------------------------------------------------------------- --- Company: --- Engineer: --- --- Create Date: 08/21/2023 09:45:40 AM --- Design Name: --- Module Name: test_fxp_mac - Behavioral --- Project Name: --- Target Devices: --- Tool Versions: --- Description: --- --- Dependencies: --- --- Revision: --- Revision 0.01 - File Created --- Additional Comments: --- ----------------------------------------------------------------------------------- - - library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use std.textio.all; use ieee.std_logic_textio.all; +use std.env.finish; --- Uncomment the following library declaration if using --- arithmetic functions with Signed or Unsigned values ---use IEEE.NUMERIC_STD.ALL; - --- Uncomment the following library declaration if instantiating --- any Xilinx leaf cells in this code. ---library UNISIM; ---use UNISIM.VComponents.all; entity testbench_fxp_mac is - generic( - VECTOR_WIDTH : integer := 2; - TOTAL_WIDTH : integer := 4; - FRAC_WIDTH : integer := 2 - ); --- Port ( ); + end testbench_fxp_mac; architecture Behavioral of testbench_fxp_mac is + constant VECTOR_WIDTH : integer := 2; + constant TOTAL_WIDTH : integer := 4; + constant FRAC_WIDTH : integer := 2; + constant total_clock_cycles: integer := 4; + signal clock_period : time := 2 ps; + signal clock : std_logic; signal reset : std_logic; signal next_sample : std_logic; - signal x1 : signed (TOTAL_WIDTH-1 downto 0); - signal x2 : signed (TOTAL_WIDTH-1 downto 0); + signal x1 : signed (TOTAL_WIDTH-1 downto 0) := (others => '0'); + signal x2 : signed (TOTAL_WIDTH-1 downto 0) := (others => '0'); signal sum : signed(TOTAL_WIDTH-1 downto 0); signal done : std_logic; - file input_buf : text; -- text is keyword - file output_buf : text; -- text is keyword + type input_array_t is array (0 to 1) of signed(TOTAL_WIDTH-1 downto 0); + signal x1_values : input_array_t := (b"0000", b"0010"); + signal x2_values : input_array_t := (b"0000", b"0010"); + + begin UUT : entity work.fxp_MAC_RoundToEven generic map(VECTOR_WIDTH => VECTOR_WIDTH, TOTAL_WIDTH=>TOTAL_WIDTH, FRAC_WIDTH => FRAC_WIDTH) port map (reset => reset, next_sample => next_sample, x1 => x1, x2 => x2, sum => sum, done => done); - - - - testbench_1 : process - variable read_col_from_input_buf : line; -- read lines one by one from input_buf - variable write_col_to_output_buf : line; -- line is keyword - variable reset_val, next_sample_val : std_logic; -- to save col1 and col2 values of 1 bit - variable x1_val, x2_val : std_logic_vector(TOTAL_WIDTH-1 downto 0); -- to save col3 value of 2 bit - variable val_SPACE : character; -- for spaces between data in file - variable delimiter : string(1 to 1) := ","; - + clock_process: process begin - file_open(input_buf, "${input_file}", read_mode); - file_open(output_buf, "${output_file}", write_mode); - - - write(write_col_to_output_buf, string'("START_SIM")); - writeline(output_buf, write_col_to_output_buf); - write(write_col_to_output_buf, string'("reset,next_sample,x1,x2,sum,done")); - writeline(output_buf, write_col_to_output_buf); - - readline(input_buf, read_col_from_input_buf); --read headerrow and throw away - - while not endfile(input_buf) loop - - -- reading input - readline(input_buf, read_col_from_input_buf); - read(read_col_from_input_buf, reset_val); - read(read_col_from_input_buf, val_SPACE); -- read in the space character - read(read_col_from_input_buf, next_sample_val); - read(read_col_from_input_buf, val_SPACE); -- read in the space character - read(read_col_from_input_buf, x1_val); - read(read_col_from_input_buf, val_SPACE); -- read in the space character - read(read_col_from_input_buf, x2_val); - - --connect input to signals - reset <= reset_val; - next_sample <= next_sample_val; - x1 <= signed(x1_val); - x2 <= signed(x2_val); + clock <= '0'; + wait for clock_period/2; + clock <= '1'; + wait for clock_period/2; + end process; - -- writing output - write(write_col_to_output_buf, reset_val); - write(write_col_to_output_buf, delimiter); - write(write_col_to_output_buf, next_sample_val); - write(write_col_to_output_buf, delimiter); - write(write_col_to_output_buf, x1_val); - write(write_col_to_output_buf, delimiter); - write(write_col_to_output_buf, x2_val); - write(write_col_to_output_buf, delimiter); - write(write_col_to_output_buf, std_logic_vector(sum)); - write(write_col_to_output_buf, delimiter); - write(write_col_to_output_buf, done); - writeline(output_buf, write_col_to_output_buf); + next_sample <= clock; - wait for 20 ns; - end loop; - write(write_col_to_output_buf, string'("END_SIM")); - writeline(output_buf, write_col_to_output_buf); - file_close(output_buf); - file_close(input_buf); - wait; + testbench_1 : process(clock) + variable iteration_id : integer := 1; + variable reset_performed : std_logic := '0'; + begin + if rising_edge(clock) and reset_performed = '0' then + reset <= '0'; + reset_performed := '1'; + end if; + if falling_edge(clock) then + if reset_performed = '0' then + reset <= '1'; + elsif iteration_id < 2 then + x1 <= x1_values(iteration_id); + x2 <= x2_values(iteration_id); + iteration_id := iteration_id + 1; + elsif done = '1' then + report "sum: " & to_bstring(sum); + report "iterations: " & to_string(iteration_id); + finish; + else + iteration_id := iteration_id + 1; + end if; + end if; end process; end Behavioral; diff --git a/elasticai/creator/vhdl/test_bench_runner.py b/elasticai/creator/vhdl/test_bench_runner.py index cdaca03b..8f386a5b 100644 --- a/elasticai/creator/vhdl/test_bench_runner.py +++ b/elasticai/creator/vhdl/test_bench_runner.py @@ -7,34 +7,39 @@ def __init__(self, workdir, files, test_bench_name): self._root = workdir self._ghdl_dir = "ghdl_build" self._files = files + self._standard = "08" self._test_bench_name = test_bench_name - @property - def _workdir_flag(self): - return f"--workdir={self._ghdl_dir}" - def initialize(self): os.makedirs(f"{self._root}/{self._ghdl_dir}", exist_ok=True) - subprocess.run(["ghdl", "-i", self._workdir_flag] + self._files, cwd=self._root) - subprocess.run( - [ - "ghdl", - "-m", - self._workdir_flag, - "-fsynopsys", - f"{self._test_bench_name}", - ], - cwd=self._root, - ) + self._load_files() + self._compile() def run(self): - subprocess.run( - [ - "ghdl", - "-r", - self._workdir_flag, - "-fsynopsys", - f"{self._test_bench_name}", - ], - cwd=self._root, + self._run() + + def _load_files(self): + self._execute_command(self._assemble_command("-i") + self._files) + + def _compile(self): + self._execute_command( + self._assemble_command("-m") + ["-fsynopsys", self._test_bench_name] + ) + + def _run(self): + output = self._execute_command_and_return_stdout( + self._assemble_command("-r") + [self._test_bench_name] ) + + def _execute_command(self, command): + return subprocess.run(command, cwd=self._root) + + def _execute_command_and_return_stdout(self, command): + return subprocess.run(command, cwd=self._root, capture_output=True).stdout + + def _assemble_command(self, command_flag): + return ["ghdl", command_flag, f"--std={self._standard}", self._workdir_flag] + + @property + def _workdir_flag(self): + return f"--workdir={self._ghdl_dir}"