2019-11-15 16:26:04 +01:00
|
|
|
#------------------------------------------------------
|
|
|
|
|
# Script for generating the "DRC manager" window.
|
|
|
|
|
#
|
|
|
|
|
# Written by Tim Edwards, November 2019
|
|
|
|
|
#------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
global Opts
|
|
|
|
|
|
|
|
|
|
if {$::tk_version >= 8.5} {
|
|
|
|
|
|
|
|
|
|
set Opts(drcmgr) 0
|
|
|
|
|
|
2025-10-08 23:11:27 +02:00
|
|
|
magic::tag add addpath "magic::drcmanager"
|
|
|
|
|
magic::tag add path "magic::drcmanager"
|
2019-11-15 16:26:04 +01:00
|
|
|
|
|
|
|
|
# Callback to the DRC manager
|
|
|
|
|
|
|
|
|
|
proc magic::drccallback {command} {
|
|
|
|
|
global Opts
|
|
|
|
|
|
|
|
|
|
set fid [.drcmgr.box.view selection]
|
|
|
|
|
if {[.drcmgr.box.view parent $fid] == {}} {
|
|
|
|
|
set value {}
|
|
|
|
|
} else {
|
|
|
|
|
set value [.drcmgr.box.view item $fid -text]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if { $Opts(target) == "default" } {
|
|
|
|
|
set winlist [magic::windownames layout]
|
|
|
|
|
set winname [lindex $winlist 0]
|
|
|
|
|
} else {
|
|
|
|
|
set winname $Opts(target)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch $command {
|
|
|
|
|
update {
|
|
|
|
|
magic::drcmanager update
|
|
|
|
|
}
|
|
|
|
|
last {
|
|
|
|
|
.drcmgr.box.view selection set [.drcmgr.box.view prev $fid]
|
|
|
|
|
magic::drccallback zoom
|
|
|
|
|
}
|
|
|
|
|
next {
|
|
|
|
|
.drcmgr.box.view selection set [.drcmgr.box.view next $fid]
|
|
|
|
|
magic::drccallback zoom
|
|
|
|
|
}
|
|
|
|
|
zoom {
|
|
|
|
|
if {$value != {}} {
|
|
|
|
|
set snaptype [snap]
|
|
|
|
|
snap internal
|
|
|
|
|
box values {*}$value
|
|
|
|
|
magic::suspendall
|
|
|
|
|
magic::findbox zoom
|
|
|
|
|
magic::zoom 2
|
|
|
|
|
magic::resumeall
|
|
|
|
|
snap $snaptype
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#----------------------------------------------
|
|
|
|
|
# Create the DRC manager window
|
|
|
|
|
#----------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::makedrcmanager { mgrpath } {
|
|
|
|
|
global filtered
|
|
|
|
|
|
|
|
|
|
set filtered 1
|
|
|
|
|
toplevel ${mgrpath}
|
|
|
|
|
wm withdraw ${mgrpath}
|
|
|
|
|
frame ${mgrpath}.actionbar
|
|
|
|
|
frame ${mgrpath}.box
|
|
|
|
|
frame ${mgrpath}.target
|
|
|
|
|
|
|
|
|
|
ttk::treeview ${mgrpath}.box.view -selectmode browse \
|
|
|
|
|
-yscrollcommand "${mgrpath}.box.vert set" \
|
|
|
|
|
-xscrollcommand "${mgrpath}.box.vert set" \
|
|
|
|
|
-columns 0
|
|
|
|
|
scrollbar ${mgrpath}.box.vert -orient vertical -command "${mgrpath}.box.view yview"
|
|
|
|
|
${mgrpath}.box.view heading #0 -text "DRC Rule"
|
|
|
|
|
${mgrpath}.box.view heading 0 -text "Error Number"
|
|
|
|
|
${mgrpath}.box.view column #0 -stretch true -anchor w -minwidth 350
|
|
|
|
|
${mgrpath}.box.view column 0 -stretch false -anchor center -minwidth 50
|
|
|
|
|
|
|
|
|
|
grid columnconfigure ${mgrpath}.box 0 -weight 1 -minsize 500
|
|
|
|
|
grid columnconfigure ${mgrpath}.box 1 -weight 0
|
|
|
|
|
grid rowconfigure ${mgrpath}.box 0 -weight 1
|
|
|
|
|
grid ${mgrpath}.box.view -row 0 -column 0 -sticky news
|
|
|
|
|
grid ${mgrpath}.box.vert -row 0 -column 1 -sticky news
|
|
|
|
|
|
|
|
|
|
grid rowconfigure ${mgrpath} 0 -weight 0
|
|
|
|
|
grid rowconfigure ${mgrpath} 1 -weight 1
|
|
|
|
|
grid rowconfigure ${mgrpath} 2 -weight 0
|
|
|
|
|
grid columnconfigure ${mgrpath} 0 -weight 1
|
|
|
|
|
grid ${mgrpath}.actionbar -row 0 -column 0 -sticky news
|
|
|
|
|
grid ${mgrpath}.box -row 1 -column 0 -sticky news
|
|
|
|
|
grid ${mgrpath}.target -row 2 -column 0 -sticky news
|
|
|
|
|
|
|
|
|
|
button ${mgrpath}.actionbar.update -text "Update" \
|
|
|
|
|
-command {magic::drccallback update}
|
|
|
|
|
button ${mgrpath}.actionbar.last -text "Last" -command {magic::drccallback last}
|
|
|
|
|
button ${mgrpath}.actionbar.next -text "Next" -command {magic::drccallback next}
|
2020-02-17 16:25:45 +01:00
|
|
|
button ${mgrpath}.actionbar.save -text "Save" -command {magic::drc_save_report}
|
2019-11-15 16:26:04 +01:00
|
|
|
button ${mgrpath}.actionbar.zoom -text "Zoom" -command {magic::drccallback zoom}
|
|
|
|
|
|
|
|
|
|
pack ${mgrpath}.actionbar.update -side left
|
|
|
|
|
pack ${mgrpath}.actionbar.last -side left
|
|
|
|
|
pack ${mgrpath}.actionbar.next -side left
|
2020-02-17 16:25:45 +01:00
|
|
|
pack ${mgrpath}.actionbar.save -side left
|
2019-11-15 16:26:04 +01:00
|
|
|
pack ${mgrpath}.actionbar.zoom -side right
|
|
|
|
|
|
|
|
|
|
label ${mgrpath}.target.name -text "Target window:"
|
|
|
|
|
menubutton ${mgrpath}.target.list -text "default" \
|
|
|
|
|
-menu ${mgrpath}.target.list.winmenu
|
|
|
|
|
|
|
|
|
|
pack ${mgrpath}.target.name -side left -padx 2
|
|
|
|
|
pack ${mgrpath}.target.list -side left
|
|
|
|
|
|
|
|
|
|
#Withdraw the window when the close button is pressed
|
|
|
|
|
wm protocol ${mgrpath} WM_DELETE_WINDOW "set Opts(drcmgr) 0 ; \
|
|
|
|
|
wm withdraw ${mgrpath}"
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------
|
|
|
|
|
# Callback when a treeview item is opened
|
|
|
|
|
#-------------------------------------------------
|
|
|
|
|
|
|
|
|
|
bind .drcmgr <<TreeviewOpen>> {
|
|
|
|
|
set s [.drcmgr.box.view selection]
|
|
|
|
|
foreach i [.drcmgr.box.view children $s] {
|
|
|
|
|
# NOTE: not hierarchical
|
|
|
|
|
.drcmgr.box.view item $i -open false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bind .drcmgr <<TreeviewClose>> {
|
|
|
|
|
set s [.drcmgr.box.view selection]
|
|
|
|
|
foreach i [.drcmgr.box.view children $s] {
|
|
|
|
|
foreach j [.drcmgr.box.view children $i] {
|
|
|
|
|
.drcmgr.box.view delete $j
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
proc magic::adddrcentry {key valuelist} {
|
|
|
|
|
set id [.drcmgr.box.view insert {} end -text ${key}]
|
|
|
|
|
set i 0
|
|
|
|
|
foreach value $valuelist {
|
|
|
|
|
.drcmgr.box.view insert $id end -text "$value"
|
|
|
|
|
incr i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
# The cell manager window main callback function
|
|
|
|
|
#--------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::drcmanager {{option "update"}} {
|
|
|
|
|
global editstack
|
|
|
|
|
global CAD_ROOT
|
|
|
|
|
|
|
|
|
|
# Check for existence of the manager widget
|
|
|
|
|
if {[catch {wm state .drcmgr}]} {
|
|
|
|
|
if {$option == "create"} {
|
|
|
|
|
magic::makedrcmanager .drcmgr
|
|
|
|
|
} else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
} elseif { $option == "create"} {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
magic::suspendall
|
|
|
|
|
|
|
|
|
|
# Get existing list of error classes and remove them
|
|
|
|
|
set currules [.drcmgr.box.view children {}]
|
|
|
|
|
foreach rule $currules {
|
|
|
|
|
.drcmgr.box.view delete ${rule}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Run DRC
|
|
|
|
|
select top cell
|
|
|
|
|
set drcdict [dict create {*}[drc listall why]]
|
|
|
|
|
|
|
|
|
|
# set first true
|
|
|
|
|
dict for {key value} $drcdict {
|
|
|
|
|
magic::adddrcentry ${key} ${value}
|
|
|
|
|
# .drcmgr.box.view item ${key} -open $first
|
|
|
|
|
# set first false
|
|
|
|
|
}
|
|
|
|
|
magic::resumeall
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} ;# (if Tk version 8.5)
|
|
|
|
|
|
2020-02-17 16:25:45 +01:00
|
|
|
#---------------------------------------------------
|
|
|
|
|
# Alternative way to view/save DRC errors in magic.
|
|
|
|
|
# Dump a text file of errors and positions. Note
|
|
|
|
|
# that the dump, using "drc listall why", enumerates
|
|
|
|
|
# every single edge check and is therefore more
|
|
|
|
|
# detailed than the areas declared by "drc count"
|
|
|
|
|
# or enumerated in "drc find".
|
|
|
|
|
#---------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::drc_save_report {{cellname ""} {outfile ""}} {
|
|
|
|
|
|
|
|
|
|
if {$outfile == ""} {set outfile "drc.out"}
|
|
|
|
|
|
|
|
|
|
set fout [open $outfile w]
|
|
|
|
|
set oscale [cif scale out]
|
|
|
|
|
|
|
|
|
|
# magic::suspendall
|
|
|
|
|
|
|
|
|
|
if {$cellname == ""} {
|
|
|
|
|
select top cell
|
|
|
|
|
set cellname [cellname list self]
|
|
|
|
|
set origname ""
|
|
|
|
|
} else {
|
|
|
|
|
set origname [cellname list self]
|
|
|
|
|
puts stdout "loading $cellname\n"
|
|
|
|
|
flush stdout
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2020-02-17 16:25:45 +01:00
|
|
|
load $cellname
|
|
|
|
|
select top cell
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drc check
|
|
|
|
|
set count [drc list count]
|
|
|
|
|
|
|
|
|
|
puts $fout "$cellname $count"
|
|
|
|
|
puts $fout "----------------------------------------"
|
|
|
|
|
set drcresult [drc listall why]
|
|
|
|
|
foreach {errtype coordlist} $drcresult {
|
|
|
|
|
puts $fout $errtype
|
|
|
|
|
puts $fout "----------------------------------------"
|
|
|
|
|
foreach coord $coordlist {
|
|
|
|
|
set bllx [expr {$oscale * [lindex $coord 0]}]
|
|
|
|
|
set blly [expr {$oscale * [lindex $coord 1]}]
|
|
|
|
|
set burx [expr {$oscale * [lindex $coord 2]}]
|
|
|
|
|
set bury [expr {$oscale * [lindex $coord 3]}]
|
|
|
|
|
set coords [format " %.3f %.3f %.3f %.3f" $bllx $blly $burx $bury]
|
|
|
|
|
puts $fout "$coords"
|
|
|
|
|
}
|
|
|
|
|
puts $fout "----------------------------------------"
|
|
|
|
|
}
|
|
|
|
|
puts $fout ""
|
|
|
|
|
|
|
|
|
|
if {$origname != ""} {
|
|
|
|
|
load $origname
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# magic::resumeall
|
|
|
|
|
|
|
|
|
|
close $fout
|
|
|
|
|
puts stdout "done with $outfile\n"
|
|
|
|
|
flush stdout
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#---------------------------------------------------
|
|
|
|
|
# Read back a dumped file of DRC errors. This is of
|
|
|
|
|
# limited use, as any layout should have DRC errors
|
|
|
|
|
# already marked. This routine loads errors into
|
|
|
|
|
# "feedback" areas, which is redundant to the error
|
|
|
|
|
# tiles. However, feedback areas are more precise,
|
|
|
|
|
# as they mark the error area at each checked edge
|
|
|
|
|
#---------------------------------------------------
|
|
|
|
|
|
|
|
|
|
proc magic::drc_load_report {{drc_file ""}} {
|
|
|
|
|
|
|
|
|
|
if {$drc_file == ""} {
|
|
|
|
|
set drc_file "drc.out"
|
|
|
|
|
}
|
|
|
|
|
set fin [open $drc_file r]
|
|
|
|
|
|
|
|
|
|
puts stdout "Reading $drc_file\n"
|
|
|
|
|
flush stdout
|
|
|
|
|
|
|
|
|
|
magic::suspendall
|
|
|
|
|
|
|
|
|
|
set error_text ""
|
|
|
|
|
while {[gets $fin line] >= 0} {
|
|
|
|
|
if {[string first " " $line] == 0} {
|
|
|
|
|
if [regexp { ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)} $line lmatch llx lly urx ury] {
|
|
|
|
|
feedback add "$error_text" vert_highlights ${llx}um ${lly}um \
|
|
|
|
|
${llx}um ${ury}um ${urx}um ${ury}um ${urx}um ${lly}um
|
|
|
|
|
}
|
|
|
|
|
} elseif {[string first "-" $line] != 0} {
|
|
|
|
|
set error_text $line
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
magic::resumeall
|
|
|
|
|
|
|
|
|
|
close $fin
|
|
|
|
|
|
|
|
|
|
puts stdout "Done.\n"
|
|
|
|
|
puts stdout "Use \"feedback find\" to enumerate errors.\n"
|
|
|
|
|
flush stdout
|
|
|
|
|
}
|
|
|
|
|
|