Controlling order of Path on Startup and Changing Technologies

edited December 2020 in Ruby Scripting

I have a nagging problem that I'd like to finally fix. For reference, I have multiple technologies that I switch between (different process PDKs with different library elements, layers, PCells, etc.) and there is some common naming of scripts and Pcells between them. When I load a GDS layout from a few years ago, the Pcells do not automatically populate and I have to run my "Load Pcells" script. If I look at the cells prior to running the script, an error indicates KLayout is looking at the wrong technology library for the Pcell script. Debugging, the PATH variable that I'm adding to in order to include my macros indicates the order of the scripts that are autorun during startup. This shows that more recently created technologies' scripts are run last. It looks like the PCell macro attempts to use the most recent PATH directory (pointing to a different technology) and it cannot find anything so the cell is left empty until I explicitly run my "Load Pcell" script which puts that directory on top of the PATH.

I'm simply adding to the $LOAD_PATH with the following line at the beginning of my "Load Pcell" script. I do this as my technologies and scripts are not located in the default location.
$:.unshift File.dirname($0)

So I have 2 questions:
1. Is there a way to set the order that scripts are run when a file is opened or the application runs? In this way, I could set my "default" technology to take priority over my lesser used ones.
2. Is there a way to trigger a script that runs when the technology button is clicked? In that way, I could make it so that when I switch technologies, I can run scripts to load Pcells and libraries for that technology only.

I know someone is going to tell me to use unique names for everything, but that is only a solution moving forward. If I want to pull up old layouts and play with the Pcells, I need to keep the names as they are. This is also part of a bigger desire to make it so that technologies only load the compatible libraries instead of every Pcell and static library that I have ever worked on.

Comments

  • I have a similar situation. I have many scripts that execute at startup, and many technologies to manage. I found it to be best to execute a code when a GDS is loaded. That way, I switch the technology right after the file is opened. I have a csv file that lists the technology names and what the GDS filename must contain in order to be recognized. This also lets you set the order of things executed. Not sure if you can just add your load_pcells script to that and be fine ? The caveat is that the tech selector GUI widget doesnt also switch. It will remain on the last used one, even though the system behind it has switched.

    I think there are some entries in the trc file that may determine the order of scripts but I am not sure.

        module FUNCTIONS
    
                app = Application.instance
    
    
                class Tech_Selector
                        def initialize(app)
                                @app = app
                                @mw = app.main_window
                        end  
    
                        def setTech(v, fn)
                                if fn!=nil
                                        title = v.title
                                        thisFile=File.open(fn, "r")
                                        thisFile.each_line do |line|
                                                if !line.start_with?("#")
                                                        line_parts = line.split(',')
                                                        if line_parts.length()>1
                                                                tech_name = line_parts[0].strip
                                                                tech_regex = line_parts[1].strip
                                                                regexp = Regexp.new(tech_regex)
                                                                if title =~ regexp
                                                                        puts "FOUND = " + tech_name
                                                                        cell_view = v.active_cellview
                                                                        cell_view.technology = tech_name
                                                                        break                                            
                                                                end
                                                        end
                                                end
                                      end
                                      thisFile.close
                                end
                        end # setTech                
                end #class
    
    
                mw = app.main_window
                mw.on_view_created do
                                fn =File.dirname(File.dirname(__FILE__)) + "/macros/TECHs.csv"
                                thisClassInstance = Tech_Selector.new(app)
                                thisClassInstance.setTech(mw.current_view, fn)
                end #mw.on_view_created
    
        end #module
    
  • Thomas,

    Appreciate some guidance on how you chose to solve this problem. I took this idea and went a slightly different direction. I didn't want to have a CSV file since I wanted to create something was a little more foolproof since I am developing these scripts assuming others will use them and I want things as clear as possible.

    Instead of reading the filename, I just decided to have a Dialog box pop-up whenever a file was loaded (based on mw.on_view_created). The dialog allows the user to select the appropriate technology from a drop-down. This allowed me to not only change the technology as you have demonstrated (cell_view.technology = tech_name), but also I can load my load_pcells and load_static_libraries scripts associated with each technology. This has a secondary benefit of cleaning up my library list when adding instances.

    Below is the code for the Qt Dialog. It's not the prettiest thing, but it's at least easy to use and easy to train others. I have another macro that autoruns and looks for the on_view_created() event from the main window, similar to your script above. When it triggers, it creates the dialog.

    class SelectTechDialog < RBA::QDialog
    
        include RBA
    
        def initialize(parent = nil)
          super
    
          setWindowTitle("Change Tech Library")
    
          resize(400, 120)
    
          layout = QVBoxLayout.new(self)
    
          #@image = QLabel.new("image", self)
          label = QLabel.new("Select the Technology that Matches the File", self)
          layout.addWidget(label)
    
          tech_dropdown = QComboBox.new(self)
          tech_dropdown.insertItems(0, ["Tech 1", "Tech 2", "Tech 3", "Tech 4"])
          layout.addWidget(tech_dropdown)
    
          hlayout = QHBoxLayout.new(self)
    
          apply_button = QPushButton.new('Apply', self)
          apply_button.setFont(QFont.new('Times', 18, QFont::Bold))
          hlayout.addWidget(apply_button)
    
          exit_button = QPushButton.new('Exit', self)
          exit_button.setFont(QFont.new('Times', 18, QFont::Bold))
          hlayout.addWidget(exit_button)
    
          layout.addLayout(hlayout)
    
          setLayout(layout)      
    
          @view = Application.instance.main_window.current_view.active_cellview
    
          apply_button.clicked do
            if @view
              #puts tech_dropdown.currentText
              load_technology(tech_dropdown.currentText)
            else
              label.setText("Error - no layout open")
            end
          end # apply button clicked
    
           exit_button.clicked do
             done(1)
          end # exit button click
    
        end #initialize
    
    
        def load_technology(tech_string)
    
           @view.technology = tech_string
           @tech_obj = Technology.technology_by_name(tech_string)
           tech_macros_dir = @tech_obj.base_path + "/macros"
    
          # clunky, but works so far - since tech files are in their own dirs, I need to add them to $LOAD_PATH to find the scripts I will load
           if $:.include?(tech_macros_dir)
            $:.delete(tech_macros_dir)
           end
           $:.unshift tech_macros_dir
           #puts $:
    
          case tech_string
            when "Tech 1"
              ### load scripts for Tech 1
              when "Tech 2"
              ### Load scripts for Tech 2
              else
                puts "Error - %s, tech_string is not a valid option" % tech_string
          end #case
    
        end #load_technology
    
      end # class
    
  • I'm a bit scared about what I read here ...

    Just to understand: we're probably talking about identical library names, right?

    There are basically two kind of name clashes:
    1. Identical class, function, variable or method names
    2. Identical library names

    For the first kind, I don't see why this can't be fixed while maintaining the backward compatibility. I should be possible to just put each piece of code into it's own Ruby module and that's it. In Python there isn't any other way anyway. When you keep the library and PCell names, there will still be backward compatibility with existing layouts.

    For the second kind I understand there is an issue here. But maybe there is half of a solution available already. Maybe it just needs to be made fully functional.

    Here is the idea: a library is associated with one or many technologies. This is already in place (use "add_technology" before registering the library), but the effect is only that such a library is not visible, except when a technology is requested which the library is associated with. Still the library names need to be unique. But I think I can drop this constraint: if the technology acts as an additional key, libraries could share names, provided they can still be distinguished by the technology they are registered for.

    Would this solve the issues you both have?

    A cruel, but effective solution within the current boundaries is to create multiple configuration spaces. I.e. by setting "$KLAYOUT_PATH" before running KLayout and pointing it to a specific environment containing one technology only. This takes away your freedom of loading files from different technologies, but if you work in a project with a specific PDK I guess you're locked to that one anyway.

    And finally, there is a discussion related to this is about the strategy of PCell layout update. There is the early and late approach. While I currently update immediately on load, there are requests to do so only when required - i.e. when the PCell parameters change. Or on explicit request. At least the late approach would reduce the risk of spoiling a layout through executing the wrong PCell code.

    I'd like to learn what's your opinion about this.

    Thanks and kind regards,

    Matthias

  • edited December 2020

    Matthias,

    First off, I am talking specifically about having PCells (classes, not their libraries) with the same names in different directories/technologies. For instance, I have a pcell under Tech A that is called 'Pcell_GenericNameForItem' and a similarly named Pcell under Tech B. They have different instantiations, using different layers and PDK elements per their respective technologies. Thus, when I load a file that was designed under Tech A, but Tech B's load_pcell script is run last on startup, the result is that those cells are created improperly. However, I can fix this by running my load_techA_pcells script manually. This is not horrible, but annoying. If I could just select the right technology to give priority prior to the layout being drawn, that might be sufficient. Alternatively, I just bite the bullet and rename my pcells to have unique names between technologies and lose the ability to modify some older designs (sigh).

    Another associated problem is the library list. I have >3 PDKs and growing that I am working with, meaning my libraries list (4-5 static and pcell libraries for each technology) is long to parse through. I do just fine, but we are doing some designs with multiple contributors and mistakes are more likely with some of the libraries being similar. A slimmed down list with just the relevant libraries for a given technology would be preferred.

    To note, I realized after posting my script for a dialog box above that this doesn't quite work. I was assuming I could tell my 'load' scripts to not run on startup and rely on my tech selection tool to pick the right scripts to run. Unfortunately, the layout is already loaded at that point and all of my Pcells become static, which would happen every time you close the window and restart. So at the moment, I just have an ugly dialog box to perform the annoying solution of manually running my load scripts. Kudos to me.

    I don't know if it is ideal, but requiring the selection of a technology prior to startup, and then creating a separate environment for that window that points to a given technology might work. If I want to try that, can I startup KLayout from a bash script in Windows and set the $KLAYOUT_PATH? I can think of instances where this might not work very well, but I might give it a whirl to explore the pros/cons. I do like the technologies approach and fear problems with this method especially when setting up new users, or god-forbid setting up a new machine.

    Finally, on the last point of early/late Pcell update: how would this work on startup? Would no Pcells display until a technology is selected and then refreshed? I like the way KLayout works now, so it's hard to suggest changing something so substantial that could be clunky for basic users even if it fixes a need for a couple of users in one corner of existence. Especially when the easy fix to my problem is effectively not using identical names going forward.

    A question: is there a way to set the order that the startup scripts are run? It appears when I close KLayout that the last technology is used on next startup as the default. And there is a base_path associated with that technology with its respective macros and pymacros. Can we prioritize that directory's scripts to run last so any Pcells and libraries that have redundant names are called last? If you switch technologies, you would still have to rerun the startup scripts manually, but at least if you are working on a primary design, every time you restart, the last technology is prioritized. That would solve the problem of identical pcell names; I can live with my long list of libraries ;)

    (PS I'm on version 0.26.5 if anything has changed or will change soon with 0.27)

    Brian

  • Hi Brian,

    at least on Linux, the startup scripts should be run in a deterministic way (alphabetical I assume). You can use "klayout -d 31 ..." to get verbose logs for finding out the order.

    But if the problem is PCell class names: these you can change without losing the ability to load your layouts. There is no rule saying you need to call the class or module like the PCell.

    This is the relevant part of the PCell sample in Ruby:

    module MyLib
    ...
      class Circle ...
    
      class MyLib < RBA::Library
        def initialize  
    ....
          layout.register_pcell("Circle", Circle::new)
          register("MyLib")
        end
      end
    
      MyLib::new
    
    end
    

    The name relevant when it comes to identifying PCells or Libraries in existing layouts are the ones given in "register" and "register_pcell".

    This is perfectly valid

    # The module name makes it unique
    module MyLibForTechA
      class Circle ...
    
      class MyLib < RBA::Library
        def initialize  
          ...
          # "MyLib.Circle" is the name stored in the layout.
          layout.register_pcell("Circle", Circle::new)
          register("MyLib")
    
        end
      end
    
      MyLib::new
    
    end
    

    If you don't use a module you can still make the class names unique without breaking layout compatibility:

    class CircleForTechA ...
    
    class MyLibForTechA < RBA::Library
      def initialize  
        ...
        # "MyLib.Circle" is the name stored in the layout.
        layout.register_pcell("Circle", CircleForTechA::new)
        register("MyLib")
    
      end
    
    end
    
    MyLibForTechA::new
    

    I'd really recommend to make the module names or class names unique. It will not break compatibility with existing layouts and overriding class definitions is discouraged as this may leave you with classes partially filled from different variants of the PCell code and I even don't know if you can actually wipe everything of a class in Python when you overwrite it with a new version.

    There still is the library/PCell name clash happening when you use the same library names in "register", like this:

    module MyLibForTechA
    ...
      class Circle ... 
    
      class MyLib < RBA::Library
        def initialize  
          layout.register_pcell("Circle", Circle::new)
          register("MyLib")
        end
      end
    
      MyLibForTechA::new
    
    end
    
    module MyLibForTechB
    ...
      # this is in fact a different class because it sits in MyLibForTechB
      class Circle ...
    
      class MyLib < RBA::Library
        def initialize  
          layout.register_pcell("Circle", Circle::new)
          register("MyLib")   # There will be two MyLib.Circle incarnations
        end
      end
    
      MyLibForTechB::new
    
    end
    

    Both modules can sit in different files and the order of loading only matters because we call "MyLibForTechX::new" in each module. This will call the constructor is and register the library. As they register under the same name, there is a conflict.

    A solution readily available is to suppress the constructor call (hence "register") conditionally. For example, by using

    module MyLibForTechA
    ...
    
      if $start_with_tech == "TechA"  
        MyLibForTechA::new
      end
    end
    
    module MyLibForTechB
    ...
    
      if $start_with_tech == "TechB"  
        MyLibForTechB::new
      end
    end
    

    Provided you have separated module or class names, you can now select a technology setup when starting KLayout:

    # "-rd start_with_tech=..." sets the global $start_with_tech variable
    klayout -rd start_with_tech=TechA ...
    

    You can also use an environment variable for that purpose (using ENV("...") to read it).

    My proposed solution was dynamic:

    module MyLibForTechA
    ...
      class Circle ... 
    
      class MyLib < RBA::Library
        def initialize  
          layout.register_pcell("Circle", Circle::new)
          register("MyLib", "TechA")    # "MyLib.Circle" for "TechA"
        end
      end
    
      MyLibForTechA::new
    
    end
    
    module MyLibForTechB
    ...
      # this is in fact a different class because it sits in MyLibForTechB
      class Circle ...
    
      class MyLib < RBA::Library
        def initialize  
          layout.register_pcell("Circle", Circle::new)
          register("MyLib", "TechB")    # "MyLib.Circle" for "TechA"
        end
      end
    
      MyLibForTechB::new
    
    end
    

    Hope this helps. But really consider changing the code names of the modules or classes. It won't break layout compatibility and the thought of clashing class definitions gives me the creeps.

    Kind regards,

    Matthias

Sign In or Register to comment.