Skip to content

spi2: Put register to I/O block for the 7-series interface #168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 91 additions & 21 deletions misoc/cores/spi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self, width):
# Parallel data out (from serial)
self.pdi = Signal(width)
# Serial data out (from parallel)
self.sdo = Signal(reset_less=True)
self.sdo = Signal()
# Serial data in
# Must be sampled at a higher layer at self.sample
self.sdi = Signal()
Expand All @@ -65,7 +65,7 @@ def __init__(self, width):

###

sr = Signal(width, reset_less=True)
sr = Signal(width)

self.comb += [
self.pdi.eq(Mux(self.lsb_first,
Expand Down Expand Up @@ -277,31 +277,91 @@ def __init__(self, *pads):
i += n


class SPIInterfaceXC7Diff(Module):
def __init__(self, pads, pads_n):
class SPIInterfaceXC7(Module):
def __init__(self, pads, pads_n=None, sdo=None):
self.cs = Signal(len(getattr(pads, "cs_n", [0])))
self.cs_polarity = Signal.like(self.cs)
self.clk_next = Signal()
self.clk_polarity = Signal()
self.cs_next = Signal()
self.ce = Signal()
self.sample = Signal()
self.offline = Signal()
self.half_duplex = Signal()
self.offline_next = Signal()
self.half_duplex_next = Signal()
self.sdi = Signal()
self.sdo = Signal()

# All data signals outputting to IO elements should be in IOB.
if hasattr(pads, "mosi"):
assert "iob" in sdo.attr
self.sdo = sdo

def get_iob_offline_signal():
offline = Signal()
# HACK: Prohibit vivado from merging IOB FFs
#
# DONT_TOUCH/KEEP attribute is inapplicable here, it risks vivado
# synthesizing this FF with incompatible control signals initially
#
# All IOB FFs in the same OLOGIC slice MUST share the same (re)set
self.specials += Instance("FDSE",
attr={"iob"},
i_D=self.offline_next,
i_CE=1,
i_C=ClockSignal(),
i_S=ResetSignal(),
o_Q=offline)
return offline

# This function generates single-ended/differential I/O instances.
# Arguments (excluding pads) are fabric facing signals.
# Directions (being sources or sinks) are from I/O bank's perspective.
def generate_io(pad, pad_n=None, source=None, sink=None, tristate=None):
use_input = sink is not None
use_output = source is not None
use_tristate = tristate is not None

assert use_input or use_output

primitive = "{}{}BUF{}{}".format(
"I" if use_input else "",
"O" if use_output else "",
"T" if use_output and not use_input and use_tristate else "",
"DS" if pad_n is not None else "",
)

io_dict = {}
if use_input:
io_dict["o_O"] = sink
if use_output:
io_dict["i_I"] = source
if use_tristate:
io_dict["i_T"] = tristate

if use_input and use_output:
dev_pad = "io_IO"
elif use_input:
dev_pad = "i_I"
elif use_output:
dev_pad = "o_O"
io_dict[dev_pad] = pad
if pad_n is not None:
io_dict[dev_pad + "B"] = pad_n

self.specials += Instance(primitive, **io_dict)

cs = Signal.like(self.cs)
cs.reset = C((1 << len(self.cs)) - 1)
clk = Signal()
clk = Signal(attr={"iob"})
half_duplex = Signal()
miso = Signal()
mosi = Signal()
miso_reg = Signal(reset_less=True)
mosi_reg = Signal(reset_less=True)
self.comb += [
self.sdi.eq(Mux(self.half_duplex, mosi_reg, miso_reg))
self.sdi.eq(Mux(half_duplex, mosi_reg, miso_reg))
]
self.sync += [
half_duplex.eq(self.half_duplex_next),
If(self.ce,
cs.eq((Replicate(self.cs_next, len(self.cs))
& self.cs) ^ ~self.cs_polarity),
Expand All @@ -314,21 +374,31 @@ def __init__(self, pads, pads_n):
]

if hasattr(pads, "cs_n"):
cs.attr.add("iob")
for i in range(len(pads.cs_n)):
self.specials += Instance("OBUFTDS",
i_I=cs[i], i_T=self.offline,
o_O=pads.cs_n[i], o_OB=pads_n.cs_n[i])
self.specials += Instance("OBUFTDS",
i_I=clk, i_T=self.offline,
o_O=pads.clk, o_OB=pads_n.clk)
generate_io(pads.cs_n[i],
pad_n=pads_n.cs_n[i] if pads_n is not None else None,
source=cs[i],
tristate=get_iob_offline_signal())
generate_io(pads.clk,
pad_n=pads_n.clk if pads_n is not None else None,
source=clk,
tristate=get_iob_offline_signal())
if hasattr(pads, "mosi"):
self.specials += Instance("IOBUFDS",
o_O=mosi, i_I=self.sdo, i_T=self.offline | self.half_duplex,
io_IO=pads.mosi, io_IOB=pads_n.mosi)
sdo_out_disable = Signal(attr={"iob"}, reset=1)
self.sync += sdo_out_disable.eq(
self.offline_next | self.half_duplex_next)
generate_io(pads.mosi,
pad_n=pads_n.mosi if pads_n is not None else None,
source=self.sdo,
sink=mosi,
tristate=sdo_out_disable)
mosi_reg.attr.add("iob")
if hasattr(pads, "miso"):
self.specials += Instance("IOBUFDS",
o_O=miso, i_I=self.sdo, i_T=1,
io_IO=pads.miso, io_IOB=pads_n.miso)
generate_io(pads.miso,
pad_n=pads_n.miso if pads_n is not None else None,
sink=miso)
miso_reg.attr.add("iob")


class SPIInterfaceiCE40Diff(Module):
Expand Down