fixed automatic analysis when elaborating

This commit is contained in:
Erik Örtenberg 2025-03-28 16:59:28 +01:00
parent b9f4929726
commit 7b5ab586cb
4 changed files with 98 additions and 52 deletions

View File

@ -3,7 +3,7 @@ from re import split
import subprocess
from typing import List
from project_man import addLibraryInProject, removeLibraryInProject
from project_man import addLibraryInProject, getLibraryInProject, removeLibraryInProject
def getCfFileId(std: str):
return "08" if std == "08" else "93" ## Weird behaviour from GHDL, but all vhdl versions besides 08 have [...]93.cf
@ -48,21 +48,19 @@ def addVHDLFiles(fileNames: List[str], std: str, lib: str):
print("no files to add.")
return 0
print(f"adding {vhdlFiles} to library {lib}")
command = ["ghdl", "-i", "--workdir=work", f"--work={lib}", f"--std={std}"] + vhdlFiles
command = ["ghdl", "-i", "-C", "--workdir=work", f"--work={lib}", f"--std={std}"] + vhdlFiles
subprocess.run(command)
return 0
def removeVHDLFiles(fileNames: List[str], std: str, lib: str):
if not ghdlEnvExists(std=std, lib=lib):
return -1
cfFileId = getCfFileId(std)
cfFileName = list(filter(lambda x: ".cf" in x and lib in x and cfFileId in x, os.listdir("work")))[0]
cfFilePath = os.path.join("work",cfFileName)
cfFilePath = getCfFilePath(std, lib)
currentlyAdded = getCurrentlyAddedFiles(cfFilePath)
for fileName in fileNames:
if fileName not in currentlyAdded:
print(f"file {fileName} is not present in {cfFileName}.")
print(f"file {fileName} is not present in {cfFilePath}.")
return 0
currentlyAdded.remove(fileName)
removeCurrentlyAddedFile(fileName, cfFilePath)
@ -72,11 +70,24 @@ def removeVHDLFiles(fileNames: List[str], std: str, lib: str):
os.remove(cfFilePath)
def getCfFilePath(std: str, lib: str):
(exists, libDict) = getLibraryInProject(lib)
if not exists:
cfFileId = getCfFileId(std)
cfFileName = list(filter(lambda x: ".cf" in x and lib in x and cfFileId in x, os.listdir("work")))[0]
return os.path.join("work",cfFileName)
workDir = os.path.join(libDict[lib]["path"], "work")
cfFileId = getCfFileId(std)
cfFileName = list(filter(lambda x: ".cf" in x and lib in x and cfFileId in x, os.listdir(workDir)))[0]
return os.path.join(workDir,cfFileName)
def getCurrentlyAddedFiles(cfFilePath:str):
f = open(cfFilePath,"r")
lines = f.readlines()
f.close()
fileLines = filter(lambda x: "file" in x, lines)
fileLines = filter(lambda x: "file" in split(" ", x)[0], lines)
files = map(lambda x: split("\" \"",x)[1], fileLines)
return list(files)

View File

@ -1,6 +1,7 @@
import os
import subprocess
import build_env
import pprint
from re import split, sub
from typing import List
from project_man import getLibrariesPresent, getLibrariesInProject, getLibraryInProject
@ -17,54 +18,81 @@ def getNeededDependencies(topDef: str, arch: str, lib: str, std: str, includes:
"-Wno-unhandled-attribute", "-Wno-elaboration"
]
command = [
"ghdl", "--elab-order", "--workdir=work", f"--work={lib}", f"--std={std}"
"ghdl","--gen-depends", "-C", "-fsynopsys", "--ieee=standard", "-fexplicit", "--workdir=work", f"--work={lib}", f"--std={std}", "-frelaxed-rules"
] + incs + disableWarnings + [
"--libraries", f"{topDef}", f"{arch}"
f"{topDef}", f"{arch}"
]
res = subprocess.run(command,capture_output=True)
if res.returncode != 0:
print(res.stdout, res.stderr)
res = split("\r\n", res.stdout.decode())
res = res[0:-1]
res = list(map(lambda x: {"lib": split(" ",x)[0], "file": split(" ", x)[1]}, res))
res = list(filter(lambda x: x["lib"] != lib, res))
outputDict = {}
for x in res:
if x["lib"] not in outputDict.keys():
outputDict[x["lib"]] = []
## For some reason, GHDL doesn't play nice with relative include paths, so
## many unnecessary "cd thing/.." occurs in a row. This regex fixes that.
fixed = sub(r"\w*/../","", x["file"])
outputDict[x["lib"]].append(fixed)
res = split("#", res.stdout.decode())
res = split("\r\n", res[-1])[1:-1]
res = list(filter(lambda x: x[0] != '/', res))
res = list(map(lambda x: split(":", x)[0], res))
res = list(map(lambda x: split("/", x[0:-2]), res))
res = list(map(lambda x: {"libPath":"/".join(x[0:-2]), "fileName":x[-1]}, res))
print(res)
return res
print(outputDict)
return outputDict
## anylyzes the needed dependecies such that elaboration using libraries is nice :)
def analyzeNeededDependencies(topDef: str, arch: str, lib: str, std: str, includes: List[str]):
print(f"Analyzing dependecies to {topDef} {arch}")
deps = getNeededDependencies(topDef, arch, lib, std, includes)
incs = generateIncludesForGHDL(includes)
print(incs)
[exists, libDict] = getLibrariesInProject()
pprint.pp(deps)
pprint.pp(libDict)
if not exists:
print("project doesn't exist")
return
for currLib in deps.keys():
currLibPath = libDict[currLib]["path"]
filesNames = list(map(lambda x: split("/",x)[-1],deps[currLib]))
vhdlFiles = list(map(lambda x: os.path.join(currLibPath, x),deps[currLib]))
succesful = 0
total = 0
for dep in deps:
currLibPath = dep["libPath"]
currFile = dep["fileName"]
currLib = ""
currStd = ""
for lib in libDict.keys():
if libDict[lib]["path"] == currLibPath:
currLib = lib
currStd = libDict[lib]["vhdl-version"]
if currLib == "":
print(f"no lib inferred for {currLibPath}")
continue
##filesNames = list(map(lambda x: split("/",x)[-1],deps[currLib]))
##vhdlFiles = list(map(lambda x: os.path.join(currLibPath, x),deps[currLib]))
##vhdlFiles = list(filter(lambda x: ".vhd" in x, os.listdir(currLibPath)))
##vhdlFiles = list(map(lambda x: os.path.join(currLibPath, x), vhdlFiles))
incs = generateIncludesForGHDL(includes, currLibPath)
vhdlFiles = build_env.getCurrentlyAddedFiles(build_env.getCfFilePath(currStd, currLib))
if len(vhdlFiles) == 0:
continue
print(f"Analyzing dependecies in {currLib}: {filesNames}")
currFileName = ""
for vhdlFile in vhdlFiles:
test = split("/",vhdlFile)[-1]
test = split(".vhd", test)[0]
if test == currFile:
currFileName = vhdlFile
if currFileName == "":
print(f"no file name found for {currFile}")
return
total = total + 1
command = [
"ghdl", "-a", f"--workdir={os.path.join(currLibPath, 'work')}", f"--work={currLib}", f"--std={std}"
] + incs + vhdlFiles
subprocess.run(command)
"ghdl", "-a", "-C", "-fsynopsys", "--ieee=standard", "-fexplicit" ,"--workdir=work", f"--work={currLib}", f"--std={std}", "-frelaxed-rules"
] + incs + [currFileName]
res = subprocess.run(command, cwd=currLibPath)
if res.returncode == 0:
succesful = succesful + 1
print(f"Successfully analyzed {currLib} - {currFileName}")
else:
print(f"Failed analysis {currLib} - {currFileName}")
print(f"Successfully analyzed {succesful}/{total} VHDL sources")
def generateIncludesForGHDL(includes: List[str]):
def generateIncludesForGHDL(includes: List[str], cwd=os.getcwd()):
cmd = []
[exists, projectLibs] = getLibrariesInProject()
[exists, projectLibs] = getLibrariesInProject(cwd=cwd)
if not exists:
return []
for lib in projectLibs.keys():
@ -80,10 +108,12 @@ def elabDesign(topDef: str, arch: str, lib: str, std: str, includes: List[str]):
print("No GHDL environment present. Add all needed files before elaborating")
analyzeNeededDependencies(topDef, arch, lib, std, includes)
incs = generateIncludesForGHDL(includes)
print("Dependecy analysis complete, continuing to elaboration")
command = [
"ghdl", "-m", "--workdir=work", f"--work={lib}", f"--std={std}"
"ghdl", "-m", "-C","-fsynopsys", "--ieee=standard", "-fexplicit" , "--workdir=work", f"--work={lib}", f"--std={std}"
] + incs + [
"-o", f"work/{topDef}-{arch}", f"work.{topDef}", f"{arch}"]
print(" ".join(command))
subprocess.run(command)
def runDesign(topDef: str, arch: str, lib: str, std: str, includes):
@ -93,7 +123,7 @@ def runDesign(topDef: str, arch: str, lib: str, std: str, includes):
wavePath = os.path.join(os.getcwd(), "wave")
incs = generateIncludesForGHDL(includes)
command = [ ## may add -v for verbose
"ghdl", "--elab-run", f"--workdir=work", f"--work={lib}", f"--std={std}"
"ghdl", "--elab-run", "-C", f"--workdir=work", f"--work={lib}", f"--std={std}"
] + incs + [
"-o", f"work/{topDef}-{arch}", f"{topDef}", f"{arch}", f"--wave=wave/{topDef}-{arch}.ghw" ##, "--read-wave-opt=<See"
]

View File

@ -1,5 +1,6 @@
import typer
import os
from re import split
from project_man import findProjectFile, getLibraryInProject, initProjectFile, addLibraryInProject, removeLibraryInProject, getLibrariesPresent
import elab as elaborate
import build_env
@ -7,7 +8,7 @@ from typing import List, Optional
from typing_extensions import Annotated
import subprocess
gantry_install_path = "/home/thesis1/exjobb-public/scripts"
gantry_install_path = "/home/thesis1/repos/gantry/src"
app = typer.Typer()
@ -25,7 +26,9 @@ def autoDetectLibrary(lib: str) -> str:
if not exists:
print("No libs found.")
return "work"
if len(presentLibs.keys()) == 1:
if len(presentLibs.keys()) == 0:
return split("/",os.getcwd())[-1]
elif len(presentLibs.keys()) == 1:
return presentLibs.popitem()[0]
else:
libs = list(map(lambda x: x[0], presentLibs.items()))
@ -48,7 +51,7 @@ def autoDetectStd(library: str, std: str) -> str:
if std == "" or std not in complete_vhdl_ver():
(exists, libDict) = getLibraryInProject(library)
if not exists:
return "93"
return "93c"
else:
return libDict["vhdl-version"]
else:
@ -69,7 +72,7 @@ def create(
def addLib(
libname: Annotated[str, typer.Argument(help="Name of the library. This is what is used for imports in VHDL.")],
libpath: Annotated[str, typer.Argument(help="Relative path to the library. This tells simulators where to import form.")],
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93"
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 94c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93c"
):
print(f"Adding library {libname} to gantry.toml")
addLibraryInProject(libname, libpath, std)
@ -84,7 +87,7 @@ def removeLib(
@software.command(help="Adds files to a library. Automatically updates gantry project file and creates GHDL project files")
def add(
filenames: Annotated[List[str], typer.Argument(help="Which files to add to the library. May be more than one.")],
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93",
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93c",
library: Annotated[str, typer.Option("--library", "-l", help="Library to compile to.")] = ""
):
library = autoDetectLibrary(library)
@ -97,7 +100,7 @@ def add(
@software.command(help="Removes files from a library. Automatically updates gantry project file and creates GHDL project files")
def remove(
filenames: Annotated[List[str], typer.Argument(help="Which files to add to the library. May be more than one.")],
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93",
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93c",
library: Annotated[str, typer.Option("--library", "-l", help="Library to compile to.")] = ""
):
library = autoDetectLibrary(library)
@ -111,7 +114,7 @@ def elab(
arch: Annotated[str, typer.Argument(help="Architecture to synthesize within the top definition provided")] = "",
library: Annotated[str, typer.Option("--library", "-l", help="Library to compile to")] = "",
includes: Annotated[Optional[List[str]], typer.Option("--include", "-i", help="Which libraries to include in compile")] = None,
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93"
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93c"
):
library = autoDetectLibrary(library)
std = autoDetectStd(library, std)
@ -128,7 +131,7 @@ def run(
arch: Annotated[str, typer.Argument(help="Architecture to synthesize within the top definition provided")] = "",
library: Annotated[str, typer.Option("--library", "-l", help="Library to compile to")] = "",
includes: Annotated[Optional[List[str]], typer.Option("--include", "-i", help="Which libraries to include in compile")] = None,
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93"
std: Annotated[str, typer.Option(help="Which VHDL standard to use. 87, 93, 93c, 00, 02 or 08", autocompletion=complete_vhdl_ver)] = "93c"
):
library = autoDetectLibrary(library)
std = autoDetectStd(library, std)

View File

@ -112,7 +112,7 @@ def addLibraryInProject(lib: str, relPath: str, std: str) -> "tuple[bool, str]":
return (wentWell, "")
return (False, "Library with this name is already declared")
def getLibraryInProject(lib: str) -> "tuple[bool, dict[str, Any]]":
def getLibraryInProject(lib: str, cwd=os.getcwd()) -> "tuple[bool, dict[str, Any]]":
(exists, projectDict) = getProjectDict()
if not exists:
return (False, {})
@ -122,9 +122,12 @@ def getLibraryInProject(lib: str) -> "tuple[bool, dict[str, Any]]":
return (False, {})
(_, projectRoot) = findProjectRoot()
if lib in projectDict["libraries"].keys():
return (True, projectDict["libraries"][lib])
libs[lib] = projectDict["libraries"][lib]
absPath = os.path.join(projectRoot, libs[lib]["path"])
libs[lib]["path"] = os.path.relpath(absPath, cwd)
return (True, libs)
return (False, {})
def getLibrariesInProject() -> "tuple[bool, dict[str, Any]]":
def getLibrariesInProject(cwd=os.getcwd()) -> "tuple[bool, dict[str, Any]]":
(exists, projectDict) = getProjectDict()
if not exists:
return (False, {})
@ -136,11 +139,10 @@ def getLibrariesInProject() -> "tuple[bool, dict[str, Any]]":
for lib in projectDict["libraries"].keys():
libs[lib] = projectDict["libraries"][lib]
absPath = os.path.join(projectRoot, libs[lib]["path"])
libs[lib]["path"] = os.path.relpath(absPath, os.getcwd())
libs[lib]["path"] = os.path.relpath(absPath, cwd)
return (True, libs)
def getLibrariesPresent() -> "tuple[bool, dict[str, Any]]":
cwd = os.getcwd()
def getLibrariesPresent(cwd=os.getcwd()) -> "tuple[bool, dict[str, Any]]":
(exists, libs) = getLibrariesInProject()
if not exists:
return (False, {})