From: Florent Kermarrec Date: Sat, 28 Feb 2015 08:41:20 +0000 (+0100) Subject: misoclib/com: add spi (only SPIMaster for now) X-Git-Tag: 24jan2021_ls180~2576 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a43c555ee34b9c6330dfa9382741a3e5fb05aa62;p=litex.git misoclib/com: add spi (only SPIMaster for now) --- diff --git a/misoclib/com/spi/__init__.py b/misoclib/com/spi/__init__.py new file mode 100644 index 00000000..7c153123 --- /dev/null +++ b/misoclib/com/spi/__init__.py @@ -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 index 00000000..32abfd8e --- /dev/null +++ b/misoclib/com/spi/test/spi_master_tb.py @@ -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