def elab(self, m): # Track previous restart, next was_restart = Signal() m.d.sync += was_restart.eq(self.restart) was_next = Signal() m.d.sync += was_next.eq(self.next) # Decide address to be output (determines data available next cycle) last_mem_addr = Signal.like(self.limit) m.d.sync += last_mem_addr.eq(self.mem_addr) incremented_addr = Signal.like(self.limit) m.d.comb += incremented_addr.eq( Mux(last_mem_addr == self.limit - 1, 0, last_mem_addr + 1)) with m.If(self.restart): m.d.comb += self.mem_addr.eq(0) with m.Elif(was_next | was_restart): m.d.comb += self.mem_addr.eq(incremented_addr) with m.Else(): m.d.comb += self.mem_addr.eq(last_mem_addr) # Decide data to be output last_data = Signal.like(self.data) m.d.sync += last_data.eq(self.data) with m.If(was_restart | was_next): m.d.comb += self.data.eq(self.mem_data) with m.Else(): m.d.comb += self.data.eq(last_data)
def __init__(self): # Inputs self.year = Signal(range(1, 10000)) self.month = Signal(range(1, 13)) self.day = Signal(range(1, 32)) # Outputs self.next_year = Signal(range(1, 10001)) self.next_month = Signal.like(self.month) self.next_day = Signal.like(self.day) self.invalid = Signal()
def _elab_write(self, m, dps, r_full): w_curr_buf = Signal() # Address within current buffer w_addr = Signal.like(self.input_depth) # Connect to memory write port for n, dp in enumerate(dps): m.d.comb += [ dp.w_addr.eq(Cat(w_addr[2:], w_curr_buf)), dp.w_data.eq(self.w_data), dp.w_en.eq(self.w_en & self.w_ready & (n == w_addr[:2])), ] # Ready to write current buffer if reading is not allowed m.d.comb += self.w_ready.eq(~r_full[w_curr_buf]) # Write address increment with m.If(self.w_en & self.w_ready): with m.If(w_addr == self.input_depth - 1): # at end of buffer - mark buffer ready for reading and go to # next m.d.sync += [ w_addr.eq(0), r_full[w_curr_buf].eq(1), w_curr_buf.eq(~w_curr_buf), ] with m.Else(): m.d.sync += w_addr.eq(w_addr + 1) with m.If(self.restart): m.d.sync += [ w_addr.eq(0), w_curr_buf.eq(0), ]
def _elab_read(self, m, dps, r_full): r_curr_buf = Signal() # Address within current buffer r_addr = Signal.like(self.input_depth) # Create and connect sequential memory readers smrs = [ SequentialMemoryReader( max_depth=self.max_depth // 2, width=32) for _ in range(4)] for (n, dp, smr) in zip(range(4), dps, smrs): m.submodules[f"smr_{n}"] = smr m.d.comb += [ dp.r_addr.eq(Cat(smr.mem_addr, r_curr_buf)), smr.mem_data.eq(dp.r_data), smr.limit.eq((self.input_depth + 3 - n) >> 2), ] smr_nexts = Array(smr.next for smr in smrs) # Ready if current buffer is full full = Signal() m.d.comb += full.eq(r_full[r_curr_buf]) last_full = Signal() m.d.sync += last_full.eq(full) with m.If(full & ~last_full): m.d.sync += self.r_ready.eq(1) m.d.comb += [smr.restart.eq(1) for smr in smrs] # Increment address with m.If(self.r_next & self.r_ready): with m.If(r_addr == self.input_depth - 1): m.d.sync += r_addr.eq(0) with m.Else(): m.d.sync += r_addr.eq(r_addr + 1) m.d.comb += smr_nexts[r_addr[:2]].eq(1) # Get data smr_datas = Array(smr.data for smr in smrs) m.d.comb += self.r_data.eq(smr_datas[r_addr[:2]]) # On finished (overrides r_next) with m.If(self.r_finished): m.d.sync += [ r_addr.eq(0), r_full[r_curr_buf].eq(0), last_full.eq(0), self.r_ready.eq(0), r_curr_buf.eq(~r_curr_buf), ] # On restart, reset addresses and Sequential Memory readers, go to not # ready with m.If(self.restart): m.d.sync += [ r_addr.eq(0), last_full.eq(0), self.r_ready.eq(0), r_curr_buf.eq(0), ]
def __init__(self, width, depth): super().__init__() self.w_en = Signal() self.w_addr = Signal(range(depth)) self.w_data = Signal(width) self.restart = Signal() self.count = Signal.like(self.w_addr)
def _generate_wide_incrementer(m, platform, adder_input): """ Attempts to create an optimal wide-incrementer for counters. Yosys on certain platforms (ice40 UltraPlus) doesn't currently use hardware resources effectively for wide adders. We'll manually instantiate the relevant resources to get rid of an 18-bit carry chain; avoiding a long critical path. Parameters: platform -- The platform we're working with. adder_input -- The input to our incrementer. """ # If this isn't an iCE40 UltraPlus, let Yosys do its thing. if (not platform) or not platform.device.startswith('iCE40UP'): return adder_input + 1 # Otherwise, we'll create a DSP adder itself. output = Signal.like(adder_input) m.submodules += Instance('SB_MAC16', # Hook up our inputs and outputs. # A = upper bits of input; B = lower bits of input i_A = adder_input[16:], i_B = adder_input[0:16], o_O = output, p_TOPADDSUB_UPPERINPUT =0b1, # Use as a normal adder p_TOPADDSUB_CARRYSELECT=0b11, # Connect our top and bottom adders together. p_BOTADDSUB_UPPERINPUT =0b1, # Use as a normal adder. p_BOTADDSUB_CARRYSELECT=0b01 # Always increment. ) return output
def capture(self, m: Core, core: Core, past: int): comb = m.d.comb if past > 0: prefix = f"past{past}" else: prefix = "now" self.r = RegisterFile(core.xlen, prefix=prefix) for i in range(self.r.main_gpr_count()): comb += self.r[i].eq(Past(core.register_file.r[i], past)) comb += self.r.pc.eq(Past(core.pc, past)) # TODO: move to additional structure self.itype = IType(prefix=f"{prefix}_i") self.itype.elaborate(comb, Past(core.current_instruction, past)) self.jtype = JType(prefix=f"{prefix}_j") self.jtype.elaborate(comb, Past(core.current_instruction, past)) self.utype = UType(prefix=f"{prefix}_u") self.utype.elaborate(comb, Past(core.current_instruction, past)) self.btype = BType(prefix=f"{prefix}_b") self.btype.elaborate(comb, Past(core.current_instruction, past)) # TODO: membus self.input_ready = Signal.like(core.mem2core.ready, name=f"{prefix}_input_ready") self.input_data = Array([ Signal(core.xlen, name=f"{prefix}_input_{i}") for i in range(core.look_ahead) ]) self.cycle = Signal.like(core.cycle, name=f"{prefix}_cycle") comb += self.cycle.eq(Past(core.cycle, past)) # TODO: move to structure self.mem2core_addr = Signal.like(core.mem2core.addr, name=f"{prefix}_mem2core_addr") self.mem2core_en = Signal.like(core.mem2core.en, name=f"{prefix}_mem2core_en") self.mem2core_seq = Signal.like(core.mem2core.seq, name=f"{prefix}_mem2core_seq") comb += self.mem2core_addr.eq(Past(core.mem2core.addr, past)) comb += self.mem2core_en.eq(Past(core.mem2core.en, past)) comb += self.mem2core_seq.eq(Past(core.mem2core.seq, past)) comb += self.input_ready.eq(Past(core.mem2core.ready, past)) comb += self.input_data[0].eq(Past(core.mem2core.value, past))
def __init__(self): int_width = 12 frac_width = 4 width = int_width + frac_width self.width = width self.one = 1 << frac_width # Input line coordinates self.i_x0 = Signal(width) self.i_y0 = Signal(width) self.i_x1 = Signal(width) self.i_y1 = Signal(width) # Input colours self.i_r0 = Signal(8) self.i_g0 = Signal(8) self.i_b0 = Signal(8) self.i_r1 = Signal(8) self.i_g1 = Signal(8) self.i_b1 = Signal(8) self.i_start = Signal() # Start processing line self.i_next = Signal() # Advance to next pixel # Output pixel coordinates self.o_x = Signal(int_width) self.o_y = Signal(int_width) # Output pixel colour self.o_r = Signal(8) self.o_g = Signal(8) self.o_b = Signal(8) self.o_valid = Signal() # Output pixel coordinates are valid self.o_last = Signal() # Last pixel of the line self.r_steep = Signal() # True if line is transposed due to being steep (dy > dx) # Internal line coordinates; may be transposed due to line quadrant. self.r_x0 = Signal.like(self.i_x0) self.r_y0 = Signal.like(self.i_y0) self.r_x1 = Signal.like(self.i_x1) self.r_y1 = Signal.like(self.i_y1) # Internal pixel colours self.r_red = Signal(self.width) self.r_green = Signal(self.width) self.r_blue = Signal(self.width) # Internal pixel colour dividers self.r_rdiv = ColourDivider() self.r_gdiv = ColourDivider() self.r_bdiv = ColourDivider() # Absolute change in X and Y. self.r_dx = Signal.like(self.i_x0) self.r_dy = Signal.like(self.i_y0) self.r_error = Signal.like(self.i_y0) self.r_y_inc = Signal((width, True))
def add_register(self, address, *, value_signal=None, size=None, name=None, read_strobe=None, write_strobe=None, reset=0): """ Adds a standard, memory-backed register. Parameters: address -- the register's address, as a big-endian integer value_signal -- the signal that will store the register's value; if omitted a storage register will be created automatically size -- if value_signal isn't provided, this sets the size of the created register reset -- if value_signal isn't provided, this sets the reset value of the created register read_strobe -- a Signal to be asserted when the register is read; ignored if not provided write_strobe -- a Signal to be asserted when the register is written; ignored if not provided Returns: value_signal -- a signal that stores the register's value; which may be the value_signal arg, or may be a signal created during execution """ self._ensure_register_is_unused(address) # Generate a name for the register, if we don't already have one. name = name if name else "register_{:x}".format(address) # Generate a backing store for the register, if we don't already have one. if value_signal is None: size = self.register_size if (size is None) else size value_signal = Signal(size, name=name, reset=reset) # If we don't have a write strobe signal, create an internal one. if write_strobe is None: write_strobe = Signal(name=name + "_write_strobe") # Create our register-value-input and our write strobe. write_value = Signal.like(value_signal, name=name + "_write_value") # Create a generator for a the fragments that will manage the register's memory. def _elaborate_memory_register(m): with m.If(write_strobe): m.d.sync += value_signal.eq(write_value) # Add the register to our collection. self.registers[address] = { 'read': value_signal, 'write_signal': write_value, 'write_strobe': write_strobe, 'read_strobe': read_strobe, 'elaborate': _elaborate_memory_register, } return value_signal
def elab(self, m): count = Signal.like(self.max) last_count = self.max - 1 next_count = Mux(count == last_count, 0, count + 1) with m.If(self.en): m.d.sync += count.eq(next_count) m.d.comb += self.done.eq(count == last_count) with m.If(self.restart): m.d.sync += count.eq(0)
def delay_by(signal, cycles, m): delayed_signal = signal for i in range(cycles): last = delayed_signal if hasattr(signal, 'name'): name = "{}_delayed_{}".format(signal.name, i + 1) else: name = "expression_delayed_{}".format(i + 1) delayed_signal = Signal.like(signal, name=name) m.d.sync += delayed_signal.eq(last) return delayed_signal
def elaborate(self, platform): m = Module() ac = Signal(self.width + 1) ac_next = Signal.like(ac) temp = Signal.like(ac) q1 = Signal(self.width) q1_next = Signal.like(q1) i = Signal(range(self.width)) # combinatorial with m.If(ac >= self.y): m.d.comb += [ temp.eq(ac - self.y), Cat(q1_next, ac_next).eq(Cat(1, q1, temp[0:self.width - 1])) ] with m.Else(): m.d.comb += [Cat(q1_next, ac_next).eq(Cat(q1, ac) << 1)] # synchronized with m.If(self.start): m.d.sync += [self.valid.eq(0), i.eq(0)] with m.If(self.y == 0): m.d.sync += [self.busy.eq(0), self.dbz.eq(1)] with m.Else(): m.d.sync += [ self.busy.eq(1), self.dbz.eq(0), Cat(q1, ac).eq(Cat(Const(0, 1), self.x, Const(0, self.width))) ] with m.Elif(self.busy): with m.If(i == self.width - 1): m.d.sync += [ self.busy.eq(0), self.valid.eq(1), i.eq(0), self.q.eq(q1_next), self.r.eq(ac_next >> 1) ] with m.Else(): m.d.sync += [i.eq(i + 1), ac.eq(ac_next), q1.eq(q1_next)] return m
def elab(self, m): # Current r_addr is the address being presented to memory this cycle # By default current address is last address, but this can be # modied by the reatart or next signals last_addr = Signal.like(self.r_addr) m.d.sync += last_addr.eq(self.r_addr) m.d.comb += self.r_addr.eq(last_addr) # Respond to inputs with m.If(self.restart): m.d.comb += self.r_addr.eq(0) with m.Elif(self.next): m.d.comb += self.r_addr.eq(Mux(last_addr >= self.limit - 1, 0, last_addr + 1))
def __init__(self, *, endpoint_number, max_packet_size): self._endpoint_number = endpoint_number self._max_packet_size = max_packet_size # # I/O Port # self.interface = EndpointInterface() self.bytes_in_frame = Signal(range(0, self._MAX_FRAME_DATA + 1)) self.address = Signal(range(0, self._MAX_FRAME_DATA)) self.next_address = Signal.like(self.address) self.value = Signal(8)
def elab(self, m): areg = Signal.like(self.a) breg = Signal.like(self.b) ab = Signal(signed(64)) overflow = Signal() # for some reason negative nudge is not used nudge = 1 << 30 # cycle 0, register a and b m.d.sync += [ areg.eq(self.a), breg.eq(self.b), ] # cycle 1, decide if this is an overflow and multiply m.d.sync += [ overflow.eq((areg == INT32_MIN) & (breg == INT32_MIN)), ab.eq(areg * breg), ] # cycle 2, apply nudge determine result m.d.sync += [ self.result.eq(Mux(overflow, INT32_MAX, (ab + nudge)[31:])), ]
def elaborate(self, platform): m = Module() rdata = Signal.like(self.iport.dat_r) # handle transaction logic with m.If(self.iport.cyc): with m.If(self.iport.ack | self.iport.err | ~self.f_valid): m.d.sync += [ self.iport.cyc.eq(0), self.iport.stb.eq(0), rdata.eq(self.iport.dat_r) ] with m.Elif(self.a_valid & ~self.a_stall): # start transaction m.d.sync += [ self.iport.addr.eq(self.a_pc), self.iport.cyc.eq(1), self.iport.stb.eq(1) ] m.d.comb += [ self.iport.dat_w.eq(0), self.iport.sel.eq(0), self.iport.we.eq(0), self.iport.cti.eq(CycleType.CLASSIC), self.iport.bte.eq(0) ] # in case of error, make the instruction a NOP with m.If(self.f_bus_error): m.d.comb += self.f_instruction.eq(0x00000013) # NOP with m.Else(): m.d.comb += self.f_instruction.eq(rdata) # excepcion with m.If(self.iport.cyc & self.iport.err): m.d.sync += [ self.f_bus_error.eq(1), self.f_badaddr.eq(self.iport.addr) ] with m.Elif( ~self.f_stall ): # in case of error, but the pipe is stalled, do not lose the error m.d.sync += self.f_bus_error.eq(0) # busy flag m.d.comb += self.f_busy.eq(self.iport.cyc) return m
def __init__(self): # # I/O port. # self.tx_data = Signal(8) self.tx_valid = Signal() self.tx_ready = Signal() self.op_mode = Signal(2) self.bus_available = Signal() self.ulpi_data_out = Signal.like(self.tx_data) self.ulpi_data_en = Signal() self.ulpi_nxt = Signal() self.ulpi_stp = Signal()
def elaborate(self, _: Platform) -> Module: """Implements the logic for the NextDay module.""" m = Module() is_leap_year = Signal() max_day = Signal.like(self.day) m.d.comb += is_leap_year.eq(0) # We can override this below! with m.If((self.year % 4) == 0): m.d.comb += is_leap_year.eq(1) with m.If((self.year % 100) == 0): m.d.comb += is_leap_year.eq(0) with m.If((self.year % 400) == 0): m.d.comb += is_leap_year.eq(1) with m.Switch(self.month): with m.Case(1, 3, 5, 7, 8, 10, 12): m.d.comb += max_day.eq(31) with m.Case(2): m.d.comb += max_day.eq(28 + is_leap_year) with m.Default(): m.d.comb += max_day.eq(30) m.d.comb += self.next_year.eq(self.year) m.d.comb += self.next_month.eq(self.month) m.d.comb += self.next_day.eq(self.day + 1) with m.If(self.day == max_day): m.d.comb += self.next_day.eq(1) m.d.comb += self.next_month.eq(self.month + 1) with m.If(self.month == 12): m.d.comb += self.next_month.eq(1) m.d.comb += self.next_year.eq(self.year + 1) m.d.comb += self.invalid.eq(0) with m.If((self.day < 1) | (self.day > max_day) | (self.month < 1) | (self.month > 12) | (self.year < 1) | (self.year > 9999)): m.d.comb += self.invalid.eq(1) with m.If(self.invalid): m.d.comb += self.next_year.eq(0) m.d.comb += self.next_month.eq(0) m.d.comb += self.next_day.eq(0) return m
def delay(m, signal, interval, *, out=None): """ Creates a delayed copy of a given I/O signal. Currently only works at the FPGA's I/O boundary, and only on ECP5s. Parameters: signal -- The signal to be delayed. Must be either an I/O signal connected directly to a platform resource. delay -- Delay, in arbitrary units. These units will vary from unit to unit, but seem to be around 300ps on most ECP5 FPGAs. On ECP5s, maxes out at 127. out -- The signal to received the delayed signal; or None ot have a signal created for you. Returns: delayed -- The delayed signal. Will be equivalent to 'out' if provided; or a new signal otherwise. """ # If we're not being passed our output signal, create one. if out is None: out = Signal.like(signal) # If we have more than one signal, call this function on each # of the subsignals. if len(signal) > 1: # If we have a vector of signals, but a integer delay, # convert that integer to a vector of same-valued delays. if isinstance(interval, int): interval = [interval] * len(signal) return Cat( delay(m, s, d, out=o) for s, d, o in zip(signal, interval, out)) # # Base case: create a delayed version of the relevant signal. # m.submodules += Instance("DELAYG", i_A=signal, o_Z=out, p_DEL_VALUE=interval) return out
def __init__(self, command_size=8, word_size=32): self.command_size = command_size self.word_size = word_size # # I/O port. # # SPI self.spi = SPIBus() # Command I/O. self.command = Signal(self.command_size) self.command_ready = Signal() # Data I/O self.word_received = Signal(self.word_size) self.word_to_send = Signal.like(self.word_received) self.word_complete = Signal()
def elaborate(self, platform): m = Module() spi = self.spi sample_edge = falling_edge_detector(m, spi.sck) # Bit counter: counts the number of bits received. max_bit_count = max(self.word_size, self.command_size) bit_count = Signal(range(0, max_bit_count + 1)) # Shift registers for our command and data. current_command = Signal.like(self.command) current_word = Signal.like(self.word_received) # De-assert our control signals unless explicitly asserted. m.d.sync += [ self.command_ready.eq(0), self.word_complete.eq(0) ] with m.FSM(): # STALL: entered when we can't accept new bits -- either when # CS starts asserted, or when we've received more data than expected. with m.State("STALL"): # Wait for CS to clear. with m.If(~spi.cs): m.next = 'IDLE' # We ignore all data until chip select is asserted, as that data Isn't For Us (TM). # We'll spin and do nothing until the bus-master addresses us. with m.State('IDLE'): m.d.sync += bit_count.eq(0) with m.If(spi.cs): m.next = 'RECEIVE_COMMAND' # Once CS is low, we'll shift in our command. with m.State('RECEIVE_COMMAND'): # Continue shifting in data until we have a full command. with m.If(bit_count < self.command_size): with m.If(sample_edge): m.d.sync += [ bit_count .eq(bit_count + 1), current_command .eq(Cat(spi.sdi, current_command[:-1])) ] # ... and then pass that command out to our controller. with m.Else(): m.d.sync += [ bit_count .eq(0), self.command_ready .eq(1), self.command .eq(current_command) ] m.next = 'PROCESSING' # Give our controller a wait state to prepare any response they might want to... with m.State('PROCESSING'): m.next = 'LATCH_OUTPUT' # ... and then latch in the response to transmit. with m.State('LATCH_OUTPUT'): m.d.sync += current_word.eq(self.word_to_send) m.next = 'SHIFT_DATA' # Finally, exchange data. with m.State('SHIFT_DATA'): m.d.sync += spi.sdo.eq(current_word[-1]) # Continue shifting data until we have a full word. with m.If(bit_count < self.word_size): with m.If(sample_edge): m.d.sync += [ bit_count .eq(bit_count + 1), current_word .eq(Cat(spi.sdi, current_word[:-1])) ] # ... and then output that word on our bus. with m.Else(): m.d.sync += [ bit_count .eq(0), self.word_complete .eq(1), self.word_received .eq(current_word) ] # Stay in the stall state until CS is de-asserted. m.next = 'STALL' return m
def elaborate(self, platform): m = Module() MULTIFRAME_LENGTH = 24 FRAME_LENGTH = 193 m.d.sync += self.strobe_out.eq(self.strobe_in) m.d.comb += self.end_of_bit.eq(self.strobe_in) m.d.sync += self.start_of_bit.eq(self.end_of_bit) ############################################################### # Bit counter. count = Signal(range(FRAME_LENGTH)) count_next = Signal.like(count) with m.If(self.end_of_frame): m.d.comb += count_next.eq(0) with m.Else(): m.d.comb += count_next.eq(count + 1) with m.If(self.strobe_in): m.d.sync += count.eq(count_next) ############################################################### # Frame counter. end_of_frame_next = count_next == (FRAME_LENGTH - 1) frame_count = Signal(range(MULTIFRAME_LENGTH)) with m.If(self.strobe_in): m.d.sync += [ self.start_of_multiframe.eq(self.end_of_multiframe), self.end_of_multiframe.eq(0), ] with m.If(end_of_frame_next): with m.If(frame_count == (MULTIFRAME_LENGTH - 1)): m.d.sync += self.end_of_multiframe.eq(1), with m.If(self.end_of_frame): with m.If(self.end_of_multiframe): m.d.sync += frame_count.eq(0) with m.Else(): m.d.sync += frame_count.eq(frame_count + 1) ############################################################### # Event signals. with m.If(self.strobe_in): m.d.sync += [ self.start_of_frame.eq(count_next == 0), self.f.eq(count_next == 0), self.payload.eq(count_next != 0), self.end_of_frame.eq(end_of_frame_next), self.bit.eq(~(count[0:3])), self.start_of_slot.eq((self.end_of_slot ^ self.end_of_frame) | self.f), self.end_of_slot.eq(self.bit == 1), ] with m.If(self.end_of_frame): m.d.sync += self.slot.eq(0), with m.Else(): m.d.sync += self.slot.eq(count[3:]), return m
def elaborate(self, platform): m = Module() # Create our core, single-byte-wide endpoint, and attach it directly to our interface. m.submodules.stream_ep = stream_ep = USBStreamInEndpoint( endpoint_number=self._endpoint_number, max_packet_size=self._max_packet_size ) stream_ep.interface = self.interface # Create semantic aliases for byte-wise and word-wise streams; # so the code below reads more clearly. byte_stream = stream_ep.stream word_stream = self.stream # We'll put each word to be sent through an shift register # that shifts out words a byte at a time. data_shift = Signal.like(word_stream.payload) # Latched versions of our first and last signals. first_latched = Signal() last_latched = Signal() # Count how many bytes we have left to send. bytes_to_send = Signal(range(0, self._byte_width + 1)) # Always provide our inner transmitter with the least byte of our shift register. m.d.comb += byte_stream.payload.eq(data_shift[0:8]) with m.FSM(domain="usb"): # IDLE: transmitter is waiting for input with m.State("IDLE"): m.d.comb += word_stream.ready.eq(1) # Once we get a send request, fill in our shift register, and start shifting. with m.If(word_stream.valid): m.d.usb += [ data_shift .eq(word_stream.payload), first_latched .eq(word_stream.first), last_latched .eq(word_stream.last), bytes_to_send .eq(self._byte_width - 1), ] m.next = "TRANSMIT" # TRANSMIT: actively send each of the bytes of our word with m.State("TRANSMIT"): m.d.comb += byte_stream.valid.eq(1) # Once the byte-stream is accepting our input... with m.If(byte_stream.ready): is_first_byte = (bytes_to_send == self._byte_width - 1) is_last_byte = (bytes_to_send == 0) # Pass through our First and Last signals, but only on the first and # last bytes of our word, respectively. m.d.comb += [ byte_stream.first .eq(first_latched & is_first_byte), byte_stream.last .eq(last_latched & is_last_byte) ] # ... if we have bytes left to send, move to the next one. with m.If(bytes_to_send > 0): m.d.usb += [ bytes_to_send .eq(bytes_to_send - 1), data_shift .eq(data_shift[8:]), ] # Otherwise, complete the frame. with m.Else(): m.d.comb += word_stream.ready.eq(1) # If we still have data to send, move to the next byte... with m.If(self.stream.valid): m.d.usb += [ data_shift .eq(word_stream.payload), first_latched .eq(word_stream.first), last_latched .eq(word_stream.last), bytes_to_send .eq(self._byte_width - 1), ] # ... otherwise, move to our idle state. with m.Else(): m.next = "IDLE" return m
def elaborate(self, platform: Platform) -> Module: m = Module() snoop_addr = Record(self.pc_layout) snoop_valid = Signal() # ------------------------------------------------------------------------- # Performance counter # TODO: connect to CSR's performance counter with m.If(~self.s1_stall & self.s1_valid & self.s1_access): m.d.sync += self.access_cnt.eq(self.access_cnt + 1) with m.If(self.s2_valid & self.s2_miss & ~self.bus_valid & self.s2_access): m.d.sync += self.miss_cnt.eq(self.miss_cnt + 1) # ------------------------------------------------------------------------- way_layout = [('data', 32 * self.nwords), ('tag', self.s1_address.tag.shape()), ('valid', 1), ('sel_lru', 1), ('snoop_hit', 1)] if self.enable_write: way_layout.append(('sel_we', 1)) ways = Array( Record(way_layout, name='way_idx{}'.format(_way)) for _way in range(self.nways)) fill_cnt = Signal.like(self.s1_address.offset) # Check hit/miss way_hit = m.submodules.way_hit = Encoder(self.nways) for idx, way in enumerate(ways): m.d.comb += way_hit.i[idx].eq((way.tag == self.s2_address.tag) & way.valid) m.d.comb += self.s2_miss.eq(way_hit.n) if self.enable_write: # Asumiendo que hay un HIT, indicar que la vía que dió hit es en la cual se va a escribir m.d.comb += ways[way_hit.o].sel_we.eq(self.s2_we & self.s2_valid) # set the LRU if self.nways == 1: # One way: LRU is useless lru = Const(0) # self.nlines else: # LRU es un vector de N bits, cada uno indicado el set a reemplazar # como NWAY es máximo 2, cada LRU es de un bit lru = Signal(self.nlines) _lru = lru.bit_select(self.s2_address.line, 1) write_ended = self.bus_valid & self.bus_ack & self.bus_last # err ^ ack = = 1 access_hit = ~self.s2_miss & self.s2_valid & (way_hit.o == _lru) with m.If(write_ended | access_hit): m.d.sync += _lru.eq(~_lru) # read data from the cache m.d.comb += self.s2_rdata.eq(ways[way_hit.o].data.word_select( self.s2_address.offset, 32)) # Internal Snoop snoop_use_cache = Signal() snoop_tag_match = Signal() snoop_line_match = Signal() snoop_cancel_refill = Signal() if not self.enable_write: bits_range = log2_int(self.end_addr - self.start_addr, need_pow2=False) m.d.comb += [ snoop_addr.eq(self.dcache_snoop.addr), # aux snoop_valid.eq(self.dcache_snoop.we & self.dcache_snoop.valid & self.dcache_snoop.ack), snoop_use_cache.eq(snoop_addr[bits_range:] == ( self.start_addr >> bits_range)), snoop_tag_match.eq(snoop_addr.tag == self.s2_address.tag), snoop_line_match.eq(snoop_addr.line == self.s2_address.line), snoop_cancel_refill.eq(snoop_use_cache & snoop_valid & snoop_line_match & snoop_tag_match), ] else: m.d.comb += snoop_cancel_refill.eq(0) with m.FSM(): with m.State('READ'): with m.If(self.s2_re & self.s2_miss & self.s2_valid): m.d.sync += [ self.bus_addr.eq(self.s2_address), self.bus_valid.eq(1), fill_cnt.eq(self.s2_address.offset - 1) ] m.next = 'REFILL' with m.State('REFILL'): m.d.comb += self.bus_last.eq(fill_cnt == self.bus_addr.offset) with m.If(self.bus_ack): m.d.sync += self.bus_addr.offset.eq(self.bus_addr.offset + 1) with m.If(self.bus_ack & self.bus_last | self.bus_err): m.d.sync += self.bus_valid.eq(0) with m.If(~self.bus_valid | self.s1_flush | snoop_cancel_refill): m.next = 'READ' m.d.sync += self.bus_valid.eq(0) # mark the way to use (replace) m.d.comb += ways[lru.bit_select(self.s2_address.line, 1)].sel_lru.eq(self.bus_valid) # generate for N ways for way in ways: # create the memory structures for valid, tag and data. valid = Signal(self.nlines) # Valid bits tag_m = Memory(width=len(way.tag), depth=self.nlines) # tag memory tag_rp = tag_m.read_port() snoop_rp = tag_m.read_port() tag_wp = tag_m.write_port() m.submodules += tag_rp, tag_wp, snoop_rp data_m = Memory(width=len(way.data), depth=self.nlines) # data memory data_rp = data_m.read_port() data_wp = data_m.write_port( granularity=32 ) # implica que solo puedo escribir palabras de 32 bits. m.submodules += data_rp, data_wp # handle valid with m.If(self.s1_flush & self.s1_valid): # flush m.d.sync += valid.eq(0) with m.Elif(way.sel_lru & self.bus_last & self.bus_ack): # refill ok m.d.sync += valid.bit_select(self.bus_addr.line, 1).eq(1) with m.Elif(way.sel_lru & self.bus_err): # refill error m.d.sync += valid.bit_select(self.bus_addr.line, 1).eq(0) with m.Elif(self.s2_evict & self.s2_valid & (way.tag == self.s2_address.tag)): # evict m.d.sync += valid.bit_select(self.s2_address.line, 1).eq(0) # assignments m.d.comb += [ tag_rp.addr.eq( Mux(self.s1_stall, self.s2_address.line, self.s1_address.line)), tag_wp.addr.eq(self.bus_addr.line), tag_wp.data.eq(self.bus_addr.tag), tag_wp.en.eq(way.sel_lru & self.bus_ack & self.bus_last), data_rp.addr.eq( Mux(self.s1_stall, self.s2_address.line, self.s1_address.line)), way.data.eq(data_rp.data), way.tag.eq(tag_rp.data), way.valid.eq(valid.bit_select(self.s2_address.line, 1)) ] # update cache: CPU or Refill # El puerto de escritura se multiplexa debido a que la memoria solo puede tener un # puerto de escritura. if self.enable_write: update_addr = Signal(len(data_wp.addr)) update_data = Signal(len(data_wp.data)) update_we = Signal(len(data_wp.en)) aux_wdata = Signal(32) with m.If(self.bus_valid): m.d.comb += [ update_addr.eq(self.bus_addr.line), update_data.eq(Repl(self.bus_data, self.nwords)), update_we.bit_select(self.bus_addr.offset, 1).eq(way.sel_lru & self.bus_ack), ] with m.Else(): m.d.comb += [ update_addr.eq(self.s2_address.line), update_data.eq(Repl(aux_wdata, self.nwords)), update_we.bit_select(self.s2_address.offset, 1).eq(way.sel_we & ~self.s2_miss) ] m.d.comb += [ # Aux data: no tengo granularidad de byte en el puerto de escritura. Así que para el # caso en el cual el CPU tiene que escribir, hay que construir el dato (wrord) a reemplazar aux_wdata.eq( Cat( Mux(self.s2_sel[0], self.s2_wdata.word_select(0, 8), self.s2_rdata.word_select(0, 8)), Mux(self.s2_sel[1], self.s2_wdata.word_select(1, 8), self.s2_rdata.word_select(1, 8)), Mux(self.s2_sel[2], self.s2_wdata.word_select(2, 8), self.s2_rdata.word_select(2, 8)), Mux(self.s2_sel[3], self.s2_wdata.word_select(3, 8), self.s2_rdata.word_select(3, 8)))), # data_wp.addr.eq(update_addr), data_wp.data.eq(update_data), data_wp.en.eq(update_we), ] else: m.d.comb += [ data_wp.addr.eq(self.bus_addr.line), data_wp.data.eq(Repl(self.bus_data, self.nwords)), data_wp.en.bit_select(self.bus_addr.offset, 1).eq(way.sel_lru & self.bus_ack), ] # -------------------------------------------------------------- # intenal snoop # for FENCE.i instruction _match_snoop = Signal() m.d.comb += [ snoop_rp.addr.eq(snoop_addr.line), # read tag memory _match_snoop.eq(snoop_rp.data == snoop_addr.tag), way.snoop_hit.eq(snoop_use_cache & snoop_valid & _match_snoop & valid.bit_select(snoop_addr.line, 1)), ] # check is the snoop match a write from this core with m.If(way.snoop_hit): m.d.sync += valid.bit_select(snoop_addr.line, 1).eq(0) # -------------------------------------------------------------- return m
] with m.Else(): m.d.sync += [ counter.eq(counter - 1), ] return m if __name__ == '__main__': duration = 3 design = OneShot(duration) # work aroun nMigen bug #280 m = Module() m.submodules.design = design i_trg = Signal.like(design.i_trg) m.d.comb += [ design.i_trg.eq(i_trg), ] #280 with Main(design).sim as sim: with Main(m).sim as sim: @sim.sync_process def test_proc(): def set(value): #280 yield design.i_trg.eq(value) yield i_trg.eq(value) def chk(expected): actual = yield design.o_pulse assert actual == expected, f'wanted {expected}, got {actual}'
def elaborate(self, platform): m = Module() # Shortcuts. tx = self.interface.tx tokenizer = self.interface.tokenizer # Grab a copy of the relevant signal that's in our USB domain; synchronizing if we need to. if self._signal_domain == "usb": target_signal = self.signal else: target_signal = synchronize(m, self.signal, o_domain="usb") # Store a latched version of our signal, captured before we start a transmission. latched_signal = Signal.like(self.signal) # Grab an byte-indexable reference into our signal. bytes_in_signal = (self._width + 7) // 8 signal_bytes = Array(latched_signal[n * 8:n * 8 + 8] for n in range(bytes_in_signal)) # Store how many bytes we've transmitted. bytes_transmitted = Signal(range(0, bytes_in_signal + 1)) # # Data transmission logic. # # If this signal is big endian, send them in reading order; otherwise, index our multiplexer in reverse. # Note that our signal is captured little endian by default, due the way we use Array() above. If we want # big endian; then we'll flip it. if self._endianness == "little": index_to_transmit = bytes_transmitted else: index_to_transmit = bytes_in_signal - bytes_transmitted - 1 # Always transmit the part of the latched signal byte that corresponds to our m.d.comb += tx.payload.eq(signal_bytes[index_to_transmit]) # # Core control FSM. # endpoint_number_matches = (tokenizer.endpoint == self._endpoint_number) targeting_endpoint = endpoint_number_matches & tokenizer.is_in packet_requested = targeting_endpoint & tokenizer.ready_for_response with m.FSM(domain="usb"): # IDLE -- we've not yet gotten an token requesting data. Wait for one. with m.State('IDLE'): # Once we're ready to send a response... with m.If(packet_requested): m.d.usb += [ # ... clear our transmit counter ... bytes_transmitted.eq(0), # ... latch in our response... latched_signal.eq(self.signal), ] # ... and start transmitting it. m.next = "TRANSMIT_RESPONSE" # TRANSMIT_RESPONSE -- we're now ready to send our latched response to the host. with m.State("TRANSMIT_RESPONSE"): is_last_byte = bytes_transmitted + 1 == bytes_in_signal # While we're transmitting, our Tx data is valid. m.d.comb += [ tx.valid.eq(1), tx.first.eq(bytes_transmitted == 0), tx.last.eq(is_last_byte) ] # Each time we receive a byte, move on to the next one. with m.If(tx.ready): m.d.usb += bytes_transmitted.eq(bytes_transmitted + 1) # If this is the last byte to be transmitted, move to waiting for an ACK. with m.If(is_last_byte): m.next = "WAIT_FOR_ACK" # WAIT_FOR_ACK -- we've now transmitted our full packet; we need to wait for the host to ACK it with m.State("WAIT_FOR_ACK"): # If the host does ACK, we're done! Move back to our idle state. with m.If(self.interface.handshakes_in.ack): m.d.comb += self.status_read_complete.eq(1) m.d.usb += self.interface.tx_pid_toggle[0].eq( ~self.interface.tx_pid_toggle[0]) m.next = "IDLE" # If the host starts a new packet without ACK'ing, we'll need to retransmit. # Wait for a new IN token. with m.If(self.interface.tokenizer.new_token): m.next = "RETRANSMIT" # RETRANSMIT -- the host failed to ACK the data we've most recently sent. # Wait here for the host to request the data again. with m.State("RETRANSMIT"): # Once the host does request the data again... with m.If(packet_requested): # ... retransmit it, starting from the beginning. m.d.usb += bytes_transmitted.eq(0), m.next = "TRANSMIT_RESPONSE" return m