Skip to content

Commit

Permalink
Merge pull request #1248 from Xilinx/infra/hlsvector
Browse files Browse the repository at this point in the history
Infrastructure to incorporate components with hls::vector interface
  • Loading branch information
auphelia authored Dec 9, 2024
2 parents a054165 + 95bc8a6 commit d1b286a
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 29 deletions.
112 changes: 83 additions & 29 deletions src/finn/custom_op/fpgadataflow/hlsbackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ def get_nodeattr_types(self):
"code_gen_dir_cppsim": ("s", False, ""),
"executable_path": ("s", False, ""),
"res_hls": ("s", False, ""),
# temporary node attribute to keep track of interface style of hls ops
"cpp_interface": ("s", False, "packed", {"packed", "hls_vector"}),
}

def get_all_verilog_paths(self):
Expand Down Expand Up @@ -206,7 +208,13 @@ def code_generation_cppsim(self, model):
self.dataoutstrm()
self.save_as_npy()

template = templates.docompute_template
if self.get_nodeattr("cpp_interface") == "hls_vector":
self.timeout_value()
self.timeout_condition()
self.timeout_read_stream()
template = templates.docompute_template_timeout
else:
template = templates.docompute_template

for key in self.code_gen_dict:
# transform list into long string separated by '\n'
Expand Down Expand Up @@ -371,24 +379,40 @@ def read_npy_data(self):
if dtype == DataType["BIPOLAR"]:
# use binary for bipolar storage
dtype = DataType["BINARY"]
elem_bits = dtype.bitwidth()
packed_bits = self.get_instream_width()
packed_hls_type = "ap_uint<%d>" % packed_bits
elem_hls_type = dtype.get_hls_datatype_str()
npy_type = "float"
npy_in = "%s/input_0.npy" % code_gen_dir
self.code_gen_dict["$READNPYDATA$"] = []
self.code_gen_dict["$READNPYDATA$"].append(
'npy2apintstream<%s, %s, %d, %s>("%s", in0_%s);'
% (
packed_hls_type,
elem_hls_type,
elem_bits,
npy_type,
npy_in,
self.hls_sname(),

cpp_interface = self.get_nodeattr("cpp_interface")

if cpp_interface == "packed":
elem_bits = dtype.bitwidth()
packed_bits = self.get_instream_width()
packed_hls_type = "ap_uint<%d>" % packed_bits
self.code_gen_dict["$READNPYDATA$"].append(
'npy2apintstream<%s, %s, %d, %s>("%s", in0_%s);'
% (
packed_hls_type,
elem_hls_type,
elem_bits,
npy_type,
npy_in,
self.hls_sname(),
)
)
else:
folded_shape = self.get_folded_input_shape()
self.code_gen_dict["$READNPYDATA$"].append(
'npy2vectorstream<%s, %s, %d>("%s", in0_%s, false);'
% (
elem_hls_type,
npy_type,
folded_shape[-1],
npy_in,
self.hls_sname(),
)
)
)

def strm_decl(self):
"""Function to generate the commands for the stream declaration in c++,
Expand Down Expand Up @@ -422,27 +446,43 @@ def dataoutstrm(self):
if dtype == DataType["BIPOLAR"]:
# use binary for bipolar storage
dtype = DataType["BINARY"]
elem_bits = dtype.bitwidth()
packed_bits = self.get_outstream_width()
packed_hls_type = "ap_uint<%d>" % packed_bits
elem_hls_type = dtype.get_hls_datatype_str()
npy_type = "float"
npy_out = "%s/output.npy" % code_gen_dir
oshape = self.get_folded_output_shape()
oshape_cpp_str = str(oshape).replace("(", "{").replace(")", "}")

self.code_gen_dict["$DATAOUTSTREAM$"] = [
'apintstream2npy<%s, %s, %d, %s>(out_%s, %s, "%s");'
% (
packed_hls_type,
elem_hls_type,
elem_bits,
npy_type,
self.hls_sname(),
oshape_cpp_str,
npy_out,
)
]
cpp_interface = self.get_nodeattr("cpp_interface")

if cpp_interface == "packed":
elem_bits = dtype.bitwidth()
packed_bits = self.get_outstream_width()
packed_hls_type = "ap_uint<%d>" % packed_bits

self.code_gen_dict["$DATAOUTSTREAM$"] = [
'apintstream2npy<%s, %s, %d, %s>(out_%s, %s, "%s");'
% (
packed_hls_type,
elem_hls_type,
elem_bits,
npy_type,
self.hls_sname(),
oshape_cpp_str,
npy_out,
)
]
else:
folded_shape = self.get_folded_output_shape()
self.code_gen_dict["$DATAOUTSTREAM$"] = [
'vectorstream2npy<%s, %s, %d>(strm, %s, "%s");'
% (
elem_hls_type,
npy_type,
folded_shape[-1],
oshape_cpp_str,
npy_out,
)
]

def save_as_npy(self):
"""Function to generate the commands for saving data in .npy file in c++"""
Expand Down Expand Up @@ -474,3 +514,17 @@ def get_ap_int_max_w(self):
ret = max([instream, outstream])
assert ret <= 8191, "AP_INT_MAX_W=%d is larger than allowed maximum of 8191" % ret
return ret

def timeout_value(self):
"""Set timeout value for HLS functions defined for one clock cycle"""
self.code_gen_dict["$TIMEOUT_VALUE$"] = ["1000"]

def timeout_condition(self):
"""Set timeout condition for HLS functions defined for one clock cycle"""
self.code_gen_dict["$TIMEOUT_CONDITION$"] = ["out_{}.empty()".format(self.hls_sname())]

def timeout_read_stream(self):
"""Set reading output stream procedure for HLS functions defined for one clock cycle"""
self.code_gen_dict["$TIMEOUT_READ_STREAM$"] = [
"strm << out_{}.read();".format(self.hls_sname())
]
46 changes: 46 additions & 0 deletions src/finn/custom_op/fpgadataflow/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define AP_INT_MAX_W $AP_INT_MAX_W$
#include "cnpy.h"
#include "npy2apintstream.hpp"
#include "npy2vectorstream.hpp"
#include <vector>
#include "bnn-library.h"
Expand All @@ -58,6 +59,51 @@
"""

# template for single node execution with timeout (for single clock hls operations)
docompute_template_timeout = """
#define AP_INT_MAX_W $AP_INT_MAX_W$
#include "cnpy.h"
#include "npy2apintstream.hpp"
#include "npy2vectorstream.hpp"
#include <vector>
#include "bnn-library.h"
// includes for network parameters
$GLOBALS$
// defines for network parameters
$DEFINES$
int main(){
$PRAGMAS$
$STREAMDECLARATIONS$
$READNPYDATA$
unsigned timeout = 0;
while(timeout < $TIMEOUT_VALUE$){
$DOCOMPUTE$
if($TIMEOUT_CONDITION$){
timeout++;
}
else{
$TIMEOUT_READ_STREAM$
timeout = 0;
}
}
$DATAOUTSTREAM$
$SAVEASCNPY$
}
"""

# templates for single node ip generation

# cpp file
Expand Down

0 comments on commit d1b286a

Please sign in to comment.