Files
mirny-pld/mirny.py

388 lines
12 KiB
Python

from migen import *
# increment this if the behavior (LEDs, registers, EEM pins) changes
__proto_rev__ = 0
class AsyncRst(Module):
def __init__(self, width=None, cd="sys", reset=0, **kwargs):
self.i = Signal(width, reset=reset, **kwargs)
self.o = Signal(width, **kwargs)
self.ce = Signal(reset=1)
for i in range(len(self.i)):
ini = (reset >> i) & 1
self.specials += Instance("FDCPE",
p_INIT=ini,
i_D=self.i[i],
i_C=ClockSignal(cd),
i_CE=self.ce,
i_PRE=ResetSignal(cd) if ini else 0,
i_CLR=ResetSignal(cd) if not ini else 0,
o_Q=self.o[i])
bus_layout = [
("adr", 7),
("re", 1),
("dat_r", 16),
("we", 1),
("dat_w", 16),
]
ext_layout = [
("cs", 1),
("sck", 1),
("sdo", 1),
("sdi", 1),
]
class REG(Module):
def __init__(self, width=None, read=True, write=True):
self.bus = Record(bus_layout)
if width is None:
width = len(self.bus.dat_w)
assert width <= len(self.bus.dat_w)
if write:
self.write = Signal(width)
self.sync.reg += If(self.bus.we, self.write.eq(self.bus.dat_w))
if read:
self.read = Signal(width)
self.comb += self.bus.dat_r.eq(self.read)
# R,F edges happen left in each column
# CSN -F__________R
# LI L
# LO L
# CLK _RFRFRFRFRF_
#fMOSI AABBWW0011
#rSDI AABBWW0011
#fWE WW
#fRE 11
#fSDO 0011
#fMISO 0011
#fN 443322110004
#fN A 332211000003
#fN D 222222221102
#
# default: falling
# MOSI->SDI: rising
class SR(Module):
def __init__(self):
self.bus = Record(bus_layout)
self.ext = Record(ext_layout)
self._slaves = []
sdi = Signal(reset_less=True)
sr_adr = Signal(len(self.bus.adr), reset_less=True)
sr_dat = Signal(len(self.bus.dat_w), reset_less=True)
we = Signal(reset_less=True)
width = len(sr_adr) + 1 + len(sr_dat)
# bits to be transferred - 1
n = AsyncRst(max=width, reset=width - 1)
p = AsyncRst()
self.submodules += n, p
self.comb += [
n.i.eq(n.o - 1),
n.ce.eq(n.o != 0),
p.i.eq(1),
p.ce.eq(n.o == len(sr_dat)),
self.ext.sdo.eq(sr_dat[-1]),
self.bus.adr.eq(sr_adr),
self.bus.dat_w.eq(Cat(sdi, sr_dat)),
self.bus.re.eq(p.ce),
self.bus.we.eq(~n.ce & we),
]
self.sync.sck += sdi.eq(self.ext.sdi)
self.sync += [
sr_dat.eq(self.bus.dat_w),
If(p.ce,
we.eq(sdi),
sr_dat.eq(self.bus.dat_r),
).Elif(~p.o,
sr_adr.eq(Cat(sdi, sr_adr)),
)
]
def _check_intersection(self, adr, mask):
for _, b_adr, b_mask in self._slaves:
if intersection((b_adr, b_mask), (adr, mask)):
raise ValueError("{} intersects {}".format(
(adr, mask), (b_adr, b_mask)))
def connect(self, bus, adr, mask):
adr &= mask
self._check_intersection(adr, mask)
self._slaves.append((bus, adr, mask))
stb = Signal()
self.comb += [
stb.eq(self.bus.adr & mask == adr),
bus.adr.eq(self.bus.adr),
bus.dat_w.eq(self.bus.dat_w),
bus.we.eq(self.bus.we & stb),
bus.re.eq(self.bus.re & stb),
If(stb,
self.bus.dat_r.eq(bus.dat_r)
)
]
def connect_ext(self, ext, adr, mask, sdi_passthrough=False):
adr &= mask
self._check_intersection(adr, mask)
self._slaves.append((ext, adr, mask))
stb = AsyncRst()
self.submodules += stb
self.comb += [
stb.i.eq(self.bus.adr & mask == adr),
stb.ce.eq(self.bus.re),
# don't glitch with &stb.o
ext.sck.eq(self.ext.sck),
ext.cs.eq(stb.o),
If(stb.o,
self.ext.sdo.eq(ext.sdo),
),
]
# Almazny shares one SDI with 4 devices, it cannot be masked by stb
if sdi_passthrough:
self.comb += ext.sdi.eq(self.ext.sdi)
else:
self.comb += ext.sdi.eq(self.ext.sdi & stb.o),
def intersection(a, b):
(aa, am), (ba, bm) = a, b
# TODO
return False
class Mirny(Module):
"""
Mirny IO router and configuration/status
========================================
Pin Out
-------
| EEM LVDS pair | Function |
|---------------+--------------|
| EEM 0 | SCLK |
| EEM 1 | MOSI |
| EEM 2 | MISO, MUXOUT |
| EEM 3 | CS |
| EEM 4 | SW0, MUXOUT0 |
| EEM 5 | SW1, MUXOUT1 |
| EEM 6 | SW2, MUXOUT2 |
| EEM 7 | SW3, MUXOUT3 |
SPI
---
SPI xfer is ADR(7), WE(1), DAT(REG: 16, ATT: 8, PLL: 32, MEZ: 8)
| ADDR | TARGET |
|------+--------|
| 0 | REG0 |
| 1 | REG1 |
| 2 | REG2 |
| 3 | REG3 |
| 4 | PLL0 |
| 5 | PLL1 |
| 6 | PLL2 |
| 7 | PLL3 |
| 8 | ATT0 |
| 9 | ATT1 |
| a | ATT2 |
| b | ATT3 |
| c | MEZ0 |
| d | MEZ1 |
| e | MEZ2 |
| f | MEZ3 |
The SPI interface is CPOL=0, CPHA=0, SPI mode 0, 4-wire, full fuplex.
Configuration register
The status bits are read on the falling edge of after the WE bit (8th
falling edge).
The configuration bits are updated at the last falling SCK edge of the SPI
transaction (24th falling edge). The initial state is 0 (all bits cleared).
The bits in the registers (from LSB to MSB) are:
| Name | Width | Function |
|-----------+-------+------------------------------------|
| HW_REV | 2 | HW rev |
| PROTO_REV | 2 | Protocol (see __proto_rev__) |
| IFC_MODE | 4 | IFC_MODE[0:4] |
| MUXOUT | 4 | Muxout values |
| Name | Width | Function |
|-----------+-------+------------------------------------|
| CE_N | 4 | PLL chip enable (bar) |
| CLK_SEL | 2 | Selects CLK source: |
| | | 0 OSC, 1 reserved, 2 MMCX, 3 SMA |
| DIV | 2 | Clock divider configuration: |
| | | 0: divide-by-one, |
| | | 1: reserved, |
| | | 2: divider-by-two, |
| | | 3: divide-by-four |
| ATT_RST | 1 | Attenuator reset |
| FSEN_N | 1 | LVDS fail safe, Type 2 (bar) |
| MUXOUT_EEM| 1 | route MUXOUT to EEM[4:8] |
| MEZ_OE_N | 1 | Mezzanine (Almazny) REG_NOE |
| Name | Width | Function |
|-----------+-------+------------------------------------|
| RF_SW | 4 | RF switch state |
Test points
-----------
The test points expose miscellaneous signals for debugging and are not part
of the protocol revision.
"""
def __init__(self, platform):
self.eem = eem = []
for i in range(8):
tsi = TSTriple()
eemi = platform.request("eem", i)
tsi._pin = eemi.io
eem.append(tsi)
self.specials += tsi.get_tristate(eemi.io)
self.comb += [
eemi.oe.eq(tsi.oe),
tsi.oe.eq(i in (2,)),
]
platform.add_period_constraint(eem[0]._pin, 8.)
platform.add_period_constraint(eem[3]._pin, 8.)
self.clock_domains.cd_sys = ClockDomain("sys")
self.clock_domains.cd_sck = ClockDomain("sck")
self.clock_domains.cd_reg = ClockDomain("reg", reset_less=True)
self.specials += [
Instance("BUFG",
i_I=eem[0].i,
o_O=self.cd_sck.clk),
]
self.comb += [
self.cd_sck.rst.eq(~eem[3].i),
self.cd_sys.clk.eq(~self.cd_sck.clk),
self.cd_sys.rst.eq(self.cd_sck.rst),
self.cd_reg.clk.eq(self.cd_sys.clk),
]
self.submodules.sr = SR()
mask = 0b0001111
self.comb += [
self.sr.ext.sck.eq(self.cd_sck.clk),
self.sr.ext.sdi.eq(eem[1].i),
eem[2].o.eq(self.sr.ext.sdo),
self.sr.ext.cs.eq(eem[3].i),
]
regs = [REG(), REG(width=12), REG(width=4)]
self.submodules += regs
for i, reg in enumerate(regs):
self.sr.connect(reg.bus, adr=i, mask=mask)
self.comb += [
regs[0].read[:8].eq(Cat(
platform.request("hw_rev"),
Constant(__proto_rev__, 2),
platform.request("ifc_mode"),
)),
regs[1].read.eq(regs[1].write),
regs[2].read.eq(regs[2].write),
]
clk = platform.request("clk")
clk_div = TSTriple()
self.specials += clk_div.get_tristate(clk.div)
# in_sel: 00: XO, 01: n/a (SMA+XO), 10: MMXC, 11: SMA
# dividers: 00(z): 1, 01(z): 1, 10(low): 2, 11(high) 4
self.comb += [
Cat(clk.in_sel, clk_div.o, clk_div.oe).eq(regs[1].write[4:8]),
platform.request("fsen").eq(~regs[1].write[9]),
]
mezz_io = platform.request("mezz_io")
mezz = []
for i in range(len(mezz_io)):
t = TSTriple()
self.specials += t.get_tristate(mezz_io[i])
mezz.append(t)
self.comb += [
mezz[7].oe.eq(1),
mezz[7].o.eq(regs[1].write[11]), # Almazny REG_NOE
mezz[2].oe.eq(0), # Almazny PGOOD
]
for i in range(4):
rf_sw = platform.request("rf_sw", i)
self.comb += [
rf_sw.eq(regs[2].write[i] | (
~regs[1].write[10] & eem[4 + i].i)),
eem[4 + i].oe.eq(regs[1].write[10]),
]
pll = platform.request("pll", i)
ext = Record(ext_layout)
self.sr.connect_ext(ext, adr=i + 4, mask=mask)
self.comb += [
pll.ce.eq(~regs[1].write[i]),
pll.scki.eq(ext.sck),
pll.sdi.eq(ext.sdi),
ext.sdo.eq(pll.muxout),
regs[0].read[i + 8].eq(pll.muxout),
eem[4 + i].o.eq(pll.muxout),
pll.le.eq(~ext.cs),
]
self.comb += platform.request("led", i).eq(
Cat(rf_sw, ~pll.muxout & ~regs[1].write[10]))
att = platform.request("att", i)
ext = Record(ext_layout)
self.sr.connect_ext(ext, adr=i + 8, mask=mask)
self.comb += [
att.rstn.eq(~regs[1].write[8]),
att.clk.eq(ext.sck),
att.s_in.eq(ext.sdi),
ext.sdo.eq(att.s_out),
att.le.eq(~ext.cs),
]
ext = Record(ext_layout)
self.sr.connect_ext(ext, adr=i + 12, mask=mask, sdi_passthrough=True)
self.comb += [
mezz[i + 3].oe.eq(1),
mezz[i + 3].o.eq(~ext.cs), # Almazny REG_LATCH
]
if i == 0:
self.comb += [
mezz[0].oe.eq(1),
mezz[0].o.eq(ext.sdi), # Almazny SER_MOSI
mezz[1].oe.eq(1),
mezz[1].o.eq(ext.sck), # Almazny SER_SCK
]
tp = [platform.request("tp", i) for i in range(5)]
self.comb += [
tp[0].eq(self.sr.ext.sck),
tp[1].eq(eem[1].i),
tp[2].eq(eem[2].i),
tp[3].eq(self.sr.bus.re),
tp[4].eq(self.sr.bus.we),
]