Initial commit.
This commit is contained in:
295
src/main/scala/devices/gpio/GPIO.scala
Normal file
295
src/main/scala/devices/gpio/GPIO.scala
Normal file
@ -0,0 +1,295 @@
|
||||
// See LICENSE for license details.
|
||||
package sifive.blocks.devices.gpio
|
||||
|
||||
import Chisel._
|
||||
import config._
|
||||
import regmapper._
|
||||
import uncore.tilelink2._
|
||||
import rocketchip.PeripheryBusConfig
|
||||
import util.AsyncResetRegVec
|
||||
|
||||
case class GPIOConfig(address: BigInt, width: Int)
|
||||
|
||||
trait HasGPIOParameters {
|
||||
val params: Tuple2[Parameters, GPIOConfig]
|
||||
implicit val p = params._1
|
||||
val c = params._2
|
||||
}
|
||||
|
||||
// YAGNI: Make the PUE, DS, and
|
||||
// these also optionally HW controllable.
|
||||
// This is the base class of things you "always"
|
||||
// want to control from a HW block.
|
||||
class GPIOCtrl extends Bundle {
|
||||
val oval = Bool()
|
||||
val oe = Bool()
|
||||
val ie = Bool()
|
||||
}
|
||||
|
||||
// This is the actual IOF interface.
|
||||
// Add a valid bit to indicate whether
|
||||
// there is something actually connected
|
||||
// to this.
|
||||
class GPIOPinIOFCtrl extends GPIOCtrl {
|
||||
val valid = Bool()
|
||||
}
|
||||
|
||||
// By default,
|
||||
object GPIOPinIOFCtrl {
|
||||
def apply(): GPIOPinIOFCtrl = {
|
||||
val iof = Wire(new GPIOPinIOFCtrl())
|
||||
iof.valid := Bool(false)
|
||||
iof.oval := Bool(false)
|
||||
iof.oe := Bool(false)
|
||||
iof.ie := Bool(false)
|
||||
iof
|
||||
}
|
||||
}
|
||||
|
||||
// This is the control for a physical
|
||||
// Pad.
|
||||
|
||||
class GPIOPinCtrl extends GPIOCtrl {
|
||||
val pue = Bool() // Pull-up Enable
|
||||
val ds = Bool() // Drive Strength
|
||||
}
|
||||
|
||||
object GPIOPinCtrl {
|
||||
def apply(): GPIOPinCtrl = {
|
||||
val pin = Wire(new GPIOPinCtrl())
|
||||
pin.oval := Bool(false)
|
||||
pin.oe := Bool(false)
|
||||
pin.pue := Bool(false)
|
||||
pin.ds := Bool(false)
|
||||
pin.ie := Bool(false)
|
||||
pin
|
||||
}
|
||||
}
|
||||
|
||||
// Package up the inputs and outputs
|
||||
// for the IOF
|
||||
class GPIOPinIOF extends Bundle {
|
||||
val i = new Bundle {
|
||||
val ival = Bool(INPUT)
|
||||
}
|
||||
val o = new GPIOPinIOFCtrl().asOutput
|
||||
}
|
||||
|
||||
// Connect both the i and o side of the pin,
|
||||
// and drive the valid signal for the IOF.
|
||||
object GPIOPinToIOF {
|
||||
|
||||
def apply (pin: GPIOPin, iof: GPIOPinIOF): Unit = {
|
||||
iof <> pin
|
||||
iof.o.valid := Bool(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Package up the inputs and outputs
|
||||
// for the Pin
|
||||
class GPIOPin extends Bundle {
|
||||
val i = new Bundle {
|
||||
val ival = Bool(INPUT)
|
||||
}
|
||||
val o = new GPIOPinCtrl().asOutput
|
||||
}
|
||||
|
||||
// This is sort of weird because
|
||||
// the IOF end up at the RocketChipTop
|
||||
// level, and we have to do the pinmux
|
||||
// outside of RocketChipTop.
|
||||
|
||||
class GPIOPortIO(c: GPIOConfig) extends Bundle {
|
||||
val pins = Vec(c.width, new GPIOPin)
|
||||
val iof_0 = Vec(c.width, new GPIOPinIOF).flip
|
||||
val iof_1 = Vec(c.width, new GPIOPinIOF).flip
|
||||
}
|
||||
|
||||
// It would be better if the IOF were here and
|
||||
// we could do the pinmux inside.
|
||||
trait GPIOBundle extends Bundle with HasGPIOParameters {
|
||||
val port = new GPIOPortIO(c)
|
||||
}
|
||||
|
||||
trait GPIOModule extends Module with HasGPIOParameters with HasRegMap {
|
||||
val io: GPIOBundle
|
||||
|
||||
//--------------------------------------------------
|
||||
// CSR Declarations
|
||||
// -------------------------------------------------
|
||||
|
||||
// SW Control only.
|
||||
val portReg = Reg(init = UInt(0, c.width))
|
||||
|
||||
val oeReg = Module(new AsyncResetRegVec(c.width, 0))
|
||||
val pueReg = Module(new AsyncResetRegVec(c.width, 0))
|
||||
val dsReg = Reg(init = UInt(0, c.width))
|
||||
val ieReg = Module(new AsyncResetRegVec(c.width, 0))
|
||||
|
||||
// Synchronize Input to get valueReg
|
||||
val inVal = Wire(UInt(0, width=c.width))
|
||||
inVal := Vec(io.port.pins.map(_.i.ival)).asUInt
|
||||
val inSyncReg = ShiftRegister(inVal, 3)
|
||||
val valueReg = Reg(init = UInt(0, c.width), next = inSyncReg)
|
||||
|
||||
// Interrupt Configuration
|
||||
val highIeReg = Reg(init = UInt(0, c.width))
|
||||
val lowIeReg = Reg(init = UInt(0, c.width))
|
||||
val riseIeReg = Reg(init = UInt(0, c.width))
|
||||
val fallIeReg = Reg(init = UInt(0, c.width))
|
||||
val highIpReg = Reg(init = UInt(0, c.width))
|
||||
val lowIpReg = Reg(init = UInt(0, c.width))
|
||||
val riseIpReg = Reg(init = UInt(0, c.width))
|
||||
val fallIpReg = Reg(init = UInt(0, c.width))
|
||||
|
||||
// HW IO Function
|
||||
val iofEnReg = Module(new AsyncResetRegVec(c.width, 0))
|
||||
val iofSelReg = Reg(init = UInt(0, c.width))
|
||||
|
||||
// Invert Output
|
||||
val xorReg = Reg(init = UInt(0, c.width))
|
||||
|
||||
//--------------------------------------------------
|
||||
// CSR Access Logic (most of this section is boilerplate)
|
||||
// -------------------------------------------------
|
||||
|
||||
val rise = ~valueReg & inSyncReg;
|
||||
val fall = valueReg & ~inSyncReg;
|
||||
|
||||
// Note that these are out of order.
|
||||
regmap(
|
||||
GPIOCtrlRegs.value -> Seq(RegField.r(c.width, valueReg)),
|
||||
GPIOCtrlRegs.output_en -> Seq(RegField.rwReg(c.width, oeReg.io)),
|
||||
GPIOCtrlRegs.rise_ie -> Seq(RegField(c.width, riseIeReg)),
|
||||
GPIOCtrlRegs.rise_ip -> Seq(RegField.w1ToClear(c.width, riseIpReg, rise)),
|
||||
GPIOCtrlRegs.fall_ie -> Seq(RegField(c.width, fallIeReg)),
|
||||
GPIOCtrlRegs.fall_ip -> Seq(RegField.w1ToClear(c.width, fallIpReg, fall)),
|
||||
GPIOCtrlRegs.high_ie -> Seq(RegField(c.width, highIeReg)),
|
||||
GPIOCtrlRegs.high_ip -> Seq(RegField.w1ToClear(c.width, highIpReg, valueReg)),
|
||||
GPIOCtrlRegs.low_ie -> Seq(RegField(c.width, lowIeReg)),
|
||||
GPIOCtrlRegs.low_ip -> Seq(RegField.w1ToClear(c.width,lowIpReg, ~valueReg)),
|
||||
GPIOCtrlRegs.port -> Seq(RegField(c.width, portReg)),
|
||||
GPIOCtrlRegs.pullup_en -> Seq(RegField.rwReg(c.width, pueReg.io)),
|
||||
GPIOCtrlRegs.iof_en -> Seq(RegField.rwReg(c.width, iofEnReg.io)),
|
||||
GPIOCtrlRegs.iof_sel -> Seq(RegField(c.width, iofSelReg)),
|
||||
GPIOCtrlRegs.drive -> Seq(RegField(c.width, dsReg)),
|
||||
GPIOCtrlRegs.input_en -> Seq(RegField.rwReg(c.width, ieReg.io)),
|
||||
GPIOCtrlRegs.out_xor -> Seq(RegField(c.width, xorReg))
|
||||
|
||||
)
|
||||
|
||||
//--------------------------------------------------
|
||||
// Actual Pinmux
|
||||
// -------------------------------------------------
|
||||
|
||||
val swPinCtrl = Wire(Vec(c.width, new GPIOPinCtrl()))
|
||||
|
||||
// This strips off the valid.
|
||||
val iof0Ctrl = Wire(Vec(c.width, new GPIOCtrl()))
|
||||
val iof1Ctrl = Wire(Vec(c.width, new GPIOCtrl()))
|
||||
|
||||
val iofCtrl = Wire(Vec(c.width, new GPIOCtrl()))
|
||||
val iofPlusSwPinCtrl = Wire(Vec(c.width, new GPIOPinCtrl()))
|
||||
|
||||
|
||||
for (pin <- 0 until c.width) {
|
||||
|
||||
// Software Pin Control
|
||||
swPinCtrl(pin).pue := pueReg.io.q(pin)
|
||||
swPinCtrl(pin).oval := portReg(pin)
|
||||
swPinCtrl(pin).oe := oeReg.io.q(pin)
|
||||
swPinCtrl(pin).ds := dsReg(pin)
|
||||
swPinCtrl(pin).ie := ieReg.io.q(pin)
|
||||
|
||||
// Allow SW Override for invalid inputs.
|
||||
iof0Ctrl(pin) <> swPinCtrl(pin)
|
||||
when (io.port.iof_0(pin).o.valid) {
|
||||
iof0Ctrl(pin) <> io.port.iof_0(pin).o
|
||||
}
|
||||
|
||||
iof1Ctrl(pin) <> swPinCtrl(pin)
|
||||
when (io.port.iof_1(pin).o.valid) {
|
||||
iof1Ctrl(pin) <> io.port.iof_1(pin).o
|
||||
}
|
||||
|
||||
// Select IOF 0 vs. IOF 1.
|
||||
iofCtrl(pin) <> Mux(iofSelReg(pin), iof1Ctrl(pin), iof0Ctrl(pin))
|
||||
|
||||
// Allow SW Override for things IOF doesn't control.
|
||||
iofPlusSwPinCtrl(pin) <> swPinCtrl(pin)
|
||||
iofPlusSwPinCtrl(pin) <> iofCtrl(pin)
|
||||
|
||||
// Final XOR & Pin Control
|
||||
val pre_xor: GPIOPinCtrl = Mux(iofEnReg.io.q(pin), iofPlusSwPinCtrl(pin), swPinCtrl(pin))
|
||||
io.port.pins(pin).o := pre_xor
|
||||
io.port.pins(pin).o.oval := pre_xor.oval ^ xorReg(pin)
|
||||
|
||||
// Generate Interrupts
|
||||
interrupts(pin) := (riseIpReg(pin) & riseIeReg(pin)) |
|
||||
(fallIpReg(pin) & fallIeReg(pin)) |
|
||||
(highIpReg(pin) & highIeReg(pin)) |
|
||||
(lowIpReg(pin) & lowIeReg(pin))
|
||||
|
||||
// Send Value to all consumers
|
||||
io.port.iof_0(pin).i.ival := inSyncReg(pin)
|
||||
io.port.iof_1(pin).i.ival := inSyncReg(pin)
|
||||
}
|
||||
}
|
||||
|
||||
object GPIOOutputPinCtrl {
|
||||
|
||||
def apply( pin: GPIOPin, signal: Bool,
|
||||
pue: Bool = Bool(false),
|
||||
ds: Bool = Bool(false),
|
||||
ie: Bool = Bool(false)
|
||||
): Unit = {
|
||||
pin.o.oval := signal
|
||||
pin.o.oe := Bool(true)
|
||||
pin.o.pue := pue
|
||||
pin.o.ds := ds
|
||||
pin.o.ie := ie
|
||||
}
|
||||
|
||||
def apply(pins: Vec[GPIOPin], signals: Bits,
|
||||
pue: Bool, ds: Bool, ie: Bool
|
||||
): Unit = {
|
||||
for ((signal, pin) <- (signals.toBools zip pins)) {
|
||||
apply(pin, signal, pue, ds, ie)
|
||||
}
|
||||
}
|
||||
|
||||
def apply(pins: Vec[GPIOPin], signals: Bits): Unit = apply(pins, signals,
|
||||
Bool(false), Bool(false), Bool(false))
|
||||
|
||||
}
|
||||
|
||||
object GPIOInputPinCtrl {
|
||||
|
||||
def apply (pin: GPIOPin, pue: Bool = Bool(false)): Bool = {
|
||||
pin.o.oval := Bool(false)
|
||||
pin.o.oe := Bool(false)
|
||||
pin.o.pue := pue
|
||||
pin.o.ds := Bool(false)
|
||||
pin.o.ie := Bool(true)
|
||||
|
||||
pin.i.ival
|
||||
}
|
||||
|
||||
def apply (pins: Vec[GPIOPin], pue: Bool): Vec[Bool] = {
|
||||
val signals = Wire(Vec.fill(pins.size)(Bool(false)))
|
||||
for ((signal, pin) <- (signals zip pins)) {
|
||||
signal := GPIOInputPinCtrl(pin, pue)
|
||||
}
|
||||
signals
|
||||
}
|
||||
|
||||
def apply (pins: Vec[GPIOPin]): Vec[Bool] = apply(pins, Bool(false))
|
||||
|
||||
}
|
||||
|
||||
// Magic TL2 Incantation to create a TL2 Slave
|
||||
class TLGPIO(p: Parameters, c: GPIOConfig)
|
||||
extends TLRegisterRouter(c.address, interrupts = c.width, beatBytes = p(PeripheryBusConfig).beatBytes)(
|
||||
new TLRegBundle(Tuple2(p, c), _) with GPIOBundle)(
|
||||
new TLRegModule(Tuple2(p, c), _, _) with GPIOModule)
|
22
src/main/scala/devices/gpio/GPIOCtrlRegs.scala
Normal file
22
src/main/scala/devices/gpio/GPIOCtrlRegs.scala
Normal file
@ -0,0 +1,22 @@
|
||||
// See LICENSE for license details.
|
||||
package sifive.blocks.devices.gpio
|
||||
|
||||
object GPIOCtrlRegs {
|
||||
val value = 0x00
|
||||
val input_en = 0x04
|
||||
val output_en = 0x08
|
||||
val port = 0x0c
|
||||
val pullup_en = 0x10
|
||||
val drive = 0x14
|
||||
val rise_ie = 0x18
|
||||
val rise_ip = 0x1c
|
||||
val fall_ie = 0x20
|
||||
val fall_ip = 0x24
|
||||
val high_ie = 0x28
|
||||
val high_ip = 0x2c
|
||||
val low_ie = 0x30
|
||||
val low_ip = 0x34
|
||||
val iof_en = 0x38
|
||||
val iof_sel = 0x3c
|
||||
val out_xor = 0x40
|
||||
}
|
28
src/main/scala/devices/gpio/GPIOPeriphery.scala
Normal file
28
src/main/scala/devices/gpio/GPIOPeriphery.scala
Normal file
@ -0,0 +1,28 @@
|
||||
// See LICENSE for license details.
|
||||
package sifive.blocks.devices.gpio
|
||||
|
||||
import Chisel._
|
||||
import diplomacy.LazyModule
|
||||
import rocketchip.{TopNetwork,TopNetworkModule}
|
||||
import uncore.tilelink2.TLFragmenter
|
||||
|
||||
trait PeripheryGPIO {
|
||||
this: TopNetwork { val gpioConfig: GPIOConfig } =>
|
||||
val gpio = LazyModule(new TLGPIO(p, gpioConfig))
|
||||
gpio.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node)
|
||||
intBus.intnode := gpio.intnode
|
||||
}
|
||||
|
||||
trait PeripheryGPIOBundle {
|
||||
this: { val gpioConfig: GPIOConfig } =>
|
||||
val gpio = new GPIOPortIO(gpioConfig)
|
||||
}
|
||||
|
||||
trait PeripheryGPIOModule {
|
||||
this: TopNetworkModule {
|
||||
val gpioConfig: GPIOConfig
|
||||
val outer: PeripheryGPIO
|
||||
val io: PeripheryGPIOBundle
|
||||
} =>
|
||||
io.gpio <> outer.gpio.module.io.port
|
||||
}
|
43
src/main/scala/devices/gpio/JTAG.scala
Normal file
43
src/main/scala/devices/gpio/JTAG.scala
Normal file
@ -0,0 +1,43 @@
|
||||
// See LICENSE for license details.
|
||||
package sifive.blocks.devices.gpio
|
||||
|
||||
import Chisel._
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// SPI, UART, etc are with their
|
||||
// respective packages,
|
||||
// This file is for those that don't seem to have a good place
|
||||
// to put them otherwise.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
import config._
|
||||
import junctions.{JTAGIO}
|
||||
|
||||
class JTAGPinsIO extends Bundle {
|
||||
|
||||
val TCK = new GPIOPin()
|
||||
val TMS = new GPIOPin()
|
||||
val TDI = new GPIOPin()
|
||||
val TDO = new GPIOPin()
|
||||
val TRST_n = new GPIOPin()
|
||||
|
||||
}
|
||||
|
||||
class JTAGGPIOPort(drvTdo: Boolean = false)(implicit p: Parameters) extends Module {
|
||||
|
||||
val io = new Bundle {
|
||||
val jtag = new JTAGIO(drvTdo)
|
||||
val pins = new JTAGPinsIO()
|
||||
}
|
||||
|
||||
io.jtag.TCK := GPIOInputPinCtrl(io.pins.TCK, pue = Bool(true)).asClock
|
||||
io.jtag.TMS := GPIOInputPinCtrl(io.pins.TMS, pue = Bool(true))
|
||||
io.jtag.TDI := GPIOInputPinCtrl(io.pins.TDI, pue = Bool(true))
|
||||
io.jtag.TRST := ~GPIOInputPinCtrl(io.pins.TRST_n, pue = Bool(true))
|
||||
|
||||
GPIOOutputPinCtrl(io.pins.TDO, io.jtag.TDO)
|
||||
if (drvTdo) {
|
||||
io.pins.TDO.o.oe := io.jtag.DRV_TDO.get
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user