Skip to content

Commit ad3be5d

Browse files
stridge-cruxmlStephen Tridgellbopeng-cruxml
authored
Feature/hls dev (#12)
* Add vitis rules for HLS. * update the directories for vitis_hls headers (#8) * change c++ version to c++11 for vivado_hls (#10) * Change module names (#11) * change module name when generating rtl * clean up def.bzl * remove bug prints --------- Co-authored-by: Stephen Tridgell <stephen.tridgell@cruxml.com> Co-authored-by: bopeng-cruxml <88636268+bopeng-cruxml@users.noreply.github.com>
1 parent e6540a5 commit ad3be5d

File tree

299 files changed

+294808
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

299 files changed

+294808
-0
lines changed

vitis/BUILD

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
exports_files(["vitis_generate.tcl.template"])
2+
3+
# Define versions of vitis_hls/vivado_hls as needed.
4+
# These are copied from the installation.
5+
cc_library(
6+
name = "v2021_2_cc",
7+
srcs = glob([
8+
"v2021_2/**/*.h",
9+
]),
10+
strip_include_prefix = "external/rules_hdl/",
11+
visibility = ["//visibility:public"],
12+
)
13+
14+
cc_library(
15+
name = "v2020_1_cc",
16+
srcs = glob([
17+
"v2020_1/**/*.h",
18+
]),
19+
strip_include_prefix = "external/rules_hdl/",
20+
visibility = ["//visibility:public"],
21+
)
22+
23+
py_binary(
24+
name = "hls_generator",
25+
srcs = ["hls_generator.py"],
26+
visibility = ["//visibility:public"],
27+
)

vitis/defs.bzl

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
"""Defs for generating verilog using HLS"""
2+
3+
HlsFileInfo = provider(
4+
"HLS files required by vitis",
5+
fields = {
6+
"files": "a list of files",
7+
},
8+
)
9+
10+
def _vitis_hls_files_aspect_impl(target, ctx):
11+
"""Filter out the vitis header deps."""
12+
files = []
13+
14+
for f in target[CcInfo].compilation_context.headers.to_list():
15+
if "vitis/v" not in f.dirname:
16+
files.append(f)
17+
18+
if hasattr(ctx.rule.attr, "srcs"):
19+
for src in ctx.rule.attr.srcs:
20+
for f in src.files.to_list():
21+
if f not in files and "vitis/v" not in f.dirname:
22+
files.append(f)
23+
24+
if hasattr(ctx.rule.attr, "deps"):
25+
for dep in ctx.rule.attr.deps:
26+
files = files + dep[HlsFileInfo].files
27+
28+
return [HlsFileInfo(files = files)]
29+
30+
vitis_hls_files_aspect = aspect(
31+
implementation = _vitis_hls_files_aspect_impl,
32+
attr_aspects = ["deps"],
33+
)
34+
35+
def _vitis_generate_impl(ctx):
36+
all_files = []
37+
if ctx.attr.use_vivado_hls:
38+
cflags = "-D__SYNTHESIS__=1 --std=c++11"
39+
else:
40+
cflags = "-D__SYNTHESIS__=1 --std=c++17"
41+
for dep in ctx.attr.deps:
42+
for file in dep[HlsFileInfo].files:
43+
external_path = "/".join([file.root.path, file.owner.workspace_root]) if file.root.path else file.owner.workspace_root
44+
cflags += " -I" + external_path
45+
46+
source_file_str = ""
47+
for dep in ctx.attr.deps:
48+
for file in dep[HlsFileInfo].files:
49+
all_files.append(file)
50+
source_file_str += "add_file " + file.path + " -cflags \"" + cflags + "\"\n"
51+
52+
vitis_tcl = ctx.actions.declare_file("{}_run_hls.tcl".format(ctx.label.name))
53+
vitis_log = ctx.actions.declare_file("{}_hls.log".format(ctx.label.name))
54+
55+
substitutions = {
56+
"{{PROJECT_NAME}}": ctx.label.name,
57+
"{{SOURCE_FILES}}": source_file_str,
58+
"{{TOP_LEVEL_FUNCTION}}": ctx.attr.top_func,
59+
"{{PART_NUMBER}}": ctx.attr.part_number,
60+
"{{CLOCK_PERIOD}}": ctx.attr.clock_period,
61+
}
62+
63+
ctx.actions.expand_template(
64+
template = ctx.file._vitis_generate_template,
65+
output = vitis_tcl,
66+
substitutions = substitutions,
67+
)
68+
args = []
69+
args.append("--vitis_tcl")
70+
args.append(vitis_tcl.path)
71+
args.append("--vitis_log")
72+
args.append(vitis_log.path)
73+
args.append("--outputs")
74+
args.append(ctx.outputs.out.path)
75+
args.append("--label")
76+
args.append(ctx.label.name)
77+
args.append("--xilinx_env")
78+
args.append(ctx.file.xilinx_env.path)
79+
if ctx.attr.use_vivado_hls:
80+
args.append("--use_vivado_hls")
81+
82+
outputs = [vitis_log, ctx.outputs.out]
83+
84+
if ctx.attr.use_vivado_hls:
85+
progress_message = "Running with vivado_hls: {}".format(ctx.label.name)
86+
else:
87+
progress_message = "Running with vitis_hls: {}".format(ctx.label.name)
88+
89+
ctx.actions.run(
90+
outputs = outputs,
91+
inputs = all_files + [vitis_tcl, ctx.file.xilinx_env],
92+
arguments = args,
93+
progress_message = progress_message,
94+
executable = ctx.executable._run_hls_gen,
95+
)
96+
97+
return [
98+
DefaultInfo(files = depset(outputs)),
99+
]
100+
101+
vitis_generate = rule(
102+
implementation = _vitis_generate_impl,
103+
attrs = {
104+
"top_func": attr.string(doc = "The name of the top level function.", mandatory = True),
105+
"clock_period": attr.string(doc = "The clock period for the module.", mandatory = True),
106+
"part_number": attr.string(doc = "The part number to use. Default is ZCU111", default = "xczu28dr-ffvg1517-2-e"),
107+
"deps": attr.label_list(doc = "The file to generate from", aspects = [vitis_hls_files_aspect], mandatory = True),
108+
"out": attr.output(doc = "The generated verilog files", mandatory = True),
109+
"use_vivado_hls": attr.bool(doc = "Use vivado HLS instead of vitis hls.", default = False),
110+
"_vitis_generate_template": attr.label(
111+
doc = "The tcl template to run with vitis.",
112+
default = "@rules_hdl//vitis:vitis_generate.tcl.template",
113+
allow_single_file = [".template"],
114+
),
115+
"_run_hls_gen": attr.label(
116+
doc = "Tool used to run hls generator.",
117+
executable = True,
118+
cfg = "exec",
119+
default = ":hls_generator",
120+
),
121+
"xilinx_env": attr.label(
122+
doc = "Environment variables for xilinx tools.",
123+
allow_single_file = [".sh"],
124+
mandatory = True,
125+
),
126+
},
127+
)

vitis/hls_generator.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import argparse
2+
import os
3+
import logging
4+
import subprocess
5+
import re
6+
import tarfile
7+
8+
9+
logger = logging.getLogger(__name__)
10+
11+
logging_levels = {
12+
"critical": logging.CRITICAL,
13+
"error": logging.ERROR,
14+
"warn": logging.WARNING,
15+
"warning": logging.WARNING,
16+
"info": logging.INFO,
17+
"debug": logging.DEBUG,
18+
}
19+
20+
21+
def parse_args():
22+
parser = argparse.ArgumentParser()
23+
parser.add_argument(
24+
"--vitis_tcl",
25+
type=str,
26+
required=True,
27+
help="The path to the vitis tcl",
28+
)
29+
parser.add_argument(
30+
"--vitis_log",
31+
type=str,
32+
required=True,
33+
help="The path to the vitis log",
34+
)
35+
parser.add_argument(
36+
"--outputs",
37+
type=str,
38+
required=True,
39+
help="The path to the outputs",
40+
)
41+
parser.add_argument(
42+
"--label",
43+
type=str,
44+
required=True,
45+
help="The label name",
46+
)
47+
parser.add_argument(
48+
"--xilinx_env",
49+
type=str,
50+
required=True,
51+
help="The path to the xilinx_env.",
52+
)
53+
parser.add_argument(
54+
"--use_vivado_hls",
55+
type=bool,
56+
default=False,
57+
help="If use vivado hls",
58+
)
59+
parser.add_argument(
60+
"-l",
61+
"--log_level",
62+
default="debug",
63+
choices=logging_levels.keys(),
64+
help="The logging level to use.",
65+
)
66+
return parser.parse_args()
67+
68+
69+
def send_command(command):
70+
build_process = subprocess.Popen(
71+
"/bin/bash", stdin=subprocess.PIPE, stdout=subprocess.PIPE
72+
)
73+
out, err = build_process.communicate(command.encode("utf-8"))
74+
logger.info(out.decode("utf-8"))
75+
if err:
76+
logger.error(err.decode("utf-8"))
77+
78+
79+
def replace_module_names(verilog_files, verilog_files_dir, top_name):
80+
module_pattern = re.compile(r"(?<=^module\s).\S+", re.IGNORECASE)
81+
module_names_to_change = []
82+
data_to_write = {}
83+
# Read files and find module names.
84+
for verilog_file in verilog_files:
85+
full_path = os.path.join(verilog_files_dir, verilog_file)
86+
with open(full_path, "r") as f:
87+
data_to_write[full_path] = f.readlines()
88+
for line in data_to_write[full_path]:
89+
module_name = module_pattern.findall(line)
90+
# Keep the top level name.
91+
if module_name and module_name[0] != top_name:
92+
module_names_to_change += module_name
93+
# replace file contents
94+
for verilog_file in verilog_files:
95+
full_path = os.path.join(verilog_files_dir, verilog_file)
96+
this_data = data_to_write[full_path]
97+
for i in range(len(this_data)):
98+
for module_name_to_change in module_names_to_change:
99+
this_data[i] = re.sub(
100+
r"\b{module_name}\b".format(module_name=module_name_to_change),
101+
f"{module_name_to_change}_{top_name}",
102+
this_data[i],
103+
)
104+
105+
for verilog_file in verilog_files:
106+
full_path = os.path.join(verilog_files_dir, verilog_file)
107+
this_data = data_to_write[full_path]
108+
with open(full_path, "w") as f:
109+
for line in this_data:
110+
f.write(line)
111+
112+
113+
def main():
114+
args = parse_args()
115+
# TODO(cruxml-bopeng): Move this to utils_logging.
116+
logging.basicConfig(
117+
format="%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s",
118+
datefmt="%Y-%m-%d:%H:%M:%S",
119+
level=logging_levels[args.log_level],
120+
)
121+
122+
if args.use_vivado_hls:
123+
builder_name = "vivado_hls"
124+
else:
125+
builder_name = "vitis_hls"
126+
build_commands = (
127+
f"source {args.xilinx_env}; {builder_name} {args.vitis_tcl} -l {args.vitis_log}"
128+
)
129+
send_command(build_commands)
130+
verilog_files_dir = os.path.join(args.label, "sol1/impl/verilog")
131+
verilog_files = os.listdir(verilog_files_dir)
132+
replace_module_names(verilog_files, verilog_files_dir, args.label)
133+
# Writ files
134+
with tarfile.open(args.outputs, "w:gz") as tar:
135+
for verilog_file in verilog_files:
136+
full_path = os.path.join(verilog_files_dir, verilog_file)
137+
tar.add(full_path, arcname=os.path.basename(full_path))
138+
139+
140+
if __name__ == "__main__":
141+
main()

0 commit comments

Comments
 (0)