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])] + } + +}