Creating Forms in SKILL Code: A Practical Guide
Contents:
- Layout Forms
- GUI Template
- Map Callback
- Modify Callback
- Button Callback - Browse
- Button Callback - Run
- Conclusion
- Full code
Why should we create graphical user interfaces (GUIs) for our tools?
GUIs offer a user-friendly and intuitive way to interact with software, making complex tasks more accessible and efficient. They provide visual feedback and structured workflows, reducing errors and enhancing productivity, allowing users to focus on their work rather than memorizing commands. GUIs also simplify training, making it easier for new users to explore features without extensive documentation.
SKILL code offers powerful ways to develop GUIs (or forms) within the Virtuoso environment. Cadence provides comprehensive documentation for all their hi* functions, which handle form development, along with examples of various features:
- Worked example using all available SKILL UI fields (Cadence Support Portal)
- Advanced GUI building using dynamically resizable layout forms (Cadence Support Portal)
In this guide, we'll walk through the GUI development process using a practical example. We'll use layout forms and define several callbacks, with the full code attached at the end.
1. Layout Forms
There are two main approaches for field placement in a GUI: using pixels (hiCreateAppForm()) and using layout forms (hiCreateLayoutForm()). Placing fields using pixels gives you freedom in choosing exact x, y coordinates and a size for your fields. Despite the fact that it’s intuitive, the major problem with this approach is it’s not scalable. The more complex the GUI is, the harder it’s to add a new field to it. Imagine you have to add a new field to a complex GUI such this:
Complex GUI example
In the by-pixel approach, in addition to a new field, you’ll have to change the location of other fields manually. Which will end up in long and tedious work. A more modern and scalable approach is using layout forms. At first, it’s not as intuitive as defining locations by pixels, but after you get hold of it, it turns into a powerful skill in GUI development. Layout forms allow you to build GUIs with container layouts (like Lego). When you have to add a new field, you just add it to the required layout and other fields will move automatically.
So for scalable and robust GUIs it is recommended to use layout forms approach.
Now, let’s get to our practical example.
2. GUI Template
Start by building a GUI template. Our GUI will receive the top cell's library, cell, and view names as input and display library and cell names used in the top cell's hierarchy.
1procedure( createLibrariesCellsInUseForm()
2 let( (libraryLabel libraryStringField cellLabel cellStringField viewLabel
3 viewStringField topGridLayout browseButton horizontalLayout
4 infoReportField getInfoButton verticalLayout)
5
6 libraryLabel = hiCreateLabel(
7 ?name 'libraryLabel
8 ?labelText "Library"
9 ?justification 'right
10 )
11
12 libraryStringField = hiCreateStringField(
13 ?name 'libraryStringField
14 )
15
16 cellLabel = hiCreateLabel(
17 ?name 'cellLabel
18 ?labelText "Cell"
19 ?justification 'right
20 )
21
22 cellStringField = hiCreateStringField(
23 ?name 'cellStringField
24 )
25
26 viewLabel = hiCreateLabel(
27 ?name 'viewLabel
28 ?labelText "View"
29 ?justification 'right
30 )
31
32 viewStringField = hiCreateStringField(
33 ?name 'viewStringField
34 )
35
36 topGridLayout = hiCreateGridLayout(
37 'topGridLayout
38 ?spacing 10
39 ?items list(
40 list(libraryLabel 'row 0 'col 0)
41 list(libraryStringField 'row 0 'col 1)
42 list(cellLabel 'row 1 'col 0)
43 list(cellStringField 'row 1 'col 1)
44 list(viewLabel 'row 2 'col 0)
45 list(viewStringField 'row 2 'col 1)
46 list('col_stretch 0 0)
47 list('col_stretch 1 1)
48 )
49 )
50
51 browseButton = hiCreateButton(
52 ?name 'browseButton
53 ?buttonText "Browse"
54 ?callback ""
55 )
56
57 horizontalLayout = hiCreateHorizontalBoxLayout(
58 'horizontalLayout
59 ?frame "Top Cell Info"
60 ?spacing 10
61 ?items list(
62 list(topGridLayout 'stretch 1)
63 list(browseButton 'stretch 0)
64 )
65 )
66
67 infoReportField = hiCreateReportField(
68 ?name 'infoReportField
69 ?title "Hierarchy Info"
70 ?headers list(
71 list("Library" 100 'left 'string t)
72 list("Cell" 100 'left 'string t)
73 )
74 )
75
76 getInfoButton = hiCreateButton(
77 ?name 'getInfoButton
78 ?buttonText "Get Hierarchy Info"
79 ?callback ""
80 )
81
82 verticalLayout = hiCreateVerticalBoxLayout(
83 'verticalLayout
84 ?items list(horizontalLayout infoReportField getInfoButton)
85 )
86
87 hiCreateLayoutForm(
88 'librariesCellsInUseForm
89 "Libraries Cells in Use"
90 verticalLayout
91 ?buttonLayout 'Empty
92 )
93 );let
94);procedure
Here, we define labels, string fields, buttons and a report field, and use form layouts to organize the fields into sections. To display the GUI in SKILL, create the form and display it with these two commands:
1form = createLibrariesCellsInUseForm()
2hiDisplayForm(form)
Display GUI in SKILL
Tip: If you change anything in the form’s creation function, you have to re-create the form to see the changes. Re-run the two functions above.
3. Map Callback
Map callbacks are used to perform additional setup or customization when a form is instantiated and displayed. They are particularly useful for initializing form fields, setting default values, adjusting field sizes, or performing any other setup tasks that need to occur when the form is first displayed.
Define a map callback for our GUI to change the size of "Browse" and "Get Hierarchy Info" buttons and initialize top cell info fields if a cell view is currently open.
1procedure( librariesCellsInUseFormMapCB(form)
2 ;Map callback function for initializing the Libraries Cells In Use
3 ;form fields.
4
5 ;@param form formObject
6 ;The form object being instantiated.
7
8 let( (cv)
9
10 hiInstantiateForm(form)
11 hiSetFieldMinSize(form 'browseButton ?widgetHeight 25)
12 hiSetFieldMinSize(form 'getInfoButton ?widgetHeight 35)
13
14 cv = geGetEditCellView()
15 when( cv
16 form~>libraryStringField~>value = cv~>libName
17 form~>cellStringField~>value = cv~>cellName
18 form~>viewStringField~>value = cv~>viewName
19 );when
20 );let
21);procedure
Here, we must instantiate the form before modifying its fields’ sizes. Additionally, we assign values to our string fields. Set the function as a map callback by adding the ?mapCB keyword to the hiCreateLayoutForm() function.
1hiCreateLayoutForm(
2 'librariesCellsInUseForm
3 "Libraries Cells in Use"
4 verticalLayout
5 ?buttonLayout 'Empty
6 ?mapCB 'librariesCellsInUseFormMapCB
7 )
Note: You can define field callbacks either as a symbol, like 'myFunc, or as a string, such as "myFunc()". Using a symbol automatically passes default arguments to the function based on the field, with each field's callback having its own set of default arguments. In case of the ?mapCB, it sends the current form object as an argument. Setting callbacks as strings is useful when you need to pass different arguments to a function.
Callback example
4. Modify Callback
A modify callback in SKILL is a function that is triggered whenever the user changes the value of a string field, such as by typing a new value. This callback is executed immediately when a change is detected in the field, but before the change is displayed.
The modify callback function can return one of three values:
- t: If the function returns t, the changes made to the field are allowed and displayed as they are entered.
- nil: If the function returns nil, the changes are not allowed, and the original value of the field is retained.
- value: If the function returns a string, this value replaces the current value of the field.
This mechanism provides a powerful way to validate input, update other fields, or trigger additional logic based on the new value, enabling dynamic and responsive interactions within SKILL forms.
Add a modify callback to the library string field to validate the entered name and highlight the field if incorrect.
1procedure( libraryNameModifyCB(field scope latestTextValue sourceOfChange)
2 ;Modify callback function for validating and highlighting the library
3 ;name field.
4 ;
5 ;@param field formField
6 ; The form field being modified.
7 ;@param scope formObject
8 ; The form object contains the field.
9 ;@param latestTextValue string
10 ; The latest text value entered in the field.
11 ;@param sourceOfChange any
12 ; The source of the change, indicating whether the modification
13 ; was user-initiated.
14 ;@return t boolean
15 ; Returns t to allow the changes made to the field.
16
17 let( (libraryObject)
18 when( sourceOfChange
19 libraryObject = ddGetObj(latestTextValue)
20 if( !libraryObject
21 then
22 hiHighlightField(scope field~>hiFieldSym 'error)
23 else
24 hiHighlightField(scope field~>hiFieldSym 'background)
25 );if
26 );when
27
28 t
29 );let
30);procedure
Here, we attempt to retrieve a library object using its name and adjust the string field's highlight based on the result. Set the function as a modify callback by adding the ?modifyCallback keyword to the library's hiCreateStringField() function.
1libraryStringField = hiCreateStringField(
2 ?name 'libraryStringField
3 ?modifyCallback 'libraryNameModifyCB
4 )
Note: To practice, you can add modify callbacks to the cell and view string fields.
5. Button Callback - Browse
Instead of manually entering library/cell/view names, we can define a callback to invoke the Library Manager for selection.
1procedure( browseLibraryCellViewCB(form)
2 ;Callback function for synchronizing library, cell, and view fields with
3 ;the form.
4
5 ;@param form formObject
6 ;The form object containing the fields to be synchronized.
7 ddsSyncWithForm(form 'browse 'libraryStringField 'cellStringField
8 'viewStringField)
9);procedure
Here, we use the built-in ddsSyncWithForm() function, which requires a form, an action to perform, and fields’ symbols to update when selection is done.
Add this function to the browse button as a callback. Use the string approach this time, as we want to send only the form as an argument.
1browseButton = hiCreateButton(
2 ?name 'browseButton
3 ?buttonText "Browse"
4 ?callback "browseLibraryCellViewCB(hiGetCurrentForm())"
5 )
6. Button Callback - Run
The run button executes the main algorithm. Here, we’ll use the getLibrariesCellsUsedIn() function, described in the “Extracting Library and Cell Names from a Top Cell Hierarchy Using SKILL Code” guide.
Define a function to run the algorithm, get results, and display them in a table.
1procedure( getUsedLibrariesCellsCB(form)
2 ;Callback function to extract and display used libraries and cells from a
3 ;specified cell view.
4 ;
5 ;@param form formObject
6 ;The form object containing the input fields and report field.
7 prog( (libName cellName viewName cellView usedLibrariesCellsTable choices)
8
9 ; Get input
10 libName = form~>libraryStringField~>value
11 cellName = form~>cellStringField~>value
12 viewName = form~>viewStringField~>value
13
14 ; Check input
15 unless( checkInput(libName cellName viewName)
16 return()
17 );unless
18
19 ; Pre-process input
20 cellView = dbOpenCellViewByType(libName cellName viewName)
21
22 ; Run the libraries and cells extraction function
23 usedLibrariesCellsTable = getLibrariesCellsUsedIn(cellView)
24
25 ; Post-process output
26 foreach( library usedLibrariesCellsTable
27 foreach( cell usedLibrariesCellsTable[library]
28 choices = cons(list(library cell) choices)
29 );foreach
30 );foreach
31
32 ; Show output in a table
33 form~>infoReportField~>choices = choices
34
35 return(t)
36 );prog
37);procedure
Here, first of all, we check the input, and if it’s incorrect we exit the function in an early stage by return(). To use the return() function to exit your function at a desired stage, you’ll have to wrap your code in the prog() scope, instead of the let(). Next, we get the cell view object, using the provided library/cell/view names.
After this, we use the cell view object to get libraries and cells names that are in use in this cell view’s hierarchy. When we get the result, we need to process it to be able to add to the report field, our results table. And finally, we add the processed results to the report field.
Add this function to the run button as a callback.
1getInfoButton = hiCreateButton(
2 ?name 'getInfoButton
3 ?buttonText "Get Hierarchy Info"
4 ?callback "getUsedLibrariesCellsCB(hiGetCurrentForm())"
5 )
Now, when you provide correct library/cell/view names and click the run button, you'll see the extracted results in the report field.
7. Conclusion
In this guide, we've explored how to use layout forms, define various callbacks, highlight string fields, display error messages, invoke the Library Manager, and utilize let() and prog() scopes. To further explore, check out Cadence guides on creating forms and experiment with different fields and parameters.
8. Full code
1procedure( createLibrariesCellsInUseForm()
2
3 ;Creates a form for displaying and interacting with library, cell, and
4 ;view information.
5
6 ;@return formObject
7 ; The created form object for libraries and cells in use.
8
9 let( (libraryLabel libraryStringField cellLabel cellStringField viewLabel
10 viewStringField topGridLayout browseButton horizontalLayout
11 infoReportField getInfoButton verticalLayout)
12
13 libraryLabel = hiCreateLabel(
14 ?name 'libraryLabel
15 ?labelText "Library"
16 ?justification 'right
17 )
18
19 libraryStringField = hiCreateStringField(
20 ?name 'libraryStringField
21 ?modifyCallback 'libraryNameModifyCB
22 )
23
24 cellLabel = hiCreateLabel(
25 ?name 'cellLabel
26 ?labelText "Cell"
27 ?justification 'right
28 )
29
30 cellStringField = hiCreateStringField(
31 ?name 'cellStringField
32 )
33
34 viewLabel = hiCreateLabel(
35 ?name 'viewLabel
36 ?labelText "View"
37 ?justification 'right
38 )
39
40 viewStringField = hiCreateStringField(
41 ?name 'viewStringField
42 )
43
44 topGridLayout = hiCreateGridLayout(
45 'topGridLayout
46 ?spacing 10
47 ?items list(
48 list(libraryLabel 'row 0 'col 0)
49 list(libraryStringField 'row 0 'col 1)
50 list(cellLabel 'row 1 'col 0)
51 list(cellStringField 'row 1 'col 1)
52 list(viewLabel 'row 2 'col 0)
53 list(viewStringField 'row 2 'col 1)
54 list('col_stretch 0 0)
55 list('col_stretch 1 1)
56 )
57 )
58
59 browseButton = hiCreateButton(
60 ?name 'browseButton
61 ?buttonText "Browse"
62 ?callback "browseLibraryCellViewCB(hiGetCurrentForm())"
63 )
64
65 horizontalLayout = hiCreateHorizontalBoxLayout(
66 'horizontalLayout
67 ?frame "Top Cell Info"
68 ?spacing 10
69 ?items list(
70 list(topGridLayout 'stretch 1)
71 list(browseButton 'stretch 0)
72 )
73 )
74
75 infoReportField = hiCreateReportField(
76 ?name 'infoReportField
77 ?title "Hierarchy Info"
78 ?headers list(
79 list("Library" 100 'left 'string t)
80 list("Cell" 100 'left 'string t)
81 )
82 )
83
84 getInfoButton = hiCreateButton(
85 ?name 'getInfoButton
86 ?buttonText "Get Hierarchy Info"
87 ?callback "getUsedLibrariesCellsCB(hiGetCurrentForm())"
88 )
89
90 verticalLayout = hiCreateVerticalBoxLayout(
91 'verticalLayout
92 ?items list(horizontalLayout infoReportField getInfoButton)
93 )
94
95 hiCreateLayoutForm(
96 'librariesCellsInUseForm
97 "Libraries Cells in Use"
98 verticalLayout
99 ?buttonLayout 'Empty
100 ?mapCB 'librariesCellsInUseFormMapCB
101 )
102 );let
103);procedure
104
105
106procedure( librariesCellsInUseFormMapCB(form)
107
108 ;Map callback function for initializing the Libraries Cells In Use
109 ;form fields.
110
111 ;@param form formObject
112 ; The form object being instantiated.
113
114 let( (cellView)
115
116 hiInstantiateForm(form)
117 hiSetFieldMinSize(form 'browseButton ?widgetHeight 25)
118 hiSetFieldMinSize(form 'getInfoButton ?widgetHeight 35)
119
120 cellView = geGetEditCellView()
121 when( cellView
122 form~>libraryStringField~>value = cellView~>libName
123 form~>cellStringField~>value = cellView~>cellName
124 form~>viewStringField~>value = cellView~>viewName
125 );when
126 );let
127);procedure
128
129
130procedure( libraryNameModifyCB(field scope latestTextValue sourceOfChange)
131
132 ;Modify callback function for validating and highlighting the library
133 ;name field.
134
135 ;@param field formField
136 ; The form field being modified.
137 ;@param scope formObject
138 ; The form object containing the field.
139 ;@param latestTextValue string
140 ; The latest text value entered in the field.
141 ;@param sourceOfChange any
142 ; The source of the change, indicating whether the modification
143 ; was user-initiated.
144 ;@return t boolean
145 ; Returns t to allow the changes made to the field.
146
147 let( (libraryObject)
148 when( sourceOfChange
149 libraryObject = ddGetObj(latestTextValue)
150 if( !libraryObject
151 then
152 hiHighlightField(scope field~>hiFieldSym 'error)
153 else
154 hiHighlightField(scope field~>hiFieldSym 'background)
155 );if
156 );when
157
158 t
159 );let
160);procedure
161
162
163procedure( browseLibraryCellViewCB(form)
164
165 ;Callback function for synchronizing library, cell, and view fields with
166 ;the form.
167
168 ;@param form formObject
169 ; The form object containing the fields to be synchronized.
170
171 ddsSyncWithForm(form 'browse 'libraryStringField 'cellStringField
172 'viewStringField)
173);procedure
174
175
176procedure( getUsedLibrariesCellsCB(form)
177
178 ;Callback function to extract and display used libraries and cells from a
179 ;specified cell view.
180
181 ;@param form formObject
182 ; The form object containing the input fields and report field.
183
184 prog( (libName cellName viewName cellView usedLibrariesCellsTable choices)
185
186 ; Get input
187 libName = form~>libraryStringField~>value
188 cellName = form~>cellStringField~>value
189 viewName = form~>viewStringField~>value
190
191 ; Check input
192 unless( checkInput(libName cellName viewName)
193 return()
194 );unless
195
196 ; Pre-process input
197 cellView = dbOpenCellViewByType(libName cellName viewName)
198
199 ; Run the libraries and cells extraction function
200 usedLibrariesCellsTable = getLibrariesCellsUsedIn(cellView)
201
202 ; Post-process output
203 foreach( library usedLibrariesCellsTable
204 foreach( cell usedLibrariesCellsTable[library]
205 choices = cons(list(library cell) choices)
206 );foreach
207 );foreach
208
209 ; Show output in a table
210 form~>infoReportField~>choices = choices
211
212 return(t)
213 );prog
214);procedure
215
216
217procedure( checkInput(libName cellName viewName)
218
219 ;Validates the existence of a specified library, cell, and view
220 ;combination.
221
222 ;@param libName string
223 ; The name of the library to check.
224 ;@param cellName string
225 ; The name of the cell to check.
226 ;@param viewName string
227 ; The name of the view to check.
228 ;@return boolean
229 ; Returns t if the library, cell, and view exist; otherwise,
230 ; displays an error dialog and returns nil.
231
232 prog( (viewObject)
233
234 viewObject = ddGetObj(libName cellName viewName)
235 unless( viewObject
236 hiDisplayAppDBox(
237 ?name 'errorAppDBox
238 ?dboxBanner "*ERROR* Libraries Cells in Use"
239 ?dboxText "Selected library, cell or view don't exist!"
240 ?dialogType hicErrorDialog
241 ?buttonLayout 'Close
242 )
243
244 return()
245 );unless
246
247 return(t)
248 );prog
249);procedure
250
251
252procedure( getLibrariesCellsUsedIn(cellView
253 @optional (usedLibrariesCellsTable nil))
254
255 ;Retrieves all libraries and cells used in the hierarchy of a given
256 ;cell view.
257
258 ;@param cellView dbObject
259 ;The cell view object from which to retrieve the hierarchy.
260
261 ;@param usedLibrariesCellsTable table
262 ;Optional. A table to keep track of used libraries and cells.
263 ;If not provided, a new table is created.
264
265 ;@return table
266 ;A table containing libraries as keys and tables of cell names as
267 ;values, representing the hierarchy.
268
269 let( (cellTable libName cellName cellObject message viewName nextCellView)
270
271 ; First initialization
272 unless( usedLibrariesCellsTable
273 usedLibrariesCellsTable = makeTable('usedLibrariesCellsTable nil)
274 cellTable = makeTable('cellTable nil)
275 cellTable[cellView~>cellName] = t
276 usedLibrariesCellsTable[cellView~>libName] = cellTable
277 );unless
278
279 foreach( instance cellView~>instHeaders
280 libName = instance~>libName
281 cellName = instance~>cellName
282 cellObject = ddGetObj(libName cellName)
283
284 if( !cellObject
285 then
286 message = strcat("[getLibrariesCellsUsedIn] " libName "/"
287 cellName " cell doesn't exist in your Library Manager")
288 warn(message)
289 else
290 ; Creates cells' table for a library
291 unless( usedLibrariesCellsTable[libName]
292 cellTable = makeTable('cellTable nil)
293 usedLibrariesCellsTable[libName] = cellTable
294 );unless
295
296 unless( usedLibrariesCellsTable[libName][cellName]
297 ; This cell is not in table yet
298 usedLibrariesCellsTable[libName][cellName] = t
299
300 ; Gets instance's cell view
301 viewName = mapViewName(instance~>viewName)
302 nextCellView = dbOpenCellViewByType(libName cellName viewName)
303 when( nextCellView
304 usedLibrariesCellsTable = getLibrariesCellsUsedIn(nextCellView
305 usedLibrariesCellsTable)
306 );when
307 );unless
308 );if
309 );foreach
310
311 usedLibrariesCellsTable
312 );let
313);procedure
314
315
316procedure( mapViewName(viewName)
317 ; Maps a view name to a common view name that includes 'instances'
318 ;for hierarchy traversal.
319
320 ;@param viewName string
321 ;The name of the view to be mapped.
322
323 ;@return string
324 ;The mapped view name.
325
326
327 if( viewName == "symbol" ; Symbol's cell view doesn't have ~>instances
328 then
329 "schematic"
330 else
331 viewName
332 );if
333);procedure
Author: Eugeny Khanchin