Skip to content

Commit c79b9e6

Browse files
committed
Merge branch 'finn-dev' into sync/finn-dev-2805
2 parents e4efa8a + 51abad9 commit c79b9e6

20 files changed

Lines changed: 321 additions & 510 deletions

docs/finn/command_line.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ as it goes through numerous steps:
8181
Running step: step_measure_rtlsim_performance [15/19]
8282
Running step: step_out_of_context_synthesis [16/19]
8383
Running step: step_synthesize_bitfile [17/19]
84-
Running step: step_make_pynq_driver [18/19]
84+
Running step: step_make_driver [18/19]
8585
Running step: step_deployment_package [19/19]
8686
8787

docs/finn/hw_build.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ To rapidly test the generated design on PYNQ platforms, FINN is capable of
3535
generating a Python driver for the given design. This driver packs/unpacks the
3636
input/output tensors in the expected format, then uses PYNQ APIs to initiate
3737
data movement and transfer back the results to the host CPU. The generation of
38-
the driver is done by transformation pass :py:mod:`finn.transformation.fpgadataflow.make_pynq_driver.MakePYNQDriver`.
38+
the driver is done by transformation pass :py:mod:`finn.transformation.fpgadataflow.make_driver.MakePYNQDriver`.
3939

4040
DMA and DWC Node Insertion
4141
---------------------------

docs/finn/source_code/finn.transformation.fpgadataflow.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ finn.transformation.fpgadataflow.insert\_tlastmarker
149149
finn.transformation.fpgadataflow.make\_pynq\_driver
150150
----------------------------------------------------------
151151

152-
.. automodule:: finn.transformation.fpgadataflow.make_pynq_driver
152+
.. automodule:: finn.transformation.fpgadataflow.make_driver
153153
:members:
154154
:undoc-members:
155155
:show-inheritance:

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ toposort = "1.7.0"
5454
vcdvcd = "1.0.5"
5555
wget = "3.2"
5656
pygments = "2.14.0"
57-
torch = "2.6.0"
58-
torchvision = "0.21.0"
57+
torch = "2.7.0"
58+
torchvision = "0.22.0"
5959
ipykernel = "6.21.2"
6060
jupyter = "1.0.0"
6161
markupsafe = "2.0.1"

src/finn/builder/build_dataflow_steps.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -837,15 +837,13 @@ def step_make_driver(model: ModelWrapper, cfg: DataflowBuildConfig):
837837
log.info("PYNQ Python driver written into " + driver_dir)
838838
elif DataflowOutputType.CPP_DRIVER in cfg.generate_outputs:
839839
# generate C++ Driver
840-
841840
model = model.transform(
842841
MakeCPPDriver(
843842
cfg._resolve_driver_platform(),
844-
build_dir=cfg.output_dir,
845843
version=cfg.cpp_driver_version,
846-
driver_dir=driver_dir,
847844
)
848845
)
846+
shutil.copytree(model.get_metadata_prop("cpp_driver_dir"), driver_dir, dirs_exist_ok=True)
849847
log.info("C++ driver written into " + driver_dir)
850848
else:
851849
log.warning(

src/finn/custom_op/fpgadataflow/addstreams.py

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2727
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2828

29+
import math
2930
import numpy as np
3031
from qonnx.core.datatype import DataType
3132

@@ -46,7 +47,7 @@ def get_nodeattr_types(self):
4647
"NumChannels": ("i", True, ""),
4748
"PE": ("i", True, ""),
4849
# FINN DataTypes for inputs; output datatype inferred from input
49-
"inputDataType": ("s", True, ""),
50+
"inputDataTypes": ("strings", True, [""]),
5051
# number of input vectors, examples:
5152
# [1] is a single vector (like a FC layer with batch=1)
5253
# [4] is four vectors (like a FC layer with batch=4)
@@ -78,38 +79,49 @@ def get_folded_output_shape(self, ind=0):
7879
return self.get_folded_input_shape()
7980

8081
def infer_node_datatype(self, model):
81-
node = self.onnx_node
82-
idt = model.get_tensor_datatype(node.input[0])
83-
if idt != self.get_input_datatype():
84-
warn_str = "inputDataType changing for %s: %s -> %s " % (
85-
node.name,
86-
str(self.get_input_datatype()),
87-
str(idt),
88-
)
89-
log.warning(warn_str)
90-
self.set_nodeattr("inputDataType", idt.name)
82+
# check all input datatypes
83+
for i, inp in enumerate(self.onnx_node.input):
84+
idt = model.get_tensor_datatype(inp)
85+
if idt != self.get_input_datatype(i):
86+
warn_str = "inputDataType changing for %s: %s -> %s " % (
87+
self.onnx_node.name,
88+
str(self.get_input_datatype(i)),
89+
str(idt),
90+
)
91+
log.warning(warn_str)
92+
old_datatypes_attr = self.get_nodeattr("inputDataTypes")
93+
old_datatypes_attr[i] = idt.name
94+
self.set_nodeattr("inputDataTypes", old_datatypes_attr)
9195
# enforce output data type (calculated based on idt)
9296
odt = self.get_output_datatype()
9397
model.set_tensor_datatype(self.onnx_node.output[0], odt)
9498

9599
def get_input_datatype(self, ind=0):
96100
"""Returns FINN DataType of input."""
97-
return DataType[self.get_nodeattr("inputDataType")]
101+
return DataType[self.get_nodeattr("inputDataTypes")[ind]]
98102

99103
def get_output_datatype(self, ind=0):
100104
"""Returns FINN DataType of output."""
101-
# we need to set output datatype to the next larger int or uint
102-
# enhancement: consider specifying w/ explicit outputDataType attribute
103-
# to allow overflow and use the same idt if user wants
104-
idt = DataType[self.get_nodeattr("inputDataType")]
105-
if idt.signed():
106-
return DataType.get_smallest_possible(2 * idt.min())
105+
min_input = 0
106+
max_input = 0
107+
for i in range(len(self.get_nodeattr("inputDataTypes"))):
108+
idt = self.get_input_datatype(i)
109+
if idt.min() < min_input:
110+
min_input = idt.min()
111+
if idt.max() > max_input:
112+
max_input = idt.max()
113+
if min_input >= 0:
114+
out_bit_width = math.ceil(np.log2(max_input + 1))
115+
odt = DataType[f"UINT{out_bit_width + 1}"]
107116
else:
108-
return DataType.get_smallest_possible(2 * idt.max())
117+
max_abs_input = max(-min_input, 1 + max_input)
118+
out_bit_width = math.ceil(np.log2(max_abs_input) + 1)
119+
odt = DataType[f"INT{out_bit_width + 1}"]
120+
return odt
109121

110122
def get_instream_width(self, ind=0):
111123
"""Returns input stream width."""
112-
ibits = self.get_input_datatype().bitwidth()
124+
ibits = self.get_input_datatype(ind).bitwidth()
113125
pe = self.get_nodeattr("PE")
114126
in_width = pe * ibits
115127
return in_width

src/finn/custom_op/fpgadataflow/hls/addstreams_hls.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def verify_node(self):
5757
self.get_nodeattr("executable_path")
5858
self.get_nodeattr("NumChannels")
5959
self.get_nodeattr("PE")
60-
self.get_nodeattr("inputDataType")
60+
self.get_nodeattr("inputDataTypes")
6161
info_messages.append("All necessary attributes exist")
6262
except Exception:
6363
info_messages.append("""The required LabelSelect_Batch attributes do not exist.""")
@@ -76,10 +76,10 @@ def defines(self, var):
7676
def strm_decl(self):
7777
self.code_gen_dict["$STREAMDECLARATIONS$"] = []
7878
self.code_gen_dict["$STREAMDECLARATIONS$"].append(
79-
'hls::stream<ap_uint<{}>> in0_V ("in0_V");'.format(self.get_instream_width())
79+
'hls::stream<ap_uint<{}>> in0_V ("in0_V");'.format(self.get_instream_width(0))
8080
)
8181
self.code_gen_dict["$STREAMDECLARATIONS$"].append(
82-
'hls::stream<ap_uint<{}>> in1_V ("in1_V");'.format(self.get_instream_width())
82+
'hls::stream<ap_uint<{}>> in1_V ("in1_V");'.format(self.get_instream_width(1))
8383
)
8484
self.code_gen_dict["$STREAMDECLARATIONS$"].append(
8585
'hls::stream<ap_uint<{}>> out0_V ("out0_V");'.format(self.get_outstream_width())
@@ -91,8 +91,8 @@ def docompute(self):
9191
"""{}<{}, {}, {}, {}, {}> (in0_V, in1_V, out0_V, 1);""".format(
9292
hls_call,
9393
self.get_nodeattr("PE"),
94-
self.get_input_datatype().get_hls_datatype_str(),
95-
self.get_input_datatype().get_hls_datatype_str(),
94+
self.get_input_datatype(0).get_hls_datatype_str(),
95+
self.get_input_datatype(1).get_hls_datatype_str(),
9696
self.get_output_datatype().get_hls_datatype_str(),
9797
self.get_number_output_values(),
9898
)
@@ -103,8 +103,8 @@ def blackboxfunction(self):
103103
"""void {}(hls::stream<ap_uint<{}>> &in0_V, hls::stream<ap_uint<{}>> &in1_V,
104104
hls::stream<ap_uint<{}>> &out0_V)""".format(
105105
self.onnx_node.name,
106-
self.get_nodeattr("PE") * self.get_input_datatype().bitwidth(),
107-
self.get_nodeattr("PE") * self.get_input_datatype().bitwidth(),
106+
self.get_nodeattr("PE") * self.get_input_datatype(0).bitwidth(),
107+
self.get_nodeattr("PE") * self.get_input_datatype(1).bitwidth(),
108108
self.get_nodeattr("PE") * self.get_output_datatype().bitwidth(),
109109
)
110110
]

src/finn/custom_op/fpgadataflow/rtl/matrixvectoractivation_rtl.py

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ def execute_node(self, context, graph):
7575
str(context[inputs].dtype) == "float32"
7676
), """Input datatype is
7777
not float32 as expected."""
78-
expected_inp_shape = self.get_folded_input_shape()
78+
expected_inp_shape = self.get_folded_input_shape(in_ind)
7979
reshaped_input = context[inputs].reshape(expected_inp_shape)
80-
export_idt = self.get_input_datatype(0)
80+
export_idt = self.get_input_datatype(in_ind)
8181
# make copy before saving the array
8282
reshaped_input = reshaped_input.copy()
8383
np.save(
@@ -87,40 +87,38 @@ def execute_node(self, context, graph):
8787
elif in_ind > 1:
8888
raise Exception("Unexpected input found for MatrixVectorActivation_rtl")
8989
in_ind += 1
90-
sim = self.get_rtlsim()
91-
nbits = self.get_instream_width(0)
92-
inp = npy_to_rtlsim_input("{}/input_0.npy".format(code_gen_dir), export_idt, nbits)
93-
super().reset_rtlsim(sim)
94-
if mem_mode in ["external", "internal_decoupled"]:
95-
wnbits = self.get_instream_width(1)
96-
export_wdt = self.get_input_datatype(1)
97-
wei = npy_to_rtlsim_input(
98-
"{}/weights.npy".format(code_gen_dir), export_wdt, wnbits
99-
)
100-
num_w_reps = np.prod(self.get_nodeattr("numInputVectors"))
101-
io_dict = {
102-
"inputs": {"in0": inp, "in1": wei * num_w_reps},
103-
"outputs": {"out0": []},
104-
}
105-
else:
106-
io_dict = {
107-
"inputs": {"in0": inp},
108-
"outputs": {"out0": []},
109-
}
110-
self.rtlsim_multi_io(sim, io_dict)
111-
super().close_rtlsim(sim)
112-
output = io_dict["outputs"]["out0"]
113-
odt = self.get_output_datatype()
114-
target_bits = odt.bitwidth()
115-
packed_bits = self.get_outstream_width()
116-
out_npy_path = "{}/output.npy".format(code_gen_dir)
117-
out_shape = self.get_folded_output_shape()
118-
rtlsim_output_to_npy(output, out_npy_path, odt, out_shape, packed_bits, target_bits)
119-
# load and reshape output
120-
output = np.load(out_npy_path)
121-
oshape = self.get_normal_output_shape()
122-
output = np.asarray([output], dtype=np.float32).reshape(*oshape)
123-
context[node.output[0]] = output
90+
sim = self.get_rtlsim()
91+
nbits = self.get_instream_width(0)
92+
inp = npy_to_rtlsim_input("{}/input_0.npy".format(code_gen_dir), export_idt, nbits)
93+
super().reset_rtlsim(sim)
94+
if mem_mode in ["external", "internal_decoupled"]:
95+
wnbits = self.get_instream_width(1)
96+
export_wdt = self.get_input_datatype(1)
97+
wei = npy_to_rtlsim_input("{}/weights.npy".format(code_gen_dir), export_wdt, wnbits)
98+
num_w_reps = np.prod(self.get_nodeattr("numInputVectors"))
99+
io_dict = {
100+
"inputs": {"in0": inp, "in1": wei * num_w_reps},
101+
"outputs": {"out0": []},
102+
}
103+
else:
104+
io_dict = {
105+
"inputs": {"in0": inp},
106+
"outputs": {"out0": []},
107+
}
108+
self.rtlsim_multi_io(sim, io_dict)
109+
super().close_rtlsim(sim)
110+
output = io_dict["outputs"]["out0"]
111+
odt = self.get_output_datatype()
112+
target_bits = odt.bitwidth()
113+
packed_bits = self.get_outstream_width()
114+
out_npy_path = "{}/output.npy".format(code_gen_dir)
115+
out_shape = self.get_folded_output_shape()
116+
rtlsim_output_to_npy(output, out_npy_path, odt, out_shape, packed_bits, target_bits)
117+
# load and reshape output
118+
output = np.load(out_npy_path)
119+
oshape = self.get_normal_output_shape()
120+
output = np.asarray([output], dtype=np.float32).reshape(*oshape)
121+
context[node.output[0]] = output
124122
else:
125123
raise Exception(
126124
"""Invalid value for attribute exec_mode! Is currently set to: {}

src/finn/transformation/fpgadataflow/convert_to_hw_layers.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -488,14 +488,8 @@ def apply(self, model):
488488
idt0 = model.get_tensor_datatype(in0)
489489
idt1 = model.get_tensor_datatype(in1)
490490

491-
# skip if different data types on inputs
492-
if idt0 != idt1:
493-
continue
494-
495-
idt = idt0
496-
497491
# skip conversion for layers with float input
498-
if not idt.is_integer():
492+
if not idt0.is_integer() or not idt1.is_integer():
499493
continue
500494

501495
# check layout and convert if necessary
@@ -535,7 +529,7 @@ def apply(self, model):
535529
backend="fpgadataflow",
536530
NumChannels=num_channels,
537531
PE=pe,
538-
inputDataType=idt.name,
532+
inputDataTypes=[idt0.name, idt1.name],
539533
numInputVectors=in0_shape[:-1],
540534
name="AddStreams_" + node.name,
541535
)

0 commit comments

Comments
 (0)