cores/spi: Add Manual CS Mode (to allow doing Bulk Xfers without external changes), also cleanup/simplify a bit CSR descriptions.

This commit is contained in:
Florent Kermarrec 2021-06-18 09:58:53 +02:00
parent ac73474d66
commit 8ce7c583e6
1 changed files with 55 additions and 26 deletions

View File

@ -26,6 +26,7 @@ class SPIMaster(Module, AutoCSR):
pads = Record(self.pads_layout) pads = Record(self.pads_layout)
if not hasattr(pads, "cs_n"): if not hasattr(pads, "cs_n"):
pads.cs_n = Signal() pads.cs_n = Signal()
assert len(pads.cs_n) <= 16
self.pads = pads self.pads = pads
self.data_width = data_width self.data_width = data_width
@ -36,6 +37,7 @@ class SPIMaster(Module, AutoCSR):
self.mosi = Signal(data_width) self.mosi = Signal(data_width)
self.miso = Signal(data_width) self.miso = Signal(data_width)
self.cs = Signal(len(pads.cs_n), reset=1) self.cs = Signal(len(pads.cs_n), reset=1)
self.cs_mode = Signal()
self.loopback = Signal() self.loopback = Signal()
self.clk_divider = Signal(16, reset=math.ceil(sys_clk_freq/spi_clk_freq)) 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() clk_enable = Signal()
cs_enable = Signal() xfer_enable = Signal()
count = Signal(max=data_width) count = Signal(max=data_width)
mosi_latch = Signal() mosi_latch = Signal()
miso_latch = Signal() miso_latch = Signal()
# Clock generation ------------------------------------------------------------------------- # Clock generation -------------------------------------------------------------------------
clk_divider = Signal(16) clk_divider = Signal(16)
@ -79,13 +81,13 @@ class SPIMaster(Module, AutoCSR):
fsm.act("START", fsm.act("START",
NextValue(count, 0), NextValue(count, 0),
If(clk_fall, If(clk_fall,
cs_enable.eq(1), xfer_enable.eq(1),
NextState("RUN") NextState("RUN")
) )
) )
fsm.act("RUN", fsm.act("RUN",
clk_enable.eq(1), clk_enable.eq(1),
cs_enable.eq(1), xfer_enable.eq(1),
If(clk_fall, If(clk_fall,
NextValue(count, count + 1), NextValue(count, count + 1),
If(count == (self.length - 1), If(count == (self.length - 1),
@ -94,7 +96,7 @@ class SPIMaster(Module, AutoCSR):
) )
) )
fsm.act("STOP", fsm.act("STOP",
cs_enable.eq(1), xfer_enable.eq(1),
If(clk_rise, If(clk_rise,
miso_latch.eq(1), miso_latch.eq(1),
self.irq.eq(1), self.irq.eq(1),
@ -105,7 +107,10 @@ class SPIMaster(Module, AutoCSR):
# Chip Select generation ------------------------------------------------------------------- # Chip Select generation -------------------------------------------------------------------
if hasattr(pads, "cs_n"): if hasattr(pads, "cs_n"):
for i in range(len(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) ---------------- # Master Out Slave In (MOSI) generation (generated on spi_clk falling edge) ----------------
mosi_data = Signal(data_width) mosi_data = Signal(data_width)
@ -116,7 +121,7 @@ class SPIMaster(Module, AutoCSR):
mosi_data.eq(self.mosi), mosi_data.eq(self.mosi),
mosi_sel.eq((self.length-1) if mode == "aligned" else (data_width-1)), mosi_sel.eq((self.length-1) if mode == "aligned" else (data_width-1)),
).Elif(clk_fall, ).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) mosi_sel.eq(mosi_sel - 1)
), ),
] ]
@ -136,30 +141,54 @@ class SPIMaster(Module, AutoCSR):
self.sync += If(miso_latch, self.miso.eq(miso_data)) self.sync += If(miso_latch, self.miso.eq(miso_data))
def add_csr(self, with_cs=True, with_loopback=True): def add_csr(self, with_cs=True, with_loopback=True):
self._control = CSRStorage(fields=[ # Control / Status.
CSRField("start", size=1, offset=0, pulse=True, description="Write ``1`` to start SPI Xfer"), self._control = CSRStorage(description="SPI Control.", fields=[
CSRField("length", size=8, offset=8, description="SPI Xfer length (in bits).") CSRField("start", size=1, offset=0, pulse=True, description="SPI Xfer Start (Write ``1`` to start Xfer)."),
], description="SPI Control.") CSRField("length", size=8, offset=8, description="SPI Xfer Length (in bits).")
self._status = CSRStatus(fields=[ ])
CSRField("done", size=1, offset=0, description="SPI Xfer done when read as ``1``.") self._status = CSRStatus(description="SPI Status.", fields=[
], description="SPI Status.") CSRField("done", size=1, offset=0, description="SPI Xfer Done (when read as ``1``).")
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.comb += [
self.start.eq(self._control.fields.start), self.start.eq(self._control.fields.start),
self.length.eq(self._control.fields.length), self.length.eq(self._control.fields.length),
self.mosi.eq(self._mosi.storage),
self._status.fields.done.eq(self.done), 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), self._miso.status.eq(self.miso),
] ]
# Chip Select.
if with_cs: if with_cs:
self._cs = CSRStorage(fields=[ self._cs = CSRStorage(description="SPI CS Chip-Select and Mode.", fields=[
CSRField("sel", len(self.cs), reset=1, description="Write ``1`` to corresponding bit to enable Xfer for chip.") CSRField("sel", size=len(self.cs), offset=0, reset=1, values=[
], description="SPI Chip Select.") ("``0b0..001``", "Chip ``0`` selected for SPI Xfer."),
self.comb += self.cs.eq(self._cs.storage) ("``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: if with_loopback:
self._loopback = CSRStorage(description="SPI loopback mode.\n\n Write ``1`` to enable MOSI to MISO internal loopback.") self._loopback = CSRStorage(description="SPI Loopback Mode.", fields=[
self.comb += self.loopback.eq(self._loopback.storage) 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): def add_clk_divider(self):
self._clk_divider = CSRStorage(16, description="SPI Clk Divider.", reset=self.clk_divider.reset) self._clk_divider = CSRStorage(16, description="SPI Clk Divider.", reset=self.clk_divider.reset)