1
0
Fork 0
sifive-blocks/src/main/scala/devices/spi/SPIFlash.scala

163 lines
4.3 KiB
Scala

// 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
}
}
}
}