Skip to content

Commit

Permalink
spi: make counter independent of CS; fix simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
HarryMakes committed Feb 7, 2020
1 parent 99e51ec commit fc4fac1
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 33 deletions.
65 changes: 36 additions & 29 deletions nmigen_stdio/spiflash.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def __init__(self, *, protocol, data_width=32, granularity=8,
# (Note: DQ pins are both input/output only for Dual or Quad SPI protocols)
self.shreg = None
self.counter = Signal.like(self.divisor)
self.counter_en = Signal()
self.clk_posedge_next = Signal()
self.clk_negedge_next = Signal()

Expand All @@ -109,6 +110,7 @@ def _add_spi_hardware_logic(self, platform, module):
raise NotImplementedError("Shift register shreg has not been defined!")
shreg = self.shreg
counter = self.counter
counter_en = self.counter_en

if self._pins is not None:
module.d.comb += [
Expand Down Expand Up @@ -153,13 +155,15 @@ 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)
# Enable SCLK only when CS:
with module.If(self.cs):
# When counter is enabled:
with module.If(counter_en):
# 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)
# Enable SCLK only when CS:
with module.If(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":
Expand All @@ -170,16 +174,19 @@ def _add_spi_hardware_logic(self, platform, module):
# - 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)
]
# Enable SCLK only when CS:
with module.If(self.cs):
module.d.sync += self.clk.eq(0)
module.d.sync += 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)
# When counter is disabled, reset:
with module.Else():
module.d.sync += counter.eq(self.divisor)

# MOSI logic for Standard SPI protocol:
# MOSI always output the leftmost bit of shreg
Expand Down Expand Up @@ -222,6 +229,7 @@ def elaborate(self, platform):

shreg = self.shreg
counter = self.counter
counter_en = self.counter_en

# fcmd: get formatted command based on cmd_dict
fcmd = self._format_cmd()
Expand All @@ -230,10 +238,9 @@ def elaborate(self, platform):
# FSM
with m.FSM() as fsm:
state_durations = {
"SLOWREAD-CMD" : self.cmd_width//self.spi_width,
"SLOWREAD-ADDR" : self._addr_width//self.spi_width,
"SLOWREAD-READ" : self._data_width//self.spi_width,
"SLOWREAD-RDYWAIT": 1
"SLOWREAD-CMD" : self.cmd_width//self.spi_width,
"SLOWREAD-ADDR": self._addr_width//self.spi_width,
"SLOWREAD-READ": self._data_width//self.spi_width
}
max_duration = max([dur for state, dur in state_durations.items()])
# A "count-up" counter for each state of the command
Expand All @@ -244,6 +251,7 @@ def elaborate(self, platform):
with m.If(self.ack):
m.d.sync += [
counter.eq(0),
counter_en.eq(1),
self.clk.eq(0)
]
m.next = "SLOWREAD-CS"
Expand Down Expand Up @@ -283,23 +291,22 @@ def elaborate(self, platform):
with m.State("SLOWREAD-READ"):
with m.If((state_counter == state_durations["SLOWREAD-READ"] - 1) &
self.clk_posedge_next):
m.d.sync += state_counter.eq(0)
if self._protocol in ["dual", "quad"]:
m.d.sync += self.dq_oe.eq(0)
m.next = "SLOWREAD-RDYWAIT"
with m.Elif(self.clk_posedge_next):
m.d.sync += state_counter.eq(state_counter + 1)
# State: Send r_rdy (for 1 clock period), and then Wait
with m.State("SLOWREAD-RDYWAIT"):
with m.If((state_counter == 0) & self.clk_negedge_next):
m.d.sync += self.r_rdy.eq(1)
# Early check to skip 1 clock period of doing nothing
with m.If((state_counter == state_durations["SLOWREAD-RDYWAIT"] - 1) &
self.clk_posedge_next):
with m.If(self.clk_negedge_next):
m.d.sync += self.cs.eq(0)
with m.If(self.clk_posedge_next):
m.d.sync += [
self.r_rdy.eq(1),
counter_en.eq(0)
]
with m.If(self.r_rdy):
m.next = "SLOWREAD-IDLE"
with m.Elif(self.clk_posedge_next):
m.d.sync += state_counter.eq(state_counter + 1)

return m

Expand Down Expand Up @@ -328,6 +335,7 @@ def elaborate(self, platform):

shreg = self.shreg
counter = self.counter
counter_en = self.counter_en

# fcmd: get formatted command based on cmd_dict
fcmd = self._format_cmd()
Expand All @@ -338,8 +346,7 @@ def elaborate(self, platform):
state_durations = {
"FASTREAD-CMD" : self.cmd_width//self.spi_width,
"FASTREAD-ADDR" : self._addr_width//self.spi_width,
"FASTREAD-WAITREAD": self._dummy_cycles+self._data_width//self.spi_width,
"FASTREAD-RDYWAIT" : 1
"FASTREAD-WAITREAD": self._dummy_cycles+self._data_width//self.spi_width
}
max_duration = max([dur for state, dur in state_durations.items()])
# A "count-up" counter for each state of the command
Expand All @@ -350,6 +357,7 @@ def elaborate(self, platform):
with m.If(self.ack):
m.d.sync += [
counter.eq(0),
counter_en.eq(1),
self.clk.eq(0)
]
m.next = "FASTREAD-CS"
Expand Down Expand Up @@ -389,22 +397,21 @@ def elaborate(self, platform):
with m.State("FASTREAD-WAITREAD"):
with m.If((state_counter == state_durations["FASTREAD-WAITREAD"] - 1) &
self.clk_posedge_next):
m.d.sync += state_counter.eq(0)
if self._protocol in ["dual", "quad"]:
m.d.sync += self.dq_oe.eq(0)
m.next = "FASTREAD-RDYWAIT"
with m.Elif(self.clk_posedge_next):
m.d.sync += state_counter.eq(state_counter + 1)
# State: Send r_rdy (for 1 clock period), and then Wait
with m.State("FASTREAD-RDYWAIT"):
with m.If((state_counter == 0) & self.clk_negedge_next):
m.d.sync += self.r_rdy.eq(1)
# Early check to skip 1 clock period of doing nothing
with m.If((state_counter == state_durations["FASTREAD-RDYWAIT"] - 1) &
self.clk_posedge_next):
with m.If(self.clk_negedge_next):
m.d.sync += self.cs.eq(0)
with m.If(self.clk_posedge_next):
m.d.sync += [
self.r_rdy.eq(1),
counter_en.eq(0)
]
with m.If(self.r_rdy):
m.next = "FASTREAD-IDLE"
with m.Elif(self.clk_posedge_next):
m.d.sync += state_counter.eq(state_counter + 1)

return m
8 changes: 4 additions & 4 deletions nmigen_stdio/test/test_spiflash.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ def elaborate(self, platform):
with m.State("INIT"):
m.d.sync += data_sig.eq(self.data)
with m.If(self.dut.cs):
with m.If(self.dut.counter == self.dut.divisor >> 1):
with m.If(self.dut.clk_posedge_next):
m.d.comb += recv_data.w_en.eq(1)
with m.Else():
m.d.comb += recv_data.w_en.eq(0)
with m.If(~recv_data.w_rdy & stored_data.w_rdy & (self.dut.counter == 0)):
with m.If(~recv_data.w_rdy & stored_data.w_rdy & self.dut.clk_negedge_next):
m.next = "PUT-DATA"
with m.State("PUT-DATA"):
with m.If(stored_data_num_left != 0):
Expand All @@ -66,9 +66,9 @@ def elaborate(self, platform):
]
with m.Else():
m.d.comb += stored_data.w_en.eq(0)
with m.If((dummy_counter != 0) & (self.dut.counter == 0)):
with m.If((dummy_counter != 0) & self.dut.clk_negedge_next):
m.d.sync += dummy_counter.eq(dummy_counter - 1)
with m.Elif((dummy_counter == 0) & (self.dut.counter == 0)):
with m.Elif((dummy_counter == 0) & self.dut.clk_negedge_next):
m.d.comb += stored_data.r_en.eq(1)
m.next = "RETURN-DATA"
with m.State("RETURN-DATA"):
Expand Down

0 comments on commit fc4fac1

Please sign in to comment.