Skip to content

Commit

Permalink
Add initial and basic VPR support with the new Edalize API (#304)
Browse files Browse the repository at this point in the history
* Enable VPR flow

Signed-off-by: Alessandro Comodi <acomodi@antmicro.com>
Co-authored-by: Mateusz  Kosmala <mkosmala@internships.antmicro.com>
  • Loading branch information
acomodi and Mateusz Kosmala committed Feb 9, 2022
1 parent c805761 commit 6406063
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 0 deletions.
28 changes: 28 additions & 0 deletions edalize/flows/vpr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright edalize contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause

import os.path

from edalize.flows.edaflow import Edaflow


class Vpr(Edaflow):
"""VPR is an open source academic CAD tool designed for the exploration of new FPGA architectures and CAD algorithms, at the packing, placement and routing phases of the CAD flow"""

argtypes = ["vlogdefine", "vlogparam"]

FLOW = [
("yosys", ["vpr"], {"output_format": "blif"}),
("vpr", [], {}),
]

FLOW_OPTIONS = {}

def build_tool_graph(self):
return super().build_tool_graph()

def configure_tools(self, nodes):
super().configure_tools(nodes)
name = self.edam["name"]
self.commands.set_default_target(name + ".analysis")
128 changes: 128 additions & 0 deletions edalize/tools/vpr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Copyright edalize contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause

import shutil
from edalize.tools.edatool import Edatool
from edalize.utils import EdaCommands
import logging
import pathlib
import os.path
import platform
import re
import subprocess


logger = logging.getLogger(__name__)


class Vpr(Edatool):
"""
VPR tool Backend
The VPR backend performs Packing, Placement, Routing & Timing Analysis.
"""

TOOL_OPTIONS = {
"arch_xml": {
"type": "str",
"desc": "Path to target architecture in XML format",
},
"vpr_options": {
"type": "str",
"desc": "Additional options for VPR.",
"list": True,
},
}

def get_version(self):
"""
Get tool version.
This gets the VPR version by running "vpr --version" and
parsing the output.
If this command fails, "unknown" is returned.
"""
version = "unknown"
try:
vpr_output = subprocess.Popen(
["vpr", "--version"], stdout=subprocess.PIPE, env=os.environ
).communicate()[0]
version_exp = r"Version:(.*)"
match = re.search(version_exp, str(vpr_output.decode()))
if match:
version = match.groups()[0]
except Exception:
logger.warning("Unable to recognize VPR version")
return version

def configure(self, edam):
"""
Configuration is the first phase of the build.
This writes the project TCL files and Makefile. It first collects all
sources, IPs and constraints and then writes them to the TCL file along
with the build steps.
"""
super().configure(edam)

src_files = []
incdirs = set()

file_netlist = []
timing_constraints = []

for f in src_files:
if f.file_type in ["blif", "eblif"]:
file_netlist.append(f.name)
if f.file_type in ["SDC"]:
timing_constraints.append(f.name)

arch_xml = self.tool_options.get("arch_xml")
if not arch_xml:
logger.error('Missing required "arch" parameter')

_vo = self.tool_options.get("vpr_options")

vpr_options = _vo if _vo else []
sdc_opts = ["--sdc_file"] + timing_constraints if timing_constraints else []

commands = EdaCommands()

depends = self.name + ".blif"
targets = self.name + ".net"
command = ["vpr", arch_xml, self.name + ".blif", "--pack"]
command += sdc_opts + vpr_options
commands.add(command, [targets], [depends])

depends = self.name + ".net"
targets = self.name + ".place"
command = ["vpr", arch_xml, self.name + ".blif", "--place"]
command += sdc_opts + vpr_options
commands.add(command, [targets], [depends])

depends = self.name + ".place"
targets = self.name + ".route"
command = ["vpr", arch_xml, self.name + ".blif", "--route"]
command += sdc_opts + vpr_options
commands.add(command, [targets], [depends])

depends = self.name + ".route"
targets = self.name + ".analysis"
command = ["vpr", arch_xml, self.name + ".blif", "--analysis"]
command += sdc_opts + vpr_options
commands.add(command, [targets], [depends])

for ext in [".net", ".place", ".route", ".analysis"]:
self.edam["files"].append(
{"name": self.name + str(ext), "file_type": "vpr_" + str(ext[1:])}
)

self.commands = commands.commands
commands.set_default_target(targets)
commands.write(os.path.join(self.work_root, "Makefile"))

def build(self):
logger.info("Building")
return ("make", self.args, self.work_root)
57 changes: 57 additions & 0 deletions edalize/vpr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright edalize contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause

import logging
import os.path
import platform
import re
import subprocess

from edalize.edatool import Edatool
from edalize.flows.vpr import Vpr

logger = logging.getLogger(__name__)


class Vpr(Edatool):
"""
VPR tool Backend
The VPR backend performs Packing, Placement, Routing & Timing Analysis.
VPR is an open source academic CAD tool designed for the exploration of new FPGA architectures and CAD algorithms, at the packing, placement and routing phases of the CAD flow
"""

@classmethod
def get_doc(cls, api_ver):
if api_ver == 0:
return {
"description": "The VPR backend performs Packing, Placement, Routing & Timing Analysis.",
"members": [
{
"name": "arch_xml",
"type": "String",
"desc": "Path to target architecture.",
},
{
"name": "vpr_options",
"type": "String",
"desc": "Additional options for VPR.",
},
],
}

def __init__(self, edam=None, work_root=None, eda_api=None, verbose=True):
super().__init__(edam, work_root, eda_api, verbose)
edam["flow_options"] = edam["tool_options"]["vpr"]
self.vpr = VPR(edam, work_root, verbose)

def configure_main(self):
self.vpr.configure()

def build_pre(self):
pass

def build_post(self):
pass
36 changes: 36 additions & 0 deletions tests/test_vpr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os
import pytest
from edalize_common import make_edalize_test


@pytest.mark.parametrize("params", [("minimal", "vpr")])
def test_vpr(params, tmpdir):
import os
import edalize
from edalize_common import compare_files, tests_dir

test_name = "vpr"
ref_dir = os.path.join(tests_dir, __name__, test_name)
os.environ["PATH"] = (
os.path.join(tests_dir, "mock_commands") + ":" + os.environ["PATH"]
)
tool = "vpr"
name = "test_vpr_{}_0".format(test_name)
work_root = str(tmpdir)

edam = {
"name": name,
"flow_options": {
"arch": "xilinx",
"arch_xml": "/tmp/k6_N10_mem32K_40nm.xml",
"vpr_options": [],
},
}

vpr_flow = edalize.get_flow("vpr")
vpr_backend = vpr_flow(edam=edam, work_root=work_root)
vpr_backend.configure()
config_file_list = [
"Makefile",
]
compare_files(ref_dir, work_root, config_file_list)
28 changes: 28 additions & 0 deletions tests/test_vpr/vpr/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#Auto generated by Edalize

all: post_build

pre_build:

test_vpr_vpr_0.blif: edalize_yosys_template.tcl | pre_build
$(EDALIZE_LAUNCHER) yosys -l yosys.log -p 'tcl edalize_yosys_template.tcl'

test_vpr_vpr_0.net: test_vpr_vpr_0.blif
$(EDALIZE_LAUNCHER) vpr /tmp/k6_N10_mem32K_40nm.xml test_vpr_vpr_0.blif --pack

test_vpr_vpr_0.place: test_vpr_vpr_0.net
$(EDALIZE_LAUNCHER) vpr /tmp/k6_N10_mem32K_40nm.xml test_vpr_vpr_0.blif --place

test_vpr_vpr_0.route: test_vpr_vpr_0.place
$(EDALIZE_LAUNCHER) vpr /tmp/k6_N10_mem32K_40nm.xml test_vpr_vpr_0.blif --route

test_vpr_vpr_0.analysis: test_vpr_vpr_0.route
$(EDALIZE_LAUNCHER) vpr /tmp/k6_N10_mem32K_40nm.xml test_vpr_vpr_0.blif --analysis

post_build: test_vpr_vpr_0.analysis

pre_run:

run: pre_run

post_run: run

0 comments on commit 6406063

Please sign in to comment.