From 31a2b9bf61033db7804bac744615205dba69c270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Thu, 24 Oct 2024 15:24:01 +0200 Subject: [PATCH 1/4] phy: sdr: improve phy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit improve sdr phy Signed-off-by: Fin Maaß --- litespi/phy/generic_sdr.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/litespi/phy/generic_sdr.py b/litespi/phy/generic_sdr.py index f6706ca..166018c 100644 --- a/litespi/phy/generic_sdr.py +++ b/litespi/phy/generic_sdr.py @@ -96,11 +96,10 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) cs_enable = Signal() self.comb += cs_timer.wait.eq(self.cs) self.comb += cs_enable.eq(cs_timer.done) - self.comb += pads.cs_n.eq(~cs_enable) - - # I/Os. - data_bits = 32 - cmd_bits = 8 + self.specials += SDROutput( + i = ~cs_enable, + o = pads.cs_n + ) if hasattr(pads, "mosi"): dq_o = Signal() @@ -115,9 +114,9 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) o = dq_i[1] ) else: - dq_o = Signal(len(pads.dq)) - dq_i = Signal(len(pads.dq)) - dq_oe = Signal(len(pads.dq)) + dq_o = Signal().like(pads.dq) + dq_i = Signal().like(pads.dq) + dq_oe = Signal().like(pads.dq) for i in range(len(pads.dq)): self.specials += SDRTristate( io = pads.dq[i], @@ -130,13 +129,12 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) sr_cnt = Signal(8, reset_less=True) sr_out_load = Signal() sr_out_shift = Signal() - sr_out = Signal(len(sink.data), reset_less=True) + sr_out = Signal().like(sink.data) sr_in_shift = Signal() - sr_in = Signal(len(sink.data), reset_less=True) + sr_in = Signal().like(sink.data) # Data Out Generation/Load/Shift. self.comb += [ - dq_oe.eq(sink.mask), Case(sink.width, { 1 : dq_o.eq(sr_out[-1:]), 2 : dq_o.eq(sr_out[-2:]), @@ -145,15 +143,11 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) }) ] self.sync += If(sr_out_load, - sr_out.eq(sink.data << (len(sink.data) - sink.len)) + sr_out.eq(sink.data << (len(sink.data) - sink.len)), + sr_in.eq(0), ) self.sync += If(sr_out_shift, - Case(sink.width, { - 1 : sr_out.eq(Cat(Signal(1), sr_out)), - 2 : sr_out.eq(Cat(Signal(2), sr_out)), - 4 : sr_out.eq(Cat(Signal(4), sr_out)), - 8 : sr_out.eq(Cat(Signal(8), sr_out)), - }) + sr_out.eq(sr_out << sink.width), ) # Data In Shift. @@ -173,6 +167,7 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) If(cs_enable & sink.valid, # Load Shift Register Count/Data Out. NextValue(sr_cnt, sink.len - sink.width), + NextValue(dq_oe, sink.mask), sr_out_load.eq(1), # Start XFER. NextState("XFER"), @@ -214,6 +209,7 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) # Send Data In to Core and return to WAIT when accepted. source.valid.eq(1), source.last.eq(1), + NextValue(dq_oe, 0), If(source.ready, NextState("WAIT-CMD-DATA"), ) From c6a5d81ca300010af3b5ccb1b17d94787ff2cdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Thu, 24 Oct 2024 17:55:43 +0200 Subject: [PATCH 2/4] support multiple cs_n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit support multiple cs_n Signed-off-by: Fin Maaß --- litespi/__init__.py | 15 ++++++++++----- litespi/core/mmap.py | 7 +++++-- litespi/crossbar.py | 6 +++--- litespi/phy/generic_ddr.py | 12 ++++++++---- litespi/phy/generic_sdr.py | 11 ++++++----- 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/litespi/__init__.py b/litespi/__init__.py index 27797fd..63fda54 100644 --- a/litespi/__init__.py +++ b/litespi/__init__.py @@ -71,17 +71,21 @@ class LiteSPI(LiteXModule): def __init__(self, phy, clock_domain="sys", with_mmap=True, mmap_endianness="big", with_master=True, master_tx_fifo_depth=1, master_rx_fifo_depth=1, - with_csr=True, with_mmap_write=False): + with_csr=True, with_mmap_write=False, mmap_cs_mask=1): - self.crossbar = crossbar = LiteSPICrossbar(clock_domain) + cs_width=len(phy.cs) + + self.crossbar = crossbar = LiteSPICrossbar(clock_domain, cs_width) self.comb += phy.cs.eq(crossbar.cs) if with_mmap: self.mmap = mmap = LiteSPIMMAP(flash=phy.flash, endianness=mmap_endianness, with_csr=with_csr, - with_write=with_mmap_write) - port_mmap = crossbar.get_port(mmap.cs) + with_write=with_mmap_write, + cs_width=cs_width, + cs_mask=mmap_cs_mask) + port_mmap = crossbar.get_port(mmap.cs, mmap.request) self.bus = mmap.bus self.comb += [ port_mmap.source.connect(mmap.sink), @@ -92,7 +96,8 @@ def __init__(self, phy, clock_domain="sys", if with_master: self.master = master = LiteSPIMaster( tx_fifo_depth = master_tx_fifo_depth, - rx_fifo_depth = master_rx_fifo_depth) + rx_fifo_depth = master_rx_fifo_depth, + cs_width = cs_width) port_master = crossbar.get_port(master.cs) self.comb += [ port_master.source.connect(master.sink), diff --git a/litespi/core/mmap.py b/litespi/core/mmap.py index eccd6e3..1b2ec48 100644 --- a/litespi/core/mmap.py +++ b/litespi/core/mmap.py @@ -75,11 +75,12 @@ class LiteSPIMMAP(LiteXModule): write_config : CSRStorage Optional register holding configuration bits for the write mode. """ - def __init__(self, flash, clock_domain="sys", endianness="big", with_csr=True, with_write=False): + def __init__(self, flash, clock_domain="sys", endianness="big", with_csr=True, with_write=False, cs_width=1, cs_mask=1): self.source = source = stream.Endpoint(spi_core2phy_layout) self.sink = sink = stream.Endpoint(spi_phy2core_layout) self.bus = bus = wishbone.Interface() - self.cs = cs = Signal() + self.cs = Signal(cs_width) + self.request = cs = Signal() self.offset = offset = Signal(len(bus.adr)) # Burst Control. @@ -123,6 +124,8 @@ def __init__(self, flash, clock_domain="sys", endianness="big", with_csr=True, w self.byte_count = byte_count = Signal(2, reset_less=True) self.data_write = Signal(32) + self.comb += If(self.request, self.cs.eq(cs_mask)) + # FSM. self.fsm = fsm = FSM(reset_state="IDLE") fsm.act("IDLE", diff --git a/litespi/crossbar.py b/litespi/crossbar.py index 206297a..679fa0e 100644 --- a/litespi/crossbar.py +++ b/litespi/crossbar.py @@ -27,7 +27,7 @@ def __init__(self): class LiteSPICrossbar(Module): - def __init__(self, cd): + def __init__(self, cd, cs_width=1): self.cd = cd self.users = [] self.master = LiteSPIMasterPort() @@ -41,7 +41,7 @@ def __init__(self, cd): self.master.source.connect(self.tx_cdc.sink), ] - self.cs = Signal() + self.cs = Signal(cs_width) self.user_cs = [] self.user_request = [] @@ -59,7 +59,7 @@ def get_port(self, cs, request = None): if request is None: request = Signal() - self.comb += request.eq(cs) + self.comb += request.eq(cs != 0) self.users.append(internal_port) self.user_cs.append(self.cs.eq(cs)) diff --git a/litespi/phy/generic_ddr.py b/litespi/phy/generic_ddr.py index b440161..7d4dfd0 100644 --- a/litespi/phy/generic_ddr.py +++ b/litespi/phy/generic_ddr.py @@ -15,7 +15,7 @@ from litex.soc.interconnect import stream -from litex.build.io import DDRTristate +from litex.build.io import DDRTristate, SDROutput # LiteSPI DDR PHY Core ----------------------------------------------------------------------------- @@ -54,7 +54,7 @@ class LiteSPIDDRPHYCore(LiteXModule): def __init__(self, pads, flash, cs_delay, extra_latency=0): self.source = source = stream.Endpoint(spi_phy2core_layout) self.sink = sink = stream.Endpoint(spi_core2phy_layout) - self.cs = Signal() + self.cs = Signal().like(pads.cs_n) if hasattr(pads, "miso"): bus_width = 1 @@ -75,9 +75,13 @@ def __init__(self, pads, flash, cs_delay, extra_latency=0): # CS control. self.cs_timer = cs_timer = WaitTimer(cs_delay + 1) # Ensure cs_delay cycles between XFers. cs_enable = Signal() - self.comb += cs_timer.wait.eq(self.cs) + self.comb += cs_timer.wait.eq(self.cs != 0) self.comb += cs_enable.eq(cs_timer.done) - self.comb += pads.cs_n.eq(~cs_enable) + for i in range(len(pads.cs_n)): + self.specials += SDROutput( + i = ~(cs_enable & self.cs[i]), + o = pads.cs_n[i] + ) # I/Os. data_bits = 32 diff --git a/litespi/phy/generic_sdr.py b/litespi/phy/generic_sdr.py index 166018c..de1a0a6 100644 --- a/litespi/phy/generic_sdr.py +++ b/litespi/phy/generic_sdr.py @@ -63,7 +63,7 @@ class LiteSPISDRPHYCore(LiteXModule): def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay): self.source = source = stream.Endpoint(spi_phy2core_layout) self.sink = sink = stream.Endpoint(spi_core2phy_layout) - self.cs = Signal() + self.cs = Signal().like(pads.cs_n) self._spi_clk_divisor = spi_clk_divisor = Signal(8) self._default_divisor = default_divisor @@ -94,11 +94,12 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) # CS control. self.cs_timer = cs_timer = WaitTimer(cs_delay + 1) # Ensure cs_delay cycles between XFers. cs_enable = Signal() - self.comb += cs_timer.wait.eq(self.cs) + self.comb += cs_timer.wait.eq(self.cs != 0) self.comb += cs_enable.eq(cs_timer.done) - self.specials += SDROutput( - i = ~cs_enable, - o = pads.cs_n + for i in range(len(pads.cs_n)): + self.specials += SDROutput( + i = ~(cs_enable & self.cs[i]), + o = pads.cs_n[i] ) if hasattr(pads, "mosi"): From dcd9c191eca19e20629decd33ee381df274597cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Thu, 31 Oct 2024 11:04:35 +0100 Subject: [PATCH 3/4] phy: switch to bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit switch to SDR/DDRTristateBus and SDROutputBus. Signed-off-by: Fin Maaß --- litespi/phy/generic_ddr.py | 27 ++++++++++++--------------- litespi/phy/generic_sdr.py | 22 +++++++++++----------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/litespi/phy/generic_ddr.py b/litespi/phy/generic_ddr.py index 7d4dfd0..b9c1316 100644 --- a/litespi/phy/generic_ddr.py +++ b/litespi/phy/generic_ddr.py @@ -77,26 +77,23 @@ def __init__(self, pads, flash, cs_delay, extra_latency=0): cs_enable = Signal() self.comb += cs_timer.wait.eq(self.cs != 0) self.comb += cs_enable.eq(cs_timer.done) - for i in range(len(pads.cs_n)): - self.specials += SDROutput( - i = ~(cs_enable & self.cs[i]), - o = pads.cs_n[i] - ) - - # I/Os. - data_bits = 32 + cs_n = Signal().like(pads.cs_n) + self.comb += cs_n.eq(~(Replicate(cs_enable, len(pads.cs_n)) & self.cs)) + self.specials += SDROutput( + i = cs_n, + o = pads.cs_n + ) dq_o = Array([Signal(len(pads.dq)) for _ in range(2)]) dq_i = Array([Signal(len(pads.dq)) for _ in range(2)]) dq_oe = Array([Signal(len(pads.dq)) for _ in range(2)]) - for i in range(len(pads.dq)): - self.specials += DDRTristate( - io = pads.dq[i], - o1 = dq_o[0][i], o2 = dq_o[1][i], - oe1 = dq_oe[0][i], oe2 = dq_oe[1][i], - i1 = dq_i[0][i], i2 = dq_i[1][i] - ) + self.specials += DDRTristate( + io = pads.dq, + o1 = dq_o[0], o2 = dq_o[1], + oe1 = dq_oe[0], oe2 = dq_oe[1], + i1 = dq_i[0], i2 = dq_i[1] + ) # Data Shift Registers. sr_cnt = Signal(8, reset_less=True) diff --git a/litespi/phy/generic_sdr.py b/litespi/phy/generic_sdr.py index de1a0a6..a9091bf 100644 --- a/litespi/phy/generic_sdr.py +++ b/litespi/phy/generic_sdr.py @@ -96,11 +96,12 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) cs_enable = Signal() self.comb += cs_timer.wait.eq(self.cs != 0) self.comb += cs_enable.eq(cs_timer.done) - for i in range(len(pads.cs_n)): - self.specials += SDROutput( - i = ~(cs_enable & self.cs[i]), - o = pads.cs_n[i] - ) + cs_n = Signal().like(pads.cs_n) + self.comb += cs_n.eq(~(Replicate(cs_enable, len(pads.cs_n)) & self.cs)) + self.specials += SDROutput( + i = cs_n, + o = pads.cs_n + ) if hasattr(pads, "mosi"): dq_o = Signal() @@ -118,12 +119,11 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) dq_o = Signal().like(pads.dq) dq_i = Signal().like(pads.dq) dq_oe = Signal().like(pads.dq) - for i in range(len(pads.dq)): - self.specials += SDRTristate( - io = pads.dq[i], - o = dq_o[i], - oe = dq_oe[i], - i = dq_i[i], + self.specials += SDRTristate( + io = pads.dq, + o = dq_o, + oe = dq_oe, + i = dq_i, ) # Data Shift Registers. From d98c7426054fdf2c65576ff2fbb6727d6a29b693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Thu, 14 Nov 2024 13:34:18 +0100 Subject: [PATCH 4/4] phy: use common cs control for sdr and ddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use common cs control for sdr and ddr Signed-off-by: Fin Maaß --- litespi/cscontrol.py | 27 +++++++++++++++++++++++++++ litespi/phy/generic_ddr.py | 16 ++++------------ litespi/phy/generic_sdr.py | 16 +++------------- 3 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 litespi/cscontrol.py diff --git a/litespi/cscontrol.py b/litespi/cscontrol.py new file mode 100644 index 0000000..a63aa45 --- /dev/null +++ b/litespi/cscontrol.py @@ -0,0 +1,27 @@ +# +# This file is part of LiteSPI +# +# Copyright (c) 2024 Fin Maaß +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.gen import * +from litex.gen.genlib.misc import WaitTimer +from litex.build.io import SDROutput + +class LiteSPICSControl(LiteXModule): + def __init__(self, pads, cs, cs_delay): + self.enable = enable = Signal() + cs_n = Signal().like(pads.cs_n) + + self.timer = timer = WaitTimer(cs_delay + 1) # Ensure cs_delay cycles between XFers. + + self.comb += timer.wait.eq(cs != 0) + self.comb += enable.eq(timer.done) + self.comb += cs_n.eq(~(Replicate(enable, len(pads.cs_n)) & cs)) + + self.specials += SDROutput( + i = cs_n, + o = pads.cs_n + ) diff --git a/litespi/phy/generic_ddr.py b/litespi/phy/generic_ddr.py index b9c1316..414fd98 100644 --- a/litespi/phy/generic_ddr.py +++ b/litespi/phy/generic_ddr.py @@ -12,10 +12,11 @@ from litespi.common import * from litespi.clkgen import DDRLiteSPIClkGen +from litespi.cscontrol import LiteSPICSControl from litex.soc.interconnect import stream -from litex.build.io import DDRTristate, SDROutput +from litex.build.io import DDRTristate # LiteSPI DDR PHY Core ----------------------------------------------------------------------------- @@ -73,16 +74,7 @@ def __init__(self, pads, flash, cs_delay, extra_latency=0): self.clkgen = clkgen = DDRLiteSPIClkGen(pads) # CS control. - self.cs_timer = cs_timer = WaitTimer(cs_delay + 1) # Ensure cs_delay cycles between XFers. - cs_enable = Signal() - self.comb += cs_timer.wait.eq(self.cs != 0) - self.comb += cs_enable.eq(cs_timer.done) - cs_n = Signal().like(pads.cs_n) - self.comb += cs_n.eq(~(Replicate(cs_enable, len(pads.cs_n)) & self.cs)) - self.specials += SDROutput( - i = cs_n, - o = pads.cs_n - ) + self.cs_control = cs_control = LiteSPICSControl(pads, self.cs, cs_delay) dq_o = Array([Signal(len(pads.dq)) for _ in range(2)]) dq_i = Array([Signal(len(pads.dq)) for _ in range(2)]) @@ -143,7 +135,7 @@ def __init__(self, pads, flash, cs_delay, extra_latency=0): # Stop Clk. NextValue(clkgen.en, 0), # Wait for CS and a CMD from the Core. - If(cs_enable & sink.valid, + If(cs_control.enable & sink.valid, # Load Shift Register Count/Data Out. NextValue(sr_cnt, sink.len - sink.width), sr_out_load.eq(1), diff --git a/litespi/phy/generic_sdr.py b/litespi/phy/generic_sdr.py index a9091bf..f3c528b 100644 --- a/litespi/phy/generic_sdr.py +++ b/litespi/phy/generic_sdr.py @@ -9,10 +9,9 @@ from litex.gen import * -from litex.gen.genlib.misc import WaitTimer - from litespi.common import * from litespi.clkgen import LiteSPIClkGen +from litespi.cscontrol import LiteSPICSControl from litex.soc.interconnect import stream from litex.soc.interconnect.csr import * @@ -92,16 +91,7 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) self.comb += clkgen.div.eq(spi_clk_divisor) # CS control. - self.cs_timer = cs_timer = WaitTimer(cs_delay + 1) # Ensure cs_delay cycles between XFers. - cs_enable = Signal() - self.comb += cs_timer.wait.eq(self.cs != 0) - self.comb += cs_enable.eq(cs_timer.done) - cs_n = Signal().like(pads.cs_n) - self.comb += cs_n.eq(~(Replicate(cs_enable, len(pads.cs_n)) & self.cs)) - self.specials += SDROutput( - i = cs_n, - o = pads.cs_n - ) + self.cs_control = cs_control = LiteSPICSControl(pads, self.cs, cs_delay) if hasattr(pads, "mosi"): dq_o = Signal() @@ -165,7 +155,7 @@ def __init__(self, pads, flash, device, clock_domain, default_divisor, cs_delay) self.fsm = fsm = FSM(reset_state="WAIT-CMD-DATA") fsm.act("WAIT-CMD-DATA", # Wait for CS and a CMD from the Core. - If(cs_enable & sink.valid, + If(cs_control.enable & sink.valid, # Load Shift Register Count/Data Out. NextValue(sr_cnt, sink.len - sink.width), NextValue(dq_oe, sink.mask),