From c64c89c6526b54a4401929a1e47f1f67dfe93697 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Sat, 11 Mar 2023 13:41:10 +1000 Subject: [PATCH] add docstrings to bitbang --- litex/soc/cores/bitbang.py | 129 +++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 35 deletions(-) diff --git a/litex/soc/cores/bitbang.py b/litex/soc/cores/bitbang.py index 8453c90f7..4ac78bb47 100644 --- a/litex/soc/cores/bitbang.py +++ b/litex/soc/cores/bitbang.py @@ -12,29 +12,41 @@ from litex.soc.interconnect.csr import * # I2C Master Bit-Banging --------------------------------------------------------------------------- class I2CMaster(Module, AutoCSR): - """I2C Master Bit-Banging + """I2C bus master (bit-banged). - Provides the minimal hardware to do software I2C Master bit banging. + This core provides minimal hardware for use as a software controlled bit-banged I2C bus master. I2C uses a + tristate/open-drain output driver configuration with pull-up resistors, and this core expects that the pull-ups will + be provided externally. - On the same write CSRStorage (_w), software can control: - - SCL (I2C_SCL). - - SDA direction and value (I2C_OE, I2C_W). + Further information about the I2C bus can be found in the I2C standard document from NXP, `UM10204`_. - Software get back SDA value with the read CSRStatus (_r). + .. _UM10204: https://www.pololu.com/file/0J435/UM10204.pdf """ pads_layout = [("scl", 1), ("sda", 1)] + def __init__(self, pads=None, default_dev=False): + """ + Class constructor. + + :param pads: (optional) A ``Record`` object containing the pads ``scl`` and ``sda``. + :param default_dev: (optional) A `bool` indicating whether this I2C master should be used as the default I2C + interface (default is ``False``) + """ self.init = [] if pads is None: pads = Record(self.pads_layout) self.pads = pads self._w = CSRStorage(fields=[ - CSRField("scl", size=1, offset=0, reset=1), - CSRField("oe", size=1, offset=1), - CSRField("sda", size=1, offset=2, reset=1)], + CSRField("scl", size=1, offset=0, reset=1, access=CSRAccess.WriteOnly, + description="Drives the state of the SCL pad."), + CSRField("oe", size=1, offset=1, access=CSRAccess.WriteOnly, + description="Output Enable - if 0, both the SCL and SDA output drivers are disconnected."), + CSRField("sda", size=1, offset=2, reset=1, access=CSRAccess.WriteOnly, + description="Drives the state of the SDA pad.")], name="w") self._r = CSRStatus(fields=[ - CSRField("sda", size=1, offset=0)], + CSRField("sda", size=1, offset=0, access=CSRAccess.ReadOnly, + description="Contains the current state of the SDA pad.")], name="r") self.default_dev = default_dev @@ -42,25 +54,48 @@ class I2CMaster(Module, AutoCSR): self.connect(pads) def connect(self, pads): + """ + Attaches the signals from inside the core to the input/output pads. This function is normally only called from + inside the class constructor. + + :param pads: A ``Record`` object containing the pads ``scl`` and ``sda``. + """ # SCL self.specials += Tristate(pads.scl, - o = 0, # I2C uses Pull-ups, only drive low. - oe = ~self._w.fields.scl # Drive when scl is low. + o = 0, # I2C uses Pull-ups, only drive low. + oe = ~self._w.fields.scl # Drive when scl is low. ) # SDA self.specials += Tristate(pads.sda, - o = 0, # I2C uses Pull-ups, only drive low. - oe = self._w.fields.oe & ~self._w.fields.sda, # Drive when oe and sda is low. + o = 0, # I2C uses Pull-ups, only drive low. + oe = self._w.fields.oe & ~self._w.fields.sda, # Drive when oe and sda is low. i = self._r.fields.sda ) def add_init(self, addr, init, init_addr_len=1): + """ + Adds an I2C write transaction that will be executed on startup. This method can be called multiple times to add + multiple transactions that will be executed in order for this core instance. + + :param addr: The I2C slave address to write to + :param init: The bytes to write to the slave. + :param init_addr_len: (optional) The init address length in bytes (default is 1) + """ + if init_addr_len not in (1, 2): + raise ValueError("I2C slave addresses can only have a length of one or two bytes") + + if init_addr_len == 1 and not 0 <= addr <= 127: + raise ValueError("I2C slave address must be between 0 and 127 (inclusive)") + elif init_addr_len == 2 and not 0 <= addr <= 1023: + raise ValueError("I2C slave address must be between 0 and 1023 (inclusive)") + self.init.append((addr, init, init_addr_len)) -class I2CMasterSim(I2CMaster): - """I2C Master Bit-Banging for Verilator simulation - Uses separate pads for SDA IN/OUT as Verilator does not support tristate pins well. +class I2CMasterSim(I2CMaster): + """I2C bus master (bit-banged) for Verilator simulation + + This core uses separate pads for SDA IN/OUT as Verilator does not support tristate pins well. """ pads_layout = [("scl", 1), ("sda_in", 1), ("sda_out", 1)] @@ -88,44 +123,68 @@ class I2CMasterSim(I2CMaster): # TODO: Find a more generic way to do it that would also apply to other peripherals? def collect_i2c_info(soc): + """ + Collects all the I2C write transactions that have been added to run on startup for all ``I2CMaster`` instances + into a single list. This information is used to generate C header files in ``litex.soc.integration.export``. + + See ``I2CMaster.add_init`` for more information. + + :param soc: ``SoCBase`` instance to scan for ``I2CMaster`` instances. + :return: ``i2c_devs, i2c_init`` where ``i2c_devs`` is a list of all ``I2CMaster`` instances, and ``i2c_init`` is + a list of tuples, where each tuple is (core instance name, slave address, bytes to write, slave address + length in bytes) + """ i2c_init = [] i2c_devs = [] for name, obj in xdir(soc, True): if isinstance(obj, I2CMaster): soc.add_config("HAS_I2C", check_duplicate=False) - i2c_devs.append((name, getattr(obj, "default_dev"))) - if hasattr(obj, "init"): - for addr, init, init_addr_len in obj.init: - i2c_init.append((name, addr, init, init_addr_len)) + i2c_devs.append((name, obj.default_dev)) + for addr, init, init_addr_len in obj.init: + i2c_init.append((name, addr, init, init_addr_len)) return i2c_devs, i2c_init # SPI Master Bit-Banging --------------------------------------------------------------------------- class SPIMaster(Module, AutoCSR): - """3/4-wire SPI Master Bit-Banging + """3/4-wire SPI bus master (bit-banged). - Provides the minimal hardware to do software 3/4-wire SPI Master bit banging. + This core provides minimal hardware for use as a software controlled bit-banged SPI bus master. - 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). + This core supports the typical SPI pads (MOSI, MISO, CLK) and a maximum of 4 CS outputs. If pull-up resistors are + needed for 3 wire operation, they must be added externally. """ pads_layout = [("clk", 1), ("cs_n", 4), ("mosi", 1), ("miso", 1)] + def __init__(self, pads=None): + """ + Class constructor. + + :param pads: (optional) A ``Record`` object containing: ``clk``, ``cs_n``, ``mosi`` and ``miso``. + """ if pads is None: pads = Record(self.pads_layout) self.pads = pads - assert len(pads.cs_n) <= 4 + + if len(pads.cs_n) > 4: + raise ValueError("This core only supports a maximum of 4 CS outputs") + self._w = CSRStorage(fields=[ - CSRField("clk", size=1, offset=0), - CSRField("mosi", size=1, offset=1), - CSRField("oe", size=1, offset=2), - CSRField("cs", size=1, offset=4)], - name="w") + CSRField("clk", size=1, offset=0, access=CSRAccess.WriteOnly, + description="Drives the state of the CLK pad."), + CSRField("mosi", size=1, offset=1, access=CSRAccess.WriteOnly, + description="Drives the state of the MOSI pad."), + CSRField("oe", size=1, offset=2, access=CSRAccess.WriteOnly, + description="Output Enable for MOSI - if 0, the MOSI output driver is disconnected."), + CSRField("cs", size=4, offset=4, access=CSRAccess.WriteOnly, + description="Drives the state of the CS pads (up to 4, active high).")], + name="w", description="SPI master output pad controls.") self._r = CSRStatus(fields=[ - CSRField("miso", size=1, offset=0), - CSRField("mosi", size=1, offset=1)], - name="r") + CSRField("miso", size=1, offset=0, access=CSRAccess.ReadOnly, + description="Contains the current state of the MISO pad."), + CSRField("mosi", size=1, offset=1, access=CSRAccess.ReadOnly, + description="Contains the current state of the MOSI pad.")], + name="r", description="SPI master input pad states.") # # #