Source code for hwtLib.peripheral.spi.intf
from collections import deque
from hwt.constants import DIRECTION
from hwt.hdl.types.bits import HBits
from hwt.hwIO import HwIO
from hwt.hwIOs.hwIOTristate import HwIOTristateSig
from hwt.hwIOs.std import HwIOClk, HwIOSignal, HwIOVectSignal
from hwt.hwParam import HwParam
from hwt.pyUtils.typingFuture import override
from hwt.simulator.agentBase import SyncAgentBase
from hwtSimApi.agents.base import AgentBase
from hwtSimApi.hdlSimulator import HdlSimulator
from hwtSimApi.process_utils import OnRisingCallbackLoop, OnFallingCallbackLoop
from hwtSimApi.triggers import WaitCombRead, WaitWriteOnly, Timer
from pyMathBitPrecise.bit_utils import mask, get_bit
[docs]
class SpiAgent(SyncAgentBase):
"""
Simulation agent for SPI interface
:ivar ~.txData: data to transceiver container
:ivar ~.rxData: received data
:ivar ~.chipSelects: values of chip select
chipSelects, rxData and txData are lists of integers
"""
BITS_IN_WORD = 8
[docs]
def __init__(self, sim: HdlSimulator, hwIO: "Spi", allowNoReset=False):
AgentBase.__init__(self, sim, hwIO)
self.txData = deque()
self.rxData = deque()
self.chipSelects = deque()
self._txBitBuff = deque()
self._rxBitBuff = deque()
self.csMask = mask(hwIO.cs._dtype.bit_length())
self.slaveEn = False
# resolve clk and rstn
self.clk = self.hwIO._getAssociatedClk()._sigInside
self.rst, self.rstOffIn = self._discoverReset(hwIO, allowNoReset=allowNoReset)
# read on rising edge write on falling
self.monitorRx = OnRisingCallbackLoop(self.sim, self.clk,
self.monitorRx,
self.getEnable)
self.monitorTx = OnFallingCallbackLoop(self.sim, self.clk,
self.monitorTx,
self.getEnable)
self.driverRx = OnFallingCallbackLoop(self.sim, self.clk,
self.driverRx,
self.getEnable)
self.driverTx = OnRisingCallbackLoop(self.sim, self.clk,
self.driverTx,
self.getEnable)
[docs]
@override
def setEnable(self, en):
self._enabled = en
self.monitorRx.setEnable(en)
self.monitorTx.setEnable(en)
self.driverRx.setEnable(en)
self.driverTx.setEnable(en)
[docs]
def splitBits(self, v):
return deque([get_bit(v, i)
for i in range(self.BITS_IN_WORD - 1, -1, -1)])
[docs]
def mergeBits(self, bits):
t = HBits(self.BITS_IN_WORD, False)
val = 0
vld_mask = 0
for v in bits:
val <<= 1
val |= v.val
vld_mask <<= 1
vld_mask |= v.vld_mask
return t.getConstCls()(t, val, vld_mask)
[docs]
def readRxSig(self, sig):
d = sig.read()
bits = self._rxBitBuff
bits.append(d)
if len(bits) == self.BITS_IN_WORD:
self.rxData.append(self.mergeBits(bits))
self._rxBitBuff = []
[docs]
def writeTxSig(self, sig):
bits = self._txBitBuff
if not bits:
if not self.txData:
return
d = self.txData.popleft()
bits = self._txBitBuff = self.splitBits(d)
sig.write(bits.popleft())
[docs]
def monitorRx(self):
yield WaitCombRead()
if self.notReset():
cs = self.hwIO.cs.read()
cs = int(cs)
if cs != self.csMask: # if any slave is enabled
if not self._rxBitBuff:
self.chipSelects.append(cs)
self.readRxSig(self.hwIO.mosi)
# def monitorTx_pre_set(self):
# yield WaitWriteOnly()
# self.writeTxSig(self.intf.miso)
[docs]
def monitorTx(self):
yield WaitCombRead()
if self.notReset():
cs = self.hwIO.cs.read()
cs = int(cs)
if cs != self.csMask:
yield Timer(1)
yield WaitWriteOnly()
self.writeTxSig(self.hwIO.miso)
[docs]
def driverRx(self):
yield WaitCombRead()
if self.notReset() and self.slaveEn:
self.readRxSig(self.hwIO.miso)
[docs]
def driverTx(self):
yield WaitCombRead()
if self.notReset():
if not self._txBitBuff:
try:
cs = self.chipSelects.popleft()
except IndexError:
self.slaveEn = False
yield WaitWriteOnly()
self.hwIO.cs.write(self.csMask)
return
self.slaveEn = True
yield WaitWriteOnly()
self.hwIO.cs.write(cs)
yield WaitWriteOnly()
self.writeTxSig(self.hwIO.mosi)
[docs]
@override
def getDrivers(self):
yield self.driverRx()
yield self.driverTx()
[docs]
@override
def getMonitors(self):
yield self.monitorRx()
# yield self.monitorTx_pre_set()
yield self.monitorTx()
# http://www.corelis.com/education/SPI_Tutorial.htm
[docs]
class Spi(HwIO):
"""
Bare SPI interface (Serial peripheral interface)
.. hwt-autodoc::
"""
@override
def hwConfig(self):
self.SLAVE_CNT = HwParam(1)
self.HAS_MISO = HwParam(True)
self.HAS_MOSI = HwParam(True)
self.CLK_FREQ = HwParam(HwIOClk.DEFAULT_FREQ)
@override
def hwDeclr(self):
self.clk = HwIOClk()
self.clk.FREQ = self.CLK_FREQ
assert self.HAS_MOSI or self.HAS_MISO
if self.HAS_MOSI:
self.mosi = HwIOSignal() # master out slave in
if self.HAS_MISO:
self.miso = HwIOSignal(masterDir=DIRECTION.IN) # master in slave out
if self.SLAVE_CNT is not None:
self.cs = HwIOVectSignal(self.SLAVE_CNT) # chip select
self._associatedClk = self.clk
[docs]
@override
def _initSimAgent(self, sim: HdlSimulator):
self._ag = SpiAgent(sim, self)
[docs]
class SpiTristate(Spi):
"""
SPI interface where mosi and miso signal are merged into one tri-state wire
.. hwt-autodoc::
"""
@override
def hwConfig(self):
Spi.hwConfig(self)
self.DATA_WIDTH = HwParam(1)
@override
def hwDeclr(self):
self.clk = HwIOClk()
with self._hwParamsShared():
self.io = HwIOTristateSig() # mosi and miso in one wire
self.cs = HwIOVectSignal(self.SLAVE_CNT) # chip select
self._associatedClk = self.clk
[docs]
class QSPI(SpiTristate):
"""
SPI interface with 4 tristate data wires
.. hwt-autodoc::
"""
@override
def hwConfig(self):
Spi.hwConfig(self)
self.DATA_WIDTH = HwParam(4)