CoreToVHImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
written by Ch. Louis Monier October 25, 1988 8:12:50 pm PDT
DIRECTORY
CDCommandOps, CDSequencer, Core, CoreCDUser, CoreCreate, CoreOps, CoreClasses, CoreProperties, FS, IO, RefTab, Rope, SymTab, TerminalIO;
CoreToVHImpl: CEDAR PROGRAM    
IMPORTS
CDCommandOps, CoreOps, CoreCDUser, CoreCreate, CoreClasses, CoreProperties, FS, IO, RefTab, Rope, SymTab, TerminalIO
= BEGIN OPEN CoreCreate;
Globals
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
LOR: TYPE = LIST OF ROPE;
Constants and types
vTypeProp: ATOM = $VerilogType;
vName: ATOM = $VerilogName;
dirProp: ATOM = $VerilogDirection;
nbGatesProp: ATOM = $LSIGates;
maxLineLength: INT = 200;
comma: ROPE = ", ";
RangeProc: TYPE = PROC[wire: Wire, start, size: INT] RETURNS [quit: BOOLFALSE];
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] ~ {
name: ROPE = NARROW[CoreProperties.GetCellTypeProp[cellType, vName]];
IF Rope.IsEmpty[name] THEN ERROR; -- a predefined cell should have a name!
SetName[cellType, name];
IF IsModule[cellType] THEN FOR i: NAT IN [0..cellType.public.size) DO
wire: Wire ← cellType.public[i];
IF wire.size>0 THEN ERROR; -- Current restriction: predefined cells have atomic publics
SetNetName[cellType.public, wire];
ENDLOOP;
};
WriteCellType: PUBLIC PROC [cellType: CellType, s: IO.STREAM] ~ {
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;
SetCTName[cellType]; -- do this first!!!
data ← NARROW[cellType.data];
FOR i: NAT IN [0..data.size) DO WriteCellType[data.instances[i].type, s]; ENDLOOP;
-- recursively print all celltypes used to define this one
PropagateDirection[cellType];
WritePublic[cellType, s];
WriteInstances[cellType, s];
IO.PutF[s, "endmodule //%g\n\n", IO.rope[Name[cellType]]];
};
NameOfStrWire: PROC [wire: Wire] RETURNS [name: ROPE] ~ {
IF wire.size=0 THEN RETURN[Name[wire]];
name ← IO.PutFR["[0:%g] %g", IO.int[Width[wire]-1], IO.rope[Name[wire]]];
};
tagProp: ATOM = $VerilogTagged; -- this wire or a parent is defined in the Verilog netlist
Tag: PROC [wire: Wire] ~ {
TagWire: CoreOps.EachWireProc ~ {CoreProperties.PutWireProp[wire, tagProp, tagProp]};
[] ← CoreOps.VisitWire[wire, TagWire];
};
IsTagged: PROC [wire: Wire] RETURNS [BOOL] ~ {
RETURN [CoreProperties.GetWireProp[wire, tagProp]#NIL];
};
WritePublic: PROC [cellType: CellType, s: IO.STREAM] ~ {
assigns, ports: LORNIL;
assigns ← NameRoot[cellType.public, assigns]; -- name the public wire
IO.PutF[s, "module %g", IO.rope[Name[cellType]]]; -- write header for this non-leaf cell
-- print the port list
FOR i: NAT IN [0..cellType.public.size) DO
wire: Wire ← cellType.public[i];
IF IsPower[wire] THEN LOOP; -- filter out these guys
ports ← CONS[Name[wire], ports];
ENDLOOP;
PrintList[s, "(", comma, ");\n", ports, FALSE];
IO.PutF[s, "\tsupply0 Gnd; supply1 Vdd; \n"]; -- they don't hurt!
-- print the ports, by type
FOR i: NAT IN [0..cellType.public.size) DO
wire: Wire ← cellType.public[i];
IF IsPower[wire] THEN LOOP;
Tag[wire];
IO.PutF[s, "\t%g %g;\n", IO.atom[GetDir[wire]], IO.rope[NameOfStrWire[wire]]];
ENDLOOP;
};
WriteInstances: PROC [cellType: CellType, s: IO.STREAM] ~ {
assigns: LORNIL;
data: CoreClasses.RecordCellType ← NARROW[cellType.data];
wiresR, assignsR, instR: ROPENIL;
assigns ← NameRoot[data.internal, assigns]; -- try to name the internal wire
FOR i: NAT IN [0..data.internal.size) DO
wire: Wire ← data.internal[i];
IF IsPower[wire] OR wire.size=0 THEN LOOP; -- by the way, redundant...
IF IsTagged[wire] THEN LOOP;
Tag[wire];
IO.PutF[s, "\twire %g;\n", IO.rope[NameOfStrWire[wire]]];
ENDLOOP;
FOR i: NAT IN [0..data.size) DO
header: ROPENIL;
first: BOOLTRUE;
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
SetInstName[inst, i];
header ← IF IsLeaf[recasted] THEN "\t%g #1 %g(" ELSE "\t%g %g(";
instR ← Rope.Cat[instR, IO.PutFR[header, IO.rope[Name[recasted]], IO.rope[Name[inst]]]];
FOR i: NAT IN [0..recasted.public.size) DO -- order in public is very important!!!
originalPublic: Wire ← NARROW[RefTab.Fetch[recastBindings, recasted.public[i]].val];
subActual: Wire ← CoreOps.CorrespondingActual[inst.actual, inst.type.public, originalPublic];
actualName: ROPE;
IF IsPower[subActual] THEN LOOP;
IF ~first THEN instR ← Rope.Cat[instR, comma];
first ← FALSE;
assigns ← NameWire[subActual, assigns];
actualName ← Name[subActual];
IF ~IsTagged[subActual] THEN {
IO.PutF[s, "\twire %g;\n", IO.rope[NameOfStrWire[subActual]]];
Tag[subActual];
};
IF IsLeaf[recasted] THEN { -- order in public is very important!!!
IF subActual=NIL OR subActual.size#0 THEN ERROR; -- remember, it is a leaf
instR ← Rope.Cat[instR, IO.PutFR["%g", IO.rope[actualName]]];
}
ELSE {
instR ← Rope.Cat[instR, ".", Name[recasted.public[i]]];
instR ← Rope.Cat[instR, "(", actualName, ")"];
};
ENDLOOP;
instR ← Rope.Cat[instR, ");\n"];
ENDLOOP;
IO.PutF[s, wiresR];
PrintList[s, "\tassign ", comma, ";\n", assigns]; -- DAGs
IO.PutF[s, assignsR];
IO.PutF[s, instR];
};
Utilities
-- sum of arities, i..e number of paths leading to atomics, not the number of atomics
Width: PROC [wire: Wire] RETURNS [w: INT ← 0] ~ {
IF wire.size=0 THEN RETURN [1];
FOR i: NAT IN [0..wire.size) DO
w ← w+Width[wire[i]];
ENDLOOP;
};
-- if list is empty and ignoreEmpty, does not print anything
PrintList: PROC [s: IO.STREAM, first, separator, end: ROPE, list: LOR, ignoreEmpty: BOOLTRUE] ~ {
start: INTIO.GetIndex[s];
IF list=NIL AND ignoreEmpty THEN RETURN;
IF list=NIL AND ~ignoreEmpty THEN {
IO.PutF[s, "%g%g", IO.rope[first], IO.rope[end]]; RETURN};
IO.PutF[s, "%g%g", IO.rope[first], IO.rope[list.first]];
FOR l: LOR ← list.rest, l.rest WHILE l#NIL DO
IO.PutF[s, separator];
IF (IO.GetIndex[s]-start)>maxLineLength THEN {start ← IO.GetIndex[s]; IO.PutF[s, "\n\t"]};
IO.PutF[s, "%g", IO.rope[l.first]];
ENDLOOP;
IO.PutF[s, "%g", IO.rope[end]];
};
IsPower: PROC [wire: Wire] RETURNS [BOOL] ~ {
name: ROPE = CoreOps.GetShortWireName[wire];
RETURN [Rope.Equal[name, "Vdd"] OR Rope.Equal[name, "Gnd"]];
};
SetDir: PROC [wire: Wire, dir: ATOM] ~ {CoreProperties.PutWireProp[wire, dirProp, dir]};
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];
};
PushDirUp: PROC [wire: Wire] ~ {
allIns, allOuts: BOOLTRUE;
IF wire.size=0 THEN RETURN;
FOR i: NAT IN [0..wire.size) DO
PushDirUp[wire[i]];
SELECT GetDir[wire[i]] FROM
$input => allOuts ← FALSE;
$output => allIns ← FALSE;
$inout => {allIns ← FALSE; allOuts ← FALSE};
ENDCASE => NULL;
ENDLOOP;
IF allIns AND allOuts THEN SetDir[wire, $input]; -- unknown
IF allIns AND ~allOuts THEN SetDir[wire, $input];
IF ~allIns AND allOuts THEN SetDir[wire, $output];
IF ~allIns AND ~allOuts THEN SetDir[wire, $inout];
};
PropagateDirection: PROC [cellType: CellType] ~ {
-- d1=NIL is OK, but d2=NIL is a bug
MergeDir: PROC [d1, d2: ATOM] RETURNS [d: ATOM] ~ {
IF d2=NIL THEN RETURN [$input];
IF d1=NIL THEN RETURN [d2];
IF d1=$inout OR d2=$inout THEN RETURN [$inout];
IF d1=$output OR d2=$output THEN RETURN [$output];
IF d1=$input AND d2=$input THEN RETURN [$input];
};
UpdateAtomicDir: PROC [actualWire, publicWire: Wire] ~ {
SetDir[actualWire, MergeDir[GetDir[actualWire], GetDir[publicWire]]];
};
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];
CoreOps.VisitAtomicPairs[inst.actual, recasted.public, UpdateAtomicDir];
ENDLOOP;
FOR i: NAT IN [0..data.internal.size) DO
PushDirUp[data.internal[i]];
ENDLOOP;
};
Names (takes care of collapsing structure and DAGs)
-- The purpose is to give a name to all public or internal wires: atomic wires and top-level sequence wires keep their names; subwires have a name expressing their position with respect to their root.
-- Since Verilog accepts only one level in wire structure, we have to restructure the wires as follows: the arity of a wire is defined as the number of atomics reachable from it; the components of a sequence wire are named according to their position, i.e. A[1:3], A[0].
-- If a wire is part of a DAG, an assigment is generated for every new path leading to it; i.e. a[0]=b[7], or C=D[2:5].
NameRoot: PROC [root: WireSeq, prevAssigns: LORNIL] RETURNS [assigns: LORNIL] ~ {
assigns ← prevAssigns;
FOR i: NAT IN [0..root.size) DO
assigns ← NameWire[root[i], assigns];
ENDLOOP;
};
NameWire: PROC [wire: Wire, prevAssigns: LORNIL] RETURNS [assigns: LORNIL] ~ {
PutName: RangeProc ~ {
fullName: ROPESELECT size FROM
1 => IO.PutFR["%g[%g]", IO.rope[name], IO.int[start]],
ENDCASE => IO.PutFR["%g[%g:%g]", IO.rope[name], IO.int[start], IO.int[start+size-1]];
IF HasName[wire] THEN {
oldName: ROPE ← Name[wire];
IF ~Rope.Equal[oldName, fullName] THEN
assigns ← CONS[IO.PutFR["%g = %g", IO.rope[fullName], IO.rope[oldName]], assigns];
RETURN[TRUE];
}
ELSE SetName[wire, fullName];
};
name: ROPE = CleanUpName[CoreOps.GetShortWireName[wire], "w"];
assigns ← prevAssigns;
SetName[wire, name];
VisitSeqInOrder[wire, PutName];
};
-- root first, then subtree
VisitSeqInOrder: PROC [root: Wire, p: RangeProc] ~ {
pos: NAT ← 0;
FOR i: NAT IN [0..root.size) DO
step: INT = Width[root[i]];
VisitInOrder[root[i], pos, step, p];
pos ← pos+step;
ENDLOOP;
};
VisitInOrder: PROC [root: Wire, start, size: NAT, p: RangeProc] ~ {
pos: NAT ← start;
IF p[root, pos, size] THEN RETURN; -- this subwire is already named: we have a DAG
FOR i: NAT IN [0..root.size) DO
step: INT = Width[root[i]];
VisitInOrder[root[i], pos, step, p];
pos ← pos+step;
ENDLOOP;
};
Names (old)
magicCnt: NAT ← 0;
NewName: PROC [prefix: ROPE] RETURNS [name: ROPE] ~ { -- Generates a new name
name ← IO.PutFR["%g%g", IO.rope[prefix], IO.int[magicCnt ← magicCnt+1]];
};
CleanUpName: PROC [name: ROPE, prefix: ROPE ← "x"] RETURNS [new: ROPE] ~ {
clean: BOOLTRUE;
IF Rope.IsEmpty[name] THEN RETURN [NewName[prefix]];
IF ~IsLetter[Rope.Fetch[name, 0]] THEN clean ← FALSE;
FOR i: INT IN [0..Rope.Length[name]) DO
c: CHAR ← Rope.Fetch[name, i];
IF c=IO.SP THEN ERROR; -- no space in a name please
IF ~IsLetter[c] AND ~IsDigit[c] THEN clean ← FALSE;
ENDLOOP;
RETURN [IF clean THEN name ELSE Rope.Cat["\\", 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])]};
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]];
};
SetNetName: PROC [root: WireSeq, wire: Wire] ~ {
name: ROPE;
IF HasName[wire] THEN RETURN;
name ← CleanUpName[CoreOps.GetFullWireName[root, wire], "w"];
[] ← RefTab.Store[nameTable, wire, name];
};
SetCTName: PROC [cellType: CellType] ~ {
name: ROPE;
IF HasName[cellType] THEN RETURN;
name ← CleanUpName[CoreOps.GetCellTypeName[cellType], "Cell"];
IF SymTab.Fetch[reverseNameTable, name].found THEN name ← NewName["Cell"];
[] ← SymTab.Store[reverseNameTable, name, cellType];
[] ← RefTab.Store[nameTable, cellType, name];
};
SetInstName: PROC [inst: CellInstance, index: INT] ~ {
instanceName: ROPE ← CoreClasses.GetCellInstanceName[inst];
IF HasName[inst] THEN RETURN;
IF Rope.IsEmpty[instanceName] THEN instanceName ← IO.PutFR["inst%g", IO.int[index]]
ELSE instanceName ← CleanUpName[instanceName];
[] ← RefTab.Store[nameTable, inst, instanceName];
};
SetInstName: PROC [inst: CellInstance, index: INT] ~ {
instanceName: ROPEIO.PutFR["inst%g", IO.int[index]];
[] ← RefTab.Store[nameTable, inst, instanceName];
};
SetName: PROC [ref: REF, name: ROPE, unique: BOOLFALSE] ~ {
found: BOOL; val: REF;
[found, val] ← RefTab.Fetch[nameTable, ref];
IF found THEN RETURN; -- we should never encounter twice the same ref
IF unique THEN {
IF SymTab.Fetch[reverseNameTable, name].found THEN name ← NewName["new"];
[] ← SymTab.Store[reverseNameTable, name, ref];
};
[] ← RefTab.Store[nameTable, ref, name];
};
------------------------------------------------------------
NetName: PROC [root: WireSeq, wire: Wire] RETURNS [name: ROPE] ~ {
fullName: ROPE ← CoreOps.GetFullWireName[root, wire];
IF Rope.IsEmpty[fullName] THEN ERROR;
name ← CleanUpName[fullName];
};
SanitizeName: PROC [name, prefix: ROPE] RETURNS [simpler: ROPE] ~ {
Sanitize: PROC [old: CHAR] RETURNS [new: CHAR] ~ { -- Replaces funny characters by x's
new ← IF IsLetter[old] OR IsDigit[old] THEN old ELSE 'x;
};
IF Rope.IsEmpty[name] THEN RETURN[NewName[prefix]];
simpler ← Rope.Translate[base: name, translator: Sanitize];
IF IsDigit[Rope.Fetch[simpler, 0]] THEN simpler ← Rope.Cat["x", simpler];
};
Statistics
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];
};
sub1: Wire ← CoreCreate.Wires[wr1: "b", wr2: Seq["c", 3], name: "sub1"];
testWire: Wire ← CoreCreate.Wires["a", sub1, Seq["d", 3], "e"];
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];
testWire[1][1][1] ← testWire[2][1];
testWire[2][1] ← testWire[1][1];
END.
← &ct ← Logic.Nand[7]
← &ct ← Logic.Nand[17]
← CoreToVHImpl.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