misoclib/com: add spi (only SPIMaster for now)
This commit is contained in:
parent
2c51adcd68
commit
a43c555ee3
|
@ -0,0 +1,153 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.bank.description import *
|
||||
from migen.genlib.fsm import FSM, NextState
|
||||
|
||||
class SPIMaster(Module, AutoCSR):
|
||||
def __init__(self, pads, width=24, div=2, cpha=1):
|
||||
self.pads = pads
|
||||
|
||||
self._ctrl = CSR()
|
||||
self._length = CSRStorage(8)
|
||||
self._status = CSRStatus()
|
||||
if hasattr(pads, "mosi"):
|
||||
self._mosi = CSRStorage(width)
|
||||
if hasattr(pads, "miso"):
|
||||
self._miso = CSRStatus(width)
|
||||
|
||||
self.irq = Signal()
|
||||
|
||||
###
|
||||
|
||||
# ctrl
|
||||
start = Signal()
|
||||
length = self._length.storage
|
||||
enable_cs = Signal()
|
||||
enable_shift = Signal()
|
||||
done = Signal()
|
||||
|
||||
self.comb += [
|
||||
start.eq(self._ctrl.re & self._ctrl.r[0]),
|
||||
self._status.status.eq(done)
|
||||
]
|
||||
|
||||
# clk
|
||||
i = Signal(max=div)
|
||||
clk_en = Signal()
|
||||
set_clk = Signal()
|
||||
clr_clk = Signal()
|
||||
self.sync += [
|
||||
If(set_clk,
|
||||
pads.clk.eq(enable_cs)
|
||||
),
|
||||
If(clr_clk,
|
||||
pads.clk.eq(0),
|
||||
i.eq(0)
|
||||
).Else(
|
||||
i.eq(i + 1),
|
||||
)
|
||||
]
|
||||
|
||||
self.comb +=[
|
||||
set_clk.eq(i==div//2-1),
|
||||
clr_clk.eq(i==div-1)
|
||||
]
|
||||
|
||||
# fsm
|
||||
cnt = Signal(8)
|
||||
clr_cnt = Signal()
|
||||
inc_cnt = Signal()
|
||||
self.sync += \
|
||||
If(clr_cnt,
|
||||
cnt.eq(0)
|
||||
).Elif(inc_cnt,
|
||||
cnt.eq(cnt+1)
|
||||
)
|
||||
|
||||
fsm = FSM(reset_state="IDLE")
|
||||
self.submodules += fsm
|
||||
fsm.act("IDLE",
|
||||
If(start,
|
||||
NextState("WAIT_CLK")
|
||||
),
|
||||
done.eq(1),
|
||||
clr_cnt.eq(1)
|
||||
)
|
||||
fsm.act("WAIT_CLK",
|
||||
If(clr_clk,
|
||||
NextState("SHIFT")
|
||||
),
|
||||
)
|
||||
fsm.act("SHIFT",
|
||||
If(cnt == length,
|
||||
NextState("END")
|
||||
).Else(
|
||||
inc_cnt.eq(clr_clk),
|
||||
),
|
||||
enable_cs.eq(1),
|
||||
enable_shift.eq(1),
|
||||
)
|
||||
fsm.act("END",
|
||||
If(set_clk,
|
||||
NextState("IDLE")
|
||||
),
|
||||
enable_shift.eq(1),
|
||||
self.irq.eq(1)
|
||||
)
|
||||
|
||||
# miso
|
||||
if hasattr(pads, "miso"):
|
||||
miso = Signal()
|
||||
sr_miso = Signal(width)
|
||||
|
||||
# (cpha = 1: capture on clk falling edge)
|
||||
if cpha:
|
||||
self.sync += \
|
||||
If(enable_shift,
|
||||
If(clr_clk,
|
||||
miso.eq(pads.miso),
|
||||
).Elif(set_clk,
|
||||
sr_miso.eq(Cat(miso, sr_miso[:-1]))
|
||||
)
|
||||
)
|
||||
# (cpha = 0: capture on clk rising edge)
|
||||
else:
|
||||
self.sync += \
|
||||
If(enable_shift,
|
||||
If(set_clk,
|
||||
miso.eq(pads.miso),
|
||||
).Elif(clr_clk,
|
||||
sr_miso.eq(Cat(miso, sr_miso[:-1]))
|
||||
)
|
||||
)
|
||||
self.comb += self._miso.status.eq(sr_miso)
|
||||
|
||||
# mosi
|
||||
if hasattr(pads, "mosi"):
|
||||
mosi = Signal()
|
||||
sr_mosi = Signal(width)
|
||||
|
||||
# (cpha = 1: propagated on clk rising edge)
|
||||
if cpha:
|
||||
self.sync += \
|
||||
If(start,
|
||||
sr_mosi.eq(self._mosi.storage)
|
||||
).Elif(clr_clk & enable_shift,
|
||||
sr_mosi.eq(Cat(Signal(), sr_mosi[:-1]))
|
||||
).Elif(set_clk,
|
||||
pads.mosi.eq(sr_mosi[-1])
|
||||
)
|
||||
|
||||
# (cpha = 0: propagated on clk falling edge)
|
||||
else:
|
||||
self.sync += [
|
||||
If(start,
|
||||
sr_mosi.eq(self._mosi.storage)
|
||||
).Elif(set_clk & enable_shift,
|
||||
sr_mosi.eq(Cat(Signal(), sr_mosi[:-1]))
|
||||
).Elif(clr_clk,
|
||||
pads.mosi.eq(sr_mosi[-1])
|
||||
)
|
||||
]
|
||||
|
||||
# cs_n
|
||||
self.comb += pads.cs_n.eq(~enable_cs)
|
|
@ -0,0 +1,84 @@
|
|||
from migen.fhdl.std import *
|
||||
from migen.genlib.record import *
|
||||
from migen.sim.generic import run_simulation
|
||||
|
||||
from misoclib.com.spi import SPIMaster
|
||||
|
||||
class SPISlave(Module):
|
||||
def __init__(self, pads, width):
|
||||
self.pads = pads
|
||||
self.width = width
|
||||
|
||||
###
|
||||
|
||||
self.mosi = 0
|
||||
self.miso = 0
|
||||
|
||||
self.last_cs_n = 1
|
||||
self.last_clk = 0
|
||||
|
||||
|
||||
def get_mosi(self):
|
||||
return self.mosi
|
||||
|
||||
def set_miso(self, value):
|
||||
self.miso = value
|
||||
|
||||
def do_simulation(self, selfp):
|
||||
# detect edges
|
||||
cs_n_rising = 0
|
||||
cs_n_falling = 0
|
||||
clk_rising = 0
|
||||
clk_falling = 0
|
||||
if selfp.pads.cs_n and not self.last_cs_n:
|
||||
cs_n_rising = 1
|
||||
if not selfp.pads.cs_n and self.last_cs_n:
|
||||
cs_n_falling = 1
|
||||
if selfp.pads.clk and not self.last_clk:
|
||||
clk_rising = 1
|
||||
if not selfp.pads.clk and self.last_clk:
|
||||
clk_falling = 1
|
||||
|
||||
# input mosi
|
||||
if clk_falling and not selfp.pads.cs_n:
|
||||
self.mosi = self.mosi << 1
|
||||
self.mosi |= selfp.pads.mosi
|
||||
|
||||
# output miso
|
||||
if (clk_rising and not selfp.pads.cs_n):
|
||||
selfp.pads.miso = (self.miso >> (self.width-1)) & 0x1
|
||||
self.miso = self.miso << 1
|
||||
|
||||
# save signal states
|
||||
self.last_cs_n = selfp.pads.cs_n
|
||||
self.last_clk = selfp.pads.clk
|
||||
|
||||
|
||||
def spi_access(selfp, length, mosi):
|
||||
selfp.spi_master._mosi.storage = mosi
|
||||
yield
|
||||
selfp.spi_master._ctrl.r = (length << 8) | 1
|
||||
selfp.spi_master._ctrl.re = 1
|
||||
yield
|
||||
selfp.spi_master._ctrl.r = 0
|
||||
selfp.spi_master._ctrl.re = 0
|
||||
yield
|
||||
while not (selfp.spi_master._status.status & 0x1):
|
||||
yield
|
||||
|
||||
class TB(Module):
|
||||
def __init__(self):
|
||||
pads = Record([("cs_n", 1), ("clk", 1), ("mosi", 1), ("miso", 1)])
|
||||
self.submodules.spi_master = SPIMaster(pads, 24, 4)
|
||||
self.submodules.spi_slave = SPISlave(pads, 24)
|
||||
|
||||
def gen_simulation(self, selfp):
|
||||
for i in range(16):
|
||||
yield
|
||||
self.spi_slave.set_miso(0x123457)
|
||||
yield from spi_access(selfp, 8, 0x123457)
|
||||
print("%08x" %self.spi_slave.get_mosi())
|
||||
print("%08x" %selfp.spi_master._miso.status)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_simulation(TB(), ncycles=1000, vcd_name="my.vcd", keep_files=True)
|
Loading…
Reference in New Issue