class ClockDebug(Elaboratable): def __init__(self, domain_name, reset_less=False): self.domain_name = domain_name self.reset_less = reset_less self.counter = StatusSignal(32) if not self.reset_less: self.is_reset = StatusSignal() def elaborate(self, platform): m = Module() m.d[self.domain_name] += self.counter.eq(self.counter + 1) if not self.reset_less: m.d.comb += self.is_reset.eq(ResetSignal(self.domain_name)) return m @driver_property def mhz(self): from time import sleep, time initial_counter = self.counter start = time() sleep(0.1) counter_difference = (self.counter - initial_counter) return counter_difference * (1 / (time() - start)) / 1e6
class StreamFIFO(Elaboratable): def __init__(self, input: Stream, fifo_type, output_stream_name="fifo_out", **fifo_args): self.input = input self.output = input.clone(name=output_stream_name) if "r_domain" in fifo_args: self.output_domain = fifo_args["r_domain"] self.fifo = fifo_type(width=len(Cat(self.input.payload_signals.values())), **fifo_args) self.depth = fifo_args['depth'] self.r_level = StatusSignal(range(self.fifo.depth + 1)) self.w_level = StatusSignal(range(self.fifo.depth + 1)) def elaborate(self, platform): m = Module() fifo = m.submodules.fifo = self.fifo if self.depth == 0: m.d.comb += self.output.connect_upstream(self.input) else: m.d.comb += self.r_level.eq(fifo.r_level) m.d.comb += self.w_level.eq(fifo.w_level) m.d.comb += self.input.ready.eq(fifo.w_rdy) m.d.comb += fifo.w_data.eq(Cat(self.input.payload_signals.values())) m.d.comb += fifo.w_en.eq(self.input.valid) m.d.comb += Cat(self.output.payload_signals.values()).eq(fifo.r_data) m.d.comb += self.output.valid.eq(fifo.r_rdy) m.d.comb += fifo.r_en.eq(self.output.ready) return m
def elaborate(self, platform): m = Module() with m.If(self.stream.valid & ~self.stream.ready): m.d.sync += self.valid_not_ready.eq(self.valid_not_ready + 1) with m.If(self.stream.ready & ~self.stream.valid): m.d.sync += self.ready_not_valid.eq(self.ready_not_valid + 1) m.d.sync += self.reference_counter.eq(self.reference_counter + 1) transaction = Signal() m.d.comb += transaction.eq(self.stream.valid & self.stream.ready) with m.If(transaction): m.d.sync += self.successful_transactions_counter.eq( self.successful_transactions_counter + 1) m.d.comb += self.current_ready.eq(self.stream.ready) m.d.comb += self.current_valid.eq(self.stream.valid) for name, signal in self.stream.payload_signals.items(): if len(signal) == 1: m.submodules[name] = MetadataSignalDebug(signal, transaction) else: current_state = StatusSignal(signal.shape()) m.d.comb += current_state.eq(signal) setattr(self, "current_{}".format(name), current_state) last_transaction_state = StatusSignal(signal.shape()) with m.If(transaction): m.d.sync += last_transaction_state.eq(signal) setattr(self, "last_transaction_{}".format(name), last_transaction_state) return m
class Tracer(Elaboratable): def __init__(self, fsm: FSM, trace_length=128): self.fsm = fsm self.trace_length = trace_length self.write_ptr = StatusSignal(range(trace_length)) self.trace_decoder = {} def elaborate(self, platform): m = Module() mem = m.submodules.mem = SocMemory(width=len(self.fsm.state), depth=self.trace_length, soc_write=False) write_port = m.submodules.write_port = mem.write_port(domain="sync") with m.If(Changed(m, self.fsm.state)): m.d.comb += write_port.en.eq(1) m.d.comb += write_port.data.eq(self.fsm.state) m.d.comb += write_port.addr.eq(self.write_ptr) with m.If(self.write_ptr < self.trace_length): m.d.sync += self.write_ptr.eq(self.write_ptr + 1) with m.Else(): m.d.sync += self.write_ptr.eq(0) self.trace_decoder.update(self.fsm.decoding) return m @driver_method def print_trace(self): r = list(range(self.trace_length)) for i in r[self.write_ptr:] + r[:self.write_ptr]: print(self.trace_decoder[self.mem[i]])
def elaborate(self, platform): m = Module() m.submodules.core = self.core last_fifo_input = BasicStream(self.last_rle_bits) last_fifo = m.submodules.last_fifo = BufferedSyncStreamFIFO( last_fifo_input, self.last_fifo_depth) overflow_word = (2**self.last_rle_bits - 1) rle_input_counter = StatusSignal(self.last_rle_bits) with m.If(self.input.valid & last_fifo_input.ready): m.d.comb += self.core_input.valid.eq(1) m.d.comb += self.core_input.payload.eq(self.input.payload) with m.If(self.core_input.ready): m.d.comb += self.input.ready.eq(1) with m.If(self.input.last | (rle_input_counter == overflow_word - 1)): with m.If(~self.input.last & (rle_input_counter == overflow_word - 1)): m.d.comb += last_fifo_input.payload.eq(overflow_word) with m.Else(): m.d.comb += last_fifo_input.payload.eq( rle_input_counter) m.d.comb += last_fifo_input.valid.eq(1) m.d.sync += rle_input_counter.eq(0) with m.Else(): m.d.sync += rle_input_counter.eq(rle_input_counter + 1) rle_output_counter = StatusSignal(self.last_rle_bits) with m.If(self.core_output.valid): m.d.comb += self.output.valid.eq(1) m.d.comb += self.output.payload.eq(self.core_output.payload) overflow = (last_fifo.output.payload == overflow_word) & ( rle_output_counter == (overflow_word - 1)) with m.If(( (rle_output_counter == last_fifo.output.payload) | overflow) & last_fifo.output.valid): with m.If(~overflow): m.d.comb += self.output.last.eq(1) with m.If(self.output.ready): m.d.sync += rle_output_counter.eq(0) m.d.comb += last_fifo.output.ready.eq(1) m.d.comb += self.core_output.ready.eq(1) with m.Elif((rle_output_counter > last_fifo.output.payload) & last_fifo.output.valid): with m.If(self.output.ready): m.d.comb += self.core_output.ready.eq(1) m.d.sync += self.error.eq(self.error + 1) with m.Else(): with m.If(self.output.ready): m.d.comb += self.core_output.ready.eq(1) m.d.sync += rle_output_counter.eq(rle_output_counter + 1) return m
class Ft60xLegalizer(Elaboratable): def __init__(self, input: PacketizedStream, packet_len): self.input = input self.output = BasicStream(input.payload.shape()) # we calculate everything in bytes to make it easier to reason about buffer_size = 2048 * 4 blanking = buffer_size aligned_len = ceil((packet_len + blanking) / buffer_size) * buffer_size print("ft60x paddnig:", (aligned_len - packet_len), (aligned_len - packet_len) // 4) self.padding = ControlSignal(16, reset=(aligned_len - packet_len) // 4) self.frame_len = StatusSignal(32) self.frame_len_changed = StatusSignal(32) def elaborate(self, platform): m = Module() padding_ctr = Signal.like(self.padding) frame_len_ctr = Signal.like(self.frame_len) input_transaction = self.input.ready & self.input.valid with m.FSM(): with m.State("ACTIVE"): m.d.comb += self.input.ready.eq(self.output.ready) m.d.comb += self.output.valid.eq(self.input.valid) with m.If( self.input.payload == 0 ): # we disallow the transfer of 0 to ease the alignment detection TODO: this only really works for 8 bit this way m.d.comb += self.output.payload.eq(1) with m.Else(): m.d.comb += self.output.payload.eq(self.input.payload) with m.If(input_transaction): m.d.sync += frame_len_ctr.eq(frame_len_ctr + 1) with m.If(self.input.last & input_transaction): m.next = "PADDING" m.d.sync += padding_ctr.eq(0) with m.State("PADDING"): m.d.comb += self.output.valid.eq(1) m.d.comb += self.input.ready.eq(0) m.d.comb += self.output.payload.eq(0) with m.If(self.output.ready): with m.If(padding_ctr < self.padding - 1): m.d.sync += padding_ctr.eq(padding_ctr + 1) m.d.sync += frame_len_ctr.eq(frame_len_ctr + 1) with m.Else(): m.next = "ACTIVE" m.d.sync += self.frame_len.eq(frame_len_ctr + 1) with m.If(self.frame_len != frame_len_ctr + 1): m.d.sync += self.frame_len_changed.eq( self.frame_len_changed + 1) m.d.sync += frame_len_ctr.eq(0) return m
class ConsolePacketSource(Elaboratable): def __init__(self, data_width=8, max_packet_size=1024): self.max_packet_size = max_packet_size self.reset = ControlSignal() self.packet_length = ControlSignal(range(max_packet_size)) self.read_ptr = StatusSignal(range(max_packet_size)) self.done = StatusSignal(reset=1) self.memory = SocMemory( width=data_width, depth=self.max_packet_size, soc_read=False, attrs=dict(syn_ramstyle="block_ram") ) self.output = PacketizedStream(data_width) def elaborate(self, platform): m = Module() memory = m.submodules.memory = self.memory address_stream = PacketizedStream(bits_for(self.max_packet_size)) with m.If(~self.done): m.d.comb += address_stream.valid.eq(1) m.d.comb += address_stream.last.eq(self.read_ptr == self.packet_length) m.d.comb += address_stream.payload.eq(self.read_ptr) with m.If(address_stream.ready): m.d.sync += self.read_ptr.eq(self.read_ptr + 1) m.d.sync += self.done.eq(self.read_ptr == self.packet_length) reset = Signal() m.submodules += FFSynchronizer(self.reset, reset) with m.If(Changed(m, reset)): m.d.sync += self.read_ptr.eq(0) m.d.sync += self.done.eq(0) reader = m.submodules.reader = StreamMemoryReader(address_stream, memory) buffer = m.submodules.buffer = StreamBuffer(reader.output) m.d.comb += self.output.connect_upstream(buffer.output) return m @driver_method def write_packet(self, packet, timeout=0): from time import sleep for i in range(int(timeout * 10)): if self.done: break sleep(0.1) assert self.done for i, word in enumerate(packet): self.memory[i] = word self.packet_length = len(packet) - 1 self.reset = not self.reset
class AxiWriter(Elaboratable): def __init__(self, address_source: BasicStream, data_source: BasicStream, axi=None): self.address_source = address_source self.data_source = data_source self.axi = axi self.axi_address_ready = StatusSignal() self.axi_data_ready = StatusSignal() self.write_response_ok = StatusSignal(32) self.write_response_err = StatusSignal(32) def elaborate(self, platform): m = Module() burster = m.submodules.burster = AxiWriterBurster( self.address_source, self.data_source) axi = if_none_get_zynq_hp_port(self.axi, m, platform) for fifo_signal_name in [ "write_address_fifo_level", "write_data_fifo_level" ]: if hasattr(axi, fifo_signal_name): axi_fifo_signal = axi[fifo_signal_name] fifo_signal = StatusSignal(axi_fifo_signal.shape(), name=f"axi_{fifo_signal_name}") m.d.comb += fifo_signal.eq(axi_fifo_signal) setattr(self, f"axi_{fifo_signal_name}", fifo_signal) m.d.comb += axi.write_data.connect_upstream(burster.data_output) m.d.comb += axi.write_address.connect_upstream(burster.address_output) # we do not currently care about the write responses m.d.comb += axi.write_response.ready.eq(1) with m.If(axi.write_response.valid): with m.If(axi.write_response.resp == AxiResponse.OKAY): m.d.sync += self.write_response_ok.eq(self.write_response_ok + 1) with m.Else(): m.d.sync += self.write_response_err.eq( self.write_response_err + 1) m.d.comb += self.axi_data_ready.eq(axi.write_data.ready) m.d.comb += self.axi_address_ready.eq(axi.write_address.ready) info_axi_address = m.submodules.info_axi_address = StreamInfo( axi.write_address) info_axi_data = m.submodules.info_axi_data = StreamInfo(axi.write_data) return m
class ConsolePacketSink(Elaboratable): def __init__(self, input: PacketizedStream, max_packet_size=1024): self.max_packet_size = max_packet_size self.reset = ControlSignal() self.write_pointer = StatusSignal(range(self.max_packet_size)) self.packet_done = StatusSignal() self.memory = SocMemory( width=len(input.payload), depth=self.max_packet_size, soc_write=False, attrs=dict(syn_ramstyle="block_ram") ) self.input = input def elaborate(self, platform): m = Module() memory = m.submodules.memory = self.memory write_port = m.submodules.write_port = memory.write_port(domain="sync") with m.If(~self.packet_done & (self.write_pointer < self.max_packet_size)): m.d.comb += self.input.ready.eq(1) with m.If(self.input.valid): m.d.comb += write_port.en.eq(1) m.d.comb += write_port.addr.eq(self.write_pointer) m.d.comb += write_port.data.eq(self.input.payload) m.d.sync += self.write_pointer.eq(self.write_pointer + 1) with m.If(self.input.last): m.d.sync += self.packet_done.eq(1) reset = Signal() m.submodules += FFSynchronizer(self.reset, reset) with m.If(Changed(m, reset)): m.d.sync += self.write_pointer.eq(0) m.d.sync += self.packet_done.eq(0) return m @driver_method def read_packet(self, timeout=0): from time import sleep for i in range(int(timeout * 10)): if self.packet_done: break sleep(0.1) if not self.packet_done: return None to_return = [self.memory[i] for i in range(self.write_pointer)] self.reset = not self.reset return to_return
class Top(Elaboratable): def __init__(self): self.up_counter = StatusSignal(16) self.down_counter = StatusSignal(16, reset=1000) def elaborate(self, platform): m = Module() m.d.sync += self.up_counter.eq(self.up_counter + 1) m.d.sync += self.down_counter.eq(self.down_counter - 1) add_ila(platform, trace_length=100) probe(m, self.up_counter) probe(m, self.down_counter) trigger(m, self.up_counter > 200) return m
class DPhyClockLane(Elaboratable): def __init__(self, lp_pins: TristateIo, hs_pins: TristateDdrIo, ck_domain): self.lp_pins = lp_pins self.hs_pins = hs_pins self.ck_domain = ck_domain self.request_hs = Signal() self.is_hs = StatusSignal() def elaborate(self, platform): m = Module() m.d.comb += self.lp_pins.oe.eq(1) m.d.comb += self.hs_pins.oe.eq(self.is_hs) m.d.comb += self.hs_pins.o_clk.eq(ClockSignal(self.ck_domain)) lp = self.lp_pins.o[::-1] with m.FSM(): with m.State("LP"): m.d.comb += lp.eq(STOP) with m.If(self.request_hs): m.next = "HS_REQUEST" with Process(m, "HS_REQUEST", to="HS") as p: m.d.comb += lp.eq(STOP) p += process_delay( m, 6) # we need to stay in lp state for some minimum time m.d.comb += lp.eq(HS_REQUEST) p += process_delay(m, 6) m.d.comb += lp.eq(BRIDGE) p += process_delay(m, 5) with m.State("HS"): m.d.comb += lp.eq(0) m.d.comb += self.is_hs.eq(1) m.d.comb += self.hs_pins.o0.eq(fake_differential(1)) m.d.comb += self.hs_pins.o1.eq(fake_differential(0)) with m.If(~self.request_hs): m.next = "HS_END" with Process(m, name="HS_END", to="LP") as p: m.d.comb += self.is_hs.eq(1) m.d.comb += lp.eq(0) m.d.comb += self.hs_pins.o0.eq(fake_differential(0)) m.d.comb += self.hs_pins.o1.eq(fake_differential(0)) p += process_delay(m, 2) # delay minimum 60ns return m
class InflexibleSinkDebug(Elaboratable): def __init__(self, stream): self.stream = stream self.invalid = StatusSignal(32) def elaborate(self, platform): m = Module() with m.If(self.stream.ready & ~self.stream.valid): m.d.sync += self.invalid.eq(self.invalid + 1) return m
def fsm_status_reg(platform, m, fsm: FSM): if isinstance(platform, SocPlatform): fsm_state = StatusSignal( name=f"{fsm.state.name}_reg" ) # TODO: use meaningful shape value here (needs deferring) def signal_fixup_hook(platform, top_fragment: Fragment, sames): fsm_state.width = fsm.state.width fsm_state.decoder = fsm.state.decoder platform.prepare_hooks.insert(0, signal_fixup_hook) m.d.comb += fsm_state.eq(fsm.state)
class ImageSplitter2(Elaboratable): """Splits an Image into n chunks horizontally""" def __init__(self, input: ImageStream, chunk_width, n_chunks, height): self.chunk_width = chunk_width self.height = height self.n_chunks = n_chunks self.input = input self.x_ctr = StatusSignal(16) self.y_ctr = StatusSignal(16) self.outputs = [ self.input.clone(f'splitter_output_{i}') for i in range(n_chunks) ] def elaborate(self, platform): m = Module() with m.If(self.input.ready & self.input.valid): m.d.sync += self.x_ctr.eq(self.x_ctr + 1) with m.If(self.input.line_last): m.d.sync += self.x_ctr.eq(0) m.d.sync += self.y_ctr.eq(self.y_ctr + 1) with m.If(self.input.frame_last): m.d.sync += self.y_ctr.eq(0) for i, output in enumerate(self.outputs): start, end = i * self.chunk_width, (i + 1) * self.chunk_width m.d.comb += self.input.ready.eq( 1) # if noone is responsible, we dont hang everything with m.If((self.x_ctr >= start) & (self.x_ctr < end)): m.d.comb += output.connect_upstream( self.input, exclude=['frame_last', 'line_last']) m.d.comb += output.line_last.eq(self.x_ctr == end - 1) m.d.comb += output.frame_last.eq((self.x_ctr == end - 1) & (self.y_ctr == self.height - 1)) return m
class IDelayCtrl(Elaboratable): def __init__(self, refclk_domain): self.refclk_domain = refclk_domain self.ready = StatusSignal() def elaborate(self, platform): m = Module() idelay_ctl = m.submodules.idelay_ctl = _IDelayCtrl() m.d.comb += self.ready.eq(idelay_ctl.rdy) m.d.comb += idelay_ctl.refclk.eq(ClockSignal(self.refclk_domain)) m.d.comb += idelay_ctl.rst.eq(ResetSignal(self.refclk_domain)) return m
class HdmiTimingGenerator(Elaboratable): def __init__(self, video_timing, vertical_signals_shape=range(8000), horizontal_signals_shape=range(4000)): self.hscan = ControlSignal(horizontal_signals_shape, reset=video_timing.hscan) self.vscan = ControlSignal(vertical_signals_shape, reset=video_timing.vscan) self.width = ControlSignal(horizontal_signals_shape, reset=video_timing.hres) self.height = ControlSignal(vertical_signals_shape, reset=video_timing.vres) self.hsync_start = ControlSignal(horizontal_signals_shape, reset=video_timing.hsync_start) self.hsync_end = ControlSignal(horizontal_signals_shape, reset=video_timing.hsync_end) self.vsync_start = ControlSignal(vertical_signals_shape, reset=video_timing.vsync_start) self.vsync_end = ControlSignal(vertical_signals_shape, reset=video_timing.vsync_end) self.x = StatusSignal(horizontal_signals_shape,) self.y = StatusSignal(vertical_signals_shape) self.active = StatusSignal() self.is_blanking_x = StatusSignal() self.is_blanking_y = StatusSignal() self.hsync = StatusSignal() self.vsync = StatusSignal() def elaborate(self, plat): m = Module() # set the xy coordinates with m.If(self.x < self.hscan - 1): m.d.sync += self.x.eq(self.x + 1) with m.Else(): m.d.sync += self.x.eq(0) with m.If(self.y < self.vscan - 1): m.d.sync += self.y.eq(self.y + 1) with m.Else(): m.d.sync += self.y.eq(0) m.d.comb += [ self.is_blanking_x.eq(self.x >= self.width), self.is_blanking_y.eq(self.y >= self.height), self.active.eq((self.x < self.width) & (self.y < self.height)), self.hsync.eq((self.x >= self.hsync_start) & (self.x < self.hsync_end)), self.vsync.eq((self.y >= self.vsync_start) & (self.y < self.vsync_end)) ] return m
class MetadataSignalDebug(Elaboratable): def __init__(self, signal, transaction): assert len(signal) == 1 self.signal = signal self.transaction = transaction self.cycle_1_length = StatusSignal(32) self.cycle_1_length_changed = StatusSignal(32) self.cycle_0_length = StatusSignal(32) self.cycle_0_length_changed = StatusSignal(32) self.cycles = StatusSignal(32) self.current = StatusSignal() def elaborate(self, platform): m = Module() m.d.comb += self.current.eq(self.signal) cycle_0_counter = Signal(32) cycle_1_counter = Signal(32) with m.If(self.transaction): with m.If(self.signal): m.d.sync += cycle_0_counter.eq(0) with m.If(cycle_0_counter != 0): m.d.sync += self.cycle_0_length.eq(cycle_0_counter) with m.If(self.cycle_0_length != cycle_0_counter): m.d.sync += self.cycle_0_length_changed.eq( self.cycle_0_length_changed + 1) m.d.sync += cycle_1_counter.eq(cycle_1_counter + 1) with m.Else(): m.d.sync += cycle_1_counter.eq(0) with m.If(cycle_1_counter != 0): m.d.sync += self.cycle_1_length.eq(cycle_1_counter) with m.If(self.cycle_1_length != cycle_1_counter): m.d.sync += self.cycle_1_length_changed.eq( self.cycle_1_length_changed + 1) m.d.sync += cycle_0_counter.eq(cycle_0_counter + 1) signal_last = Signal() m.d.sync += signal_last.eq(self.signal) with m.If(self.signal & ~signal_last): m.d.sync += self.cycles.eq(self.cycles + 1) return m @driver_property def cycle_length(self): return self.cycle_0_length + self.cycle_1_length
def elaborate(self, platform): m = Module() axi = if_none_get_zynq_hp_port(self.axi, m, platform) assert len(self.output.payload) == axi.data_bits assert len(self.address_source.payload) == axi.addr_bits for fifo_signal_name in [ "read_address_fifo_level", "read_data_fifo_level" ]: if hasattr(axi, fifo_signal_name): axi_fifo_signal = axi[fifo_signal_name] fifo_signal = StatusSignal(axi_fifo_signal.shape(), name=f"axi_{fifo_signal_name}") m.d.comb += fifo_signal.eq(axi_fifo_signal) setattr(self, f"axi_{fifo_signal_name}", fifo_signal) burster = m.submodules.burster = AxiReaderBurster( self.address_source, data_bytes=axi.data_bytes) m.d.comb += axi.read_address.connect_upstream(burster.output) m.d.comb += self.output.connect_upstream(axi.read_data, allow_partial=True) return m
class PacketizedStream2ImageStream(Elaboratable): """Convert a Packetized stream to an Image stream by creating lines with `width`""" def __init__(self, input: PacketizedStream, width): self.input = input self.width = width self.not_exact_number_of_lines_error = StatusSignal(32) self.output = ImageStream(input.payload.shape(), name="adapted_image_stream") def elaborate(self, platform): m = Module() line_ctr = Signal(16) m.d.comb += self.output.connect_upstream( self.input, only=["ready", "valid", "payload"]) with m.If(self.input.ready & self.input.valid): with m.If(self.input.last): m.d.sync += line_ctr.eq(0) with m.If(line_ctr != self.width - 1): m.d.sync += self.not_exact_number_of_lines_error.eq( self.not_exact_number_of_lines_error + 1) with m.Else(): with m.If(line_ctr >= (self.width - 1)): m.d.sync += line_ctr.eq(0) with m.Else(): m.d.sync += line_ctr.eq(line_ctr + 1) m.d.comb += self.output.payload.eq(self.input.payload) with m.If(self.input.last): m.d.comb += self.output.frame_last.eq(1) m.d.comb += self.output.line_last.eq(1) with m.Else(): with m.If(line_ctr >= (self.width - 1)): m.d.comb += self.output.line_last.eq(1) return m
class HdmiStreamSource(Elaboratable): def __init__(self, resource): self.resource = resource self.blanking_threshold = ControlSignal(16, reset=(480 * 16)) self.measured_width = StatusSignal(16) self.measured_height = StatusSignal(16) self.width = ControlSignal(16, reset=1440) self.height = ControlSignal(16, reset=480) self.blank_r = ControlSignal() self.blank_g = ControlSignal() self.blank_b = ControlSignal() self.stable_lines_needed = 2000 self.lines_stable = StatusSignal(range(self.stable_lines_needed + 1)) self.frames_stable = StatusSignal(32) self.stable = StatusSignal() self.always_valid = ControlSignal() self.output_not_ready = StatusSignal(32) self.output = ImageStream(24) def elaborate(self, platform): m = Module() resource = self.resource self.lane_b = m.submodules.lane_b = HdmiRxLane(resource.b, "hdmi_eclk", "hdmi_qdr") self.lane_g = m.submodules.lane_g = HdmiRxLane(resource.g, "hdmi_eclk", "hdmi_qdr") self.lane_r = m.submodules.lane_r = HdmiRxLane(resource.r, "hdmi_eclk", "hdmi_qdr") m.d.comb += self.stable.eq( self.lines_stable > self.stable_lines_needed - 1) de = Signal() m.d.comb += de.eq((self.lane_b.data_enable + self.lane_r.data_enable + self.lane_g.data_enable) > 1) ce = Signal() m.d.comb += ce.eq( (~self.lane_b.data_enable + ~self.lane_r.data_enable + ~self.lane_g.data_enable) > 1) x_ctr = Signal(16) y_ctr = Signal(16) long_blanking = Signal() blanking_ctr = Signal.like(self.blanking_threshold) with m.If(ce): with m.If(blanking_ctr < self.blanking_threshold): m.d.sync += blanking_ctr.eq(blanking_ctr + 1) with m.If(blanking_ctr == (self.blanking_threshold - 1)): m.d.comb += long_blanking.eq(1) with m.Else(): m.d.sync += blanking_ctr.eq(0) probe(m, self.lane_b.data_enable, "de_b") probe(m, self.lane_g.data_enable, "de_g") probe(m, self.lane_r.data_enable, "de_r") probe(m, long_blanking) trigger(m, long_blanking) output = self.output.clone() line_started = Signal() with m.If(de | (x_ctr > 0)): m.d.sync += x_ctr.eq(x_ctr + 1) with m.If(x_ctr < self.width): with m.If(~output.ready): self.output_not_ready.eq(self.output_not_ready + 1) m.d.comb += output.valid.eq(self.stable | self.always_valid) m.d.comb += output.payload.eq( Cat( self.lane_r.data & Repl(~self.blank_r, 8), self.lane_g.data & Repl(~self.blank_g, 8), self.lane_b.data & Repl(~self.blank_b, 8), )) m.d.comb += output.line_last.eq(x_ctr == self.width - 1) m.d.comb += output.frame_last.eq((x_ctr == self.width - 1) & (y_ctr == self.height - 1)) with m.If(ce & ((x_ctr >= self.width) | (x_ctr == 0))): m.d.sync += x_ctr.eq(0) with m.If(x_ctr > 128): m.d.sync += y_ctr.eq(y_ctr + 1) m.d.sync += self.measured_width.eq(x_ctr) with m.If(x_ctr == self.measured_width): with m.If(self.lines_stable < self.stable_lines_needed): m.d.sync += self.lines_stable.eq(self.lines_stable + 1) with m.Else(): m.d.sync += self.frames_stable.eq(0) m.d.sync += self.lines_stable.eq(0) with m.If(long_blanking): m.d.sync += y_ctr.eq(0) with m.If(y_ctr > 128): m.d.sync += self.measured_height.eq(y_ctr) with m.If(y_ctr == self.height): m.d.sync += self.frames_stable.eq(self.frames_stable + 1) with m.Else(): m.d.sync += self.frames_stable.eq(0) buffer = m.submodules.buffer = StreamBuffer(output) m.d.comb += self.output.connect_upstream(buffer.output) return m @driver_method def train(self): print("training hdmi") print("tranining lane b...") _, delay, alignment = self.lane_b.train() self.set_delay(delay) self.lane_g.select.offset = alignment self.lane_r.select.offset = alignment @driver_method def set_delay(self, delay): self.lane_b.delayf.set_delay(delay) self.lane_g.delayf.set_delay(delay) self.lane_r.delayf.set_delay(delay)
class StreamInfo(Elaboratable): def __init__(self, stream: Stream): self.stream = stream self.reference_counter = StatusSignal(32) self.successful_transactions_counter = StatusSignal(32) self.ready_not_valid = StatusSignal(32) self.valid_not_ready = StatusSignal(32) self.current_ready = StatusSignal() self.current_valid = StatusSignal() def elaborate(self, platform): m = Module() with m.If(self.stream.valid & ~self.stream.ready): m.d.sync += self.valid_not_ready.eq(self.valid_not_ready + 1) with m.If(self.stream.ready & ~self.stream.valid): m.d.sync += self.ready_not_valid.eq(self.ready_not_valid + 1) m.d.sync += self.reference_counter.eq(self.reference_counter + 1) transaction = Signal() m.d.comb += transaction.eq(self.stream.valid & self.stream.ready) with m.If(transaction): m.d.sync += self.successful_transactions_counter.eq( self.successful_transactions_counter + 1) m.d.comb += self.current_ready.eq(self.stream.ready) m.d.comb += self.current_valid.eq(self.stream.valid) for name, signal in self.stream.payload_signals.items(): if len(signal) == 1: m.submodules[name] = MetadataSignalDebug(signal, transaction) else: current_state = StatusSignal(signal.shape()) m.d.comb += current_state.eq(signal) setattr(self, "current_{}".format(name), current_state) last_transaction_state = StatusSignal(signal.shape()) with m.If(transaction): m.d.sync += last_transaction_state.eq(signal) setattr(self, "last_transaction_{}".format(name), last_transaction_state) return m @driver_property def efficiency_percent(self): return self.successful_transactions_counter / self.reference_counter * 100 @driver_property def stall_source_percent(self): return self.ready_not_valid / self.reference_counter * 100 @driver_property def stall_sink_percent(self): return self.valid_not_ready / self.reference_counter * 100 @driver_property def stall_both_percent(self): return (100 - self.efficiency_percent ) - self.stall_source_percent - self.stall_sink_percent
class Pll(Elaboratable): vco_multipliers = list(range(2, 64)) vco_dividers = list(range(1, 56)) output_dividers = list(range(1, 128)) @staticmethod def is_valid_vco_conf(input_freq, mul, div, exception=False): if not mul in Pll.vco_multipliers: if exception: raise ValueError(f'mul {mul} is not an allowed multiplier') return False if not div in Pll.vco_dividers: if exception: raise ValueError(f'div {div} is not an allowed divider') return False vco_freq = input_freq * mul / div if 800e6 > vco_freq: if exception: raise ValueError( f'{vco_freq} is a to low vco freq. minimum is 800Mhz') return False if 1600e6 < vco_freq: if exception: raise ValueError( f'{vco_freq} is a to high vco freq. maximum is 1600Mhz') return False return True def __init__(self, input_freq, vco_mul, vco_div, input_domain="sync"): Pll.is_valid_vco_conf(input_freq, vco_mul, vco_div, exception=True) self._pll = _Pll( clkin1_period=1 / input_freq * 1e9, clkfbout_mult=vco_mul, divclk_divide=vco_div, ) m = self.m = Module() m.d.comb += self._pll.clk.fbin.eq(self._pll.clk.fbout) m.d.comb += self._pll.clk.in_[1].eq(ClockSignal(input_domain)) m.d.comb += self._pll.clk.insel.eq(1) # HIGH for clkin1 self.locked = StatusSignal() self._input_domain = input_domain self._input_freq = input_freq self._vco = Clock(input_freq * vco_mul / vco_div) self._clock_constraints = {} def output_domain(self, domain_name, divisor, number=None, bufg=True): if number is None: number = next(x for x in range(6) if x not in self._clock_constraints.keys()) assert number not in self._clock_constraints.keys( ), "port {} is already taken".format(number) assert divisor in Mmcm.output_dividers self._pll.parameters["CLKOUT{}_DIVIDE".format(number)] = divisor self._pll.parameters["CLKOUT{}_PHASE".format(number)] = 0.0 m = self.m clock_signal = Signal(name="pll_out_{}".format(number), attrs={"KEEP": "TRUE"}) m.d.comb += clock_signal.eq(self._pll.clk.out[number]) if bufg: # TODO: seems to not change anything bufg = m.submodules["bufg_{}".format(number)] = BufG(clock_signal) output = bufg.o else: output = clock_signal m.domains += ClockDomain(domain_name) m.d.comb += ClockSignal(domain_name).eq(output) m.d.comb += ResetSignal(domain_name).eq(~self.locked) frequency = self._vco.frequency / divisor self._clock_constraints[number] = (clock_signal, frequency) print("PLL: creating domain '{}' with frequency {}Mhz".format( domain_name, frequency / 1e6)) return Clock(frequency) def elaborate(self, platform): m = Module() m.submodules.pll_block = self._pll m.submodules.connections = self.m for i, (clock_signal, frequency) in self._clock_constraints.items(): platform.add_clock_constraint(clock_signal, frequency) m.d.comb += self.locked.eq(self._pll.locked) m.d.comb += self._pll.rst.eq(ResetSignal(self._input_domain)) if isinstance(platform, SocPlatform): m.submodules.drp_bridge = DrpBridge( DrpInterface(self._pll.dwe, self._pll.den, self._pll.daddr, self._pll.di, self._pll.do, self._pll.drdy, self._pll.dclk)) return m
class VideoResizer(Elaboratable): """Resize an ImageStream by cropping it / extending it with blackness to the desired resolution""" def __init__(self, input: ImageStream, desired_width, desired_height): self.input = input self.output = input.clone(name="resized") self.output_width = ControlSignal(16, reset=desired_width) self.output_height = ControlSignal(16, reset=desired_height) self.shift_x = ControlSignal(signed(16)) self.shift_y = ControlSignal(signed(16)) self.input_width = StatusSignal(16) self.input_height = StatusSignal(16) def elaborate(self, platform): m = Module() input_x = Signal(16) input_y = Signal(16) input_read = (self.input.ready & self.input.valid) with m.If(input_read): with m.If(~self.input.line_last): m.d.sync += input_x.eq(input_x + 1) with m.Else(): m.d.sync += input_x.eq(0) m.d.sync += self.input_width.eq(input_x + 1) m.d.sync += input_y.eq(input_y + 1) with m.If(self.input.frame_last): m.d.sync += input_y.eq(0) m.d.sync += self.input_height.eq(input_y + 1) output_x = Signal(16) output_y = Signal(16) output_write = (self.output.ready & self.output.valid) with m.If(output_write): with m.If(output_x < self.output_width - 1): m.d.sync += output_x.eq(output_x + 1) with m.Else(): m.d.sync += output_x.eq(0) m.d.comb += self.output.line_last.eq(1) with m.If(output_y < self.output_height - 1): m.d.sync += output_y.eq(output_y + 1) with m.Else(): m.d.sync += output_y.eq(0) m.d.comb += self.output.frame_last.eq(1) eff_x = output_x - self.shift_x eff_y = output_y - self.shift_y with m.If((eff_x == input_x) & (eff_y == input_y)): m.d.comb += self.output.valid.eq(self.input.valid) m.d.comb += self.input.ready.eq(self.output.ready) m.d.comb += self.output.payload.eq(self.input.payload) with m.Elif(((eff_x < input_x) | (eff_y < input_x)) & (eff_x >= 0) & (eff_y >= 0)): m.d.comb += self.output.valid.eq(0) m.d.comb += self.input.ready.eq(1) with m.Else(): m.d.comb += self.output.valid.eq(1) m.d.comb += self.input.ready.eq(0) m.d.comb += self.output.payload.eq(0) m.submodules.input_stream_info = StreamInfo(self.input) m.submodules.output_stream_info = StreamInfo(self.output) return m
class HispiPhy(Elaboratable): def __init__(self, num_lanes=4, bits=12, hispi_domain="hispi"): assert bits == 12 self.hispi_domain = hispi_domain self.hispi_clk = Signal() self.hispi_lanes = Signal(num_lanes) self.bitslip = [Signal() for _ in range(num_lanes)] self.out = [Signal(12) for _ in range(num_lanes)] self.hispi_x6_in_domain_counter = StatusSignal(32) self.enable_bitslip = ControlSignal(reset=1) self.word_reverse = ControlSignal() def elaborate(self, platform): m = Module() hispi_6_in = "{}_x6_in".format(self.hispi_domain) m.domains += ClockDomain(hispi_6_in) m.d.comb += ClockSignal(hispi_6_in).eq(self.hispi_clk) m.d[hispi_6_in] += self.hispi_x6_in_domain_counter.eq( self.hispi_x6_in_domain_counter + 1) mul = 3 pll = m.submodules.pll = Mmcm(300e6, mul, 1, input_domain=hispi_6_in.format( self.hispi_domain)) pll.output_domain("{}_x6".format(self.hispi_domain), mul * 1) pll.output_domain("{}_x3".format(self.hispi_domain), mul * 2) pll.output_domain("{}_x2".format(self.hispi_domain), mul * 3) pll.output_domain("{}".format(self.hispi_domain), mul * 6) for lane in range(0, len(self.hispi_lanes)): iserdes = m.submodules["hispi_iserdes_" + str(lane)] = _ISerdes( data_width=6, data_rate="DDR", serdes_mode="master", interface_type="networking", num_ce=1, iobDelay="none", ) m.d.comb += iserdes.d.eq(self.hispi_lanes[lane]) m.d.comb += iserdes.ce[1].eq(1) m.d.comb += iserdes.clk.eq( ClockSignal("{}_x6".format(self.hispi_domain))) m.d.comb += iserdes.clkb.eq( ~ClockSignal("{}_x6".format(self.hispi_domain))) m.d.comb += iserdes.rst.eq( ResetSignal("{}_x6".format(self.hispi_domain))) m.d.comb += iserdes.clkdiv.eq( ClockSignal("{}_x2".format(self.hispi_domain))) data = Signal(12) iserdes_output = Signal(6) m.d.comb += iserdes_output.eq( Cat(iserdes.q[j] for j in range(1, 7))) hispi_x2 = "{}_x2".format(self.hispi_domain) lower_upper_half = Signal() m.d[hispi_x2] += lower_upper_half.eq(~lower_upper_half) with m.If(lower_upper_half): m.d[hispi_x2] += data[6:12].eq(iserdes_output) with m.Else(): m.d[hispi_x2] += data[0:6].eq(iserdes_output) data_in_hispi_domain = Signal(12) m.submodules["data_cdc_{}".format(lane)] = FFSynchronizer( data, data_in_hispi_domain, o_domain=self.hispi_domain) hispi_domain = m.d[self.hispi_domain] bitslip = Signal() was_bitslip = Signal() hispi_domain += was_bitslip.eq(bitslip) with m.If(self.bitslip[lane] & ~was_bitslip & self.enable_bitslip): hispi_domain += bitslip.eq(1) with m.Else(): hispi_domain += bitslip.eq(0) serdes_or_emulated_bitslip = Signal() with m.If(bitslip): hispi_domain += serdes_or_emulated_bitslip.eq( ~serdes_or_emulated_bitslip) m.d.comb += iserdes.bitslip.eq(bitslip & serdes_or_emulated_bitslip) data_order_index = Signal(range(4)) with m.If(bitslip & ~serdes_or_emulated_bitslip): hispi_domain += data_order_index.eq(data_order_index + 1) data_order = StatusSignal(range(16)) setattr(self, "data_order_{}".format(lane), data_order) m.d.comb += data_order.eq(Array((1, 4, 9, 12))[data_order_index]) current = Signal(12) last = Signal(12) m.d.comb += current.eq(data_in_hispi_domain) hispi_domain += last.eq(data_in_hispi_domain) reordered = Signal(12) parts = [current[0:6], current[6:12], last[0:6], last[6:12]] for cond, i in iterator_with_if_elif(range(16), m): with cond(data_order == i): first = parts[i % 4] second = parts[i // 4] m.d.comb += reordered.eq(Cat(first, second)) with m.If(self.word_reverse): m.d.comb += self.out[lane].eq( Cat(reordered[i] for i in range(12))) with m.Else(): m.d.comb += self.out[lane].eq( Cat(reordered[i] for i in reversed(range(12)))) out_status_signal = StatusSignal(12, name="out_{}".format(lane)) setattr(self, "out_{}".format(lane), out_status_signal) m.d.comb += out_status_signal.eq(data_in_hispi_domain) return m
class LaneManager(Elaboratable): def __init__(self, input_data: Signal, sync_pattern=(-1, 0, 0)): """ Aligns the word boundries of one Hispi lane and detects control codes. Compatible only with Packetized-SP mode because it needs end markers. :param sync_pattern: the preamble of a control word (default is correct for most cases) :param timeout: Issue a bit slip after a control word wasnt found for n cycles """ self.sync_pattern = sync_pattern self.input_data = input_data self.is_aligned = StatusSignal() self.timeout = ControlSignal(32, reset=10000) self.timeouts_to_resync = ControlSignal(32, reset=10000) self.since_last_sync_pattern_or_bitslip = StatusSignal(32) self.performed_bitslips = StatusSignal(32) self.timeouts_since_alignment = StatusSignal(32) self.last_word = StatusSignal(input_data.shape()) self.last_control_word = StatusSignal( input_data.shape(), decoder=lambda x: next( ("{}/{:012b}".format(control_word, x) for control_word, ending in control_words.items() if "{:012b}" .format(x).endswith(ending)), "UNKNOWN/{:012b}".format(x))) self.do_bitslip = Signal() self.output = ImageStream(self.input_data.shape()) def elaborate(self, platform): m = Module() m.d.sync += self.last_word.eq(self.input_data) m.d.comb += self.output.payload.eq(self.input_data) with m.If(self.since_last_sync_pattern_or_bitslip < self.timeout): m.d.sync += self.since_last_sync_pattern_or_bitslip.eq( self.since_last_sync_pattern_or_bitslip + 1) with m.Else(): with m.If(self.is_aligned): with m.If( self.timeouts_since_alignment > self.timeouts_to_resync ): m.d.sync += self.is_aligned.eq(0) m.d.sync += self.timeouts_since_alignment.eq(0) with m.Else(): m.d.sync += self.timeouts_since_alignment.eq( self.timeouts_since_alignment + 1) with m.Else(): m.d.sync += self.since_last_sync_pattern_or_bitslip.eq(0) m.d.sync += self.performed_bitslips.eq( self.performed_bitslips + 1) m.d.comb += self.do_bitslip.eq(1) with m.FSM(): for i, pattern_word in enumerate(self.sync_pattern): with m.State("sync{}".format(i)): with m.If(self.input_data == Const( pattern_word, self.input_data.shape())): if i < len(self.sync_pattern) - 1: m.next = "sync{}".format(i + 1) else: m.next = "control_word" with m.Else(): with m.If(self.input_data == Const( self.sync_pattern[0], self.input_data.shape())): m.next = "sync1" with m.Else(): m.next = "sync0" with m.State("control_word"): with m.If(ends_with(self.input_data, *control_words.values())): m.d.sync += self.last_control_word.eq(self.input_data) m.d.sync += self.since_last_sync_pattern_or_bitslip.eq(0) m.d.sync += self.is_aligned.eq(1) m.next = "sync0" # assemble the output stream valid = Signal() m.d.comb += valid.eq( ends_with( self.last_control_word, # control_words["START_OF_ACTIVE_FRAME_EMBEDDED_DATA"], control_words["START_OF_ACTIVE_FRAME_IMAGE_DATA"], # control_words["START_OF_ACTIVE_LINE_EMBEDDED_DATA"], control_words["START_OF_ACTIVE_LINE_IMAGE_DATA"])) # delay is needed because we only know that the line finished when the control code is done # this is len(sync_pattern) + 1 + 1 cycles after the line really ended delayed_valid = delay_by(valid, len(self.sync_pattern) + 2, m) delayed_data = delay_by(self.input_data, len(self.sync_pattern) + 2, m) with m.If( ends_with(self.last_control_word, control_words["END_OF_ACTIVE_FRAME"], control_words["END_OF_ACTIVE_LINE"])): m.d.sync += delayed_valid.eq(0) m.d.comb += self.output.payload.eq(delayed_data) m.d.comb += self.output.valid.eq(delayed_valid) m.d.comb += self.output.frame_last.eq( ends_with(self.last_control_word, control_words["END_OF_ACTIVE_FRAME"])) m.d.comb += self.output.line_last.eq( ends_with(self.last_control_word, control_words["END_OF_ACTIVE_LINE"], control_words["END_OF_ACTIVE_FRAME"])) m.submodules.debug = InflexibleSourceDebug(self.output) return m
class Ila(Elaboratable): def __init__(self, trace_length=2048, after_trigger=None): self.trace_length = trace_length self.after_trigger = ControlSignal( range(trace_length), reset=(trace_length // 2 if after_trigger is None else after_trigger)) self.reset = ControlSignal() # Yosys cannot handle a signal named `initial` (bug #2914) self.initial_ = StatusSignal(reset=1) self.running = StatusSignal() self.write_ptr = StatusSignal(range(trace_length)) self.trigger_since = StatusSignal(range(trace_length + 1)) self.probes = [] self.decoders = [] def elaborate(self, platform): m = Module() if hasattr(platform, "ila_error"): raise platform.ila_error after_trigger = Signal.like(self.after_trigger) m.submodules += FFSynchronizer(self.after_trigger, after_trigger) assert hasattr(platform, "trigger"), "No trigger in Design" trigger = Signal() m.submodules += FFSynchronizer(platform.trigger, trigger) assert hasattr(platform, "probes"), "No probes in Design" platform_probes = list(platform.probes.items()) probes = [(k, Signal.like(signal)) for k, (signal, decoder) in platform_probes] for (_, (i, _)), (_, o) in zip(platform_probes, probes): m.submodules += FFSynchronizer(i, o) self.probes = [(k, (len(signal), decoder)) for k, (signal, decoder) in platform_probes] probe_bits = sum(length for name, (length, decoder) in self.probes) print(f"ila: using {probe_bits} probe bits") self.mem = m.submodules.mem = SocMemory( width=ceil(probe_bits / 32) * 32, depth=self.trace_length, soc_write=False, attrs=dict(syn_ramstyle="block_ram")) write_port = m.submodules.write_port = self.mem.write_port( domain="sync") since_reset = Signal(range(self.trace_length + 1)) with m.If(self.running): with m.If(self.write_ptr < (self.trace_length - 1)): m.d.sync += self.write_ptr.eq(self.write_ptr + 1) with m.Else(): m.d.sync += self.write_ptr.eq(0) m.d.comb += write_port.addr.eq(self.write_ptr) m.d.comb += write_port.en.eq(1) m.d.comb += write_port.data.eq(Cat([s for _, s in probes])) # we wait trace_length cycles to be sure to overwrite the whole buffer at least once # and avoid confusing results with m.If(since_reset < self.trace_length): m.d.sync += since_reset.eq(since_reset + 1) with m.If(self.trigger_since == 0): with m.If(trigger & (since_reset > self.trace_length - 1)): m.d.sync += self.trigger_since.eq(1) with m.Else(): with m.If(self.trigger_since < (after_trigger - 1)): m.d.sync += self.trigger_since.eq(self.trigger_since + 1) with m.Else(): m.d.sync += self.running.eq(0) m.d.sync += self.initial_.eq(0) with m.Else(): reset = Signal() m.submodules += FFSynchronizer(self.reset, reset) with m.If(Changed(m, reset)): m.d.sync += self.running.eq(1) m.d.sync += self.trigger_since.eq(0) m.d.sync += self.write_ptr.eq(0) m.d.sync += since_reset.eq(0) return m @driver_method def arm(self): self.reset = not self.reset @driver_method def get_values(self): assert (not self.running) and ( not self.initial_), "ila didnt trigger yet" r = list(range(self.trace_length)) addresses = r[self.write_ptr:] + r[:self.write_ptr] for address in addresses: value = self.mem[address] current_offset = 0 current_row = [] for name, (size, _) in self.probes: current_row.append((value >> current_offset) & (2**size - 1)) current_offset += size yield current_row @driver_method def write_vcd(self, path="/tmp/ila.vcd"): from pathlib import Path path = Path(path) print(f"writing vcd to {path.absolute()}") from vcd import VCDWriter with open(path, "w") as f: with VCDWriter(f) as writer: vcd_vars = [(writer.register_var( 'ila_signals', name, 'reg' if decoder is None else 'string', size=size), decoder) for name, (size, decoder) in self.probes] clk = writer.register_var('ila_signals', 'clk', 'reg', size=1) for timestamp, values in enumerate(self.get_values()): writer.change(clk, timestamp * 2, 1) for (var, decoder), value in zip(vcd_vars, values): writer.change( var, timestamp * 2, value if decoder is None else decoder.get( value, str(value))) writer.change(clk, timestamp * 2 + 1, 0)
class PluginModuleStreamerRx(Elaboratable): def __init__(self, plugin, domain_name="sync"): self.plugin = plugin self.output = BasicStream(32) self.domain_name = domain_name self.trained = ControlSignal() self.valid = StatusSignal() def elaborate(self, platform): m = Module() domain = self.domain_name domain_in = domain + "_in" domain_ddr = domain + "_ddr" domain_ddr_eclk = domain + "_ddr_eclk" m.domains += ClockDomain(domain_in) m.d.comb += ClockSignal(domain_in).eq(self.plugin.clk_word) pll = m.submodules.pll = Pll(input_freq=50e6, vco_mul=4, vco_div=1, input_domain=domain_in) pll.output_domain(domain_ddr, 1) m.submodules.eclk_ddr = EClkSync(domain_ddr, domain_ddr_eclk, input_frequency=400e6) lanes = [] bitslip_signal = Signal() for i in range(4): lane = m.submodules["lane{}".format(i)] = LaneBitAligner( input=self.plugin["lvds{}".format(i)], in_testpattern_mode=~self.valid, bitslip_signal=bitslip_signal, ddr_domain=domain_ddr_eclk, ) lanes.append(lane) word_aligner = m.submodules.word_aligner = WordAligner(domain_ddr_eclk, lanes[0].output) m.d.comb += bitslip_signal.eq(word_aligner.bitslip) valid_iserdes = m.submodules.valid_iserdes = ISerdes8(self.plugin.valid, domain_ddr_eclk, word_domain="sync", invert=True) m.d.comb += valid_iserdes.bitslip.eq(bitslip_signal) m.d.comb += self.valid.eq(valid_iserdes.output[4]) m.d.sync += self.output.payload.eq(Cat(*[lane.output for lane in lanes])) m.d.comb += self.output.valid.eq( self.valid & self.trained ) m.submodules.inflexible_output = InflexibleSourceDebug(self.output) return DomainRenamer(domain)(m) @driver_method def train(self, timeout=32): self.lane0.delay = 15 print("doing word alignment...") for i in range(timeout): if self.lane0.output == 0b00010110: print("-> {} slips".format(i)) print("training lane 0...") self.lane0.train() print("training lane 1...") self.lane1.train() print("training lane 2...") self.lane2.train() print("training lane 3...") self.lane3.train() self.trained = True return else: self.word_aligner.slip() raise TimeoutError()
class LaneBitAligner(Elaboratable): def __init__(self, input, in_testpattern_mode, ddr_domain, bitslip_signal, testpattern=0b00010110): """ Does bit alignment of one lane usig a given testpattern if in_testpattern_mode is high """ self.input = input self.bitslip_signal = bitslip_signal self.in_testpattern_mode = in_testpattern_mode self.ddr_domain = ddr_domain self.testpattern = testpattern self.delay = ControlSignal(5, reset=15) self.error = StatusSignal(32) self.output = StatusSignal(8) def elaborate(self, platform): m = Module() delayed = Signal() m.submodules.delayd = Instance( "DELAYD", i_A=self.input, i_DEL0=self.delay[0], i_DEL1=self.delay[1], i_DEL2=self.delay[2], i_DEL3=self.delay[3], i_DEL4=self.delay[4], o_Z=delayed, ) iserdes = m.submodules.iserdes = ISerdes8(delayed, self.ddr_domain, word_domain="sync", invert=True) m.d.comb += self.output.eq(iserdes.output) m.d.comb += iserdes.bitslip.eq(self.bitslip_signal) with m.If(self.in_testpattern_mode & (self.output != self.testpattern)): m.d.sync += self.error.eq(self.error + 1) return m @driver_method def train(self): from time import sleep start_current = 0 start_longest = 0 len_longest = 0 was_good = True for i in range(32): self.delay = i e_start = self.error sleep(0.01) difference = self.error - e_start if difference == 0 and not was_good: start_current = i elif ((difference != 0) or (i == 31)) and was_good: len = i - start_current if len > len_longest: len_longest = len start_longest = start_current print(i, difference) was_good = difference == 0 self.delay = int(start_longest + (len_longest / 2)) print("-> delay tap", self.delay)
class HdmiStreamAligner(Elaboratable): """ Aligns the HDMI output to the Image stream by 'slipping' data during the blanking periods until the frame is aligned. """ def __init__(self, input: ImageStream, hdmi): self.hdmi = hdmi self.input = input self.allow_slip_h = ControlSignal(reset=1) self.allow_slip_v = ControlSignal(reset=1) self.slipped_v = StatusSignal(32) self.slipped_h = StatusSignal(32) self.line_cycles = StatusSignal(32) self.frame_cycles = StatusSignal(32) def elaborate(self, platform): m = Module() m.submodules.debug = InflexibleSinkDebug(self.input) input_stream_info = m.submodules.input_stream_info = StreamInfo( stream=self.input) line_length_counter = Signal(32) was_blanking_x = Signal() m.d.sync += was_blanking_x.eq(self.hdmi.timing_generator.is_blanking_x) with m.If(~self.hdmi.timing_generator.is_blanking_x): m.d.sync += line_length_counter.eq(line_length_counter + 1) with m.If(self.hdmi.timing_generator.is_blanking_x & ~was_blanking_x): m.d.sync += self.line_cycles.eq(line_length_counter) m.d.sync += line_length_counter.eq(0) frame_length_counter = Signal(32) was_blanking_y = Signal() m.d.sync += was_blanking_y.eq(self.hdmi.timing_generator.is_blanking_y) with m.If(self.hdmi.timing_generator.active): m.d.sync += frame_length_counter.eq(frame_length_counter + 1) with m.If(self.hdmi.timing_generator.is_blanking_y & ~was_blanking_y): m.d.sync += self.frame_cycles.eq(frame_length_counter) m.d.sync += frame_length_counter.eq(0) with m.If(self.hdmi.timing_generator.active): m.d.comb += self.input.ready.eq(1) was_line_last = Signal() was_frame_last = Signal() with m.If(self.input.ready): m.d.sync += was_line_last.eq(self.input.line_last) m.d.sync += was_frame_last.eq(self.input.frame_last) with m.If(self.hdmi.timing_generator.is_blanking_x & ~was_line_last & self.allow_slip_h): m.d.sync += self.slipped_h.eq(self.slipped_h + 1) m.d.comb += self.input.ready.eq(1) with m.If(self.hdmi.timing_generator.is_blanking_y & ~was_frame_last & self.allow_slip_v): m.d.sync += self.slipped_v.eq(self.slipped_v + 1) m.d.comb += self.input.ready.eq(1) return m
class HdmiRxLane(Elaboratable): def __init__(self, pin, ddr_domain, qdr_domain): self.pin = pin self.ddr_domain = ddr_domain self.qdr_domain = qdr_domain self.not_valid_cnt = StatusSignal(16) self.blanking_threshold = ControlSignal( 16, reset=(480 * 16)) # 128 is dvi spec, for hdmi this should be 8 self.blankings_hit = StatusSignal(32) self.raw_word = StatusSignal(10) self.invert = StatusSignal(reset=1) self.data = StatusSignal(8) self.data_enable = StatusSignal() self.control = StatusSignal(2) def elaborate(self, platform): m = Module() self.delayf = m.submodules.delayf = DelayF(self.pin.i) iddr = m.submodules.iddr = DomainRenamer(self.qdr_domain)(IDDRX2F( self.delayf.o, self.ddr_domain)) iddr_stream = BasicStream(4) m.d.comb += iddr_stream.valid.eq(1) m.d.comb += iddr_stream.payload.eq(iddr.output) gearbox = m.submodules.gearbox = DomainRenamer(self.qdr_domain)( StreamGearbox(iddr_stream, target_width=10)) fifo = m.submodules.fifo = BufferedAsyncStreamFIFO( gearbox.output, 32, i_domain=self.qdr_domain, o_domain="sync") window = m.submodules.window = StreamWindow(fifo.output, window_words=2) self.select = select = m.submodules.select = StreamSelect( window.output, output_width=10) m.d.comb += select.output.ready.eq(1) with m.If(~select.output.valid): # ths should never happen if the clock domains are correctly set up m.d.sync += self.not_valid_cnt.eq(self.not_valid_cnt + 1) m.d.comb += self.raw_word.eq(select.output.payload) tmds = m.submodules.tmds = TmdsDecoder(self.raw_word ^ Repl(self.invert, 10)) m.d.comb += self.data.eq(tmds.data) m.d.comb += self.data_enable.eq(tmds.data_enable) m.d.comb += self.control.eq(tmds.control) blanking_ctr = Signal.like(self.blanking_threshold) with m.If(~self.data_enable): with m.If(blanking_ctr < self.blanking_threshold): m.d.sync += blanking_ctr.eq(blanking_ctr + 1) with m.If(blanking_ctr == (self.blanking_threshold - 1)): m.d.sync += self.blankings_hit.eq(self.blankings_hit + 1) with m.Else(): m.d.sync += blanking_ctr.eq(0) return m @driver_method def train(self, start=0, step=10, n=13, fine_training=False): self.delayf.set_delay(start) from time import sleep best = (0.0, 0, 0) for i in range(n): delay = i * step + start print(f"delay {delay}") for alignment in range(9): self.select.offset = alignment hit_before = self.blankings_hit sleep(0.1) hit = self.blankings_hit - hit_before if hit > best[0]: best = (hit, delay, alignment) print(f"alignment {alignment} hit {hit}") self.delayf.forward(step) hits, delay, alignment = best print(f"elected hits={hits} delay={delay} alignment={alignment}") self.delayf.set_delay(delay) self.select.offset = alignment if fine_training: if hits != 0: return self.train(start=delay - 10, step=1, n=20, fine_training=False) else: print("failed training") else: return best