<> <> <> <<>> 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; <> 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; <> 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: BOOL _ FALSE]; <> 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: LOR _ NIL; 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: LOR _ NIL; data: CoreClasses.RecordCellType _ NARROW[cellType.data]; wiresR, assignsR, instR: ROPE _ NIL; <> <> <> <> <> <> <> <> FOR i: NAT IN [0..data.size) DO header: ROPE _ NIL; first: BOOL _ TRUE; 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]; }; <> <<-- 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: BOOL _ TRUE] ~ { start: INT _ IO.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: BOOL _ TRUE; 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; }; <> <<-- 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: LOR _ NIL] RETURNS [assigns: LOR _ NIL] ~ { assigns _ prevAssigns; FOR i: NAT IN [0..root.size) DO assigns _ NameWire[root[i], assigns]; ENDLOOP; }; NameWire: PROC [wire: Wire, prevAssigns: LOR _ NIL] RETURNS [assigns: LOR _ NIL] ~ { PutName: RangeProc ~ { fullName: ROPE _ SELECT 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; }; <> 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: BOOL _ TRUE; 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]; }; <> <> <> <> <> <<[] _ RefTab.Store[nameTable, inst, instanceName];>> <<};>> SetInstName: PROC [inst: CellInstance, index: INT] ~ { instanceName: ROPE _ IO.PutFR["inst%g", IO.int[index]]; [] _ RefTab.Store[nameTable, inst, instanceName]; }; SetName: PROC [ref: REF, name: ROPE, unique: BOOL _ FALSE] ~ { 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]; }; ------------------------------------------------------------ <<>> <> <> <> <> <<};>> <> <> <> <<};>> <> <> <> <<};>> <> 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: 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 .v", $GenerateVerilog, DoWriteCircuit]; CDCommandOps.RegisterWithMenu[hackMenu, "Number of gates", "Estimate number of LSILogic gates", $ExtractAndMeasure, ExtractAndMeasure]; <> 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