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 elab(self, m): was_updated = Signal() m.d.sync += was_updated.eq(self.updated) # Connect sequential memory readers smr_datas = Array([Signal(32, name=f"smr_data_{n}") for n in range(4)]) smr_nexts = Array([Signal(name=f"smr_next_{n}") for n in range(4)]) for n, (smr, mem_addr, mem_data, smr_data, smr_next) in enumerate( zip(self.smrs, self.mem_addrs, self.mem_datas, smr_datas, smr_nexts)): m.submodules[f"smr_{n}"] = smr m.d.comb += [ smr.limit.eq(self.limit >> 2), mem_addr.eq(smr.mem_addr), smr.mem_data.eq(mem_data), smr_data.eq(smr.data), smr.next.eq(smr_next), smr.restart.eq(was_updated), ] curr_bank = Signal(2) m.d.comb += self.data.eq(smr_datas[curr_bank]) with m.If(self.restart): m.d.sync += curr_bank.eq(0) with m.Elif(self.next): m.d.comb += smr_nexts[curr_bank].eq(1) m.d.sync += curr_bank.eq(curr_bank + 1)
def elab(self, m): # Unset valid on transfer (may be overrridden below) with m.If(self.output.ready): m.d.sync += self.output.valid.eq(0) # Set flag to remember that value is available flags = Array(Signal(name=f"flag_{i}") for i in range(8)) for i in range(8): with m.If(self.accumulator_new[i]): m.d.sync += flags[i].eq(1) # Calculate index of value to output index = Signal(range(8)) next_index = Signal(range(8)) with m.If(self.half_mode): # counts: 0, 1, 4, 5, 0 ... m.d.comb += next_index[1].eq(0) m.d.comb += Cat(next_index[0], next_index[2] ).eq(Cat(index[0], index[2]) + 1) with m.Else(): m.d.comb += next_index.eq(index + 1) # If value available this cycle, or previously # - output new value # - unset flag # - increment index with m.If(Array(self.accumulator_new)[index] | flags[index]): m.d.sync += [ self.output.payload.eq(Array(self.accumulator)[index]), self.output.valid.eq(1), flags[index].eq(0), index.eq(next_index), ]
def __init__(self, order=3, totalbits=16, fractionalbits=5): # Fixed point arithmic # https://vha3.github.io/FixedPoint/FixedPoint.html self.totalbits = totalbits self.fractionalbits = fractionalbits self.signed = 1 # Bernstein coefficients self.coeff = Array() for _ in order: self.coeff.extend([Signal(signed(totalbits))]) # time self.t = Signal(signed(totalbits)) # In / out signals self.done = Signal() self.beta = Array().like(self.coeff)
def elab(self, m): group = Signal(range(4)) repeat_count = Signal.like(self.repeats) mem = Array([Signal(14, name=f"mem{i}") for i in range(4)]) with m.If(repeat_count == 0): # On first repeat - pass through next and addr and record addresses m.d.comb += self.gen_next.eq(self.next) m.d.comb += self.addr.eq(self.gen_addr) with m.If(self.next): m.d.sync += mem[group].eq(self.gen_addr) with m.Else(): # Subsequently use recorded data m.d.comb += self.addr.eq(mem[group]) # Update group and repeat_count on next with m.If(self.next): m.d.sync += group.eq(group + 1) with m.If(group == 3): m.d.sync += repeat_count.eq(repeat_count + 1) with m.If(repeat_count + 1 == self.repeats): m.d.sync += repeat_count.eq(0) with m.If(self.start): m.d.sync += repeat_count.eq(0) m.d.sync += group.eq(0)
def elab(self, m): # number of bytes received already byte_count = Signal(range(4)) # output channel for next byte received pixel_index = Signal(range(self._num_pixels)) # shift registers to buffer 3 incoming values shift_registers = Array( [Signal(24, name=f"sr_{i}") for i in range(self._num_pixels)]) last_pixel_index = Signal.like(pixel_index) m.d.sync += last_pixel_index.eq(Mux(self.half_mode, self._num_pixels // 2 - 1, self._num_pixels - 1)) next_pixel_index = Signal.like(pixel_index) m.d.comb += next_pixel_index.eq(Mux(pixel_index == last_pixel_index, 0, pixel_index + 1)) m.d.comb += self.input.ready.eq(1) with m.If(self.input.is_transferring()): sr = shift_registers[pixel_index] with m.If(byte_count == 3): # Output current value and shift register m.d.comb += self.output.valid.eq(1) payload = Cat(sr, self.input.payload) m.d.comb += self.output.payload.eq(payload) with m.Else(): # Save input to shift register m.d.sync += sr[-8:].eq(self.input.payload) m.d.sync += sr[:-8].eq(sr[8:]) # Increment pixel index m.d.sync += pixel_index.eq(next_pixel_index) with m.If(pixel_index == last_pixel_index): m.d.sync += pixel_index.eq(0) m.d.sync += byte_count.eq(byte_count + 1) # allow rollover
def __init__(self, bits_width, num_memories, depth): super().__init__() assert 0 == ((num_memories - 1) & num_memories ), "Num memories (f{num_memories}) not a power of two" self.num_memories_bits = (num_memories - 1).bit_length() self.w_en = Array(Signal() for _ in range(num_memories)) self.w_addr = Signal(range(depth)) self.w_data = Signal(bits_width) self.restart = Signal() self.count = Signal(range(depth * num_memories + 1)) self.updated = Signal()
def elaborate(self, platform): m = Module() uart = platform.request("uart") clock_freq = int(60e6) char_freq = int(6e6) # Create our UART transmitter. transmitter = UARTTransmitter(divisor=int(clock_freq // 115200)) m.submodules.transmitter = transmitter stream = transmitter.stream # Create a counter that will let us transmit ten times per second. counter = Signal(range(0, char_freq)) with m.If(counter == (char_freq - 1)): m.d.sync += counter.eq(0) with m.Else(): m.d.sync += counter.eq(counter + 1) # Create a simple ROM with a message for ourselves... letters = Array(ord(i) for i in "Hello, world! \r\n") # ... and count through it whenever we send a letter. current_letter = Signal(range(0, len(letters))) with m.If(stream.ready): m.d.sync += current_letter.eq(current_letter + 1) # Hook everything up. m.d.comb += [ stream.payload.eq(letters[current_letter]), stream.valid.eq(counter == 0), uart.tx.o.eq(transmitter.tx), ] # If this platform has an output-enable control on its UART, drive it iff # we're actively driving a transmission. if hasattr(uart.tx, 'oe'): m.d.comb += uart.tx.oe.eq(transmitter.driving), # Turn on a single LED, just to show something's running. led = Cat(platform.request('led', i) for i in range(6)) m.d.comb += led.eq(~transmitter.tx) return m
def elab(self, m): # Create the four memories dps = [ DualPortMemory(depth=self.max_depth, width=32, is_sim=is_pysim_run()) for _ in range(4) ] for n, dp in enumerate(dps): m.submodules[f"dp_{n}"] = dp # Tracks which buffers are "full" and ready for reading r_full = Array([Signal(name="r_full_0"), Signal(name="r_full_1")]) with m.If(self.restart): m.d.sync += [ r_full[0].eq(0), r_full[1].eq(0), ] # Track reading and writing self._elab_write(m, dps, r_full) self._elab_read(m, dps, r_full)
def elaborate(self, platform): m = Module() m.submodules += self.ila # Generate our domain clocks/resets. m.submodules.car = platform.clock_domain_generator() # Clock divider / counter. m.d.usb += self.counter.eq(self.counter + 1) # Say "hello world" constantly over our ILA... letters = Array(ord(i) for i in "Hello, world! \r\n") current_letter = Signal(range(0, len(letters))) m.d.usb += current_letter.eq(current_letter + 1) m.d.comb += self.hello.eq(letters[current_letter]) # Set our ILA to trigger each time the counter is at a random value. # This shows off our example a bit better than counting at zero. m.d.comb += self.ila.trigger.eq(self.counter == 227) # Return our elaborated module. return m
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) instructions = self.__build_instructions(m) 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 enumerate(instructions): 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 instruction in instructions: m.d.comb += [ instruction.in0.eq(self.cmd_in0), instruction.in1.eq(self.cmd_in1), instruction.funct7.eq(funct7), ] # tie "reset" and "rst" together (issue #110) rst = ResetSignal('sync') m.d.comb += rst.eq(self.reset)
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. 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.buffer_toggle read_buffer_number = ~self.buffer_toggle # 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.buffer_toggle .eq(~self.buffer_toggle), 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.d.usb += self.data_pid[0].eq(~self.data_pid[0]), 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) | in_stream.last) with m.Elif(~in_stream.ready | packet_completing): m.next = "WAIT_TO_SEND" m.d.usb += [ self.buffer_toggle .eq(~self.buffer_toggle), 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
class Casteljau(Elaboratable): ''' Implements Bezier curves using Casteljau's algorithm Once calculation is done a signal is raised. User can obtain result by looking at beta zero. ''' def __init__(self, order=3, totalbits=16, fractionalbits=5): # Fixed point arithmic # https://vha3.github.io/FixedPoint/FixedPoint.html self.totalbits = totalbits self.fractionalbits = fractionalbits self.signed = 1 # Bernstein coefficients self.coeff = Array() for _ in order: self.coeff.extend([Signal(signed(totalbits))]) # time self.t = Signal(signed(totalbits)) # In / out signals self.done = Signal() self.beta = Array().like(self.coeff) def elaborate(self, platform): m = Module() beta = self.beta temp = Signal(signed(self.totalbits * 2)) n = len(self.coeff) j = Signal(range(1, n)) k = Signal.like(j) with m.FSM(reset='INIT') as algo: with m.State('INIT'): m.d.sync += self.done.eq(0) for i in range(n): m.d.sync += beta[i].eq(self.coeff[i]) m.d.sync += [k.eq(0), j.eq(1)] m.next = 'UPDATE' with m.FSM('UPDATE'): m.d.sync += temp.eq(beta[k] * (1 - self.t) + beta[k + 1] * self.t) m.next = 'MULTIPLICATIONFIX' # Fixed point arithmetic need fix # see multiplication as https://vha3.github.io/FixedPoint/FixedPoint.html with m.FSM('MULTIPLICATIONFIX'): m.d.sync += beta[k].eq( temp[self.fractionalbits:self.fractionalbits + self.totalbits]) with m.If(k != n - j): m.d.sync += k.eq(k + 1) m.next = 'UPDATE' with m.Else(): with m.If(j != n): m.d.sync += j.eq(j + 1) m.d.sync += k.eq(0) m.next = 'UPDATE' with m.Else(): m.next = 'FINISH' with m.FSM('FINISH'): m.d.sync += self.done.eq(1) m.next = 'FINISH' 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() # 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