soc/cores/bitbang: Revert 1256ca3767
.
This commit is contained in:
parent
341850939e
commit
c2c8504bec
|
@ -4,22 +4,19 @@
|
||||||
# Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
# Copyright (c) 2019 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
from typing import Optional, Tuple, List
|
|
||||||
|
|
||||||
from migen import *
|
from migen import *
|
||||||
from migen.fhdl.specials import Tristate
|
from migen.fhdl.specials import Tristate
|
||||||
|
|
||||||
from litex.soc.interconnect.csr import *
|
from litex.soc.interconnect.csr import *
|
||||||
|
|
||||||
|
|
||||||
# I2C Master Bit-Banging ---------------------------------------------------------------------------
|
# I2C Master Bit-Banging ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class I2CMaster(Module, AutoCSR):
|
class I2CMaster(Module, AutoCSR):
|
||||||
"""I2C bus master (bit-banged).
|
"""I2C bus master (bit-banged).
|
||||||
|
|
||||||
This core provides minimal hardware for use as a software controlled bit-banged I2C bus master via a memory-mapped
|
This core provides minimal hardware for use as a software controlled bit-banged I2C bus master. I2C uses a
|
||||||
interface. I2C uses a tristate/open-drain output driver configuration with pull-up resistors, and this core expects
|
tristate/open-drain output driver configuration with pull-up resistors, and this core expects that the pull-ups will
|
||||||
that the pull-ups willbe provided externally.
|
be provided externally.
|
||||||
|
|
||||||
Further information about the I2C bus can be found in the I2C standard document from NXP, `UM10204`_.
|
Further information about the I2C bus can be found in the I2C standard document from NXP, `UM10204`_.
|
||||||
|
|
||||||
|
@ -27,38 +24,36 @@ class I2CMaster(Module, AutoCSR):
|
||||||
"""
|
"""
|
||||||
pads_layout = [("scl", 1), ("sda", 1)]
|
pads_layout = [("scl", 1), ("sda", 1)]
|
||||||
|
|
||||||
def __init__(self, pads: Optional[Record] = None, default_dev: bool = False):
|
def __init__(self, pads=None, default_dev=False):
|
||||||
"""
|
"""
|
||||||
Class constructor.
|
Class constructor.
|
||||||
|
|
||||||
:param pads: (optional) A ``Record`` object containing the pads ``scl`` and ``sda``.
|
: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
|
:param default_dev: (optional) A `bool` indicating whether this I2C master should be used as the default I2C
|
||||||
interface (default is ``False``)
|
interface (default is ``False``)
|
||||||
"""
|
"""
|
||||||
self.init = []
|
self.init = []
|
||||||
|
|
||||||
if pads is None:
|
if pads is None:
|
||||||
pads = Record(self.pads_layout)
|
pads = Record(self.pads_layout)
|
||||||
self.pads = pads
|
self.pads = pads
|
||||||
|
|
||||||
self._w = CSRStorage(fields=[
|
self._w = CSRStorage(fields=[
|
||||||
CSRField("scl", size=1, offset=0, reset=1, description="Drives the state of the SCL pad."),
|
CSRField("scl", size=1, offset=0, reset=1, access=CSRAccess.WriteOnly,
|
||||||
CSRField("oe", size=1, offset=1,
|
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."),
|
description="Output Enable - if 0, both the SCL and SDA output drivers are disconnected."),
|
||||||
CSRField("sda", size=1, offset=2, reset=1, description="Drives the state of the SDA pad.")
|
CSRField("sda", size=1, offset=2, reset=1, access=CSRAccess.WriteOnly,
|
||||||
],
|
description="Drives the state of the SDA pad.")],
|
||||||
name="w", description="I2C master output pad controls.")
|
name="w")
|
||||||
|
|
||||||
self._r = CSRStatus(fields=[
|
self._r = CSRStatus(fields=[
|
||||||
CSRField("sda", size=1, offset=0, description="Contains the current state of the SDA pad.")
|
CSRField("sda", size=1, offset=0, access=CSRAccess.ReadOnly,
|
||||||
],
|
description="Contains the current state of the SDA pad.")],
|
||||||
name="r", description="SPI master input pad states.")
|
name="r")
|
||||||
|
|
||||||
self.default_dev = default_dev
|
self.default_dev = default_dev
|
||||||
|
|
||||||
self._connect(pads)
|
self.connect(pads)
|
||||||
|
|
||||||
def _connect(self, pads: Record):
|
def connect(self, pads):
|
||||||
"""
|
"""
|
||||||
Attaches the signals from inside the core to the input/output pads. This function is normally only called from
|
Attaches the signals from inside the core to the input/output pads. This function is normally only called from
|
||||||
inside the class constructor.
|
inside the class constructor.
|
||||||
|
@ -66,20 +61,18 @@ class I2CMaster(Module, AutoCSR):
|
||||||
:param pads: A ``Record`` object containing the pads ``scl`` and ``sda``.
|
:param pads: A ``Record`` object containing the pads ``scl`` and ``sda``.
|
||||||
"""
|
"""
|
||||||
# SCL
|
# SCL
|
||||||
self.specials += Tristate(
|
self.specials += Tristate(pads.scl,
|
||||||
pads.scl,
|
o = 0, # I2C uses Pull-ups, only drive low.
|
||||||
o=0, # I2C uses Pull-ups, only drive low.
|
oe = ~self._w.fields.scl # Drive when scl is low.
|
||||||
oe=~self._w.fields.scl # Drive when scl is low.
|
|
||||||
)
|
)
|
||||||
# SDA
|
# SDA
|
||||||
self.specials += Tristate(
|
self.specials += Tristate(pads.sda,
|
||||||
pads.sda,
|
o = 0, # I2C uses Pull-ups, only drive 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.
|
||||||
oe=self._w.fields.oe & ~self._w.fields.sda, # Drive when oe and sda is low.
|
i = self._r.fields.sda
|
||||||
i=self._r.fields.sda
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_init(self, addr: int, init: bytes, init_addr_len: int = 1):
|
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
|
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.
|
multiple transactions that will be executed in order for this core instance.
|
||||||
|
@ -106,7 +99,7 @@ class I2CMasterSim(I2CMaster):
|
||||||
"""
|
"""
|
||||||
pads_layout = [("scl", 1), ("sda_in", 1), ("sda_out", 1)]
|
pads_layout = [("scl", 1), ("sda_in", 1), ("sda_out", 1)]
|
||||||
|
|
||||||
def _connect(self, pads: Record):
|
def connect(self, pads):
|
||||||
_sda_w = Signal()
|
_sda_w = Signal()
|
||||||
_sda_oe = Signal()
|
_sda_oe = Signal()
|
||||||
_sda_r = Signal()
|
_sda_r = Signal()
|
||||||
|
@ -114,8 +107,8 @@ class I2CMasterSim(I2CMaster):
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
pads.scl.eq(self._w.fields.scl),
|
pads.scl.eq(self._w.fields.scl),
|
||||||
_sda_oe.eq(self._w.fields.oe),
|
_sda_oe.eq( self._w.fields.oe),
|
||||||
_sda_w.eq(self._w.fields.sda),
|
_sda_w.eq( self._w.fields.sda),
|
||||||
If(_sda_oe,
|
If(_sda_oe,
|
||||||
pads.sda_out.eq(_sda_w),
|
pads.sda_out.eq(_sda_w),
|
||||||
self._r.fields.sda.eq(_sda_w),
|
self._r.fields.sda.eq(_sda_w),
|
||||||
|
@ -125,18 +118,17 @@ class I2CMasterSim(I2CMaster):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# I2C Master Info Collection ----------------------------------------------------------------------
|
# I2C Master Info Collection ----------------------------------------------------------------------
|
||||||
|
|
||||||
def collect_i2c_info(soc) -> Tuple[List[Tuple[str, bool]], List[Tuple[str, int, bytes, int]]]:
|
# 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
|
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``.
|
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.
|
See ``I2CMaster.add_init`` for more information.
|
||||||
|
|
||||||
TODO: Find a more generic way to do it that would also apply to other peripherals?
|
|
||||||
|
|
||||||
:param soc: ``SoCBase`` instance to scan for ``I2CMaster`` instances.
|
: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
|
: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
|
a list of tuples, where each tuple is (core instance name, slave address, bytes to write, slave address
|
||||||
|
@ -152,26 +144,23 @@ def collect_i2c_info(soc) -> Tuple[List[Tuple[str, bool]], List[Tuple[str, int,
|
||||||
i2c_init.append((name, addr, init, init_addr_len))
|
i2c_init.append((name, addr, init, init_addr_len))
|
||||||
return i2c_devs, i2c_init
|
return i2c_devs, i2c_init
|
||||||
|
|
||||||
|
|
||||||
# SPI Master Bit-Banging ---------------------------------------------------------------------------
|
# SPI Master Bit-Banging ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class SPIMaster(Module, AutoCSR):
|
class SPIMaster(Module, AutoCSR):
|
||||||
"""3/4-wire SPI bus master (bit-banged).
|
"""3/4-wire SPI bus master (bit-banged).
|
||||||
|
|
||||||
This core provides minimal hardware for use as a software controlled bit-banged SPI bus master via a memory-mapped
|
This core provides minimal hardware for use as a software controlled bit-banged SPI bus master.
|
||||||
interface.
|
|
||||||
|
|
||||||
This core supports the typical SPI pads (MOSI, MISO, CLK) and a maximum of 4 CS outputs. If pull-up resistors are
|
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.
|
needed for 3 wire operation, they must be added externally.
|
||||||
"""
|
"""
|
||||||
pads_layout = [("clk", 1), ("cs_n", 4), ("mosi", 1), ("miso", 1)]
|
pads_layout = [("clk", 1), ("cs_n", 4), ("mosi", 1), ("miso", 1)]
|
||||||
|
|
||||||
def __init__(self, pads: Optional[Record] = None):
|
def __init__(self, pads=None):
|
||||||
"""
|
"""
|
||||||
Class constructor.
|
Class constructor.
|
||||||
|
|
||||||
:param pads: (optional) A ``Record`` object containing: ``clk``, ``cs_n``, ``mosi`` and ``miso``. ``cs_n`` may
|
:param pads: (optional) A ``Record`` object containing: ``clk``, ``cs_n``, ``mosi`` and ``miso``.
|
||||||
have a length of less than or equal to 4.
|
|
||||||
"""
|
"""
|
||||||
if pads is None:
|
if pads is None:
|
||||||
pads = Record(self.pads_layout)
|
pads = Record(self.pads_layout)
|
||||||
|
@ -181,35 +170,35 @@ class SPIMaster(Module, AutoCSR):
|
||||||
raise ValueError("This core only supports a maximum of 4 CS outputs")
|
raise ValueError("This core only supports a maximum of 4 CS outputs")
|
||||||
|
|
||||||
self._w = CSRStorage(fields=[
|
self._w = CSRStorage(fields=[
|
||||||
CSRField("clk", size=1, offset=0, description="Drives the state of the CLK pad."),
|
CSRField("clk", size=1, offset=0, access=CSRAccess.WriteOnly,
|
||||||
CSRField("mosi", size=1, offset=1, description="Drives the state of the MOSI pad."),
|
description="Drives the state of the CLK pad."),
|
||||||
CSRField("oe", size=1, offset=2,
|
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."),
|
description="Output Enable for MOSI - if 0, the MOSI output driver is disconnected."),
|
||||||
CSRField("cs", size=4, offset=4,
|
CSRField("cs", size=4, offset=4, access=CSRAccess.WriteOnly,
|
||||||
description="Drives the state of the CS pads (up to 4, active high).")
|
description="Drives the state of the CS pads (up to 4, active high).")],
|
||||||
],
|
|
||||||
name="w", description="SPI master output pad controls.")
|
name="w", description="SPI master output pad controls.")
|
||||||
|
|
||||||
self._r = CSRStatus(fields=[
|
self._r = CSRStatus(fields=[
|
||||||
CSRField("miso", size=1, offset=0, description="Contains the current state of the MISO pad."),
|
CSRField("miso", size=1, offset=0, access=CSRAccess.ReadOnly,
|
||||||
CSRField("mosi", size=1, offset=1, description="Contains the current state of the MOSI pad.")
|
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.")
|
name="r", description="SPI master input pad states.")
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
_mosi_w = Signal()
|
_mosi_w = Signal()
|
||||||
_mosi_oe = Signal()
|
_mosi_oe = Signal()
|
||||||
_mosi_r = Signal()
|
_mosi_r = Signal()
|
||||||
_cs = Signal(4)
|
_cs = Signal(4)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
pads.clk.eq(self._w.fields.clk),
|
pads.clk.eq( self._w.fields.clk),
|
||||||
_mosi_w.eq(self._w.fields.mosi),
|
_mosi_w.eq( self._w.fields.mosi),
|
||||||
_mosi_oe.eq(self._w.fields.oe),
|
_mosi_oe.eq( self._w.fields.oe),
|
||||||
pads.cs_n.eq(~self._w.fields.cs),
|
pads.cs_n.eq(~self._w.fields.cs),
|
||||||
self._r.fields.mosi.eq(_mosi_r),
|
self._r.fields.mosi.eq(_mosi_r),
|
||||||
]
|
]
|
||||||
|
|
||||||
if hasattr(pads, "miso"):
|
if hasattr(pads, "miso"):
|
||||||
self.comb += self._r.fields.miso.eq(pads.miso)
|
self.comb += self._r.fields.miso.eq(pads.miso)
|
||||||
|
|
||||||
self.specials += Tristate(pads.mosi, _mosi_w, _mosi_oe, _mosi_r)
|
self.specials += Tristate(pads.mosi, _mosi_w, _mosi_oe, _mosi_r)
|
||||||
|
|
Loading…
Reference in New Issue