Skip to content

Commit

Permalink
Feature/hls dev (#12)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Jun 21, 2023
1 parent e6540a5 commit ad3be5d
Show file tree
Hide file tree
Showing 299 changed files with 294,808 additions and 0 deletions.
27 changes: 27 additions & 0 deletions vitis/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
exports_files(["vitis_generate.tcl.template"])

# Define versions of vitis_hls/vivado_hls as needed.
# These are copied from the installation.
cc_library(
name = "v2021_2_cc",
srcs = glob([
"v2021_2/**/*.h",
]),
strip_include_prefix = "external/rules_hdl/",
visibility = ["//visibility:public"],
)

cc_library(
name = "v2020_1_cc",
srcs = glob([
"v2020_1/**/*.h",
]),
strip_include_prefix = "external/rules_hdl/",
visibility = ["//visibility:public"],
)

py_binary(
name = "hls_generator",
srcs = ["hls_generator.py"],
visibility = ["//visibility:public"],
)
127 changes: 127 additions & 0 deletions vitis/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"""Defs for generating verilog using HLS"""

HlsFileInfo = provider(
"HLS files required by vitis",
fields = {
"files": "a list of files",
},
)

def _vitis_hls_files_aspect_impl(target, ctx):
"""Filter out the vitis header deps."""
files = []

for f in target[CcInfo].compilation_context.headers.to_list():
if "vitis/v" not in f.dirname:
files.append(f)

if hasattr(ctx.rule.attr, "srcs"):
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if f not in files and "vitis/v" not in f.dirname:
files.append(f)

if hasattr(ctx.rule.attr, "deps"):
for dep in ctx.rule.attr.deps:
files = files + dep[HlsFileInfo].files

return [HlsFileInfo(files = files)]

vitis_hls_files_aspect = aspect(
implementation = _vitis_hls_files_aspect_impl,
attr_aspects = ["deps"],
)

def _vitis_generate_impl(ctx):
all_files = []
if ctx.attr.use_vivado_hls:
cflags = "-D__SYNTHESIS__=1 --std=c++11"
else:
cflags = "-D__SYNTHESIS__=1 --std=c++17"
for dep in ctx.attr.deps:
for file in dep[HlsFileInfo].files:
external_path = "/".join([file.root.path, file.owner.workspace_root]) if file.root.path else file.owner.workspace_root
cflags += " -I" + external_path

source_file_str = ""
for dep in ctx.attr.deps:
for file in dep[HlsFileInfo].files:
all_files.append(file)
source_file_str += "add_file " + file.path + " -cflags \"" + cflags + "\"\n"

vitis_tcl = ctx.actions.declare_file("{}_run_hls.tcl".format(ctx.label.name))
vitis_log = ctx.actions.declare_file("{}_hls.log".format(ctx.label.name))

substitutions = {
"{{PROJECT_NAME}}": ctx.label.name,
"{{SOURCE_FILES}}": source_file_str,
"{{TOP_LEVEL_FUNCTION}}": ctx.attr.top_func,
"{{PART_NUMBER}}": ctx.attr.part_number,
"{{CLOCK_PERIOD}}": ctx.attr.clock_period,
}

ctx.actions.expand_template(
template = ctx.file._vitis_generate_template,
output = vitis_tcl,
substitutions = substitutions,
)
args = []
args.append("--vitis_tcl")
args.append(vitis_tcl.path)
args.append("--vitis_log")
args.append(vitis_log.path)
args.append("--outputs")
args.append(ctx.outputs.out.path)
args.append("--label")
args.append(ctx.label.name)
args.append("--xilinx_env")
args.append(ctx.file.xilinx_env.path)
if ctx.attr.use_vivado_hls:
args.append("--use_vivado_hls")

outputs = [vitis_log, ctx.outputs.out]

if ctx.attr.use_vivado_hls:
progress_message = "Running with vivado_hls: {}".format(ctx.label.name)
else:
progress_message = "Running with vitis_hls: {}".format(ctx.label.name)

ctx.actions.run(
outputs = outputs,
inputs = all_files + [vitis_tcl, ctx.file.xilinx_env],
arguments = args,
progress_message = progress_message,
executable = ctx.executable._run_hls_gen,
)

return [
DefaultInfo(files = depset(outputs)),
]

vitis_generate = rule(
implementation = _vitis_generate_impl,
attrs = {
"top_func": attr.string(doc = "The name of the top level function.", mandatory = True),
"clock_period": attr.string(doc = "The clock period for the module.", mandatory = True),
"part_number": attr.string(doc = "The part number to use. Default is ZCU111", default = "xczu28dr-ffvg1517-2-e"),
"deps": attr.label_list(doc = "The file to generate from", aspects = [vitis_hls_files_aspect], mandatory = True),
"out": attr.output(doc = "The generated verilog files", mandatory = True),
"use_vivado_hls": attr.bool(doc = "Use vivado HLS instead of vitis hls.", default = False),
"_vitis_generate_template": attr.label(
doc = "The tcl template to run with vitis.",
default = "@rules_hdl//vitis:vitis_generate.tcl.template",
allow_single_file = [".template"],
),
"_run_hls_gen": attr.label(
doc = "Tool used to run hls generator.",
executable = True,
cfg = "exec",
default = ":hls_generator",
),
"xilinx_env": attr.label(
doc = "Environment variables for xilinx tools.",
allow_single_file = [".sh"],
mandatory = True,
),
},
)
141 changes: 141 additions & 0 deletions vitis/hls_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import argparse
import os
import logging
import subprocess
import re
import tarfile


logger = logging.getLogger(__name__)

logging_levels = {
"critical": logging.CRITICAL,
"error": logging.ERROR,
"warn": logging.WARNING,
"warning": logging.WARNING,
"info": logging.INFO,
"debug": logging.DEBUG,
}


def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"--vitis_tcl",
type=str,
required=True,
help="The path to the vitis tcl",
)
parser.add_argument(
"--vitis_log",
type=str,
required=True,
help="The path to the vitis log",
)
parser.add_argument(
"--outputs",
type=str,
required=True,
help="The path to the outputs",
)
parser.add_argument(
"--label",
type=str,
required=True,
help="The label name",
)
parser.add_argument(
"--xilinx_env",
type=str,
required=True,
help="The path to the xilinx_env.",
)
parser.add_argument(
"--use_vivado_hls",
type=bool,
default=False,
help="If use vivado hls",
)
parser.add_argument(
"-l",
"--log_level",
default="debug",
choices=logging_levels.keys(),
help="The logging level to use.",
)
return parser.parse_args()


def send_command(command):
build_process = subprocess.Popen(
"/bin/bash", stdin=subprocess.PIPE, stdout=subprocess.PIPE
)
out, err = build_process.communicate(command.encode("utf-8"))
logger.info(out.decode("utf-8"))
if err:
logger.error(err.decode("utf-8"))


def replace_module_names(verilog_files, verilog_files_dir, top_name):
module_pattern = re.compile(r"(?<=^module\s).\S+", re.IGNORECASE)
module_names_to_change = []
data_to_write = {}
# Read files and find module names.
for verilog_file in verilog_files:
full_path = os.path.join(verilog_files_dir, verilog_file)
with open(full_path, "r") as f:
data_to_write[full_path] = f.readlines()
for line in data_to_write[full_path]:
module_name = module_pattern.findall(line)
# Keep the top level name.
if module_name and module_name[0] != top_name:
module_names_to_change += module_name
# replace file contents
for verilog_file in verilog_files:
full_path = os.path.join(verilog_files_dir, verilog_file)
this_data = data_to_write[full_path]
for i in range(len(this_data)):
for module_name_to_change in module_names_to_change:
this_data[i] = re.sub(
r"\b{module_name}\b".format(module_name=module_name_to_change),
f"{module_name_to_change}_{top_name}",
this_data[i],
)

for verilog_file in verilog_files:
full_path = os.path.join(verilog_files_dir, verilog_file)
this_data = data_to_write[full_path]
with open(full_path, "w") as f:
for line in this_data:
f.write(line)


def main():
args = parse_args()
# TODO(cruxml-bopeng): Move this to utils_logging.
logging.basicConfig(
format="%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s",
datefmt="%Y-%m-%d:%H:%M:%S",
level=logging_levels[args.log_level],
)

if args.use_vivado_hls:
builder_name = "vivado_hls"
else:
builder_name = "vitis_hls"
build_commands = (
f"source {args.xilinx_env}; {builder_name} {args.vitis_tcl} -l {args.vitis_log}"
)
send_command(build_commands)
verilog_files_dir = os.path.join(args.label, "sol1/impl/verilog")
verilog_files = os.listdir(verilog_files_dir)
replace_module_names(verilog_files, verilog_files_dir, args.label)
# Writ files
with tarfile.open(args.outputs, "w:gz") as tar:
for verilog_file in verilog_files:
full_path = os.path.join(verilog_files_dir, verilog_file)
tar.add(full_path, arcname=os.path.basename(full_path))


if __name__ == "__main__":
main()
Loading

0 comments on commit ad3be5d

Please sign in to comment.