134 lines
5.7 KiB
Scala
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))
|
|
}
|
|
|
|
}
|