Skip to content

Commit 3b77fc1

Browse files
authored
v0.3.0
2 parents 614445b + 50b689c commit 3b77fc1

File tree

14 files changed

+575
-78
lines changed

14 files changed

+575
-78
lines changed

.github/workflows/Pipeline.yml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ jobs:
8282
- name: Convert to HTML format
8383
run: |
8484
coverage html
85+
rm htmlcov/.gitignore
8586
8687
- name: 📊 Publish coverage at CodeCov
8788
continue-on-error: true
@@ -135,7 +136,8 @@ jobs:
135136
- name: Check Static Typing
136137
continue-on-error: true
137138
run: |
138-
mypy --html-report htmlmypy --pretty pyEDAA/ProjectModel
139+
pwd
140+
mypy --html-report htmlmypy -m pyEDAA.ProjectModel
139141
140142
- name: 📤 Upload 'Static Typing Report' artifact
141143
continue-on-error: true
@@ -251,6 +253,7 @@ jobs:
251253

252254
if: startsWith(github.ref, 'refs/tags')
253255
needs:
256+
- Release
254257
- Package
255258

256259
env:
@@ -284,6 +287,12 @@ jobs:
284287
run: |
285288
twine upload dist/*
286289
290+
- name: 🗑️ Delete packaging Artifacts
291+
uses: geekyeggo/delete-artifact@v1
292+
with:
293+
name: |
294+
${{ env.ARTIFACT }}
295+
287296
VerifyDocs:
288297
name: 👍 Verify example snippets using Python 3.10
289298
runs-on: ubuntu-latest
@@ -386,7 +395,7 @@ jobs:
386395
- name: Checkout repository
387396
uses: actions/checkout@v2
388397

389-
- name: 📥 Download artifacts '${{ env.DOC }}' from 'StaticTypeCheck' job
398+
- name: 📥 Download artifacts '${{ env.DOC }}' from 'BuildTheDocs' job
390399
uses: actions/download-artifact@v2
391400
with:
392401
name: ${{ env.DOC }}
@@ -421,14 +430,14 @@ jobs:
421430
name: 🗑️ Artifact Cleanup
422431
runs-on: ubuntu-latest
423432
needs:
424-
- Package
425-
- PublishOnPyPI
433+
- Coverage
434+
- StaticTypeCheck
435+
- BuildTheDocs
426436
- PublishToGitHubPages
427437

428438
env:
429439
COVERAGE: ${{ needs.Coverage.outputs.artifact }}
430440
TYPING: ${{ needs.StaticTypeCheck.outputs.artifact }}
431-
PACKAGE: ${{ needs.Package.outputs.artifact }}
432441
DOC: ${{ needs.BuildTheDocs.outputs.artifact }}
433442

434443
steps:
@@ -438,5 +447,4 @@ jobs:
438447
name: |
439448
${{ env.COVERAGE }}
440449
${{ env.TYPING }}
441-
${{ env.PACKAGE }}
442450
${{ env.DOC }}

README.md

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,71 @@
22
[![Sourcecode License](https://img.shields.io/pypi/l/pyEDAA.ProjectModel?logo=Github&label=code%20license)](LICENSE.md)
33
[![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)
44
[![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)
5-
[![GitHub release date](https://img.shields.io/github/release-date/edaa-org/pyEDAA.ProjectModel?logo=GitHub&)](https://github.com/edaa-org/pyEDAA.ProjectModel/releases)
6-
[![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)
7-
[![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)
5+
[![GitHub release date](https://img.shields.io/github/release-date/edaa-org/pyEDAA.ProjectModel?logo=GitHub&)](https://github.com/edaa-org/pyEDAA.ProjectModel/releases)
6+
[![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)
87
[![Codacy - Quality](https://img.shields.io/codacy/grade/c2635df20fa840bc85639ca2fa1d9cb4?logo=Codacy)](https://www.codacy.com/manual/edaa-org/pyEDAA.ProjectModel)
98
[![Codacy - Coverage](https://img.shields.io/codacy/coverage/c2635df20fa840bc85639ca2fa1d9cb4?logo=Codacy)](https://www.codacy.com/manual/edaa-org/pyEDAA.ProjectModel)
109
[![Codecov - Branch Coverage](https://img.shields.io/codecov/c/github/edaa-org/pyEDAA.ProjectModel?logo=Codecov)](https://codecov.io/gh/edaa-org/pyEDAA.ProjectModel)
1110
[![Libraries.io SourceRank](https://img.shields.io/librariesio/sourcerank/pypi/pyEDAA.ProjectModel)](https://libraries.io/github/edaa-org/pyEDAA.ProjectModel/sourcerank)
12-
[![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)
1311
[![PyPI](https://img.shields.io/pypi/v/pyEDAA.ProjectModel?logo=PyPI&logoColor=FBE072)](https://pypi.org/project/pyEDAA.ProjectModel/)
1412
![PyPI - Status](https://img.shields.io/pypi/status/pyEDAA.ProjectModel?logo=PyPI&logoColor=FBE072)
1513
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyEDAA.ProjectModel?logo=PyPI&logoColor=FBE072)
1614
[![Libraries.io status for latest release](https://img.shields.io/librariesio/release/pypi/pyEDAA.ProjectModel)](https://libraries.io/github/edaa-org/pyEDAA.ProjectModel)
1715
[![Requires.io](https://img.shields.io/requires/github/edaa-org/pyEDAA.ProjectModel)](https://requires.io/github/edaa-org/pyEDAA.ProjectModel/requirements/?branch=main)
18-
[![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)
1916
[![Documentation License](https://img.shields.io/badge/doc%20license-CC--BY%204.0-green)](LICENSE.md)
2017
[![Documentation - Read Now!](https://img.shields.io/badge/doc-read%20now%20%E2%9E%94-blueviolet)](https://edaa-org.github.io/pyEDAA.ProjectModel/)
2118

19+
<!--
20+
[![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)
21+
-->
22+
2223
# pyEDAA.ProjectModel
2324

24-
* abstract model of EDA tool projects
25-
* filesets, filetypes, ...
25+
This package provides a unified abstract project model for HDL designs and EDA tools.
26+
Third-party frameworks can derive own classes and implement additional logic to create
27+
a concrete project model for their tools.
28+
29+
Frameworks consuming this model can build higher level features and services on top of
30+
such a model, while supporting multiple input sources.
31+
32+
## Data Model
33+
34+
1. The toplevel element is a `Project`, which contains one or multiple designs.
35+
2. A `Design` is a variant of a project and contains filesets.
36+
3. A `FileSet` contains files or further sub-filesets.
37+
4. A `File` represents a single file. E.g. source files, configuration files, constraint files.
38+
5. A `VHDLLibrary` represents a group of `VHDLSourceFile`s being compiled into the same VHDL library.
39+
40+
![img.png](doc/datamodel.png)
41+
42+
## Features
43+
44+
* Construct a project model:
45+
* top-down (project &rarr; design &rarr; fileset &rarr; file) or
46+
* bottom-up (file &rarr; fileset &rarr; design &rarr; project) or
47+
* parsing a project file.
48+
* Designs, filesets and files can use absolute or relative paths.
49+
* `ResolvedPath` returns the resolved absolute path to an object.
50+
* Projects, designs, filesets and files can be validated (e.g. if the path exists).
51+
* Projects, designs, filesets and files can have user-defined attributes.
52+
* User-defined attributes are resolved bottom-up.
53+
54+
55+
## Project File Readers
56+
57+
### OSVVM `*.pro` File Reader
58+
59+
ProjectModel can read `*.pro` files and extract source files. Included `*.pro` files
60+
are represented as sub-filesets.
61+
62+
### Xilinx Vivado `*.xpr` Reader
2663

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

2867
## Use Cases
29-
* *tbd*
68+
* Reading OSVVM's `*.pro` files.
69+
* Reading Xilinx Vivado's `*.xpr` files.
3070

3171

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

76117

doc/datamodel.png

22.6 KB
Loading

pyEDAA/ProjectModel/GHDL.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
# SPDX-License-Identifier: Apache-2.0
3030
# ============================================================================
3131
#
32+
from pydecor import export
33+
3234
from pyEDAA.ProjectModel import WaveformExchangeFile
3335

3436

37+
@export
3538
class GHDLWaveformFile(WaveformExchangeFile):
3639
"""GHDL's waveform file (``*.ghw``) supporting VHDL and Verilog simulation results."""

pyEDAA/ProjectModel/OSVVM.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# =============================================================================
2+
# _____ ____ _ _ ____ _ _ __ __ _ _
3+
# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| |
4+
# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ |
5+
# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ |
6+
# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_|
7+
# |_| |___/ |__/
8+
# =============================================================================
9+
# Authors: Patrick Lehmann
10+
#
11+
# Package installer: An abstract model of EDA tool projects.
12+
#
13+
# License:
14+
# ============================================================================
15+
# Copyright 2017-2021 Patrick Lehmann - Boetzingen, Germany
16+
#
17+
# Licensed under the Apache License, Version 2.0 (the "License");
18+
# you may not use this file except in compliance with the License.
19+
# You may obtain a copy of the License at
20+
#
21+
# http://www.apache.org/licenses/LICENSE-2.0
22+
#
23+
# Unless required by applicable law or agreed to in writing, software
24+
# distributed under the License is distributed on an "AS IS" BASIS,
25+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26+
# See the License for the specific language governing permissions and
27+
# limitations under the License.
28+
#
29+
# SPDX-License-Identifier: Apache-2.0
30+
# ============================================================================
31+
#
32+
from enum import Enum, unique
33+
from pathlib import Path
34+
35+
from pydecor import export
36+
from typing import Optional as Nullable, List
37+
38+
from pyEDAA.ProjectModel import ProjectFile, TCLContent, Project, Design, FileSet, VHDLLibrary, VHDLSourceFile
39+
40+
41+
@export
42+
class OSVVMProjectFile(ProjectFile, TCLContent):
43+
"""An OSVVM project file (``*.pro``)."""
44+
45+
_osvvmProject: Nullable[Project]
46+
47+
def __init__(
48+
self,
49+
path: Path,
50+
project: Project = None,
51+
design: Design = None,
52+
fileSet: FileSet = None
53+
):
54+
super().__init__(path, project, design, fileSet)
55+
56+
self._osvvmProject = None
57+
58+
@property
59+
def ProjectModel(self) -> Project:
60+
return self._osvvmProject
61+
62+
class Instruction:
63+
_line: int
64+
65+
def __init__(self, line: int):
66+
self._line = line
67+
68+
class Empty(Instruction):
69+
def __init__(self, line: int):
70+
super().__init__(line)
71+
72+
class Comment(Instruction):
73+
_commentText: str
74+
75+
def __init__(self, line: int, commentText: str):
76+
super().__init__(line)
77+
self._commentText = commentText.rstrip()
78+
79+
@property
80+
def CommentText(self) -> str:
81+
return self._commentText
82+
83+
class Analyze(Instruction):
84+
_vhdlSourceFile: VHDLSourceFile
85+
86+
def __init__(self, line: int, parameterText: str):
87+
super().__init__(line)
88+
self._vhdlSourceFile = VHDLSourceFile(Path(parameterText.strip()))
89+
90+
@property
91+
def VHDLSourceFile(self) -> VHDLSourceFile:
92+
return self._vhdlSourceFile
93+
94+
class Library(Instruction):
95+
_vhdlLibrary: VHDLLibrary
96+
97+
def __init__(self, line: int, parameterText: str):
98+
super().__init__(line)
99+
self._vhdlLibrary = VHDLLibrary(parameterText.strip())
100+
101+
@property
102+
def VHDLLibrary(self) -> VHDLLibrary:
103+
return self._vhdlLibrary
104+
105+
class Include(Instruction):
106+
_osvvmProjectFile: 'OSVVMProjectFile'
107+
_fileSet: FileSet
108+
109+
def __init__(self, line: int, workingDirectory: Path, parameterText: str):
110+
super().__init__(line)
111+
112+
includeFile = Path(parameterText.strip())
113+
includePath = (workingDirectory / includeFile).resolve()
114+
115+
self._fileSet = FileSet(includeFile.name, directory=includeFile.parent)
116+
self._osvvmProjectFile = OSVVMProjectFile(includePath)
117+
118+
@property
119+
def OSVVMProjectFile(self) -> 'OSVVMProjectFile':
120+
return self._osvvmProjectFile
121+
122+
def Parse(self, fileSet: FileSet):
123+
self._fileSet.Parent = fileSet
124+
125+
for instruction in self._osvvmProjectFile._Parse():
126+
if isinstance(instruction, OSVVMProjectFile.Include):
127+
instruction.Parse(self._fileSet)
128+
elif isinstance(instruction, OSVVMProjectFile.Analyze):
129+
self._fileSet.AddFile(instruction.VHDLSourceFile)
130+
elif isinstance(instruction, OSVVMProjectFile.Library):
131+
self._fileSet.Design.AddVHDLLibrary(instruction.VHDLLibrary)
132+
# elif isinstance(instruction, OSVVMProjectFile.Build):
133+
134+
elif not isinstance(instruction, (OSVVMProjectFile.Empty, OSVVMProjectFile.Comment)):
135+
raise Exception(f"Unknown instruction '{instruction.__class__.__name__}' in OSVVM project file '{self._osvvmProjectFile.ResolvedPath}'")
136+
137+
def Parse(self):
138+
projectName = self._path.name
139+
self._osvvmProject = Project(projectName, rootDirectory=self._path.parent)
140+
141+
fileSet = self._osvvmProject.DefaultDesign.DefaultFileSet
142+
143+
for instruction in self._Parse():
144+
if isinstance(instruction, OSVVMProjectFile.Include):
145+
instruction.Parse(fileSet)
146+
elif isinstance(instruction, OSVVMProjectFile.Analyze):
147+
fileSet.AddFile(instruction.VHDLSourceFile)
148+
elif not isinstance(instruction, (OSVVMProjectFile.Empty, OSVVMProjectFile.Comment)):
149+
raise Exception(f"Unknown instruction '{instruction.__class__.__name__}' in OSVVM project file '{self.ResolvedPath}'")
150+
151+
def _Parse(self):
152+
path = self.ResolvedPath
153+
if not path.exists():
154+
raise Exception(f"OSVVM project file '{path}' not found.") from FileNotFoundError(f"File '{path}' not found.")
155+
156+
instructions: List = []
157+
print()
158+
with path.open("r") as file:
159+
i = 1
160+
for line in file:
161+
line = line.lstrip()
162+
163+
if line.startswith("#"):
164+
comment = OSVVMProjectFile.Comment(i, line[1:])
165+
instructions.append(comment)
166+
167+
elif line.startswith("analyze"):
168+
vhdlFile = OSVVMProjectFile.Analyze(i, line[8:])
169+
instructions.append(vhdlFile)
170+
171+
elif line.startswith("library"):
172+
vhdlLibrary = OSVVMProjectFile.Library(i, line[8:])
173+
instructions.append(vhdlLibrary)
174+
175+
elif line.startswith("include"):
176+
include = OSVVMProjectFile.Include(i, path.parent, line[8:])
177+
instructions.append(include)
178+
179+
elif line.startswith("build"):
180+
parameter = line[6:]
181+
print(f"BUILD: {parameter}")
182+
elif line.startswith("if"):
183+
print(f"IF (line={i}): {line[3:].rstrip()}")
184+
elif line.startswith("}"):
185+
print(f"}} (line={i}): {line[2:].rstrip()}")
186+
elif len(line) == 0:
187+
instructions.append(OSVVMProjectFile.Empty(i))
188+
else:
189+
print(f"UNKNOWN (line={i}): '{line.rstrip()}'")
190+
191+
i += 1
192+
193+
return instructions

pyEDAA/ProjectModel/Verilog.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
# SPDX-License-Identifier: Apache-2.0
3030
# ============================================================================
3131
#
32+
from pydecor import export
33+
3234
from pyEDAA.ProjectModel import WaveformExchangeFile
3335

3436

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

0 commit comments

Comments
 (0)