diff --git a/docs/example-networks.md b/docs/example-networks.md index c6a2e330fe5fb75c717ad161ba101b7a1dfa0e69..9628625f50166d3411fb8c2835a778d395e61b64 100644 --- a/docs/example-networks.md +++ b/docs/example-networks.md @@ -1,11 +1,11 @@ # Status for FINN example networks -| | Basic test | LFC-w1a1 | LFC-w1a2 | CNV-w1a1 | CNV-w1a2 | CNV-w2a2 | +| | Basic test | TFC-w1a1 | TFC-w1a2 | CNV-w1a1 | CNV-w1a2 | CNV-w2a2 | |--------------------------- |------------ |---------- |---------- |---------- |---------- |---------- | | Export/Import | x | x | x | x | | | | Streamlining | x | x | x | | | | | Convert to HLS layers | x | x | | | | | | npysim | x | x | | | | | -| Stitched IPI design | x | | | | | | +| Stitched IPI design | x | x | | | | | | rtlsim | x | | | | | | -| Hardware test | | | | | | | +| Hardware test | x | | | | | | diff --git a/notebooks/9-FINN-EndToEndFlow.ipynb b/notebooks/9-FINN-EndToEndFlow.ipynb index 144c26bc5cbaa89314fe90c9b2b07990db0f1c4b..9011d456d987e804715c4a046e155390b953e4f7 100644 --- a/notebooks/9-FINN-EndToEndFlow.ipynb +++ b/notebooks/9-FINN-EndToEndFlow.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -99,6 +99,7 @@ } ], "source": [ + "import onnx\n", "from finn.util.test import get_fc_model_trained\n", "import brevitas.onnx as bo\n", "\n", @@ -163,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -232,7 +233,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -261,13 +262,15 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "\n", + "Stopping http://0.0.0.0:8081\n", "Serving '/workspace/finn/tfc_w1_a1.onnx' at http://0.0.0.0:8081\n" ] } @@ -279,7 +282,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -312,7 +315,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -364,7 +367,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -385,7 +388,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [ { diff --git a/src/finn/transformation/fpgadataflow/codegen_ipstitch.py b/src/finn/transformation/fpgadataflow/codegen_ipstitch.py index 4cb148b17610719276410c4fa35a27bee6ac7493..a39b3c439db4ac4cbc1f2c2e9a39993e9adbc447 100644 --- a/src/finn/transformation/fpgadataflow/codegen_ipstitch.py +++ b/src/finn/transformation/fpgadataflow/codegen_ipstitch.py @@ -88,6 +88,7 @@ class CodeGen_ipstitch(Transformation): ) # create a temporary folder for the project + prjname = "finn_vivado_stitch_proj" vivado_stitch_proj_dir = make_build_dir(prefix="vivado_stitch_proj_") model.set_metadata_prop("vivado_stitch_proj", vivado_stitch_proj_dir) # start building the tcl script @@ -95,7 +96,7 @@ class CodeGen_ipstitch(Transformation): # create vivado project tcl.append( "create_project %s %s -part %s" - % ("finn_vivado_stitch_proj", vivado_stitch_proj_dir, self.fpgapart) + % (prjname, vivado_stitch_proj_dir, self.fpgapart) ) # add all the generated IP dirs to ip_repo_paths ip_dirs_str = " ".join(ip_dirs) @@ -125,6 +126,23 @@ class CodeGen_ipstitch(Transformation): tcl.append("ipx::create_xgui_files [ipx::find_open_core %s]" % block_vlnv) tcl.append("ipx::update_checksums [ipx::find_open_core %s]" % block_vlnv) tcl.append("ipx::save_core [ipx::find_open_core %s]" % block_vlnv) + # create wrapper hdl (for rtlsim later on) + bd_base = "%s/%s.srcs/sources_1/bd/%s" % ( + vivado_stitch_proj_dir, + prjname, + block_name, + ) + bd_filename = "%s/%s.bd" % (bd_base, block_name) + tcl.append("make_wrapper -files [get_files %s] -top" % bd_filename) + wrapper_filename = "%s/hdl/%s_wrapper.v" % (bd_base, block_name) + tcl.append("add_files -norecurse %s" % wrapper_filename) + model.set_metadata_prop("wrapper_filename", wrapper_filename) + # export list of used Verilog files (for rtlsim later on) + tcl.append("set all_v_files [get_files -filter {FILE_TYPE == Verilog}]") + v_file_list = "%s/all_verilog_srcs.txt" % vivado_stitch_proj_dir + tcl.append("set fp [open %s w]" % v_file_list) + tcl.append("puts $fp $all_v_files") + tcl.append("close $fp") # write the project creator tcl script tcl_string = "\n".join(tcl) + "\n" with open(vivado_stitch_proj_dir + "/make_project.tcl", "w") as f: diff --git a/src/finn/util/fpgadataflow.py b/src/finn/util/fpgadataflow.py index 9434b16ecc1ce4f6d05e5692a00cdfe9c59edbb6..f11192a121a4b4a8a0524629fe6b3988923a363b 100644 --- a/src/finn/util/fpgadataflow.py +++ b/src/finn/util/fpgadataflow.py @@ -1,12 +1,8 @@ -import subprocess import os -import numpy as np +import subprocess + +from pyverilator import PyVerilator -from finn.core.datatype import DataType -from finn.util.data_packing import ( - pack_innermost_dim_as_hex_string, - unpack_innermost_dim_from_hex_string, -) class IPGenBuilder: def __init__(self): @@ -34,3 +30,18 @@ class IPGenBuilder: bash_command = ["bash", self.ipgen_script] process_compile = subprocess.Popen(bash_command, stdout=subprocess.PIPE) process_compile.communicate() + + +def pyverilate_stitched_ip(model): + "Given a model with stitched IP, return a PyVerilator sim object." + vivado_stitch_proj_dir = model.get_metadata_prop("vivado_stitch_proj") + with open(vivado_stitch_proj_dir + "/all_verilog_srcs.txt", "r") as f: + all_verilog_srcs = f.read().split() + + def file_to_dir(x): + return os.path.dirname(os.path.realpath(x)) + + all_verilog_dirs = list(map(file_to_dir, all_verilog_srcs)) + top_verilog = model.get_metadata_prop("wrapper_filename") + sim = PyVerilator.build(top_verilog, verilog_path=all_verilog_dirs) + return sim diff --git a/tests/fpgadataflow/test_fpgadataflow_ip_stitch.py b/tests/fpgadataflow/test_fpgadataflow_ip_stitch.py index a4191a87926797a25f5e15f7225118643cf21deb..b00ac971f3e8bd403aadadfd956b96b3659746af 100644 --- a/tests/fpgadataflow/test_fpgadataflow_ip_stitch.py +++ b/tests/fpgadataflow/test_fpgadataflow_ip_stitch.py @@ -27,6 +27,7 @@ from finn.util.basic import ( make_build_dir, pynq_part_map, ) +from finn.util.fpgadataflow import pyverilate_stitched_ip test_pynq_board = os.getenv("PYNQ_BOARD", default="Pynq-Z1") test_fpga_part = pynq_part_map[test_pynq_board] @@ -220,6 +221,25 @@ def test_fpgadataflow_ipstitch_do_stitch(): model.save(ip_stitch_model_dir + "/test_fpgadataflow_ip_stitch.onnx") +@pytest.mark.dependency(depends=["test_fpgadataflow_ipstitch_do_stitch"]) +def test_fpgadataflow_ipstitch_rtlsim(): + model = ModelWrapper(ip_stitch_model_dir + "/test_fpgadataflow_ip_stitch.onnx") + sim = pyverilate_stitched_ip(model) + exp_io = [ + "ap_clk_0", + "ap_rst_n_0", + "in0_V_V_0_tdata", + "in0_V_V_0_tready", + "in0_V_V_0_tvalid", + "out_r_0_tdata", + "out_r_0_tkeep", + "out_r_0_tlast", + "out_r_0_tready", + "out_r_0_tvalid", + ] + assert dir(sim.io) == exp_io + + @pytest.mark.dependency(depends=["test_fpgadataflow_ipstitch_do_stitch"]) def test_fpgadataflow_ipstitch_pynq_projgen(): model = ModelWrapper(ip_stitch_model_dir + "/test_fpgadataflow_ip_stitch.onnx")