1
0

Initial commit.

This commit is contained in:
SiFive
2016-11-29 04:08:44 -08:00
commit 7916ef5249
42 changed files with 3763 additions and 0 deletions

View File

@ -0,0 +1,40 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
class SPIInnerIO(c: SPIConfigBase) extends SPILinkIO(c) {
val lock = Bool(OUTPUT)
}
class SPIArbiter(c: SPIConfigBase, n: Int) extends Module {
val io = new Bundle {
val inner = Vec(n, new SPIInnerIO(c)).flip
val outer = new SPILinkIO(c)
val sel = UInt(INPUT, log2Up(n))
}
val sel = Reg(init = Vec(Bool(true) +: Seq.fill(n-1)(Bool(false))))
io.outer.tx.valid := Mux1H(sel, io.inner.map(_.tx.valid))
io.outer.tx.bits := Mux1H(sel, io.inner.map(_.tx.bits))
io.outer.cnt := Mux1H(sel, io.inner.map(_.cnt))
io.outer.fmt := Mux1H(sel, io.inner.map(_.fmt))
io.outer.cs := Mux1H(sel, io.inner.map(_.cs))
(io.inner zip sel).foreach { case (inner, s) =>
inner.tx.ready := io.outer.tx.ready && s
inner.rx.valid := io.outer.rx.valid && s
inner.rx.bits := io.outer.rx.bits
inner.active := io.outer.active && s
}
val nsel = Vec.tabulate(n)(io.sel === UInt(_))
val lock = Mux1H(sel, io.inner.map(_.lock))
when (!lock) {
sel := nsel
when (sel.asUInt =/= nsel.asUInt) {
io.outer.cs.clear := Bool(true)
}
}
}

View File

@ -0,0 +1,106 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
abstract class SPIBundle(val c: SPIConfigBase) extends Bundle {
override def cloneType: SPIBundle.this.type =
this.getClass.getConstructors.head.newInstance(c).asInstanceOf[this.type]
}
class SPIDataIO extends Bundle {
val i = Bool(INPUT)
val o = Bool(OUTPUT)
val oe = Bool(OUTPUT)
}
class SPIPortIO(c: SPIConfigBase) extends SPIBundle(c) {
val sck = Bool(OUTPUT)
val dq = Vec(4, new SPIDataIO)
val cs = Vec(c.csWidth, Bool(OUTPUT))
}
trait HasSPIProtocol {
val proto = Bits(width = SPIProtocol.width)
}
trait HasSPIEndian {
val endian = Bits(width = SPIEndian.width)
}
class SPIFormat(c: SPIConfigBase) extends SPIBundle(c)
with HasSPIProtocol
with HasSPIEndian {
val iodir = Bits(width = SPIDirection.width)
}
trait HasSPILength extends SPIBundle {
val len = UInt(width = c.lengthBits)
}
class SPIClocking(c: SPIConfigBase) extends SPIBundle(c) {
val div = UInt(width = c.divisorBits)
val pol = Bool()
val pha = Bool()
}
class SPIChipSelect(c: SPIConfigBase) extends SPIBundle(c) {
val id = UInt(width = c.csIdBits)
val dflt = Vec(c.csWidth, Bool())
def toggle(en: Bool): Vec[Bool] = {
val mask = en << id
val out = Cat(dflt.reverse) ^ mask
Vec.tabulate(c.csWidth)(out(_))
}
}
trait HasSPICSMode {
val mode = Bits(width = SPICSMode.width)
}
class SPIDelay(c: SPIConfigBase) extends SPIBundle(c) {
val cssck = UInt(width = c.delayBits)
val sckcs = UInt(width = c.delayBits)
val intercs = UInt(width = c.delayBits)
val interxfr = UInt(width = c.delayBits)
}
class SPIWatermark(c: SPIConfigBase) extends SPIBundle(c) {
val tx = UInt(width = c.txDepthBits)
val rx = UInt(width = c.rxDepthBits)
}
class SPIControl(c: SPIConfigBase) extends SPIBundle(c) {
val fmt = new SPIFormat(c) with HasSPILength
val sck = new SPIClocking(c)
val cs = new SPIChipSelect(c) with HasSPICSMode
val dla = new SPIDelay(c)
val wm = new SPIWatermark(c)
}
object SPIControl {
def init(c: SPIConfigBase): SPIControl = {
val ctrl = Wire(new SPIControl(c))
ctrl.fmt.proto := SPIProtocol.Single
ctrl.fmt.iodir := SPIDirection.Rx
ctrl.fmt.endian := SPIEndian.MSB
ctrl.fmt.len := UInt(math.min(c.frameBits, 8))
ctrl.sck.div := UInt(3)
ctrl.sck.pol := Bool(false)
ctrl.sck.pha := Bool(false)
ctrl.cs.id := UInt(0)
ctrl.cs.dflt.foreach { _ := Bool(true) }
ctrl.cs.mode := SPICSMode.Auto
ctrl.dla.cssck := UInt(1)
ctrl.dla.sckcs := UInt(1)
ctrl.dla.intercs := UInt(1)
ctrl.dla.interxfr := UInt(0)
ctrl.wm.tx := UInt(0)
ctrl.wm.rx := UInt(0)
ctrl
}
}
class SPIInterrupts extends Bundle {
val txwm = Bool()
val rxwm = Bool()
}

View File

@ -0,0 +1,33 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
object SPIProtocol {
val width = 2
val Single = UInt(0, width)
val Dual = UInt(1, width)
val Quad = UInt(2, width)
val cases = Seq(Single, Dual, Quad)
def decode(x: UInt): Seq[Bool] = cases.map(_ === x)
}
object SPIDirection {
val width = 1
val Rx = UInt(0, width)
val Tx = UInt(1, width)
}
object SPIEndian {
val width = 1
val MSB = UInt(0, width)
val LSB = UInt(1, width)
}
object SPICSMode {
val width = 2
val Auto = UInt(0, width)
val Hold = UInt(2, width)
val Off = UInt(3, width)
}

View File

@ -0,0 +1,62 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
class SPIFIFOControl(c: SPIConfigBase) extends SPIBundle(c) {
val fmt = new SPIFormat(c) with HasSPILength
val cs = new Bundle with HasSPICSMode
val wm = new SPIWatermark(c)
}
class SPIFIFO(c: SPIConfigBase) extends Module {
val io = new Bundle {
val ctrl = new SPIFIFOControl(c).asInput
val link = new SPIInnerIO(c)
val tx = Decoupled(Bits(width = c.frameBits)).flip
val rx = Decoupled(Bits(width = c.frameBits))
val ip = new SPIInterrupts().asOutput
}
val txq = Module(new Queue(io.tx.bits, c.txDepth))
val rxq = Module(new Queue(io.rx.bits, c.rxDepth))
txq.io.enq <> io.tx
io.link.tx <> txq.io.deq
val fire_tx = io.link.tx.fire()
val fire_rx = io.link.rx.fire()
val rxen = Reg(init = Bool(false))
rxq.io.enq.valid := io.link.rx.valid && rxen
rxq.io.enq.bits := io.link.rx.bits
io.rx <> rxq.io.deq
when (fire_rx) {
rxen := Bool(false)
}
when (fire_tx) {
rxen := (io.link.fmt.iodir === SPIDirection.Rx)
}
val proto = SPIProtocol.decode(io.link.fmt.proto).zipWithIndex
val cnt_quot = Mux1H(proto.map { case (s, i) => s -> (io.ctrl.fmt.len >> i) })
val cnt_rmdr = Mux1H(proto.map { case (s, i) => s -> (io.ctrl.fmt.len(i, 0).orR) })
io.link.fmt <> io.ctrl.fmt
io.link.cnt := cnt_quot + cnt_rmdr
val cs_mode = RegNext(io.ctrl.cs.mode, SPICSMode.Auto)
val cs_mode_hold = (cs_mode === SPICSMode.Hold)
val cs_mode_off = (cs_mode === SPICSMode.Off)
val cs_update = (cs_mode =/= io.ctrl.cs.mode)
val cs_clear = !(cs_mode_hold || cs_mode_off)
io.link.cs.set := !cs_mode_off
io.link.cs.clear := cs_update || (fire_tx && cs_clear)
io.link.cs.hold := Bool(false)
io.link.lock := io.link.tx.valid || rxen
io.ip.txwm := (txq.io.count < io.ctrl.wm.tx)
io.ip.rxwm := (rxq.io.count > io.ctrl.wm.rx)
}

View File

@ -0,0 +1,162 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
class SPIFlashInsn(c: SPIFlashConfigBase) extends SPIBundle(c) {
val cmd = new Bundle with HasSPIProtocol {
val code = Bits(width = c.insnCmdBits)
val en = Bool()
}
val addr = new Bundle with HasSPIProtocol {
val len = UInt(width = c.insnAddrLenBits)
}
val pad = new Bundle {
val code = Bits(width = c.frameBits)
val cnt = Bits(width = c.insnPadLenBits)
}
val data = new Bundle with HasSPIProtocol
}
class SPIFlashControl(c: SPIFlashConfigBase) extends SPIBundle(c) {
val insn = new SPIFlashInsn(c)
val fmt = new Bundle with HasSPIEndian
}
object SPIFlashInsn {
def init(c: SPIFlashConfigBase): SPIFlashInsn = {
val insn = Wire(new SPIFlashInsn(c))
insn.cmd.en := Bool(true)
insn.cmd.code := Bits(0x03)
insn.cmd.proto := SPIProtocol.Single
insn.addr.len := UInt(3)
insn.addr.proto := SPIProtocol.Single
insn.pad.cnt := UInt(0)
insn.pad.code := Bits(0)
insn.data.proto := SPIProtocol.Single
insn
}
}
class SPIFlashAddr(c: SPIFlashConfigBase) extends SPIBundle(c) {
val next = UInt(width = c.insnAddrBits)
val hold = UInt(width = c.insnAddrBits)
}
class SPIFlashMap(c: SPIFlashConfigBase) extends Module {
val io = new Bundle {
val en = Bool(INPUT)
val ctrl = new SPIFlashControl(c).asInput
val addr = Decoupled(new SPIFlashAddr(c)).flip
val data = Decoupled(UInt(width = c.frameBits))
val link = new SPIInnerIO(c)
}
val addr = io.addr.bits.hold + UInt(1)
val merge = io.link.active && (io.addr.bits.next === addr)
private val insn = io.ctrl.insn
io.link.tx.valid := Bool(true)
io.link.fmt.proto := insn.addr.proto
io.link.fmt.iodir := SPIDirection.Tx
io.link.fmt.endian := io.ctrl.fmt.endian
io.link.cnt := Mux1H(
SPIProtocol.decode(io.link.fmt.proto).zipWithIndex.map {
case (s, i) => (s -> UInt(c.frameBits >> i))
})
io.link.cs.set := Bool(true)
io.link.cs.clear := Bool(false)
io.link.cs.hold := Bool(true)
io.link.lock := Bool(true)
io.addr.ready := Bool(false)
io.data.valid := Bool(false)
io.data.bits := io.link.rx.bits
val cnt = Reg(UInt(width = math.max(c.insnPadLenBits, c.insnAddrLenBits)))
val cnt_en = Wire(init = Bool(false))
val cnt_cmp = (0 to c.insnAddrBytes).map(cnt === UInt(_))
val cnt_zero = cnt_cmp(0)
val cnt_last = cnt_cmp(1) && io.link.tx.ready
val cnt_done = cnt_last || cnt_zero
when (cnt_en) {
io.link.tx.valid := !cnt_zero
when (io.link.tx.fire()) {
cnt := cnt - UInt(1)
}
}
val (s_idle :: s_cmd :: s_addr :: s_pad :: s_data_pre :: s_data_post :: Nil) = Enum(UInt(), 6)
val state = Reg(init = s_idle)
switch (state) {
is (s_idle) {
io.link.tx.valid := Bool(false)
when (io.en) {
io.addr.ready := Bool(true)
when (io.addr.valid) {
when (merge) {
state := s_data_pre
} .otherwise {
state := Mux(insn.cmd.en, s_cmd, s_addr)
io.link.cs.clear := Bool(true)
}
} .otherwise {
io.link.lock := Bool(false)
}
} .otherwise {
io.data.valid := io.addr.valid
io.addr.ready := io.data.ready
io.data.bits := UInt(0)
io.link.lock := Bool(false)
}
}
is (s_cmd) {
io.link.fmt.proto := insn.cmd.proto
io.link.tx.bits := insn.cmd.code
when (io.link.tx.ready) {
state := s_addr
cnt := insn.addr.len
}
}
is (s_addr) {
io.link.tx.bits := Mux1H(cnt_cmp.tail.zipWithIndex.map {
case (s, i) =>
val n = i * c.frameBits
val m = n + (c.frameBits - 1)
s -> io.addr.bits.hold(m, n)
})
cnt_en := Bool(true)
when (cnt_done) {
state := s_pad
}
}
is (s_pad) {
io.link.cnt := insn.pad.cnt
io.link.tx.bits := insn.pad.code
when (io.link.tx.ready) {
state := s_data_pre
}
}
is (s_data_pre) {
io.link.fmt.proto := insn.data.proto
io.link.fmt.iodir := SPIDirection.Rx
when (io.link.tx.ready) {
state := s_data_post
}
}
is (s_data_post) {
io.link.tx.valid := Bool(false)
io.data.valid := io.link.rx.valid
when (io.data.fire()) {
state := s_idle
}
}
}
}

View File

@ -0,0 +1,121 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
class SPILinkIO(c: SPIConfigBase) extends SPIBundle(c) {
val tx = Decoupled(Bits(width = c.frameBits))
val rx = Valid(Bits(width = c.frameBits)).flip
val cnt = UInt(OUTPUT, c.countBits)
val fmt = new SPIFormat(c).asOutput
val cs = new Bundle {
val set = Bool(OUTPUT)
val clear = Bool(OUTPUT) // Deactivate CS
val hold = Bool(OUTPUT) // Supress automatic CS deactivation
}
val active = Bool(INPUT)
}
class SPIMedia(c: SPIConfigBase) extends Module {
val io = new Bundle {
val port = new SPIPortIO(c)
val ctrl = new Bundle {
val sck = new SPIClocking(c).asInput
val dla = new SPIDelay(c).asInput
val cs = new SPIChipSelect(c).asInput
}
val link = new SPILinkIO(c).flip
}
val phy = Module(new SPIPhysical(c))
phy.io.ctrl.sck := io.ctrl.sck
phy.io.ctrl.fmt := io.link.fmt
private val op = phy.io.op
op.valid := Bool(true)
op.bits.fn := SPIMicroOp.Delay
op.bits.stb := Bool(false)
op.bits.cnt := io.link.cnt
op.bits.data := io.link.tx.bits
val cs = Reg(io.ctrl.cs)
val cs_set = Reg(Bool())
val cs_active = io.ctrl.cs.toggle(io.link.cs.set)
val cs_update = (cs_active.asUInt =/= cs.dflt.asUInt)
val clear = Reg(init = Bool(false))
val cs_assert = Reg(init = Bool(false))
val cs_deassert = clear || (cs_update && !io.link.cs.hold)
clear := clear || (io.link.cs.clear && cs_assert)
val continuous = (io.ctrl.dla.interxfr === UInt(0))
io.port.sck := phy.io.port.sck
io.port.dq <> phy.io.port.dq
io.port.cs := cs.dflt
io.link.rx := phy.io.rx
io.link.tx.ready := Bool(false)
io.link.active := cs_assert
val (s_main :: s_interxfr :: s_intercs :: Nil) = Enum(UInt(), 3)
val state = Reg(init = s_main)
switch (state) {
is (s_main) {
when (cs_assert) {
when (cs_deassert) {
op.bits.cnt := io.ctrl.dla.sckcs
when (op.ready) {
state := s_intercs
}
} .otherwise {
op.bits.fn := SPIMicroOp.Transfer
op.bits.stb := Bool(true)
op.valid := io.link.tx.valid
io.link.tx.ready := op.ready
when (op.fire()) {
state := s_interxfr
}
}
} .elsewhen (io.link.tx.valid) {
// Assert CS
op.bits.cnt := io.ctrl.dla.cssck
when (op.ready) {
cs_assert := Bool(true)
cs_set := io.link.cs.set
cs.dflt := cs_active
}
} .otherwise {
// Idle
op.bits.cnt := UInt(0)
op.bits.stb := Bool(true)
cs := io.ctrl.cs
}
}
is (s_interxfr) {
// Skip if interxfr delay is zero
op.valid := !continuous
op.bits.cnt := io.ctrl.dla.interxfr
when (op.ready || continuous) {
state := s_main
}
}
is (s_intercs) {
// Deassert CS
op.bits.cnt := io.ctrl.dla.intercs
op.bits.stb := Bool(true)
cs_assert := Bool(false)
clear := Bool(false)
when (op.ready) {
cs.dflt := cs.toggle(cs_set)
state := s_main
}
}
}
}

View File

@ -0,0 +1,57 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
import diplomacy.LazyModule
import uncore.tilelink2._
import rocketchip.{TopNetwork,TopNetworkModule}
trait PeripherySPI {
this: TopNetwork { val spiConfigs: Seq[SPIConfig] } =>
val spiDevices = (spiConfigs.zipWithIndex) map {case (c, i) =>
val spi = LazyModule(new TLSPI(c) { override lazy val valName = Some(s"spi$i") } )
spi.rnode := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node)
intBus.intnode := spi.intnode
spi
}
}
trait PeripherySPIBundle {
this: { val spiConfigs: Seq[SPIConfig] } =>
val spi_bc = spiConfigs.map(_.bc).reduce(_.union(_))
val spis = Vec(spiConfigs.size, new SPIPortIO(spi_bc.toSPIConfig))
}
trait PeripherySPIModule {
this: TopNetworkModule {
val spiConfigs: Seq[SPIConfig]
val outer: PeripherySPI
val io: PeripherySPIBundle
} =>
(io.spis zip outer.spiDevices).foreach { case (io, device) =>
io <> device.module.io.port
}
}
trait PeripherySPIFlash {
this: TopNetwork { val spiFlashConfig: SPIFlashConfig } =>
val qspi = LazyModule(new TLSPIFlash(spiFlashConfig))
qspi.rnode := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node)
qspi.fnode := TLFragmenter(1, cacheBlockBytes)(TLWidthWidget(peripheryBusConfig.beatBytes)(peripheryBus.node))
intBus.intnode := qspi.intnode
}
trait PeripherySPIFlashBundle {
this: { val spiFlashConfig: SPIFlashConfig } =>
val qspi = new SPIPortIO(spiFlashConfig)
}
trait PeripherySPIFlashModule {
this: TopNetworkModule {
val spiConfigs: Seq[SPIConfig]
val outer: PeripherySPIFlash
val io: PeripherySPIFlashBundle
} =>
io.qspi <> outer.qspi.module.io.port
}

View File

@ -0,0 +1,157 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
import sifive.blocks.util.ShiftRegisterInit
class SPIMicroOp(c: SPIConfigBase) extends SPIBundle(c) {
val fn = Bits(width = 1)
val stb = Bool()
val cnt = UInt(width = c.countBits)
val data = Bits(width = c.frameBits)
}
object SPIMicroOp {
val Transfer = UInt(0, 1)
val Delay = UInt(1, 1)
}
class SPIPhyControl(c: SPIConfigBase) extends SPIBundle(c) {
val sck = new SPIClocking(c)
val fmt = new SPIFormat(c)
}
class SPIPhysical(c: SPIConfigBase) extends Module {
val io = new SPIBundle(c) {
val port = new SPIPortIO(c)
val ctrl = new SPIPhyControl(c).asInput
val op = Decoupled(new SPIMicroOp(c)).flip
val rx = Valid(Bits(width = c.frameBits))
}
private val op = io.op.bits
val ctrl = Reg(io.ctrl)
val proto = SPIProtocol.decode(ctrl.fmt.proto)
val accept = Wire(init = Bool(false))
val sample = Wire(init = Bool(false))
val setup = Wire(init = Bool(false))
val last = Wire(init = Bool(false))
// Delayed versions
val setup_d = Reg(next = setup)
val sample_d = ShiftRegisterInit(sample, c.sampleDelay, Bool(false))
val last_d = ShiftRegisterInit(last, c.sampleDelay, Bool(false))
val scnt = Reg(init = UInt(0, c.countBits))
val tcnt = Reg(io.ctrl.sck.div)
val stop = (scnt === UInt(0))
val beat = (tcnt === UInt(0))
val decr = Mux(beat, scnt, tcnt) - UInt(1)
val sched = Wire(init = beat)
tcnt := Mux(sched, ctrl.sck.div, decr)
val sck = Reg(Bool())
val cref = Reg(init = Bool(true))
val cinv = ctrl.sck.pha ^ ctrl.sck.pol
private def convert(data: UInt, fmt: SPIFormat) =
Mux(fmt.endian === SPIEndian.MSB, data, Cat(data.toBools))
val rxd = Cat(io.port.dq.reverse.map(_.i))
val samples = Seq(rxd(1), rxd(1, 0), rxd)
val buffer = Reg(op.data)
val buffer_in = convert(io.op.bits.data, io.ctrl.fmt)
val shift = if (c.sampleDelay > 0) setup_d || (sample_d && stop) else sample_d
buffer := Mux1H(proto, samples.zipWithIndex.map { case (data, i) =>
val n = 1 << i
val m = c.frameBits -1
Cat(Mux(shift, buffer(m-n, 0), buffer(m, n)),
Mux(sample_d, data, buffer(n-1, 0)))
})
private def upper(x: UInt, n: Int) = x(c.frameBits-1, c.frameBits-n)
val txd = Reg(init = Bits(0, io.port.dq.size))
val txd_in = Mux(accept, upper(buffer_in, 4), upper(buffer, 4))
val txd_sel = SPIProtocol.decode(Mux(accept, io.ctrl.fmt.proto, ctrl.fmt.proto))
val txd_shf = (0 until txd_sel.size).map(i => txd_in(3, 4-(1<<i)))
when (setup) {
txd := Mux1H(txd_sel, txd_shf)
}
val tx = (ctrl.fmt.iodir === SPIDirection.Tx)
val txen_in = (proto.head +: proto.tail.map(_ && tx)).scanRight(Bool(false))(_ || _)
val txen = txen_in :+ txen_in.last
io.port.sck := sck
io.port.cs := Vec.fill(io.port.cs.size)(Bool(true)) // dummy
(io.port.dq zip (txd.toBools zip txen)).foreach {
case (dq, (o, oe)) =>
dq.o := o
dq.oe := oe
}
io.op.ready := Bool(false)
val done = Reg(init = Bool(true))
done := done || last_d
io.rx.valid := done
io.rx.bits := convert(buffer, ctrl.fmt)
val xfr = Reg(Bool())
when (stop) {
sched := Bool(true)
accept := Bool(true)
} .otherwise {
when (beat) {
cref := !cref
when (xfr) {
sck := cref ^ cinv
sample := cref
setup := !cref
}
when (!cref) {
scnt := decr
}
}
}
when (scnt === UInt(1)) {
last := beat && cref && xfr // Final sample
when (beat && !cref) { // Final shift
accept := Bool(true)
setup := Bool(false)
sck := ctrl.sck.pol
}
}
when (accept && done) {
io.op.ready := Bool(true)
when (io.op.valid) {
scnt := op.cnt
when (op.stb) {
ctrl.fmt := io.ctrl.fmt
}
xfr := Bool(false)
switch (op.fn) {
is (SPIMicroOp.Transfer) {
buffer := buffer_in
sck := cinv
setup := Bool(true)
done := (op.cnt === UInt(0))
xfr := Bool(true)
}
is (SPIMicroOp.Delay) {
when (op.stb) {
sck := io.ctrl.sck.pol
ctrl.sck := io.ctrl.sck
}
}
}
}
}
}

View File

@ -0,0 +1,34 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
import sifive.blocks.devices.gpio.{GPIOPin, GPIOOutputPinCtrl, GPIOInputPinCtrl}
class SPIPinsIO(c: SPIConfigBase) extends SPIBundle(c) {
val sck = new GPIOPin
val dq = Vec(4, new GPIOPin)
val cs = Vec(c.csWidth, new GPIOPin)
}
class SPIGPIOPort(c: SPIConfigBase, syncStages: Int = 0, driveStrength: Bool = Bool(false)) extends Module {
val io = new SPIBundle(c) {
val spi = new SPIPortIO(c).flip
val pins = new SPIPinsIO(c)
}
GPIOOutputPinCtrl(io.pins.sck, io.spi.sck, ds = driveStrength)
GPIOOutputPinCtrl(io.pins.dq, Bits(0, io.spi.dq.size))
(io.pins.dq zip io.spi.dq).foreach {
case (p, s) =>
p.o.oval := s.o
p.o.oe := s.oe
p.o.ie := ~s.oe
p.o.pue := Bool(true)
p.o.ds := driveStrength
s.i := ShiftRegister(p.i.ival, syncStages)
}
GPIOOutputPinCtrl(io.pins.cs, io.spi.cs.asUInt)
io.pins.cs.foreach(_.o.ds := driveStrength)
}

View File

@ -0,0 +1,30 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
object SPICRs {
val sckdiv = 0x00
val sckmode = 0x04
val csid = 0x10
val csdef = 0x14
val csmode = 0x18
val dcssck = 0x28
val dsckcs = 0x2a
val dintercs = 0x2c
val dinterxfr = 0x2e
val fmt = 0x40
val len = 0x42
val txfifo = 0x48
val rxfifo = 0x4c
val txmark = 0x50
val rxmark = 0x54
val insnmode = 0x60
val insnfmt = 0x64
val insnproto = 0x65
val insncmd = 0x66
val insnpad = 0x67
val ie = 0x70
val ip = 0x74
}

View File

@ -0,0 +1,132 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
import config._
import uncore.tilelink2._
import diplomacy._
import regmapper._
import junctions._
import rocketchip.PeripheryBusConfig
import sifive.blocks.util.{NonBlockingEnqueue, NonBlockingDequeue}
trait SPIConfigBase {
val rAddress: BigInt
val rSize: BigInt
val rxDepth: Int
val txDepth: Int
val csWidth: Int
val frameBits: Int
val delayBits: Int
val divisorBits: Int
val sampleDelay: Int
lazy val csIdBits = log2Up(csWidth)
lazy val lengthBits = log2Floor(frameBits) + 1
lazy val countBits = math.max(lengthBits, delayBits)
lazy val txDepthBits = log2Floor(txDepth) + 1
lazy val rxDepthBits = log2Floor(rxDepth) + 1
lazy val bc = new SPIBundleConfig(csWidth)
}
case class SPIConfig(
rAddress: BigInt,
rSize: BigInt = 0x1000,
rxDepth: Int = 8,
txDepth: Int = 8,
csWidth: Int = 1,
frameBits: Int = 8,
delayBits: Int = 8,
divisorBits: Int = 12,
sampleDelay: Int = 2)
extends SPIConfigBase {
require(frameBits >= 4)
require(sampleDelay >= 0)
}
case class SPIBundleConfig(csWidth: Int)
{
def union(that: SPIBundleConfig): SPIBundleConfig =
SPIBundleConfig(scala.math.max(csWidth, that.csWidth))
def toSPIConfig: SPIConfig = new SPIConfig(rAddress = -1,
csWidth = csWidth)
}
class SPITopBundle(val i: Vec[Vec[Bool]], val r: Vec[TLBundle]) extends Bundle
class SPITopModule[B <: SPITopBundle](c: SPIConfigBase, bundle: => B, outer: TLSPIBase)
extends LazyModuleImp(outer) {
val io = new Bundle {
val port = new SPIPortIO(c)
val tl = bundle
}
val ctrl = Reg(init = SPIControl.init(c))
val fifo = Module(new SPIFIFO(c))
val mac = Module(new SPIMedia(c))
io.port <> mac.io.port
fifo.io.ctrl.fmt := ctrl.fmt
fifo.io.ctrl.cs <> ctrl.cs
fifo.io.ctrl.wm := ctrl.wm
mac.io.ctrl.sck := ctrl.sck
mac.io.ctrl.dla := ctrl.dla
mac.io.ctrl.cs <> ctrl.cs
val ie = Reg(init = new SPIInterrupts().fromBits(Bits(0)))
val ip = fifo.io.ip
io.tl.i(0)(0) := (ip.txwm && ie.txwm) || (ip.rxwm && ie.rxwm)
protected val regmapBase = Seq(
SPICRs.sckdiv -> Seq(RegField(c.divisorBits, ctrl.sck.div)),
SPICRs.sckmode -> Seq(
RegField(1, ctrl.sck.pha),
RegField(1, ctrl.sck.pol)),
SPICRs.csid -> Seq(RegField(c.csIdBits, ctrl.cs.id)),
SPICRs.csdef -> ctrl.cs.dflt.map(x => RegField(1, x)),
SPICRs.csmode -> Seq(RegField(SPICSMode.width, ctrl.cs.mode)),
SPICRs.dcssck -> Seq(RegField(c.delayBits, ctrl.dla.cssck)),
SPICRs.dsckcs -> Seq(RegField(c.delayBits, ctrl.dla.sckcs)),
SPICRs.dintercs -> Seq(RegField(c.delayBits, ctrl.dla.intercs)),
SPICRs.dinterxfr -> Seq(RegField(c.delayBits, ctrl.dla.interxfr)),
SPICRs.fmt -> Seq(
RegField(SPIProtocol.width, ctrl.fmt.proto),
RegField(SPIEndian.width, ctrl.fmt.endian),
RegField(SPIDirection.width, ctrl.fmt.iodir)),
SPICRs.len -> Seq(RegField(c.lengthBits, ctrl.fmt.len)),
SPICRs.txfifo -> NonBlockingEnqueue(fifo.io.tx),
SPICRs.rxfifo -> NonBlockingDequeue(fifo.io.rx),
SPICRs.txmark -> Seq(RegField(c.txDepthBits, ctrl.wm.tx)),
SPICRs.rxmark -> Seq(RegField(c.rxDepthBits, ctrl.wm.rx)),
SPICRs.ie -> Seq(
RegField(1, ie.txwm),
RegField(1, ie.rxwm)),
SPICRs.ip -> Seq(
RegField.r(1, ip.txwm),
RegField.r(1, ip.rxwm)))
}
abstract class TLSPIBase(c: SPIConfigBase)(implicit val p: Parameters) extends LazyModule {
require(isPow2(c.rSize))
val rnode = TLRegisterNode(address = AddressSet(c.rAddress, c.rSize-1), beatBytes = p(PeripheryBusConfig).beatBytes)
val intnode = IntSourceNode(1)
}
class TLSPI(c: SPIConfig)(implicit p: Parameters) extends TLSPIBase(c)(p) {
lazy val module = new SPITopModule(c, new SPITopBundle(intnode.bundleOut, rnode.bundleIn), this) {
mac.io.link <> fifo.io.link
rnode.regmap(regmapBase:_*)
}
}

View File

@ -0,0 +1,114 @@
// See LICENSE for license details.
package sifive.blocks.devices.spi
import Chisel._
import config._
import diplomacy._
import regmapper._
import uncore.tilelink2._
trait SPIFlashConfigBase extends SPIConfigBase {
val fAddress: BigInt
val fSize: BigInt
val insnAddrBytes: Int
val insnPadLenBits: Int
lazy val insnCmdBits = frameBits
lazy val insnAddrBits = insnAddrBytes * frameBits
lazy val insnAddrLenBits = log2Floor(insnAddrBytes) + 1
}
case class SPIFlashConfig(
rAddress: BigInt,
fAddress: BigInt,
rSize: BigInt = 0x1000,
fSize: BigInt = 0x20000000,
rxDepth: Int = 8,
txDepth: Int = 8,
csWidth: Int = 1,
delayBits: Int = 8,
divisorBits: Int = 12,
sampleDelay: Int = 2)
extends SPIFlashConfigBase {
val frameBits = 8
val insnAddrBytes = 4
val insnPadLenBits = 4
require(insnPadLenBits <= delayBits)
require(sampleDelay >= 0)
}
class SPIFlashTopBundle(i: Vec[Vec[Bool]], r: Vec[TLBundle], val f: Vec[TLBundle]) extends SPITopBundle(i, r)
class SPIFlashTopModule[B <: SPIFlashTopBundle]
(c: SPIFlashConfigBase, bundle: => B, outer: TLSPIFlashBase)
extends SPITopModule(c, bundle, outer) {
val flash = Module(new SPIFlashMap(c))
val arb = Module(new SPIArbiter(c, 2))
private val f = io.tl.f.head
// Tie unused channels
f.b.valid := Bool(false)
f.c.ready := Bool(true)
f.e.ready := Bool(true)
val a = Reg(f.a.bits)
val a_msb = log2Ceil(c.fSize) - 1
when (f.a.fire()) {
a := f.a.bits
}
flash.io.addr.bits.next := f.a.bits.address(a_msb, 0)
flash.io.addr.bits.hold := a.address(a_msb, 0)
flash.io.addr.valid := f.a.valid
f.a.ready := flash.io.addr.ready
f.d.bits := outer.fnode.edgesIn.head.AccessAck(a, UInt(0), flash.io.data.bits)
f.d.valid := flash.io.data.valid
flash.io.data.ready := f.d.ready
val insn = Reg(init = SPIFlashInsn.init(c))
val flash_en = Reg(init = Bool(true))
flash.io.ctrl.insn := insn
flash.io.ctrl.fmt <> ctrl.fmt
flash.io.en := flash_en
arb.io.sel := !flash_en
protected val regmapFlash = Seq(
SPICRs.insnmode -> Seq(RegField(1, flash_en)),
SPICRs.insnfmt -> Seq(
RegField(1, insn.cmd.en),
RegField(c.insnAddrLenBits, insn.addr.len),
RegField(c.insnPadLenBits, insn.pad.cnt)),
SPICRs.insnproto -> Seq(
RegField(SPIProtocol.width, insn.cmd.proto),
RegField(SPIProtocol.width, insn.addr.proto),
RegField(SPIProtocol.width, insn.data.proto)),
SPICRs.insncmd -> Seq(RegField(c.insnCmdBits, insn.cmd.code)),
SPICRs.insnpad -> Seq(RegField(c.frameBits, insn.pad.code)))
}
abstract class TLSPIFlashBase(c: SPIFlashConfigBase)(implicit p: Parameters) extends TLSPIBase(c)(p) {
require(isPow2(c.fSize))
val fnode = TLManagerNode(1, TLManagerParameters(
address = Seq(AddressSet(c.fAddress, c.fSize-1)),
regionType = RegionType.UNCACHED,
executable = true,
supportsGet = TransferSizes(1, 1),
fifoId = Some(0)))
}
class TLSPIFlash(c: SPIFlashConfig)(implicit p: Parameters) extends TLSPIFlashBase(c)(p) {
lazy val module = new SPIFlashTopModule(c,
new SPIFlashTopBundle(intnode.bundleOut, rnode.bundleIn, fnode.bundleIn), this) {
arb.io.inner(0) <> flash.io.link
arb.io.inner(1) <> fifo.io.link
mac.io.link <> arb.io.outer
rnode.regmap(regmapBase ++ regmapFlash:_*)
}
}