artiq/artiq/gateware/suservo/spi.py

105 lines
2.7 KiB
Python

import logging
from collections import namedtuple
from migen import *
from migen.genlib.fsm import FSM, NextState
from migen.genlib import io
from .tools import DiffMixin
logger = logging.getLogger(__name__)
# all times in cycles
SPIParams = namedtuple("SPIParams", [
"channels", # number of MOSI? data lanes
"width", # transfer width
"clk", # CLK half cycle width (in cycles)
])
class SPISimple(Module, DiffMixin):
"""Simple reduced SPI interface.
* Multiple MOSI lines
* Supports differential CLK/CS_N/MOSI
* Fixed CLK timing
* SPI MODE 0 (CPHA=0, CPOL=0)
"""
def __init__(self, pads, params):
self.params = p = params
self.data = [Signal(p.width, reset_less=True)
for i in range(p.channels)] # data to be output, MSB first
self.start = Signal() # start transfer
self.done = Signal() # transfer complete, next transfer can be
# started
###
assert p.clk >= 1
cs_n = self._diff(pads, "cs_n", output=True)
clk = self._diff(pads, "clk", output=True)
cnt = Signal(max=max(2, p.clk), reset_less=True)
cnt_done = Signal()
cnt_next = Signal()
self.comb += cnt_done.eq(cnt == 0)
self.sync += [
If(cnt_done,
If(cnt_next,
cnt.eq(p.clk - 1)
)
).Else(
cnt.eq(cnt - 1)
)
]
for i, d in enumerate(self.data):
self.comb += [
self._diff(pads, "mosi{}".format(i), output=True).eq(d[-1])
]
bits = Signal(max=p.width + 1, reset_less=True)
self.submodules.fsm = fsm = CEInserter()(FSM("IDLE"))
self.comb += [
fsm.ce.eq(cnt_done)
]
fsm.act("IDLE",
self.done.eq(1),
cs_n.eq(1),
If(self.start,
cnt_next.eq(1),
NextState("SETUP")
)
)
fsm.act("SETUP",
cnt_next.eq(1),
If(bits == 0,
NextState("IDLE")
).Else(
NextState("HOLD")
)
)
fsm.act("HOLD",
cnt_next.eq(1),
clk.eq(1),
NextState("SETUP")
)
self.sync += [
If(fsm.ce,
If(fsm.before_leaving("HOLD"),
bits.eq(bits - 1),
[d[1:].eq(d) for d in self.data]
),
If(fsm.ongoing("IDLE"),
bits.eq(p.width)
)
)
]