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
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
def elaborate(self, platform): m = Module() # # Transciever state. # # Handle our PID-sequence reset. # Note that we store the _inverse_ of our data PID, as we'll toggle our DATA PID # before sending. with m.If(self.reset_sequence): m.d.usb += self.data_pid.eq(~self.start_with_data1) # # Transmit buffer. # # Our USB connection imposed a few requirements on our stream: # 1) we must be able to transmit packets at a full rate; i.e. # must be asserted from the start to the end of our transfer; and # 2) we must be able to re-transmit data if a given packet is not ACK'd. # # Accordingly, we'll buffer a full USB packet of data, and then transmit # it once either a) our buffer is full, or 2) the transfer ends (last=1). # # This implementation is double buffered; so a buffer fill can be pipelined # with a transmit. # # We'll create two buffers; so we can fill one as we empty the other. # Since each buffer will be used for every other transaction, and our PID toggle flips every other transcation, # we'll identify which buffer we're targeting by the current PID toggle. buffer = Array( Memory(width=8, depth=self._max_packet_size, name=f"transmit_buffer_{i}") for i in range(2)) buffer_write_ports = Array(buffer[i].write_port(domain="usb") for i in range(2)) buffer_read_ports = Array(buffer[i].read_port(domain="usb") for i in range(2)) m.submodules.read_port_0, m.submodules.read_port_1 = buffer_read_ports m.submodules.write_port_0, m.submodules.write_port_1 = buffer_write_ports # Create values equivalent to the buffer numbers for our read and write buffer; which switch # whenever we swap our two buffers. write_buffer_number = self.data_pid[0] read_buffer_number = ~self.data_pid[0] # Create a shorthand that refers to the buffer to be filled; and the buffer to send from. # We'll call these the Read and Write buffers. buffer_write = buffer_write_ports[write_buffer_number] buffer_read = buffer_read_ports[read_buffer_number] # Buffer state tracking: # - Our ``fill_count`` keeps track of how much data is stored in a given buffer. # - Our ``stream_ended`` bit keeps track of whether the stream ended while filling up # the given buffer. This indicates that the buffer cannot be filled further; and, when # ``generate_zlps`` is enabled, is used to determine if the given buffer should end in # a short packet; which determines whether ZLPs are emitted. buffer_fill_count = Array( Signal(range(0, self._max_packet_size + 1)) for _ in range(2)) buffer_stream_ended = Array( Signal(name=f"stream_ended_in_buffer{i}") for i in range(2)) # Create shortcuts to active fill_count / stream_ended signals for the buffer being written. write_fill_count = buffer_fill_count[write_buffer_number] write_stream_ended = buffer_stream_ended[write_buffer_number] # Create shortcuts to the fill_count / stream_ended signals for the packet being sent. read_fill_count = buffer_fill_count[read_buffer_number] read_stream_ended = buffer_stream_ended[read_buffer_number] # Keep track of our current send position; which determines where we are in the packet. send_position = Signal(range(0, self._max_packet_size + 1)) # Shortcut names. in_stream = self.transfer_stream out_stream = self.packet_stream # Use our memory's two ports to capture data from our transfer stream; and two emit packets # into our packet stream. Since we'll never receive to anywhere else, or transmit to anywhere else, # we can just unconditionally connect these. m.d.comb += [ # We'll only ever -write- data from our input stream... buffer_write_ports[0].data.eq(in_stream.payload), buffer_write_ports[0].addr.eq(write_fill_count), buffer_write_ports[1].data.eq(in_stream.payload), buffer_write_ports[1].addr.eq(write_fill_count), # ... and we'll only ever -send- data from the Read buffer. buffer_read.addr.eq(send_position), out_stream.payload.eq(buffer_read.data), # We're ready to receive data iff we have space in the buffer we're currently filling. in_stream.ready.eq((write_fill_count != self._max_packet_size) & ~write_stream_ended), buffer_write.en.eq(in_stream.valid & in_stream.ready) ] # Increment our fill count whenever we accept new data. with m.If(buffer_write.en): m.d.usb += write_fill_count.eq(write_fill_count + 1) # If the stream ends while we're adding data to the buffer, mark this as an ended stream. with m.If(in_stream.last & buffer_write.en): m.d.usb += write_stream_ended.eq(1) # Shortcut for when we need to deal with an in token. # Pulses high an interpacket delay after receiving an IN token. in_token_received = self.active & self.tokenizer.is_in & self.tokenizer.ready_for_response with m.FSM(domain='usb'): # WAIT_FOR_DATA -- We don't yet have a full packet to transmit, so we'll capture data # to fill the our buffer. At full throughput, this state will never be reached after # the initial post-reset fill. with m.State("WAIT_FOR_DATA"): # We can't yet send data; so NAK any packet requests. m.d.comb += self.handshakes_out.nak.eq(in_token_received) # If we have valid data that will end our packet, we're no longer waiting for data. # We'll now wait for the host to request data from us. packet_complete = (write_fill_count + 1 == self._max_packet_size) will_end_packet = packet_complete | in_stream.last with m.If(in_stream.valid & will_end_packet): # If we've just finished a packet, we now have data we can send! with m.If(packet_complete | in_stream.last): m.next = "WAIT_TO_SEND" m.d.usb += [ # We're now ready to take the data we've captured and _transmit_ it. # We'll swap our read and write buffers, and toggle our data PID. self.data_pid[0].eq(~self.data_pid[0]), # Mark our current stream as no longer having ended. read_stream_ended.eq(0) ] # WAIT_TO_SEND -- we now have at least a buffer full of data to send; we'll # need to wait for an IN token to send it. with m.State("WAIT_TO_SEND"): m.d.usb += send_position.eq(0), # Once we get an IN token, move to sending a packet. with m.If(in_token_received): # If we have a packet to send, send it. with m.If(read_fill_count): m.next = "SEND_PACKET" m.d.usb += out_stream.first.eq(1) # Otherwise, we entered a transmit path without any data in the buffer. with m.Else(): m.d.comb += [ # Send a ZLP... out_stream.valid.eq(1), out_stream.last.eq(1), ] # ... and clear the need to follow up with one, since we've just sent a short packet. m.d.usb += read_stream_ended.eq(0) m.next = "WAIT_FOR_ACK" with m.State("SEND_PACKET"): last_packet = (send_position + 1 == read_fill_count) m.d.comb += [ # We're always going to be sending valid data, since data is always # available from our memory. out_stream.valid.eq(1), # Let our transmitter know when we've reached our last packet. out_stream.last.eq(last_packet) ] # Once our transmitter accepts our data... with m.If(out_stream.ready): m.d.usb += [ # ... move to the next byte in our packet ... send_position.eq(send_position + 1), # ... and mark our packet as no longer the first. out_stream.first.eq(0) ] # Move our memory pointer to its next position. m.d.comb += buffer_read.addr.eq(send_position + 1), # If we've just sent our last packet, we're now ready to wait for a # response from our host. with m.If(last_packet): m.next = 'WAIT_FOR_ACK' # WAIT_FOR_ACK -- We've just sent a packet; but don't know if the host has # received it correctly. We'll wait to see if the host ACKs. with m.State("WAIT_FOR_ACK"): # If the host does ACK... with m.If(self.handshakes_in.ack): # ... clear the data we've sent from our buffer. m.d.usb += read_fill_count.eq(0) # Figure out if we'll need to follow up with a ZLP. If we have ZLP generation enabled, # we'll make sure we end on a short packet. If this is max-packet-size packet _and_ our # transfer ended with this packet; we'll need to inject a ZLP. follow_up_with_zlp = \ self.generate_zlps & (read_fill_count == self._max_packet_size) & read_stream_ended # If we're following up with a ZLP, move back to our "wait to send" state. # Since we've now cleared our fill count; this next go-around will emit a ZLP. with m.If(follow_up_with_zlp): m.next = "WAIT_TO_SEND" # Otherwise, there's a possibility we already have a packet-worth of data waiting # for us in our "write buffer", which we've been filling in the background. # If this is the case, we'll flip which buffer we're working with, toggle our data pid, # and then ready ourselves for transmit. packet_completing = in_stream.valid & ( write_fill_count + 1 == self._max_packet_size) with m.Elif(~in_stream.ready | packet_completing): m.next = "WAIT_TO_SEND" m.d.usb += [ self.data_pid[0].eq(~self.data_pid[0]), read_stream_ended.eq(0) ] # If neither of the above conditions are true; we now don't have enough data to send. # We'll wait for enough data to transmit. with m.Else(): m.next = "WAIT_FOR_DATA" # If the host starts a new packet without ACK'ing, we'll need to retransmit. # We'll move back to our "wait for token" state without clearing our buffer. with m.If(self.tokenizer.new_token): m.next = 'WAIT_TO_SEND' return m
def elaborate(self, platform): phase = Signal(self.phase_depth) note = Signal.like(self.note_in.i_data.note) mod = Signal.like(self.mod_in) pw = Signal.like(self.pw_in) octave = Signal(range(OCTAVES)) step = Signal(range(STEPS)) base_inc = Signal(self.inc_depth) step_incs = Array( [Signal.like(base_inc, reset=inc) for inc in self._base_incs]) inc = Signal.like(phase) pulse_sample = Signal.like(self.pulse_out.o_data) saw_sample = Signal.like(self.saw_out.o_data) m = Module() with m.If(self.sync_in): m.d.sync += [ phase.eq(0), # self.rdy_out.eq(False), ] m.d.comb += [ self.note_in.o_ready.eq(True), ] with m.If(self.note_in.received()): m.d.sync += [ note.eq(self.note_in.i_data.note), octave.eq(div12(self.note_in.i_data.note)), ] # Calculate pulse wave edges. The pulse must rise and fall # exactly once per cycle. prev_msb = Signal() new_cycle = Signal() pulse_up = Signal() up_latch = Signal() pw8 = Cat(pw, Const(0, unsigned(1))) m.d.sync += [ prev_msb.eq(phase[-1]), ] m.d.comb += [ new_cycle.eq(~phase[-1] & prev_msb), # Widen pulse to one sample period minimum. pulse_up.eq(new_cycle | (up_latch & (phase[-8:] <= pw8))), ] m.d.sync += [ up_latch.eq((new_cycle | up_latch) & pulse_up), ] with m.FSM(): with m.State(FSM.START): m.d.sync += [ # note.eq(self.note_in), mod.eq(self.mod_in), pw.eq(self.pw_in), # octave.eq(div12(self.note_in)), ] m.next = FSM.MODULUS with m.State(FSM.MODULUS): m.d.sync += [ step.eq(note - mul12(octave)), ] m.next = FSM.LOOKUP with m.State(FSM.LOOKUP): m.d.sync += [ base_inc.eq(step_incs[step]), ] # m.next = FSM.MODULATE m.next = FSM.SHIFT # with m.State(FSM.MODULATE): # ... # m.next = FSM.SHIFT with m.State(FSM.SHIFT): m.d.sync += [ inc.eq((base_inc << octave)[-self.shift:]), ] m.next = FSM.ADD with m.State(FSM.ADD): m.d.sync += [ phase.eq(phase + inc), ] m.next = FSM.SAMPLE with m.State(FSM.SAMPLE): samp_depth = self.saw_out.o_data.shape()[0] samp_max = 2**(samp_depth - 1) - 1 m.d.sync += [ pulse_sample.eq(Mux(pulse_up, samp_max, -samp_max)), saw_sample.eq(samp_max - phase[-samp_depth:]), ] m.next = FSM.EMIT with m.State(FSM.EMIT): with m.If(self.saw_out.i_ready & self.pulse_out.i_ready): m.d.sync += [ self.pulse_out.o_valid.eq(True), self.pulse_out.o_data.eq(pulse_sample), self.saw_out.o_valid.eq(True), self.saw_out.o_data.eq(saw_sample), ] m.next = FSM.START with m.If(self.pulse_out.sent()): m.d.sync += [ self.pulse_out.o_valid.eq(False), ] with m.If(self.saw_out.sent()): m.d.sync += [ self.saw_out.o_valid.eq(False), ] return m
def elaborate(self, platform): m = Module() max_size_with_crc = self._max_packet_size + 2 # Submodule: CRC16 generator. m.submodules.crc = crc = USBDataPacketCRC() last_byte_crc = Signal(16) last_word_crc = Signal(16) # Currently captured PID. active_pid = Signal(4) # Active packet transfer. active_packet = Array(Signal(8) for _ in range(max_size_with_crc)) position_in_packet = Signal(range(0, max_size_with_crc)) # Keeps track of the last_word = Signal(16) # Keep our control signals + strobes un-asserted unless otherwise specified. m.d.ulpi += self.new_packet.eq(0) m.d.comb += crc.clear.eq(0) with m.FSM(domain="ulpi"): # IDLE -- waiting for a packet to be presented with m.State("IDLE"): with m.If(self.utmi.rx_active): m.next = "READ_PID" # READ_PID -- read the packet's ID. with m.State("READ_PID"): # Clear our CRC; as we're potentially about to start a new packet. m.d.comb += crc.clear.eq(1) with m.If(~self.utmi.rx_active): m.next = "IDLE" with m.Elif(self.utmi.rx_valid): is_data = (self.utmi.rx_data[0:2] == self.DATA_SUFFIX) is_valid_pid = ( self.utmi.rx_data[0:4] == ~self.utmi.rx_data[4:8]) # If this is a data packet, capture it. with m.If(is_valid_pid & is_data): m.d.ulpi += [ active_pid.eq(self.utmi.rx_data), position_in_packet.eq(0) ] m.next = "CAPTURE_DATA" # Otherwise, ignore this packet. with m.Else(): m.next = "IRRELEVANT" with m.State("CAPTURE_DATA"): # Keep a running CRC of any data observed. m.d.comb += [ crc.valid.eq(self.utmi.rx_valid), crc.data.eq(self.utmi.rx_data) ] # If we have a new byte of data, capture it. with m.If(self.utmi.rx_valid): # If this would over-fill our internal buffer, fail out. with m.If(position_in_packet >= max_size_with_crc): # TODO: potentially signal the babble? m.next = "IRRELEVANT" with m.Else(): m.d.ulpi += [ active_packet[position_in_packet].eq( self.utmi.rx_data), position_in_packet.eq(position_in_packet + 1), last_word.eq(Cat(self.utmi.rx_data, last_word[0:8])), last_word_crc.eq(last_byte_crc), last_byte_crc.eq(crc.crc), ] # If this is the end of our packet, validate our CRC and finish. with m.If(~self.utmi.rx_active): with m.If(last_word_crc == last_word): m.d.ulpi += [ self.packet_id.eq(active_pid), self.length.eq(position_in_packet - 2), self.new_packet.eq(1) ] for i in range(self._max_packet_size): m.d.ulpi += self.packet[i].eq(active_packet[i]), # IRRELEVANT -- we've encountered a malformed or non-handshake packet with m.State("IRRELEVANT"): with m.If(~self.utmi.rx_active): m.next = "IDLE" return m
def __init__(self, *, max_line_length=128): self.line_in = Array(Signal(8) for _ in range(max_line_length)) self.line_length = Signal(range(0, max_line_length + 1))
def elaborate(self, platform): m = Module() # N is size of RAMs. N = self.M + 2 assert _is_power_of_2(N) # kernel_RAM is a buffer of convolution kernel coefficents. # It is read-only. The 0'th element is zero, because the kernel # has length N-1. kernel_RAM = Memory(width=COEFF_WIDTH, depth=256, init=kernel) # kernel_RAM = Memory(width=COEFF_WIDTH, depth=N, init=kernel) m.submodules.kr_port = kr_port = kernel_RAM.read_port() # sample_RAM is a circular buffer for incoming samples. # sample_RAM = Array( # Signal(signed(self.sample_depth), reset_less=True, reset=0) # for _ in range(N) # ) sample_RAM = Memory(width=self.sample_depth, depth=N, init=[0] * N) m.submodules.sw_port = sw_port = sample_RAM.write_port() m.submodules.sr_port = sr_port = sample_RAM.read_port() # The rotors index through sample_RAM. They have an extra MSB # so we can distinguish between buffer full and buffer empty. # # w_rotor: write rotor. Points to the next entry to be written. # s_rotor: start rotor. Points to the oldest valid entry. # r_rotor: read rotor. Points to the next entry to be read. # # The polyphase decimator reads each sample N / R times, so # `r_rotor` is **NOT** the oldest needed sample. Instead, # `s_rotor` is the oldest. `s_rotor` is incremented by `R` # at the start of each convolution. # # We initialize the rotors so that the RAM contains N-1 zero samples, # and `r_rotor` is pointing to the first sample to be used. # The convolution engine can start immediately and produce a zero # result. w_rotor = Signal(range(2 * N), reset=N) s_rotor = Signal(range(2 * N), reset=1) r_rotor = Signal(range(2 * N), reset=1) # `c_index` is the next kernel coefficient to read. # `c_index` == 0 indicates done, so start at 1. c_index = Signal(range(N), reset=1) # kernel coefficient index # Useful conditions buf_n_used = Signal(range(N + 1)) buf_is_empty = Signal() buf_is_full = Signal() buf_n_readable = Signal(range(N + 1)) buf_has_readable = Signal() m.d.comb += [ buf_n_used.eq(w_rotor - s_rotor), buf_is_empty.eq(buf_n_used == 0), buf_is_full.eq(buf_n_used == N), buf_n_readable.eq(w_rotor - r_rotor), buf_has_readable.eq(buf_n_readable != 0), # Assert(buf_n_used <= N), # Assert(buf_n_readable <= buf_n_used), ] # put incoming samples into sample_RAM. m.d.comb += [ self.samples_in.o_ready.eq(~buf_is_full), sw_port.addr.eq(w_rotor[:-1]), sw_port.data.eq(self.samples_in.i_data), ] m.d.sync += sw_port.en.eq(self.samples_in.received()) with m.If(self.samples_in.received()): m.d.sync += [ # sample_RAM[w_rotor[:-1]].eq(self.samples_in.i_data), w_rotor.eq(w_rotor + 1), ] # The convolution is pipelined. # # stage 0: fetch coefficient and sample from their RAMs. # stage 1: multiply coefficient and sample. # stage 2: add product to accumulator. # stage 3: if complete, try to send accumulated sample. # p_valid = Signal(4) p_ready = Array( Signal(name=f'p_ready{i}', reset=True) for i in range(4)) p_complete = Signal(4) m.d.sync += [ p_valid[1:].eq(p_valid[:-1]), p_ready[0].eq(p_ready[1]), p_ready[1].eq(p_ready[2]), p_ready[2].eq(p_ready[3]), ] # calculation variables coeff = Signal(COEFF_SHAPE) sample = Signal(signed(self.sample_depth)) prod = Signal(signed(COEFF_WIDTH + self.sample_depth)) acc = Signal(signed(self.acc_width)) # Stage 0. en0 = Signal() m.d.comb += en0.eq(buf_has_readable & p_ready[0] * (c_index != 0)) # with m.If(en0): # m.d.sync += [ # coeff.eq(kernel_RAM[c_index]), # ] m.d.comb += coeff.eq(kr_port.data) with m.If(en0): m.d.sync += [ sample.eq(sample_RAM[r_rotor[:-1]]), ] with m.If(en0): m.d.sync += [ c_index.eq(c_index + 1), r_rotor.eq(r_rotor + 1), p_valid[0].eq(True), p_complete[0].eq(False), # kr_port.addr.eq(c_index + 1), ] m.d.comb += kr_port.addr.eq(c_index) # with m.If(buf_has_readable & p_ready[0] & (c_index != 0)): # m.d.sync += [ # coeff.eq(kernel_RAM[c_index]), # sample.eq(sample_RAM[r_rotor[:-1]]), # c_index.eq(c_index + 1), # r_rotor.eq(r_rotor + 1), # p_valid[0].eq(True), # p_complete[0].eq(False), # ] with m.If((~buf_has_readable | ~p_ready[0]) & (c_index != 0)): m.d.sync += [ p_valid[0].eq(False), p_complete[0].eq(False), ] # When c_index is zero, all convolution samples have been read. # Set up the rotors for the next sample. (and pause the # pipelined calculation.) with m.If(c_index == 0): m.d.sync += [ c_index.eq(c_index + 1), s_rotor.eq(s_rotor + self.R), r_rotor.eq(s_rotor + self.R), p_valid[0].eq(False), p_complete[0].eq(True), ] # Stage 1. with m.If(p_valid[1] & p_ready[1]): m.d.sync += [ prod.eq(coeff * sample), p_complete[1].eq(p_complete[0]), ] # Stage 2. with m.If(p_valid[2] & p_ready[2]): m.d.sync += [ acc.eq(acc + prod), p_complete[2].eq(p_complete[1]), ] # Stage 3. m.d.comb += p_ready[3].eq(~self.samples_out.full()) m.d.sync += p_complete[3].eq(p_complete[3] | p_complete[2]) with m.If(p_valid[3] & p_ready[3] & p_complete[2]): m.d.sync += [ self.samples_out.o_valid.eq(1), self.samples_out.o_data.eq(acc[self.shift:]), acc.eq(0), p_complete[3].eq(False), ] with m.If(self.samples_out.sent()): m.d.sync += [ self.samples_out.o_valid.eq(0), ] # # s_in_index = Signal(range(2 * N), reset=N) # # s_out_index = Signal(range(2 * N), reset=self.R + 1) # # c_index = Signal(range(N), reset=1) # # start_index = Signal(range(2 * N), reset=self.R) # s_in_index = Signal(range(2 * N), reset=N) # s_out_index = Signal(range(2 * N)) # c_index = Signal(range(N)) # start_index = Signal(range(2 * N)) # # coeff = Signal(signed(16)) # sample = Signal(signed(self.sample_depth)) # prod = Signal(signed(2 * self.sample_depth)) # acc = Signal(signed(self.acc_width)) # # m = Module() # # n_full = (s_in_index - start_index - 1)[:s_in_index.shape()[0]] # n_avail = (s_in_index - s_out_index)[:-1] # print(f's_in_index shape = {s_in_index.shape()}') # print(f'start_index shape = {start_index.shape()}') # print(f'n_full shape = {n_full.shape()}') # print(f'n_avail shape = {n_avail.shape()}') # full = Signal(n_full.shape()) # avail = Signal(n_avail.shape()) # m.d.comb += full.eq(n_full) # m.d.comb += avail.eq(n_avail) # # # # input process stores new samples in the ring buffer. # # with m.If(self.samples_in.received()): # # m.d.sync += [ # # sample_RAM[s_in_index[:-1]].eq(self.samples_in.i_data), # # ] # with m.If(self.samples_in.received()): # m.d.sync += [ # sample_RAM[s_in_index[:-1]].eq(self.samples_in.i_data), # s_in_index.eq(s_in_index + 1), # ] # m.d.sync += [ # self.samples_in.o_ready.eq(n_full < N), # ] # # # convolution process # sample_ready = Signal() # sample_ready = n_avail > 0 # # # with m.If(c_index == 1): # # with m.If(sample_ready): # # m.d.sync += [ # # start_index.eq(start_index + self.R), # # s_out_index.eq(start_index + self.R + 1), # # ] # # with m.Else(): # # with m.If(sample_ready): # # m.d.sync += [ # # s_out_index.eq(s_out_index + 1), # # ] # # with m.Else(): # # with m.If(sample_ready): # # m.d.sync += [ # # s_out_index.eq(s_out_index + 1), # # ] # # with m.If(sample_ready): # with m.If(c_index == 1): # m.d.sync += [ # start_index.eq(start_index + self.R), # s_out_index.eq(start_index + self.R + 1), # ] # with m.Else(): # m.d.sync += [ # s_out_index.eq(s_out_index + 1), # ] # # with m.If(c_index == 2): # with m.If(~self.samples_out.full()): # m.d.sync += [ # self.samples_out.o_valid.eq(True), # self.samples_out.o_data.eq(acc[self.shift:]), # acc.eq(0), # c_index.eq(c_index + 1), # ] # with m.Else(): # with m.If(sample_ready): # m.d.sync += [ # acc.eq(acc + prod), # c_index.eq(c_index + 1), # ] # # with m.If(sample_ready): # m.d.sync += [ # prod.eq(coeff * sample), # ] # # with m.If(sample_ready): # m.d.sync += [ # coeff.eq(kernel_RAM[c_index]), # ] # # with m.If(sample_ready): # m.d.sync += [ # sample.eq(sample_RAM[s_out_index]), # ] # # with m.If(self.samples_out.sent()): # m.d.sync += [ # self.samples_out.o_valid.eq(False), # ] # # # convolution process # # if c_index == 0: # # if sample_ready: # # start_index += R # # s_out_index = start_index + R + 1 # # else: # # if sample_ready: # # s_out_index += 1 # # # # if c_index == 1: # # if not out_full: # # out_sample = acc[shift:] # # out_valid = True # # acc = 0 # # c_index += 1 # # else # # if sample_ready: # # acc += prod # # c_index += 1 # # # # if sample_ready: # # prod = coeff * sample # # # # if sample_ready: # # coeff = kernel_RAM[c_index] # # # # if sample_ready: # # sample = sample_RAM[s_out_index] # # # # convolution process # # fill = Signal(range(N + 1)) # # m.d.comb += fill.eq(s_in_index - s_out_index) # # with m.FSM(): # # # # with m.State('RUN'): # # # when s_out_index MSB is zero, sample is ready. # # # when MSB is one, test out_idx < in_idx. # # # extended_in_index = Cat(s_in_index, 1) # # sample_ready = (fill > 0) & (fill <= N) # # # sample_ready = s_out_index < extended_in_index # # # xii = Signal.like(s_out_index) # # sr = Signal() # # # m.d.comb += xii.eq(extended_in_index) # # m.d.comb += sr.eq(sample_ready) # # with m.If(sample_ready): # # m.d.sync += [ # # acc.eq(acc + prod), # sign extension is automatic # # prod.eq(coeff * sample), # # coeff.eq(kernel_RAM[c_index]), # # sample.eq(sample_RAM[s_out_index[:-1]]), # # c_index.eq(c_index + 1), # # s_out_index.eq(s_out_index + 1), # # ] # # with m.If(c_index == 0): # # m.next = 'DONE' # # with m.If(self.samples_out.sent()): # # m.d.sync += [ # # self.samples_out.o_valid.eq(False), # # ] # # # # with m.State('DONE'): # # with m.If(~self.samples_out.full()): # # m.d.sync += [ # # self.samples_out.o_valid.eq(True), # # self.samples_out.o_data.eq(acc[self.shift:]), # # c_index.eq(1), # # start_index.eq(start_index + self.R), # # # s_out_index[:-1].eq(start_index + self.R + 1), # # # s_out_index[-1].eq(0), # # s_out_index.eq(start_index + self.R + 1), # # coeff.eq(0), # # sample.eq(0), # # prod.eq(0), # # acc.eq(0), # # ] # # m.next = 'RUN' # # # # i_ready = true # # if received: # # sample_RAM[in_index] = samples_in.i_data # # in_index += 1 # # # # FSM: # # start: # # if start_index + cv_index < in_index: <<<<< Wrong # # acc += sign_extend(prod) # # prod = coeff * sample # # coeff = kernel_RAM[cv_index] # # sample = sample_RAM[(start_index + cv_index)[:-1]] # # # # if cv_index + 1 == 0: # # goto done # # # # done: # # o_valid = true # # o_data = acc[shift:shift + 16] # # acc = 0 # # start_index = (start_index + R) % Mp2 # # cv_index = 0 # # prod = 0 # # coeff = 0 # # sample = 0 # # # # if sent: # # o_valid = false return m
def elaborate(self, platform): #Instantiate the Module m = Module() #Instantiate a decoder/validator for each bit-flip combination of the input codeword for i in range(0, self.codeword_width + 1): m.submodules["decoder" + str(i)] = LDPC_Decoder_Validator( self.ParityCheckMatrixPythonArray, self.codeword_width) #codeword_list - An array containing the input codeword and copies of the input codeword, each with a different bit flipped codeword_list = Array([ Signal(unsigned(self.codeword_width), reset=0) for _ in range(self.codeword_width + 1) ]) #decoder_output_list - An array containing the parity check status of each parity check row for an input codeword decoder_output_list = Array([ Signal(unsigned(self.ParityCheckMatrixRows), reset=0) for _ in range(self.codeword_width + 1) ]) #decoders_done - A signal which lets the top level know when the submodules have finished decoding/validating decoders_done = Signal(1) m.d.comb += decoders_done.eq( m.submodules["decoder" + str(self.codeword_width)].done) #counter - A timeout counter for catching when the decoder never finishes decoding counter = Signal(self.codeword_width) #pipeline_stage - A signal for keeping track of the pipeline stage pipeline_stage = Signal(3, reset=0) #pipeline_stage - A signal for starting the submodules start_submodules = Signal(1, reset=0) for i in range(0, self.codeword_width + 1): m.d.comb += m.submodules["decoder" + str(i)].start.eq(start_submodules) m.d.sync += m.submodules["decoder" + str(i)].data_input.eq( codeword_list[i]) m.d.comb += decoder_output_list[i].eq( m.submodules["decoder" + str(i)].data_output) #Reset the relevant registers/wires and load the input codeword into a decoder input register with m.If(self.start): m.d.sync += [ codeword_list[self.codeword_width].eq(self.data_input), self.data_output.eq(0), self.done.eq(0), self.success.eq(0), counter.eq(0), pipeline_stage.eq(1) ] #Load the input codeword into the codeword list with m.Elif(pipeline_stage == 1): for i in range(0, self.codeword_width): m.d.sync += [ codeword_list[i].eq(codeword_list[self.codeword_width]), pipeline_stage.eq(pipeline_stage + 1) ] #Flip the relevant bit on the codewords in the codeword list with m.Elif(pipeline_stage == 2): for i in range(0, self.codeword_width): m.d.sync += [ codeword_list[i][i].eq(~codeword_list[i][i]), pipeline_stage.eq(pipeline_stage + 1) ] #Start validating all the codeword variations (Set Start Bit to 1) with m.Elif(pipeline_stage == 3): for i in range(0, self.codeword_width): m.d.sync += [ start_submodules.eq(1), pipeline_stage.eq(pipeline_stage + 1) ] #Start validating all the codeword variations (Set Start Bit to 0) with m.Elif(pipeline_stage == 4): for i in range(0, self.codeword_width): m.d.sync += [ start_submodules.eq(0), pipeline_stage.eq(pipeline_stage + 1) ] #Start counting the timeout and validating if any of the submodules was successful in validating the codeword. #Finally, output success or failure along with output data and STOP. with m.Elif(pipeline_stage == 5 & (~self.done) & (~self.start)): for i in range(0, self.codeword_width + 1): m.d.sync += counter.eq(counter + 1) with m.If((decoders_done) & (counter > (self.codeword_width + 2))): m.d.sync += [self.done.eq(1), self.success.eq(0)] with m.Elif((decoders_done)): for i in range(0, self.codeword_width + 1): with m.If(decoder_output_list[i] == 0b000): m.d.sync += [ self.data_output.eq(codeword_list[ self.codeword_width][self.codeword_width - self.data_output_width:]), self.done.eq(1), self.success.eq(1) ] return m
def elaborate(self, platform): #Instantiate the Module m = Module() #[ARRAY[SIGNAL]] - working_matrix - An array of signals which represents the matrix used to calculate the output codeword. working_matrix = Array([ Signal(unsigned(self.codeword_width), reset=0) for _ in range(self.data_input_length) ]) #[ARRAY[SIGNAL]] - adder_buffer - An array of signals which is used to accumulate the columns of the working matrix to calculate the codeword adder_buffer = Array([ Signal(unsigned(self.data_input_length), reset=0) for _ in range(self.codeword_width) ]) #[SIGNAL] - cnt - A signal to count the steps completed in the encoding process cnt = Signal(self.data_input_length) #[SIGNAL] - running - A signal which is used to indicate the pipeline is running. running = Signal(1, reset=0) #[SIGNAL] - accumulation_completed - A signal which is used to indicate when the accumulation process has completed. accumulation_completed = Signal(1) #[SIGNAL] - data_input_copy - Internal copy of the data_input data_input_copy = Signal(self.data_input_length) #The accumulation process completes in fixed time complexity in (n-1) clock cycles, where n is the length of the data input m.d.comb += accumulation_completed.eq(cnt == self.data_input_length - 1) #If 'start' is asserted, reset the adder_buffer, cnt, done and output variables. with m.If(self.start): for i in range(0, self.codeword_width): m.d.sync += [adder_buffer[i].eq(0)] m.d.sync += [ cnt.eq(0), self.done.eq(0), self.data_output.eq(0), data_input_copy.eq(self.data_input), running.eq(1) ] #If 'accumulation_completed' is not satisfied, increment cnt with m.Elif(~accumulation_completed & running): m.d.sync += [cnt.eq(cnt + 1)] #Complete the first stage of the accumulation - AND the input data with the genmatrix #Since this is in the combinatorial domain, there is 'no penalty' as the size of the generator matrix and data input increases for i in range(0, self.codeword_width): for n in range(0, self.data_input_length): m.d.comb += [ working_matrix[n][i]. eq(self.gen_matrix[n][i] & data_input_copy[(self.data_input_length - n) - 1]) ] #Accumulate the columns of the matrix, with the Gallagher Modulo 2 rules #https://ieeexplore.ieee.org/document/1057683 for i in range(0, self.codeword_width): for n in range(1, self.data_input_length): if (n == 1): m.d.sync += [ adder_buffer[i][n].eq(working_matrix[n][i] ^ working_matrix[n - 1][i]) ] else: m.d.sync += [ adder_buffer[i][n].eq(adder_buffer[i][n - 1] ^ working_matrix[n][i]) ] #Present the result on the output register and set the 'done' flag. with m.Elif(accumulation_completed & running): for i in range(0, self.codeword_width): m.d.sync += [ self.data_output[i].eq( adder_buffer[i][self.data_input_length - 1]), self.done.eq(1), running.eq(0) ] return m
def elaborate(self, platform): #Instantiate the Module m = Module() #[ARRAY[SIGNAL]] - stage_1_working_matrix - An array of signals which represents the parity matrix used to validate the input codeword stage_1_working_matrix = Array([ Signal(unsigned(self.codeword_width), reset=0) for _ in range(self.data_output_matrix_rows) ]) #[ARRAY[SIGNAL]] - stage_2_adder_buffer - An array of signals which are used to calculate whether there are an even number of 1s in each parity check row stage_2_adder_buffer = Array([ Signal(unsigned(self.codeword_width), reset=0, name="stage_2_adder_buffer") for _ in range(self.data_output_matrix_rows) ]) #[SIGNAL] - stage_2_counter - Counts the worst case scenario for the even 1s calculation stage_2_counter = Signal(self.codeword_width) #[SIGNAL] - pipeline_stage - Signal to indicate whether we are in pipeline stage 0 or 1 pipeline_stage = Signal(1) #[SIGNAL] - running - Signal to indicate that we are currently running running = Signal(1, reset=0) #if start bit is asserted, reset the system with m.If(self.start): for i in range(0, self.data_output_matrix_rows): m.d.sync += [ running.eq(1), stage_1_working_matrix[i].eq(0), stage_2_adder_buffer[i].eq(0), stage_2_counter.eq(0), self.done.eq(0), self.data_output.eq(0b000), pipeline_stage.eq(0), ] #First Pipeline Stage (0) #Data_input ANDed with each row of the parity check matrix with m.If(running & (pipeline_stage == 0)): for i in range(0, self.data_output_matrix_rows): m.d.sync += [ stage_1_working_matrix[i].eq(self.data_input & self.parity_check_matrix[i]) ] m.d.sync += [pipeline_stage.eq(1)] #Second Pipeline Stage (1) #Accumulate the ANDed data to calculate if there are an even number of 1s. with m.If(running & (pipeline_stage == 1)): m.d.sync += [stage_2_counter.eq(stage_2_counter + 1)] for i in range(0, self.data_output_matrix_rows): for n in range(1, self.codeword_width): if (n == 1): m.d.sync += [ stage_2_adder_buffer[i] [n].eq(stage_1_working_matrix[i][n] ^ stage_1_working_matrix[i][n - 1]) ] else: m.d.sync += [ stage_2_adder_buffer[i] [n].eq(stage_2_adder_buffer[i][n - 1] ^ stage_1_working_matrix[i][n]) ] #Output the result of each parity check row comparison (1=Fail, 0=Pass) with m.If(stage_2_counter == (self.codeword_width)): for i in range(0, self.data_output_matrix_rows): m.d.sync += [ self.data_output[(self.data_output_matrix_rows - 1) - i].eq(stage_2_adder_buffer[i][ self.codeword_width - 1]), running.eq(0), self.done.eq(1) ] return m
0x64, OLED_INIT.SET_PRECHARGE_B, 0x78, OLED_INIT.SET_PRECHARGE_C, 0x64, OLED_INIT.SET_PRECHARGE_LEVEL, 0x31, OLED_INIT.SET_CONTRAST_COLOR_A, 0xFF, OLED_INIT.SET_CONTRAST_COLOR_B, 0xFF, OLED_INIT.SET_CONTRAST_COLOR_C, 0xFF, OLED_INIT.SET_VCOMH, 0x3E, OLED_INIT.SET_MASTER_CURRENT_CONTROL, 0x06, OLED_INIT.SET_COLUMN_ADDRESS, 0x00, 0x5F, OLED_INIT.SET_ROW_ADDRESS, 0x00, 0x3F, OLED_INIT.SET_DISPLAY_ON, OLED_INIT.NOP1, ] oled_init_seq = Array(Value for _ in range(len(oled_init_seq_list))) for i in range(len(oled_init_seq_list)): oled_init_seq[i] = oled_init_seq_list[i]
def elab(self, m): # break out the functionid funct3 = Signal(3) funct7 = Signal(7) m.d.comb += [ funct3.eq(self.cmd_function_id[:3]), funct7.eq(self.cmd_function_id[-7:]), ] stored_function_id = Signal(3) current_function_id = Signal(3) current_function_done = Signal() stored_output = Signal(32) # Response is always OK. m.d.comb += self.rsp_ok.eq(1) instruction_outputs = Array(Signal(32) for _ in range(8)) instruction_dones = Array(Signal() for _ in range(8)) instruction_starts = Array(Signal() for _ in range(8)) for (i, instruction) in self.instructions.items(): m.d.comb += instruction_outputs[i].eq(instruction.output) m.d.comb += instruction_dones[i].eq(instruction.done) m.d.comb += instruction.start.eq(instruction_starts[i]) def check_instruction_done(): with m.If(current_function_done): m.d.comb += self.rsp_valid.eq(1) m.d.comb += self.rsp_out.eq( instruction_outputs[current_function_id]) with m.If(self.rsp_ready): m.next = "WAIT_CMD" with m.Else(): m.d.sync += stored_output.eq( instruction_outputs[current_function_id]) m.next = "WAIT_TRANSFER" with m.Else(): m.next = "WAIT_INSTRUCTION" with m.FSM(): with m.State("WAIT_CMD"): # We're waiting for a command from the CPU. m.d.comb += current_function_id.eq(funct3) m.d.comb += current_function_done.eq( instruction_dones[current_function_id]) m.d.comb += self.cmd_ready.eq(1) with m.If(self.cmd_valid): m.d.sync += stored_function_id.eq(self.cmd_function_id[:3]) m.d.comb += instruction_starts[current_function_id].eq(1) check_instruction_done() with m.State("WAIT_INSTRUCTION"): # An instruction is executing on the CFU. We're waiting until it # completes. m.d.comb += current_function_id.eq(stored_function_id) m.d.comb += current_function_done.eq( instruction_dones[current_function_id]) check_instruction_done() with m.State("WAIT_TRANSFER"): # Instruction has completed, but the CPU isn't ready to receive # the result. m.d.comb += self.rsp_valid.eq(1) m.d.comb += self.rsp_out.eq(stored_output) with m.If(self.rsp_ready): m.next = "WAIT_CMD" for (i, instruction) in self.instructions.items(): m.d.comb += [ instruction.in0.eq(self.cmd_in0), instruction.in1.eq(self.cmd_in1), instruction.funct7.eq(funct7), ] m.submodules[f"fn{i}"] = instruction
def elaborate(self, platform): m = Module() way_layout = [ ('data', 32 * self.nwords), ('tag', self.s1_address.tag.shape()), ('valid', 1), ('sel_lru', 1) ] if self.enable_write: way_layout.append(('sel_we', 1)) ways = Array(Record(way_layout) for _way in range(self.nways)) fill_cnt = Signal.like(self.s1_address.offset) # set the LRU if self.nways == 1: lru = Const(0) # self.nlines else: lru = Signal(self.nlines) with m.If(self.bus_valid & self.bus_ack & self.bus_last): # err ^ ack == 1 _lru = lru.bit_select(self.s2_address.line, 1) m.d.sync += lru.bit_select(self.s2_address.line, 1).eq(~_lru) # 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: m.d.comb += ways[way_hit.o].sel_we.eq(self.s2_we & self.s2_valid) # read data m.d.comb += self.s2_rdata.eq(ways[way_hit.o].data.word_select(self.s2_address.offset, 32)) 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), # WARNING extra_bits 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): # in case of flush, abort ongoing 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) tag_m = Memory(width=len(way.tag), depth=self.nlines) tag_rp = tag_m.read_port() tag_wp = tag_m.write_port() m.submodules += tag_rp, tag_wp data_m = Memory(width=len(way.data), depth=self.nlines) data_rp = data_m.read_port() data_wp = data_m.write_port(granularity=32) 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 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_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), ] return m
def elaborate(self, platform): m = Module() m.submodules.bridge = self._bridge # Shortcuts to our components. token = self.interface.tokenizer tx = self.interface.tx handshakes_out = self.interface.handshakes_out # # Core FIFO. # # Create our FIFO; and set it to be cleared whenever the user requests. m.submodules.fifo = fifo = \ ResetInserter(self.reset.w_stb)(SyncFIFOBuffered(width=8, depth=self.max_packet_size)) m.d.comb += [ # Whenever the user DATA register is written to, add the relevant data to our FIFO. fifo.w_en .eq(self.data.w_stb), fifo.w_data .eq(self.data.w_data), ] # Keep track of the amount of data in our FIFO. bytes_in_fifo = Signal(range(0, self.max_packet_size + 1)) # If we're clearing the whole FIFO, reset our data count. with m.If(self.reset.w_stb): m.d.usb += bytes_in_fifo.eq(0) # Keep track of our FIFO's data count as data is added or removed. increment = fifo.w_en & fifo.w_rdy decrement = fifo.r_en & fifo.r_rdy with m.Elif(increment & ~decrement): m.d.usb += bytes_in_fifo.eq(bytes_in_fifo + 1) with m.Elif(decrement & ~increment): m.d.usb += bytes_in_fifo.eq(bytes_in_fifo - 1) # # Register updates. # # Active endpoint number. with m.If(self.epno.w_stb): m.d.usb += self.epno.r_data.eq(self.epno.w_data) # Keep track of which endpoints are stalled. endpoint_stalled = Array(Signal() for _ in range(16)) # Set the value of our endpoint `stall` based on our `stall` register... with m.If(self.stall.w_stb): m.d.usb += endpoint_stalled[self.epno.r_data].eq(self.stall.w_data) # ... but clear our endpoint `stall` when we get a SETUP packet. with m.If(token.is_setup & token.new_token): m.d.usb += endpoint_stalled[token.endpoint].eq(0) # Manual data toggle control. # TODO: Remove this in favor of automated tracking? m.d.comb += self.interface.tx_pid_toggle.eq(self.pid.r_data) with m.If(self.pid.w_stb): m.d.usb += self.pid.r_data.eq(self.pid.w_data) # # Status registers. # m.d.comb += [ self.have.r_data .eq(fifo.r_rdy) ] # # Control logic. # # Logic shorthand. new_in_token = (token.is_in & token.ready_for_response) endpoint_matches = (token.endpoint == self.epno.r_data) stalled = endpoint_stalled[token.endpoint] with m.FSM(domain='usb') as f: # Drive our IDLE line based on our FSM state. m.d.comb += self.idle.r_data.eq(f.ongoing('IDLE')) # IDLE -- our CPU hasn't yet requested that we send data. # We'll wait for it to do so, and NAK any packets that arrive. with m.State("IDLE"): # If we get an IN token... with m.If(new_in_token): # STALL it, if the endpoint is STALL'd... with m.If(stalled): m.d.comb += handshakes_out.stall.eq(1) # Otherwise, NAK. with m.Else(): m.d.comb += handshakes_out.nak.eq(1) # If the user request that we send data, "prime" the endpoint. # This means we have data to send, but are just waiting for an IN token. with m.If(self.epno.w_stb & ~stalled): m.next = "PRIMED" # PRIMED -- our CPU has provided data, but we haven't been sent an IN token, yet. # Await that IN token. with m.State("PRIMED"): with m.If(new_in_token): # If the target endpoint is STALL'd, reply with STALL no matter what. with m.If(stalled): m.d.comb += handshakes_out.stall.eq(1) # If we have a new IN token to our endpoint, move to responding to it. with m.Elif(endpoint_matches): # If there's no data in our endpoint, send a ZLP. with m.If(~fifo.r_rdy): m.next = "SEND_ZLP" # Otherwise, send our data, starting with our first byte. with m.Else(): m.d.usb += tx.first.eq(1) m.next = "SEND_DATA" # Otherwise, we don't have a response; NAK the packet. with m.Else(): m.d.comb += handshakes_out.nak.eq(1) # SEND_ZLP -- we're now now ready to respond to an IN token with a ZLP. # Send our response. with m.State("SEND_ZLP"): m.d.comb += [ tx.valid .eq(1), tx.last .eq(1) ] m.next = 'IDLE' # SEND_DATA -- we're now ready to respond to an IN token to our endpoint. # Send our response. with m.State("SEND_DATA"): last_packet = (bytes_in_fifo == 1) m.d.comb += [ tx.valid .eq(1), tx.last .eq(last_packet), # Drive our transmit data directly from our FIFO... tx.payload .eq(fifo.r_data), # ... and advance our FIFO each time a data byte is transmitted. fifo.r_en .eq(tx.ready) ] # After we've sent a byte, drop our first flag. with m.If(tx.ready): m.d.usb += tx.first.eq(0) # Once we transmit our last packet, we're done transmitting. Move back to IDLE. with m.If(last_packet & tx.ready): m.next = 'IDLE' return DomainRenamer({"sync": "usb"})(m)
def elaborate(self, platform): m = Module() # Create divided clock for MDC mdc_int = Signal() mdc_rise = Signal() mdc_fall = Signal() mdc_divider = Signal(range(self.clk_div)) with m.If(mdc_divider == 0): m.d.sync += [ mdc_divider.eq(self.clk_div - 1), mdc_int.eq(0), mdc_fall.eq(1), mdc_rise.eq(0), ] with m.Elif(mdc_divider == self.clk_div // 2): m.d.sync += [ mdc_divider.eq(mdc_divider - 1), mdc_int.eq(1), mdc_fall.eq(0), mdc_rise.eq(1), ] with m.Else(): m.d.sync += [ mdc_divider.eq(mdc_divider - 1), mdc_fall.eq(0), mdc_rise.eq(0), ] # Latches for inputs _phy_addr = Signal.like(self.phy_addr) _reg_addr = Signal.like(self.reg_addr) _rw = Signal.like(self.rw) _write_data = Signal.like(self.write_data) # MDIO FSM bit_counter = Signal(6) with m.FSM() as fsm: m.d.comb += self.busy.eq(~fsm.ongoing("IDLE")) # Idle state # Constantly register input data and wait for START signal with m.State("IDLE"): m.d.comb += [ self.mdc.eq(0), self.mdio.oe.eq(0), ] # Latch input signals while in idle m.d.sync += [ _phy_addr.eq(self.phy_addr), _reg_addr.eq(self.reg_addr), _rw.eq(self.rw), _write_data.eq(self.write_data), ] with m.If(self.start): m.next = "SYNC" # Synchronise to MDC. Enter this state at any time. # Will transition to PRE_32 immediately after the next # falling edge on MDC. with m.State("SYNC"): m.d.comb += [ self.mdc.eq(0), self.mdio.oe.eq(0), ] with m.If(mdc_fall): m.d.sync += bit_counter.eq(32) m.next = "PRE_32" # PRE_32 # Preamble field: 32 bits of 1 with m.State("PRE_32"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(1), # Output all 1s self.mdio.o.eq(1), ] # Count falling edges of MDC, # transition to ST after 32 MDC clocks with m.If(mdc_fall): with m.If(bit_counter == 1): m.d.sync += bit_counter.eq(2) m.next = "ST" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # ST # Start field: always 01 with m.State("ST"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(1), self.mdio.o.eq(bit_counter[0]), ] with m.If(mdc_fall): with m.If(bit_counter == 1): m.d.sync += bit_counter.eq(2) m.next = "OP" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # OP # Opcode field: read=10, write=01 with m.State("OP"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(1), ] with m.If(_rw): m.d.comb += self.mdio.o.eq(bit_counter[0]) with m.Else(): m.d.comb += self.mdio.o.eq(~bit_counter[0]) with m.If(mdc_fall): with m.If(bit_counter == 1): m.d.sync += bit_counter.eq(5) m.next = "PA5" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # PA5 # PHY address field, 5 bits with m.State("PA5"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(1), self.mdio.o.eq(Array(_phy_addr)[bit_counter - 1]), ] with m.If(mdc_fall): with m.If(bit_counter == 1): m.d.sync += bit_counter.eq(5) m.next = "RA5" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # RA5 # Register address field, 5 bits with m.State("RA5"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(1), self.mdio.o.eq(Array(_reg_addr)[bit_counter - 1]), ] with m.If(mdc_fall): with m.If(bit_counter == 1): with m.If(_rw): m.d.sync += bit_counter.eq(2) m.next = "TA_W" with m.Else(): m.d.sync += bit_counter.eq(1) m.next = "TA_R" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # TA # Turnaround, 1 bits, OE released for read operations with m.State("TA_R"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(0), ] with m.If(mdc_fall): with m.If(bit_counter == 1): m.d.sync += bit_counter.eq(16) m.next = "D16_R" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # TA # Turnaround, 2 bits, driven to 10 for write operations with m.State("TA_W"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(1), self.mdio.o.eq(~bit_counter[0]), ] with m.If(mdc_fall): with m.If(bit_counter == 1): m.d.sync += bit_counter.eq(16) m.next = "D16_W" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # D16 # Data field, read operation with m.State("D16_R"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(0), ] with m.If(mdc_fall): bit = Array(self.read_data)[bit_counter - 1] m.d.sync += bit.eq(self.mdio.i) with m.If(bit_counter == 1): m.next = "READ_LAST_CLOCK" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) # Because we sample MDIO on the falling edge, the final clock # pulse is not used for data, but should probably be emitted to # stop things getting confused. with m.State("READ_LAST_CLOCK"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(0), ] with m.If(mdc_fall): m.next = "IDLE" # D16 # Data field, write operation with m.State("D16_W"): m.d.comb += [ self.mdc.eq(mdc_int), self.mdio.oe.eq(1), self.mdio.o.eq(Array(_write_data)[bit_counter - 1]), ] with m.If(mdc_fall): with m.If(bit_counter == 0): m.next = "IDLE" with m.Else(): m.d.sync += bit_counter.eq(bit_counter - 1) return m
def elaborate(self, platform): m = Module() m.submodules.bridge = self._bridge # Shortcuts to our components. interface = self.interface token = self.interface.tokenizer rx = self.interface.rx handshakes_out = self.interface.handshakes_out # # Control registers # # Active endpoint number. with m.If(self.epno.w_stb): m.d.usb += self.epno.r_data.eq(self.epno.w_data) # Keep track of which endpoints are stalled. endpoint_stalled = Array(Signal() for _ in range(16)) # Allow the CPU to set our enable bit. with m.If(self.enable.w_stb): m.d.usb += self.enable.r_data.eq(self.enable.w_data) # If we've just ACK'd a receive, clear our enable. with m.If(interface.handshakes_out.ack): m.d.usb += self.enable.r_data.eq(0) # Set the value of our endpoint `stall` based on our `stall` register... with m.If(self.stall.w_stb): m.d.usb += endpoint_stalled[self.epno.r_data].eq(self.stall.w_data) # ... but clear our endpoint `stall` when we get a SETUP packet. with m.If(token.is_setup & token.new_token): m.d.usb += endpoint_stalled[token.endpoint].eq(0) # # Core FIFO. # m.submodules.fifo = fifo = ResetInserter(self.reset.w_stb)(SyncFIFOBuffered(width=8, depth=10)) # Shortcut for when we should allow a receive. We'll read when: # - Our `epno` register matches the target register; and # - We've primed the relevant endpoint. # - Our most recent token is an OUT. # - We're not stalled. stalled = token.is_out & endpoint_stalled[token.endpoint] endpoint_matches = (token.endpoint == self.epno.r_data) allow_receive = endpoint_matches & self.enable.r_data & token.is_out & ~stalled nak_receives = token.is_out & ~allow_receive & ~stalled m.d.comb += [ # We'll write to the endpoint iff we have a primed fifo.w_en .eq(allow_receive & rx.valid & rx.next), fifo.w_data .eq(rx.payload), # We'll advance the FIFO whenever our CPU reads from the data CSR; # and we'll always read our data from the FIFO. fifo.r_en .eq(self.data.r_stb), self.data.r_data .eq(fifo.r_data), # Pass the FIFO status on to our CPU. self.have.r_data .eq(fifo.r_rdy), # If we've just finished an allowed receive, ACK. handshakes_out.ack .eq(allow_receive & interface.rx_ready_for_response), # If we were stalled, stall. handshakes_out.stall .eq(stalled & interface.rx_ready_for_response), # If we're not ACK'ing or STALL'ing, NAK all packets. handshakes_out.nak .eq(nak_receives & interface.rx_ready_for_response) ] # # Interrupt/status # return DomainRenamer({"sync": "usb"})(m)
def check(self, m: Module): self.assert_cycles(m, 2) input1 = self.data.pre_a pre_h = self.data.pre_ccs[Flags.H] pre_c = self.data.pre_ccs[Flags.C] lo = input1[:4] hi = input1[4:] with m.If(pre_h): m.d.comb += Assume(lo <= 3) with m.If(pre_c): m.d.comb += Assume(hi <= 3) # Conditions taken directly from Motorola 6800 Programming Reference Manual, # the DAA instruction operation table. # Conditions are: C before DAA, a condition on hi, H before DAA, a condition on lo. conds = Array( [ Array([0, hi <= 9, 0, lo <= 9]), Array([0, hi <= 8, 0, lo >= 10]), Array([0, hi <= 9, 1, lo <= 3]), Array([0, hi >= 10, 0, lo <= 9]), Array([0, hi >= 9, 0, lo >= 10]), Array([0, hi >= 10, 1, lo <= 3]), Array([1, hi <= 2, 0, lo <= 9]), Array([1, hi <= 2, 0, lo >= 10]), Array([1, hi <= 3, 1, lo <= 3]), ] ) # Results are: DAA adjustment, state of C after DAA results = Array( [ Array([0, 0]), Array([6, 0]), Array([6, 0]), Array([0x60, 1]), Array([0x66, 1]), Array([0x66, 1]), Array([0x60, 1]), Array([0x66, 1]), Array([0x66, 1]), ] ) input_is_valid = Signal() expected_output = Signal(8) expected_c = Signal() for i in range(len(conds)): with m.If( (pre_c == conds[i][0]) & conds[i][1] & (pre_h == conds[i][2]) & conds[i][3] ): m.d.comb += input_is_valid.eq(1) m.d.comb += expected_output.eq(input1 + results[i][0]) m.d.comb += expected_c.eq(results[i][1]) z = expected_output == 0 n = expected_output[7] with m.If(input_is_valid): self.assert_registers(m, A=expected_output, PC=self.data.pre_pc + 1) self.assert_flags(m, Z=z, N=n, C=expected_c)