Made a fix to the sky130.json file, which was missing a comma and therefore illegal JSON syntax. Corrected the openlane configuration for the "_OPT" tech LEF from "__max" to "__nom". Made some corrections to the project manager script, which comes up and runs now, although there is plenty of additional work to be done.
diff --git a/runtime/profile.py b/runtime/profile.py index 2fb3e0b..d533b07 100755 --- a/runtime/profile.py +++ b/runtime/profile.py
@@ -17,8 +17,6 @@ import subprocess from tkinter import ttk -import config - class Profile(tkinter.Toplevel): """Project manager profile settings management.""" @@ -74,27 +72,7 @@ if 'username' in prefs: self.username.set(prefs['username']) else: - userid = os.environ['USER'] - ''' - p = subprocess.run(['/ef/apps/bin/withnet', - config.apps_path + '/og_uid_service.py', userid], - stdout = subprocess.PIPE) - - if p.stdout: - uid_string = p.stdout.splitlines()[0].decode('utf-8') - userspec = re.findall(r'[^"\s]\S*|".+?"', uid_string) - if len(userspec) > 0: - username = userspec[0].strip('"') - # Note userspec[1] = UID and userspec[2] = role, useful - # for future applications. - else: - username = userid - else: - username = userid - ''' - username=userid - - self.username.set(username) + self.username.set(os.environ['USER']) # Graphics format for magic magicgraphics = ['X11', 'CAIRO', 'OPENGL']
diff --git a/runtime/project_manager.py b/runtime/project_manager.py index 4d26945..2be5f58 100755 --- a/runtime/project_manager.py +++ b/runtime/project_manager.py
@@ -660,8 +660,8 @@ self.project_pdkdir = os.path.realpath(self.projectpath + ProjectManager.config_path( self.projectpath) + '/techdir') self.foundry, foundry_name, self.node, desc, status = ProjectManager.pdkdir2fnd( self.project_pdkdir ) else: - if not os.path.exists(self.projectpath + '/info.yaml'): - self.error_label.configure(text = self.projectpath + ' does not contain an info.yaml file.') + if not os.path.exists(self.projectpath + '/project.json'): + self.error_label.configure(text = self.projectpath + ' does not contain an project.json file.') self.project_pdkdir = "" self.foundry = "" self.node = "" @@ -1592,10 +1592,10 @@ @classmethod def get_import_pdk(cls, projectpath): print(projectpath) - yamlname = projectpath + '/info.yaml' + jsonname = projectpath + '/project.json' - with open(yamlname, 'r') as f: - datatop = yaml.safe_load(f) + with open(jsonname, 'r') as f: + datatop = json.load(f) project_data = datatop['project'] project_foundry = project_data['foundry'] project_process = project_data['process'] @@ -1634,13 +1634,6 @@ return root + ext return None - def yaml2targz(self, yamlPath): - root = os.path.splitext(yamlPath)[0] - for ext in ('.tgz', '.tar.gz'): - if os.path.isfile(root + ext): - return root + ext - return None - #------------------------------------------------------------------------ # Remove a .json and associated tar.gz (or .tgz) if any. # If not a .json, remove just that file (no test for a tar). @@ -1706,7 +1699,7 @@ if os.path.isdir(ipath): if os.path.islink(ipath) or not self.validProjectName(item) \ or self.importProjNameBadrex1.match(item) \ - or not os.path.isfile(ipath + '/info.yaml'): + or not os.path.isfile(ipath + '/project.json'): importlist.remove(item) continue else: @@ -1752,7 +1745,7 @@ # Import for json documents and related tarballs (.gz or .tgz): #------------------------------------------------------------------------ - def importyaml(self, projname, importfile): + def importjson(self, projname, importfile): # (1) Check if there is a tarball with the same root name as the JSON importroot = os.path.splitext(importfile)[0] badrex1 = re.compile("^\.") @@ -1793,10 +1786,10 @@ # document, then change the name to match that of the project # folder. - yamlfile = newproject + '/info.yaml' + jaonfile = newproject + '/project.json' try: - shutil.copy(importfile, yamlfile) + shutil.copy(importfile, jsonfile) except IOError as e: print('Error copying files: ' + str(e)) return None @@ -2026,14 +2019,6 @@ return self.tarVglImportable(tar) - def yamlTarVglImportable(self, path): - ext = os.path.splitext(path)[1] - if ext != '.yaml': return None, None, None - - tar = self.yaml2targz(path) - if not tar: return None, None, None - - return self.tarVglImportable(tar) #------------------------------------------------------------------------ # Get a single named member (memPath) out of a JSON's tar file. # This is thin wrapper around tarMember2tempfile. Find the JSON's associated @@ -2049,15 +2034,6 @@ return self.tarMember2tempfile(tar, memPath) - def yamlTarMember2tempfile(self, path, memPath): - ext = os.path.splitext(path)[1] - if ext != '.yaml': return None - - tar = self.yaml2targz(path) - if not tar: return None - - return self.tarMember2tempfile(tar, memPath) - #------------------------------------------------------------------------ # Determine if tar-file can be imported as-if it were just a *.v. # Require exactly one yosys-output .netlist.v, and exactly one .json. @@ -2252,11 +2228,12 @@ return jData -#------------------------------------------------------------------------ - # Create info.yaml file (automatically done in create_project.py in case it's executed from the command line) + #------------------------------------------------------------------------ + # Create project.json file (automatically done in create_project.py in + # case it's executed from the command line) #------------------------------------------------------------------------ - def create_yaml(self, ipname, pdk_dir, description="(Add project description here)"): + def create_json(self, ipname, pdk_dir, description="(Add project description here)"): # ipname: Project Name data = {} project= {} @@ -2270,6 +2247,7 @@ project['flow'] = 'none' data['project']=project return data + #------------------------------------------------------------------------ # For a single named member (memPath) out of an open tarfile (tarf), # determine if it is a JSON file, and attempt to extract value of entry @@ -2377,10 +2355,10 @@ os.chdir(curdir) # Create a simple qflow_vars.sh file so that the project manager - # qflow launcher will see it as a qflow sub-project. If the meta.yaml - # file has a "stdcell" entry for the subproject, then add the line - # "techname=" with the name of the standard cell library as pulled - # from meta.yaml. + # qflow launcher will see it as a qflow sub-project. If the + # project.json file has a "stdcell" entry for the subproject, then + # add the line "techname=" with the name of the standard cell + # library as pulled from project.json. stdcell = None buildname = 'build/' + vname + '.netlist.v' @@ -3241,8 +3219,8 @@ netlistfile = None # Pull process and standard cell library from the YAML file created by - # CloudV. NOTE: YAML file has multiple documents, so must use - # yaml.load_all(), not yaml.load(). If there are refinements of this + # CloudV. NOTE: json file has multiple documents, so must use + # json.load_all(), not json.load(). If there are refinements of this # process for individual build files, they will override (see further down). # To do: Check entries for SoC builds. If there are multiple SoC builds, @@ -3252,23 +3230,24 @@ # that there can be multiple SoC builds in the project, so for now retaining # the existing parsing assuming default names.) - if os.path.exists(ppath + '/.ef-config/meta.yaml'): - print("Reading YAML file:") - ydicts = [] - with open(ppath + '/.ef-config/meta.yaml', 'r') as ifile: - yalldata = yaml.load_all(ifile, Loader=yaml.Loader) - for ydict in yalldata: - ydicts.append(ydict) + if os.path.exists(ppath + '/.config/nodeinfo.json'): + print("Reading nodeinfo.json file:") + jdicts = [] - for ydict in ydicts: - for yentry in ydict.values(): - if 'process' in yentry: - importnode = yentry['process'] + with open(ppath + '/.config/nodeinfo.json', 'r') as ifile: + jsondata = json.load_all(ifile, Loader=json.Loader) + for jdict in jsondata: + jdicts.append(jdict) + + for jdict in jdicts: + for jentry in jdict.values(): + if 'process' in jentry: + importnode = jentry['process'] # If there is a file ().soc and a directory ().model, then pull the file # ().model/().model.v, which is a chip top-level netlist. - ydicts = [] + jdicts = [] has_soc = False save_vdir = None vdirs = glob.glob(ppath + '/*') @@ -3303,22 +3282,22 @@ # Pull process and standard cell library from the YAML file # created by CloudV - # Use yaml.load_all(), not yaml.load() (see above) + # Use json.load_all(), not json.load() (see above) - if os.path.exists(ppath + '/.ef-config/meta.yaml'): + if os.path.exists(ppath + '/.config/nodeinfo.json'): print("Reading YAML file:") - ydicts = [] - with open(ppath + '/.ef-config/meta.yaml', 'r') as ifile: - yalldata = yaml.load_all(ifile, Loader=yaml.Loader) - for ydict in yalldata: - ydicts.append(ydict) + jdicts = [] + with open(ppath + '/.config/nodeinfo.json', 'r') as ifile: + jsondata = json.load_all(ifile, Loader=json.Loader) + for jdict in jsondata: + jdicts.append(jdict) - for ydict in ydicts: - for yentry in ydict.values(): - if 'process' in yentry: - importnode = yentry['process'] - if 'stdcell' in yentry: - stdcell = yentry['stdcell'] + for jdict in jdicts: + for jentry in jdict.values(): + if 'process' in jentry: + importnode = jentry['process'] + if 'stdcell' in jentry: + stdcell = jentry['stdcell'] break if importnode: @@ -3400,31 +3379,30 @@ # NOTE: Behavior is for project files to depend on "project_name". Using # the project filename as a project name is a fallback behavior. If - # there is a info.yaml file, and it defines a project_name entry, then + # there is a project.json file, and it defines a project_name entry, then # there is no need to make changes within the project. If there is - # no info.yaml file, then create one and set the project_name entry to + # no project.json file, then create one and set the project_name entry to # the old project name, which avoids the need to make changes within # the project. else: - # Check info.yaml - yamlname = newproject + '/info.yaml' + # Check project.json + jsonname = newproject + '/project.json' found = False - if os.path.isfile(yamlname): + if os.path.isfile(jsonname): # Pull the project_name into local store (may want to do this with the # datasheet as well) - with open(yamlname, 'r') as f: - datatop = yaml.safe_load(f) + with open(jsonname, 'r') as f: + datatop = json.safe_load(f) if 'project_name' in datatop['project']: found = True if not found: pdkdir = self.get_pdk_dir(newproject, path=True) - yData = self.create_yaml(oldname, pdkdir) - with open(newproject + '/info.yaml', 'w') as ofile: - print('---',file=ofile) - yaml.dump(yData, ofile) + yData = self.create_json(oldname, pdkdir) + with open(newproject + '/project.json', 'w') as ofile: + json.dump(yData, ofile) # If ngspice and electric prefs were not copied from the source # to the target, as recommended, then copy these from the @@ -3464,16 +3442,15 @@ is_subproject = True except: pass - if not os.path.exists(projectpath + '/info.yaml'): + if not os.path.exists(projectpath + '/project.json'): project_pdkdir = self.get_pdk_dir(projectpath, path=True) - data = self.create_yaml(os.path.split(projectpath)[1], project_pdkdir) - with open(projectpath + '/info.yaml', 'w') as ofile: - print('---',file=ofile) - yaml.dump(data, ofile) + data = self.create_json(os.path.split(projectpath)[1], project_pdkdir) + with open(projectpath + '/project.json', 'w') as ofile: + json.dump(data, ofile) - # Read yaml file for the selected flow - with open(projectpath + '/info.yaml','r') as f: - data = yaml.safe_load(f) + # Read json file for the selected flow + with open(projectpath + '/project.json','r') as f: + data = json.safe_load(f) project = data['project'] if 'flow' in project.keys() and project['flow']=='none' or 'flow' not in project.keys(): while True: @@ -3487,9 +3464,8 @@ break project['flow']=flow data['project']=project - with open(projectpath + '/info.yaml', 'w') as ofile: - print('---',file=ofile) - yaml.dump(data, ofile) + with open(projectpath + '/project.json', 'w') as ofile: + json.dump(data, ofile) else: flow = project['flow'] @@ -3515,16 +3491,16 @@ # read it and pull the ip-name record. If not, the fallback position # is to assume that the project filename is the project name. - # Check info.yaml + # Check project.json projectpath = self.projectdir + '/' + projname - yamlname = projectpath + '/info.yaml' + jsonname = projectpath + '/project.json' oldname = projname - if os.path.isfile(yamlname): + if os.path.isfile(jsonname): # Pull the ipname into local store (may want to do this with the # datasheet as well) - with open(yamlname, 'r') as f: - datatop = yaml.safe_load(f) + with open(jsonname, 'r') as f: + datatop = json.safe_load(f) project_data = datatop['project'] if 'project_name' in project_data: oldname = project_data['project_name'] @@ -3636,11 +3612,10 @@ os.symlink(projectpath, self.projectdir + '/' + newname) else: shutil.copytree(projectpath, self.projectdir + '/' + newname, symlinks = True) - if not os.path.exists(projectpath + '/info.yaml'): - yData = self.create_yaml(newname, project_pdkdir) - with open(projectpath + '/info.yaml', 'w') as ofile: - print('---',file=ofile) - yaml.dump(yData, ofile) + if not os.path.exists(projectpath + '/project.json'): + jData = self.create_json(newname, project_pdkdir) + with open(projectpath + '/project.json', 'w') as ofile: + json.dump(jData, ofile) else: #Create a subproject if not os.path.exists(parent_path + '/subcells'): @@ -3651,11 +3626,10 @@ self.clean(parent_path + '/subcells/' + newname) else: os.symlink(projectpath, parent_path + '/subcells/' + newname) - if not os.path.exists(parent_path + '/subcells/' + newname + '/info.yaml'): - yData = self.create_yaml(newname, project_pdkdir) - with open(parent_path + '/subcells/' + newname + '/info.yaml', 'w') as ofile: - print('---',file=ofile) - yaml.dump(yData, ofile) + if not os.path.exists(parent_path + '/subcells/' + newname + '/project.json'): + yData = self.create_json(newname, project_pdkdir) + with open(parent_path + '/subcells/' + newname + '/project.json', 'w') as ofile: + json.dump(yData, ofile) self.update_project_views() #---------------------------------------------------------------------- # "Import As" a dir in import/ as a project. based on renameproject(). @@ -4446,16 +4420,17 @@ # it. # NOTE: project.json is the preferred name for the datasheet # file. However, the .spi file, .delib file, etc., all have the name of the - # project from "project_name" in the info.yaml file, which is separate from the datasheet. + # project from "project_name" in the project.json file, which is separate from + # the datasheet. found = False ppath = selection['values'][0] - yamlname = ppath + '/info.yaml' + jsonname = ppath + '/project.json' - if os.path.isfile(yamlname): + if os.path.isfile(jsonname): # Pull the project_name into local store - with open(yamlname, 'r') as f: - datatop = yaml.safe_load(f) + with open(jsonname, 'r') as f: + datatop = json.safe_load(f) project_data = datatop['project'] ipname = project_data['project_name'] self.project_name = ipname @@ -4468,18 +4443,6 @@ datatop = json.load(f) dsheet = datatop['data-sheet'] found = True - # Do not specifically prohibit opening the characterization app if - # there is no schematic or netlist. Otherwise the user is prevented - # even from seeing the electrical parameters. Let the characterization - # tool allow or prohibit simulation based on this. - # if os.path.exists(ppath + '/spi'): - # if os.path.isfile(ppath + '/spi/' + ipname + '.spi'): - # found = True - # - # if found == False and os.path.exists(ppath + '/elec'): - # if os.path.isdir(ppath + '/elec/' + ipname + '.delib'): - # if os.path.isfile(ppath + '/elec/' + ipname + '.delib/' + ipname + '.sch'): - # found = True else: # Use 'pname' as the default project name. print('No characterization file ' + jsonname)
diff --git a/sky130/openlane/config.tcl b/sky130/openlane/config.tcl index 35f63f4..126e8aa 100755 --- a/sky130/openlane/config.tcl +++ b/sky130/openlane/config.tcl
@@ -32,7 +32,7 @@ " # Optimization library -set ::env(TECH_LEF_OPT) "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/techLEF/$::env(STD_CELL_LIBRARY_OPT)/$::env(STD_CELL_LIBRARY_OPT)__max.tlef" +set ::env(TECH_LEF_OPT) "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/techLEF/$::env(STD_CELL_LIBRARY_OPT)/$::env(STD_CELL_LIBRARY_OPT)__nom.tlef" set ::env(CELLS_LEF_OPT) [glob "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/lef/$::env(STD_CELL_LIBRARY_OPT)/*.lef"] set ::env(GDS_FILES_OPT) [glob "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/gds/$::env(STD_CELL_LIBRARY_OPT)/*.gds"] set ::env(STD_CELL_LIBRARY_OPT_CDL) "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/cdl/$::env(STD_CELL_LIBRARY_OPT)/$::env(STD_CELL_LIBRARY_OPT).cdl" @@ -56,7 +56,7 @@ " # Optimization library -set ::env(TECH_LEF_OPT) "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY_OPT)/techlef/$::env(STD_CELL_LIBRARY_OPT)__max.tlef" +set ::env(TECH_LEF_OPT) "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY_OPT)/techlef/$::env(STD_CELL_LIBRARY_OPT)__nom.tlef" set ::env(CELLS_LEF_OPT) [glob "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY_OPT)/lef/*.lef"] set ::env(GDS_FILES_OPT) [glob "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY_OPT)/gds/*.gds"] set ::env(STD_CELL_LIBRARY_OPT_CDL) "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY_OPT)/cdl/$::env(STD_CELL_LIBRARY_OPT).cdl"
diff --git a/sky130/sky130.json b/sky130/sky130.json index a399110..97a74ad 100644 --- a/sky130/sky130.json +++ b/sky130/sky130.json
@@ -83,7 +83,7 @@ "build": { "open_pdks": "OPEN_PDKS_VERSION", "magic": "MAGIC_VERSION" - } + }, "commit": { "open_pdks": "OPEN_PDKS_COMMIT", "magic": "MAGIC_COMMIT"