Added a "DRC manager" window option. This allows DRC errors to be

categorized by error type and scrolled through conveniently.  However,
it needs work dealing with finding the actual error bounds.  The
"DRC count" counts tiles, which is tile-plane-geometry-specific, and
"DRC listall why" fractures errors both over tiles and over the square
areas that the interactive DRC splits the layout into, for performance.
The DRC error plane needs to be changed to hold different types for
each error class, so that errors can be scanned by boundary instead of
by tile (work to be done).
This commit is contained in:
Tim Edwards 2019-11-15 10:26:04 -05:00
parent 0eb3b1fe1c
commit e1783a42a9
6 changed files with 251 additions and 149 deletions

View File

@ -3182,7 +3182,6 @@ CmdDrc(w, cmd)
bool doforall = FALSE;
bool dolist = FALSE;
int count_total;
DRCCountList *dcl, *dclsrch;
int argc = cmd->tx_argc;
char **argv = cmd->tx_argv;
#ifdef MAGIC_WRAPPER
@ -3199,7 +3198,7 @@ CmdDrc(w, cmd)
"check recheck area under box in all cells",
"count count error tiles in each cell under box",
"euclidean on|off enable/disable Euclidean geometry checking",
"find [nth] locate next (or nth) error in the layout",
"find [nth] locate next (or nth) error in the layout",
"help print this help information",
"off turn off background checker",
"on reenable background checker",
@ -3305,15 +3304,6 @@ CmdDrc(w, cmd)
break;
case COUNT:
count_total = -1;
if (argc == 3)
if (!strncmp(argv[2], "total", 5))
count_total = 0;
#ifdef MAGIC_WRAPPER
if (count_total == -1) lobj = Tcl_NewListObj(0, NULL);
#endif
if ((window = w) == NULL)
{
window = ToolGetBoxWindow(&rootArea, (int *) NULL);
@ -3323,61 +3313,44 @@ CmdDrc(w, cmd)
rootArea = w->w_surfaceArea;
rootUse = (CellUse *) window->w_surfaceID;
dcl = DRCCount(rootUse, &rootArea);
while (dcl != NULL)
{
if (count_total >= 0)
count_total += dcl->dcl_count;
else
{
#ifdef MAGIC_WRAPPER
if (dolist)
{
Tcl_Obj *pobj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewStringObj(dcl->dcl_def->cd_name, -1));
Tcl_ListObjAppendElement(magicinterp, pobj,
Tcl_NewIntObj(dcl->dcl_count));
Tcl_ListObjAppendElement(magicinterp, lobj, pobj);
}
else
{
#endif
if (dcl->dcl_count > 1)
TxPrintf("Cell %s has %d error tiles.\n",
dcl->dcl_def->cd_name, dcl->dcl_count);
else if (dcl->dcl_count == 1)
TxPrintf("Cell %s has just one error tile.\n",
dcl->dcl_def->cd_name);
#ifdef MAGIC_WRAPPER
}
#endif
}
freeMagic((char *)dcl);
dcl = dcl->dcl_next;
}
count_total = DRCCount(rootUse, &rootArea);
#ifdef MAGIC_WRAPPER
if (count_total >= 0)
if (dolist)
{
lobj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewStringObj(rootUse->cu_def->cd_name, -1));
Tcl_ListObjAppendElement(magicinterp, lobj,
Tcl_NewIntObj(count_total));
}
else
{
#endif
if (count_total > 1)
TxPrintf("Cell %s has %d error tiles.\n",
rootUse->cu_def->cd_name, count_total);
else if (count_total == 1)
TxPrintf("Cell %s has just one error tile.\n",
rootUse->cu_def->cd_name);
#ifdef MAGIC_WRAPPER
}
#endif
#ifdef MAGIC_WRAPPER
if (doforall)
Tcl_SetObjResult(magicinterp, lobj);
else if (count_total >= 0)
{
if (dolist)
Tcl_SetObjResult(magicinterp, Tcl_NewIntObj(count_total));
else
{
if ((DRCBackGround != DRC_SET_OFF) && (count_total == -1))
count_total = 0;
if (count_total >= 0)
TxPrintf("Total DRC errors found: %d\n", count_total);
}
TxPrintf("Total DRC errors found: %d\n", count_total);
}
else if (dolist)
Tcl_SetObjResult(magicinterp, lobj);
#else
if ((DRCBackGround != DRC_SET_OFF) && (count_total == -1))
count_total = 0;
if (count_total >= 0)
TxPrintf("Total DRC errors found: %d\n", count_total);
TxPrintf("Total DRC errors found: %d\n", count_total);
#endif
break;

View File

@ -343,7 +343,7 @@ drcListallError (celldef, rect, cptr, scx)
area = &scx->scx_area;
if ((area != NULL) && (!GEO_OVERLAP(area, rect))) return;
DRCErrorCount += 1;
h = HashFind(&DRCErrorTable, cptr->drcc_why);
h = HashFind(&DRCErrorTable, drcSubstitute(cptr));
lobj = (Tcl_Obj *) HashGetValue(h);
if (lobj == NULL)
lobj = Tcl_NewListObj(0, NULL);
@ -747,111 +747,42 @@ drcCheckFunc(scx, cdarg)
* ----------------------------------------------------------------------------
*/
DRCCountList *
int
DRCCount(use, area)
CellUse *use; /* Top-level use of hierarchy. */
Rect *area; /* Area in which violations are counted. */
{
DRCCountList *dcl, *newdcl;
HashTable dupTable;
HashEntry *he;
HashSearch hs;
int count;
SearchContext scx;
extern int drcCountFunc(); /* Forward reference. */
/* Use a hash table to make sure that we don't output information
* for any cell more than once.
*/
HashInit(&dupTable, 16, HT_WORDKEYS);
CellDef *def;
extern int drcCountFunc2();
scx.scx_use = use;
scx.scx_x = use->cu_xlo;
scx.scx_y = use->cu_ylo;
scx.scx_area = *area;
scx.scx_trans = GeoIdentityTransform;
(void) drcCountFunc(&scx, &dupTable);
/* Create the list from the hash table */
dcl = NULL;
if (dupTable.ht_table != (HashEntry **) NULL)
{
HashStartSearch(&hs);
while ((he = HashNext(&dupTable, &hs)) != (HashEntry *)NULL)
{
count = (spointertype)HashGetValue(he);
if (count > 1)
{
newdcl = (DRCCountList *)mallocMagic(sizeof(DRCCountList));
newdcl->dcl_count = count - 1;
newdcl->dcl_def = (CellDef *)he->h_key.h_ptr;
newdcl->dcl_next = dcl;
dcl = newdcl;
}
}
}
HashKill(&dupTable);
return dcl;
}
int
drcCountFunc(scx, dupTable)
SearchContext *scx;
HashTable *dupTable; /* Passed as client data, used to
* avoid searching any cell twice.
*/
{
int count;
HashEntry *h;
CellDef *def;
extern int drcCountFunc2();
/* If we've already seen this cell definition before, then skip it
* now.
*/
def = scx->scx_use->cu_def;
h = HashFind(dupTable, (char *)def);
if (HashGetValue(h) != 0) goto done;
HashSetValue(h, 1);
def = use->cu_def;
/* Count errors in this cell definition by scanning the error plane. */
count = 0;
(void) DBSrPaintArea((Tile *) NULL, def->cd_planes[PL_DRC_ERROR],
&def->cd_bbox, &DBAllButSpaceBits, drcCountFunc2, (ClientData) &count);
HashSetValue(h, (spointertype)count + 1);
&def->cd_bbox, &DBAllButSpaceBits, drcCountFunc2, (ClientData)(&count));
/* Ignore children that have not been loaded---we will only report */
/* errors that can be seen. This avoids immediately loading and */
/* drc processing large layouts simply because we asked for an */
/* error count. When the cell is loaded, drc will be checked */
/* anyway, and the count can be updated in response to that check. */
if ((scx->scx_use->cu_def->cd_flags & CDAVAILABLE) == 0) return 0;
/* New behavior: Don't search children, instead propagate errors up. */
/* (void) DBCellSrArea(scx, drcCountFunc, (ClientData) dupTable); */
/* As a special performance hack, if the complete cell area is
* handled here, don't bother to look at any more array elements.
*/
done: if (GEO_SURROUND(&scx->scx_area, &def->cd_bbox)) return 2;
else return 0;
return count;
}
int
drcCountFunc2(tile, pCount)
Tile *tile; /* Tile found in error plane. */
int *pCount; /* Address of count word. */
drcCountFunc2(tile, countptr)
Tile *tile; /* Tile found in error plane. */
int *countptr; /* Address of count word. */
{
if (TiGetType(tile) != (TileType) TT_SPACE) *pCount += 1;
if (TiGetType(tile) != (TileType) TT_SPACE) (*countptr)++;
return 0;
}
/*
* ----------------------------------------------------------------------------
*

View File

@ -125,15 +125,6 @@ typedef struct drcpendingcookie
struct drcpendingcookie *dpc_next;
} DRCPendingCookie;
/* Structure used to pass back lists of cell definitions and error tile counts */
typedef struct drccountlist
{
CellDef *dcl_def;
int dcl_count;
struct drccountlist *dcl_next;
} DRCCountList;
/* Structure used to keep information about the current DRC style */
typedef struct drckeep
@ -267,7 +258,7 @@ extern void DRCPrintRulesTable();
extern void DRCWhy();
extern void DRCPrintStats();
extern void DRCCheck();
extern DRCCountList *DRCCount();
extern int DRCCount();
extern int DRCFind();
extern void DRCCatchUp();
extern bool DRCFindInteractions();

View File

@ -18,6 +18,7 @@ TCL_FILES = \
console.tcl \
techbuilder.tcl \
cellmgr.tcl \
drcmgr.tcl \
libmgr.tcl \
texthelper.tcl \
tools.tcl \

195
tcltk/drcmgr.tcl Normal file
View File

@ -0,0 +1,195 @@
#------------------------------------------------------
# Script for generating the "DRC manager" window.
#
# Written by Tim Edwards, November 2019
#------------------------------------------------------
global Opts
if {$::tk_version >= 8.5} {
set Opts(drcmgr) 0
magic::tag addpath "magic::drcmanager"
magic::tag path "magic::drcmanager"
# 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}
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
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)

View File

@ -367,6 +367,10 @@ catch {source ${CAD_ROOT}/magic/tcl/cellmgr.tcl}
catch {source ${CAD_ROOT}/magic/tcl/libmgr.tcl}
# Generate the DRC manager
catch {source ${CAD_ROOT}/magic/tcl/drcmgr.tcl}
# Generate the text helper
catch {source ${CAD_ROOT}/magic/tcl/texthelper.tcl}
@ -1404,6 +1408,13 @@ proc magic::openwrapper {{cell ""} {framename ""}} {
} else { \
wm withdraw .techmgr } }]
$m add check -label "DRC Manager" -variable Opts(drcmgr) \
-command [subst { magic::drcmanager create; \
if { \$Opts(drcmgr) } { \
wm deiconify .drcmgr ; raise .drcmgr \
} else { \
wm withdraw .drcmgr } }]
$m add check -label "Netlist Window" -variable Opts(netlist) \
-command [subst { if { \[windownames netlist\] != {}} { \
set Opts(netlist) 0 ; closewindow \[windownames netlist\] \