Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions fud2/rsrc/hls.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Usage: vivado_hls -f hls.tcl -tclargs [impl] [top <name>]

proc lshift listVar {
upvar 1 $listVar l
set r [lindex $l 0]
set l [lreplace $l [set l 0] 0]
return $r
}

set impl 0
set top kernel
set hls_prj benchmark.prj

while {[llength $argv]} {
set flag [lshift argv]
switch -exact -- $flag {
impl {
set impl 1
}
top {
set top [lshift argv]
}
}
}

open_project ${hls_prj} -reset
set_top $top; # The name of the hardware function.
add_files [glob ./*.cpp] -cflags "-std=c++11 -DVHLS"; # HLS source files.

open_solution "solution1"
set_part xczu3eg-sbva484-1-e
create_clock -period 7

# Actions we can take include csim_design, csynth_design, or cosim_design.
csynth_design

if {$impl} {
# Exporting the design gives us a convenient way to run place and route via
# the `-flow impl` option. Another option is `-flow syn` if you only need to
# run RTL synthesis.
# The packaging options don't matter for our purposes, but we set the version
# to 1.1.0 to circumvent this bug: https://support.xilinx.com/s/article/76960
export_design -format ip_catalog -version 1.1.0 -flow impl
}

exit
46 changes: 46 additions & 0 deletions fud2/scripts/hls.rhai
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export const cpp_state = state("cpp", ["cpp"]);

export const hls_report_state = state("hls-report", ["json"]);
export const hls_impl_report_state = state("hls-impl-report", ["json"]);

fn hls_setup(e) {
e.config_var_or("vitis-hls-top", "vitis-hls.top", "kernel");

e.rsrc("hls.tcl");

e.rule("vitis-hls", "vitis_hls -f hls.tcl -tclargs top $vitis-hls-top > /dev/null");
e.rule("vitis-hls-impl", "vitis_hls -f hls.tcl -tclargs top $vitis-hls-top impl > /dev/null");

e.rule("extract-hls-json", "synthrep summary -m hls --top $vitis-hls-top > $out");
e.rule("extract-hls-impl-json", "synthrep summary -m hls-impl --top $vitis-hls-top > $out");

e.rule("copy", "cp $in $out");
}

fn hls_build(e, input, output, impl) {
let suffix = if impl { "-impl" } else { "" };

e.build_cmd(["kernel.cpp"], "copy", [input], []);
e.build_cmd(["IGNOREME"], `vitis-hls${suffix}`, [], ["kernel.cpp", "hls.tcl"]);
e.build_cmd([output], `extract-hls${suffix}-json`, ["IGNOREME"], []);
}

op(
"vitis-hls",
[hls_setup],
cpp_state,
hls_report_state,
|e, input, output| {
hls_build(e, input, output, false);
},
);

op(
"vitis-hls-impl",
[hls_setup],
cpp_state,
hls_impl_report_state,
|e, input, output| {
hls_build(e, input, output, true);
},
);
10 changes: 10 additions & 0 deletions fud2/tests/snapshots/tests__list_ops.snap
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ source: fud2/tests/tests.rs
"verilog",
"xo",
),
(
"vitis-hls",
"cpp",
"hls-report",
),
(
"vitis-hls-impl",
"cpp",
"hls-impl-report",
),
(
"xclbin",
"xo",
Expand Down
3 changes: 3 additions & 0 deletions fud2/tests/snapshots/tests__list_states.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ source: fud2/tests/tests.rs
"cider",
"cider-debug",
"cocotb-axi",
"cpp",
"dahlia",
"dat",
"firrtl",
"firrtl-with-primitives",
"flamegraph",
"hls-impl-report",
"hls-report",
"jq",
"json-report",
"mrxl",
Expand Down
26 changes: 26 additions & 0 deletions fud2/tests/snapshots/tests__test@plan_vitis-hls-impl.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: fud2/tests/tests.rs
description: "emit plan: vitis-hls-impl"
---
build-tool = fud2
rule get-rsrc
command = $build-tool get-rsrc $out

vitis-hls-top = kernel
build hls.tcl: get-rsrc
rule vitis-hls
command = vitis_hls -f hls.tcl -tclargs top $vitis-hls-top > /dev/null
rule vitis-hls-impl
command = vitis_hls -f hls.tcl -tclargs top $vitis-hls-top impl > /dev/null
rule extract-hls-json
command = synthrep summary -m hls --top $vitis-hls-top > $out
rule extract-hls-impl-json
command = synthrep summary -m hls-impl --top $vitis-hls-top > $out
rule copy
command = cp $in $out

build kernel.cpp: copy /input.ext
build IGNOREME: vitis-hls-impl | kernel.cpp hls.tcl
build /output.ext: extract-hls-impl-json IGNOREME

default /output.ext
26 changes: 26 additions & 0 deletions fud2/tests/snapshots/tests__test@plan_vitis-hls.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: fud2/tests/tests.rs
description: "emit plan: vitis-hls"
---
build-tool = fud2
rule get-rsrc
command = $build-tool get-rsrc $out

vitis-hls-top = kernel
build hls.tcl: get-rsrc
rule vitis-hls
command = vitis_hls -f hls.tcl -tclargs top $vitis-hls-top > /dev/null
rule vitis-hls-impl
command = vitis_hls -f hls.tcl -tclargs top $vitis-hls-top impl > /dev/null
rule extract-hls-json
command = synthrep summary -m hls --top $vitis-hls-top > $out
rule extract-hls-impl-json
command = synthrep summary -m hls-impl --top $vitis-hls-top > $out
rule copy
command = cp $in $out

build kernel.cpp: copy /input.ext
build IGNOREME: vitis-hls | kernel.cpp hls.tcl
build /output.ext: extract-hls-json IGNOREME

default /output.ext
13 changes: 9 additions & 4 deletions tools/report-parsing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ synthrep viz -h
Extracts a JSON summary of synthesis and implementation resource usage, as well as timing info.

```bash
synthrep summary [-d DIRECTORY] [-m {utilization,hierarchy}]
synthrep summary [-d DIRECTORY] [-m {utilization,hierarchy,hls,hls-impl}] [--top TOP]
```

**Options:**

- `-d`, `--directory` – specify Vivado output directory (default: `out`)
- `-d`, `--directory` – specify Vivado output directory (default: \<mode dependent\>)
- `-m`, `--mode` – set summary mode (default: `utilization`)
- `--top` – specify top-level module/function (default: `main`)

There are two modes:
There are four modes:

- `utilization`: prints a flat summary of total resource usage and timing results:
- LUTs, FFs, DSPs, BRAMs
Expand All @@ -48,6 +49,10 @@ There are two modes:

- `hierarchy`: prints the full utilization hierarchy, which can be passed to the profiler to obtain cycle-resource-utilization data and visualizations

- `hls`: prints a summary of estimated resource usage and latency after high-level synthesis

- `hls-impl`: prints a summary of resource usage and timing after high-level synthesis followed by RTL synthesis and implementation

**Example:**

```bash
Expand Down Expand Up @@ -83,7 +88,7 @@ synthrep viz -t sunburst -c lut
A clone of [Brendan Gregg's FlameGraph repository](https://github.com/brendangregg/FlameGraph) is needed to generate FlameGraph SVGs. The folded stack output can be directly chained into the FlameGraph Perl script:

```bash
synthrep viz -c lut -t flamegraph | path/to/FlameGraph/flamegraph.pl > flamegraph.svg
synthrep viz -c lut -t flamegraph | path/to/FlameGraph/flamegraph.pl > flamegraph.svg
```

## Supported formats
Expand Down
46 changes: 34 additions & 12 deletions tools/report-parsing/synthrep/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from synthrep.extract import place_and_route_extract
from synthrep.extract import place_and_route_extract, hls_extract
from synthrep.rpt import RPTParser
from pathlib import Path, PurePath
import pandas as pd
Expand All @@ -8,14 +8,13 @@
import json


def summary(dir):
def summary(dir, top):
print(
place_and_route_extract(
Path(dir),
"FutilBuild.runs",
PurePath("impl_1", "main_utilization_placed.rpt"),
PurePath("impl_1", "main_timing_summary_routed.rpt"),
PurePath("synth_1", "main_utilization_synth.rpt"),
Path(dir, "FutilBuild.runs"),
PurePath("impl_1", f"{top}_utilization_placed.rpt"),
PurePath("impl_1", f"{top}_timing_summary_routed.rpt"),
PurePath("synth_1", f"{top}_utilization_synth.rpt"),
)
)

Expand All @@ -39,6 +38,21 @@ def hierarchy_summary(dir):
)


def hls_summary(dir, top):
print(hls_extract(Path(dir), top))


def hls_impl_summary(dir, top):
print(
place_and_route_extract(
Path(dir, "solution1", "impl", "verilog", "report"),
PurePath(f"{top}_utilization_routed.rpt"),
PurePath(f"{top}_timing_routed.rpt"),
PurePath(f"{top}_utilization_synth.rpt"),
)
)


def create_tree(filename):
rpt_file = Path(filename)
parser = RPTParser(rpt_file)
Expand Down Expand Up @@ -111,24 +125,32 @@ def main():
summary_parser.add_argument(
"-d",
"--directory",
help="specify Vivado output directory (default: %(default)s)",
default="out",
help="specify Vivado output directory (default: <mode dependent>)",
)
summary_parser.add_argument(
"-m",
"--mode",
help="set summary mode (default: %(default)s)",
choices=["utilization", "hierarchy"],
choices=["utilization", "hierarchy", "hls", "hls-impl"],
default="utilization",
)
summary_parser.add_argument(
"--top",
help="specify top-level module/function (default: %(default)s)",
default="main",
)
args = parser.parse_args()
match args.command:
case "summary":
match args.mode:
case "utilization":
summary(args.directory)
summary(args.directory or "out", args.top)
case "hierarchy":
hierarchy_summary(args.directory)
hierarchy_summary(args.directory or "out")
case "hls":
hls_summary(args.directory or "benchmark.prj", args.top)
case "hls-impl":
hls_impl_summary(args.directory or "benchmark.prj", args.top)
case "viz":
match args.type:
case "flamegraph":
Expand Down
25 changes: 3 additions & 22 deletions tools/report-parsing/synthrep/extract.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import os
from pathlib import Path, PurePath
import re
import traceback
Expand Down Expand Up @@ -97,18 +96,10 @@ def rpt_extract(file: PurePath):

def place_and_route_extract(
directory: Path,
files_root: str,
utilization_file: PurePath,
timing_file: PurePath,
synthesis_file: PurePath,
):
# Search for the given root directory
for root, dirs, _ in os.walk(directory):
for d in dirs:
if d == files_root:
directory = Path(os.path.join(root, d))
break

util_file = directory / utilization_file
synth_file = directory / synthesis_file
timing_file = directory / timing_file
Expand Down Expand Up @@ -161,32 +152,22 @@ def place_and_route_extract(


def hls_extract(directory: Path, top: str):
# Search for directory named benchmark.prj
for root, dirs, _ in os.walk(directory):
for d in dirs:
if d == "benchmark.prj":
directory = Path(os.path.join(root, d))
break

directory = directory / "solution1"

try:
parser = rpt.RPTParser(directory / "syn" / "report" / f"{top}_csynth.rpt")
summary_table = parser.get_table(re.compile(r"== Utilization Estimates"), 2)
instance_table = parser.get_table(re.compile(r"\* Instance:"), 0)

solution_data = json.load((directory / "solution1_data.json").open())
latency = solution_data["ModuleInfo"]["Metrics"][top]["Latency"]

total_row = find_row(summary_table, "Name", "Total")
s_axi_row = find_row(instance_table, "Instance", f"{top}_control_s_axi_U")

return json.dumps(
{
"total_lut": to_int(total_row["LUT"]),
"instance_lut": to_int(s_axi_row["LUT"]),
"lut": to_int(total_row["LUT"]) - to_int(s_axi_row["LUT"]),
"dsp": to_int(total_row["DSP48E"]) - to_int(s_axi_row["DSP48E"]),
"lut": to_int(total_row["LUT"]),
"ff": to_int(total_row["FF"]),
"dsp": to_int(total_row["DSP"]),
"avg_latency": to_int(latency["LatencyAvg"]),
"best_latency": to_int(latency["LatencyBest"]),
"worst_latency": to_int(latency["LatencyWorst"]),
Expand Down
Loading