aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.coveragerc12
-rw-r--r--.gitignore42
-rw-r--r--pyGHDL/README.md12
-rw-r--r--pyGHDL/cli/__init__.py0
-rw-r--r--pyGHDL/dom/Common.py48
-rw-r--r--pyGHDL/dom/DesignUnit.py100
-rw-r--r--pyGHDL/dom/InterfaceItem.py33
-rw-r--r--pyGHDL/dom/Misc.py96
-rw-r--r--pyGHDL/dom/__init__.py4
-rw-r--r--requirements.txt2
-rw-r--r--testsuite/pyunit/dom/Instantiate.py25
-rw-r--r--testsuite/pyunit/libghdl/Initialize.py20
-rw-r--r--testsuite/requirements.txt4
13 files changed, 389 insertions, 9 deletions
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 000000000..0ebb5bd64
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,12 @@
+[run]
+branch = true
+
+[report]
+skip_covered = True
+skip_empty = True
+
+[html]
+directory = .cov
+
+[xml]
+output = coverage.xml
diff --git a/.gitignore b/.gitignore
index 897fd0904..2a290d646 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,8 @@
+# Ada outputs
*~
*.ali
*.o
b~*.ad?
-*.v87
-*.v93
-*.v08
-*.cf
-.gdb_history
-*.pyc
-
-testsuite/get_entities
# Generated files on windows.
/build/
@@ -19,7 +12,6 @@ testsuite/get_entities
# Generated files on Linux
config.status
default_paths.ads
-doc/_build/
elf_arch.ads
ghdl1-*
ghdl.gpr
@@ -44,3 +36,35 @@ run-bind.ads
src/version.ads
version.ads
version.tmp
+
+# GDB outputs
+.gdb_history
+
+# GHDL outputs
+*.v87
+*.v93
+*.v08
+*.cf
+
+# GHDLs testsuite
+testsuite/get_entities
+
+# Python cache and object files
+__pycache__/
+*.py[cod]
+
+# Python installation packages
+dist/
+
+# Coverage.py
+.coverage
+.cov
+coverage.xml
+
+# Sphinx
+doc/_build/
+doc/pyGHDL/**/*.*
+!doc/pyGHDL/index.rst
+
+# IntelliJ project files
+/.idea/workspace.xml
diff --git a/pyGHDL/README.md b/pyGHDL/README.md
new file mode 100644
index 000000000..deb45b8d8
--- /dev/null
+++ b/pyGHDL/README.md
@@ -0,0 +1,12 @@
+# pyGHDL
+
+Python binding for GHDL and high-level APIs.
+
+## Provided Packages
+
+* `pyGHDL.libghdl` - Low-level Python bindings to GHDL's `libghdl` shared library.
+ Auto generated API from Ada sources.
+* `pyGHDL.cli` - Command line interface tools.
+* `pyGHDL.lsp` - Language Server Protocol (LSP) implementation for VHDL.
+* `pyGHDL.dom` - Document Object Model (DOM) for VHDL parsed by `libghdl`.
+* `pyGHDL.xtools` - *tbd*
diff --git a/pyGHDL/cli/__init__.py b/pyGHDL/cli/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/pyGHDL/cli/__init__.py
diff --git a/pyGHDL/dom/Common.py b/pyGHDL/dom/Common.py
new file mode 100644
index 000000000..8baa6537e
--- /dev/null
+++ b/pyGHDL/dom/Common.py
@@ -0,0 +1,48 @@
+from pydecor import export
+
+from pyVHDLModel.VHDLModel import Modes
+
+from libghdl.thin import name_table
+from libghdl.thin.vhdl import nodes
+
+__all__ = []
+__api__ = __all__
+
+
+@export
+class GHDLBaseException(Exception):
+ pass
+
+
+@export
+class LibGHDLException(GHDLBaseException):
+ pass
+
+
+@export
+class GHDLException(GHDLBaseException):
+ pass
+
+
+@export
+class GHDLMixin:
+ _MODE_TRANSLATION = {
+ nodes.Iir_Mode.In_Mode: Modes.In,
+ nodes.Iir_Mode.Out_Mode: Modes.Out,
+ nodes.Iir_Mode.Inout_Mode: Modes.InOut,
+ nodes.Iir_Mode.Buffer_Mode: Modes.Buffer,
+ nodes.Iir_Mode.Linkage_Mode: Modes.Linkage
+ }
+
+ @classmethod
+ def _ghdlNodeToName(cls, node):
+ """Return the python string from node :param:`node` identifier"""
+ return name_table.Get_Name_Ptr(nodes.Get_Identifier(node)).decode("utf-8")
+
+ @classmethod
+ def _ghdlPortToMode(cls, port):
+ """Return the mode of a port."""
+ try:
+ return cls._MODE_TRANSLATION[nodes.Get_Mode(port)]
+ except KeyError:
+ raise LibGHDLException("Unknown mode.")
diff --git a/pyGHDL/dom/DesignUnit.py b/pyGHDL/dom/DesignUnit.py
new file mode 100644
index 000000000..894651c2c
--- /dev/null
+++ b/pyGHDL/dom/DesignUnit.py
@@ -0,0 +1,100 @@
+from pydecor import export
+
+from pyVHDLModel.VHDLModel import Entity as VHDLModel_Entity
+from pyVHDLModel.VHDLModel import Architecture as VHDLModel_Architecture
+from pyVHDLModel.VHDLModel import Package as VHDLModel_Package
+from pyVHDLModel.VHDLModel import PackageBody as VHDLModel_PackageBody
+from pyVHDLModel.VHDLModel import Context as VHDLModel_Context
+from pyVHDLModel.VHDLModel import Configuration as VHDLModel_Configuration
+
+from libghdl.thin.vhdl import nodes, pyutils
+
+from pyGHDL.dom.Common import GHDLMixin
+
+__all__ = []
+__api__ = __all__
+
+from pyGHDL.dom.InterfaceItem import GenericConstantInterfaceItem, PortSignalInterfaceItem
+
+
+@export
+class Entity(VHDLModel_Entity, GHDLMixin):
+
+ @classmethod
+ def parse(cls, libraryUnit):
+ name = cls._ghdlNodeToName(libraryUnit)
+ entity = cls(name)
+
+ cls.__parseGenerics(libraryUnit, entity)
+ cls.__parsePorts(libraryUnit, entity)
+
+ return entity
+
+ @classmethod
+ def __ghdlGetGenerics(cls, entity):
+ return pyutils.chain_iter(nodes.Get_Generic_Chain(entity))
+
+ @classmethod
+ def __ghdlGetPorts(cls, entity):
+ return pyutils.chain_iter(nodes.Get_Port_Chain(entity))
+
+ @classmethod
+ def __parseGenerics(cls, libraryUnit, entity):
+ for generic in cls.__ghdlGetGenerics(libraryUnit):
+ genericConstant = GenericConstantInterfaceItem.parse(generic)
+ entity.GenericItems.append(genericConstant)
+
+ @classmethod
+ def __parsePorts(cls, libraryUnit, entity):
+ for port in cls.__ghdlGetPorts(libraryUnit):
+ signalPort = PortSignalInterfaceItem.parse(port)
+ entity.PortItems.append(signalPort)
+
+@export
+class Architecture(VHDLModel_Architecture, GHDLMixin):
+ def __init__(self, name: str, entityName: str):
+ super().__init__(name)
+
+ self.__entityName = entityName
+
+ @classmethod
+ def parse(cls, libraryUnit):
+ name = cls._ghdlNodeToName(libraryUnit)
+ entityName = cls._ghdlNodeToName(nodes.Get_Entity_Name(libraryUnit))
+
+ return cls(name, entityName)
+
+ def resolve(self):
+ pass
+
+@export
+class Package(VHDLModel_Package, GHDLMixin):
+
+ @classmethod
+ def parse(cls, libraryUnit):
+ name = cls._ghdlNodeToName(libraryUnit)
+ return cls(name)
+
+@export
+class PackageBody(VHDLModel_PackageBody, GHDLMixin):
+
+ @classmethod
+ def parse(cls, libraryUnit):
+ name = cls._ghdlNodeToName(libraryUnit)
+ return cls(name)
+
+@export
+class Context(VHDLModel_Context, GHDLMixin):
+
+ @classmethod
+ def parse(cls, libraryUnit):
+ name = cls._ghdlNodeToName(libraryUnit)
+ return cls(name)
+
+@export
+class Configuration(VHDLModel_Configuration, GHDLMixin):
+
+ @classmethod
+ def parse(cls, libraryUnit):
+ name = cls._ghdlNodeToName(libraryUnit)
+ return cls(name)
diff --git a/pyGHDL/dom/InterfaceItem.py b/pyGHDL/dom/InterfaceItem.py
new file mode 100644
index 000000000..0833c2547
--- /dev/null
+++ b/pyGHDL/dom/InterfaceItem.py
@@ -0,0 +1,33 @@
+from pydecor import export
+
+from pyVHDLModel.VHDLModel import PortSignalInterfaceItem as VHDLModel_PortSignalInterfaceItem
+from pyVHDLModel.VHDLModel import GenericConstantInterfaceItem as VHDLModel_GenericConstantInterfaceItem
+
+from pyGHDL.dom.Common import GHDLMixin
+
+__all__ = []
+__api__ = __all__
+
+
+@export
+class GenericConstantInterfaceItem(VHDLModel_GenericConstantInterfaceItem, GHDLMixin):
+ @classmethod
+ def parse(cls, generic):
+ name = cls._ghdlNodeToName(generic)
+ mode = cls._ghdlPortToMode(generic)
+
+ generic = cls(name, mode)
+
+ return generic
+
+
+@export
+class PortSignalInterfaceItem(VHDLModel_PortSignalInterfaceItem, GHDLMixin):
+ @classmethod
+ def parse(cls, port):
+ name = cls._ghdlNodeToName(port)
+ mode = cls._ghdlPortToMode(port)
+
+ port = cls(name, mode)
+
+ return port
diff --git a/pyGHDL/dom/Misc.py b/pyGHDL/dom/Misc.py
new file mode 100644
index 000000000..ab47576fc
--- /dev/null
+++ b/pyGHDL/dom/Misc.py
@@ -0,0 +1,96 @@
+from pathlib import Path
+from typing import Any
+
+from pydecor import export
+
+from pyVHDLModel.VHDLModel import Design as VHDLModel_Design
+from pyVHDLModel.VHDLModel import Library as VHDLModel_Library
+from pyVHDLModel.VHDLModel import Document as VHDLModel_Document
+
+import libghdl
+from libghdl.thin import name_table, files_map, errorout_console
+from libghdl.thin.vhdl import nodes, sem_lib
+
+from pyGHDL.dom.Common import LibGHDLException, GHDLException
+from pyGHDL.dom.DesignUnit import Entity, Architecture, Package, PackageBody, Context, Configuration
+
+__all__ = []
+__api__ = __all__
+
+
+@export
+class Design(VHDLModel_Design):
+ def __init__(self):
+ super().__init__()
+
+ self.__ghdl_init()
+
+ def __ghdl_init(self):
+ """Initialization: set options and then load libraries"""
+
+ # Print error messages on the console
+ errorout_console.Install_Handler()
+
+ libghdl.set_option(b"--std=08")
+ libghdl.analyze_init()
+
+@export
+class Library(VHDLModel_Library):
+ pass
+
+
+@export
+class Document(VHDLModel_Document):
+ __ghdlFileID: Any
+ __ghdlSourceFileEntry: Any
+ __ghdlFile: Any
+
+ def __init__(self, path : Path = None):
+ super().__init__(path)
+
+ self.__ghdl_init()
+
+ def __ghdl_init(self):
+ # Read input file
+ self.__ghdlFileID = name_table.Get_Identifier(str(self.Path).encode("utf_8"))
+ self.__ghdlSourceFileEntry = files_map.Read_Source_File(name_table.Null_Identifier, self.__ghdlFileID)
+ if self.__ghdlSourceFileEntry == files_map.No_Source_File_Entry:
+ raise LibGHDLException("Cannot load file '{!s}'".format(self.Path))
+
+ # parse
+ self.__ghdlFile = sem_lib.Load_File(self.__ghdlSourceFileEntry)
+
+ def parse(self):
+ unit = nodes.Get_First_Design_Unit(self.__ghdlFile)
+ while unit != nodes.Null_Iir:
+ libraryUnit = nodes.Get_Library_Unit(unit)
+ nodeKind = nodes.Get_Kind(libraryUnit)
+
+ if (nodeKind == nodes.Iir_Kind.Entity_Declaration):
+ entity = Entity.parse(libraryUnit)
+ self.Entities.append(entity)
+
+ elif (nodeKind == nodes.Iir_Kind.Architecture_Body):
+ architecture = Architecture.parse(libraryUnit)
+ self.Architectures.append(architecture)
+
+ elif (nodeKind == nodes.Iir_Kind.Package_Declaration):
+ package = Package.parse(libraryUnit)
+ self.Packages.append(package)
+
+ elif (nodeKind == nodes.Iir_Kind.Package_Body):
+ packageBody = PackageBody.parse(libraryUnit)
+ self.PackageBodies.append(packageBody)
+
+ elif (nodeKind == nodes.Iir_Kind.Context_Declaration):
+ context = Context.parse(libraryUnit)
+ self.Contexts.append(context)
+
+ elif (nodeKind == nodes.Iir_Kind.Configuration_Declaration):
+ configuration = Configuration.parse(libraryUnit)
+ self.Configurations.append(configuration)
+
+ else:
+ raise GHDLException("Unknown design unit kind.")
+
+ unit = nodes.Get_Chain(unit)
diff --git a/pyGHDL/dom/__init__.py b/pyGHDL/dom/__init__.py
new file mode 100644
index 000000000..df2dfb868
--- /dev/null
+++ b/pyGHDL/dom/__init__.py
@@ -0,0 +1,4 @@
+from pydecor import export
+
+__all__ = []
+__api__ = __all__
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..6f7932078
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+pydecor>=2.0.1
+pyVHDLModel>=0.7.1
diff --git a/testsuite/pyunit/dom/Instantiate.py b/testsuite/pyunit/dom/Instantiate.py
new file mode 100644
index 000000000..429c1a5a8
--- /dev/null
+++ b/testsuite/pyunit/dom/Instantiate.py
@@ -0,0 +1,25 @@
+from pathlib import Path
+from unittest import TestCase
+
+from pyVHDLModel.VHDLModel import Design, Library, Document, Entity
+
+
+if __name__ == "__main__":
+ print("ERROR: you called a testcase declaration file as an executable module.")
+ print("Use: 'python -m unitest <testcase module>'")
+ exit(1)
+
+
+class Instantiate(TestCase):
+ def test_Design(self):
+ design = Design()
+
+ def test_Library(self):
+ library = Library()
+
+ def test_Document(self):
+ path = Path("tests.vhdl")
+ document = Document(path)
+
+ def test_Entity(self):
+ entity = Entity("entity_1")
diff --git a/testsuite/pyunit/libghdl/Initialize.py b/testsuite/pyunit/libghdl/Initialize.py
new file mode 100644
index 000000000..b7e370650
--- /dev/null
+++ b/testsuite/pyunit/libghdl/Initialize.py
@@ -0,0 +1,20 @@
+from unittest import TestCase
+
+
+if __name__ == "__main__":
+ print("ERROR: you called a testcase declaration file as an executable module.")
+ print("Use: 'python -m unitest <testcase module>'")
+ exit(1)
+
+class Instantiate(TestCase):
+ def test_InitializeGHDL(self):
+ pass
+
+ def test_ReadSourceFile(self):
+ pass
+
+ def test_ParseFile(self):
+ pass
+
+ def test_ListDesignUnits(self):
+ pass
diff --git a/testsuite/requirements.txt b/testsuite/requirements.txt
new file mode 100644
index 000000000..f7196c036
--- /dev/null
+++ b/testsuite/requirements.txt
@@ -0,0 +1,4 @@
+-r ../requirements.txt
+
+# Coverage collection
+Coverage>=5.3