Files
chipyard/tapeout/src/main/scala/transforms/.pads/AddPadFrame.scala
2020-05-13 10:34:57 -07:00

134 lines
5.7 KiB
Scala

// See LICENSE for license details.
package barstools.tapeout.transforms.pads
import firrtl.annotations._
import firrtl.ir._
import firrtl._
import firrtl.passes.Pass
// Analog is like UInt, SInt; it's not a direction (which is kind of weird)
// WARNING: Analog type is associated with Verilog InOut! i.e. even if digital pads are tri-statable, b/c tristate
// requires an additional ctrl signal, digital pads must be operated in a single "static" condition here; Analog will
// be paired with analog pads
class AddPadFrame(
topMod: String,
padFrameName: String,
topInternalName: String,
ioPads: Seq[PortIOPad],
supplyPads: Seq[TopSupplyPad]) extends Pass {
def run(c: Circuit): Circuit = {
// New modules consist of old modules (with top renamed to internal) + padFrame + newTop
val newMods = c.modules.map {
case mod: Module if mod.name == topMod =>
// Original top module is now internal module
mod.copy(name = topInternalName)
case m => m
} ++ Seq(buildPadFrame(), buildTopWrapper())
// Reparent so circuit top is whatever uses pads!
// TODO: Can the top level be a blackbox?
c.copy(modules = newMods, main = topMod)
}
def intName(p: PortIOPad) = s"${p.portName}_Int"
def extName(p: PortIOPad) = s"${p.portName}_Ext"
def buildTopWrapper(): Module = {
// outside -> padframe -> internal
// Top (with same name) contains 1) padframe + 2) internal signals
val padFrameInst = WDefInstance(padFrameName, padFrameName)
val topInternalInst = WDefInstance(topInternalName, topInternalName)
val padFrameRef = WRef(padFrameName)
val topInternalRef = WRef(topInternalName)
val connects = ioPads.map { p =>
val io = WRef(p.portName)
val intIo = WSubField(topInternalRef, p.portName)
val padFrameIntIo = WSubField(padFrameRef, intName(p))
val padFrameExtIo = WSubField(padFrameRef, extName(p))
p.port.tpe match {
case AnalogType(_) =>
// Analog pads only have 1 port
// If Analog port doesn't have associated pad, don't hook it up to the padframe
val analogAttachInt = Seq(Attach(NoInfo, Seq(io, intIo)))
if (p.pad.isEmpty) analogAttachInt
else analogAttachInt :+ Attach(NoInfo, Seq(io, padFrameExtIo))
case _ => p.portDirection match {
case Input =>
// input to padframe ; padframe to internal
Seq(Connect(NoInfo, padFrameExtIo, io), Connect(NoInfo, intIo, padFrameIntIo))
case Output =>
// internal to padframe ; padframe to output
Seq(Connect(NoInfo, padFrameIntIo, intIo), Connect(NoInfo, io, padFrameExtIo))
}
}
}.flatten
val stmts = Seq(padFrameInst, topInternalInst) ++ connects
val ports = ioPads.map(p => p.port)
Module(NoInfo, topMod, ports = ports, body = Block(stmts))
}
def buildPadFrame(): Module = {
// Internal = connection to original RTL; External = connection to outside world
// Note that for analog pads, since there's only 1 port, only _Ext is used
val intPorts = ioPads.map(p => p.port.tpe match {
case AnalogType(_) => None
case _ => Some(p.port.copy(name = intName(p), direction = Utils.swap(p.portDirection)))
}).flatten
val extPorts = ioPads.map(p => p.port.tpe match {
// If an analog port doesn't have a pad associated with it, don't add it to the padframe
case AnalogType(_) if p.pad.isEmpty => None
case _ => Some(p.port.copy(name = extName(p)))
} ).flatten
// Only create pad black boxes for ports that require them
val ioPadInsts = ioPads.filter(x => !x.pad.isEmpty).map(p => WDefInstance(p.firrtlBBName, p.firrtlBBName))
// Connect to pad only if used ; otherwise leave dangling for Analog
// and just connect through for digital (assumes no supplies)
val connects = ioPads.map { p =>
val intRef = WRef(intName(p), p.port.tpe)
val extRef = WRef(extName(p), p.port.tpe)
p.pad match {
// No pad needed -- just connect through
case None => p.port.tpe match {
case AnalogType(_) =>
Seq(EmptyStmt)
case _ =>
val (lhs, rhs) = p.portDirection match {
case Input => (intRef, extRef)
case Output => (extRef, intRef)
}
Seq(Connect(NoInfo, lhs, rhs))
}
// Add pad
case Some(x) =>
val padRef = WRef(p.firrtlBBName)
p.port.tpe match {
// Analog type has 1:1 mapping to inout
case AnalogType(_) =>
val padIORef = WSubField(padRef, AnalogPad.ioName)
Seq(Attach(NoInfo, Seq(padIORef, extRef)))
// Normal verilog in/out can be mapped to uint, sint, or clocktype, so need cast
case _ =>
val padBBType = UIntType(getWidth(p.port.tpe))
val padInRef = WSubField(padRef, DigitalPad.inName, padBBType, UnknownFlow)
val padOutRef = WSubField(padRef, DigitalPad.outName, padBBType, UnknownFlow)
val (rhsPadIn, lhsPadOut) = p.portDirection match {
case Input => (extRef, intRef)
case Output => (intRef, extRef)
}
// Pad inputs are treated as UInts, so need to do type conversion
// from type to UInt pad input; from pad output to type
Seq(
Connect(NoInfo, padInRef, castRhs(padBBType, rhsPadIn)),
Connect(NoInfo, lhsPadOut, castRhs(p.port.tpe, padOutRef)))
}
}
}.flatten
val supplyPadInsts = supplyPads.map(p => p.instNames.map(n => WDefInstance(n, p.firrtlBBName))).flatten
Module(NoInfo, padFrameName, ports = intPorts ++ extPorts, body = Block(ioPadInsts ++ connects ++ supplyPadInsts))
}
}