diff --git a/litex/soc/cores/spi.py b/litex/soc/cores/spi.py index 279190fe9..9549ecaf1 100644 --- a/litex/soc/cores/spi.py +++ b/litex/soc/cores/spi.py @@ -26,6 +26,7 @@ class SPIMaster(Module, AutoCSR): pads = Record(self.pads_layout) if not hasattr(pads, "cs_n"): pads.cs_n = Signal() + assert len(pads.cs_n) <= 16 self.pads = pads self.data_width = data_width @@ -36,6 +37,7 @@ class SPIMaster(Module, AutoCSR): self.mosi = Signal(data_width) self.miso = Signal(data_width) self.cs = Signal(len(pads.cs_n), reset=1) + self.cs_mode = Signal() self.loopback = Signal() self.clk_divider = Signal(16, reset=math.ceil(sys_clk_freq/spi_clk_freq)) @@ -44,11 +46,11 @@ class SPIMaster(Module, AutoCSR): # # # - clk_enable = Signal() - cs_enable = Signal() - count = Signal(max=data_width) - mosi_latch = Signal() - miso_latch = Signal() + clk_enable = Signal() + xfer_enable = Signal() + count = Signal(max=data_width) + mosi_latch = Signal() + miso_latch = Signal() # Clock generation ------------------------------------------------------------------------- clk_divider = Signal(16) @@ -79,13 +81,13 @@ class SPIMaster(Module, AutoCSR): fsm.act("START", NextValue(count, 0), If(clk_fall, - cs_enable.eq(1), + xfer_enable.eq(1), NextState("RUN") ) ) fsm.act("RUN", clk_enable.eq(1), - cs_enable.eq(1), + xfer_enable.eq(1), If(clk_fall, NextValue(count, count + 1), If(count == (self.length - 1), @@ -94,7 +96,7 @@ class SPIMaster(Module, AutoCSR): ) ) fsm.act("STOP", - cs_enable.eq(1), + xfer_enable.eq(1), If(clk_rise, miso_latch.eq(1), self.irq.eq(1), @@ -105,7 +107,10 @@ class SPIMaster(Module, AutoCSR): # Chip Select generation ------------------------------------------------------------------- if hasattr(pads, "cs_n"): for i in range(len(pads.cs_n)): - self.sync += pads.cs_n[i].eq(~self.cs[i] | ~cs_enable) + # CS set when enabled and (Xfer enabled or Manual CS mode selected). + cs = (self.cs[i] & (xfer_enable | (self.cs_mode == 1))) + # CS Output/Invert. + self.sync += pads.cs_n[i].eq(~cs) # Master Out Slave In (MOSI) generation (generated on spi_clk falling edge) ---------------- mosi_data = Signal(data_width) @@ -116,7 +121,7 @@ class SPIMaster(Module, AutoCSR): mosi_data.eq(self.mosi), mosi_sel.eq((self.length-1) if mode == "aligned" else (data_width-1)), ).Elif(clk_fall, - If(cs_enable, pads.mosi.eq(mosi_array[mosi_sel])), + If(xfer_enable, pads.mosi.eq(mosi_array[mosi_sel])), mosi_sel.eq(mosi_sel - 1) ), ] @@ -136,30 +141,54 @@ class SPIMaster(Module, AutoCSR): self.sync += If(miso_latch, self.miso.eq(miso_data)) def add_csr(self, with_cs=True, with_loopback=True): - self._control = CSRStorage(fields=[ - CSRField("start", size=1, offset=0, pulse=True, description="Write ``1`` to start SPI Xfer"), - CSRField("length", size=8, offset=8, description="SPI Xfer length (in bits).") - ], description="SPI Control.") - self._status = CSRStatus(fields=[ - CSRField("done", size=1, offset=0, description="SPI Xfer done when read as ``1``.") - ], description="SPI Status.") - self._mosi = CSRStorage(self.data_width, reset_less=True, description="SPI MOSI data (MSB-first serialization).") - self._miso = CSRStatus(self.data_width, description="SPI MISO data (MSB-first de-serialization).") + # Control / Status. + self._control = CSRStorage(description="SPI Control.", fields=[ + CSRField("start", size=1, offset=0, pulse=True, description="SPI Xfer Start (Write ``1`` to start Xfer)."), + CSRField("length", size=8, offset=8, description="SPI Xfer Length (in bits).") + ]) + self._status = CSRStatus(description="SPI Status.", fields=[ + CSRField("done", size=1, offset=0, description="SPI Xfer Done (when read as ``1``).") + ]) self.comb += [ self.start.eq(self._control.fields.start), self.length.eq(self._control.fields.length), - self.mosi.eq(self._mosi.storage), self._status.fields.done.eq(self.done), + ] + + # MOSI/MISO. + self._mosi = CSRStorage(self.data_width, reset_less=True, description="SPI MOSI data (MSB-first serialization).") + self._miso = CSRStatus(self.data_width, description="SPI MISO data (MSB-first de-serialization).") + self.comb += [ + self.mosi.eq(self._mosi.storage), self._miso.status.eq(self.miso), ] + + # Chip Select. if with_cs: - self._cs = CSRStorage(fields=[ - CSRField("sel", len(self.cs), reset=1, description="Write ``1`` to corresponding bit to enable Xfer for chip.") - ], description="SPI Chip Select.") - self.comb += self.cs.eq(self._cs.storage) + self._cs = CSRStorage(description="SPI CS Chip-Select and Mode.", fields=[ + CSRField("sel", size=len(self.cs), offset=0, reset=1, values=[ + ("``0b0..001``", "Chip ``0`` selected for SPI Xfer."), + ("``0b1..000``", "Chip ``N`` selected for SPI Xfer.") + ]), + CSRField("mode", size=1, offset=16, reset=0, values=[ + ("``0b0``", "Normal operation (CS handled by Core)."), + ("``0b1``", "Manual operation (CS handled by User, direct recopy of ``sel``), useful for Bulk transfers.") + ]), + ]) + self.comb += [ + self.cs.eq(self._cs.fields.sel), + self.cs_mode.eq(self._cs.fields.mode) + ] + + # Loopback. if with_loopback: - self._loopback = CSRStorage(description="SPI loopback mode.\n\n Write ``1`` to enable MOSI to MISO internal loopback.") - self.comb += self.loopback.eq(self._loopback.storage) + self._loopback = CSRStorage(description="SPI Loopback Mode.", fields=[ + CSRField("mode", size=1, values=[ + ("``0b0``", "Normal operation."), + ("``0b1``", "Loopback operation (MOSI to MISO).") + ]) + ]) + self.comb += self.loopback.eq(self._loopback.fields.mode) def add_clk_divider(self): self._clk_divider = CSRStorage(16, description="SPI Clk Divider.", reset=self.clk_divider.reset)