build/VHDLWrapper: adding a class to factorize VHDL handling

This commit is contained in:
Gwenhael Goavec-Merou 2022-09-23 19:34:25 +02:00
parent 759530f272
commit 8eef2cda0d
1 changed files with 144 additions and 0 deletions

144
litex/build/VHDLWrapper.py Normal file
View File

@ -0,0 +1,144 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2022 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
# Copyright (c) 2022 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import os
from migen import *
# VHDLWrapper --------------------------------------------------------------------------------------
class VHDLWrapper(Module):
"""
VHDLWrapper simplify use of VHDL code: used to convert with ghdl the code if
needed or simply pass list of files to platform. May also add an Instance.
Attributes
==========
_top_entity: str
name of the core highest level entity
_build_dir: str
directory where .ys and .v must be written and where to build
_work_package: str
when package is not default one, used to provides its name
_platform: subclass of GenericPlatform
current platform
_sources: list
list of files contained into the core (relative or absolute path)
_params: dict
Instance like params (p_ generics, o_ output, ...) when add_instance,
generics without prefix otherwise
_add_instance: bool
add if True an Instance()
_force_convert: bool
force use of GHDL even if the platform supports VHDL
_ghdl_opts: str
options to pass to ghdl
"""
def __init__(self, platform, top_entity, build_dir,
work_package = None,
force_convert = False,
add_instance = False,
params = dict(),
files = list()):
"""
constructor (see class attributes)
"""
self._top_entity = top_entity
self._build_dir = build_dir
self._work_package = work_package
self._platform = platform
self._sources = files
self._params = params
self._force_convert = force_convert
self._add_instance = add_instance
self._ghdl_opts = "--ieee=synopsys -fexplicit -frelaxed-rules --std=08 "
if work_package is not None:
self._ghdl_opts += f"--work={self._work_package} "
self._ghdl_opts += "\\"
def add_source(self, filename):
"""
append the source list with the path + name of a file
Parameters
==========
filename: str
file name + path
"""
self._sources.append(filename)
def add_sources(self, path, filenames):
"""
append the source list with a list of file after adding path
Parameters
==========
path: str
absolute or relative path for all files
filenames: list
list of file to add
"""
self._sources += [os.path.join(path, f) for f in filenames]
def do_finalize(self):
"""
- convert vhdl to verilog when toolchain can't deal with VHDL or
when force_convert is set to true
- appends platform file's list with the list of VHDL sources or
with resulting verilog
- add an Instance for this core
"""
inst_name = self._top_entity
# platform able to synthesis verilog and vhdl -> no conversion
if self._platform.support_mixed_language and not self._force_convert:
ip_params = self._params
for file in self._files:
platform.add_source(file)
else: # platform is only able to synthesis verilog -> convert vhdl to verilog
# check if more than one core is instanciated
# if so -> append with _X
# FIXME: better solution ?
v_list = []
for file, _, _ in self._platform.sources:
if self._top_entity in file:
v_list.append(file)
if len(v_list) != 0:
inst_name += f"_{len(v_list)}"
verilog_out = os.path.join(self._build_dir, f"{inst_name}.v")
script = os.path.join(self._build_dir, f"{inst_name}.ys")
ys = []
ys.append("ghdl " + self._ghdl_opts)
ip_params = dict()
generics = []
if self._add_instance:
for k, v in self._params.items():
if k.startswith("p_"):
ys.append("-g" + k[2:] + "=" + str(v) + " \\")
else:
ip_params[k] = v
else:
ip_params = self._params
from litex.build import tools
import subprocess
for source in self._sources:
ys.append(source + " \\")
ys.append(f"-e {self._top_entity}")
ys.append("chformal -assert -remove")
ys.append("write_verilog {}".format(verilog_out))
tools.write_to_file(script, "\n".join(ys))
if subprocess.call(["yosys", "-q", "-m", "ghdl", script]):
raise OSError(f"Unable to convert {inst_name} to verilog, please check your GHDL-Yosys-plugin install")
# more than one instance of this core? rename top entity to avoid conflict
if inst_name != self._top_entity:
tools.replace_in_file(verilog_out, f"module {self._top_entity}(", f"module {inst_name}(")
self._platform.add_source(verilog_out)
if self._add_instance:
self.specials += Instance(inst_name, **ip_params)