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 ROPE ← NIL;
inputs: LIST OF ROPE ← NIL;
outputs: LIST OF ROPE ← NIL;
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 ROPE ← NIL;
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: INT ← IO.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:
BOOL ←
FALSE] ~ {
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: INT ← IF 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: ROPE ← NARROW[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];
← CoreToVImpl.WriteCircuit[&ct, TerminalIO.TOS[]]