add generic command processing state machine

facilitates page writes and sector erases
first commit, debugging now commencing
This commit is contained in:
bunnie 2020-10-29 05:09:18 +08:00
parent 37f2ebe675
commit e8c39ec3d2
1 changed files with 144 additions and 19 deletions

View File

@ -5,6 +5,7 @@
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
from migen.genlib.cdc import MultiReg from migen.genlib.cdc import MultiReg
from migen.genlib.fifo import SyncFIFOBuffered
from litex.soc.interconnect import wishbone from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr_eventmanager import * from litex.soc.interconnect.csr_eventmanager import *
@ -413,12 +414,22 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
self.command = CSRStorage(description="Write individual bits to issue special commands to SPI; setting multiple bits at once leads to undefined behavior.", self.command = CSRStorage(description="Write individual bits to issue special commands to SPI; setting multiple bits at once leads to undefined behavior.",
fields=[ fields=[
CSRField("wakeup", size=1, description="Sequence through init & wakeup routine"), CSRField("wakeup", size=1, description="Sequence through init & wakeup routine"),
CSRField("sector_erase", size=1, description="Erase a sector"), CSRField("exec_cmd", size=1, description="Writing a `1` executes a manual command", pulse=True),
CSRField("cmd_code", size=8, description="Manual command code (first 8 bits, e.g. PP4B is 0x12)"),
CSRField("has_arg", size=1, description="When set, transmits the value of `cmd_arg` as the argument to the command"),
# CSRField("write_cmd", size=1, description="When `1`, `data_bytes` are written from page FIFO; when `0`, up to 4 STR `data_bytes` are read into readback CSR"),
CSRField("dummy_cycles", size=5, description="Number of dummy cycles for manual command; 0 implies a write, >0 implies read"),
CSRField("data_bytes", size=8, description="Number of data bytes"),
]) ])
self.sector = CSRStorage(description="Sector to erase", self.cmd_arg = CSRStorage(description="Command argument",
fields=[ fields=[
CSRField("sector", size=32, description="Sector to erase") CSRField("cmd_arg", size=32, description="Argument to manual command")
]) ])
self.cmd_rbk_data = CSRStatus(description = "Readback data from commands",
fields=[
CSRField("cmd_rbk_data", size=32, description="Data read back from a cmd_code that has `write_code` set to 0"),
]
)
self.status = CSRStatus(description="Interface status", self.status = CSRStatus(description="Interface status",
fields=[ fields=[
CSRField("wip", size=1, description="Operation in progress (write or erease)") CSRField("wip", size=1, description="Operation in progress (write or erease)")
@ -520,16 +531,34 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
self.sync.dqs += opi_di.eq(self.di) self.sync.dqs += opi_di.eq(self.di)
self.comb += opi_fifo_wd.eq(Cat(opi_di, self.di)) self.comb += opi_fifo_wd.eq(Cat(opi_di, self.di))
self.sync += rx_fifo_rst_pipe.eq(rx_fifo_rst) # add one pipe register to help relax this timing path. It is critical so it must be timed, but one extra cycle is OK. self.sync += rx_fifo_rst_pipe.eq(rx_fifo_rst) # add one pipe register to help relax this timing path. It is critical so it must be timed, but one extra cycle is OK.
rbk_data = Signal(32)
self.sync += rbk_data.eq(opi_fifo_wd) # buffer for capture to CSR on command cycles
bus_ack_r = Signal()
bus_ack_w = Signal()
#--------- Page write data responder -----------------------
self.submodules.txwr_fifo = SyncFIFOBuffered(width=16, depth=256)
got_wb_wr = Signal()
got_wb_wr_r = Signal()
self.comb += [
self.txwr_fifo.din.eq(bus.dat_r[:16]), # lower 16 bits only used
got_wb_wr.eq(bus.cyc & bus.stb & bus.we),
bus.ack.eq(bus_ack_r | bus_ack_w),
]
self.sync += [
got_wb_wr_r.eq(got_wb_wr),
self.txwr_fifo.we.eq(got_wb_wr & ~got_wb_wr_r),
bus_ack_w.eq(got_wb_wr),
]
#--------- OPI Rx Phy machine ------------------------------ #--------- OPI Rx Phy machine ------------------------------
self.submodules.rxphy = rxphy = FSM(reset_state="IDLE") self.submodules.rxphy = rxphy = FSM(reset_state="IDLE")
cti_pipe = Signal(3)
rxphy_cnt = Signal(3) rxphy_cnt = Signal(3)
rxphy.act("IDLE", rxphy.act("IDLE",
If(spi_mode, If(spi_mode,
NextState("IDLE"), NextState("IDLE"),
).Else( ).Else(
NextValue(bus.ack, 0), NextValue(bus_ack_r, 0),
If(opi_reset_rx_req, If(opi_reset_rx_req,
NextState("WAIT_RESET"), NextState("WAIT_RESET"),
NextValue(rxphy_cnt, 6), NextValue(rxphy_cnt, 6),
@ -543,7 +572,7 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
NextValue(bus.dat_r, opi_fifo_rd), NextValue(bus.dat_r, opi_fifo_rd),
rx_rden.eq(1), rx_rden.eq(1),
NextValue(opi_addr, opi_addr + 4), NextValue(opi_addr, opi_addr + 4),
NextValue(bus.ack, 1) NextValue(bus_ack_r, 1)
) )
) )
) )
@ -569,6 +598,7 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
txcmd_clken = Signal() txcmd_clken = Signal()
txphy_oe = Signal() txphy_oe = Signal()
txcmd_oe = Signal() txcmd_oe = Signal()
txwr_cnt = Signal(8)
self.sync += opi_cs_n.eq( (tx_run & txphy_cs_n) | (~tx_run & txcmd_cs_n) ) self.sync += opi_cs_n.eq( (tx_run & txphy_cs_n) | (~tx_run & txcmd_cs_n) )
self.comb += If( tx_run, self.do.eq(txphy_do) ).Else( self.do.eq(txcmd_do) ) self.comb += If( tx_run, self.do.eq(txphy_do) ).Else( self.do.eq(txcmd_do) )
self.comb += opi_clk_en.eq( (tx_run & txphy_clken) | (~tx_run & txcmd_clken) ) self.comb += opi_clk_en.eq( (tx_run & txphy_clken) | (~tx_run & txcmd_clken) )
@ -579,15 +609,30 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
self.sync += txphy_bus.eq(bus.cyc & bus.stb & ~bus.we & ((bus.cti == 2) | (bus.cti == 0))) self.sync += txphy_bus.eq(bus.cyc & bus.stb & ~bus.we & ((bus.cti == 2) | (bus.cti == 0)))
tx_resetcycle = Signal() tx_resetcycle = Signal()
cmd_req = Signal()
cmd_ack = Signal()
self.sync += [
If(self.command.fields.exec_cmd,
cmd_req.eq(1),
).Elif(cmd_ack,
cmd_req.eq(0),
).Else(
cmd_req.eq(cmd_req)
)
]
cmd_run = Signal()
cmd_done = Signal()
self.submodules.txphy = txphy = FSM(reset_state="RESET") self.submodules.txphy = txphy = FSM(reset_state="RESET")
txphy.act("RESET", txphy.act("RESET",
NextValue(opi_rx_run, 0), NextValue(opi_rx_run, 0),
NextValue(txphy_oe, 0), NextValue(txphy_oe, 0),
NextValue(txphy_cs_n, 1), NextValue(txphy_cs_n, 1),
NextValue(txphy_clken, 0), NextValue(txphy_clken, 0),
NextValue(cmd_done, 0),
# guarantee that the first state we go to out of reset is a four-cycle burst # guarantee that the first state we go to out of reset is a four-cycle burst
NextValue(txphy_cnt, 4), NextValue(txphy_cnt, 4),
If(tx_run & ~spi_mode, If( (tx_run | cmd_run) & ~spi_mode,
NextState("TX_SETUP") NextState("TX_SETUP")
) )
) )
@ -603,7 +648,11 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
) )
) )
txphy.act("TX_CMD_CS_DELAY", # meet setup timing for CS-to-clock txphy.act("TX_CMD_CS_DELAY", # meet setup timing for CS-to-clock
NextState("TX_CMD") If( tx_run,
NextState("TX_CMD")
).Elif( cmd_run,
NextState("TX_MAN_CMD")
)
) )
txphy.act("TX_CMD", txphy.act("TX_CMD",
NextValue(txphy_do, 0xEE11), NextValue(txphy_do, 0xEE11),
@ -672,6 +721,82 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
NextValue(txphy_clken, 1), NextValue(txphy_clken, 1),
) )
) )
txphy.act("TX_MAN_CMD",
NextValue(txphy_do, Cat(~self.command.fields.cmd_code, self.command.fields.cmd_code)),
NextValue(txphy_clken, 1),
If(self.command.fields.has_arg,
NextState("TX_ARGHI")
).Elif(self.command.fields.dummy_cycles > 0, # implies a read
NextValue(txphy_cnt, self.command.fields.dummy_cycles - 1),
NextState("TX_MAN_DUMMY")
).Elif(self.command.fields.data_bytes > 0, # write is implied if dummy cycles is 0
NextValue(txwr_cnt, self.command.fields.data_bytes),
NextState("TX_WRDATA")
).Else( # simple command with no data or readback
NextState("RESET"),
NextValue(cmd_done, 1),
)
)
txphy.act("TX_ARGHI",
NextValue(txphy_do, self.cmd_arg.fields.cmd_arg[16:]),
NextState("TX_ARGLO")
)
txphy.act("TX_ARGLO",
NextValue(txphy_do, self.cmd_arg.fields.cmd_arg[:16]),
If(self.command.fields.dummy_cycles > 0,
NextValue(txphy_cnt, self.command.fields.dummy_cycles - 1),
NextState("TX_MAN_DUMMY")
).Else(# self.command.fields.write_cmd, # write is implied if dummy cycles is 0
NextValue(txwr_cnt, self.command.fields.data_bytes),
NextState("TX_WRDATA")
)
)
txphy.act("TX_MAN_DUMMY",
NextValue(txphy_oe, 0),
NextValue(txphy_do, 0),
NextValue(txphy_cnt, txphy_cnt - 1),
If(txphy_cnt == 0,
NextValue(opi_rx_run, 1),
# always a readback after a dummy cycle
NextValue(txphy_cnt, self.command.fields.data_bytes[:4] - 1), # ignore upper bits
NextState("TX_MAN_RBK"),
)
)
txphy.act("TX_MAN_RBK",
If(txphy_cnt == 0,
NextValue(txphy_clken, 1),
NextValue(opi_reset_rx_req, 1),
NextState("TX_RESET_RX"),
NextValue(self.cmd_rbk_data.fields.cmd_rbk_data, rbk_data),
NextValue(cmd_done, 1), # done with readback
).Else(
NextValue(txphy_cnt, txphy_cnt - 1),
)
)
txphy.act("TX_WRDATA",
If(txwr_cnt == 0,
NextState("TX_WR_RESET"),
).Else(
NextValue(txwr_cnt, txwr_cnt - 1),
NextValue(txphy_do, self.txwr_fifo.dout),
self.txwr_fifo.re.eq(1),
)
)
txphy.act("TX_WR_RESET",
NextValue(opi_rx_run, 0),
NextValue(txphy_oe, 0),
NextValue(txphy_cs_n, 1),
NextValue(txphy_clken, 0),
NextValue(cmd_done, 0),
# drain any excess values in the page FIFO
If(self.txwr_fifo.readable,
self.txwr_fifo.re.eq(1),
).Else(
NextState("RESET"),
NextValue(cmd_done, 1),
)
)
#--------- OPI CMD machine ------------------------------ #--------- OPI CMD machine ------------------------------
self.submodules.opicmd = opicmd = FSM(reset_state="RESET") self.submodules.opicmd = opicmd = FSM(reset_state="RESET")
@ -679,6 +804,7 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
NextValue(txcmd_do, 0), NextValue(txcmd_do, 0),
NextValue(txcmd_oe, 0), NextValue(txcmd_oe, 0),
NextValue(tx_run, 0), NextValue(tx_run, 0),
NextValue(cmd_run, 0),
NextValue(txcmd_cs_n, 1), NextValue(txcmd_cs_n, 1),
If(~spi_mode, If(~spi_mode,
NextState("IDLE") NextState("IDLE")
@ -711,14 +837,14 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
# Handle other cases here, e.g. what do we do if we get a write? probably # Handle other cases here, e.g. what do we do if we get a write? probably
# should just ACK it without doing anything so the CPU doesn't freeze... # should just ACK it without doing anything so the CPU doesn't freeze...
) )
).Elif(self.command.re, ).Elif(cmd_req,
NextState("DISPATCH_CMD"), NextState("DISPATCH_CMD"),
) )
) )
) )
opicmd.act("TX_RUN", opicmd.act("TX_RUN",
NextValue(tx_run, 1), NextValue(tx_run, 1),
If(self.command.re, # Respond to commands If(cmd_req, # Respond to commands
NextState("WAIT_DISPATCH") NextState("WAIT_DISPATCH")
) )
) )
@ -730,15 +856,14 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
) )
) )
opicmd.act("DISPATCH_CMD", opicmd.act("DISPATCH_CMD",
If(self.command.fields.sector_erase, cmd_ack.eq(1), # clear the command dispatch pulse cache
NextState("DO_SECTOR_ERASE") If(cmd_done,
NextValue(cmd_run, 0),
NextState("TX_RUN"),
).Else( ).Else(
NextState("IDLE") NextValue(cmd_run, 1),
) )
) )
opicmd.act("DO_SECTOR_ERASE",
# Placeholder
)
# MAC/PHY abstraction for the SPI machine # MAC/PHY abstraction for the SPI machine
spi_req = Signal() spi_req = Signal()
@ -850,12 +975,12 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
NextValue(mac_count, 0), NextValue(mac_count, 0),
NextState("WAKEUP_PRE"), NextState("WAKEUP_PRE"),
NextValue(new_cycle, 1), NextValue(new_cycle, 1),
If(spi_mode, NextValue(bus.ack, 0)), If(spi_mode, NextValue(bus_ack_r, 0)),
) )
if spiread: if spiread:
mac.act("IDLE", mac.act("IDLE",
If(spi_mode, # This machine stays in idle once spi_mode is dropped If(spi_mode, # This machine stays in idle once spi_mode is dropped
NextValue(bus.ack, 0), NextValue(bus_ack_r, 0),
If((bus.cyc == 1) & (bus.stb == 1) & (bus.we == 0) & (bus.cti != 7), # read cycle requested, not end-of-burst If((bus.cyc == 1) & (bus.stb == 1) & (bus.we == 0) & (bus.cti != 7), # read cycle requested, not end-of-burst
If( (rom_addr[2:] != bus.adr) & new_cycle, If( (rom_addr[2:] != bus.adr) & new_cycle,
NextValue(rom_addr, Cat(Signal(2, reset=0), bus.adr)), NextValue(rom_addr, Cat(Signal(2, reset=0), bus.adr)),
@ -1064,7 +1189,7 @@ class S7SPIOPI(Module, AutoCSR, AutoDoc):
# handle otherwise implicit dual-controller situation # handle otherwise implicit dual-controller situation
If(spi_mode, If(spi_mode,
NextValue(bus.dat_r, Cat(d_to_wb[8:],spi_di)), NextValue(bus.dat_r, Cat(d_to_wb[8:],spi_di)),
NextValue(bus.ack, 1), NextValue(bus_ack_r, 1),
), ),
NextValue(rom_addr, rom_addr + 1), NextValue(rom_addr, rom_addr + 1),
NextState("IDLE") NextState("IDLE")