CoreToVImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
written by Ch. Louis Monier October 17, 1988 9:51:34 am PDT
DIRECTORY
CDCommandOps, CDSequencer, Core, CoreCDUser, CoreCreate, CoreOps, CoreClasses, CoreProperties, FS, IO, RefTab, Rope, SymTab, TerminalIO;
CoreToVImpl: CEDAR PROGRAM    
IMPORTS
CDCommandOps, CoreOps, CoreCDUser, CoreClasses, CoreProperties, FS, IO, RefTab, Rope, SymTab, TerminalIO
= BEGIN OPEN CoreCreate;
nameTable: RefTab.Ref ← RefTab.Create[];
cellType, instance, or atomic internal -> ROPE
reverseNameTable: SymTab.Ref ← SymTab.Create[];
hackMenu: ATOM ← $SpecialMenu; -- where DAUserHacks commands are forcefully registered
Public Procs
DoWriteCircuit: PROC [command: CDSequencer.Command] = {
WriteOne: CoreCDUser.EachRootCellTypeProc ~ {
fileName: ROPE ← CoreOps.GetCellTypeName[root];
IF Rope.IsEmpty[fileName] THEN fileName ← "UnnamedCell";
TerminalIO.PutF["Generating Verilog for %g ...\n", IO.rope[fileName]];
fileName ← Rope.Cat[fileName, ".v"];
stream ← FS.StreamOpen[fileName, $create];
WriteCircuit[root, stream];
IO.Close[stream];
TerminalIO.PutF["Verilog: %g generated!\n", IO.rope[fileName]];
quit ← TRUE; -- only one cell
};
stream: IO.STREAM;
[] ← CoreCDUser.EnumerateSelectedCellTypes[command.design, WriteOne];
};
WriteCircuit: PUBLIC PROC [cellType: CellType, s: IO.STREAM] ~ {
Init[];
IO.PutF[s, "// New Circuit\n\n"];
WriteCellType[cellType, s];
};
DefineLeaf: PROC [cellType: CellType] ~ {
NameNet: PROC [wire: Wire] ~ {
IF HasName[wire] THEN RETURN;
SetName[wire, NetName[cellType.public, wire]];
};
name: ROPE = NARROW[CoreProperties.GetCellTypeProp[cellType, vName]];
IF Rope.IsEmpty[name] THEN ERROR; -- a pre-defined cell should have a name!
SetName[cellType, name];
IF IsModule[cellType] THEN [] ← CoreOps.VisitRootAtomics[cellType.public, NameNet];
};
WriteCellType: PUBLIC PROC [cellType: CellType, s: IO.STREAM] ~ {
NameNet: PROC [wire: Wire] ~ {
IF HasName[wire] THEN RETURN;
SetName[wire, NetName[data.internal, wire]];
};
data: CoreClasses.RecordCellType;
cellType ← CoreOps.ToBasic[cellType]; -- think about this!!!
IF HasName[cellType] THEN RETURN;
IF IsLeaf[cellType] OR IsModule[cellType] THEN {DefineLeaf[cellType]; RETURN};
IF cellType.class # CoreClasses.recordCellClass THEN ERROR;
data ← NARROW[cellType.data];
SetName[cellType, CTName[cellType], TRUE]; -- do this first!!!
[] ← CoreOps.VisitRootAtomics[data.internal, NameNet];
-- name every net (atomic internal)
FOR i: NAT IN [0..data.size) DO
inst: CellInstance ← data.instances[i];
SetName[inst, InstanceName[cellType, inst]];
-- name every instance
WriteCellType[inst.type, s];
-- recursively write every sub-cell
ENDLOOP;
PropagateDirection[cellType];
IO.PutF[s, "module %g", IO.rope[Name[cellType]]];
-- write header for this non-leaf cell
WritePublic[cellType, s];
WriteInstances[cellType, s];
IO.PutF[s, "endmodule //%g\n\n", IO.rope[Name[cellType]]];
};
WritePublic: PROC [cellType: CellType, s: IO.STREAM] ~ {
WritePort: PROC [wire: Wire] ~ {
IF IsPower[wire] THEN RETURN;
ports ← CONS[Name[wire], ports];
IF GetDir[wire]=$Output THEN outputs ← CONS[Name[wire], outputs]
ELSE inputs ← CONS[Name[wire], inputs];
};
ports: LIST OF ROPENIL;
inputs: LIST OF ROPENIL;
outputs: LIST OF ROPENIL;
VisitAtomicsOnce[cellType.public, WritePort];
IO.PutF[s, "("]; PrintList[s, ports]; IO.PutF[s, ");\n"];
IF inputs#NIL THEN IO.PutF[s, "\t input "]; PrintList[s, inputs]; IO.PutF[s, ";\n"];
IF outputs#NIL THEN IO.PutF[s, "\t output "]; PrintList[s, outputs]; IO.PutF[s, ";\n"];
IO.PutF[s, "\t supply0 Gnd; supply1 Vdd; \n"];
};
WriteInstances: PROC [cellType: CellType, s: IO.STREAM] ~ {
data: CoreClasses.RecordCellType ← NARROW[cellType.data];
FOR i: NAT IN [0..data.size) DO
inst: CellInstance ← data.instances[i];
recasted: CellType ← CoreOps.ToBasic[inst.type];
recastBindings: RefTab.Ref ← CoreOps.CreateBindingTable[recasted.public, inst.type.public];
-- from recasted to original
IF IsLeaf[recasted] THEN {
-- order is important!!!
ordered: WireSeq ← recasted.public;
IF recasted.public=NIL THEN ERROR; -- every leaf cell should be treated first
IO.PutF[s, "\t %g #1 %g(", IO.rope[Name[recasted]], IO.rope[Name[inst]]];
FOR i: NAT IN [0..ordered.size) DO
-- we actually need a VisitAtomic in order!!!
-- and simplify this code, now that the order of the public is correct
originalPublic: Wire ← NARROW[RefTab.Fetch[recastBindings, ordered[i]].val];
subActual: Wire ← CoreOps.CorrespondingActual[inst.actual, inst.type.public, originalPublic];
IF subActual=NIL OR subActual.size#0 THEN ERROR;
IF i>0 THEN IO.PutF[s, ", "];
IO.PutF[s, "%g", IO.rope[Name[subActual]]];
ENDLOOP;
IO.PutF[s, ");\n"];
}
ELSE {
WriteBinding: PROC [actualWire, publicWire: Wire] ~ {
bd: ROPE;
IF IsPower[publicWire] AND IsPower[actualWire] THEN RETURN;
bd ← Rope.Cat[".", Name[publicWire], "(", Name[actualWire], ")"];
bindings ← CONS[bd, bindings];
};
bindings: LIST OF ROPENIL;
IO.PutF[s, "\t %g %g(", IO.rope[Name[recasted]], IO.rope[Name[inst]]];
[] ← CoreOps.VisitAtomicPairs[inst.actual, recasted.public, WriteBinding];
PrintList[s, bindings];
IO.PutF[s, ");\n"];
};
ENDLOOP;
};
Utilities
vTypeProp: ATOM = $VerilogType;
vName: ATOM = $VerilogName;
dirProp: ATOM = $VerilogDirection;
nbGatesProp: ATOM = $LSIGates;
maxLineLength: INT = 200;
PrintList: PROC [s: IO.STREAM, list: LIST OF ROPE] ~ {
start: INTIO.GetIndex[s];
IF list=NIL THEN RETURN;
IO.PutF[s, "%g", IO.rope[list.first]];
FOR l: LIST OF ROPE ← list.rest, l.rest WHILE l#NIL DO
IF (IO.GetIndex[s]-start)>maxLineLength THEN {start ← IO.GetIndex[s]; IO.PutF[s, ",\n"]}
ELSE IO.PutF[s, ", "];
IO.PutF[s, "%g", IO.rope[l.first]];
ENDLOOP;
};
IsPower: PROC [wire: Wire] RETURNS [BOOL] ~ {
RETURN [Rope.Equal[Name[wire], "Vdd"] OR Rope.Equal[Name[wire], "Gnd"]];
};
GetDir: PROC [wire: Wire] RETURNS [dir: ATOM] ~ {
RETURN [NARROW[CoreProperties.GetWireProp[wire, dirProp]]];
};
IsLeaf: PROC [cellType: CellType] RETURNS [BOOL] ~ {
RETURN [CoreProperties.GetCellTypeProp[cellType, vTypeProp]=$Leaf];
};
IsModule: PROC [cellType: CellType] RETURNS [BOOL] ~ {
RETURN [CoreProperties.GetCellTypeProp[cellType, vTypeProp]=$Module];
};
VisitAtomicsOnce : PUBLIC PROC [root: WireSeq, eachWire: PROC [Wire]] = {
VisitAtomicWires: PROC [wire: Wire] = {
IF wire.size=0 THEN {
IF RefTab.Fetch[alreadySeen, wire].found THEN RETURN
ELSE {eachWire[wire]; [] ← RefTab.Store[alreadySeen, wire, $Yep];}
}
ELSE FOR i: NAT IN [0 .. wire.size) DO VisitAtomicWires[wire[i]] ENDLOOP;
};
alreadySeen: RefTab.Ref ← RefTab.Create[];
FOR i: NAT IN [0 .. root.size) DO VisitAtomicWires[root[i]] ENDLOOP;
};
PropagateDirection: PROC [cellType: CellType] ~ {
UpdateDir: PROC [actualWire, publicWire: Wire] ~ {
dir, pubDir: ATOM;
dir ← GetDir[actualWire];
pubDir ← GetDir[publicWire];
IF dir=NIL THEN dir$Input;
IF dir=$Output OR pubDir=$Output THEN
CoreProperties.PutWireProp[actualWire, dirProp, $Output]
ELSE CoreProperties.PutWireProp[actualWire, dirProp, $Input];
};
data: CoreClasses.RecordCellType ← NARROW[cellType.data];
FOR i: NAT IN [0..data.size) DO
inst: CellInstance ← data.instances[i];
recasted: CellType ← CoreOps.ToBasic[inst.type];
recastBindings: RefTab.Ref ← CoreOps.CreateBindingTable[recasted.public, inst.type.public];
CoreOps.VisitAtomicPairs[inst.actual, recasted.public, UpdateDir];
ENDLOOP;
};
Names
nameMagicCounter: NAT ← 0;
NewName: PROC [] RETURNS [name: ROPE] ~ { -- Generates a new name on every call
nameMagicCounter ← nameMagicCounter+1;
name ← IO.PutFR["Cell%g", IO.int[nameMagicCounter]];
};
HasName: PROC [ref: REF] RETURNS [BOOL] ~ {
RETURN [RefTab.Fetch[nameTable, ref].found];
};
Name: PROC [ref: REF] RETURNS [ROPE] ~ {
found: BOOL; val: REF;
[found, val] ← RefTab.Fetch[nameTable, ref];
IF ~found THEN ERROR;
RETURN [NARROW[val]];
};
SetName: PROC [ref: REF, name: ROPE, unique: BOOLFALSE] ~ {
found: BOOL; val: REF;
[found, val] ← RefTab.Fetch[nameTable, ref];
IF found THEN ERROR; -- we should never encounter twice the same ref
IF unique THEN {
IF SymTab.Fetch[reverseNameTable, name].found THEN name ← NewName[];
[] ← SymTab.Store[reverseNameTable, name, ref];
};
[] ← RefTab.Store[nameTable, ref, name];
};
IsLetter: PROC [c: CHAR] RETURNS [BOOL] ~ {RETURN [(c IN ['A..'Z]) OR (c IN ['a..'z])]};
IsDigit: PROC [c: CHAR] RETURNS [BOOL] ~ {RETURN [(c IN ['0..'9])]};
Sanitize: PROC [old: CHAR] RETURNS [new: CHAR] ~ { -- Replaces funny characters by x's
new ← IF IsLetter[old] OR IsDigit[old] THEN old ELSE 'x;
};
-- Turns a full wire name into a simpler name: foo[3].bar -> foox3xbar starting with a letter
CanonizeWireName: PROC [name: ROPE] RETURNS [simpler: ROPE] ~ {
components: LIST OF ROPE;
[simpler, components] ← CoreOps.ParseWireName[name];
FOR l: LIST OF ROPE ← components, l.rest WHILE l#NIL DO
simpler ← Rope.Cat[simpler, "x", l.first];
ENDLOOP;
IF Rope.IsEmpty[name] THEN RETURN[NewName[]];
simpler ← Rope.Translate[base: name, translator: Sanitize];
IF IsDigit[Rope.Fetch[simpler, 0]] THEN simpler ← Rope.Cat["x", simpler];
};
SanitizeName: PROC [name: ROPE] RETURNS [simpler: ROPE] ~ {
IF Rope.IsEmpty[name] THEN RETURN[NewName[]];
simpler ← Rope.Translate[base: name, translator: Sanitize];
IF IsDigit[Rope.Fetch[simpler, 0]] THEN simpler ← Rope.Cat["x", simpler];
};
NetName: PROC [root: WireSeq, wire: Wire] RETURNS [name: ROPE] ~ {
fullName: ROPE ← CoreOps.GetFullWireName[root, wire];
IF Rope.IsEmpty[fullName] THEN ERROR;
name ← CanonizeWireName[fullName];
};
CTName: PROC [cellType: CellType] RETURNS [name: ROPE] ~ {
RETURN [SanitizeName[CoreOps.GetCellTypeName[cellType]]];
};
InstanceName: PROC [parent: CellType, instance: CellInstance] RETURNS [name: ROPE] ~ {
index: INT;
name ← CoreClasses.GetCellInstanceName[instance];
IF ~Rope.IsEmpty[name] THEN RETURN[SanitizeName[name]];
index ← CoreClasses.InstanceIndex[parent, instance];
IF index=-1 THEN ERROR;
name ← IO.PutFR["Inst%g", IO.int[index]];
};
SizeRef: TYPE = REF SizeRec;
SizeRec: TYPE = RECORD[totalArea: REAL ← 0.0, nb: INT ← 0];
GetAreaProc: TYPE ~ PROC [cell: CellType] RETURNS [area: REAL, key: ROPE];
Leaf: GetAreaProc ~ {
val: REF ← CoreProperties.GetCellTypeProp[cell, nbGatesProp];
intVal: INTIF val=NIL THEN 0 ELSE NARROW[val, REF INT]^;
area ← REAL[intVal]; -- square microns
key ← CoreOps.GetCellTypeName[cell];
};
HistogramProp: PROC [cell: CellType, proc: GetAreaProc, table: SymTab.Ref] RETURNS [area: REAL ← 0.0, nbCells: INT ← 0] ~ {
key: ROPE;
[area, key] ← proc[cell]; -- total area
IF area#0 THEN {
ref: REF;
item: SizeRef;
found: BOOL;
[found, ref] ← SymTab.Fetch[table, key];
IF found THEN {
item ← NARROW[ref];
item.totalArea ← item.totalArea+area;
item.nb ← item.nb+1}
ELSE item ← NEW[SizeRec ← [totalArea: area, nb: 1]];
[] ← SymTab.Store[table, key, item];
RETURN[area, 1];
};
SELECT TRUE FROM
cell.class=CoreClasses.recordCellClass => {
rct: CoreClasses.RecordCellType ← NARROW[cell.data];
FOR i: NAT IN [0..rct.size) DO
subSize: REAL ← 0.0;
subNb: INT ← 0;
[subSize, subNb] ← HistogramProp[rct[i].type, proc, table];
area ← area+subSize;
nbCells ← nbCells+subNb;
ENDLOOP;
};
cell.class.recast=NIL => RETURN[0, 0]; -- all other atomic classes are ignored
ENDCASE => RETURN HistogramProp[CoreOps.Recast[cell], proc, table]; -- recast
};
SortAndPrint: PROC [table: SymTab.Ref, area: REAL] ~ {
WHILE SymTab.GetSize[table]>0 DO-- the laziest way to sort a few entries
largest: ROPE;
value: REF;
maxSize: REAL ← 0.0;
SelectLargestItem: SymTab.EachPairAction ~ {
item: SizeRef ← NARROW[val];
name: ROPENARROW[key];
IF item.totalArea > maxSize THEN {largest ← key; maxSize ← item.totalArea};
};
PrintItem: PROC [name: ROPE, item: SizeRef] ~ {
TerminalIO.PutF["%g %g => %5.0f gates, %5.1f%% of total area.\n", IO.int[item.nb], IO.rope[name], IO.real[item.totalArea], IO.real[100*item.totalArea/area]];
};
[] ← SymTab.Pairs[table, SelectLargestItem];
value ← SymTab.Fetch[table, largest].val;
PrintItem[NARROW[largest], NARROW[value]];
[] ← SymTab.Delete[table, largest];
ENDLOOP;
};
Measure: CoreCDUser.EachRootCellTypeProc ~ {
leafTable: SymTab.Ref ← SymTab.Create[]; -- "xnor2" -> [totalArea, nb]
area: REAL ← 0.0;
nbCells: INT ← 0;
TerminalIO.PutF["\nEstimating gate count of %g in LSI Logic equivalent gates\n", IO.rope[CoreOps.GetCellTypeName[root]]];
[area, nbCells] ← HistogramProp[root, Leaf, leafTable];
TerminalIO.PutF["The cell has %g primitives, and %g gates\n", IO.int[nbCells], IO.real[area]];
SortAndPrint[leafTable, area];
};
ExtractAndMeasure: PROC [comm: CDSequencer.Command] = {
[] ← CoreCDUser.EnumerateSelectedCellTypes[comm.design, Measure];
};
Init
Init: PROC ~ {
RefTab.Erase[nameTable];
SymTab.Erase[reverseNameTable];
};
Init[];
CDCommandOps.RegisterWithMenu[hackMenu, "Generate Verilog", "Generate file <name>.v", $GenerateVerilog, DoWriteCircuit];
CDCommandOps.RegisterWithMenu[hackMenu, "Number of gates", "Estimate number of LSILogic gates", $ExtractAndMeasure, ExtractAndMeasure];
END.
← &ct ← Logic.Nand[7]
← &ct ← Logic.Nand[17]
← CoreToVImpl.WriteCircuit[&ct, TerminalIO.TOS[]]
← CoreOps.Print[&ct]
(Q, QN)= FD1 (D, CP)
(Q, QN)= LD1 (D, G)
Z=  BTS5 (A, E) -- inverting; A is the data
Gate counts:
FD1 7
LD1 5
BTS5 3
Install CellLibraries; run -a LogicVSimpleImpl CoreToVImpl