Skip to content

Commit

Permalink
v0.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Paebbels authored Nov 1, 2021
2 parents 614445b + 50b689c commit 3b77fc1
Show file tree
Hide file tree
Showing 14 changed files with 575 additions and 78 deletions.
20 changes: 14 additions & 6 deletions .github/workflows/Pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
- name: Convert to HTML format
run: |
coverage html
rm htmlcov/.gitignore
- name: 📊 Publish coverage at CodeCov
continue-on-error: true
Expand Down Expand Up @@ -135,7 +136,8 @@ jobs:
- name: Check Static Typing
continue-on-error: true
run: |
mypy --html-report htmlmypy --pretty pyEDAA/ProjectModel
pwd
mypy --html-report htmlmypy -m pyEDAA.ProjectModel
- name: 📤 Upload 'Static Typing Report' artifact
continue-on-error: true
Expand Down Expand Up @@ -251,6 +253,7 @@ jobs:

if: startsWith(github.ref, 'refs/tags')
needs:
- Release
- Package

env:
Expand Down Expand Up @@ -284,6 +287,12 @@ jobs:
run: |
twine upload dist/*
- name: 🗑️ Delete packaging Artifacts
uses: geekyeggo/delete-artifact@v1
with:
name: |
${{ env.ARTIFACT }}
VerifyDocs:
name: 👍 Verify example snippets using Python 3.10
runs-on: ubuntu-latest
Expand Down Expand Up @@ -386,7 +395,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v2

- name: 📥 Download artifacts '${{ env.DOC }}' from 'StaticTypeCheck' job
- name: 📥 Download artifacts '${{ env.DOC }}' from 'BuildTheDocs' job
uses: actions/download-artifact@v2
with:
name: ${{ env.DOC }}
Expand Down Expand Up @@ -421,14 +430,14 @@ jobs:
name: 🗑️ Artifact Cleanup
runs-on: ubuntu-latest
needs:
- Package
- PublishOnPyPI
- Coverage
- StaticTypeCheck
- BuildTheDocs
- PublishToGitHubPages

env:
COVERAGE: ${{ needs.Coverage.outputs.artifact }}
TYPING: ${{ needs.StaticTypeCheck.outputs.artifact }}
PACKAGE: ${{ needs.Package.outputs.artifact }}
DOC: ${{ needs.BuildTheDocs.outputs.artifact }}

steps:
Expand All @@ -438,5 +447,4 @@ jobs:
name: |
${{ env.COVERAGE }}
${{ env.TYPING }}
${{ env.PACKAGE }}
${{ env.DOC }}
57 changes: 49 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,71 @@
[![Sourcecode License](https://img.shields.io/pypi/l/pyEDAA.ProjectModel?logo=Github&label=code%20license)](LICENSE.md)
[![GitHub tag (latest SemVer incl. pre-release)](https://img.shields.io/github/v/tag/edaa-org/pyEDAA.ProjectModel?logo=GitHub&include_prereleases)](https://github.com/edaa-org/pyEDAA.ProjectModel/tags)
[![GitHub release (latest SemVer incl. including pre-releases)](https://img.shields.io/github/v/release/edaa-org/pyEDAA.ProjectModel?logo=GitHub&include_prereleases)](https://github.com/edaa-org/pyEDAA.ProjectModel/releases/latest)
[![GitHub release date](https://img.shields.io/github/release-date/edaa-org/pyEDAA.ProjectModel?logo=GitHub&)](https://github.com/edaa-org/pyEDAA.ProjectModel/releases)
[![Dependent repos (via libraries.io)](https://img.shields.io/librariesio/dependent-repos/pypi/pyEDAA.ProjectModel?logo=GitHub)](https://github.com/edaa-org/pyEDAA.ProjectModel/network/dependents)
[![GitHub Workflow - Build and Test Status](https://img.shields.io/github/workflow/status/edaa-org/pyEDAA.ProjectModel/Test%20and%20Coverage?label=build%20and%20test&logo=GitHub%20Actions&logoColor=FFFFFF)](https://github.com/edaa-org/pyEDAA.ProjectModel/actions?query=workflow%3A%22Test+and+Coverage%22)
[![GitHub release date](https://img.shields.io/github/release-date/edaa-org/pyEDAA.ProjectModel?logo=GitHub&)](https://github.com/edaa-org/pyEDAA.ProjectModel/releases)
[![GitHub Workflow - Build and Test Status](https://img.shields.io/github/workflow/status/edaa-org/pyEDAA.ProjectModel/Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish?label=build%20and%20test&logo=GitHub%20Actions&logoColor=FFFFFF)](https://github.com/edaa-org/pyEDAA.ProjectModel/actions?query=workflow%3A%22Unit%20Testing,%20Coverage%20Collection,%20Package,%20Release,%20Documentation%20and%20Publish%22)
[![Codacy - Quality](https://img.shields.io/codacy/grade/c2635df20fa840bc85639ca2fa1d9cb4?logo=Codacy)](https://www.codacy.com/manual/edaa-org/pyEDAA.ProjectModel)
[![Codacy - Coverage](https://img.shields.io/codacy/coverage/c2635df20fa840bc85639ca2fa1d9cb4?logo=Codacy)](https://www.codacy.com/manual/edaa-org/pyEDAA.ProjectModel)
[![Codecov - Branch Coverage](https://img.shields.io/codecov/c/github/edaa-org/pyEDAA.ProjectModel?logo=Codecov)](https://codecov.io/gh/edaa-org/pyEDAA.ProjectModel)
[![Libraries.io SourceRank](https://img.shields.io/librariesio/sourcerank/pypi/pyEDAA.ProjectModel)](https://libraries.io/github/edaa-org/pyEDAA.ProjectModel/sourcerank)
[![GitHub Workflow Release Status](https://img.shields.io/github/workflow/status/edaa-org/pyEDAA.ProjectModel/Release?label=release&logo=GitHub%20Actions&logoColor=FFFFFF)](https://github.com/edaa-org/pyEDAA.ProjectModel/actions?query=workflow%3A%22Release%22)
[![PyPI](https://img.shields.io/pypi/v/pyEDAA.ProjectModel?logo=PyPI&logoColor=FBE072)](https://pypi.org/project/pyEDAA.ProjectModel/)
![PyPI - Status](https://img.shields.io/pypi/status/pyEDAA.ProjectModel?logo=PyPI&logoColor=FBE072)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyEDAA.ProjectModel?logo=PyPI&logoColor=FBE072)
[![Libraries.io status for latest release](https://img.shields.io/librariesio/release/pypi/pyEDAA.ProjectModel)](https://libraries.io/github/edaa-org/pyEDAA.ProjectModel)
[![Requires.io](https://img.shields.io/requires/github/edaa-org/pyEDAA.ProjectModel)](https://requires.io/github/edaa-org/pyEDAA.ProjectModel/requirements/?branch=main)
[![GitHub Workflow - Documentation Status](https://img.shields.io/github/workflow/status/edaa-org/pyEDAA.ProjectModel/Documentation?label=documentation&logo=GitHub%20Actions&logoColor=FFFFFF)](https://github.com/edaa-org/pyEDAA.ProjectModel/actions?query=workflow%3A%22Documentation%22)
[![Documentation License](https://img.shields.io/badge/doc%20license-CC--BY%204.0-green)](LICENSE.md)
[![Documentation - Read Now!](https://img.shields.io/badge/doc-read%20now%20%E2%9E%94-blueviolet)](https://edaa-org.github.io/pyEDAA.ProjectModel/)

<!--
[![Dependent repos (via libraries.io)](https://img.shields.io/librariesio/dependent-repos/pypi/pyEDAA.ProjectModel?logo=GitHub)](https://github.com/edaa-org/pyEDAA.ProjectModel/network/dependents)
-->

# pyEDAA.ProjectModel

* abstract model of EDA tool projects
* filesets, filetypes, ...
This package provides a unified abstract project model for HDL designs and EDA tools.
Third-party frameworks can derive own classes and implement additional logic to create
a concrete project model for their tools.

Frameworks consuming this model can build higher level features and services on top of
such a model, while supporting multiple input sources.

## Data Model

1. The toplevel element is a `Project`, which contains one or multiple designs.
2. A `Design` is a variant of a project and contains filesets.
3. A `FileSet` contains files or further sub-filesets.
4. A `File` represents a single file. E.g. source files, configuration files, constraint files.
5. A `VHDLLibrary` represents a group of `VHDLSourceFile`s being compiled into the same VHDL library.

![img.png](doc/datamodel.png)

## Features

* Construct a project model:
* top-down (project &rarr; design &rarr; fileset &rarr; file) or
* bottom-up (file &rarr; fileset &rarr; design &rarr; project) or
* parsing a project file.
* Designs, filesets and files can use absolute or relative paths.
* `ResolvedPath` returns the resolved absolute path to an object.
* Projects, designs, filesets and files can be validated (e.g. if the path exists).
* Projects, designs, filesets and files can have user-defined attributes.
* User-defined attributes are resolved bottom-up.


## Project File Readers

### OSVVM `*.pro` File Reader

ProjectModel can read `*.pro` files and extract source files. Included `*.pro` files
are represented as sub-filesets.

### Xilinx Vivado `*.xpr` Reader

ProjectModel can read `*.xpr` files and extract source, constraint and simulation
files while preserving the fileset structure.

## Use Cases
* *tbd*
* Reading OSVVM's `*.pro` files.
* Reading Xilinx Vivado's `*.xpr` files.


## Examples
Expand Down Expand Up @@ -71,6 +111,7 @@ for file in designA.Files(fileType=VHDLSourceFile):
## Contributors
* [Patrick Lehmann](https://github.com/Paebbels) (Maintainer)
* [Unai Martinez-Corral](https://github.com/umarcor)
* [Stefan Unrein](https://github.com/stefanunrein)
* [and more...](https://github.com/edaa-org/pyEDAA.ProjectModel/graphs/contributors)


Expand Down
Binary file added doc/datamodel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions pyEDAA/ProjectModel/GHDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@
# SPDX-License-Identifier: Apache-2.0
# ============================================================================
#
from pydecor import export

from pyEDAA.ProjectModel import WaveformExchangeFile


@export
class GHDLWaveformFile(WaveformExchangeFile):
"""GHDL's waveform file (``*.ghw``) supporting VHDL and Verilog simulation results."""
193 changes: 193 additions & 0 deletions pyEDAA/ProjectModel/OSVVM.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# =============================================================================
# _____ ____ _ _ ____ _ _ __ __ _ _
# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| |
# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ |
# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ |
# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_|
# |_| |___/ |__/
# =============================================================================
# Authors: Patrick Lehmann
#
# Package installer: An abstract model of EDA tool projects.
#
# License:
# ============================================================================
# Copyright 2017-2021 Patrick Lehmann - Boetzingen, Germany
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# ============================================================================
#
from enum import Enum, unique
from pathlib import Path

from pydecor import export
from typing import Optional as Nullable, List

from pyEDAA.ProjectModel import ProjectFile, TCLContent, Project, Design, FileSet, VHDLLibrary, VHDLSourceFile


@export
class OSVVMProjectFile(ProjectFile, TCLContent):
"""An OSVVM project file (``*.pro``)."""

_osvvmProject: Nullable[Project]

def __init__(
self,
path: Path,
project: Project = None,
design: Design = None,
fileSet: FileSet = None
):
super().__init__(path, project, design, fileSet)

self._osvvmProject = None

@property
def ProjectModel(self) -> Project:
return self._osvvmProject

class Instruction:
_line: int

def __init__(self, line: int):
self._line = line

class Empty(Instruction):
def __init__(self, line: int):
super().__init__(line)

class Comment(Instruction):
_commentText: str

def __init__(self, line: int, commentText: str):
super().__init__(line)
self._commentText = commentText.rstrip()

@property
def CommentText(self) -> str:
return self._commentText

class Analyze(Instruction):
_vhdlSourceFile: VHDLSourceFile

def __init__(self, line: int, parameterText: str):
super().__init__(line)
self._vhdlSourceFile = VHDLSourceFile(Path(parameterText.strip()))

@property
def VHDLSourceFile(self) -> VHDLSourceFile:
return self._vhdlSourceFile

class Library(Instruction):
_vhdlLibrary: VHDLLibrary

def __init__(self, line: int, parameterText: str):
super().__init__(line)
self._vhdlLibrary = VHDLLibrary(parameterText.strip())

@property
def VHDLLibrary(self) -> VHDLLibrary:
return self._vhdlLibrary

class Include(Instruction):
_osvvmProjectFile: 'OSVVMProjectFile'
_fileSet: FileSet

def __init__(self, line: int, workingDirectory: Path, parameterText: str):
super().__init__(line)

includeFile = Path(parameterText.strip())
includePath = (workingDirectory / includeFile).resolve()

self._fileSet = FileSet(includeFile.name, directory=includeFile.parent)
self._osvvmProjectFile = OSVVMProjectFile(includePath)

@property
def OSVVMProjectFile(self) -> 'OSVVMProjectFile':
return self._osvvmProjectFile

def Parse(self, fileSet: FileSet):
self._fileSet.Parent = fileSet

for instruction in self._osvvmProjectFile._Parse():
if isinstance(instruction, OSVVMProjectFile.Include):
instruction.Parse(self._fileSet)
elif isinstance(instruction, OSVVMProjectFile.Analyze):
self._fileSet.AddFile(instruction.VHDLSourceFile)
elif isinstance(instruction, OSVVMProjectFile.Library):
self._fileSet.Design.AddVHDLLibrary(instruction.VHDLLibrary)
# elif isinstance(instruction, OSVVMProjectFile.Build):

elif not isinstance(instruction, (OSVVMProjectFile.Empty, OSVVMProjectFile.Comment)):
raise Exception(f"Unknown instruction '{instruction.__class__.__name__}' in OSVVM project file '{self._osvvmProjectFile.ResolvedPath}'")

def Parse(self):
projectName = self._path.name
self._osvvmProject = Project(projectName, rootDirectory=self._path.parent)

fileSet = self._osvvmProject.DefaultDesign.DefaultFileSet

for instruction in self._Parse():
if isinstance(instruction, OSVVMProjectFile.Include):
instruction.Parse(fileSet)
elif isinstance(instruction, OSVVMProjectFile.Analyze):
fileSet.AddFile(instruction.VHDLSourceFile)
elif not isinstance(instruction, (OSVVMProjectFile.Empty, OSVVMProjectFile.Comment)):
raise Exception(f"Unknown instruction '{instruction.__class__.__name__}' in OSVVM project file '{self.ResolvedPath}'")

def _Parse(self):
path = self.ResolvedPath
if not path.exists():
raise Exception(f"OSVVM project file '{path}' not found.") from FileNotFoundError(f"File '{path}' not found.")

instructions: List = []
print()
with path.open("r") as file:
i = 1
for line in file:
line = line.lstrip()

if line.startswith("#"):
comment = OSVVMProjectFile.Comment(i, line[1:])
instructions.append(comment)

elif line.startswith("analyze"):
vhdlFile = OSVVMProjectFile.Analyze(i, line[8:])
instructions.append(vhdlFile)

elif line.startswith("library"):
vhdlLibrary = OSVVMProjectFile.Library(i, line[8:])
instructions.append(vhdlLibrary)

elif line.startswith("include"):
include = OSVVMProjectFile.Include(i, path.parent, line[8:])
instructions.append(include)

elif line.startswith("build"):
parameter = line[6:]
print(f"BUILD: {parameter}")
elif line.startswith("if"):
print(f"IF (line={i}): {line[3:].rstrip()}")
elif line.startswith("}"):
print(f"}} (line={i}): {line[2:].rstrip()}")
elif len(line) == 0:
instructions.append(OSVVMProjectFile.Empty(i))
else:
print(f"UNKNOWN (line={i}): '{line.rstrip()}'")

i += 1

return instructions
3 changes: 3 additions & 0 deletions pyEDAA/ProjectModel/Verilog.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@
# SPDX-License-Identifier: Apache-2.0
# ============================================================================
#
from pydecor import export

from pyEDAA.ProjectModel import WaveformExchangeFile


@export
class ValueChangeDumpFile(WaveformExchangeFile):
"""Verilog's waveform file (``*.vcd``) for exchanging value changes as defined by IEEE Std. 1364."""
Loading

0 comments on commit 3b77fc1

Please sign in to comment.