cores: add bitbang class with minimal hardware for I2C/SPI software bit-banging
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 5 Jul 2019 12:26:10 +0000 (14:26 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 5 Jul 2019 12:26:10 +0000 (14:26 +0200)
litex/soc/cores/bitbang.py [new file with mode: 0644]
test/test_bitbang.py [new file with mode: 0644]

diff --git a/litex/soc/cores/bitbang.py b/litex/soc/cores/bitbang.py
new file mode 100644 (file)
index 0000000..ed7c71d
--- /dev/null
@@ -0,0 +1,90 @@
+# This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+from migen import *
+from migen.fhdl.specials import Tristate
+
+from litex.soc.interconnect.csr import *
+
+# I2C Master Bit-Banging ---------------------------------------------------------------------------
+
+I2C_W_SCL = 0
+I2C_W_OE  = 1
+I2C_W_SDA = 2
+
+I2C_R_SDA = 0
+
+class I2CMaster(Module, AutoCSR):
+    """I2C Master Bit-Banging
+
+    Provides the minimal hardware to do software I2C Master bit banging.
+
+    On the same write CSRStorage (_w), software can control SCL (I2C_SCL), SDA direction and value
+    (I2C_OE, I2C_W). Software get back SDA value with the read CSRStatus (_r).
+    """
+    pads_layout = [("scl", 1), ("sda", 1)]
+    def __init__(self, pads=None):
+        if pads is None:
+            pads = Record(self.pads_layout)
+        self.pads = pads
+        self._w = CSRStorage(8, name="w")
+        self._r = CSRStatus(1,  name="r")
+
+        # # #
+
+        _sda_w  = Signal()
+        _sda_oe = Signal()
+        _sda_r  = Signal()
+        self.comb += [
+            pads.scl.eq(self._w.storage[I2C_W_SCL]),
+            _sda_oe.eq( self._w.storage[I2C_W_OE]),
+            _sda_w.eq(  self._w.storage[I2C_W_SDA]),
+            self._r.status[I2C_R_SDA].eq(_sda_r),
+        ]
+        self.specials += Tristate(pads.sda, _sda_w, _sda_oe, _sda_r)
+
+
+# SPI Master Bit-Banging ---------------------------------------------------------------------------
+
+SPI_W_CLK  = 0
+SPI_W_MOSI = 1
+SPI_W_OE   = 2
+SPI_W_CS   = 4
+
+SPI_R_MOSI = 1
+SPI_R_MISO = 0
+
+class SPIMaster(Module, AutoCSR):
+    """3/4-wire SPI Master Bit-Banging
+
+    Provides the minimal hardware to do software 3/4-wire SPI Master bit banging.
+
+    On the same write CSRStorage (_w), software can control CLK (SPI_CLK), MOSI (SPI_MOSI), MOSI
+    direction (SPI_OE) in the case 3-wire SPI and up to 4 Chip Selects (SPI_CS). Software get back
+    MISO (SPI_MISO) with the read CSRStatus (_r).
+    """
+    pads_layout = [("clk", 1), ("cs_n", 4), ("mosi", 1), ("miso", 1)]
+    def __init__(self, pads=None):
+        if pads is None:
+            pads = Record(self.pads_layout)
+        self.pads = pads
+        assert len(pads.cs_n) <= 4
+        self._w = CSRStorage(8, name="w")
+        self._r = CSRStatus(2,  name="r")
+
+        # # #
+
+        _mosi_w  = Signal()
+        _mosi_oe = Signal()
+        _mosi_r  = Signal()
+        _cs      = Signal(4)
+        self.comb += [
+            pads.clk.eq(  self._w.storage[SPI_W_CLK]),
+            _mosi_w.eq(   self._w.storage[SPI_W_MOSI]),
+            _mosi_oe.eq(  self._w.storage[SPI_W_OE]),
+            pads.cs_n.eq(~self._w.storage[SPI_W_CS]),
+            self._r.status[SPI_R_MOSI].eq(_mosi_r),
+        ]
+        if hasattr(pads, "miso"):
+            self._r.status[SPI_R_MISO].eq(pads.miso)
+        self.specials += Tristate(pads.mosi, _mosi_w, _mosi_oe, _mosi_r)
diff --git a/test/test_bitbang.py b/test/test_bitbang.py
new file mode 100644 (file)
index 0000000..0ca69a7
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+import unittest
+
+from migen import *
+
+from litex.soc.cores.bitbang import I2CMaster, SPIMaster
+
+class TestBitBang(unittest.TestCase):
+    def test_i2c_master_syntax(self):
+        i2c_master = I2CMaster()
+        self.assertEqual(hasattr(i2c_master, "pads"), 1)
+        i2c_master = I2CMaster(Record(I2CMaster.pads_layout))
+        self.assertEqual(hasattr(i2c_master, "pads"), 1)
+
+    def test_spi_master_syntax(self):
+        spi_master = SPIMaster()
+        self.assertEqual(hasattr(spi_master, "pads"), 1)
+        spi_master = SPIMaster(Record(SPIMaster.pads_layout))
+        self.assertEqual(hasattr(spi_master, "pads"), 1)