Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/main/scala/devices/spi/SPIArbiter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,21 @@ class SPIInnerIO(c: SPIParamsBase) extends SPILinkIO(c) {
val lock = Output(Bool())
}


/** Arbitor for SPI FlashMap and SPI FIFO
*
* {{{
* Input 0: SPI FlashMap
* Input 1: SPI FIFO
* Output: to SPI Media
* Select signal: come from SoC
* }}}
*/
class SPIArbiter(c: SPIParamsBase, n: Int) extends Module {
val io = IO(new Bundle {
/** from fifo and SPIFlashMap */
val inner = Flipped(Vec(n, new SPIInnerIO(c)))
/** to SPIMedia */
val outer = new SPILinkIO(c)
val sel = Input(UInt(log2Up(n).W))
})
Expand Down
7 changes: 7 additions & 0 deletions src/main/scala/devices/spi/SPIConsts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ object SPIProtocol {
def Quad = 2.U(width.W)

def cases = Seq(Single, Dual, Quad)
/** protocol select status
* {{{
* Single -> (1,0,0)
* Dual -> (0,1,0)
* Quad -> (0,0,1)
* }}}
*/
def decode(x: UInt): Seq[Bool] = cases.map(_ === x)
}

Expand Down
18 changes: 18 additions & 0 deletions src/main/scala/devices/spi/SPIFlash.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ import chisel3.util._

class SPIFlashInsn(c: SPIFlashParamsBase) extends SPIBundle(c) {
val cmd = new Bundle with HasSPIProtocol {
/** value of command byte */
val code = Bits(c.insnCmdBits.W)
/** enable sending of command */
val en = Bool()
}
val addr = new Bundle with HasSPIProtocol {
/** number of address bytes */
val len = UInt(c.insnAddrLenBits.W)
}
val pad = new Bundle {
/** first 8 bits to transmit during dummy cycles */
val code = Bits(c.frameBits.W)
/** number of dummy cycles */
val cnt = Bits(c.insnPadLenBits.W)
}
val data = new Bundle with HasSPIProtocol
Expand Down Expand Up @@ -46,12 +51,25 @@ class SPIFlashAddr(c: SPIFlashParamsBase) extends SPIBundle(c) {
val hold = UInt(c.insnAddrBits.W)
}

/** SPI SoC Controller
*
* This module processes the operation information from SoC in proper format and order
* before transmits them to SPI Media.
*/
class SPIFlashMap(c: SPIFlashParamsBase) extends Module {
val io = IO(new Bundle {
/** enable signal from SPI Top */
val en = Input(Bool())
/** control signals from SPI Top
*
* insn from top TL bus CRs
*/
val ctrl = Input(new SPIFlashControl(c))
/** falsh address info from TL bus */
val addr = Flipped(Decoupled(new SPIFlashAddr(c)))
/** transmits Flash response to TL bus */
val data = Decoupled(UInt(c.frameBits.W))
/** connected to SPI Aribitor */
val link = new SPIInnerIO(c)
})

Expand Down
28 changes: 28 additions & 0 deletions src/main/scala/devices/spi/SPIMedia.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,44 @@ class SPILinkIO(c: SPIParamsBase) extends SPIBundle(c) {
val disableOE = c.oeDisableDummy.option(Output(Bool())) // disable oe during dummy cycles in flash mode
}

/** Low level SPI protocol controller
*
* It recives operations from SPI Fifo(Tx) and outputs them in SPI Port(out).
* In the meantime, it recives response from slave device and uploads them to SPI Fifo(Rx).
* It maintain a statemachine for inter-frame delay and chip select function(control signals from TL bus).
*
* ==Component==
* - SPI Physical
* - SPI Physical control logic
*
* ==Datapass==
* {{{
* TL bus <-> SPI Fifo <-> SPI Media <-> SPI Port
* }}}
*
* ==ControlPass==
* {{{
* S_main : 3 possible job
* transfer op to SPIPhysical /(drive op.valid)
* assert CS
* idle
* s_interxfr: process inter-frame delay cycles
* s_intercs: Deassert CS
* }}}
*/
class SPIMedia(c: SPIParamsBase) extends Module {
val io = IO(new Bundle {
/** top level port */
val port = new SPIPortIO(c)
/** recives ctrl signals from SPITopModule */
val ctrl = new Bundle {
val sck = Input(new SPIClocking(c))
val dla = Input(new SPIDelay(c))
val cs = Input(new SPIChipSelect(c))
val extradel = Input(new SPIExtraDelay(c))
val sampledel = Input(new SPISampleDelay(c))
}
/** connected to SPI Fifo */
val link = Flipped(new SPILinkIO(c))
})

Expand Down
81 changes: 74 additions & 7 deletions src/main/scala/devices/spi/SPIPhysical.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,78 @@ class SPIPhyControl(c: SPIParamsBase) extends SPIBundle(c) {
val sampledel = new SPISampleDelay (c)
}

/** Basic SPI component to deal with data transfer.
*
* It recives operations from SPI Fifo(Tx) and outputs them in SPI Port(out).
* In the meantime, it recives feedback from slave device and uploads them to TL bus.
*
* ==Structure==
* - baud rate generator: counter to implement the target baud rate
* - scnt counter: counter of operation transfer
* - sample counter: delay counter for 'sample'
* - last counter: delay counter for 'last'
* - buffer control logic
* - rxd logic: transmit slave device response(dq.i) to buffer
* - txd logic: construct the data to be transmited on dq.o from buffer
*
* ==datapath==
* {{{
* dq.i -> rxd -> samples -> buffer -> txd -> dq.o
* operation data(parellel in):
* TL Interface -> buffer
* }}}
*/
class SPIPhysical(c: SPIParamsBase) extends Module {
val io = IO(new SPIBundle(c) {
/** top-level SPI port */
val port = new SPIPortIO(c)
/** recives ctrol signals from SPI TopModule */
val ctrl = Input(new SPIPhyControl(c))
/** recives operation from TL bus through SPI Fifo(Tx) */
val op = Flipped(Decoupled(new SPIMicroOp(c)))
/** transmits slave device response to SPI Fifo(Rx) */
val rx = Valid(Bits(c.frameBits.W))
})

private val op = io.op.bits
val ctrl = Reg(new SPIPhyControl(c))
val proto = SPIProtocol.decode(ctrl.fmt.proto)

/** indicates operations have been all accepted */
val accept = WireDefault(false.B)
/** sample counter enable signal
*
* follows [[cref]]
*/
val sample = WireDefault(false.B)
/** setup txd
*
* follows [[cref]]
* drives [[setup_d]]
*/
val setup = WireDefault(false.B)
/** enable for last delay counter */
val last = WireDefault(false.B)

val setup_d = RegNext(setup)

/** counter of operation transfer
*
* init = op.cnt when op.data comes in
* related ctrl signals: [[beat]]
*/
val scnt = RegInit(0.U(c.countBits.W))
/** Baud rate generator
*
* reset = io.ctrl.sck.div(3) when
* related ctrl signals: [[stop]]
*/
val tcnt = Reg(UInt(c.divisorBits.W))

val stop = (scnt === 0.U)
val beat = (tcnt === 0.U)

//Making a delay counter for 'sample'
// Making a delay counter for 'sample'
val totalCoarseDel = (io.ctrl.extradel.coarse + io.ctrl.sampledel.sd)
val sample_d = RegInit(false.B)
val del_cntr = RegInit(UInt(c.divisorBits.W), (c.defaultSampleDel).U)
Expand All @@ -87,7 +133,8 @@ class SPIPhysical(c: SPIParamsBase) extends Module {
}.otherwise {
sample_d := false.B
}
//Making a delay counter for 'last'
// Making a delay counter for 'last'
/** indicates the last set of data */
val last_d = RegInit(false.B)
val del_cntr_last = RegInit(UInt(c.divisorBits.W), (c.defaultSampleDel).U)
when (beat && last) {
Expand All @@ -110,16 +157,27 @@ class SPIPhysical(c: SPIParamsBase) extends Module {
}
val decr = Mux(beat, scnt, tcnt) - 1.U
val sched = WireDefault(beat)
/** tcnt counter reset signal */
tcnt := Mux(sched, ctrl.sck.div, decr)

/** sck output */
val sck = Reg(Bool())
/** actual clock for
*
* flips in [[beat]]
*
* drives [[sample]] and [[setup]]
*/
val cref = RegInit(true.B)
/** spi mode */
val cinv = ctrl.sck.pha ^ ctrl.sck.pol

/** converts data to matched endian */
private def convert(data: UInt, fmt: SPIFormat) =
Mux(fmt.endian === SPIEndian.MSB, data, Cat(data.asBools))

// recives reversed dq.i
val rxd = Cat(io.port.dq.reverse.map(_.i))
// rxd after dalay added
val rxd_delayed = VecInit(Seq.fill(io.port.dq.size)(false.B))

//Adding fine-granularity delay buffers on the received data
Expand All @@ -134,29 +192,37 @@ class SPIPhysical(c: SPIParamsBase) extends Module {
rxd_delayed := rxd.asBools
}

// buffer logic
val rxd_fin = rxd_delayed.asUInt
/** @todo why */
val samples = Seq(rxd_fin(1), rxd_fin(1, 0), rxd_fin)

// assuming quad
val buffer = Reg(UInt(c.frameBits.W))
val buffer_in = convert(io.op.bits.data, io.ctrl.fmt)
val shift = Mux ((totalCoarseDel > 0.U), setup_d || (sample_d && stop), sample_d)
buffer := Mux1H(proto, samples.zipWithIndex.map { case (data, i) =>
val n = 1 << i
val m = c.frameBits -1
// shift: buffer[3:0], sample[3:0]
// stay
// buffer(3,0) or buffer(8,4)
Cat(Mux(shift, buffer(m-n, 0), buffer(m, n)),
// sample[3:0], or buffer (3:0)
Mux(sample_d, data, buffer(n-1, 0)))
})

private def upper(x: UInt, n: Int) = x(c.frameBits-1, c.frameBits-n)

/** the data to be transmited in port.dq.o */
val txd = RegInit(0.U(io.port.dq.size.W))
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)
}

// txd enable
val tx = (ctrl.fmt.iodir === SPIDirection.Tx)
val txen_in = (proto.head +: proto.tail.map(_ && tx)).scanRight(false.B)(_ || _).init
val txen = txen_in :+ txen_in.last
Expand All @@ -171,13 +237,13 @@ class SPIPhysical(c: SPIParamsBase) extends Module {
dq.ie := ~(dq.oe)
}
io.op.ready := false.B

/** when true, transmits buffer to SPI fifo */
val done = RegInit(true.B)
done := done || last_d

io.rx.valid := done
io.rx.bits := convert(buffer, ctrl.fmt)

/** indicates if the op requires data transfer */
val xfr = Reg(Bool())

when (stop) {
Expand Down Expand Up @@ -205,9 +271,10 @@ class SPIPhysical(c: SPIParamsBase) extends Module {
sck := ctrl.sck.pol
}
}

// data and buffer transfer complete
when (accept && done) {
io.op.ready := true.B
// op data comes in
when (io.op.valid) {
scnt := op.cnt
rdisableOE := io.op.bits.disableOE.getOrElse(false.B)
Expand Down
41 changes: 40 additions & 1 deletion src/main/scala/devices/spi/TLSPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,21 @@ trait SPIParamsBase {
lazy val rxDepthBits = log2Floor(rxDepth) + 1

}

/** SPI Parameters
*
* @param rAddress spi device base address
* @param rSize SPI control registers address space size
* @param rxDepth rx fifo depth
* @param txDepth tx fifo depth
* @param csWidth width of chip select signal
* @param frameBits number of bits in a frame
* @param delayBits width of delay control registers
* @param divisorBits baud rate divisor
* @param fineDelayBits width of fine delay control register
* @param sampleDelayBits width of sample delay control register
* @param defaultSampleDel default sample delay number
* @param oeDisableDummy disable outout enable during dummy cycles in flash mode
*/
case class SPIParams(
rAddress: BigInt,
rSize: BigInt = 0x1000,
Expand All @@ -62,6 +76,31 @@ case class SPIParams(
require(!oeDisableDummy)
}

/** Controller for Quad Serial Peripheral Interface.
*
* It's a memory-mapped device using data queue to transfer data across the SPI bus.
*
* ==Features==
* i. Full-duplex operation, simultaneous receive and transmit
* i. Operates in master mode
* i. FIFO depth and width is configurable
* i. Serial clock with programmable polarity, programmable baud rate generator, configurable external device selects number
* i. Separate transmit and receive FIFOs
* i. TL slave interface to Soc
* i. Programmable master mode clock frequencies
*
* ==Component==
* - SPI FIFO
* - SPI Media
* - SPI control registers
*
* ==Datapass==
* {{{
* TL bus <--> SPITopModule <--> IO Port
* }}}
* ==Interruption==
* send interrupt if fifo watermark triggers
*/
class SPITopModule(c: SPIParamsBase, outer: TLSPIBase)
extends LazyModuleImp(outer) {

Expand Down
Loading