From 7b5ab586cb9d51d29c532839c41a1ab81114fa76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20=C3=96rtenberg?= Date: Fri, 28 Mar 2025 16:59:28 +0100 Subject: [PATCH] fixed automatic analysis when elaborating --- src/build_env.py | 25 +++++++++---- src/elab.py | 92 ++++++++++++++++++++++++++++++---------------- src/gantry.py | 19 ++++++---- src/project_man.py | 14 ++++--- 4 files changed, 98 insertions(+), 52 deletions(-) diff --git a/src/build_env.py b/src/build_env.py index de63805..965a6d4 100644 --- a/src/build_env.py +++ b/src/build_env.py @@ -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) diff --git a/src/elab.py b/src/elab.py index 1e7cec6..9108c86 100644 --- a/src/elab.py +++ b/src/elab.py @@ -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= 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) diff --git a/src/project_man.py b/src/project_man.py index 55f062d..14c66d0 100644 --- a/src/project_man.py +++ b/src/project_man.py @@ -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, {})