# -*- coding: utf-8 -*-
#
# Copyright 2013-2017 Robert Jordens <jordens@gmail.com>
#
# This file is part of pdq.
#
# pdq is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pdq is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pdq. If not, see <http://www.gnu.org/licenses/>.
from migen import *
from migen.genlib.cdc import MultiReg
from migen.genlib.misc import WaitTimer
from misoc.interconnect.stream import Endpoint
[docs]class Debouncer(Module):
"""Debounce a signal.
The initial change on input is passed through immediately. But
further changes are suppressed for `cycles`.
Args:
cycles (int): Block furhter level changes for that many cycles
after an initial change.
Attributes:
i (Signal): Input, needs a `MultiReg` in front of it
if this is an asynchronous signal.
o (Signal): Debounced output.
"""
def __init__(self, cycles=1):
self.i = Signal()
self.o = Signal()
###
timer = WaitTimer(cycles - 1)
self.submodules += timer
new = Signal()
rst = Signal(reset=1)
self.sync += [
If(timer.wait,
If(timer.done,
timer.wait.eq(0),
new.eq(~new),
),
).Elif(self.i == new,
timer.wait.eq(1),
),
If(rst,
rst.eq(0),
timer.wait.eq(0),
new.eq(~self.i),
),
]
self.comb += [
self.o.eq(Mux(timer.wait, new, self.i)),
]
[docs]class ShiftRegister(Module):
"""Shift register for an SPI slave.
Args:
width (int): Register width in bits.
Attributes:
i (Signal): Serial input.
o (Signal): Serial output.
data (Signal(width)): Content of the shift register.
next (Signal(width)): Combinatorial content of the
register in the next cycle.
stb (Signal): Strobe signal indicating that `width` bits have been
shifted (in and out) and the register value can be swapped.
"""
def __init__(self, width):
self.i = Signal()
self.o = Signal()
self.data = Signal(width)
self.next = Signal(width)
self.stb = Signal() # width bits available in next
###
n = Signal(max=width)
self.comb += [
self.o.eq(self.data[-1]),
self.next.eq(Cat(self.i, self.data)),
self.stb.eq(n == width - 1),
]
self.sync += [
n.eq(n + 1),
If(self.stb,
n.eq(0),
).Else(
self.data.eq(self.next),
),
]
spi_layout = [
("cs_n", 1, DIR_M_TO_S),
("clk", 1, DIR_M_TO_S),
("mosi", 1, DIR_M_TO_S),
("miso", 1, DIR_S_TO_M),
("oe_m", 1, DIR_M_TO_S),
("oe_s", 1, DIR_S_TO_M),
]
[docs]@ResetInserter()
class SPISlave(Module):
"""SPI slave.
* CLK_PHA, CLK_POL = 0,0
* MSB first
Args:
width (int): Shift register width in bits.
Attributes:
spi (Record): SPI bus record consisting of `cs_n`, `clk`, `mosi`,
`miso`, `oe_s`, and `oe_m`. Use `oe_s` (driven by the slave) to
wire up a tristate half-duplex data line. Use `oe_m` on the master
side.
data (Endpoint): SPI parallel communication stream.
* `mosi`: `width` bits read on the mosi line in the previous clock
cycles
* `miso`: `width` bits to be written on miso line in the next
cycles
* `stb`: data available in mosi and data read from miso.
* `ack`: in half-duplex mode, drive miso on the combined
miso/mosi data line
cs_n (Signal): use to `s.reset.eq(s.cs_n)` and for framing logic.
"""
def __init__(self, width=8):
self.spi = spi = Record(spi_layout)
self.mosi = mosi = Endpoint([("data", width)])
self.miso = miso = Endpoint([("data", width)])
self.cs_n = Signal()
###
inp = Debouncer(cycles=1)
sr = CEInserter()(ShiftRegister(width))
self.submodules += inp, sr
self.specials += [
MultiReg(self.spi.clk, inp.i),
MultiReg(self.spi.cs_n, self.cs_n),
MultiReg(self.spi.mosi, sr.i, n=3), # latency matching
]
clk0 = Signal()
edge = Signal()
self.sync += [
clk0.eq(inp.o),
If(edge & ~inp.o, # falling
spi.miso.eq(sr.o),
),
If(mosi.stb,
sr.data.eq(miso.data),
spi.oe_s.eq(miso.stb),
),
]
self.comb += [
edge.eq(clk0 != inp.o),
sr.ce.eq(edge & inp.o), # rising
mosi.stb.eq(sr.stb & sr.ce),
miso.ack.eq(mosi.stb),
mosi.data.eq(sr.next),
mosi.eop.eq(self.cs_n),
]