diff --git a/artiq/gateware/rtio/phy/fastino.py b/artiq/gateware/rtio/phy/fastino.py index 14d9d9cf1..ba8a7b5c9 100644 --- a/artiq/gateware/rtio/phy/fastino.py +++ b/artiq/gateware/rtio/phy/fastino.py @@ -4,132 +4,7 @@ from migen.genlib.io import DifferentialOutput, DifferentialInput, DDROutput from misoc.cores.liteeth_mini.mac.crc import LiteEthMACCRCEngine from artiq.gateware.rtio import rtlink - - -class SerDes(Module): - def transpose(self, i, n): - # i is n,m c-contiguous - # o is m,n c-contiguous - m = len(i)//n - assert n*m == len(i) - - def __init__(self, pins, pins_n): - n_bits = 16 # bits per dac data word - n_channels = 32 # channels per fastino - n_div = 7 # bits per lane and word - assert n_div == 7 - n_frame = 14 # word per frame - n_lanes = len(pins.mosi) # number of data lanes - n_checksum = 12 # checksum bits - n_addr = 4 # readback address bits - n_word = n_lanes*n_div - n_body = n_word*n_frame - (n_frame//2 + 1) - n_checksum - - # dac data words - self.dacs = [Signal(n_bits) for i in range(n_channels)] - # dac update enable - self.enable = Signal(n_channels) - # configuration word - self.cfg = Signal(20) - # readback data - self.dat_r = Signal(n_frame//2*(1 << n_addr)) - # data load synchronization event - self.stb = Signal() - - # # # - - # crc-12 telco - self.submodules.crc = LiteEthMACCRCEngine( - data_width=2*n_lanes, width=n_checksum, polynom=0x80f) - - addr = Signal(4) - body_ = Cat(self.cfg, addr, self.enable, self.dacs) - assert len(body_) == n_body - body = Signal(n_body) - self.comb += body.eq(body_) - - words_ = [] - j = 0 - for i in range(n_frame): # iterate over words - if i == 0: # data and checksum - k = n_word - n_checksum - elif i == 1: # marker - words_.append(C(1)) - k = n_word - 1 - elif i < n_frame//2 + 2: # marker - words_.append(C(0)) - k = n_word - 1 - else: # full word - k = n_word - # append corresponding frame body bits - words_.append(body[j:j + k]) - j += k - words_ = Cat(words_) - assert len(words_) == n_frame*n_word - n_checksum - words = Signal(len(words_)) - self.comb += words.eq(words_) - - clk = Signal(n_div, reset=0b1100011) - clk_stb = Signal() - i_frame = Signal(max=n_div*n_frame//2) # DDR - frame_stb = Signal() - sr = [Signal(n_frame*n_div - n_checksum//n_lanes, reset_less=True) - for i in range(n_lanes)] - assert len(Cat(sr)) == len(words) - # DDR bits for each register - ddr_data = Cat([sri[-2] for sri in sr], [sri[-1] for sri in sr]) - self.comb += [ - # assert one cycle ahead - clk_stb.eq(~clk[0] & clk[-1]), - # double period because of DDR - frame_stb.eq(i_frame == n_div*n_frame//2 - 1), - - # LiteETHMACCRCEngine takes data LSB first - self.crc.data[::-1].eq(ddr_data), - self.stb.eq(frame_stb & clk_stb), - ] - miso = Signal() - miso_sr = Signal(n_frame, reset_less=True) - self.sync.rio_phy += [ - # shift 7 bit clock pattern by two bits each DDR cycle - clk.eq(Cat(clk[-2:], clk)), - [sri[2:].eq(sri) for sri in sr], - self.crc.last.eq(self.crc.next), - If(clk[:2] == 0, # TODO: tweak MISO sampling - miso_sr.eq(Cat(miso, miso_sr)), - ), - If(~frame_stb, - i_frame.eq(i_frame + 1), - ), - If(frame_stb & clk_stb, - i_frame.eq(0), - self.crc.last.eq(0), - # transpose, load - Cat(sr).eq(Cat(words[mm::n_lanes] for mm in range(n_lanes))), - Array([self.dat_r[i*n_frame//2:(i + 1)*n_frame//2] - for i in range(1 << len(addr))])[addr].eq(miso_sr), - addr.eq(addr + 1), - ), - If(i_frame == n_div*n_frame//2 - 2, - # inject crc - ddr_data.eq(self.crc.next), - ), - ] - - clk_ddr = Signal() - miso0 = Signal() - self.specials += [ - DDROutput(clk[-1], clk[-2], clk_ddr, ClockSignal("rio_phy")), - DifferentialOutput(clk_ddr, pins.clk, pins_n.clk), - DifferentialInput(pins.miso, pins_n.miso, miso0), - MultiReg(miso0, miso, "rio_phy"), - ] - for sri, ddr, mp, mn in zip( - sr, Signal(n_lanes), pins.mosi, pins_n.mosi): - self.specials += [ - DDROutput(sri[-1], sri[-2], ddr, ClockSignal("rio_phy")), - DifferentialOutput(ddr, mp, mn), - ] +from .fastlink import SerDes, SerInterface class Fastino(Module): @@ -139,9 +14,31 @@ class Fastino(Module): rtlink.OInterface(data_width=max(16*width, 32), address_width=8, enable_replace=False), - rtlink.IInterface(data_width=32)) + rtlink.IInterface(data_width=14)) - self.submodules.serializer = SerDes(pins, pins_n) + self.submodules.serializer = SerDes( + n_data=8, t_clk=7, d_clk=0b1100011, + n_frame=14, n_crc=12, poly=0x80f) + self.submodules.intf = SerInterface(pins, pins_n) + self.comb += [ + Cat(self.intf.data[:-1]).eq(Cat(self.serializer.data[:-1])), + self.serializer.data[-1].eq(self.intf.data[-1]), + ] + + # dac data words + dacs = [Signal(16) for i in range(32)] + header = Record([ + ("cfg", 4), + ("leds", 8), + ("reserved", 8), + ("addr", 4), + ("enable", len(dacs)), + ]) + body = Cat(header.raw_bits(), dacs) + assert len(body) == len(self.serializer.payload) + self.comb += self.serializer.payload.eq(body) + + # # # # Support staging DAC data (in `dacs`) by writing to the # DAC RTIO addresses, if a channel is not "held" by its @@ -164,37 +61,36 @@ class Fastino(Module): # LSBs of the RTIO address for a DAC channel write must be zero and the # address space is sparse. - hold = Signal.like(self.serializer.enable) + hold = Signal.like(header.enable) - # TODO: stb, timestamp - read_regs = Array([ - self.serializer.dat_r[i*7:(i + 1)*7] - for i in range(1 << 4) - ]) + read_regs = Array([Signal.like(self.serializer.readback) + for _ in range(1 << len(header.addr))]) cases = { # update - 0x20: self.serializer.enable.eq(self.serializer.enable | self.rtlink.o.data), + 0x20: header.enable.eq(header.enable | self.rtlink.o.data), # hold 0x21: hold.eq(self.rtlink.o.data), # cfg - 0x22: self.serializer.cfg[:4].eq(self.rtlink.o.data), + 0x22: header.cfg.eq(self.rtlink.o.data), # leds - 0x23: self.serializer.cfg[4:12].eq(self.rtlink.o.data), + 0x23: header.leds.eq(self.rtlink.o.data), # reserved - 0x24: self.serializer.cfg[12:].eq(self.rtlink.o.data), + 0x24: header.reserved.eq(self.rtlink.o.data), } - for i in range(0, len(self.serializer.dacs), width): + for i in range(0, len(dacs), width): cases[i] = [ - Cat(self.serializer.dacs[i:i + width]).eq(self.rtlink.o.data), + Cat(dacs[i:i + width]).eq(self.rtlink.o.data), [If(~hold[i + j], - self.serializer.enable[i + j].eq(1), + header.enable[i + j].eq(1), ) for j in range(width)] ] self.sync.rio_phy += [ If(self.serializer.stb, - self.serializer.enable.eq(0), + header.enable.eq(0), + read_regs[header.addr].eq(self.serializer.readback), + header.addr.eq(header.addr + 1), ), If(self.rtlink.o.stb & ~self.rtlink.o.address[-1], Case(self.rtlink.o.address[:-1], cases),