From 84c880d231435b59e8ee8791c0d350bdaddb718b Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 17 Mar 2020 14:02:11 -0700 Subject: [PATCH 1/8] WIP; does not compile, but useful as a code review starting point --- .../resources/barstools/iocell/vsrc/Analog.v | 11 ++ .../resources/barstools/iocell/vsrc/IOCell.v | 46 +++++ iocell/src/main/scala/chisel/Analog.scala | 16 ++ iocell/src/main/scala/chisel/IOCell.scala | 185 ++++++++++++++++++ 4 files changed, 258 insertions(+) create mode 100644 iocell/src/main/resources/barstools/iocell/vsrc/Analog.v create mode 100644 iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v create mode 100644 iocell/src/main/scala/chisel/Analog.scala create mode 100644 iocell/src/main/scala/chisel/IOCell.scala diff --git a/iocell/src/main/resources/barstools/iocell/vsrc/Analog.v b/iocell/src/main/resources/barstools/iocell/vsrc/Analog.v new file mode 100644 index 00000000..0a9abf03 --- /dev/null +++ b/iocell/src/main/resources/barstools/iocell/vsrc/Analog.v @@ -0,0 +1,11 @@ +// See LICENSE for license details + +`timescale 1ns/1ps + +module AnalogConst #(CONST, WIDTH) ( + output [WIDTH-1:0] io +); + + assign io = CONST; + +endmodule diff --git a/iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v b/iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v new file mode 100644 index 00000000..d0be6b0b --- /dev/null +++ b/iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v @@ -0,0 +1,46 @@ +// See LICENSE for license details + +`timescale 1ns/1ps + +module ExampleAnalogIOCell( + inout pad, + inout core +); + + assign core = 1'bz; + assign pad = core; + +endmodule + +module ExampleDigitalGPIOCell( + inout pad, + output i, + input ie, + input o, + input oe +); + + assign pad = oe ? o : 1'bz; + assign i = ie ? pad : 1'b0; + +endmodule + +module ExampleDigitalInIOCell( + input pad, + output i, + input ie +); + + assign i = ie ? pad : 1'b0; + +endmodule + +module ExampleDigitalOutIOCell( + output pad, + input o, + output oe +); + + assign pad = oe ? o : 1'bz; + +endmodule diff --git a/iocell/src/main/scala/chisel/Analog.scala b/iocell/src/main/scala/chisel/Analog.scala new file mode 100644 index 00000000..e1b4fc78 --- /dev/null +++ b/iocell/src/main/scala/chisel/Analog.scala @@ -0,0 +1,16 @@ +// See LICENSE for license details + +package barstools.iocell.chisel + +import chisel3._ +import chisel3.util.{HasBlackBoxResource} +import chisel3.experimental.{Analog, IntParam} + +class AnalogConst(value: Int, width: Int = 1) extends BlackBox(Map("CONST" -> IntParam(value), "WIDTH" -> IntParam(width))) with HasBlackBoxResource{ + val io = IO(new Bundle {val io = Analog(width.W) } ) + addResource("/barstools/iocell/vsrc/Analog.v") +} + +object AnalogConst { + def apply(value: Int, width: Int = 1) = Module(new AnalogConst(value, width)).io.io +} diff --git a/iocell/src/main/scala/chisel/IOCell.scala b/iocell/src/main/scala/chisel/IOCell.scala new file mode 100644 index 00000000..52c935cb --- /dev/null +++ b/iocell/src/main/scala/chisel/IOCell.scala @@ -0,0 +1,185 @@ +// See LICENSE for license details + +package barstools.iocell.chisel + +import chisel3._ +import chisel3.util.{Cat, HasBlackBoxResource} +import chisel3.experimental.{Analog, DataMirror} + +class AnalogIOCellBundle extends Bundle { + val pad = Analog(1.W) + val core = Analog(1.W) +} + +class DigitalGPIOCellBundle extends Bundle { + val pad = Analog(1.W) + val i = Output(Bool()) + val ie = Input(Bool()) + val o = Input(Bool()) + val oe = Input(Bool()) +} + +class DigitalOutIOCellBundle extends Bundle { + val pad = Output(Bool()) + val o = Input(Bool()) + val oe = Input(Bool()) +} + +class DigitalInIOCellBundle extends Bundle { + val pad = Input(Bool()) + val i = Output(Bool()) + val ie = Input(Bool()) +} + +abstract class IOCell extends BlackBox with HasBlackBoxResource + +abstract class AnalogIOCell extends IOCell { + val io: AnalogIOCellBundle +} + +abstract class DigitalGPIOCell extends IOCell { + val io: DigitalGPIOCellBundle +} + +abstract class DigitalInIOCell extends IOCell { + val io: DigitalInIOCellBundle +} + +abstract class DigitalOutIOCell extends IOCell { + val io: DigitalOutIOCellBundle +} + +class ExampleAnalogIOCell extends AnalogIOCell { + val io = IO(new AnalogIOCellBundle) + addResource("/barstools/iocell/vsrc/IOCell.v") +} + +class ExampleDigitalGPIOCell extends DigitalGPIOCell { + val io = IO(new DigitalGPIOCellBundle) + addResource("/barstools/iocell/vsrc/IOCell.v") +} + +class ExampleDigitalInIOCell extends DigitalInIOCell { + val io = IO(new DigitalInIOCellBundle) + addResource("/barstools/iocell/vsrc/IOCell.v") +} + +class ExampleDigitalOutIOCell extends DigitalOutIOCell { + val io = IO(new DigitalOutIOCellBundle) + addResource("/barstools/iocell/vsrc/IOCell.v") +} + +object IOCell { + + def exampleAnalog() = Module(new ExampleAnalogIOCell) + def exampleGPIO() = Module(new ExampleDigitalGPIOCell) + def exampleInput() = Module(new ExampleDigitalInIOCell) + def exampleOutput() = Module(new ExampleDigitalOutIOCell) + + def generateRaw[T <: Data](signal: T, + inFn: () => DigitalInIOCell = IOCell.exampleInput, + outFn: () => DigitalOutIOCell = IOCell.exampleOutput, + anaFn: () => AnalogIOCell = IOCell.exampleAnalog): (T, Seq[IOCell]) = + { + (signal match { + case signal: Analog => { + require(signal.getWidth <= 1, "Analogs wider than 1 bit are not supported because we can't bit Analogs (https://github.com/freechipsproject/chisel3/issues/536)") + if (signal.getWidth == 0) { + (Analog(0.W), Seq()) + } else { + val iocell = anaFn() + iocell.io.core <> signal + (iocell.io.pad, Seq(iocell)) + } + } + case signal: Clock => { + DataMirror.specifiedDirectionOf(signal) match { + case SpecifiedDirection.Input => { + val iocell = inFn() + signal := iocell.io.i.asClock + iocell.io.ie := true.B + val ck = Wire(Clock()) + iocell.io.pad := ck.asUInt.asBool + (ck, Seq(iocell)) + } + case SpecifiedDirection.Output => { + val iocell = outFn() + iocell.io.o := signal.asUInt.asBool + iocell.io.oe := true.B + (iocell.io.pad.asClock, Seq(iocell)) + } + case _ => throw new Exception("Unknown direction") + } + } + // TODO we may not actually need Bool (it is probably covered by Bits) + case signal: Bool => { + DataMirror.specifiedDirectionOf(signal) match { + case SpecifiedDirection.Input => { + val iocell = inFn() + signal := iocell.io.i + iocell.io.ie := true.B + (iocell.io.pad, Seq(iocell)) + } + case SpecifiedDirection.Output => { + val iocell = outFn() + iocell.io.o := signal + iocell.io.oe := true.B + (iocell.io.pad, Seq(iocell)) + } + case _ => throw new Exception("Unknown direction") + } + } + case signal: Bits => { + DataMirror.specifiedDirectionOf(signal) match { + case SpecifiedDirection.Input => { + val wire = Wire(chiselTypeOf(signal)) + val iocells = wire.asBools.map { w => + val iocell = inFn() + iocell.io.pad := w + iocell.io.ie := true.B + iocell + } + if (iocells.size > 0) { + signal := Cat(iocells.map(_.io.i).reverse) + } + (wire, iocells) + } + case SpecifiedDirection.Output => { + val iocells = signal.asBools.map { b => + val iocell = outFn() + iocell.io.o := b + iocell.io.oe := true.B + iocell + } + if (iocells.size > 0) { + (Cat(iocells.map(_.io.pad).reverse), iocells) + } else { + (Wire(Bits(0.W)), iocells) + } + } + case _ => throw new Exception("Unknown direction") + } + } + case signal: Vec[_] => { + val wire = Wire(chiselTypeOf(signal)) + val iocells = signal.zip(wire).foldLeft(Seq.empty[IOCell]) { case (total, (sig, w)) => + val (pad, ios) = IOCell.generateRaw(sig, inFn, outFn, anaFn) + w <> pad + total ++ ios + } + (wire, iocells) + } + case signal: Record => { + val wire = Wire(chiselTypeOf(signal)) + val iocells = signal.elements.foldLeft(Seq.empty[IOCell]) { case (total, (name, sig)) => + val (pad, ios) = IOCell.generateRaw(sig, inFn, outFn, anaFn) + wire.elements(name) <> pad + total ++ ios + } + (wire, iocells) + } + case _ => { throw new Exception("Oops, I don't know how to handle this signal.") } + }).asInstanceOf[(T, Seq[IOCell])] + } + +} From 8a38171d18b07cd8a45c60aa8eb716a3e4ea9f21 Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 18 Mar 2020 21:05:27 -0700 Subject: [PATCH 2/8] First pass that works --- iocell/src/main/scala/chisel/IOCell.scala | 159 +++++++++++----------- 1 file changed, 82 insertions(+), 77 deletions(-) diff --git a/iocell/src/main/scala/chisel/IOCell.scala b/iocell/src/main/scala/chisel/IOCell.scala index 52c935cb..d6de4baa 100644 --- a/iocell/src/main/scala/chisel/IOCell.scala +++ b/iocell/src/main/scala/chisel/IOCell.scala @@ -4,7 +4,7 @@ package barstools.iocell.chisel import chisel3._ import chisel3.util.{Cat, HasBlackBoxResource} -import chisel3.experimental.{Analog, DataMirror} +import chisel3.experimental.{Analog, DataMirror, IO} class AnalogIOCellBundle extends Bundle { val pad = Analog(1.W) @@ -76,110 +76,115 @@ object IOCell { def exampleInput() = Module(new ExampleDigitalInIOCell) def exampleOutput() = Module(new ExampleDigitalOutIOCell) - def generateRaw[T <: Data](signal: T, +/* This doesn't work because chiselTypeOf doesn't preserve direction info :( + def generateIOFromSignal[T <: Data](coreSignal: T, inFn: () => DigitalInIOCell = IOCell.exampleInput, outFn: () => DigitalOutIOCell = IOCell.exampleOutput, anaFn: () => AnalogIOCell = IOCell.exampleAnalog): (T, Seq[IOCell]) = { - (signal match { - case signal: Analog => { - require(signal.getWidth <= 1, "Analogs wider than 1 bit are not supported because we can't bit Analogs (https://github.com/freechipsproject/chisel3/issues/536)") - if (signal.getWidth == 0) { - (Analog(0.W), Seq()) + val padSignal = DataMirror.specifiedDirectionOf(coreSignal) match { + case SpecifiedDirection.Input => IO(Input(chiselTypeOf(coreSignal))) + case SpecifiedDirection.Output => IO(Output(chiselTypeOf(coreSignal))) + case SpecifiedDirection.Flip => IO(Flipped(chiselTypeOf(coreSignal))) + case _ => IO(chiselTypeOf(coreSignal)) + } + + val iocells = IOCell.generateFromSignal(coreSignal, padSignal, inFn, outFn, anaFn) + (padSignal, iocells).asInstanceOf[(T, Seq[IOCell])] + } +*/ + + def generateFromSignal[T <: Data](coreSignal: T, padSignal: T, + inFn: () => DigitalInIOCell = IOCell.exampleInput, + outFn: () => DigitalOutIOCell = IOCell.exampleOutput, + anaFn: () => AnalogIOCell = IOCell.exampleAnalog): Seq[IOCell] = + { + coreSignal match { + case coreSignal: Analog => { + if (coreSignal.getWidth == 0) { + Seq() } else { + require(coreSignal.getWidth == 1, "Analogs wider than 1 bit are not supported because we can't bit-select Analogs (https://github.com/freechipsproject/chisel3/issues/536)") val iocell = anaFn() - iocell.io.core <> signal - (iocell.io.pad, Seq(iocell)) + iocell.io.core <> coreSignal + padSignal <> iocell.io.pad + Seq(iocell) } } - case signal: Clock => { - DataMirror.specifiedDirectionOf(signal) match { - case SpecifiedDirection.Input => { + case coreSignal: Clock => { + DataMirror.directionOf(coreSignal) match { + case ActualDirection.Input => { val iocell = inFn() - signal := iocell.io.i.asClock + coreSignal := iocell.io.i.asClock iocell.io.ie := true.B - val ck = Wire(Clock()) - iocell.io.pad := ck.asUInt.asBool - (ck, Seq(iocell)) + iocell.io.pad := padSignal.asUInt.asBool + Seq(iocell) } - case SpecifiedDirection.Output => { + case ActualDirection.Output => { val iocell = outFn() - iocell.io.o := signal.asUInt.asBool + iocell.io.o := coreSignal.asUInt.asBool iocell.io.oe := true.B - (iocell.io.pad.asClock, Seq(iocell)) + padSignal := iocell.io.pad.asClock + Seq(iocell) } case _ => throw new Exception("Unknown direction") } } - // TODO we may not actually need Bool (it is probably covered by Bits) - case signal: Bool => { - DataMirror.specifiedDirectionOf(signal) match { - case SpecifiedDirection.Input => { - val iocell = inFn() - signal := iocell.io.i - iocell.io.ie := true.B - (iocell.io.pad, Seq(iocell)) + case coreSignal: Bits => { + require(padSignal.getWidth == coreSignal.getWidth, "padSignal and coreSignal must be the same width") + if (padSignal.getWidth == 0) { + // This dummy assignment will prevent invalid firrtl from being emitted + DataMirror.directionOf(coreSignal) match { + case ActualDirection.Input => coreSignal := 0.U } - case SpecifiedDirection.Output => { - val iocell = outFn() - iocell.io.o := signal - iocell.io.oe := true.B - (iocell.io.pad, Seq(iocell)) + Seq() + } else { + DataMirror.directionOf(coreSignal) match { + case ActualDirection.Input => { + // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that + val iocells = padSignal.asInstanceOf[Bits].asBools.map { w => + val iocell = inFn() + iocell.io.pad := w + iocell.io.ie := true.B + iocell + } + coreSignal := Cat(iocells.map(_.io.i).reverse) + iocells + } + case ActualDirection.Output => { + val iocells = coreSignal.asBools.map { w => + val iocell = outFn() + iocell.io.o := w + iocell.io.oe := true.B + iocell + } + padSignal := Cat(iocells.map(_.io.pad).reverse) + iocells + } + case _ => throw new Exception("Unknown direction") } - case _ => throw new Exception("Unknown direction") } } - case signal: Bits => { - DataMirror.specifiedDirectionOf(signal) match { - case SpecifiedDirection.Input => { - val wire = Wire(chiselTypeOf(signal)) - val iocells = wire.asBools.map { w => - val iocell = inFn() - iocell.io.pad := w - iocell.io.ie := true.B - iocell - } - if (iocells.size > 0) { - signal := Cat(iocells.map(_.io.i).reverse) - } - (wire, iocells) - } - case SpecifiedDirection.Output => { - val iocells = signal.asBools.map { b => - val iocell = outFn() - iocell.io.o := b - iocell.io.oe := true.B - iocell - } - if (iocells.size > 0) { - (Cat(iocells.map(_.io.pad).reverse), iocells) - } else { - (Wire(Bits(0.W)), iocells) - } - } - case _ => throw new Exception("Unknown direction") - } - } - case signal: Vec[_] => { - val wire = Wire(chiselTypeOf(signal)) - val iocells = signal.zip(wire).foldLeft(Seq.empty[IOCell]) { case (total, (sig, w)) => - val (pad, ios) = IOCell.generateRaw(sig, inFn, outFn, anaFn) - w <> pad + case coreSignal: Vec[Data] => { + // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that + val padSignal2 = padSignal.asInstanceOf[Vec[Data]] + require(padSignal2.size == coreSignal.size, "size of Vec for padSignal and coreSignal must be the same") + coreSignal.zip(padSignal2).foldLeft(Seq.empty[IOCell]) { case (total, (core, pad)) => + val ios = IOCell.generateFromSignal(core, pad, inFn, outFn, anaFn) total ++ ios } - (wire, iocells) } - case signal: Record => { - val wire = Wire(chiselTypeOf(signal)) - val iocells = signal.elements.foldLeft(Seq.empty[IOCell]) { case (total, (name, sig)) => - val (pad, ios) = IOCell.generateRaw(sig, inFn, outFn, anaFn) - wire.elements(name) <> pad + case coreSignal: Record => { + // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that + val padSignal2 = padSignal.asInstanceOf[Record] + coreSignal.elements.foldLeft(Seq.empty[IOCell]) { case (total, (name, core)) => + val pad = padSignal2.elements(name) + val ios = IOCell.generateFromSignal(core, pad, inFn, outFn, anaFn) total ++ ios } - (wire, iocells) } case _ => { throw new Exception("Oops, I don't know how to handle this signal.") } - }).asInstanceOf[(T, Seq[IOCell])] + } } } From f6057ff947497a091b4c3f6d72a266f6545ef83f Mon Sep 17 00:00:00 2001 From: John Wright Date: Wed, 18 Mar 2020 22:25:08 -0700 Subject: [PATCH 3/8] Allow naming, make the auto-clone IO method work --- iocell/src/main/scala/chisel/IOCell.scala | 38 +++++++++++------------ 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/iocell/src/main/scala/chisel/IOCell.scala b/iocell/src/main/scala/chisel/IOCell.scala index d6de4baa..cd69dccd 100644 --- a/iocell/src/main/scala/chisel/IOCell.scala +++ b/iocell/src/main/scala/chisel/IOCell.scala @@ -76,25 +76,17 @@ object IOCell { def exampleInput() = Module(new ExampleDigitalInIOCell) def exampleOutput() = Module(new ExampleDigitalOutIOCell) -/* This doesn't work because chiselTypeOf doesn't preserve direction info :( - def generateIOFromSignal[T <: Data](coreSignal: T, + def generateIOFromSignal[T <: Data](coreSignal: T, name: Option[String] = None, inFn: () => DigitalInIOCell = IOCell.exampleInput, outFn: () => DigitalOutIOCell = IOCell.exampleOutput, anaFn: () => AnalogIOCell = IOCell.exampleAnalog): (T, Seq[IOCell]) = { - val padSignal = DataMirror.specifiedDirectionOf(coreSignal) match { - case SpecifiedDirection.Input => IO(Input(chiselTypeOf(coreSignal))) - case SpecifiedDirection.Output => IO(Output(chiselTypeOf(coreSignal))) - case SpecifiedDirection.Flip => IO(Flipped(chiselTypeOf(coreSignal))) - case _ => IO(chiselTypeOf(coreSignal)) - } - - val iocells = IOCell.generateFromSignal(coreSignal, padSignal, inFn, outFn, anaFn) - (padSignal, iocells).asInstanceOf[(T, Seq[IOCell])] + val padSignal = IO(DataMirror.internal.chiselTypeClone[T](coreSignal)) + val iocells = IOCell.generateFromSignal(coreSignal, padSignal, name, inFn, outFn, anaFn) + (padSignal, iocells) } -*/ - def generateFromSignal[T <: Data](coreSignal: T, padSignal: T, + def generateFromSignal[T <: Data](coreSignal: T, padSignal: T, name: Option[String] = None, inFn: () => DigitalInIOCell = IOCell.exampleInput, outFn: () => DigitalOutIOCell = IOCell.exampleOutput, anaFn: () => AnalogIOCell = IOCell.exampleAnalog): Seq[IOCell] = @@ -106,6 +98,7 @@ object IOCell { } else { require(coreSignal.getWidth == 1, "Analogs wider than 1 bit are not supported because we can't bit-select Analogs (https://github.com/freechipsproject/chisel3/issues/536)") val iocell = anaFn() + name.foreach(n => iocell.suggestName(n)) iocell.io.core <> coreSignal padSignal <> iocell.io.pad Seq(iocell) @@ -115,6 +108,7 @@ object IOCell { DataMirror.directionOf(coreSignal) match { case ActualDirection.Input => { val iocell = inFn() + name.foreach(n => iocell.suggestName(n)) coreSignal := iocell.io.i.asClock iocell.io.ie := true.B iocell.io.pad := padSignal.asUInt.asBool @@ -122,6 +116,7 @@ object IOCell { } case ActualDirection.Output => { val iocell = outFn() + name.foreach(n => iocell.suggestName(n)) iocell.io.o := coreSignal.asUInt.asBool iocell.io.oe := true.B padSignal := iocell.io.pad.asClock @@ -136,14 +131,16 @@ object IOCell { // This dummy assignment will prevent invalid firrtl from being emitted DataMirror.directionOf(coreSignal) match { case ActualDirection.Input => coreSignal := 0.U + case _ => {} } Seq() } else { DataMirror.directionOf(coreSignal) match { case ActualDirection.Input => { // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that - val iocells = padSignal.asInstanceOf[Bits].asBools.map { w => + val iocells = padSignal.asInstanceOf[Bits].asBools.zipWithIndex.map { case (w, i) => val iocell = inFn() + name.foreach(n => iocell.suggestName(n + "_" + i)) iocell.io.pad := w iocell.io.ie := true.B iocell @@ -152,8 +149,9 @@ object IOCell { iocells } case ActualDirection.Output => { - val iocells = coreSignal.asBools.map { w => + val iocells = coreSignal.asBools.zipWithIndex.map { case (w, i) => val iocell = outFn() + name.foreach(n => iocell.suggestName(n + "_" + i)) iocell.io.o := w iocell.io.oe := true.B iocell @@ -169,17 +167,17 @@ object IOCell { // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that val padSignal2 = padSignal.asInstanceOf[Vec[Data]] require(padSignal2.size == coreSignal.size, "size of Vec for padSignal and coreSignal must be the same") - coreSignal.zip(padSignal2).foldLeft(Seq.empty[IOCell]) { case (total, (core, pad)) => - val ios = IOCell.generateFromSignal(core, pad, inFn, outFn, anaFn) + coreSignal.zip(padSignal2).zipWithIndex.foldLeft(Seq.empty[IOCell]) { case (total, ((core, pad), i)) => + val ios = IOCell.generateFromSignal(core, pad, name.map(_ + "_" + i), inFn, outFn, anaFn) total ++ ios } } case coreSignal: Record => { // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that val padSignal2 = padSignal.asInstanceOf[Record] - coreSignal.elements.foldLeft(Seq.empty[IOCell]) { case (total, (name, core)) => - val pad = padSignal2.elements(name) - val ios = IOCell.generateFromSignal(core, pad, inFn, outFn, anaFn) + coreSignal.elements.foldLeft(Seq.empty[IOCell]) { case (total, (eltName, core)) => + val pad = padSignal2.elements(eltName) + val ios = IOCell.generateFromSignal(core, pad, name.map(_ + "_" + eltName), inFn, outFn, anaFn) total ++ ios } } From a6731f6a5e631b7a9b531661fadda35a99d428f1 Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 30 Mar 2020 12:33:44 -0700 Subject: [PATCH 4/8] Rename example -> generic --- iocell/src/main/scala/chisel/IOCell.scala | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/iocell/src/main/scala/chisel/IOCell.scala b/iocell/src/main/scala/chisel/IOCell.scala index cd69dccd..c3f899ee 100644 --- a/iocell/src/main/scala/chisel/IOCell.scala +++ b/iocell/src/main/scala/chisel/IOCell.scala @@ -49,37 +49,37 @@ abstract class DigitalOutIOCell extends IOCell { val io: DigitalOutIOCellBundle } -class ExampleAnalogIOCell extends AnalogIOCell { +class GenericAnalogIOCell extends AnalogIOCell { val io = IO(new AnalogIOCellBundle) addResource("/barstools/iocell/vsrc/IOCell.v") } -class ExampleDigitalGPIOCell extends DigitalGPIOCell { +class GenericDigitalGPIOCell extends DigitalGPIOCell { val io = IO(new DigitalGPIOCellBundle) addResource("/barstools/iocell/vsrc/IOCell.v") } -class ExampleDigitalInIOCell extends DigitalInIOCell { +class GenericDigitalInIOCell extends DigitalInIOCell { val io = IO(new DigitalInIOCellBundle) addResource("/barstools/iocell/vsrc/IOCell.v") } -class ExampleDigitalOutIOCell extends DigitalOutIOCell { +class GenericDigitalOutIOCell extends DigitalOutIOCell { val io = IO(new DigitalOutIOCellBundle) addResource("/barstools/iocell/vsrc/IOCell.v") } object IOCell { - def exampleAnalog() = Module(new ExampleAnalogIOCell) - def exampleGPIO() = Module(new ExampleDigitalGPIOCell) - def exampleInput() = Module(new ExampleDigitalInIOCell) - def exampleOutput() = Module(new ExampleDigitalOutIOCell) + def genericAnalog() = Module(new GenericAnalogIOCell) + def genericGPIO() = Module(new GenericDigitalGPIOCell) + def genericInput() = Module(new GenericDigitalInIOCell) + def genericOutput() = Module(new GenericDigitalOutIOCell) def generateIOFromSignal[T <: Data](coreSignal: T, name: Option[String] = None, - inFn: () => DigitalInIOCell = IOCell.exampleInput, - outFn: () => DigitalOutIOCell = IOCell.exampleOutput, - anaFn: () => AnalogIOCell = IOCell.exampleAnalog): (T, Seq[IOCell]) = + inFn: () => DigitalInIOCell = IOCell.genericInput, + outFn: () => DigitalOutIOCell = IOCell.genericOutput, + anaFn: () => AnalogIOCell = IOCell.genericAnalog): (T, Seq[IOCell]) = { val padSignal = IO(DataMirror.internal.chiselTypeClone[T](coreSignal)) val iocells = IOCell.generateFromSignal(coreSignal, padSignal, name, inFn, outFn, anaFn) @@ -87,9 +87,9 @@ object IOCell { } def generateFromSignal[T <: Data](coreSignal: T, padSignal: T, name: Option[String] = None, - inFn: () => DigitalInIOCell = IOCell.exampleInput, - outFn: () => DigitalOutIOCell = IOCell.exampleOutput, - anaFn: () => AnalogIOCell = IOCell.exampleAnalog): Seq[IOCell] = + inFn: () => DigitalInIOCell = IOCell.genericInput, + outFn: () => DigitalOutIOCell = IOCell.genericOutput, + anaFn: () => AnalogIOCell = IOCell.genericAnalog): Seq[IOCell] = { coreSignal match { case coreSignal: Analog => { From 62df79934ed84d4a9cafb3ac54543e3141c00062 Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 30 Mar 2020 13:10:00 -0700 Subject: [PATCH 5/8] Remove type casts; use a tuple match instead --- iocell/src/main/scala/chisel/IOCell.scala | 25 +++++++++-------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/iocell/src/main/scala/chisel/IOCell.scala b/iocell/src/main/scala/chisel/IOCell.scala index c3f899ee..1e623888 100644 --- a/iocell/src/main/scala/chisel/IOCell.scala +++ b/iocell/src/main/scala/chisel/IOCell.scala @@ -91,8 +91,8 @@ object IOCell { outFn: () => DigitalOutIOCell = IOCell.genericOutput, anaFn: () => AnalogIOCell = IOCell.genericAnalog): Seq[IOCell] = { - coreSignal match { - case coreSignal: Analog => { + (coreSignal: T, padSignal: T) match { + case (coreSignal: Analog, padSignal: Analog) => { if (coreSignal.getWidth == 0) { Seq() } else { @@ -104,7 +104,7 @@ object IOCell { Seq(iocell) } } - case coreSignal: Clock => { + case (coreSignal: Clock, padSignal: Clock) => { DataMirror.directionOf(coreSignal) match { case ActualDirection.Input => { val iocell = inFn() @@ -125,7 +125,7 @@ object IOCell { case _ => throw new Exception("Unknown direction") } } - case coreSignal: Bits => { + case (coreSignal: Bits, padSignal: Bits) => { require(padSignal.getWidth == coreSignal.getWidth, "padSignal and coreSignal must be the same width") if (padSignal.getWidth == 0) { // This dummy assignment will prevent invalid firrtl from being emitted @@ -137,8 +137,7 @@ object IOCell { } else { DataMirror.directionOf(coreSignal) match { case ActualDirection.Input => { - // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that - val iocells = padSignal.asInstanceOf[Bits].asBools.zipWithIndex.map { case (w, i) => + val iocells = padSignal.asBools.zipWithIndex.map { case (w, i) => val iocell = inFn() name.foreach(n => iocell.suggestName(n + "_" + i)) iocell.io.pad := w @@ -163,20 +162,16 @@ object IOCell { } } } - case coreSignal: Vec[Data] => { - // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that - val padSignal2 = padSignal.asInstanceOf[Vec[Data]] - require(padSignal2.size == coreSignal.size, "size of Vec for padSignal and coreSignal must be the same") - coreSignal.zip(padSignal2).zipWithIndex.foldLeft(Seq.empty[IOCell]) { case (total, ((core, pad), i)) => + case (coreSignal: Vec[Data], padSignal: Vec[Data]) => { + require(padSignal.size == coreSignal.size, "size of Vec for padSignal and coreSignal must be the same") + coreSignal.zip(padSignal).zipWithIndex.foldLeft(Seq.empty[IOCell]) { case (total, ((core, pad), i)) => val ios = IOCell.generateFromSignal(core, pad, name.map(_ + "_" + i), inFn, outFn, anaFn) total ++ ios } } - case coreSignal: Record => { - // this type cast is safe because we guarantee that padSignal and coreSignal are the same type (T), but the compiler is not smart enough to know that - val padSignal2 = padSignal.asInstanceOf[Record] + case (coreSignal: Record, padSignal: Record) => { coreSignal.elements.foldLeft(Seq.empty[IOCell]) { case (total, (eltName, core)) => - val pad = padSignal2.elements(eltName) + val pad = padSignal.elements(eltName) val ios = IOCell.generateFromSignal(core, pad, name.map(_ + "_" + eltName), inFn, outFn, anaFn) total ++ ios } From bc3f8a42b30801ae35b96b5792b1aacbb883fd2d Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 30 Mar 2020 13:50:27 -0700 Subject: [PATCH 6/8] Forgot to update the verilog modules --- iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v b/iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v index d0be6b0b..b3ee47ce 100644 --- a/iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v +++ b/iocell/src/main/resources/barstools/iocell/vsrc/IOCell.v @@ -2,7 +2,7 @@ `timescale 1ns/1ps -module ExampleAnalogIOCell( +module GenericAnalogIOCell( inout pad, inout core ); @@ -12,7 +12,7 @@ module ExampleAnalogIOCell( endmodule -module ExampleDigitalGPIOCell( +module GenericDigitalGPIOCell( inout pad, output i, input ie, @@ -25,7 +25,7 @@ module ExampleDigitalGPIOCell( endmodule -module ExampleDigitalInIOCell( +module GenericDigitalInIOCell( input pad, output i, input ie @@ -35,7 +35,7 @@ module ExampleDigitalInIOCell( endmodule -module ExampleDigitalOutIOCell( +module GenericDigitalOutIOCell( output pad, input o, output oe From c043f344b8b7624b8cc029d184481681df69b9b2 Mon Sep 17 00:00:00 2001 From: John Wright Date: Mon, 30 Mar 2020 19:15:19 -0700 Subject: [PATCH 7/8] Code review feedback --- iocell/src/main/scala/chisel/IOCell.scala | 93 ++++++++++++++++++----- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/iocell/src/main/scala/chisel/IOCell.scala b/iocell/src/main/scala/chisel/IOCell.scala index 1e623888..eb3bb994 100644 --- a/iocell/src/main/scala/chisel/IOCell.scala +++ b/iocell/src/main/scala/chisel/IOCell.scala @@ -6,11 +6,31 @@ import chisel3._ import chisel3.util.{Cat, HasBlackBoxResource} import chisel3.experimental.{Analog, DataMirror, IO} +// The following four IO cell bundle types are bare-minimum functional connections +// for modeling 4 different IO cell scenarios. The intention is that the user +// would create wrapper modules that extend these interfaces with additional +// control signals. These are loosely similar to the sifive-blocks PinCtrl bundles +// (https://github.com/sifive/sifive-blocks/blob/master/src/main/scala/devices/pinctrl/PinCtrl.scala), +// but we want to avoid a dependency on an external libraries. + +/** + * The base IO bundle for an analog signal (typically something with no digital buffers inside) + * pad: off-chip (external) connection + * core: internal connection + */ class AnalogIOCellBundle extends Bundle { - val pad = Analog(1.W) - val core = Analog(1.W) + val pad = Analog(1.W) // Pad/bump signal (off-chip) + val core = Analog(1.W) // core signal (on-chip) } +/** + * The base IO bundle for a signal with runtime-controllable direction + * pad: off-chip (external) connection + * i: input to chip logic (output from IO cell) + * ie: enable signal for i + * o: output from chip logic (input to IO cell) + * oe: enable signal for o + */ class DigitalGPIOCellBundle extends Bundle { val pad = Analog(1.W) val i = Output(Bool()) @@ -19,19 +39,31 @@ class DigitalGPIOCellBundle extends Bundle { val oe = Input(Bool()) } +/** + * The base IO bundle for a digital output signal + * pad: off-chip (external) connection + * o: output from chip logic (input to IO cell) + * oe: enable signal for o + */ class DigitalOutIOCellBundle extends Bundle { val pad = Output(Bool()) val o = Input(Bool()) val oe = Input(Bool()) } +/** + * The base IO bundle for a digital input signal + * pad: off-chip (external) connection + * i: input to chip logic (output from IO cell) + * ie: enable signal for i + */ class DigitalInIOCellBundle extends Bundle { val pad = Input(Bool()) val i = Output(Bool()) val ie = Input(Bool()) } -abstract class IOCell extends BlackBox with HasBlackBoxResource +abstract class IOCell extends BlackBox abstract class AnalogIOCell extends IOCell { val io: AnalogIOCellBundle @@ -49,24 +81,28 @@ abstract class DigitalOutIOCell extends IOCell { val io: DigitalOutIOCellBundle } -class GenericAnalogIOCell extends AnalogIOCell { +// The following Generic IO cell black boxes have verilog models that mimic a very simple +// implementation of an IO cell. For building a real chip, it is important to implement +// and use similar classes which wrap the foundry-specific IO cells. + +trait GenericIOCell extends HasBlackBoxResource { + addResource("/barstools/iocell/vsrc/IOCell.v") +} + +class GenericAnalogIOCell extends AnalogIOCell with IsGenericIOCell { val io = IO(new AnalogIOCellBundle) - addResource("/barstools/iocell/vsrc/IOCell.v") } -class GenericDigitalGPIOCell extends DigitalGPIOCell { +class GenericDigitalGPIOCell extends DigitalGPIOCell with IsGenericIOCell { val io = IO(new DigitalGPIOCellBundle) - addResource("/barstools/iocell/vsrc/IOCell.v") } -class GenericDigitalInIOCell extends DigitalInIOCell { +class GenericDigitalInIOCell extends DigitalInIOCell with IsGenericIOCell { val io = IO(new DigitalInIOCellBundle) - addResource("/barstools/iocell/vsrc/IOCell.v") } -class GenericDigitalOutIOCell extends DigitalOutIOCell { +class GenericDigitalOutIOCell extends DigitalOutIOCell with IsGenericIOCell { val io = IO(new DigitalOutIOCellBundle) - addResource("/barstools/iocell/vsrc/IOCell.v") } object IOCell { @@ -76,6 +112,16 @@ object IOCell { def genericInput() = Module(new GenericDigitalInIOCell) def genericOutput() = Module(new GenericDigitalOutIOCell) + /** + * From within a RawModule or MultiIOModule context, generate new module IOs from a given + * signal and return the new IO and a Seq containing all generated IO cells. + * @param coreSignal The signal onto which to add IO cells + * @param name An optional name or name prefix to use for naming IO cells + * @param inFn A function to generate a DigitalInIOCell to use for input signals + * @param outFn A function to generate a DigitalOutIOCell to use for output signals + * @param anaFn A function to generate an AnalogIOCell to use for analog signals + * @return A tuple of (the generated IO data node, a Seq of all generated IO cell instances) + */ def generateIOFromSignal[T <: Data](coreSignal: T, name: Option[String] = None, inFn: () => DigitalInIOCell = IOCell.genericInput, outFn: () => DigitalOutIOCell = IOCell.genericOutput, @@ -86,6 +132,17 @@ object IOCell { (padSignal, iocells) } + /** + * Connect two identical signals together by adding IO cells between them and return a Seq + * containing all generated IO cells. + * @param coreSignal The core-side (internal) signal onto which to connect/add IO cells + * @param padSignal The pad-side (external) signal onto which to connect IO cells + * @param name An optional name or name prefix to use for naming IO cells + * @param inFn A function to generate a DigitalInIOCell to use for input signals + * @param outFn A function to generate a DigitalOutIOCell to use for output signals + * @param anaFn A function to generate an AnalogIOCell to use for analog signals + * @return A Seq of all generated IO cell instances + */ def generateFromSignal[T <: Data](coreSignal: T, padSignal: T, name: Option[String] = None, inFn: () => DigitalInIOCell = IOCell.genericInput, outFn: () => DigitalOutIOCell = IOCell.genericOutput, @@ -122,7 +179,7 @@ object IOCell { padSignal := iocell.io.pad.asClock Seq(iocell) } - case _ => throw new Exception("Unknown direction") + case _ => throw new Exception("Clock signal does not have a direction and cannot be matched to an IOCell") } } case (coreSignal: Bits, padSignal: Bits) => { @@ -137,28 +194,30 @@ object IOCell { } else { DataMirror.directionOf(coreSignal) match { case ActualDirection.Input => { - val iocells = padSignal.asBools.zipWithIndex.map { case (w, i) => + val iocells = padSignal.asBools.zipWithIndex.map { case (sig, i) => val iocell = inFn() name.foreach(n => iocell.suggestName(n + "_" + i)) - iocell.io.pad := w + iocell.io.pad := sig iocell.io.ie := true.B iocell } + // Note that the reverse here is because Cat(Seq(a,b,c,d)) yields abcd, but a is index 0 of the Seq coreSignal := Cat(iocells.map(_.io.i).reverse) iocells } case ActualDirection.Output => { - val iocells = coreSignal.asBools.zipWithIndex.map { case (w, i) => + val iocells = coreSignal.asBools.zipWithIndex.map { case (sig, i) => val iocell = outFn() name.foreach(n => iocell.suggestName(n + "_" + i)) - iocell.io.o := w + iocell.io.o := sig iocell.io.oe := true.B iocell } + // Note that the reverse here is because Cat(Seq(a,b,c,d)) yields abcd, but a is index 0 of the Seq padSignal := Cat(iocells.map(_.io.pad).reverse) iocells } - case _ => throw new Exception("Unknown direction") + case _ => throw new Exception("Bits signal does not have a direction and cannot be matched to IOCell(s)") } } } From 6638f5c77e878315eb7dff13e03456ce9e56c118 Mon Sep 17 00:00:00 2001 From: John Wright Date: Tue, 31 Mar 2020 13:06:01 -0700 Subject: [PATCH 8/8] More CR feedback, fix bug introduced in previous commit --- iocell/src/main/scala/chisel/IOCell.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/iocell/src/main/scala/chisel/IOCell.scala b/iocell/src/main/scala/chisel/IOCell.scala index eb3bb994..340a4e9d 100644 --- a/iocell/src/main/scala/chisel/IOCell.scala +++ b/iocell/src/main/scala/chisel/IOCell.scala @@ -85,7 +85,7 @@ abstract class DigitalOutIOCell extends IOCell { // implementation of an IO cell. For building a real chip, it is important to implement // and use similar classes which wrap the foundry-specific IO cells. -trait GenericIOCell extends HasBlackBoxResource { +trait IsGenericIOCell extends HasBlackBoxResource { addResource("/barstools/iocell/vsrc/IOCell.v") } @@ -196,7 +196,10 @@ object IOCell { case ActualDirection.Input => { val iocells = padSignal.asBools.zipWithIndex.map { case (sig, i) => val iocell = inFn() - name.foreach(n => iocell.suggestName(n + "_" + i)) + // Note that we are relying on chisel deterministically naming this in the index order (which it does) + // This has the side-effect of naming index 0 with no _0 suffix, which is how chisel names other signals + // An alternative solution would be to suggestName(n + "_" + i) + name.foreach(n => iocell.suggestName(n)) iocell.io.pad := sig iocell.io.ie := true.B iocell @@ -208,7 +211,10 @@ object IOCell { case ActualDirection.Output => { val iocells = coreSignal.asBools.zipWithIndex.map { case (sig, i) => val iocell = outFn() - name.foreach(n => iocell.suggestName(n + "_" + i)) + // Note that we are relying on chisel deterministically naming this in the index order (which it does) + // This has the side-effect of naming index 0 with no _0 suffix, which is how chisel names other signals + // An alternative solution would be to suggestName(n + "_" + i) + name.foreach(n => iocell.suggestName(n)) iocell.io.o := sig iocell.io.oe := true.B iocell