From f9d86f40aed7807db88062719e5efdddbfad7016 Mon Sep 17 00:00:00 2001 From: Yaman Umuroglu <maltanar@gmail.com> Date: Tue, 27 Sep 2022 18:34:11 +0200 Subject: [PATCH] [FIFO] add new verilator_fifosim util function to call C++ rtlsim --- src/finn/util/pyverilator.py | 168 ++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/src/finn/util/pyverilator.py b/src/finn/util/pyverilator.py index f6a51da8e..f66458b9e 100644 --- a/src/finn/util/pyverilator.py +++ b/src/finn/util/pyverilator.py @@ -26,10 +26,176 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import pkg_resources as pk + +import numpy as np import os +import shutil from pyverilator import PyVerilator +from qonnx.custom_op.registry import getCustomOp + +from finn.util.basic import ( + get_rtlsim_trace_depth, + launch_process_helper, + make_build_dir, +) + + +def make_single_verilog_file(model): + """Dump all Verilog code used by stitched IP into a single file. + This is because large models with many files require a verilator + command line too long for bash on most systems""" + + 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)) + + def file_to_basename(x): + return os.path.basename(os.path.realpath(x)) + + top_module_file_name = file_to_basename(model.get_metadata_prop("wrapper_filename")) + + # dump all Verilog code to a single file + # this is because large models with many files require + # a verilator command line too long for bash on most systems + # NOTE: there are duplicates in this list, and some files + # are identical but in multiple directories (regslice_core.v) + + # remove duplicates from list by doing list -> set -> list + all_verilog_files = list( + set(filter(lambda x: x.endswith(".v") or x.endswith(".sv"), all_verilog_srcs)) + ) + + # remove all but one instances of regslice_core.v + filtered_verilog_files = [] + remove_entry = False + for vfile in all_verilog_files: + if "regslice_core" in vfile: + if not remove_entry: + filtered_verilog_files.append(vfile) + remove_entry = True + else: + filtered_verilog_files.append(vfile) + + # concatenate all verilog code into a single file + with open(vivado_stitch_proj_dir + "/" + top_module_file_name, "w") as wf: + for vfile in filtered_verilog_files: + with open(vfile) as rf: + wf.write("//Added from " + vfile + "\n\n") + wf.write(rf.read()) + return vivado_stitch_proj_dir + + +def verilator_fifosim(model, n_inputs, max_iters=100000000): + """Create a Verilator model of stitched IP and use a simple C++ + driver to drive the input stream. Useful for FIFO sizing, latency + and throughput measurement.""" + + vivado_stitch_proj_dir = make_single_verilog_file(model) + build_dir = make_build_dir("verilator_fifosim_") + fifosim_cpp_fname = pk.resource_filename( + "finn.qnn-data", "cpp/verilator_fifosim.cpp" + ) + with open(fifosim_cpp_fname, "r") as f: + fifosim_cpp_template = f.read() + assert len(model.graph.input) == 1, "Only a single input stream is supported" + assert len(model.graph.output) == 1, "Only a single output stream is supported" + iname = model.graph.input[0].name + first_node = model.find_consumer(iname) + oname = model.graph.output[0].name + last_node = model.find_producer(oname) + assert (first_node is not None) and ( + last_node is not None + ), "Failed to find first/last nodes" + fnode_inst = getCustomOp(first_node) + lnode_inst = getCustomOp(last_node) + ishape_folded = fnode_inst.get_folded_input_shape() + oshape_folded = lnode_inst.get_folded_output_shape() + + fifo_log = [] + fifo_log_templ = ' results_file << "maxcount%s" << "\\t" ' + fifo_log_templ += "<< to_string(top->maxcount%s) << endl;" + fifo_nodes = model.get_nodes_by_op_type("StreamingFIFO") + fifo_ind = 0 + for fifo_node in fifo_nodes: + fifo_node = getCustomOp(fifo_node) + if fifo_node.get_nodeattr("depth_monitor") == 1: + suffix = "" if fifo_ind == 0 else "_%d" % fifo_ind + fifo_log.append(fifo_log_templ % (suffix, suffix)) + fifo_ind += 1 + fifo_log = "\n".join(fifo_log) + + template_dict = { + "ITERS_PER_INPUT": np.prod(ishape_folded[:-1]), + "ITERS_PER_OUTPUT": np.prod(oshape_folded[:-1]), + "N_INPUTS": n_inputs, + "MAX_ITERS": max_iters, + "FIFO_DEPTH_LOGGING": fifo_log, + } + + for (key, val) in template_dict.items(): + fifosim_cpp_template = fifosim_cpp_template.replace(f"@{key}@", str(val)) + + with open(build_dir + "/verilator_fifosim.cpp", "w") as f: + f.write(fifosim_cpp_template) + + which_verilator = shutil.which("verilator") + if which_verilator is None: + raise Exception("'verilator' executable not found") + + verilator_args = [ + "perl", + which_verilator, + "-Wno-fatal", + "-Mdir", + build_dir, + "-y", + vivado_stitch_proj_dir, + "--CFLAGS", + "--std=c++11", + "-O3", + "--x-assign", + "fast", + "--x-initial", + "fast", + "--noassert", + "--cc", + "finn_design_wrapper.v", + "--top-module", + "finn_design_wrapper", + "--exe", + "verilator_fifosim.cpp", + "--threads", + "4", + ] + launch_process_helper(verilator_args, cwd=build_dir) + + proc_env = os.environ.copy() + proc_env["OPT_FAST"] = "-O3 -march=native" + make_args = [ + "make", + "-j4", + "-C", + build_dir, + "-f", + "Vfinn_design_wrapper.mk", + "Vfinn_design_wrapper", + ] + launch_process_helper(make_args, proc_env=proc_env, cwd=build_dir) + + sim_launch_args = ["./Vfinn_design_wrapper"] + launch_process_helper(sim_launch_args, cwd=build_dir) -from finn.util.basic import get_rtlsim_trace_depth, make_build_dir + with open(build_dir + "/results.txt", "r") as f: + results = f.read().strip().split("\n") + ret_dict = {} + for result_line in results: + key, val = result_line.split("\t") + ret_dict[key] = int(val) + return ret_dict def pyverilate_stitched_ip( -- GitLab