From 72c0f4b3d3f2236c2477b0ab405f5fd26c58b520 Mon Sep 17 00:00:00 2001 From: abejgonzalez Date: Sun, 13 Sep 2020 16:37:20 -0700 Subject: [PATCH] Add GPIO Overlay --- fpga/src/main/scala/vcu118/BringupGPIOs.scala | 28 ++++++ fpga/src/main/scala/vcu118/Configs.scala | 19 +++- .../main/scala/vcu118/CustomOverlays.scala | 86 +++++++++++++++++++ fpga/src/main/scala/vcu118/Platform.scala | 10 +++ fpga/src/main/scala/vcu118/TestHarness.scala | 57 ++++++++++-- 5 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 fpga/src/main/scala/vcu118/BringupGPIOs.scala diff --git a/fpga/src/main/scala/vcu118/BringupGPIOs.scala b/fpga/src/main/scala/vcu118/BringupGPIOs.scala new file mode 100644 index 00000000..1e11dfa2 --- /dev/null +++ b/fpga/src/main/scala/vcu118/BringupGPIOs.scala @@ -0,0 +1,28 @@ +package chipyard.fpga.vcu118.bringup + +import scala.collection.mutable.{LinkedHashMap} + +object BringupGPIOs { + // map of the pin name (akin to die pin name) to (fpga package pin, IOSTANDARD) + val pinMapping = LinkedHashMap( + // these connect to LEDs and switches on the VCU118 (and use 1.2V) + "led0" -> ("AT32", "LVCMOS12"), // 0 + "led1" -> ("AV34", "LVCMOS12"), // 1 + "led2" -> ("AY30", "LVCMOS12"), // 2 + "led3" -> ("BB32", "LVCMOS12"), // 3 + "led4" -> ("BF32", "LVCMOS12"), // 4 + "led5" -> ("AU37", "LVCMOS12"), // 5 + "led6" -> ("AV36", "LVCMOS12"), // 6 + "led7" -> ("BA37", "LVCMOS12"), // 7 + "sw0" -> ("B17", "LVCMOS12"), // 8 + "sw1" -> ("G16", "LVCMOS12"), // 9 + "sw2" -> ("J16", "LVCMOS12"), // 10 + "sw3" -> ("D21", "LVCMOS12") // 11 + ) + + // return list of names (ordered) + def names: Seq[String] = pinMapping.keys.toSeq + + // return number of GPIOs + def width: Int = pinMapping.size +} diff --git a/fpga/src/main/scala/vcu118/Configs.scala b/fpga/src/main/scala/vcu118/Configs.scala index 42663f0b..7cc106d5 100644 --- a/fpga/src/main/scala/vcu118/Configs.scala +++ b/fpga/src/main/scala/vcu118/Configs.scala @@ -1,6 +1,8 @@ // See LICENSE for license details. package chipyard.fpga.vcu118 +import math.min + import freechips.rocketchip.config._ import freechips.rocketchip.subsystem._ import freechips.rocketchip.devices.debug._ @@ -9,7 +11,6 @@ import freechips.rocketchip.diplomacy.{DTSModel, DTSTimebase} import freechips.rocketchip.system._ import freechips.rocketchip.tile._ -import sifive.blocks.devices.mockaon._ import sifive.blocks.devices.gpio._ import sifive.blocks.devices.pwm._ import sifive.blocks.devices.spi._ @@ -20,6 +21,7 @@ import sifive.fpgashells.shell.{DesignKey} import sifive.fpgashells.shell.xilinx.{VCU118ShellPMOD} import chipyard.{BuildTop} +import chipyard.fpga.vcu118.bringup.{BringupGPIOs} class WithChipyardBuildTop extends Config((site, here, up) => { case DesignKey => {(p: Parameters) => new VCU118Platform()(p) } @@ -32,9 +34,22 @@ class WithBringupPeripherals extends Config((site, here, up) => { case PeripherySPIKey => List( SPIParams(rAddress = BigInt(0x64001000L)), SPIParams(rAddress = BigInt(0x64004000L))) + case VCU118ShellPMOD => "SDIO" case PeripheryI2CKey => List( I2CParams(address = BigInt(0x64005000L))) - case VCU118ShellPMOD => "SDIO" + case PeripheryGPIOKey => { + if (BringupGPIOs.width > 0) { + require(BringupGPIOs.width <= 64) // currently only support 64 GPIOs (change addrs to get more) + val gpioAddrs = Seq(BigInt(0x64002000), BigInt(0x64007000)) + val maxGPIOSupport = 32 // max gpios supported by SiFive driver (split by 32) + List.tabulate(((BringupGPIOs.width - 1)/maxGPIOSupport) + 1)(n => { + GPIOParams(address = gpioAddrs(n), width = min(BringupGPIOs.width - maxGPIOSupport*n, maxGPIOSupport)) + }) + } + else { + List.empty[GPIOParams] + } + } }) class FakeBringupConfig extends Config( diff --git a/fpga/src/main/scala/vcu118/CustomOverlays.scala b/fpga/src/main/scala/vcu118/CustomOverlays.scala index 3d51c3e7..ccb61f0e 100644 --- a/fpga/src/main/scala/vcu118/CustomOverlays.scala +++ b/fpga/src/main/scala/vcu118/CustomOverlays.scala @@ -1,13 +1,17 @@ package chipyard.fpga.vcu118.bringup import chisel3._ +import chisel3.experimental.{attach} import freechips.rocketchip.diplomacy._ +import chipsalliance.rocketchip.config.{Parameters, Field} import sifive.fpgashells.shell._ import sifive.fpgashells.ip.xilinx._ import sifive.fpgashells.shell.xilinx._ +import sifive.blocks.devices.gpio._ + import chipyard.fpga.vcu118.{FMCPMap} /* Connect the I2C to certain FMC pins */ @@ -63,3 +67,85 @@ class BringupUARTVCU118ShellPlacer(shell: VCU118Shell, val shellInput: UARTShell } /* Connect SPI to ADI device */ +class BringupSPIVCU118PlacedOverlay(val shell: VCU118Shell, name: String, val designInput: SPIDesignInput, val shellInput: SPIShellInput) + extends SDIOXilinxPlacedOverlay(name, designInput, shellInput) +{ + shell { InModuleBody { + val packagePinsWithPackageIOs = Seq((FMCPMap("H37"), IOPin(io.spi_clk)), + (FMCPMap("H19"), IOPin(io.spi_cs)), + (FMCPMap("H17"), IOPin(io.spi_dat(0))), + (FMCPMap("H28"), IOPin(io.spi_dat(1))), + (FMCPMap("H29"), IOPin(io.spi_dat(2))), + (FMCPMap("H16"), IOPin(io.spi_dat(3)))) + + packagePinsWithPackageIOs foreach { case (pin, io) => { + shell.xdc.addPackagePin(io, pin) + shell.xdc.addIOStandard(io, "LVCMOS18") + } } + packagePinsWithPackageIOs drop 1 foreach { case (pin, io) => { + shell.xdc.addPullup(io) + shell.xdc.addIOB(io) + } } + } } +} + +class BringupSPIVCU118ShellPlacer(shell: VCU118Shell, val shellInput: SPIShellInput)(implicit val valName: ValName) + extends SPIShellPlacer[VCU118Shell] { + def place(designInput: SPIDesignInput) = new BringupSPIVCU118PlacedOverlay(shell, valName.name, designInput, shellInput) +} + +// TODO: Move this to a different location +// SPI device description for ADI part +class ADISPIDevice(spi: Device, maxMHz: Double = 1) extends SimpleDevice("clkgen", Seq("analog,adi9516-4")) { + override def parent = Some(spi) + override def describe(resources: ResourceBindings): Description = { + val Description(name, mapping) = super.describe(resources) + val extra = Map("spi-max-frequency" -> Seq(ResourceInt(maxMHz * 1000000))) + Description(name, mapping ++ extra) + } +} + +/* Connect GPIOs to FMC */ +abstract class GPIOXilinxPlacedOverlay(name: String, di: GPIODesignInput, si: GPIOShellInput) + extends GPIOPlacedOverlay(name, di, si) +{ + def shell: XilinxShell + + shell { InModuleBody { + (io.gpio zip tlgpioSink.bundle.pins).map { case (ioPin, sinkPin) => + val iobuf = Module(new IOBUF) + iobuf.suggestName(s"gpio_iobuf") + attach(ioPin, iobuf.io.IO) + sinkPin.i.ival := iobuf.io.O + iobuf.io.T := !sinkPin.o.oe + iobuf.io.I := sinkPin.o.oval + } + } } +} + +class BringupGPIOVCU118PlacedOverlay(val shell: VCU118Shell, name: String, val designInput: GPIODesignInput, val shellInput: GPIOShellInput, gpioNames: Seq[String]) + extends GPIOXilinxPlacedOverlay(name, designInput, shellInput) +{ + shell { InModuleBody { + require(gpioNames.length == io.gpio.length) + + val packagePinsWithIOStdWithPackageIOs = (gpioNames zip io.gpio).map { case (name, io) => + val (pin, iostd) = BringupGPIOs.pinMapping(name) + (pin, iostd, IOPin(io)) + } + + packagePinsWithIOStdWithPackageIOs foreach { case (pin, iostd, io) => { + shell.xdc.addPackagePin(io, pin) + shell.xdc.addIOStandard(io, iostd) + // TODO: no drive strength found + //if (iostd == "LVCMOS12") { shell.xdc.addDriveStrength(io, "8") } + } } + } } +} + +class BringupGPIOVCU118ShellPlacer(shell: VCU118Shell, val shellInput: GPIOShellInput, gpioNames: Seq[String])(implicit val valName: ValName) + extends GPIOShellPlacer[VCU118Shell] { + def place(designInput: GPIODesignInput) = new BringupGPIOVCU118PlacedOverlay(shell, valName.name, designInput, shellInput, gpioNames) +} + + diff --git a/fpga/src/main/scala/vcu118/Platform.scala b/fpga/src/main/scala/vcu118/Platform.scala index 47e64de0..8f9a1ae8 100644 --- a/fpga/src/main/scala/vcu118/Platform.scala +++ b/fpga/src/main/scala/vcu118/Platform.scala @@ -12,11 +12,13 @@ import chipyard.{BuildSystem} import sifive.blocks.devices.uart._ import sifive.blocks.devices.spi._ import sifive.blocks.devices.i2c._ +import sifive.blocks.devices.gpio._ trait HasVCU118PlatformIO { val io_uart: Seq[UARTPortIO] val io_spi: Seq[SPIPortIO] val io_i2c: Seq[I2CPort] + val io_gpio: Seq[GPIOPortIO] } class VCU118Platform(override implicit val p: Parameters) extends LazyModule { @@ -52,4 +54,12 @@ class VCU118PlatformModule[+L <: VCU118Platform](_outer: L) extends LazyModuleIm } io_i2c_pins_temp } + + val io_gpio = _outer.lazySystem.module match { case sys: HasPeripheryGPIOModuleImp => + val io_gpio_pins_temp = p(PeripheryGPIOKey).zipWithIndex.map { case (p, i) => IO(new GPIOPortIO(p)).suggestName(s"gpio_$i") } + (io_gpio_pins_temp zip sys.gpio).map { case (io, sysio) => + io <> sysio + } + io_gpio_pins_temp + } } diff --git a/fpga/src/main/scala/vcu118/TestHarness.scala b/fpga/src/main/scala/vcu118/TestHarness.scala index 31f3c0ff..9a58960f 100644 --- a/fpga/src/main/scala/vcu118/TestHarness.scala +++ b/fpga/src/main/scala/vcu118/TestHarness.scala @@ -3,9 +3,8 @@ package chipyard.fpga.vcu118 import chisel3._ import chisel3.experimental.{Analog, IO} -import freechips.rocketchip.diplomacy.{LazyModule, LazyRawModuleImp} +import freechips.rocketchip.diplomacy._ import freechips.rocketchip.config.{Parameters} -import freechips.rocketchip.diplomacy.{InModuleBody, BundleBridgeSource} import sifive.fpgashells.shell.xilinx._ import sifive.fpgashells.ip.xilinx._ @@ -15,6 +14,9 @@ import sifive.fpgashells.clocks._ import sifive.blocks.devices.uart._ import sifive.blocks.devices.spi._ import sifive.blocks.devices.i2c._ +import sifive.blocks.devices.gpio._ + +import chipyard.fpga.vcu118.bringup._ class VCU118FPGATestHarness(override implicit val p: Parameters) extends VCU118Shell { @@ -47,18 +49,44 @@ class VCU118FPGATestHarness(override implicit val p: Parameters) extends VCU118S } /*** SPI ***/ - require(p(PeripherySPIKey).size >= 1) + require(p(PeripherySPIKey).size == 2) + + // 1st SPI goes to the VCU118 SDIO port val io_spi_bb = BundleBridgeSource(() => (new SPIPortIO(p(PeripherySPIKey).head))) - designParameters(SPIOverlayKey).head.place(SPIDesignInput(p(PeripherySPIKey).head, io_spi_bb)) + val sdio_placed = designParameters(SPIOverlayKey).head.place(SPIDesignInput(p(PeripherySPIKey).head, io_spi_bb)) InModuleBody { topDesign.module match { case dutMod: HasVCU118PlatformIO => io_spi_bb.bundle <> dutMod.io_spi.head } } + // TODO: No access to the TLSPI node... + //val mmcDev = new MMCDevice(sdio_placed.device, 1) + //ResourceBinding { + // Resource(mmcDev, "reg").bind(ResourceAddress(0)) + //} + + // 2nd SPI goes to the ADI port + + val adi = Overlay(SPIOverlayKey, new chipyard.fpga.vcu118.bringup.BringupSPIVCU118ShellPlacer(this, SPIShellInput())) + + val io_spi_bb_2 = BundleBridgeSource(() => (new SPIPortIO(p(PeripherySPIKey).last))) + val adi_placed = designParameters(SPIOverlayKey).last.place(SPIDesignInput(p(PeripherySPIKey).last, io_spi_bb_2)) + InModuleBody { + topDesign.module match { case dutMod: HasVCU118PlatformIO => + io_spi_bb_2.bundle <> dutMod.io_spi.last + } + } + + // TODO: No access to the TLSPI node... + //val adiDev = new chipyard.fpga.vcu118.bringup.ADISPIDevice(adi_placed.device, 1) + //ResourceBinding { + // Resource(adiDev, "reg").bind(ResourceAddress(0)) + //} + /*** I2C ***/ - require(p(PeripheryI2CKey).size >= 1) + require(p(PeripheryI2CKey).size == 1) val i2c = Overlay(I2COverlayKey, new chipyard.fpga.vcu118.bringup.BringupI2CVCU118ShellPlacer(this, I2CShellInput())) @@ -69,5 +97,24 @@ class VCU118FPGATestHarness(override implicit val p: Parameters) extends VCU118S io_i2c_bb.bundle <> dutMod.io_i2c.head } } + + /*** GPIO ***/ + val gpio = Seq.tabulate(p(PeripheryGPIOKey).size)(i => { + val maxGPIOSupport = 32 + val names = BringupGPIOs.names.slice(maxGPIOSupport*i, maxGPIOSupport*(i+1)) + Overlay(GPIOOverlayKey, new chipyard.fpga.vcu118.bringup.BringupGPIOVCU118ShellPlacer(this, GPIOShellInput(), names)) + }) + + val io_gpio_bb = p(PeripheryGPIOKey).map { p => BundleBridgeSource(() => (new GPIOPortIO(p))) } + (designParameters(GPIOOverlayKey) zip p(PeripheryGPIOKey)).zipWithIndex.map { case ((placer, params), i) => + placer.place(GPIODesignInput(params, io_gpio_bb(i))) + } + InModuleBody { + topDesign.module match { case dutMod: HasVCU118PlatformIO => + (io_gpio_bb zip dutMod.io_gpio).map { case (bb_io, dut_io) => + bb_io.bundle <> dut_io + } + } + } }