magic/README

287 lines
13 KiB
Plaintext

Hierarchical "extresist" notes:
------------------------------------
Project started January 28, 2026
Follow-on from code development of "extresist" to remove dependence
on ".sim" and ".node" files and instead to read only the ".ext" file
that will be annotated.
When we last left off. . .
The code works both for running the "extresist" code standalone,
and for using "extract do resistance" to run the detailed extraction
in sequence with the normal extraction.
Since "extresist" has only ever been recommended for use with flattened
layout, the differences appear minimal. However, by avoiding the
(flat) .sim file in favor of the (hierarchical) .ext file, it is now
possible in theory to run "extresist" on hierarchical layouts.
The one thing that is missing from this is the ability to find where
a wire connects to a subcell, and to mark this as a kind of terminal;
specifically, a terminal that connects to a subcell, as opposed to a
terminal that connects to a device. If the contents of subcells are
considered to be "black boxes", then each cell def should have its
own self-contained network of drivers and terminals. Drivers are
already handled as requiring being ports. That's meaningful only on
the cell top level, where something has to be marked as a point of
entry for each signal expected to drive the subcircuit. However,
within a hierarchy, all drivepoints that are parent-to-child connections
will be identified and recorded as "merge" entries in the .ext file.
Since the ".ext" files are built from bottom up, it should be possible
to get a complete list of drive points with exact locations instead of
relying on port labels as the expected point of entry.
Question: If "extresist" is done right after "extract", then won't
the parent cell not be available at the time of processing any given
cell, such that the list of node merges not be available? How to work
around that problem?
There is a point in extExtractStack() in ExtMain.c when all cells up
to the top have been processed but the substrate planes have not been
replaced. Here the "extresist" could simply operate on the same stack
as "extract", and parent cell .ext files will be available to parse
for "merge" statements for finding drive points upward in the hierarchy.
At the top level, when no parent exists, then the declared ports can be
used to determine the drive points.
This will require duplicating the code that actually runs "extresist",
because it will have to be run independently in any routine that calls
"ExtCell". This looks like two places, discounting "ExtTech" which
shouldn't need it: ExtractOneCell() and extExtractStack().
Done. Now it should be possible to read the .ext file of every parent
cell of a cell def to find all of the potential points of entry.
Dealing with those points of entry will be the hard part.
First, re-run the existing test (non-hierarchical) to make sure that
moving the point where extResisForDef() is called didn't mess up
anything. Looks good.
Next, run a test of how the existing code works given a hierarchical
layout. Example in ~/devel/magic/extresist_new/hierarchical/. Copying
layout from chipalooza_projects_2, top level cell sky130_am_ip__ldo_01v8.
First problem: Not finding the .ext files when reading for extresist.
But this literally *just* worked fine for the non-hierarchical cell.
What gives? --- "EFSearchPath" is NULL, this is not what's changed
by "extract path"? The value is in global variable ExtLocalPath.
Oh. . . So it *writes* the .res.ext file to the right place but it
doesn't *read* the .res.ext file from the right place. This also
means that the non-hierarchical test pulled the wrong .ext file and
should be done over, although eyeballing it, it looks okay.
There is a command "extFileOpen" that should be used.
Fixed, try again.
This more or less worked.
Needs to print out the name of the cell being processed.
Once cell, looks like the level shifter, had "missing terminal
connection" errors.
Might be source-drain connected? No, these are perfectly normal
transistors. . . Why this cell specifically? Happens even if
only the cell itself is being extracted (no hierarchy).
Error issued at ResRex.c:1512. layoutDev->rd_terminals has all
null contents.
Note that "rd_fet_gate/source/drain/subs" is equivalent to
"rd_terminals[0/1/2/3]" and either form might be used. The
rd_terminals[*] form seems to be used only for initialization.
Looks like ResNewSDDevice() was never run.
Would have come from walking the device on all four sides;
ResNewSDDevice would have been called from ResEachTile.
Break on ResEachTile() for the gate tile in question, which would
be at 1406, 563. Apparently that never happens?
5th call to ResProcessTiles().
Note that "Adding <node name>" only appears sometimes. When it
is omitted, resisdata->rg_bigdevres is zero. Example, VPWR?
rg_bigdevres copied from "minRes" in ResProcessNode().
"Find largest SD device connected to node" seems non-controversial
and should produce a result for node VPWR.
Break on ResProcessNode until node->name is "VPWR". Problem happens
immediately.
Ah---Caused by ptr->thisDev->resistance = 0.
This is "linear resistance" from FET line. In .sim files, this is
found in the devptr record and is relevant.
Added code to get the value of device width from the .ext file and
determine the linear resistance according to the tech file values.
(Except that the open PDKs don't have these values. . . something
to deal with later.) (Since this seems important, made it default
to 10k instead of 0.)
Oddly, these changes made exactly zero difference to the output.
"minRes" is being set to zero, perhaps for no good reason.
Now things are worse: "Adding" only shows up once, for VPB,
and now there's a new error about "VPWR" being an "orphaned node"
and being connected (arbitrarily) to "VPWR.t1". . .
Went back to setting minRes to zero for FORCE or DRIVELOC.
Anyway, this seems to have nothing to do with the missing terminal
connection and has just been a distraction.
Example where ResNewSDDevice() is being called correctly:
tile = pdiff at 686, 439
tile has clientdata with resDev = L=30, W=168 @656, 439.
Tile came from a contact point record, cp->cp_tile[0].
This does not say a whole lot about why a terminal connection
wasn't found.
Staring at it for a while, I get it now: These devices are on nets that
are split into two unconnected parts but labeled the same. So the
connectivity is tracing from one of the labels, and the other gets
unprocessed. Probably gets solved by using "extract do unique"?
Yes, it does!
Found an error in other code: "extract do unique" turned a font label
into a regular label, so it got left that way. No, this is something
else and is in the original cell. Actually, this is a strange error.
"select area labels" even when applied to the entire cell, does not
find (or does not display) VGND, LVPWR, VPWR, or VPB font labels.
All non-font labels and font labels VNB, A, and X are displayed.
This behavior is not new and I will punt on it. Have no idea what's
going on, as the graphics routines appear to be working and displaying
the font label as expected.
Anyway, with "extract do unique" and "extract do resistance", I can get
a full R-C extraction netlist from the LDO layout. That leaves a lot
to be analyzed. . .
Will need a simpler hierarchical layout example.
---------------------------------------------------------------------
For now, work on analyzing the code to understand how the points of
entry (above) and exit (below) are handled. My understanding of my
previous work (which has been a while) is that there is *no* dealing
with exit points, and entry points only consider labeled ports.
Confirm this, then strategize on how to rewrite this such that any
given cell can find its exit points by looking at its .ext file
"merge" records, and its entry points by looking at its parent's
.ext file "merge" records to itself. The full list of parent uses
can be used to find all potential points of entry (some of which
might not be used in a given instance of the cell). Overlaps must
be detected and handled.
The current process stack looks like:
ResMain.c: extExtractStack()
ResRex.c: ExtResisForDef()
ResRex.c: ResCheckExtNodes()
ResRex.c: ResProcessNode()
ResMain.c: ResExtractNet()
ResSimple.c: ResDoSimplify()
ResRex.c: ResWriteExtFile()
RexExtractNet() does the following:
(loop over devices)
resMakeDevFunc()
ResShaveContacts()
ResConnectWithSD()
ResDissolveContacts()
(loop over planes)
ResAddPlumbing()
ResMakePortBreakpoints()
ResMakeLabelBreakpoints()
ResFindNewContactTiles()
ResPreProcessDevices()
ResProcessTiles()
ResEachTile()
(Then work through ResNodeQueue stack)
The relevant parts here to what needs to be added functionally are the
two routines ResMakePortBreakpoints() and ResMakeLabelBreakpoints()
These two routines should be run *only* on the top level cell of a layout,
since there is no other information specifying what it connects to.
Otherwise, I need two new routines which effectively do the same thing:
ResMakeBreakpointsUp()
ResMakeBreakpointsDown()
I have a question as to whether these really are supposed to be
"breakpoints" or if that is an appropriate designation for a terminal.
Both routines above call ResAddBreakpointFunc() which calls NEWPORT()
(macro). This is different than a breakpoint, which has NEWBREAK,
so I should probably rename the routines above to make them correct.
Each resInfo record (attached to a tile) has a "breakList" and a
"portList". But---"resAllPortNodes()" creates a NEWBREAK for every
entry in portList, and ResMakeLabelBreakpoints() calls
ResAddBreakpointFunc(), although the latter calls NEWPORT. This seems
circular and could be a case of "I did this when I didn't know what I
was doing". "resAllPortNodes()" is called at the end of ResEachTile().
Another question: ResMakePortBreakpoints() does not check the cell def but
works from the node list created by reading the .ext file, where any node
that has a "port" entry is flagged. But all ports are labels, so wouldn't
ResMakeLabelBreakpoints() create a redundant entry?
Note check at ResProcesNode() for node->status & REDUNDANT; It is not clear
to me what REDUNDANT ought to be doing in the ResReadExt code. Possibly it
should just be eliminated, and replaced by forcing EXTRACT_DOUNIQUE. For
R-C extraction there should never be two of the same node name in different
places. "node_uq0" would be just a name, like "node.n0"; it should not
matter that the label is for a junction or a terminal.
The routines appear to assume one "drive point" and multiple "terminals".
This would be a typical digital setup. The node can have a single
"drivepoint" position. This is not particularly meaningful for analog.
Maybe it makes sense to pre-partition nets so that they all end up with
the single-driver, multiple-terminal form? The idea that a net has multiple
drivers means that there are multiple points of attachment to that net from
outside. But it may be sufficient just to arbitrarily pick one node as the
driver and declare all the rest to be terminals.
------------------------
At what point in the call stack above is it possible to know whether the
current cell def is a top level cell or not? Should be possible for
anything with "def" in its argument list.
Top level cell has def->cd_parents->cu_parent = 0?
What prevents a breakpoint from being considered dangling and getting
removed? Answer: The node is marked with RES_NODE_ORIGIN. This origin
is placed at the position of a port, and if breakpoints are added, then
only the resInfo record at the origin position keeps RES_NODE_ORIGIN,
and that keeps the record from being pruned from the node. Will likely
need a different flag, such as "RES_NODE_SINK".
Seems like this should run after basic extraction and before simplification.
If a list of parent and child connection points exists after reading the
.ext files, then the connection points can be located and the resInfo
at the connection point can be set to be a sink. That may involve adding
a junction, which might be as simple as just calling ResProcessJunction()?
Or more likely, need a new handler (in ResJunct.c) to process a sink point.
Note that "ResProcessJunction()" requires two connecting tiles, which would
not be the case with a sink.
Analyze how these junction processor routines work. The resInfo field of
"tp" is searched, but the resInfo field of "tile" gets the new breakpoint.
Need to keep in mind the difference between ResNodeList, which has entries
of type "resNode" (which is basically a sub-node), and ResOriginalNodes,
which has entries of type "ResExtNode". Particularly confusing is
ResOriginalNodes vs. ResOriginNode. . . may want to change something, like
referring to the originals as Nets?
(Cleaned up a bunch of mess in the code meanwhile.)
Added RES_NODE_SINK and handling, but nothing defines it yet. It will
prevent nodes having the flag from being merged or simplified away, like
RES_NODE_ORIGIN.
Now need a routine that reads the "merge" lines from .ext files and
creates sinks.
Currently resAllPortNodes() runs at the end of ResEachTile().
It takes the Info record attached to the tile, runs through the portList,
and adds a new breakpoint. . . to the same tile. There is no check of
whether the port intersects the tile, which is probably wrong.
Time to start working with some simple examples.