def elaborate(self, platform): def pat(value, width): return Const(value, unsigned(width)) m = Module() h = self.resolution.horizontal v = self.resolution.vertical h_count = Signal(bits_for(h.active // 16)) v_count = Signal(bits_for(v.active)) led = platform.request('led') # Always present data to write # Since we always have it # This only works with the -retime attribute # I wonder if there is something odd about a FIFO here m.d.sync += self.fifo.w_en.eq(1) m.d.sync += self.fifo.w_data.eq(Mux(h_count[2] ^ v_count[6], 0xffff, 0)) # When data is accepted (w_rdy == 1), then increment counters with m.If(self.fifo.w_rdy): # Increment h_count m.d.sync += h_count.eq(h_count + 1) with m.If(h_count == (h.active // 16) - 1): m.d.sync += h_count.eq(0) m.d.sync += v_count.eq(v_count + 1) with m.If(v_count == v.active - 1): m.d.sync += v_count.eq(0) return m
def __init__(self, freq): # type: (int) -> None min_baud_rate = 9600 max_baud_rate = 115200 default_baud_rate = 9600 self.freq = freq baud_rate_div_width = bits_for(freq // min_baud_rate) self.baud_rate_div_default = freq // default_baud_rate self.baud_rate_div = Signal(baud_rate_div_width, reset=self.baud_rate_div_default) self.baud_rate_val = Signal(bits_for(max_baud_rate), reset=default_baud_rate) self.baud_rate_idx = Signal(3, reset=0) self.baud_rate_update_done = Signal() self.baud_rate_update = Signal() self.baud_rate_mode_prev = Signal(2) self.uart_rx = Signal() self.uart_rx_bit = Signal(bits_for(10)) self.uart_rx_div_cnt = Signal(8) self.uart_rx_symbol_act = Signal(reset=0) self.uart_rx_time_per_sub_bit = Signal(16, reset=freq * 16 // (default_baud_rate * 16)) self.uart_rx_time = Signal(32, reset=0) self.uart_rx_next_sub_bit_time = Signal(32, reset=0) self.uart_rx_pos = Signal(bits_for(16 * 10)) self.uart_rx_shaped = Signal() self.uart_rx_ctrl_word = Signal(8) self.rx_bit_time = Signal(32, reset=0) self.irda_rx = Signal(reset=1) self.irda_rx_pulse_timeout = Signal(baud_rate_div_width) self.irda_rx_shaped = Signal(reset=1) self.baud_rate_mode = Signal()
def __init__(self, n=1, w=320, h=240, dw=8): # Parameters self.n = n self.w = w self.h = h self.dw = dw # Inputs self.i_p = Signal(dw) self.i_valid = Signal() self.i_ready = Signal() self.i_reset = Signal() # Outputs self.o_p = Signal(dw) self.o_valid = Signal() self.o_ready = Signal() self.o_x = Signal(bits_for(w + 2 * n), reset=0) self.o_y = Signal(bits_for(h + 2 * n), reset=0) self.o_eof = Signal() # Diagnostics self.bep = Signal() self.bl = Signal() self.bsp = Signal() self.b = Signal()
def __init__(self, resolution, double_buffer): self.resolution = resolution self.db = double_buffer # h_count and v_count track progress of display through active area self.h_count = Signal(bits_for(resolution.horizontal.active // 16)) self.v_count = Signal(bits_for(resolution.vertical.active)) self.f_count = Signal(18) # enough for more than an hour
def elaborate(self, platform): m = Module() read_input = self.i_valid & self.o_ready write_output = self.o_valid & self.i_ready # Duplicate input x = Signal(bits_for(self.w), reset=0) y = Signal(bits_for(self.h), reset=0) got = Signal(2, reset=0) left_half = (x < (self.w // 2)) with m.If(self.i_en): # Calculate input co-ordinates with m.If(read_input): m.d.sync += x.eq(x + 1) with m.If(x == self.w - 1): m.d.sync += [x.eq(0), y.eq(y + 1)] with m.If(y == self.h - 1): m.d.sync += y.eq(0) # We are ready unless we have an output that has been written m.d.comb += self.o_ready.eq((got == 0) | ((got == 1) & self.i_ready)) # Read input with m.If(read_input & left_half): m.d.sync += [self.o.eq(self.i), got.eq(2)] # Output in valid when got is greater than 0 m.d.comb += self.o_valid.eq(got > 0) # Move on when output is consumed with m.If(write_output): with m.If((got == 2) | ~left_half | ~self.i_valid): m.d.sync += got.eq(got - 1) with m.Else(): # Not enabled self.buffer1(m) # Update output co-ordinates self.out_coords(m, self.w, self.h) # Camera reset with m.If(self.i_reset): m.d.sync += [ self.o_x.eq(0), self.o_y.eq(0), x.eq(0), y.eq(0), got.eq(0) ] return m
def __init__(self, n=1, w=320, h=240): super().__init__() # Parameters self.n = n self.w = w self.h = h # Outputs self.o_x = Signal(bits_for(w + 2 * n)) self.o_y = Signal(bits_for(h + 2 * n)) self.o_eof = Signal()
def __init__(self, w=640, h=320): super().__init__() # Inputs self.i_reset = Signal(reset=0) # Parameters self.w = w self.h = h # Outputs self.o_x = Signal(bits_for(w) - 1, reset=0) self.o_y = Signal(bits_for(h) - 1, reset=0) self.o_eof = Signal()
def __init__(self, w=320, h=240): super().__init__() # Inputs self.i_reset = Signal() self.i_en = Signal() # Parameters self.w = w self.h = h # Outputs self.o_x = Signal(bits_for(w)) self.o_y = Signal(bits_for(h)) self.o_eof = Signal()
def __init__(self, video_timer, pixel_fifo): super().__init__(video_timer) self.horizontal_config = video_timer.params.horizontal self.fifo = pixel_fifo # Inputs from video timer - exposed for debugging # 1 clock before next active line self.at_active_line_m1 = Signal() # In vertical blanking area self.vertical_blanking = Signal() # Internal self.shift_register = Signal(16) self.num_pixels = self.horizontal_config.active self.pixel_counter = Signal(bits_for(self.num_pixels)) self.state = Signal(bits_for(PullState.OUTPUTING))
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
def __init__(self, *, divisor, divisor_bits=None, data_bits=8, parity="none", pins=None): _check_parity(parity) self._parity = parity # The clock divisor must be at least 5 to keep the FSM synchronized with the serial input # during a DONE->IDLE->BUSY transition. _check_divisor(divisor, 5) self.divisor = Signal(divisor_bits or bits_for(divisor), reset=divisor) self.data = Signal(data_bits) self.err = Record([ ("overflow", 1), ("frame", 1), ("parity", 1), ]) self.rdy = Signal() self.ack = Signal() self.i = Signal(reset=1) self._pins = pins
def __init__(self, address_input: BasicStream, memory: Memory): assert len(address_input.payload) == bits_for(memory.depth) self.address_input = address_input self.memory = memory self.output = address_input.clone() self.output.payload = Signal(memory.width)
def __init__(self, k, sh=0, w=320, h=240, dw=8): # Parameters self.w = w self.h = h self.k = k self.sh = sh self.dw = dw # Inputs self.i_p = Signal(dw) self.i_valid = Signal() # Outputs self.o_p = Signal(dw) self.o_valid = Signal() self.o_stall = Signal() self.o_x = Signal(bits_for(w), reset=self.w - 1) self.o_y = Signal(bits_for(h), reset=self.h - 1)
def __init__(self, *, divisor, divisor_bits=None, **kwargs): self.divisor = Signal(divisor_bits or bits_for(divisor), reset=divisor) self.rx = AsyncSerialRX(divisor=divisor, divisor_bits=divisor_bits, **kwargs) self.tx = AsyncSerialTX(divisor=divisor, divisor_bits=divisor_bits, **kwargs)
def __init__(self, width=16, depth=32): self.rdat = Signal(width) self.wdat = Signal(width) self.we = Signal() self.re = Signal() self.pos = Signal(bits_for(depth)) # counter self.updown = Signal() self.depth = depth self.mem = Memory(width=width, depth=depth)
def __init__(self, num_steps, restart_value, is_private_call=0): """Private use num_steps() or num_bits()""" assert is_private_call self.num_steps = num_steps self.num_bits = max(bits_for(num_steps), 4) assert 4 <= self.num_bits <= 32 self.polynomial = POLYNOMIALS[self.num_bits] # Ensure restart_value is in allowed range self.restart_value = (((restart_value or 1) - 1) % num_steps) + 1 # values is a list of all values by step self.values = [self.restart_value]
def __init__(self, value, *, width=None, signed=None): if not isinstance(value, int): raise TypeError("Value must be an integer, not {!r}".format(value)) self._value = value if width is None: width = bits_for(value) if not isinstance(width, int): raise TypeError("Width must be an integer, not {!r}".format(width)) if width < bits_for(value): raise ValueError( "Width must be greater than or equal to the number of bits needed to" " represent {}".format(value)) self._width = width if signed is None: signed = value < 0 if not isinstance(signed, bool): raise TypeError( "Signedness must be a bool, not {!r}".format(signed)) self._signed = signed
def __init__(self, trans_func, kw=3, w=320, h=240, dw=8): # Parameters self.kw = kw self.w = w self.h = h self.trans_func = trans_func self.dw = dw # Inputs self.i_p = Signal(dw) self.i_valid = Signal() self.i_ready = Signal() self.i_reset = Signal() # Outputs self.o_p = Signal(dw) self.o_valid = Signal(reset=0) self.o_ready = Signal() self.o_x = Signal(bits_for(w - kw + 1), reset=0) self.o_y = Signal(bits_for(h - kw + 1), reset=0) self.o_eof = Signal(reset=0)
def __init__(self, k, kw=3, sh=0, w=320, h=240, dw=8, same=0): # Parameters self.kw = kw self.w = w self.h = h self.k = k self.sh = sh self.dw = dw # Inputs self.i_p = Signal(dw) self.i_valid = Signal() self.i_ready = Signal() self.i_reset = Signal() # Outputs self.o_p = Signal(dw) self.o_valid = Signal(reset=0) self.o_ready = Signal() self.o_x = Signal(bits_for(w - self.kw + 1), reset=0) self.o_y = Signal(bits_for(h - self.kw + 1), reset=0) self.o_eof = Signal(reset=0)
def __init__(self, k, kw=3, sh=0, w=320, h=240, dw=8, same=0): # Parameters self.kw = kw self.w = w self.h = h self.k = k self.sh = sh self.dw = dw self.same = same # Inputs self.i_p = Signal(dw) self.i_valid = Signal() self.i_ready = Signal() self.i_reset = Signal() # Outputs self.o_p = Signal(dw) self.o_valid = Signal() self.o_ready = Signal(reset=1) self.o_x = Signal(bits_for(w), reset=self.w - 1) self.o_y = Signal(bits_for(h), reset=self.h - 1) self.frame_done = Signal()
def _compute_addr_range(self, addr, size, step=1, *, alignment, extend): if addr is not None: if not isinstance(addr, int) or addr < 0: raise ValueError( "Address must be a non-negative integer, not {!r}".format( addr)) if addr % (1 << self.alignment) != 0: raise ValueError( "Explicitly specified address {:#x} must be a multiple of " "{:#x} bytes".format(addr, 1 << alignment)) else: addr = self._align_up(self._next_addr, alignment) if not isinstance(size, int) or size < 0: raise ValueError( "Size must be a non-negative integer, not {!r}".format(size)) size = self._align_up(max(size, 1), alignment) if addr > (1 << self.addr_width) or addr + size > ( 1 << self.addr_width): if extend: self.addr_width = bits_for(addr + size) else: raise ValueError( "Address range {:#x}..{:#x} out of bounds for memory map spanning " "range {:#x}..{:#x} ({} address bits)".format( addr, addr + size, 0, 1 << self.addr_width, self.addr_width)) addr_range = range(addr, addr + size, step) overlaps = self._ranges.overlaps(addr_range) if overlaps: overlap_descrs = [] for overlap in overlaps: if id(overlap) in self._resources: _, _, resource_range = self._resources[id(overlap)] overlap_descrs.append( "resource {!r} at {:#x}..{:#x}".format( overlap, resource_range.start, resource_range.stop)) if id(overlap) in self._windows: _, window_range = self._windows[id(overlap)] overlap_descrs.append("window {!r} at {:#x}..{:#x}".format( overlap, window_range.start, window_range.stop)) raise ValueError( "Address range {:#x}..{:#x} overlaps with {}".format( addr, addr + size, ", ".join(overlap_descrs))) return addr_range
def __init__(self, x_res=320, y_res=240, fifo_depth=1024): super().__init__() # Parameters self.x_res = x_res self.y_res = y_res self.fifo_depth = fifo_depth # Inputs self.i_reset = Signal() # Outputs self.o_eof = Signal() self.o_x = Signal(9) self.o_y = Signal(9) self.o_level = Signal(bits_for(fifo_depth))
def __init__(self, *, divisor, divisor_bits=None, data_bits=8, parity="none", pins=None): _check_parity(parity) self._parity = parity _check_divisor(divisor, 1) self.divisor = Signal(divisor_bits or bits_for(divisor), reset=divisor) self.data = Signal(data_bits) self.rdy = Signal() self.ack = Signal() self.o = Signal(reset=1) self._pins = pins
def write_port(self): port = Record([("addr", bits_for(self.depth)), ("en", 1), ("data", self.width)]) self._write_ports.append(port) return port
def elaborate(self, platform): m = Module() # Useful constants kw1 = self.kw - 1 # One less than kernel width #print("kw1:", kw1) kw2 = self.kw // 2 + 1 # One more than half the kernel width #print("kw2:", kw2) # x and y co-ordinates of latest received pixel x = Signal(bits_for(self.w), reset=0) y = Signal(bits_for(self.h + self.kw), reset=0) # x2 is two columns ahead of x with wraparound x2 = Signal(bits_for(self.h)) m.d.comb += x2.eq(Mux(x >= self.w - 2, x + 2 - self.w, x + 2)) # Indicates if pixel generation has started started = Signal(reset=0) # Current pixel window signals p = Array( Array( Signal(self.dw, name="p{}{}".format(x, y)) for y in range(self.kw)) for x in range(self.kw)) # Bottom right corner of window is combinatorial m.d.comb += p[kw1][kw1].eq( Mux(x == 0, p[kw1][kw1 - 1], Mux(y == self.h, p[kw1 - 1][kw1], self.i_p))) # Signals used to retain the previous value read from line buffers pd = Array(Signal(self.dw, name="pd{}".format(x)) for x in range(kw1)) # Line buffers r = [] w = [] pl = [] for i in range(kw1): # Create line buffer mem = Memory(width=self.dw, depth=self.w) pl.append(mem) rp = mem.read_port() m.submodules += rp r.append(rp) wp = mem.write_port() m.submodules += wp w.append(wp) # Connect addresses m.d.comb += wp.addr.eq(x) m.d.comb += rp.addr.eq(x2) # Read 2 ahead with wraparound # TODO Correct formula for this if (self.kw == 3 and i < 1) or (self.kw > 3 and i < kw2): m.d.comb += w[i].en.eq(self.i_valid & self.o_ready & (y != 1)) else: m.d.comb += w[i].en.eq(self.i_valid & self.o_ready) if i == kw1 - 1: m.d.comb += w[i].data.eq(self.i_p) else: m.d.comb += w[i].data.eq(Mux(y >= 2, pd[i + 1], self.i_p)) # For simulation if platform is None: for i in range(kw1): ln = "l{}".format(i) self.__dict__[ln] = Signal(self.w * self.dw, name=ln) m.d.comb += self.__dict__[ln].eq( Cat([pl[i][self.w - 1 - j] for j in range(self.w)])) # Pixel not valid, and frame not done, by default m.d.sync += [self.o_valid.eq(0), self.frame_done.eq(0)] # New pixel value n_p = Signal(self.dw + self.sh + 1) m.d.comb += n_p.eq( sum([ sum([ p[x][y] * self.k[self.kw * x + y] for x in range(self.kw) ]) for y in range(self.kw) ])) # Process pixel with m.If(self.i_reset): # Partial reset (e.g. due to camera reset) m.d.sync += [ x.eq(0), y.eq(0), started.eq(0), self.o_ready.eq(1), self.o_valid.eq(0), self.o_x.eq(0), self.o_y.eq(0) ] with m.Elif(self.i_ready & (self.i_valid | ~self.o_ready) ): # Valid input pixel or trailing pixels # Save last values read for wraparound for i in range(kw1): m.d.sync += pd[i].eq(r[i].data), # Increment x and y (allowing y to go past last line) m.d.sync += x.eq(x + 1) with m.If(x == self.w - 1): m.d.sync += [x.eq(0), y.eq(y + 1)] # Test for frame done with m.If((y == self.h + 1)): m.d.sync += [ x.eq(0), y.eq(0), self.o_ready.eq(1), started.eq(0), self.frame_done.eq(1) ] # Stall for the last row plus 1 pixel, while last pixels flushed with m.If((y == self.h - 1) & (x == self.w - 1)): m.d.sync += self.o_ready.eq(0) # Start generating pixels at bottom right of window with m.If((y == kw2 - 1) & (x == kw2 - 2)): m.d.sync += started.eq(1) # Move window and generate pixel with m.If((x == 0) & (y == 0)): # Extend edges with closest pixel # Top left corner for i in range(kw2): for j in range(kw2): m.d.sync += p[i][j].eq(self.i_p) with m.Elif((x < kw2) & (y == 0)): # Start of top row for i in range(kw2): m.d.sync += p[i][x + kw2 - 1].eq(self.i_p) with m.Elif((y < kw2) & (x == 0)): # First column of top rows for j in range(kw2): m.d.sync += p[y + kw2 - 1][j].eq(self.i_p) if (self.kw > 3): # Fill in rest of starting window for kernels larger than 3 # TODO fix for kernels larger than 5 for i in range(1, kw2): with m.Elif((y == 1) & (x == i)): m.d.sync += p[3][i + kw2 - 1].eq(self.i_p) with m.Elif((y == 2) & (x == 1)): m.d.sync += p[4][3].eq(self.i_p) with m.Elif(started): # Started generating pixels # Move the window for i in range(kw1): # Omit the last row for j in range(self.kw): if j == kw1: # For last column, if at right hand edge, repeat last pixel m.d.sync += p[i][j].eq( Mux(x == self.w - 1, p[i][j], r[i].data)) else: # If first column get first pixel rather than shuffling left m.d.sync += p[i][j].eq( Mux(x == kw2 - 2, pd[i], p[i][j + 1])) if self.kw == 3: m.d.sync += [ # Deal with last row of the window p[kw1][0].eq( Mux(y == self.h, Mux(x == 0, pd[kw1 - 1], p[kw1 - 1][kw1 - 1]), Mux(x == 0, self.i_p, p[kw1][kw1 - 1]))), p[kw1][1].eq( Mux(y == self.h, Mux(x == 0, pd[kw1 - 1], p[kw1 - 1][kw1]), self.i_p)) ] else: for j in range(kw1): m.d.sync += p[kw1][j].eq( Mux(x == 0, pd[kw1 - 1], p[kw1][j + 1])) # Generate the pixel m.d.sync += [ self.o_valid.eq(1), # Check for overflow self.o_p.eq((Mux(self.same & n_p[-1], (p[1][1] << self.sh), n_p) >> self.sh)) ] # Generate output pixel co-ordinates m.d.sync += self.o_x.eq(self.o_x + 1) with m.If(self.o_x == self.w - 1): m.d.sync += [self.o_y.eq(self.o_y + 1), self.o_x.eq(0)] with m.If(self.o_y == self.h - 1): m.d.sync += self.o_y.eq(0) return m
def __exit__(self, exc_type, exc_val, exc_tb): self.current_conditional.__exit__(exc_type, exc_val, exc_tb) self.last_stage.width = bits_for(self.stage) self.last_stage.value = self.stage
def read_port(self): port = Record([("addr", bits_for(self.depth)), ("en", 1), ("data", self.width)], name="rp") self._read_ports.append(port) return port
def elaborate(self, platform): m = Module() # Create line buffer mem = Memory(width=16, depth=self.w) m.submodules.r = r = mem.read_port() m.submodules.w = w = mem.write_port() # Conditions for consuming input and output read_input = self.i_valid & self.o_ready write_output = self.o_valid & self.i_ready # Extended pixels for output o_r = Signal(7) o_g = Signal(8) o_b = Signal(7) # Save input pixels p0 = Rgb565() p1 = Rgb565() p2 = Rgb565() p3 = Rgb565() # For simulation if platform is None: lb = Signal(self.w * 16) m.d.comb += lb.eq(Cat([mem[self.w - 1 - j] for j in range(self.w)])) i_r = Signal(5) p0_r = Signal(5) p1_r = Signal(5) p2_r = Signal(5) p3_r = Signal(5) m.d.comb += [ i_r.eq(self.i.r), p0_r.eq(p0.r), p1_r.eq(p1_r), p2_r.eq(p2_r), p3_r.eq(p3_r) ] # Toggle between first and second pixel first_col = Signal(reset=1) first_row = Signal(reset=1) # Input x co-ordinate x = Signal(bits_for(self.w), reset=0) # Connect line buffer m.d.comb += [ w.en.eq(read_input & first_row), w.data.eq(self.i.as_data()), w.addr.eq(x), r.addr.eq(x) ] # Get p0 and p1 from line buffer with m.If(~first_row): with m.If(first_col): m.d.sync += [ p1.r.eq(r.data[11:]), p1.g.eq(r.data[5:11]), p1.b.eq(r.data[:5]) ] with m.Else(): m.d.sync += [ p0.r.eq(r.data[11:]), p0.g.eq(r.data[5:11]), p0.b.eq(r.data[:5]) ] # When input is consumed, save pixel and if second, set output valid with m.If(read_input): m.d.sync += [x.eq(x + 1), first_col.eq(~first_col)] with m.If(x == self.w - 1): m.d.sync += [first_row.eq(~first_row), x.eq(0)] with m.If(~first_row): with m.If(first_col): m.d.sync += p2.eq(self.i) with m.Else(): m.d.sync += [p3.eq(self.i), self.o_valid.eq(1)] # We are always ready for second pixel, but only ready for the first pixel when downstream is ready m.d.comb += self.o_ready.eq(first_row | ~first_col | self.i_ready) # Calculate output m.d.comb += [ o_r.eq(p0.r + p1.r + p2.r + p3.r + 1), o_g.eq(p0.g + p1.g + p2.g + p3.g + 1), o_b.eq(p0.b + p1.b + p2.b + p3.b + 1), self.o.r.eq(o_r[2:]), self.o.g.eq(o_g[2:]), self.o.b.eq(o_b[2:]) ] # When output is consumed set not valid with m.If(write_output): m.d.sync += self.o_valid.eq(0) # Calculate output co-ordinates self.out_coords(m, self.w // 2, self.h // 2) # Camera reset with m.If(self.i_reset): m.d.sync += [ self.o_x.eq(0), self.o_y.eq(0), x.eq(0), first_col.eq(1), first_row.eq(1) ] return m
def elaborate(self, platform): m = Module() # Consume input condition read_input = self.i_valid & self.o_ready # x and y co-ordinates of input pixel x = Signal(bits_for(self.w), reset=0) y = Signal(bits_for(self.h), reset=0) # Line buffer mem = Memory(width=16, depth=self.w) m.submodules.rp = rp = mem.read_port() m.submodules.wp = wp = mem.write_port() # Connect line buffer m.d.comb += [ rp.addr.eq(self.o_x - self.n + 1), wp.addr.eq(x), wp.en.eq(read_input), wp.addr.eq(x), wp.data.eq(self.i.as_data()) ] # For simulation if platform is None: # Make the line buffer visible in gtkwave l0 = Signal(self.w * 16) m.d.comb += l0.eq(Cat([mem[self.w - 1 - i] for i in range(self.w)])) # Save start and end pixel s_p = Rgb565() e_p = Rgb565() # Keep a copy of start and end pixels, and calculate input co-ordinates with m.If(read_input): m.d.sync += x.eq(x + 1) with m.If(x == 0): m.d.sync += s_p.eq(self.i) with m.If(x == self.w - 1): m.d.sync += [e_p.eq(self.i), x.eq(0), y.eq(y + 1)] with m.If(y == self.h - 1): m.d.sync += y.eq(0) # Determine o_ready m.d.comb += self.o_ready.eq((self.o_y < self.h + self.n) & (self.o_x < self.w + self.n) & ((self.o_y == 0) | (self.o_y > self.n)) & ((self.o_x == 0) | (self.o_x > self.n))) # Determine end of frame m.d.comb += self.o_eof.eq(((self.o_x == self.w + self.n * 2 - 1) & (self.o_y == self.h + self.n * 2 - 1))) # Determine the output pixel with m.If((self.o_x >= self.h + self.n)): m.d.comb += self.o.eq(e_p) # Saved end pixel with m.Elif((self.o_y == 0) | ((self.o_y > self.n) & (self.o_y < self.h + self.n))): # Input pixel line with m.If((self.o_x > 0) & (self.o_x <= self.n)): m.d.comb += self.o.eq(s_p) # Saved start pixel with m.Else(): m.d.comb += self.o.eq(self.i) # Input pixel with m.Else(): # Line buffer line with m.If(self.o_x < self.n): m.d.comb += self.o.eq(s_p) # Saved start pixel with m.Else(): m.d.comb += [ # From line buffer self.o.r.eq(rp.data[11:]), self.o.g.eq(rp.data[5:11]), self.o.b.eq(rp.data[:5]) ] # Output pixels with m.If(self.i_ready): # Output pixel always valid, when downstream is ready m.d.comb += self.o_valid.eq(1) # Set the output co-ordinates m.d.sync += self.o_x.eq(self.o_x + 1) with m.If(self.o_x == self.w + self.n * 2 - 1): m.d.sync += [self.o_y.eq(self.o_y + 1), self.o_x.eq(0)] with m.If(self.o_y == self.h + self.n * 2 - 1): m.d.sync += self.o_y.eq(0), return m
def elaborate(self, platform): m = Module() # Useful constants kw1 = self.kw - 1 # One less than kernel width kw2 = self.kw // 2 + 1 # One more than half the kernel width n = kw2 - 1 # Width of border # Condition to consume input read_input = self.i_valid & self.o_ready # x and y co-ordinates of input pixel x = Signal(bits_for(self.w), reset=0) y = Signal(bits_for(self.h + self.kw), reset=0) # x1 is one column ahead of x with wraparound x1 = Signal(bits_for(self.h)) m.d.comb += x1.eq(Mux(x >= self.w - 1, x + 1 - self.w, x + 1)) # Current pixel window signals p = Array( Array( Signal(self.dw, name="p{}{}".format(x, y)) for y in range(self.kw)) for x in range(self.kw)) # Line buffers r = [] w = [] pl = [] for i in range(self.kw): # Create line buffer mem = Memory(width=self.dw, depth=self.w, name="m{}".format(i)) pl.append(mem) rp = mem.read_port() m.submodules += rp r.append(rp) wp = mem.write_port() m.submodules += wp w.append(wp) # Connect ports m.d.comb += wp.addr.eq(x) m.d.comb += rp.addr.eq(x1) m.d.comb += wp.en.eq(read_input) if i == kw1: m.d.comb += wp.data.eq(self.i_p) for i in range(kw1): m.d.comb += w[i].data.eq(r[i + 1].data) # For simulation if platform is None: for i in range(self.kw): ln = "l{}".format(i) self.__dict__[ln] = Signal(self.w * self.dw, name=ln) m.d.comb += self.__dict__[ln].eq( Cat([pl[i][self.w - 1 - j] for j in range(self.w)])) # New pixel value n_p = Signal(self.dw + self.sh + 1) m.d.comb += n_p.eq( sum([ sum([ p[x][y] * self.k[self.kw * x + y] for x in range(self.kw) ]) for y in range(self.kw) ])) m.d.comb += self.o_p.eq(n_p >> self.sh) # Ready when output is not valid or downstream is ready m.d.comb += self.o_ready.eq(~self.o_valid | self.i_ready) with m.If(read_input): # Determine input co-ordinates m.d.sync += x.eq(x + 1) with m.If(x == self.w - 1): m.d.sync += [x.eq(0), y.eq(y + 1)] with m.If(y == self.h - 1): m.d.sync += y.eq(0) # Move window for i in range(self.kw): for j in range(kw1): m.d.sync += p[i][j].eq(p[i][j + 1]) for i in range(kw1): m.d.sync += p[i][kw1].eq(r[i + 1].data) m.d.sync += p[kw1][kw1].eq(self.i_p) # Output valid after window of kernel size is reached m.d.sync += self.o_valid.eq(((x >= kw1) & (y >= kw1))) # Set output co-ordinates m.d.sync += [self.o_x.eq(x + n * 2), self.o_y.eq(y + n * 2)] # End of frame after end of input m.d.sync += self.o_eof.eq((y == self.h - 1) & (x == self.w - 1)) # Camera reset with m.If(self.i_reset): # Partial reset (e.g. due to camera reset) m.d.sync += [ x.eq(0), y.eq(0), self.o_x.eq(0), self.o_y.eq(0), self.o_eof.eq(0), self.o_valid.eq(0) ] return m