diff --git a/src/main/scala/devices/spi/SPIArbiter.scala b/src/main/scala/devices/spi/SPIArbiter.scala index c9650ef..4f401af 100644 --- a/src/main/scala/devices/spi/SPIArbiter.scala +++ b/src/main/scala/devices/spi/SPIArbiter.scala @@ -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)) }) diff --git a/src/main/scala/devices/spi/SPIConsts.scala b/src/main/scala/devices/spi/SPIConsts.scala index 0681635..2460ddc 100644 --- a/src/main/scala/devices/spi/SPIConsts.scala +++ b/src/main/scala/devices/spi/SPIConsts.scala @@ -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) } diff --git a/src/main/scala/devices/spi/SPIFlash.scala b/src/main/scala/devices/spi/SPIFlash.scala index a7f0868..5df8226 100644 --- a/src/main/scala/devices/spi/SPIFlash.scala +++ b/src/main/scala/devices/spi/SPIFlash.scala @@ -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 @@ -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) }) diff --git a/src/main/scala/devices/spi/SPIMedia.scala b/src/main/scala/devices/spi/SPIMedia.scala index 9ca309d..9e9d46b 100644 --- a/src/main/scala/devices/spi/SPIMedia.scala +++ b/src/main/scala/devices/spi/SPIMedia.scala @@ -19,9 +19,36 @@ 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)) @@ -29,6 +56,7 @@ class SPIMedia(c: SPIParamsBase) extends Module { val extradel = Input(new SPIExtraDelay(c)) val sampledel = Input(new SPISampleDelay(c)) } + /** connected to SPI Fifo */ val link = Flipped(new SPILinkIO(c)) }) diff --git a/src/main/scala/devices/spi/SPIPhysical.scala b/src/main/scala/devices/spi/SPIPhysical.scala index 389879f..e0a29e7 100644 --- a/src/main/scala/devices/spi/SPIPhysical.scala +++ b/src/main/scala/devices/spi/SPIPhysical.scala @@ -39,11 +39,36 @@ 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)) }) @@ -51,20 +76,41 @@ class SPIPhysical(c: SPIParamsBase) extends Module { 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) @@ -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) { @@ -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 @@ -134,21 +192,29 @@ 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)) @@ -156,7 +222,7 @@ class SPIPhysical(c: SPIParamsBase) extends Module { 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 @@ -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) { @@ -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) diff --git a/src/main/scala/devices/spi/TLSPI.scala b/src/main/scala/devices/spi/TLSPI.scala index ef23fc4..c24d413 100644 --- a/src/main/scala/devices/spi/TLSPI.scala +++ b/src/main/scala/devices/spi/TLSPI.scala @@ -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, @@ -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) { diff --git a/src/main/scala/devices/spi/TLSPIFlash.scala b/src/main/scala/devices/spi/TLSPIFlash.scala index 89121d3..4d709d8 100644 --- a/src/main/scala/devices/spi/TLSPIFlash.scala +++ b/src/main/scala/devices/spi/TLSPIFlash.scala @@ -25,6 +25,22 @@ trait SPIFlashParamsBase extends SPIParamsBase { lazy val insnAddrLenBits = log2Floor(insnAddrBytes) + 1 } +/** SPI Flash Parameters + * + * @param rAddress control registers base address in TL + * @param fAddress flash base address in TL + * @param rSize SPI control registers address space size + * @param fSize SPI Flash address space size + * @param rxDepth rx fifo depth + * @param txDepth tx fifo depth + * @param csWidth width of chip select signal + * @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 SPIFlashParams( rAddress: BigInt, fAddress: BigInt, @@ -51,6 +67,15 @@ case class SPIFlashParams( require(defaultSampleDel >= 0) } +/** QSPI which contains Serial Memory Mode to act as a Serial Flash memory controller + * + * ==Features== + * 1. Support two mode: + * i. SPI mode as descrriped in TLSPI + * i. Serial Memory Mode: In this Mode, the QSPI acts as a serial Flash memory controller. + * 1. Set of TL accessible FLASH control registers to perform any Flash command + * 1. Command, address and data can be sent independently using different modes.(Single, Dual, Quad). + */ class SPIFlashTopModule(c: SPIFlashParamsBase, outer: TLSPIFlashBase) extends SPITopModule(c, outer) {