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 # Logic condition for getting a new setup packet. new_setup = token.new_token & token.is_setup # # Core FIFO. # m.submodules.fifo = fifo = ResetInserter(new_setup)(SyncFIFOBuffered(width=8, depth=10)) m.d.comb += [ # We'll write to the active FIFO whenever the last received token is a SETUP # token, and we have incoming data; and we'll always write the data received fifo.w_en .eq(token.is_setup & 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), # Always acknowledge SETUP packets as they arrive. handshakes_out.ack .eq(token.is_setup & interface.rx_ready_for_response) ] # # Control registers # # Our address register always reads the current address of the device; # but will generate a m.d.comb += self._address.r_data.eq(interface.active_address) with m.If(self._address.w_stb): m.d.comb += [ interface.address_changed .eq(1), interface.new_address .eq(self._address.w_data), ] # # Status and interrupts. # with m.If(token.new_token): m.d.usb += self.epno.r_data.eq(token.endpoint) # TODO: generate interrupts return DomainRenamer({"sync": "usb"})(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 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() sync_pulse = Signal(8) da_reset_shifter = Signal() da_reset_bitstuff = Signal( ) # Need to reset the bit stuffer 1 cycle after the shifter. stall = Signal() # These signals are set during the sync pulse sp_reset_bitstuff = Signal() sp_reset_shifter = Signal() sp_bit = Signal() sp_o_data_strobe = Signal() # 12MHz domain bitstuff_valid_data = Signal() # Keep a Gray counter around to smoothly transition between states state_gray = Signal(2) state_data = Signal() state_sync = Signal() # # Transmit gearing. # m.submodules.shifter = shifter = TxShifter(width=8) m.d.comb += [ shifter.i_data.eq(self.i_data_payload), shifter.i_enable.eq(~stall), shifter.i_clear.eq(da_reset_shifter | sp_reset_shifter) ] # # Bit-stuffing and NRZI. # bitstuff = ResetInserter(da_reset_bitstuff)(TxBitstuffer()) m.submodules.bitstuff = bitstuff m.submodules.nrzi = nrzi = TxNRZIEncoder() # # Transmit controller. # m.d.comb += [ # Send a data strobe when we're two bits from the end of the sync pulse. # This is because the pipeline takes two bit times, and we want to ensure the pipeline # has spooled up enough by the time we're there. bitstuff.i_data.eq(shifter.o_data), stall.eq(bitstuff.o_stall), sp_bit.eq(sync_pulse[0]), sp_reset_bitstuff.eq(sync_pulse[0]), # The shifter has one clock cycle of latency, so reset it # one cycle before the end of the sync byte. sp_reset_shifter.eq(sync_pulse[1]), sp_o_data_strobe.eq(sync_pulse[5]), state_data.eq(state_gray[0] & state_gray[1]), state_sync.eq(state_gray[0] & ~state_gray[1]), self.fit_oe.eq(state_data | state_sync), self.fit_dat.eq((state_data & shifter.o_data & ~bitstuff.o_stall) | sp_bit), self.o_data_strobe.eq(state_data & shifter.o_get & ~stall & self.i_oe), ] # If we reset the shifter, then o_empty will go high on the next cycle. # m.d.usb += [ # If the shifter runs out of data, percolate the "reset" signal to the # shifter, and then down to the bitstuffer. # da_reset_shifter.eq(~stall & shifter.o_empty & ~da_stalled_reset), # da_stalled_reset.eq(da_reset_shifter), # da_reset_bitstuff.eq(~stall & da_reset_shifter), bitstuff_valid_data.eq(~stall & shifter.o_get & self.i_oe), ] with m.FSM(domain="usb"): with m.State('IDLE'): with m.If(self.i_oe): m.d.usb += [sync_pulse.eq(1 << 7), state_gray.eq(0b01)] m.next = "SEND_SYNC" with m.Else(): m.d.usb += state_gray.eq(0b00) with m.State('SEND_SYNC'): m.d.usb += sync_pulse.eq(sync_pulse >> 1) with m.If(sync_pulse[0]): m.d.usb += state_gray.eq(0b11) m.next = "SEND_DATA" with m.Else(): m.d.usb += state_gray.eq(0b01) with m.State('SEND_DATA'): with m.If(~self.i_oe & shifter.o_empty & ~bitstuff.o_stall): with m.If(bitstuff.o_will_stall): m.next = 'STUFF_LAST_BIT' with m.Else(): m.d.usb += state_gray.eq(0b10) m.next = 'IDLE' with m.Else(): m.d.usb += state_gray.eq(0b11) with m.State('STUFF_LAST_BIT'): m.d.usb += state_gray.eq(0b10) m.next = 'IDLE' # 48MHz domain # NRZI encoding nrzi_dat = Signal() nrzi_oe = Signal() # Cross the data from the 12MHz domain to the 48MHz domain cdc_dat = FFSynchronizer(self.fit_dat, nrzi_dat, o_domain="usb_io", stages=3) cdc_oe = FFSynchronizer(self.fit_oe, nrzi_oe, o_domain="usb_io", stages=3) m.submodules += [cdc_dat, cdc_oe] m.d.comb += [ nrzi.i_valid.eq(self.i_bit_strobe), nrzi.i_data.eq(nrzi_dat), nrzi.i_oe.eq(nrzi_oe), self.o_usbp.eq(nrzi.o_usbp), self.o_usbn.eq(nrzi.o_usbn), self.o_oe.eq(nrzi.o_oe), ] return m
def elaborate(self, platform): m = Module() # # Clock/Data recovery. # clock_data_recovery = RxClockDataRecovery(self.i_usbp, self.i_usbn) m.submodules.clock_data_recovery = clock_data_recovery m.d.comb += self.o_bit_strobe.eq(clock_data_recovery.line_state_valid) # # NRZI decoding # m.submodules.nrzi = nrzi = RxNRZIDecoder() m.d.comb += [ nrzi.i_valid.eq(self.o_bit_strobe), nrzi.i_dj.eq(clock_data_recovery.line_state_dj), nrzi.i_dk.eq(clock_data_recovery.line_state_dk), nrzi.i_se0.eq(clock_data_recovery.line_state_se0), ] # # Packet boundary detection. # m.submodules.detect = detect = RxPacketDetect() m.d.comb += [ detect.i_valid.eq(nrzi.o_valid), detect.i_se0.eq(nrzi.o_se0), detect.i_data.eq(nrzi.o_data), ] # # Bitstuff remover. # m.submodules.bitstuff = bitstuff = \ ResetInserter(~detect.o_pkt_active)(RxBitstuffRemover()) m.d.comb += [ bitstuff.i_valid.eq(nrzi.o_valid), bitstuff.i_data.eq(nrzi.o_data), self.o_receive_error.eq(bitstuff.o_error) ] # # 1bit->8bit (1byte) gearing # m.submodules.shifter = shifter = RxShifter(width=8) m.d.comb += [ shifter.reset.eq(detect.o_pkt_end), shifter.i_data.eq(bitstuff.o_data), shifter.i_valid.eq(~bitstuff.o_stall & Past(detect.o_pkt_active, domain="usb_io")), ] # # Clock domain crossing. # flag_start = Signal() flag_end = Signal() flag_valid = Signal() m.submodules.payload_fifo = payload_fifo = AsyncFIFOBuffered( width=8, depth=4, r_domain="usb", w_domain="usb_io") m.d.comb += [ payload_fifo.w_data.eq(shifter.o_data[::-1]), payload_fifo.w_en.eq(shifter.o_put), self.o_data_payload.eq(payload_fifo.r_data), self.o_data_strobe.eq(payload_fifo.r_rdy), payload_fifo.r_en.eq(1) ] m.submodules.flags_fifo = flags_fifo = AsyncFIFOBuffered( width=2, depth=4, r_domain="usb", w_domain="usb_io") m.d.comb += [ flags_fifo.w_data[1].eq(detect.o_pkt_start), flags_fifo.w_data[0].eq(detect.o_pkt_end), flags_fifo.w_en.eq(detect.o_pkt_start | detect.o_pkt_end), flag_start.eq(flags_fifo.r_data[1]), flag_end.eq(flags_fifo.r_data[0]), flag_valid.eq(flags_fifo.r_rdy), flags_fifo.r_en.eq(1), ] # Packet flag signals (in 12MHz domain) m.d.comb += [ self.o_pkt_start.eq(flag_start & flag_valid), self.o_pkt_end.eq(flag_end & flag_valid), ] with m.If(self.o_pkt_start): m.d.usb += self.o_pkt_in_progress.eq(1) with m.Elif(self.o_pkt_end): m.d.usb += self.o_pkt_in_progress.eq(0) return m
def elaborate(self, platform): m = Module() sync_pulse = Signal(8) da_reset_shifter = Signal() da_reset_bitstuff = Signal() # Need to reset the bit stuffer 1 cycle after the shifter. stall = Signal() # These signals are set during the sync pulse sp_reset_bitstuff = Signal() sp_reset_shifter = Signal() sp_bit = Signal() sp_o_data_strobe = Signal() # 12MHz domain bitstuff_valid_data = Signal() # Keep a Gray counter around to smoothly transition between states state_gray = Signal(2) state_data = Signal() state_sync = Signal() tx_pipeline_fsm = FSM() m.submodules.tx_pipeline_fsm = DomainRenamer({"sync": "usb"})(tx_pipeline_fsm) shifter = EnableInserter(~stall)(ResetInserter(da_reset_shifter | sp_reset_shifter)(TxShifter(width=8))) m.submodules.shifter = DomainRenamer({"sync": "usb"})(shifter) bitstuff = ResetInserter(da_reset_bitstuff)(TxBitstuffer()) m.submodules.bitstuff = DomainRenamer({"sync": "usb"})(bitstuff) nrzi = TxNRZIEncoder() m.submodules.nrzi = DomainRenamer({"sync": "usb_io"})(nrzi) m.d.comb += [ shifter.i_data.eq(self.i_data_payload), # Send a data strobe when we're two bits from the end of the sync pulse. # This is because the pipeline takes two bit times, and we want to ensure the pipeline # has spooled up enough by the time we're there. bitstuff.i_data.eq(shifter.o_data), stall.eq(bitstuff.o_stall), sp_bit.eq(sync_pulse[0]), sp_reset_bitstuff.eq(sync_pulse[0]), # The shifter has one clock cycle of latency, so reset it # one cycle before the end of the sync byte. sp_reset_shifter.eq(sync_pulse[1]), sp_o_data_strobe.eq(sync_pulse[5]), state_data.eq(state_gray[0] & state_gray[1]), state_sync.eq(state_gray[0] & ~state_gray[1]), self.fit_oe.eq(state_data | state_sync), self.fit_dat.eq((state_data & shifter.o_data & ~bitstuff.o_stall) | sp_bit), self.o_data_strobe.eq(state_data & shifter.o_get & ~stall & self.i_oe), ] # If we reset the shifter, then o_empty will go high on the next cycle. # m.d.usb += [ # If the shifter runs out of data, percolate the "reset" signal to the # shifter, and then down to the bitstuffer. # da_reset_shifter.eq(~stall & shifter.o_empty & ~da_stalled_reset), # da_stalled_reset.eq(da_reset_shifter), # da_reset_bitstuff.eq(~stall & da_reset_shifter), bitstuff_valid_data.eq(~stall & shifter.o_get & self.i_oe), ] tx_pipeline_fsm.act('IDLE', If(self.i_oe, NextState('SEND_SYNC'), NextValue(sync_pulse, 1 << 7), NextValue(state_gray, 0b01), ).Else( NextValue(state_gray, 0b00), ) ) tx_pipeline_fsm.act('SEND_SYNC', NextValue(sync_pulse, sync_pulse >> 1), If(sync_pulse[0], NextState('SEND_DATA'), NextValue(state_gray, 0b11), ).Else( NextValue(state_gray, 0b01), ), ) tx_pipeline_fsm.act('SEND_DATA', If(~self.i_oe & shifter.o_empty & ~bitstuff.o_stall, If(bitstuff.o_will_stall, NextState('STUFF_LAST_BIT') ).Else( NextValue(state_gray, 0b10), NextState('IDLE'), ) ).Else( NextValue(state_gray, 0b11), ), ) tx_pipeline_fsm.act('STUFF_LAST_BIT', NextValue(state_gray, 0b10), NextState('IDLE'), ) # 48MHz domain # NRZI encoding nrzi_dat = Signal() nrzi_oe = Signal() # Cross the data from the 12MHz domain to the 48MHz domain cdc_dat = FFSynchronizer(self.fit_dat, nrzi_dat, o_domain="usb_io", stages=3) cdc_oe = FFSynchronizer(self.fit_oe, nrzi_oe, o_domain="usb_io", stages=3) m.submodules += [cdc_dat, cdc_oe] m.d.comb += [ nrzi.i_valid.eq(self.i_bit_strobe), nrzi.i_data.eq(nrzi_dat), nrzi.i_oe.eq(nrzi_oe), self.o_usbp.eq(nrzi.o_usbp), self.o_usbn.eq(nrzi.o_usbn), self.o_oe.eq(nrzi.o_oe), ] return m