def __init__(self, pad): self.rtlink = rtlink.Interface( rtlink.OInterface(2, 2), rtlink.IInterface(1)) self.overrides = [] self.probes = [] # # # sensitivity = Signal(2) sample = Signal() self.sync.rio += [ sample.eq(0), If(self.rtlink.o.stb & self.rtlink.o.address[1], sensitivity.eq(self.rtlink.o.data), If(self.rtlink.o.address[0], sample.eq(1)) ) ] i = Signal() i_d = Signal() self.specials += MultiReg(pad, i, "rio_phy") self.sync.rio_phy += i_d.eq(i) self.comb += [ self.rtlink.i.stb.eq( sample | (sensitivity[0] & ( i & ~i_d)) | (sensitivity[1] & (~i & i_d)) ), self.rtlink.i.data.eq(i) ] self.probes += [i]
def __init__(self, o_width, i_width): self.rtlink = rtlink.Interface( rtlink.OInterface(o_width), rtlink.IInterface(i_width, timestamped=True)) self.received_data = Signal(o_width) self.sync.rio_phy += If(self.rtlink.o.stb, self.received_data.eq(self.rtlink.o.data))
def __init__(self, address_width, wb=None): if wb is None: wb = wishbone.Interface() self.wb = wb self.rtlink = rtlink.Interface( rtlink.OInterface(flen(wb.dat_w), address_width + 1, suppress_nop=False), rtlink.IInterface(flen(wb.dat_r), timestamped=False)) # # # active = Signal() self.sync.rio += [ If(self.rtlink.o.stb, active.eq(1), wb.adr.eq(self.rtlink.o.address[:address_width]), wb.we.eq(~self.rtlink.o.address[address_width]), wb.dat_w.eq(self.rtlink.o.data), wb.sel.eq(2**flen(wb.sel) - 1)), If(wb.ack, active.eq(0)) ] self.comb += [ self.rtlink.o.busy.eq(active), wb.cyc.eq(active), wb.stb.eq(active), self.rtlink.i.stb.eq(wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ]
def __init__(self, pins, roi_engine_count=16, res_width=12, count_shift=0): self.config = rtlink.Interface( rtlink.OInterface(res_width, bits_for(4*roi_engine_count-1))) self.gate_data = rtlink.Interface( rtlink.OInterface(roi_engine_count), rtlink.IInterface(1+ROI.count_len(res_width, count_shift), timestamped=False)) self.submodules.deserializer = deserializer_7series.Deserializer(pins) self.submodules.frequency_counter = FrequencyCounter() self.submodules.parser = Parser(res_width) self.comb += self.parser.cl.eq(self.deserializer.q) self.roi_engines = [ROI(self.parser.pix, count_shift) for _ in range(roi_engine_count)] self.submodules += self.roi_engines self.submodules.synchronizer = Synchronizer(self.roi_engines) self.submodules.serializer = Serializer(self.synchronizer.update, self.synchronizer.counts, self.gate_data.i) for n, roi_engine in enumerate(self.roi_engines): for offset, target in enumerate([roi_engine.cfg.x0, roi_engine.cfg.y0, roi_engine.cfg.x1, roi_engine.cfg.y1]): roi_boundary = Signal.like(target) roi_boundary.attr.add("no_retiming") self.sync.rtio += If(self.config.o.stb & (self.config.o.address == 4*n+offset), roi_boundary.eq(self.config.o.data)) self.specials += MultiReg(roi_boundary, target, "cl") self.sync.rio += If(self.gate_data.o.stb, self.serializer.gate.eq(self.gate_data.o.data))
def __init__(self, pad, pad_n=None): self.rtlink = rtlink.Interface(rtlink.OInterface(2, 2), rtlink.IInterface(1)) self.overrides = [] self.probes = [] # # # sensitivity = Signal(2) sample = Signal() self.sync.rio += [ sample.eq(0), If(self.rtlink.o.stb & self.rtlink.o.address[1], sensitivity.eq(self.rtlink.o.data), If(self.rtlink.o.address[0], sample.eq(1))) ] i = Signal() i_d = Signal(reset_less=True) pad_i = Signal() if pad_n is None: self.comb += pad_i.eq(pad) else: self.specials += DifferentialInput(pad, pad_n, pad_i) self.specials += MultiReg(pad_i, i, "rio_phy") self.sync.rio_phy += i_d.eq(i) self.comb += [ self.rtlink.i.stb.eq(sample | (sensitivity[0] & (i & ~i_d)) | (sensitivity[1] & (~i & i_d))), self.rtlink.i.data.eq(i) ] self.probes += [i]
def __init__(self, pad): self.rtlink = rtlink.Interface( rtlink.OInterface(2, 2), rtlink.IInterface(1)) override_en = Signal() override_o = Signal() override_oe = Signal() self.overrides = [override_en, override_o, override_oe] self.probes = [] # # # ts = TSTriple() self.specials += ts.get_tristate(pad) sensitivity = Signal(2) o_k = Signal() oe_k = Signal() self.sync.rio_phy += [ If(self.rtlink.o.stb, If(self.rtlink.o.address == 0, o_k.eq(self.rtlink.o.data[0])), If(self.rtlink.o.address == 1, oe_k.eq(self.rtlink.o.data[0])), ), If(override_en, ts.o.eq(override_o), ts.oe.eq(override_oe) ).Else( ts.o.eq(o_k), ts.oe.eq(oe_k) ) ] sample = Signal() self.sync.rio += [ sample.eq(0), If(self.rtlink.o.stb & self.rtlink.o.address[1], sensitivity.eq(self.rtlink.o.data), If(self.rtlink.o.address[0], sample.eq(1)) ) ] i = Signal() i_d = Signal() self.specials += MultiReg(ts.i, i, "rio_phy") self.sync.rio_phy += i_d.eq(i) self.comb += [ self.rtlink.i.stb.eq( sample | (sensitivity[0] & ( i & ~i_d)) | (sensitivity[1] & (~i & i_d)) ), self.rtlink.i.data.eq(i) ] self.probes += [i, ts.oe]
def __init__(self, pad): self.rtlink = rtlink.Interface(rtlink.OInterface(2, 2), rtlink.IInterface(1)) override_en = Signal() override_o = Signal() override_oe = Signal() self.overrides = [override_en, override_o, override_oe] self.probes = [] # Output enable, for interfacing to external buffers. self.oe = Signal() # Registered copy of the input state, in the rio_phy clock domain. self.input_state = Signal() # # # ts = TSTriple() self.specials += ts.get_tristate(pad) sensitivity = Signal(2) o_k = Signal() oe_k = Signal() self.oe.attr.add("no_retiming") self.sync.rio_phy += [ If( self.rtlink.o.stb, If(self.rtlink.o.address == 0, o_k.eq(self.rtlink.o.data[0])), If(self.rtlink.o.address == 1, oe_k.eq(self.rtlink.o.data[0])), ), If(override_en, ts.o.eq(override_o), self.oe.eq(override_oe)).Else(ts.o.eq(o_k), self.oe.eq(oe_k)) ] self.comb += ts.oe.eq(self.oe) sample = Signal() self.sync.rio += [ sample.eq(0), If(self.rtlink.o.stb & self.rtlink.o.address[1], sensitivity.eq(self.rtlink.o.data), If(self.rtlink.o.address[0], sample.eq(1))) ] i = Signal() i_d = Signal() self.specials += MultiReg(ts.i, i, "rio_phy") self.sync.rio_phy += i_d.eq(i) self.comb += [ self.rtlink.i.stb.eq(sample | (sensitivity[0] & (i & ~i_d)) | (sensitivity[1] & (~i & i_d))), self.rtlink.i.data.eq(i), self.input_state.eq(i) ] self.probes += [i, ts.oe]
def __init__(self, serdes): serdes_width = len(serdes.o) assert len(serdes.i) == serdes_width self.rtlink = rtlink.Interface( rtlink.OInterface(2, 2, fine_ts_width=log2_int(serdes_width)), rtlink.IInterface(1, fine_ts_width=log2_int(serdes_width))) self.probes = [serdes.i[-1], serdes.oe] override_en = Signal() override_o = Signal() override_oe = Signal() self.overrides = [override_en, override_o, override_oe] # # # # Output self.submodules += _SerdesDriver(serdes_o=serdes.o, stb=self.rtlink.o.stb & (self.rtlink.o.address == 0), data=self.rtlink.o.data[0], fine_ts=self.rtlink.o.fine_ts, override_en=override_en, override_o=override_o) oe_k = Signal() self.sync.rio_phy += [ If(self.rtlink.o.stb & (self.rtlink.o.address == 1), oe_k.eq(self.rtlink.o.data[0])), If(override_en, serdes.oe.eq(override_oe)).Else(serdes.oe.eq(oe_k)) ] # Input sensitivity = Signal(2) sample = Signal() self.sync.rio += [ sample.eq(0), If(self.rtlink.o.stb & self.rtlink.o.address[1], sensitivity.eq(self.rtlink.o.data), If(self.rtlink.o.address[0], sample.eq(1))) ] i = serdes.i[-1] i_d = Signal() self.sync.rio_phy += [ i_d.eq(i), self.rtlink.i.stb.eq(sample | (sensitivity[0] & (i & ~i_d)) | (sensitivity[1] & (~i & i_d))), self.rtlink.i.data.eq(i), ] pe = PriorityEncoder(serdes_width) self.submodules += pe self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width)) self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o)
def __init__(self, regs, name, identifier=None): self.name = name self.regs = regs data_width = max([x[1] for x in regs]) self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=data_width, address_width=len(regs).bit_length() + 1), rtlink.IInterface(data_width=data_width, timestamped=False)) write_enable = self.rtlink.o.address[0] address = self.rtlink.o.address[1:] data_signals_list = [] for idx, r in enumerate(regs): if len(r) > 2: reset_value = r[2] else: reset_value = 0 if len(r) > 3: mode = r[3] else: mode = "rw" signal = Signal(bits_sign=r[1], name=r[0], reset=reset_value) setattr(self, r[0], signal) data_signals_list.append(signal) if mode == "rw": ld_signal_name = r[0] + "_ld" setattr(self, ld_signal_name, Signal(bits_sign=1, name=ld_signal_name)) ld_signal = getattr(self, ld_signal_name) self.sync.rio_phy += [ ld_signal.eq(0), If(self.rtlink.o.stb & write_enable & (address == idx), signal.eq(self.rtlink.o.data), ld_signal.eq(1)) ] data_signals = Array(data_signals_list) self.sync.rio_phy += [ self.rtlink.i.stb.eq(0), If(self.rtlink.o.stb & ~write_enable, self.rtlink.i.stb.eq(1), self.rtlink.i.data.eq(data_signals[address])) ] if identifier is not None: self.add_rtio_channels(channel=Channel.from_phy(self), device_id=identifier, module="elhep_cores.coredevice.rtlink_csr", class_name="RtlinkCsr", arguments={"regs": regs})
def __init__(self): self.rtlink = rtlink.Interface(rtlink.OInterface(1), rtlink.IInterface(1)) self.overrides = [] self.probes = [] # # # counter = Signal(2) trigger = Signal() self.sync += [ Cat(counter, trigger).eq(counter + 1), self.rtlink.i.stb.eq(0), If(trigger, self.rtlink.i.stb.eq(1), self.rtlink.i.data.eq(~self.rtlink.i.data)) ]
def _add_rtlink(self): # Address 0: enabled # Address 1: pulse length # Address 2: mask mask_adr_no = (len(self.mask) + 31) // 32 adr_width = len(Signal(max=mask_adr_no + 1)) + 2 self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=32, address_width=adr_width), rtlink.IInterface(data_width=32, timestamped=False)) self.rtlink_address = rtlink_address = Signal.like( self.rtlink.o.address) self.rtlink_wen = rtlink_wen = Signal() self.comb += [ rtlink_address.eq(self.rtlink.o.address[1:]), rtlink_wen.eq(self.rtlink.o.address[0]), ] mask_array = self.signal_to_array(self.mask) self.sync.rio_phy += [ self.rtlink.i.stb.eq(0), If( self.rtlink.o.stb, # Write If(rtlink_wen & (rtlink_address == 0), self.enabled.eq(self.rtlink.o.data[0])).Elif( rtlink_wen & (rtlink_address == 1), self.pulse_length.eq(self.rtlink.o.data)).Elif( rtlink_wen & (rtlink_address >= 2), mask_array[rtlink_address - 2].eq( self.rtlink.o.data)). # Readout Elif(~rtlink_wen & (rtlink_address == 0), self.rtlink.i.data.eq(self.enabled), self.rtlink.i.stb.eq(1)).Elif( ~rtlink_wen & (rtlink_address == 1), self.rtlink.i.data.eq(self.pulse_length), self.rtlink.i.stb.eq(1)).Elif( ~rtlink_wen & (rtlink_address >= 2), self.rtlink.i.data.eq(mask_array[rtlink_address - 2]), self.rtlink.i.stb.eq(1))) ]
def __init__(self, servo): self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=32, address_width=8, enable_replace=False), rtlink.IInterface(data_width=32)) ### # reg addresses - starts with R/!W cases = { # control/config reg - rst, afe, dac_clr 0x00: servo.ctrl[:4].eq(self.rtlink.o.data), # enable channels 0x01: servo.ctrl[4:6].eq(self.rtlink.o.data), # enable ADC AFE X10 gain 0x02: servo.ctrl[6:].eq(self.rtlink.o.data), # set dac 0x03: servo.mode.eq(self.rtlink.o.data[-2:]), servo.dac_data.eq(self.rtlink.o.data[:-2]) # init/status reg 0x04: servo.init, # adc data 0x05: servo.adc_data, } input_data = Signal(32, reset_less=True) self.sync.rio_phy += [ If(self.rtlink.o.stb & ~self.rtlink.o.address[-1], Case(self.rtlink.o.address[:-1], cases) ), If(self.rtlink.i.stb, input_data.eq(Case(self.rtlink.o.address[:-1], cases)) ) ] self.comb += [ self.rtlink.i.stb.eq(self.rtlink.o.stb & self.rtlink.o.address[-1]), self.rtlink.i.data.eq(input_data), ]
def __init__(self, address_width, wb=None, rtio_enable_replace=False, write_only=False): if wb is None: wb = wishbone.Interface() self.wb = wb self.rtlink = rtlink.Interface( rtlink.OInterface(len(wb.dat_w), address_width + 1 if not write_only else address_width, enable_replace=rtio_enable_replace), rtlink.IInterface(len(wb.dat_r), timestamped=False) if not write_only else None) # # # active = Signal() self.sync.rio += [ If( self.rtlink.o.stb, active.eq(1), wb.adr.eq(self.rtlink.o.address[:address_width]), wb.we.eq(~self.rtlink.o.address[address_width] if not write_only else 1), wb.dat_w.eq(self.rtlink.o.data), wb.sel.eq(2**len(wb.sel) - 1)), If(wb.ack, active.eq(0)) ] self.comb += [ self.rtlink.o.busy.eq(active), wb.cyc.eq(active), wb.stb.eq(active), ] if not write_only: self.comb += [ self.rtlink.i.stb.eq(wb.ack & ~wb.we), self.rtlink.i.data.eq(wb.dat_r) ]
def __init__(self, data_i, stb_i, trigger_dclk, trigger_id_dclk=None, circular_buffer_length=128): iiface_width = len(data_i) + len(trigger_id_dclk) assert iiface_width <= 32, f"Data width summarized with trigger " \ "ID width ({iiface_width}) must be <= 32" self.data_i = data_i pretrigger_rio_phy = Signal(max=circular_buffer_length) posttrigger_rio_phy = Signal.like(pretrigger_rio_phy) pretrigger_dclk = Signal.like(pretrigger_rio_phy) posttrigger_dclk = Signal.like(posttrigger_rio_phy) # Interface - rtlink self.rtlink = rtlink_iface = rtlink.Interface( rtlink.OInterface(data_width=len(pretrigger_rio_phy), address_width=1), rtlink.IInterface(data_width=iiface_width, timestamped=False)) self.sync.rio_phy += [ If( rtlink_iface.o.stb, If(self.rtlink.o.address == 0, pretrigger_rio_phy.eq(rtlink_iface.o.data)), If(self.rtlink.o.address == 1, posttrigger_rio_phy.eq(rtlink_iface.o.data)), ) ] # We're embedding stb into data stream going into the cyclic buffer cb_data_in = Signal(len(data_i) + 1) self.comb += [cb_data_in.eq(Cat(stb_i, data_i))] self.cbuf = circular_buffer = ClockDomainsRenamer({"sys": "dclk"})( TriggeredCircularBuffer(data_width=len(cb_data_in), trigger_id_width=len(trigger_id_dclk), length=circular_buffer_length)) async_fifo = ClockDomainsRenamer({ "write": "dclk", "read": "rio_phy" })(AsyncFIFOBuffered(width=len(circular_buffer.data_out), depth=16)) trigger_cdc = PulseSynchronizer("rio_phy", "dclk") pretrigger_cdc = MultiReg(pretrigger_rio_phy, pretrigger_dclk, "dclk") posttrigger_cdc = MultiReg(posttrigger_rio_phy, posttrigger_dclk, "dclk") self.submodules += [circular_buffer, async_fifo, trigger_cdc] self.specials += [pretrigger_cdc, posttrigger_cdc] self.comb += [ circular_buffer.data_in.eq(cb_data_in), circular_buffer.we.eq(1), circular_buffer.trigger.eq(trigger_dclk), circular_buffer.trigger_id.eq(trigger_id_dclk), circular_buffer.pretrigger.eq(pretrigger_dclk), circular_buffer.posttrigger.eq(posttrigger_dclk), async_fifo.din.eq(circular_buffer.data_out), async_fifo.re.eq(async_fifo.readable), async_fifo.we.eq(circular_buffer.stb_out), rtlink_iface.i.data.eq(async_fifo.dout[1:]), rtlink_iface.i.stb.eq( async_fifo.dout[0] & async_fifo.readable) # stb if there is data and frame ]
def __init__(self, pins, pins_n, log2_width=0): width = 1 << log2_width self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=max(16 * width, 32), address_width=8, enable_replace=False), rtlink.IInterface(data_width=32)) self.submodules.serializer = SerDes(pins, pins_n) # Support staging DAC data (in `dacs`) by writing to the # DAC RTIO addresses, if a channel is not "held" by its # bit in `hold` the next frame will contain the update. # For the DACs held, the update is triggered by setting the # corresponding bit in `update`. Update is self-clearing. # This enables atomic DAC updates synchronized to a frame edge. # # The `log2_width=0` RTIO layout uses one DAC channel per RTIO address # and a dense RTIO address space. The RTIO words are narrow. # (32 bit compared to 512) and few-channel updates are efficient. # There is the least amount of DAC state tracking in kernels, # at the cost of more DMA and RTIO data ((n*(32+32+64) vs # 32+32*16+64)) # # Other `log2_width` (up to `log2_width=5) settings pack multiple # (in powers of two) DAC channels into one group and # into one RTIO write. # The RTIO data width increases accordingly. The `log2_width` # 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) # TODO: stb, timestamp read_regs = Array( [self.serializer.dat_r[i * 7:(i + 1) * 7] for i in range(1 << 4)]) cases = { # update 0x20: self.serializer.enable.eq(self.serializer.enable | self.rtlink.o.data), # hold 0x21: hold.eq(self.rtlink.o.data), # cfg 0x22: self.serializer.cfg[:4].eq(self.rtlink.o.data), # leds 0x23: self.serializer.cfg[4:12].eq(self.rtlink.o.data), # reserved 0x24: self.serializer.cfg[12:].eq(self.rtlink.o.data), } for i in range(0, len(self.serializer.dacs), width): cases[i] = [ Cat(self.serializer.dacs[i:i + width]).eq(self.rtlink.o.data), [ If( ~hold[i + j], self.serializer.enable[i + j].eq(1), ) for j in range(width) ] ] self.sync.rio_phy += [ If( self.serializer.stb, self.serializer.enable.eq(0), ), If( self.rtlink.o.stb & ~self.rtlink.o.address[-1], Case(self.rtlink.o.address[:-1], cases), ), ] self.sync.rtio += [ self.rtlink.i.stb.eq(self.rtlink.o.stb & self.rtlink.o.address[-1]), self.rtlink.i.data.eq(read_regs[self.rtlink.o.address[:-1]]), ]
def __init__(self, pins, pins_n): self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=32, address_width=8, enable_replace=False), rtlink.IInterface(data_width=32)) self.submodules.serializer = SerDes(pins, pins_n) # Support staging DAC data (in `dacs`) by writing to the # 32 DAC RTIO addresses, if a channel is not "held" by its # bit in `hold` the next frame will contain the update. # For the DACs held, the update is triggered by setting the # corresponding bit in `update`. Update is self-clearing. # This enables atomic DAC updates synchronized to a frame edge. # # This RTIO layout enables narrow RTIO words (32 bit # compared to 512), efficient few-channel updates, # least amount of DAC state tracking in kernels, # at the cost of more DMA and RTIO data ((n*(32+32+64) vs # 32+32*16+64)) hold = Signal.like(self.serializer.enable) # TODO: stb, timestamp read_regs = Array([ self.serializer.dat_r[i*7:(i + 1)*7] for i in range(1 << 4) ]) cases = { # update 0x20: self.serializer.enable.eq(self.serializer.enable | self.rtlink.o.data), # hold 0x21: hold.eq(self.rtlink.o.data), # cfg 0x22: self.serializer.cfg[:4].eq(self.rtlink.o.data), # leds 0x23: self.serializer.cfg[4:12].eq(self.rtlink.o.data), # reserved 0x24: self.serializer.cfg[12:].eq(self.rtlink.o.data), } for i in range(len(self.serializer.dacs)): cases[i] = [ self.serializer.dacs[i].eq(self.rtlink.o.data), If(~hold[i], self.serializer.enable[i].eq(1), ) ] self.sync.rio_phy += [ If(self.serializer.stb, self.serializer.enable.eq(0), ), If(self.rtlink.o.stb & ~self.rtlink.o.address[-1], Case(self.rtlink.o.address[:-1], cases), ), ] self.sync.rtio += [ self.rtlink.i.stb.eq(self.rtlink.o.stb & self.rtlink.o.address[-1]), self.rtlink.i.data.eq( read_regs[self.rtlink.o.address[:-1]]), ]
def __init__(self, pins): self.config = rtlink.Interface(rtlink.OInterface(10)) self.gate_data = rtlink.Interface(rtlink.OInterface(1), rtlink.IInterface(10)) self.submodules.deserializer = deserializer_7series.Deserializer(pins)
def __init__(self, pins, pins_n, log2_width=0): width = 1 << log2_width self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=max(16 * width, 32), address_width=8, enable_replace=False), rtlink.IInterface(data_width=14)) 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), ("typ", 1), ("reserved", 7), ("addr", 4), ("enable", len(dacs)), ]) assert len(Cat(header.raw_bits(), dacs)) == len(self.serializer.payload) # # # # Support staging DAC data (in `dacs`) by writing to the # DAC RTIO addresses, if a channel is not "held" by its # bit in `hold` the next frame will contain the update. # For the DACs held, the update is triggered by setting the # corresponding bit in `update`. Update is self-clearing. # This enables atomic DAC updates synchronized to a frame edge. # # The `log2_width=0` RTIO layout uses one DAC channel per RTIO address # and a dense RTIO address space. The RTIO words are narrow. # (32 bit compared to 512) and few-channel updates are efficient. # There is the least amount of DAC state tracking in kernels, # at the cost of more DMA and RTIO data ((n*(32+32+64) vs # 32+32*16+64)) # # Other `log2_width` (up to `log2_width=5) settings pack multiple # (in powers of two) DAC channels into one group and # into one RTIO write. # The RTIO data width increases accordingly. The `log2_width` # LSBs of the RTIO address for a DAC channel write must be zero and the # address space is sparse. hold = Signal.like(header.enable) continuous = Signal.like(header.enable) cic_config = Signal(16) read_regs = Array([ Signal.like(self.serializer.readback) for _ in range(1 << len(header.addr)) ]) cases = { # update 0x20: [ header.enable.eq(self.rtlink.o.data), header.typ.eq(0), ], # hold 0x21: hold.eq(self.rtlink.o.data), # cfg 0x22: header.cfg.eq(self.rtlink.o.data), # leds 0x23: header.leds.eq(self.rtlink.o.data), # reserved bits 0x24: header.reserved.eq(self.rtlink.o.data), # force continuous DAC updates 0x25: continuous.eq(self.rtlink.o.data), # interpolator configuration stage 0x26: cic_config.eq(self.rtlink.o.data), # interpolator update flags 0x27: [ header.enable.eq(self.rtlink.o.data), header.typ.eq(1), ], } for i in range(0, len(dacs), width): cases[i] = [ Cat(dacs[i:i + width]).eq(self.rtlink.o.data), [ If( ~hold[i + j] & (header.typ == 0), header.enable[i + j].eq(1), ) for j in range(width) ] ] self.comb += [ If( header.typ == 0, self.serializer.payload.eq(Cat(header.raw_bits(), dacs)), ).Else( self.serializer.payload.eq( Cat(header.raw_bits(), Replicate(cic_config, len(dacs)))), ), ] self.sync.rio_phy += [ If( self.serializer.stb, header.typ.eq(0), header.enable.eq(continuous), read_regs[header.addr].eq(self.serializer.readback), header.addr.eq(header.addr + 1), ), If( self.rtlink.o.stb, Case(self.rtlink.o.address, cases), ), ] self.sync.rtio += [ self.rtlink.i.stb.eq(self.rtlink.o.stb & self.rtlink.o.address[-1]), self.rtlink.i.data.eq(read_regs[self.rtlink.o.address[:-1]]), ]
def __init__(self, serdes): serdes_width = len(serdes.o) assert len(serdes.i) == serdes_width self.rtlink = rtlink.Interface( rtlink.OInterface(2, 2, fine_ts_width=log2_int(serdes_width)), rtlink.IInterface(1, fine_ts_width=log2_int(serdes_width))) self.probes = [serdes.i[-1], serdes.oe] override_en = Signal() override_o = Signal() override_oe = Signal() self.overrides = [override_en, override_o, override_oe] # Output enable, for interfacing to external buffers. self.oe = Signal() # input state exposed for edge_counter: latest serdes sample # support for short pulses will need a more involved solution self.input_state = Signal() # # # # Output self.submodules += _SerdesDriver(serdes_o=serdes.o, stb=self.rtlink.o.stb & (self.rtlink.o.address == 0), data=self.rtlink.o.data[0], fine_ts=self.rtlink.o.fine_ts, override_en=override_en, override_o=override_o) oe_k = Signal() self.oe.attr.add("no_retiming") self.sync.rio_phy += [ If(self.rtlink.o.stb & (self.rtlink.o.address == 1), oe_k.eq(self.rtlink.o.data[0])), If(override_en, self.oe.eq(override_oe)).Else(self.oe.eq(oe_k)) ] self.comb += serdes.oe.eq(self.oe) # Input sensitivity = Signal(2) sample = Signal() self.sync.rio += [ sample.eq(0), If(self.rtlink.o.stb & self.rtlink.o.address[1], sensitivity.eq(self.rtlink.o.data), If(self.rtlink.o.address[0], sample.eq(1))) ] i = serdes.i[-1] self.comb += self.input_state.eq(i) i_d = Signal() self.sync.rio_phy += [ i_d.eq(i), self.rtlink.i.data.eq(i), ] pe = PriorityEncoder(serdes_width) self.submodules += pe self.comb += pe.i.eq((serdes.i ^ Cat(i_d, serdes.i)) & ( (serdes.i & Replicate(sensitivity[0], serdes_width)) | (~serdes.i & Replicate(sensitivity[1], serdes_width)))) self.sync.rio_phy += [ self.rtlink.i.fine_ts.eq(pe.o), self.rtlink.i.stb.eq(sample | ~pe.n), ]
def __init__(self, w, servo): m_coeff = servo.iir.m_coeff.get_port(write_capable=True, mode=READ_FIRST, we_granularity=w.coeff, clock_domain="rio") assert len(m_coeff.we) == 2 m_state = servo.iir.m_state.get_port( write_capable=True, # mode=READ_FIRST, clock_domain="rio") self.specials += m_state, m_coeff # just expose the w.coeff (18) MSBs of state assert w.state >= w.coeff # ensure that we can split the coefficient storage correctly assert len(m_coeff.dat_w) == 2 * w.coeff # ensure that the DDS word data fits into the coefficient mem assert w.coeff >= w.word # coeff, profile, channel, 2 mems, rw # this exceeds the 8-bit RTIO address, so we move the extra ("overflow") # address bits into data. internal_address_width = 3 + w.profile + w.channel + 1 + 1 rtlink_address_width = min(8, internal_address_width) overflow_address_width = internal_address_width - rtlink_address_width self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=overflow_address_width + w.coeff, address_width=rtlink_address_width, enable_replace=False), rtlink.IInterface(data_width=w.coeff, timestamped=False)) # # # config = Signal(w.coeff, reset=0) status = Signal(w.coeff) pad = Signal(6) self.comb += [ Cat(servo.start).eq(config), status.eq( Cat(servo.start, servo.done, pad, [_.clip for _ in servo.iir.ctrl])) ] assert len(self.rtlink.o.address) + len( self.rtlink.o.data) - w.coeff == ( 1 + # we 1 + # state_sel 1 + # high_coeff len(m_coeff.adr)) # ensure that we can fit config/status into the state address space assert len(self.rtlink.o.address) + len( self.rtlink.o.data) - w.coeff >= ( 1 + # we 1 + # state_sel 1 + # config_sel len(m_state.adr)) internal_address = Signal(internal_address_width) self.comb += internal_address.eq( Cat(self.rtlink.o.address, self.rtlink.o.data[w.coeff:])) coeff_data = Signal(w.coeff) self.comb += coeff_data.eq(self.rtlink.o.data[:w.coeff]) we = internal_address[-1] state_sel = internal_address[-2] config_sel = internal_address[-3] high_coeff = internal_address[0] self.comb += [ self.rtlink.o.busy.eq(0), m_coeff.adr.eq(internal_address[1:]), m_coeff.dat_w.eq(Cat(coeff_data, coeff_data)), m_coeff.we[0].eq(self.rtlink.o.stb & ~high_coeff & we & ~state_sel), m_coeff.we[1].eq(self.rtlink.o.stb & high_coeff & we & ~state_sel), m_state.adr.eq(internal_address), m_state.dat_w[w.state - w.coeff:].eq(self.rtlink.o.data), m_state.we.eq(self.rtlink.o.stb & we & state_sel & ~config_sel), ] read = Signal() read_state = Signal() read_high = Signal() read_config = Signal() self.sync.rio += [ If(read, read.eq(0)), If( self.rtlink.o.stb, read.eq(~we), read_state.eq(state_sel), read_high.eq(high_coeff), read_config.eq(config_sel), ) ] self.sync.rio_phy += [ If(self.rtlink.o.stb & we & state_sel & config_sel, config.eq(self.rtlink.o.data)), If(read & read_config & read_state, [_.clip.eq(0) for _ in servo.iir.ctrl]) ] self.comb += [ self.rtlink.i.stb.eq(read), self.rtlink.i.data.eq( Mux( read_state, Mux(read_config, status, m_state.dat_r[w.state - w.coeff:]), Mux(read_high, m_coeff.dat_r[w.coeff:], m_coeff.dat_r[:w.coeff]))) ]
def __init__(self, pins, pins_n): self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=8, address_width=8, enable_replace=False), rtlink.IInterface(data_width=10)) # share a CosSinGen LUT between the two channels self.submodules.ch0 = DDSChannel() self.submodules.ch1 = DDSChannel(share_lut=self.ch0.dds.cs.lut) n_channels = 2 n_samples = 8 n_bits = 14 body = Signal(n_samples * n_channels * 2 * n_bits, reset_less=True) self.sync.rio_phy += [ If( self.ch0.dds.valid, # & self.ch1.dds.valid, # recent:ch0:i as low order in body Cat(body).eq( Cat(self.ch0.dds.o.i[2:], self.ch0.dds.o.q[2:], self.ch1.dds.o.i[2:], self.ch1.dds.o.q[2:], body)), ), ] self.submodules.serializer = SerDes(n_data=8, t_clk=8, d_clk=0b00001111, n_frame=10, n_crc=6, poly=0x2f) 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]), ] header = Record([("we", 1), ("addr", 7), ("data", 8), ("type", 4)]) assert len(Cat(header.raw_bits(), body)) == \ len(self.serializer.payload) self.comb += self.serializer.payload.eq(Cat(header.raw_bits(), body)) re_dly = Signal(3) # stage, send, respond self.sync.rtio += [ header.type.eq(1), # body type is baseband data If( self.serializer.stb, self.ch0.dds.stb.eq(1), # synchronize self.ch1.dds.stb.eq(1), # synchronize header.we.eq(0), re_dly.eq(re_dly[1:]), ), If( self.rtlink.o.stb, re_dly[-1].eq(~self.rtlink.o.address[-1]), header.we.eq(self.rtlink.o.address[-1]), header.addr.eq(self.rtlink.o.address), header.data.eq(self.rtlink.o.data), ), self.rtlink.i.stb.eq(re_dly[0] & self.serializer.stb), self.rtlink.i.data.eq(self.serializer.readback), ]
def __init__(self, pads, pads_n=None): to_rio_phy = ClockDomainsRenamer("rio_phy") if pads_n is None: interface = SPIInterface(pads) else: interface = SPIInterfaceXC7Diff(pads, pads_n) interface = to_rio_phy(interface) spi = to_rio_phy(SPIMachine(data_width=32, div_width=8)) self.submodules += interface, spi self.rtlink = rtlink.Interface( rtlink.OInterface(len(spi.reg.pdo), address_width=1, enable_replace=False), rtlink.IInterface(len(spi.reg.pdi), timestamped=False)) ### config = Record([ ("offline", 1), ("end", 1), ("input", 1), ("cs_polarity", 1), ("clk_polarity", 1), ("clk_phase", 1), ("lsb_first", 1), ("half_duplex", 1), ("length", 5), ("padding", 3), ("div", 8), ("cs", 8), ]) assert len(config) == len(spi.reg.pdo) == len(spi.reg.pdi) == 32 config.offline.reset = 1 config.end.reset = 1 read = Signal() self.sync.rio += [ If(self.rtlink.i.stb, read.eq(0)), If( self.rtlink.o.stb & spi.writable, If(self.rtlink.o.address, config.raw_bits().eq(self.rtlink.o.data)).Else( read.eq(config.input))), ] self.comb += [ spi.length.eq(config.length), spi.end.eq(config.end), spi.cg.div.eq(config.div), spi.clk_phase.eq(config.clk_phase), spi.reg.lsb_first.eq(config.lsb_first), interface.half_duplex.eq(config.half_duplex), interface.cs.eq(config.cs), interface.cs_polarity.eq( Replicate(config.cs_polarity, len(interface.cs_polarity))), interface.clk_polarity.eq(config.clk_polarity), interface.offline.eq(config.offline), interface.cs_next.eq(spi.cs_next), interface.clk_next.eq(spi.clk_next), interface.ce.eq(spi.ce), interface.sample.eq(spi.reg.sample), spi.reg.sdi.eq(interface.sdi), interface.sdo.eq(spi.reg.sdo), spi.load.eq(self.rtlink.o.stb & spi.writable & ~self.rtlink.o.address), spi.reg.pdo.eq(self.rtlink.o.data), self.rtlink.o.busy.eq(~spi.writable), self.rtlink.i.stb.eq(spi.readable & read), self.rtlink.i.data.eq(spi.reg.pdi) ] self.probes = []
def __init__(self, core_link_pads, output_pads, passthrough_sigs, input_phys, simulate=False): """ core_link_pads: EEM pads for inter-Kasli link output_pads: pads for 4 output signals (422sigma, 1092, 422 ps trigger, aux) passthrough_sigs: signals from output phys, connected to output_pads when core not running input_phys: serdes phys for 5 inputs – APD0-3 and 422ps trigger in """ event_counter_width = 14 self.rtlink = rtlink.Interface( rtlink.OInterface( data_width=32, address_width=6, enable_replace=False), rtlink.IInterface( data_width=max(14, event_counter_width), timestamped=True) ) # # # self.submodules.core = ClockDomainsRenamer("rio")(EntanglerCore( core_link_pads, output_pads, passthrough_sigs, input_phys, event_counter_width, simulate=simulate)) read_en = self.rtlink.o.address[5] write_timings = Signal() write_patterns = Signal() self.comb += [ self.rtlink.o.busy.eq(0), write_timings.eq(self.rtlink.o.address[3:6] == 1), write_patterns.eq(self.rtlink.o.address[3:6] == 2), ] output_t_starts = [seq.m_start for seq in self.core.sequencers] output_t_ends = [seq.m_stop for seq in self.core.sequencers] output_t_starts += [gater.gate_start for gater in self.core.apd_gaters] output_t_ends += [gater.gate_stop for gater in self.core.apd_gaters] write_timing_cases = {} for i in range(len(output_t_starts)): write_timing_cases[i] = [output_t_starts[i].eq(self.rtlink.o.data[:16]), output_t_ends[i].eq(self.rtlink.o.data[16:])] # Write timeout counter and start core running self.comb += [ self.core.msm.time_remaining_buf.eq(self.rtlink.o.data), self.core.msm.run_stb.eq( (self.rtlink.o.address==1) & self.rtlink.o.stb ) ] self.sync.rio += [ If(write_timings & self.rtlink.o.stb, Case(self.rtlink.o.address[:3], write_timing_cases) ), If(write_patterns & self.rtlink.o.stb, Cat( *Array(p.patterns for p in self.core.pattern_counters)[ self.rtlink.o.address[:3]]).eq(self.rtlink.o.data) ), If((self.rtlink.o.address == 0) & self.rtlink.o.stb, # Write config self.core.enable.eq(self.rtlink.o.data[0]), self.core.msm.standalone.eq(self.rtlink.o.data[2]), ), If((self.rtlink.o.address == 2) & self.rtlink.o.stb, # Write cycle length self.core.msm.m_end.eq(self.rtlink.o.data[:10]) ), If((self.rtlink.o.address == 3) & self.rtlink.o.stb, # Write herald patterns and enables *[ self.core.heralder.patterns[i].eq( self.rtlink.o.data[4 * i:4 * (i + 1)]) for i in range(4) ], self.core.heralder.pattern_ens.eq(self.rtlink.o.data[16:20]) ), ] # Write is_master bit in rio_phy reset domain to not break 422ps trigger # forwarding on core.reset(). self.sync.rio_phy += If((self.rtlink.o.address == 0) & self.rtlink.o.stb, self.core.msm.is_master.eq(self.rtlink.o.data[1]) ) read = Signal() read_counters = Signal() read_timestamps = Signal() read_addr = Signal(3) # Input timestamps are [apd0, apd1, apd2, apd3, ref] input_timestamps = [gater.sig_ts for gater in self.core.apd_gaters] input_timestamps.append(self.core.apd_gaters[0].ref_ts) cases = {} timing_data = Signal(14) for i, ts in enumerate(input_timestamps): cases[i] = [timing_data.eq(ts)] self.comb += Case(read_addr, cases) self.sync.rio += [ If(read, read.eq(0) ), If(self.rtlink.o.stb, read.eq(read_en), read_counters.eq(self.rtlink.o.address[3:5] == 0b10), read_timestamps.eq(self.rtlink.o.address[3:5] == 0b01), read_addr.eq(self.rtlink.o.address[:3]), ) ] status = Signal(3) self.comb += status.eq(Cat(self.core.msm.ready, self.core.msm.success, self.core.msm.timeout)) reg_data = Signal(event_counter_width) cases = {} cases[0] = [reg_data.eq(status)] cases[1] = [reg_data.eq(self.core.msm.cycles_completed)] cases[2] = [reg_data.eq(self.core.msm.time_remaining)] cases[3] = [reg_data.eq(self.core.triggers_received)] self.comb += Case(read_addr, cases) counter_data = Signal(event_counter_width) self.comb += Case(read_addr, {i: [counter_data.eq(c.counter)] for i, c in enumerate(self.core.counters)}) # Generate an input event if we have a read request RTIO Output event, or if the # core has finished. If the core is finished output the herald match, or 0x3fff # on timeout. # # Simultaneous read requests and core-done events are not currently handled, but # are easy to avoid in the client code. self.comb += [ self.rtlink.i.stb.eq(read | self.core.enable & self.core.msm.done_stb), self.rtlink.i.data.eq( Mux(self.core.enable & self.core.msm.done_stb, Mux(self.core.msm.success, self.core.heralder.matches, 0x3fff), Mux(read_counters, counter_data, Mux(read_timestamps, timing_data, reg_data) ) ) ) ]