core/spi: add minimal SPISlave
This commit is contained in:
parent
b845755995
commit
41fe7cae0b
|
@ -4,6 +4,7 @@
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
|
from migen.genlib.cdc import MultiReg
|
||||||
|
|
||||||
from litex.soc.interconnect.csr import *
|
from litex.soc.interconnect.csr import *
|
||||||
|
|
||||||
|
@ -145,3 +146,92 @@ class SPIMaster(Module, AutoCSR):
|
||||||
self._status.status[SPI_STATUS_DONE].eq(self.done),
|
self._status.status[SPI_STATUS_DONE].eq(self.done),
|
||||||
self._miso.status.eq(self.miso),
|
self._miso.status.eq(self.miso),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# SPI Slave ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class SPISlave(Module):
|
||||||
|
"""4-wire SPI Slave
|
||||||
|
|
||||||
|
Provides a simple and minimal hardware SPI Slave with CPOL=0, CPHA=0 and build time configurable
|
||||||
|
data_width.
|
||||||
|
"""
|
||||||
|
pads_layout = [("clk", 1), ("cs_n", 1), ("mosi", 1), ("miso", 1)]
|
||||||
|
def __init__(self, pads, data_width):
|
||||||
|
if pads is None:
|
||||||
|
pads = Record(self.pads_layout)
|
||||||
|
if not hasattr(pads, "cs_n"):
|
||||||
|
pads.cs_n = Signal()
|
||||||
|
self.pads = pads
|
||||||
|
self.data_width = data_width
|
||||||
|
|
||||||
|
self.start = Signal()
|
||||||
|
self.length = Signal(8)
|
||||||
|
self.done = Signal()
|
||||||
|
self.irq = Signal()
|
||||||
|
self.mosi = Signal(data_width)
|
||||||
|
self.miso = Signal(data_width)
|
||||||
|
self.cs = Signal()
|
||||||
|
self.loopback = Signal()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
clk = Signal()
|
||||||
|
cs = Signal()
|
||||||
|
mosi = Signal()
|
||||||
|
miso = Signal()
|
||||||
|
|
||||||
|
# IOs <--> Internal (input resynchronization) ----------------------------------------------
|
||||||
|
self.specials += [
|
||||||
|
MultiReg(pads.clk, clk),
|
||||||
|
MultiReg(~pads.cs_n, cs),
|
||||||
|
MultiReg(pads.mosi, mosi),
|
||||||
|
]
|
||||||
|
self.comb += pads.miso.eq(miso)
|
||||||
|
|
||||||
|
# Clock detection --------------------------------------------------------------------------
|
||||||
|
clk_d = Signal()
|
||||||
|
clk_rise = Signal()
|
||||||
|
clk_fall = Signal()
|
||||||
|
self.sync += clk_d.eq(clk)
|
||||||
|
self.comb += clk_rise.eq(clk & ~clk_d)
|
||||||
|
self.comb += clk_fall.eq(~clk & clk_d)
|
||||||
|
|
||||||
|
# Control FSM ------------------------------------------------------------------------------
|
||||||
|
self.submodules.fsm = fsm = FSM(reset_state="IDLE")
|
||||||
|
fsm.act("IDLE",
|
||||||
|
If(cs,
|
||||||
|
self.start.eq(1),
|
||||||
|
NextValue(self.length, 0),
|
||||||
|
NextState("XFER")
|
||||||
|
).Else(
|
||||||
|
self.done.eq(1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("XFER",
|
||||||
|
If(~cs,
|
||||||
|
self.irq.eq(1),
|
||||||
|
NextState("IDLE")
|
||||||
|
),
|
||||||
|
NextValue(self.length, self.length + clk_rise)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Master In Slave Out (MISO) generation (generated on spi_clk falling edge) ----------------
|
||||||
|
miso_data = Signal(data_width)
|
||||||
|
self.sync += \
|
||||||
|
If(self.start,
|
||||||
|
miso_data.eq(self.miso)
|
||||||
|
).Elif(cs & clk_fall,
|
||||||
|
miso_data.eq(Cat(Signal(), miso_data[:-1]))
|
||||||
|
)
|
||||||
|
self.comb += \
|
||||||
|
If(self.loopback,
|
||||||
|
miso.eq(mosi)
|
||||||
|
).Else(
|
||||||
|
miso.eq(miso_data[-1]),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Master Out Slave In (MOSI) capture (captured on spi_clk rising edge) ---------------------
|
||||||
|
self.sync += \
|
||||||
|
If(cs & clk_rise,
|
||||||
|
self.mosi.eq(Cat(mosi, self.mosi[:-1]))
|
||||||
|
)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import unittest
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
|
|
||||||
from litex.soc.cores.spi import SPIMaster
|
from litex.soc.cores.spi import SPIMaster, SPISlave
|
||||||
|
|
||||||
|
|
||||||
class TestSPI(unittest.TestCase):
|
class TestSPI(unittest.TestCase):
|
||||||
|
@ -13,7 +13,7 @@ class TestSPI(unittest.TestCase):
|
||||||
spi_master = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6)
|
spi_master = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6)
|
||||||
self.assertEqual(hasattr(spi_master, "pads"), 1)
|
self.assertEqual(hasattr(spi_master, "pads"), 1)
|
||||||
|
|
||||||
def test_spi_xfer_loopback(self):
|
def test_spi_master_xfer_loopback(self):
|
||||||
def generator(dut):
|
def generator(dut):
|
||||||
yield dut.loopback.eq(1)
|
yield dut.loopback.eq(1)
|
||||||
yield dut.mosi.eq(0xdeadbeef)
|
yield dut.mosi.eq(0xdeadbeef)
|
||||||
|
@ -28,3 +28,39 @@ class TestSPI(unittest.TestCase):
|
||||||
|
|
||||||
dut = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6, with_csr=False)
|
dut = SPIMaster(pads=None, data_width=32, sys_clk_freq=100e6, spi_clk_freq=5e6, with_csr=False)
|
||||||
run_simulation(dut, generator(dut))
|
run_simulation(dut, generator(dut))
|
||||||
|
|
||||||
|
def test_spi_slave_syntax(self):
|
||||||
|
spi_slave = SPISlave(pads=None, data_width=32)
|
||||||
|
self.assertEqual(hasattr(spi_slave, "pads"), 1)
|
||||||
|
|
||||||
|
def test_spi_slave_xfer(self):
|
||||||
|
class DUT(Module):
|
||||||
|
def __init__(self):
|
||||||
|
pads = Record([("clk", 1), ("cs_n", 1), ("mosi", 1), ("miso", 1)])
|
||||||
|
self.submodules.master = SPIMaster(pads, data_width=32,
|
||||||
|
sys_clk_freq=100e6, spi_clk_freq=5e6,
|
||||||
|
with_csr=False)
|
||||||
|
self.submodules.slave = SPISlave(pads, data_width=32)
|
||||||
|
|
||||||
|
def master_generator(dut):
|
||||||
|
yield dut.master.mosi.eq(0xdeadbeef)
|
||||||
|
yield dut.master.length.eq(32)
|
||||||
|
yield dut.master.start.eq(1)
|
||||||
|
yield
|
||||||
|
yield dut.master.start.eq(0)
|
||||||
|
yield
|
||||||
|
while (yield dut.master.done) == 0:
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield dut.master.miso), 0x12345678)
|
||||||
|
|
||||||
|
def slave_generator(dut):
|
||||||
|
yield dut.slave.miso.eq(0x12345678)
|
||||||
|
while (yield dut.slave.start) == 0:
|
||||||
|
yield
|
||||||
|
while (yield dut.slave.done) == 0:
|
||||||
|
yield
|
||||||
|
self.assertEqual((yield dut.slave.mosi), 0xdeadbeef)
|
||||||
|
self.assertEqual((yield dut.slave.length), 32)
|
||||||
|
|
||||||
|
dut = DUT()
|
||||||
|
run_simulation(dut, [master_generator(dut), slave_generator(dut)])
|
||||||
|
|
Loading…
Reference in New Issue