Skip to content
Snippets Groups Projects
Commit d6395400 authored by Georg Streich's avatar Georg Streich
Browse files

Cleanup stuff

parent d619f338
No related branches found
No related tags found
No related merge requests found
Showing
with 258 additions and 18774 deletions
......@@ -140,6 +140,5 @@ COPY docker/finn_entrypoint.sh /usr/local/bin/
COPY docker/quicktest.sh /usr/local/bin/
RUN chmod 755 /usr/local/bin/finn_entrypoint.sh
RUN chmod 755 /usr/local/bin/quicktest.sh
ENTRYPOINT ["finn_entrypoint.sh"]
CMD ["bash"]
......@@ -57,7 +57,6 @@ recho () {
# qonnx (using workaround for https://github.com/pypa/pip/issues/7953)
# to be fixed in future Ubuntu versions (https://bugs.launchpad.net/ubuntu/+source/setuptools/+bug/1994016)
pip install --no-build-isolation --no-warn-script-location -e ${FINN_ROOT}/deps/qonnx
# finn-experimental
pip install --user -e ${FINN_ROOT}/deps/finn-experimental
# brevitas
......
This diff is collapsed.
......@@ -35,20 +35,29 @@ steps = [
"step_create_dataflow_partition",
"step_target_fps_parallelization",
"step_apply_folding_config",
"step_minimize_bit_width",
"step_assign_partition_ids",
"step_insert_accl",
"step_split_dataflow",
"step_generate_estimate_reports",
"step_hls_codegen",
"step_hls_ipgen",
"step_set_fifo_depths",
"step_create_stitched_ip",
"step_setup_accl_interface",
]
cfg_splits = build.DataflowBuildConfig(
verbose = True,
output_dir = estimates_output_dir,
steps = steps,
mvau_wwidth_max = 1000,
mvau_wwidth_max = 80,
target_fps = 1000000,
synth_clk_period_ns = 10.0,
fpga_part = "xc7z020clg400-1",
generate_outputs = [
build_cfg.DataflowOutputType.ESTIMATE_REPORTS,
build_cfg.DataflowOutputType.STITCHED_IP,
],
# verify_steps = [build_cfg.VerificationStepType.FOLDED_HLS_CPPSIM],
board = 'U250',
......@@ -57,5 +66,5 @@ cfg_splits = build.DataflowBuildConfig(
# start_step = 'step_setup_accl_interface',
)
build.build_dataflow_cfg(model_file, cfg_splits)
build.build_distributed_dataflow_cfg(model_file, cfg_splits)
......@@ -34,7 +34,9 @@ import pdb # NOQA
import sys
import time
import traceback
from copy import deepcopy
from qonnx.core.modelwrapper import ModelWrapper
from qonnx.custom_op.registry import getCustomOp
from finn.builder.build_dataflow_config import (
DataflowBuildConfig,
......@@ -193,7 +195,11 @@ def build_distributed_dataflow_cfg(model_filename, cfg: DataflowBuildConfig):
steps = resolve_build_steps(cfg)
step_names = list(map(lambda x: x.__name__, steps))
split_step = "step_split_dataflow"
# TODO: Not sure if splitting up the config implicitly up is the best way to do this.
# Maybe it would be better for the user to explicitly provide global and local build
# configs.
split_step = "step_split_dataflow"
if split_step not in step_names:
print("Dataflow should be split up as part of distributed build")
return -1
......@@ -201,7 +207,7 @@ def build_distributed_dataflow_cfg(model_filename, cfg: DataflowBuildConfig):
global_cfg = deepcopy(cfg)
global_cfg.steps = steps[:step_names.index(split_step) + 1]
if not cfg.save_intermediate_models:
if not cfg.save_intermediate_models:
print("save_intermediate_models must be enabled for distributed build")
return -1
......@@ -210,7 +216,7 @@ def build_distributed_dataflow_cfg(model_filename, cfg: DataflowBuildConfig):
return -1
intermediate_models_dir = cfg.output_dir + "/intermediate_models"
parent_model = ModelWrapper(intermediate_model_dir)
parent_model = ModelWrapper(f"{intermediate_models_dir}/{split_step}.onnx")
local_cfg = deepcopy(cfg)
local_cfg.steps = steps[step_names.index(split_step) + 1:]
......@@ -225,6 +231,7 @@ def build_distributed_dataflow_cfg(model_filename, cfg: DataflowBuildConfig):
print(f"Launching build for partition {i}")
build_dataflow_cfg(child_model_filename, local_cfg)
def build_dataflow_directory(path_to_cfg_dir: str):
"""Best-effort build a dataflow accelerator from the specified directory.
......
......@@ -380,7 +380,7 @@ def step_create_dataflow_partition(model: ModelWrapper, cfg: DataflowBuildConfig
parent_model = model.transform(
CreateDataflowPartition(
partition_model_dir=cfg.output_dir + "/intermediate_models/supported_op_partitions",
partition_model_dir=cfg.output_dir + "/intermediate_models/supported_op_partitions"
)
)
sdp_nodes = parent_model.get_nodes_by_op_type("StreamingDataflowPartition")
......
......@@ -34,7 +34,7 @@ import psutil
from concurrent.futures import ThreadPoolExecutor, as_completed
import qonnx.analysis.topology as ta
from qonnx.core.onnx_exec import execute_onnx as execute_onnx_base, execute_node
from qonnx.core.onnx_exec import execute_onnx as execute_onnx_base
from qonnx.util.basic import (
get_sanitize_quant_tensors,
sanitize_quant_values,
......@@ -42,6 +42,7 @@ from qonnx.util.basic import (
from finn.core.rtlsim_exec import rtlsim_exec
def execute_onnx(model, input_dict, return_full_exec_context=False, start_node=None, end_node=None):
"""Executes given ONNX ModelWrapper with given named inputs.
If return_full_exec_context is False, a dict of named outputs is returned
......@@ -148,4 +149,3 @@ def compare_execution(
res_b = list(execute_onnx(model_b, input_dict).items())[0][1]
return compare_fxn(res_a, res_b)
# Copyright (c) 2020, Xilinx
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of FINN nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# 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 math
import numpy as np
import warnings
......@@ -95,7 +67,7 @@ class ACCLOp(HLSCustomOp):
self.set_nodeattr("executable_path", code_gen_dir + "/node_model")
def execute_kernel(self, edge_name):
def execute_op(self, edge_name):
idx = ACCLOp.barriers[edge_name].wait()
emulator = None
try:
......@@ -332,8 +304,10 @@ class ACCLOut(ACCLOp):
reshaped_input,
)
# Execute node in a new thread so execution can continue to the receiving ACCLIn
# node.
self.thread = threading.Thread(
target=self.execute_kernel,
target=self.execute_op,
args=(self.onnx_node.output[0],)
)
self.thread.start()
......@@ -475,7 +449,7 @@ class ACCLIn(ACCLOp):
code_gen_dir = self.get_nodeattr("code_gen_dir_cppsim")
self.execute_kernel(self.onnx_node.input[0])
self.execute_op(self.onnx_node.input[0])
super().npy_to_dynamic_output(context)
......
......@@ -344,14 +344,12 @@ class HLSCustomOp(CustomOp):
def ipgen_singlenode_code(self):
"""Builds the bash script for IP generation using the CallHLS utility."""
node = self.onnx_node
code_gen_dir = self.get_nodeattr("code_gen_dir_ipgen")
builder = CallHLS()
builder.append_tcl(code_gen_dir + "/hls_syn_{}.tcl".format(node.name))
builder.set_ipgen_path(code_gen_dir + "/project_{}".format(node.name))
builder.build(code_gen_dir)
ipgen_path = builder.ipgen_path
assert os.path.isdir(ipgen_path), "IPGen failed: %s not found" % (ipgen_path)
self.set_nodeattr("ipgen_path", ipgen_path)
ip_path = ipgen_path + "/sol1/impl/ip"
......
......@@ -70,10 +70,7 @@ class StreamingDataflowPartition(CustomOp):
if old_iname != new_iname:
inp_ctx[new_iname] = inp_ctx[old_iname]
del inp_ctx[old_iname]
ret = execute_onnx(model, inp_ctx, return_full_exec_context)
# outputs may have been renamed in partition
for i, node_oname in enumerate(node.output):
model_oname = model.graph.output[i].name
......
......@@ -610,7 +610,6 @@ class InferBinaryMatrixVectorActivation(Transformation):
graph = model.graph
node_ind = 0
graph_modified = False
for n in graph.node:
node_ind += 1
if n.op_type == "XnorPopcountMatMul":
......@@ -745,9 +744,7 @@ class InferQuantizedMatrixVectorActivation(Transformation):
graph = model.graph
node_ind = 0
graph_modified = False
for n in graph.node:
node_ind += 1
if n.op_type == "MatMul" and model.get_tensor_sparsity(n.input[1]) is None:
mm_input = n.input[0]
......
# Copyright (c) 2020, Xilinx # All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of FINN nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# 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 math
import numpy as np
from onnx import TensorProto
......@@ -86,15 +59,16 @@ class InsertACCL(Transformation):
dataType=str(tensor_dtype),
domain="finn.custom_op.fpgadataflow",
backend="fpgadataflow",
device_id=producer_rank,
worldSize=world_size,
otherRank=consumer_rank,
)
getCustomOp(accl_out).set_nodeattr("device_id", producer_rank)
# As we are sorting the graph afterwards it should be fine to insert this at
# beginning
model.graph.node.insert(0, accl_out)
consumer_shape = producer_inst.get_folded_output_shape()
consumer_shape = consumer_inst.get_folded_input_shape()
accl_in = oh.make_node(
"ACCLIn",
......@@ -105,12 +79,11 @@ class InsertACCL(Transformation):
dataType=str(tensor_dtype),
domain="finn.custom_op.fpgadataflow",
backend="fpgadataflow",
device_id=consumer_rank,
worldSize=world_size,
otherRank=producer_rank,
)
getCustomOp(accl_in).set_nodeattr("device_id", consumer_rank)
model.graph.node.insert(0, accl_in)
for idx, inp in enumerate(consumer.input):
......
......@@ -95,7 +95,6 @@ class InsertFIFO(Transformation):
graph = model.graph
node_ind = -1
graph_modified = False
for first_node in graph.node:
node_ind += 1
if _suitable_node(first_node):
......@@ -182,8 +181,8 @@ class InsertFIFO(Transformation):
graph_in_names = [x.name for x in model.graph.input]
for graph_in_name in graph_in_names:
first_node = model.find_consumer(graph_in_name)
# insert FIFO as first node, except when first node is DMA, ACCL
if first_node.op_type not in ["StreamingFIFO", "IODMA"]:
# insert FIFO as first node, except when first node is DMA
if first_node.op_type != "StreamingFIFO" and first_node.op_type != "IODMA":
inp_ind = list(first_node.input).index(graph_in_name)
n_input = first_node.input[inp_ind]
n0 = getCustomOp(first_node)
......@@ -231,11 +230,11 @@ class InsertFIFO(Transformation):
% (graph_in_name, fifo_depth)
)
# insert FIFO as last node, except when last node is DMA, ACCL
# insert FIFO as last node, except when last node is DMA
graph_out_names = [x.name for x in model.graph.output]
for graph_out_name in graph_out_names:
final_node = model.find_producer(graph_out_name)
if final_node.op_type not in ["StreamingFIFO", "IODMA"]:
if final_node.op_type != "StreamingFIFO" and final_node.op_type != "IODMA":
assert (
final_node.op_type != "TLastMarker"
), """Insert tlast marker should be done
......
......@@ -106,7 +106,7 @@ class InsertIODMA(Transformation):
graph_in_names = [x.name for x in model.graph.input]
for graph_in_name in graph_in_names:
first_node = model.find_consumer(graph_in_name)
if first_node.p_type in ["IODMA", "Accl"]:
if first_node.op_type == "IODMA":
# IODMA already inserted for this input
continue
else:
......@@ -153,7 +153,7 @@ class InsertIODMA(Transformation):
graph_out_names = [x.name for x in model.graph.output]
for graph_out_name in graph_out_names:
final_node = model.find_producer(graph_out_name)
if final_node.p_type in ["IODMA", "Accl"]:
if final_node.op_type == "IODMA":
continue
else:
out_shape = model.get_tensor_shape(graph_out_name)
......
......@@ -338,7 +338,6 @@ class InsertAndSetFIFODepths(Transformation):
# set sufficiently large threshold for 1 image to fully execute and exit
ncycles = int(latency + max_cycles)
# prepare pyverilator model
sim = pyverilate_stitched_ip(model)
......@@ -377,13 +376,13 @@ class InsertAndSetFIFODepths(Transformation):
if len(swg_nodes) == 0:
# MLP, no layer overlap
# assuming half the nodes are now FIFOs, use half the # of
# nodes as # inputs to drive the 2mulation
# nodes as # inputs to drive the imulation
n_inputs = int(len(model.graph.node) / 2)
else:
# convnet, two inputs are typically enough to fill entire
# layer pipeline due to overlaps
n_inputs = 2
sim = verilator_fifosim(model, n_inputs, max_iters=10000)
sim = verilator_fifosim(model, n_inputs)
for ind, node in enumerate(fifo_nodes):
maxcount_name = "maxcount_%d" % ind
......
import subprocess
from qonnx.transformation.base import Transformation
from distutils.dir_util import copy_tree
from finn.util.basic import make_build_dir
class SetupACCLInterface(Transformation):
def __init__(self, ip_name="finn_design"):
......@@ -8,43 +11,54 @@ class SetupACCLInterface(Transformation):
def apply(self, model):
vivado_stitch_proj_dir = model.get_metadata_prop("vivado_stitch_proj")
project_dir = make_build_dir("accl_ip")
model.set_metadata_prop("accl_ip_dir", project_dir)
copy_tree(vivado_stitch_proj_dir, project_dir)
prjname = "finn_vivado_stitch_proj"
tcl = []
tcl.append(f"open_project {vivado_stitch_proj_dir}/{prjname}.xpr")
tcl.append(f"open_project {project_dir}/{prjname}.xpr")
# TODO: Maybe we can avoid writing out the full path out here, seems a bit brittle
tcl.append(f"open_bd_design {vivado_stitch_proj_dir}/{prjname}.srcs/sources_1/bd/{self.ip_name}/{self.ip_name}.bd")
tcl.append(f"open_bd_design {project_dir}/{prjname}.srcs/sources_1/bd/{self.ip_name}/{self.ip_name}.bd")
has_accl_input = bool(model.get_metadata_prop("has_accl_input"))
has_accl_output = bool(model.get_metadata_prop("has_accl_output"))
has_accl_in = any(node.op_type == "ACCLIn" for node in model.graph.node)
if has_accl_input:
tcl.append('set_property name data_from_cclo [get_bd_intf_ports s_axis_0]')
tcl.append('create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:axis_rtl:1.0 s_axis_0')
if has_accl_in:
tcl.append("set_property name data_from_cclo [get_bd_intf_ports s_axis_0]")
tcl.append("create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:axis_rtl:1.0 s_axis_0")
else:
tcl.append('create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:axis_rtl:1.0 data_from_cclo')
tcl.append("create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:axis_rtl:1.0 data_from_cclo")
graph_out_names = [x.name for x in model.graph.output]
assert len(graph_out_names) == 1, "Expected only one output at this point"
final_node = model.find_producer(graph_out_names[0])
accl_out_node = None
for node in model.graph.node:
if node.op_type == "ACCLOut":
accl_out_node = node
break
if has_accl_output:
tcl.append('set_property name data_to_cclo [get_bd_intf_ports m_axis_0]')
tcl.append('create_bd_intf_port -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 m_axis_0')
if accl_out_node is not None:
tcl.append("set_property name data_to_cclo [get_bd_intf_ports m_axis_0]")
tcl.append("create_bd_intf_port -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 m_axis_0")
# TODO: In a case where we have multiple nodes that access this interface we
# need to add an arbiter for these and the data streams.
tcl += [
'make_bd_intf_pins_external [get_bd_intf_pins {}/{}]'.format(
final_node.name,
"make_bd_intf_pins_external [get_bd_intf_pins {}/{}]".format(
accl_out_node.name,
pin_name
)
for pin_name in ["cmd_to_cclo", "sts_from_cclo"]
]
tcl.append("create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_0")
tcl.append("connect_bd_net [get_bd_pins xlconstant_0/dout] [get_bd_pins {}/wait_for_ack]".format(accl_out_node.name))
else:
tcl.append('create_bd_intf_port -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 cmd_to_cclo')
tcl.append('create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:axis_rtl:1.0 sts_from_cclo')
tcl.append('create_bd_intf_port -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 data_to_cclo')
tcl.append("create_bd_intf_port -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 cmd_to_cclo")
tcl.append("create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:axis_rtl:1.0 sts_from_cclo")
tcl.append("create_bd_intf_port -mode Master -vlnv xilinx.com:interface:axis_rtl:1.0 data_to_cclo")
tcl.append('save_bd_design')
tcl.append("save_bd_design")
tcl_string = "\n".join(tcl) + "\n"
......
......@@ -176,14 +176,11 @@ class CppBuilder:
with open(self.compile_script, "w") as f:
f.write("#!/bin/bash \n")
f.write(bash_compile + "\n")
bash_command = ["bash", self.compile_script]
process_compile = subprocess.Popen(bash_command, stdout=subprocess.PIPE)
process_compile.communicate()
def launch_process_helper(args, proc_env=None, cwd=None):
"""Helper function to launch a process in a way that facilitates logging
stdout/stderr with Python loggers.
......
......@@ -65,5 +65,5 @@ class CallHLS:
f.write("cd {}\n".format(working_dir))
f.close()
bash_command = ["bash", self.ipgen_script]
process_compile = subprocess.run(bash_command)
# process_compile.communicate()
process_compile = subprocess.Popen(bash_command, stdout=subprocess.PIPE)
process_compile.communicate()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment