Skip to content

Commit

Permalink
Adds GHDL support.
Browse files Browse the repository at this point in the history
  • Loading branch information
kraigher committed Jun 1, 2015
1 parent d422d29 commit 9849f08
Show file tree
Hide file tree
Showing 7 changed files with 332 additions and 3 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ VUnit depends on a number of components as listed below. Full VUnit functionalit
## Operating systems
* Windows
* Linux
* Mac OS X

## Python
* Python 2.7
* Python 3.3 or higher

## Simulators
* Mentor Graphics ModelSim (tested with 10.1 - 10.3)
* [Mentor Graphics ModelSim](http://www.mentor.com/products/fv/modelsim/)
* Tested with 10.1 - 10.3
* [GHDL](https://sourceforge.net/projects/ghdl-updates/)
* Only VHDL 2008
* Only versions >= 0.33 dev (There is no official release yet, must be built from source)
* Tested with LLVM and mcode backends, gcc backend might work aswell.
* Integrated support for using [GTKWave](http://gtkwave.sourceforge.net/) to view waveforms.

# Getting Started
There are a number of ways to get started.
Expand Down
12 changes: 12 additions & 0 deletions user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ modelsim:
variables and signals
--new-vsim Do not re-use the same vsim process for running
different test cases (slower)
ghdl:
GHDL specific flags
--gtkwave {vcd,ghw} Save .vcd or .ghw and open in gtkwave
--gtkwave-args GTKWAVE_ARGS
Arguments to pass to gtkwave
```

## VHDL Test Benches
Expand Down Expand Up @@ -222,6 +229,11 @@ It is also possible to automatically run the test case in the gui while logging
After simulation the user can manually add objects of interest to the waveform viewer without re-running since everything has been logged.
When running large designs this mode can be quite slow and it might be better to just do `--gui=load` and manually add a few signals of interest.

## GHDL - Viewing signals in GTKWave
Signals can be viewed in GTKWave when using the GHDL simulator and GTKWave executable is found in the `PATH` environment variable.
The `--gtkwave={vcd,ghw}` flag is used to select the file format to use.
The `--gtkwave-args` flag can be used to pass additional arguments to GTKWave, remember to wrap them in double quotes (`""`).

## Disabling warnings from IEEE packages
Warnings from IEEE packages can be disabled in `run.py`:
```python
Expand Down
210 changes: 210 additions & 0 deletions vunit/ghdl_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (c) 2015, Lars Asplund lars.anders.asplund@gmail.com

"""
Interface for GHDL simulator
"""

from __future__ import print_function
import logging
LOGGER = logging.getLogger(__name__)

from vunit.ostools import Process
from vunit.exceptions import CompileError

from os.path import exists, join
import os
from distutils.spawn import find_executable
import subprocess
import shlex
from sys import stdout # To avoid output catched in non-verbose mode


class GHDLInterface:
"""
Interface for GHDL simulator
"""

name = "ghdl"

@staticmethod
def add_arguments(parser):
"""
Add command line arguments
"""
has_gtkwave = find_executable('gtkwave')
if not has_gtkwave:
return
group = parser.add_argument_group("ghdl",
description="GHDL specific flags")
group.add_argument("--gtkwave", choices=["vcd", "ghw"],
default=None,
help="Save .vcd or .ghw and open in gtkwave")
group.add_argument("--gtkwave-args",
default="",
help="Arguments to pass to gtkwave")

@classmethod
def from_args(cls, output_path, args):
return cls(gtkwave=args.gtkwave,
gtkwave_args=args.gtkwave_args)

@staticmethod
def is_available():
"""
Return True if GHDL is installed
"""
return find_executable('ghdl') is not None

def __init__(self, gtkwave=None, gtkwave_args=""):
self._libraries = {}
self._vhdl_standard = None
self._gtkwave = gtkwave
self._gtkwave_args = gtkwave_args
self._backend = self.determine_backend()

@staticmethod
def determine_backend():
"""
Determine the GHDL backend
"""
mapping = {
"mcode code generator": "mcode",
"llvm code generator": "llvm",
"GCC back-end code generator": "gcc"
}
output = subprocess.check_output(["ghdl", "--version"]).decode()
for name, backend in mapping.items():
if name in output:
LOGGER.info("Detected GHDL %s", name)
return backend

LOGGER.error("Could not detect known LLVM backend by parsing 'ghdl --version'")
print("Expected to find one of %r" % mapping.keys())
print("== Output of 'ghdl --version'" + ("=" * 60))
print(output)
print("=============================" + ("=" * 60))
raise AssertionError("No known GHDL back-end could be detected from running 'ghdl --version'")

def _has_output_flag(self):
"""
Returns if backend supports output flag
"""
return self._backend in ("llvm", "gcc")

def compile_project(self, project, vhdl_standard):
"""
Compile project using vhdl_standard
"""
self._libraries = {}
self._vhdl_standard = vhdl_standard
for library in project.get_libraries():
if not exists(library.directory):
os.makedirs(library.directory)
self._libraries[library.name] = library.directory

for source_file in project.get_files_in_compile_order():
print('Compiling ' + source_file.name + ' ...')

if source_file.file_type == 'vhdl':
success = self.compile_vhdl_file(source_file.name, source_file.library.name,
source_file.library.directory, vhdl_standard)
elif source_file.file_type == 'verilog':
raise RuntimeError("Unkown file type: " + source_file.file_type)

if not success:
raise CompileError("Failed to compile '%s'" % source_file.name)
project.update(source_file)

def _std_str(self):
"""
Convert standard to format of GHDL command line flag
"""
if self._vhdl_standard == "2002":
return "02"
elif self._vhdl_standard == "2008":
return "08"
elif self._vhdl_standard == "93":
return "93"
else:
assert False

def compile_vhdl_file(self, source_file_name, library_name, library_path, vhdl_standard):
"""
Compile a vhdl file
"""
try:
cmd = ['ghdl', '-a', '--workdir=%s' % library_path,
'--work=%s' % library_name,
'--std=%s' % self._std_str()]
for library_name, library_path in self._libraries.items():
cmd += ["-P%s" % library_path]
cmd += [source_file_name]
proc = Process(cmd)
proc.consume_output()
except Process.NonZeroExitCode:
return False
return True

def simulate(self, # pylint: disable=too-many-arguments, too-many-locals
output_path, library_name, entity_name, architecture_name=None, generics=None, pli=None,
load_only=False, fail_on_warning=False, disable_ieee_warnings=False):
"""
Simulate with entity as top level using generics
"""
assert pli in (None, [])

ghdl_output_path = join(output_path, self.name)
data_file_name = join(ghdl_output_path, "wave.%s" % self._gtkwave)
if not exists(ghdl_output_path):
os.makedirs(ghdl_output_path)

launch_gtkwave = self._gtkwave is not None and not load_only

status = True
try:
cmd = []
cmd += ['--elab-run']
cmd += ['--std=%s' % self._std_str()]
cmd += ['--work=%s' % library_name]
cmd += ['--workdir=%s' % self._libraries[library_name]]
cmd += ['-P%s' % path for path in self._libraries.values()]

if self._backend == "llvm":
cmd += ['-o', join(ghdl_output_path, "%s-%s" % (entity_name, architecture_name))]

cmd += [entity_name, architecture_name]

for name, value in generics.items():
cmd += ['-g%s=%s' % (name, value)]

cmd += ['--assert-level=%s' % ("warning" if fail_on_warning else "error")]

if disable_ieee_warnings:
cmd += ["--ieee-asserts=disable"]

if load_only:
cmd += ["--no-run"]

if launch_gtkwave:
if exists(data_file_name):
os.remove(data_file_name)
if self._gtkwave == "ghw":
cmd += ['--wave=%s' % data_file_name]
elif self._gtkwave == "vcd":
cmd += ['--vcd=%s' % data_file_name]

proc = Process(['ghdl'] + cmd)
proc.consume_output()
except Process.NonZeroExitCode:
status = False

if launch_gtkwave:
cmd = ["gtkwave"] + shlex.split(self._gtkwave_args) + [data_file_name]
stdout.write("%s\n" % " ".join(cmd))
subprocess.call(cmd)

return status
3 changes: 2 additions & 1 deletion vunit/simulator_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""

from vunit.modelsim_interface import ModelSimInterface
from vunit.ghdl_interface import GHDLInterface
from os.path import join, exists
import os

Expand All @@ -23,7 +24,7 @@ def supported_simulators():
"""
Return a list of supported simulator classes
"""
return [ModelSimInterface]
return [ModelSimInterface, GHDLInterface]

@classmethod
def available_simulators(cls):
Expand Down
1 change: 1 addition & 0 deletions vunit/test/acceptance/test_artificial.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def test_run_selected_tests_in_same_sim_test_bench(self):
("failed", "lib.tb_same_sim_some_fail.Test 2"),
("skipped", "lib.tb_same_sim_some_fail.Test 3")])

@unittest.skipIf(simulator_is("ghdl"), "GHDL does not support verilog")
def test_compile_verilog(self):
verilog_path = join(dirname(__file__), "verilog")
ui = VUnit.from_argv(argv=["--clean",
Expand Down
Loading

0 comments on commit 9849f08

Please sign in to comment.