Skip to content

Commit

Permalink
spi: use granularity (defaults to 8); improve counter logic
Browse files Browse the repository at this point in the history
  • Loading branch information
HarryMakes committed Feb 7, 2020
1 parent e941997 commit ef12c2a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 28 deletions.
75 changes: 47 additions & 28 deletions nmigen_stdio/spiflash.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,27 @@ def _format_cmd(self):
fcmd &= ~(1 << (bit*self.spi_width))
return fcmd

def __init__(self, *, protocol, data_width=32, divisor=1, divisor_bits=None, pins=None):
def __init__(self, *, protocol, data_width=32, granularity=8,
divisor=1, divisor_bits=None, pins=None):
if protocol not in ["standard", "dual", "quad"]:
raise ValueError("Invalid SPI protocol {!r}; must be one of "
"\"standard\", \"dual\", or \"quad\""
.format(protocol))
self._protocol = protocol
if not isinstance(data_width, int) or data_width < 1:
raise ValueError("Data width must be a positive integer, not {}"
.format(repr(data_width)))
if not isinstance(granularity, int) or granularity < 8 or (
granularity != 1 << (granularity.bit_length()-1)
):
raise ValueError("Granularity must be a positive integer, "
"at least 8 and a power of 2, not {}"
.format(repr(granularity)))
if data_width < granularity or data_width % granularity:
raise ValueError("Data width must be divisible by granularity ({} % {} != 0)"
.format(data_width, granularity))
self._data_width = data_width
self._granularity = granularity

if divisor < 1:
raise ValueError("Invalid divisor value; must be at least 1"
Expand Down Expand Up @@ -83,14 +97,16 @@ def __init__(self, *, protocol, data_width=32, divisor=1, divisor_bits=None, pin
self._fcmd_width = self.cmd_width * self.spi_width
# A two-way register storing the current value on the DQ I/O pins
# (Note: DQ pins are both input/output only for Dual or Quad SPI protocols)
self.shreg = Signal(max(self._fcmd_width, self._data_width))
self.shreg = None
self.counter = Signal.like(self.divisor)
self.clk_posedge_next = Signal()
self.clk_negedge_next = Signal()

def _add_spi_hardware_logic(self, platform, module):
"""Add the foundamental hardware logic for controlling all SPI pins to be used
"""
if self.shreg is None:
raise NotImplementedError("Shift register shreg has not been defined!")
shreg = self.shreg
counter = self.counter

Expand Down Expand Up @@ -137,30 +153,33 @@ def _add_spi_hardware_logic(self, platform, module):
# Countdown logic for counter based on divisor
# Also implements MISO logic
dq_i = Signal(self.spi_width)
# When countdown is half-way done, clock edge goes up (positive);
# MISO starts latching bit/byte from slave
with module.If((counter == (self.divisor+1) >> 1) & self.cs):
module.d.sync += self.clk.eq(1)
# Indicate imminent posedge
module.d.comb += self.clk_posedge_next.eq(1)
if self._protocol == "standard":
module.d.sync += dq_i.eq(self.miso)
elif self._protocol in ["dual", "quad"]:
module.d.sync += dq_i.eq(self.dq.i)
# When countdown reaches 0, clock edge goes down (negative)
# shreg latches from MISO for r_data to read
with module.If((counter == 0) & self.cs):
module.d.sync += [
self.clk.eq(0),
counter.eq(self.divisor)
]
# Indicate imminent negedge
module.d.comb += self.clk_negedge_next.eq(1)
# MOSI latches from shreg by "pushing" old data out from the left of shreg
module.d.sync += shreg.eq(Cat(dq_i, shreg[:-self.spi_width]))
# Normal countdown
with module.Elif(self.cs):
module.d.sync += counter.eq(counter - 1)
# Enable SCLK only when CS:
with module.If(self.cs):
# When countdown is half-way done, clock edge goes up (positive):
# - MISO latches bit/byte from slave
# - slave has been latching from MOSI mid-way (since previous negedge)
with module.If(counter == (self.divisor+1) >> 1):
module.d.sync += self.clk.eq(1)
# Indicate imminent posedge
module.d.comb += self.clk_posedge_next.eq(1)
if self._protocol == "standard":
module.d.sync += dq_i.eq(self.miso)
elif self._protocol in ["dual", "quad"]:
module.d.sync += dq_i.eq(self.dq.i)
# When countdown reaches 0, clock edge goes down (negative):
# - MOSI latches from shreg by "pushing" old data out from the left of shreg;
# - slave has been outputting MISO mid-way (since previous posedge)
with module.If(counter == 0):
module.d.sync += [
self.clk.eq(0),
counter.eq(self.divisor)
]
# Indicate imminent negedge
module.d.comb += self.clk_negedge_next.eq(1)
module.d.sync += shreg.eq(Cat(dq_i, shreg[:-self.spi_width]))
# Normal countdown
with module.Else():
module.d.sync += counter.eq(counter - 1)

# MOSI logic for Standard SPI protocol:
# MOSI always output the leftmost bit of shreg
Expand Down Expand Up @@ -203,7 +222,7 @@ def elaborate(self, platform):
# fcmd: get formatted command based on cmd_dict
fcmd = self._format_cmd()
# addr: convert bus address to byte-sized address
byte_addr = Cat(Repl(0, log2_int(self._data_width//8)), self.addr)
byte_addr = Cat(Repl(0, log2_int(self._data_width // self._granularity)), self.addr)
# FSM
with m.FSM() as fsm:
state_durations = {
Expand Down Expand Up @@ -311,7 +330,7 @@ def elaborate(self, platform):
# fcmd: get formatted command based on cmd_dict
fcmd = self._format_cmd()
# addr: convert bus address to byte-sized address
byte_addr = Cat(Repl(0, log2_int(self._data_width//8)), self.addr)
byte_addr = Cat(Repl(0, log2_int(self._data_width // self._granularity)), self.addr)
# FSM
with m.FSM() as fsm:
state_durations = {
Expand Down
3 changes: 3 additions & 0 deletions nmigen_stdio/test/test_spiflash.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def test_standard(self):
self.dut = SPIFlashFastReader(protocol="standard",
addr_width=24,
data_width=32,
granularity=8,
divisor=self.divisor,
dummy_cycles=15)
self.flash = (SPIFlashFastReadTestCase.
Expand All @@ -120,6 +121,7 @@ def test_dual(self):
self.dut = SPIFlashFastReader(protocol="dual",
addr_width=24,
data_width=32,
granularity=8,
divisor=self.divisor,
dummy_cycles=15)
self.flash = (SPIFlashFastReadTestCase.
Expand All @@ -134,6 +136,7 @@ def test_quad(self):
self.dut = SPIFlashFastReader(protocol="quad",
addr_width=24,
data_width=32,
granularity=8,
divisor=self.divisor,
dummy_cycles=15)
self.flash = (SPIFlashFastReadTestCase.
Expand Down

0 comments on commit ef12c2a

Please sign in to comment.