misoclib/com: add spi (only SPIMaster for now)
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Sat, 28 Feb 2015 08:41:20 +0000 (09:41 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Sat, 28 Feb 2015 08:43:03 +0000 (09:43 +0100)
misoclib/com/spi/__init__.py [new file with mode: 0644]
misoclib/com/spi/test/spi_master_tb.py [new file with mode: 0644]

diff --git a/misoclib/com/spi/__init__.py b/misoclib/com/spi/__init__.py
new file mode 100644 (file)
index 0000000..7c15312
--- /dev/null
@@ -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)
diff --git a/misoclib/com/spi/test/spi_master_tb.py b/misoclib/com/spi/test/spi_master_tb.py
new file mode 100644 (file)
index 0000000..32abfd8
--- /dev/null
@@ -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)
\ No newline at end of file