Merge pull request #1453 from trabucayre/vhdl_wrapper

Vhdl wrapper
This commit is contained in:
enjoy-digital 2022-10-12 11:42:51 +02:00 committed by GitHub
commit c700f9d0ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 169 additions and 19 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)

View File

@ -323,6 +323,7 @@ class ConstraintManager:
class GenericPlatform:
def __init__(self, device, io, connectors=[], name=None):
self.toolchain = None
self.device = device
self.constraint_manager = ConstraintManager(io, connectors)
if name is None:
@ -462,3 +463,7 @@ class GenericPlatform:
def create_programmer(self):
raise NotImplementedError
@property
def support_mixed_language(self):
return self.toolchain.support_mixed_language

View File

@ -18,6 +18,7 @@ class GenericToolchain:
}
supported_build_backend = ["litex"]
_support_mixed_language = True
def __init__(self):
self.clocks = dict()
@ -27,6 +28,10 @@ class GenericToolchain:
self._vns = None
self._synth_opts = ""
@property
def support_mixed_language(self):
return self._support_mixed_language
def finalize(self):
pass # Pass since optional.

View File

@ -57,10 +57,13 @@ class YosysNextPNRToolchain(GenericToolchain):
target package (optional/target dependant)
_speed_grade: str
target speed grade (optional/target dependant)
_support_mixed_language: bool
informs if toolchain is able to use only verilog or verilog + vhdl
"""
attr_translate = {
"keep": ("keep", "true"),
}
_support_mixed_language = False
family = ""
synth_fmt = ""

View File

@ -8,6 +8,8 @@ import os
from migen import *
from litex.build.VHDLWrapper import *
from litex.soc.interconnect import wishbone
from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32
@ -97,15 +99,21 @@ class NEORV32(CPU):
i_wb_err_i = idbus.err,
)
self.submodules.vhdlwrapper = VHDLWrapper(platform,
top_entity = "neorv32_litex_core_complex",
build_dir = os.path.abspath(os.path.dirname(__file__)),
work_package = "neorv32",
force_convert = True,
)
# Add Verilog sources
self.add_sources(platform, variant)
self.add_sources(variant)
def set_reset_address(self, reset_address):
self.reset_address = reset_address
assert reset_address == 0x0000_0000
@staticmethod
def add_sources(platform, variant):
def add_sources(self, variant):
cdir = os.path.abspath(os.path.dirname(__file__))
# List VHDL sources.
sources = {
@ -152,6 +160,7 @@ class NEORV32(CPU):
# Download VHDL sources (if not already present).
for directory, vhds in sources.items():
for vhd in vhds:
self.vhdlwrapper.add_source(os.path.join(cdir, vhd))
if not os.path.exists(os.path.join(cdir, vhd)):
os.system(f"wget https://raw.githubusercontent.com/stnolting/neorv32/main/rtl/{directory}/{vhd} -P {cdir}")
@ -188,22 +197,6 @@ class NEORV32(CPU):
variant = variant,
)
# Convert VHDL to Verilog through GHDL/Yosys.
from litex.build import tools
import subprocess
cdir = os.path.dirname(__file__)
ys = []
ys.append("ghdl --ieee=synopsys -fexplicit -frelaxed-rules --std=08 --work=neorv32 \\")
for directory, vhds in sources.items():
for vhd in vhds:
ys.append(os.path.join(cdir, vhd) + " \\")
ys.append("-e neorv32_litex_core_complex")
ys.append("chformal -assert -remove")
ys.append("write_verilog {}".format(os.path.join(cdir, "neorv32_litex_core_complex.v")))
tools.write_to_file(os.path.join(cdir, "neorv32_litex_core_complex.ys"), "\n".join(ys))
if subprocess.call(["yosys", "-q", "-m", "ghdl", os.path.join(cdir, "neorv32_litex_core_complex.ys")]):
raise OSError("Unable to convert NEORV32 CPU to verilog, please check your GHDL-Yosys-plugin install.")
platform.add_source(os.path.join(cdir, "neorv32_litex_core_complex.v"))
def do_finalize(self):
assert hasattr(self, "reset_address")