def spi_edge_detectors(self, m): """ Generates edge detectors for the sample and output clocks, based on the current SPI mode. Returns: sample_edge, output_edge -- signals that pulse high for a single cycle when we should sample and change our outputs, respectively """ # Select whether we're working with an inverted or un-inverted serial clock. serial_clock = Signal() if self.clock_polarity: m.d.comb += serial_clock.eq(~self.spi.sck) else: m.d.comb += serial_clock.eq(self.spi.sck) # Generate the leading and trailing edge detectors. # Note that we use rising and falling edge detectors, but call these leading and # trailing edges, as our clock here may have been inverted. leading_edge = Rose(serial_clock) trailing_edge = Fell(serial_clock) # Determine the sample and output edges based on the SPI clock phase. sample_edge = trailing_edge if self.clock_phase else leading_edge output_edge = leading_edge if self.clock_phase else trailing_edge return sample_edge, output_edge
def elaborate(self, platform): bus = self.bus stream = self.stream m = Module() # Output SCLK asynchronously, to be passed on to dependent TX blocks. m.d.comb += [ self.recovered_clock.eq(bus.sclk), ] # Configure outputs and output enables. # SYNC and SERCLK are driven by framer in this mode. m.d.comb += [ bus.sync.o.eq(0), bus.sync.oe.eq(0), bus.serclk.o.eq(0), bus.serclk.oe.eq(0), ] # Register inputs. sync = Signal() crcsync = Signal() casync = Signal() serclk = Signal() ser = Signal() sclk = Signal() m.d.sync += [ sync.eq(bus.sync.i), crcsync.eq(bus.crcsync), casync.eq(bus.casync), serclk.eq(bus.serclk.i), ser.eq(bus.ser), sclk.eq(bus.sclk), ] # Falling edge of SERCLK captures other inputs on this interface. capture_strobe = Fell(serclk) # (E1 mode only) CASYNC output is high during first bit of an E1 CAS multiframe. e1_cas_multiframe_strobe = Signal() m.d.sync += [ stream.data_strobe.eq(0), stream.frame_strobe.eq(0), stream.multiframe_strobe.eq(0), e1_cas_multiframe_strobe.eq(0), ] with m.If(capture_strobe): m.d.sync += [ stream.data.eq(ser), stream.data_strobe.eq(1), stream.frame_strobe.eq(sync), stream.multiframe_strobe.eq(crcsync), e1_cas_multiframe_strobe.eq(casync), ] return m
def elaborate(self, platform): bus = self.bus stream = self.stream m = Module() # Configure outputs and output enables. m.d.comb += [ # Output base rate clock (wherever it comes from) to framer's SERCLK pin. bus.serclk.o.eq(self.base_rate_clock), bus.serclk.oe.eq(self.enable), bus.sync.o.eq(0), bus.sync.oe.eq(0), bus.msync.o.eq(0), bus.msync.oe.eq(0), ] # Register inputs. serclk = Signal() sync = Signal() msync = Signal() # Synchronize input signals. m.d.sync += [ # Framer SERCLK input is driven by FPGA SERCLK output. serclk.eq(bus.serclk.i), sync.eq(bus.sync.i), msync.eq(bus.msync.i), ] # Capture signals on the rising edge of TxSERCLKn. capture_strobe = Fell(serclk) m.d.sync += [ stream.data_strobe.eq(0), stream.frame_strobe.eq(0), stream.multiframe_strobe.eq(0), ] with m.If(capture_strobe): m.d.sync += [ stream.data_strobe.eq(1), stream.frame_strobe.eq(sync), stream.multiframe_strobe.eq(msync), ] m.d.comb += [ bus.ser.eq(stream.data), ] return m
def elaborate(self, platform): m = Module() # # Sequence tracking. # # Keep track of which sequence number we expect to see. expected_sequence_number = Signal(self.SEQUENCE_NUMBER_WIDTH) # Keep track of which credit we'll need to issue next... next_credit_to_issue = Signal(range(self._buffer_count)) # ... and which header we'll need to ACK next. # We'll start with the maximum number, so our first advertisement wraps us back around to zero. next_header_to_ack = Signal.like(expected_sequence_number, reset=-1) # # Task "queues". # # Keep track of how many header received acknowledgements (LGOODs) we need to send. acks_to_send = Signal(range(self._buffer_count + 1), reset=1) enqueue_ack = Signal() dequeue_ack = Signal() with m.If(enqueue_ack & ~dequeue_ack): m.d.ss += acks_to_send.eq(acks_to_send + 1) with m.If(dequeue_ack & ~enqueue_ack): m.d.ss += acks_to_send.eq(acks_to_send - 1) # Keep track of how many link credits we've yet to free. # We'll start with every one of our buffers marked as "pending free"; this ensures # we perform our credit restoration properly. credits_to_issue = Signal.like(acks_to_send, reset=self._buffer_count) enqueue_credit_issue = Signal() dequeue_credit_issue = Signal() with m.If(enqueue_credit_issue & ~dequeue_credit_issue): m.d.ss += credits_to_issue.eq(credits_to_issue + 1) with m.If(dequeue_credit_issue & ~enqueue_credit_issue): m.d.ss += credits_to_issue.eq(credits_to_issue - 1) # Keep track of whether we should be sending an LBAD. lbad_pending = Signal() # Keep track of whether a retry has been requested. retry_pending = Signal() with m.If(self.retry_required): m.d.ss += retry_pending.eq(1) # Keep track of whether a keepalive has been requested. keepalive_pending = Signal() with m.If(self.keepalive_required): m.d.ss += keepalive_pending.eq(1) # Keep track of whether we're expected to send an power state response. lau_pending = Signal() lxu_pending = Signal() lpma_pending = Signal() with m.If(self.accept_power_state): m.d.ss += lau_pending.eq(1) with m.If(self.reject_power_state): m.d.ss += lxu_pending.eq(1) with m.If(self.acknowledge_power_state): m.d.ss += lpma_pending.eq(1) # # Header Packet Buffers # # Track which buffer will be filled next. read_pointer = Signal(range(self._buffer_count)) write_pointer = Signal.like(read_pointer) # Track how many buffers we currently have in use. buffers_filled = Signal.like(credits_to_issue, reset=0) reserve_buffer = Signal() release_buffer = Signal() with m.If(reserve_buffer & ~release_buffer): m.d.ss += buffers_filled.eq(buffers_filled + 1) with m.If(release_buffer & ~reserve_buffer): m.d.ss += buffers_filled.eq(buffers_filled - 1) # Create buffers to receive any incoming header packets. buffers = Array(HeaderPacket() for _ in range(self._buffer_count)) # # Packet reception (physical layer -> link layer). # # Flag that determines when we should ignore packets. # # After a receive error, we'll want to ignore all packets until we see a "retry" # link command; so we don't receive packets out of order. ignore_packets = Signal() # Create our raw packet parser / receiver. m.submodules.receiver = rx = RawHeaderPacketReceiver() m.d.comb += [ # Our receiver passively monitors the data received for header packets. rx.sink.tap(self.sink), # Ensure it's always up to date about what sequence numbers we expect. rx.expected_sequence.eq(expected_sequence_number), # If we ever get a bad header packet sequence, we're required to retrain # the link [USB3.2r1: 7.2.4.1.5]. Pass the event through directly. self.recovery_required.eq(rx.bad_sequence & ~ignore_packets), # Notify the link layer when packets are received, for keeping track of our timers. self.packet_received.eq(rx.new_packet), # Notify the link layer if any bad packets are received; for diagnostics. self.bad_packet_received.eq(rx.bad_packet) ] # If we receive a valid packet, it's time for us to buffer it! with m.If(rx.new_packet & ~ignore_packets): m.d.ss += [ # Load our header packet into the next write buffer... buffers[write_pointer].eq(rx.packet), # ... advance to the next buffer and sequence number... write_pointer.eq(write_pointer + 1), expected_sequence_number.eq(expected_sequence_number + 1), ] m.d.comb += [ # ... mark the buffer space as occupied by valid data ... reserve_buffer.eq(1), # ... and queue an ACK for this packet. enqueue_ack.eq(1) ] # If we receive a bad packet, we'll need to request that the other side re-send. # The rules for this are summarized in [USB3.2r1: 7.2.4.1.5], and in comments below. with m.If(rx.bad_packet & ~ignore_packets): m.d.ss += [ # First, we'll need to schedule transmission of an LBAD, which notifies the other # side that we received a bad packet; and that it'll need to transmit all unack'd # header packets to us again. lbad_pending.eq(1), # Next, we'll need to make sure we don't receive packets out of sequence. This means # we'll have to start ignoring packets until the other side responds to the LBAD. # The other side respond with an Retry link command (LRTY) once it's safe for us to # pay attention to packets again. ignore_packets.eq(1) ] # Finally, if we receive a Retry link command, this means we no longer need to ignore packets. # This typically happens in response to us sending an LBAD and marking future packets as ignored. with m.If(self.retry_received): m.d.ss += ignore_packets.eq(0) # # Packet delivery (link layer -> physical layer). # m.d.comb += [ # As long as we have at least one buffer filled, we have header packets pending. self.queue.valid.eq(buffers_filled > 0), # Always provide the value of our oldest packet out to our consumer. self.queue.header.eq(buffers[read_pointer]) ] # If the protocol layer is marking one of our packets as consumed, we no longer # need to buffer it -- it's the protocol layer's problem, now! with m.If(self.queue.valid & self.queue.ready): # Move on to reading from the next buffer in sequence. m.d.ss += read_pointer.eq(read_pointer + 1) m.d.comb += [ # First, we'll free the buffer associated with the relevant packet... release_buffer.eq(1), # ... and request that our link partner be notified of the new space. enqueue_credit_issue.eq(1) ] # # Automatic credit expiration. # # FIXME: implement this! # # Link command generation. # m.submodules.lc_generator = lc_generator = LinkCommandGenerator() m.d.comb += [ self.source.stream_eq(lc_generator.source), self.link_command_sent.eq(lc_generator.done), ] with m.FSM(domain="ss"): # DISPATCH_COMMAND -- the state in which we identify any pending link commands necessary, # and then move to the state in which we'll send them. with m.State("DISPATCH_COMMAND"): with m.If(self.enable): # NOTE: the order below is important; changing it can easily break things: # - ACKS must come before credits, as we must send an LGOOD before we send our initial credits. # - LBAD must come after ACKs and credit management, as all scheduled ACKs need to be # sent to the other side for the LBAD to have the correct semantic meaning. with m.If(retry_pending): m.next = "SEND_LRTY" # If we have acknowledgements to send, send them. with m.Elif(acks_to_send): m.next = "SEND_ACKS" # If we have link credits to issue, move to issuing them to the other side. with m.Elif(credits_to_issue): m.next = "ISSUE_CREDITS" # If we need to send an LBAD, do so. with m.Elif(lbad_pending): m.next = "SEND_LBAD" # If we need to send a link power-state command, do so. with m.Elif(lxu_pending): m.next = "SEND_LXU" # If we need to send a keepalive, do so. with m.Elif(keepalive_pending): m.next = "SEND_KEEPALIVE" # Once we've become disabled, we'll want to prepare for our next enable. # This means preparing for our advertisement, by: with m.If(Fell(self.enable) | self.usb_reset): m.d.ss += [ # -Resetting our pending ACKs to 1, so we perform an sequence number advertisement # when we're next enabled. acks_to_send.eq(1), # -Decreasing our next sequence number; so we maintain a continuity of sequence numbers # without counting the advertising one. This doesn't seem to be be strictly necessary # per the spec; but seem to make analyzers happier, so we'll go with it. next_header_to_ack.eq(next_header_to_ack - 1), # - Clearing all of our buffers. read_pointer.eq(0), write_pointer.eq(0), buffers_filled.eq(0), # - Preparing to re-issue all of our buffer credits. next_credit_to_issue.eq(0), credits_to_issue.eq(self._buffer_count), # - Clear our pending events. retry_pending.eq(0), lbad_pending.eq(0), keepalive_pending.eq(0), ignore_packets.eq(0) ] # If this is a USB Reset, also reset our sequences. with m.If(self.usb_reset): m.d.ss += [ expected_sequence_number.eq(0), next_header_to_ack.eq(-1) ] # SEND_ACKS -- a valid header packet has been received, or we're advertising # our initial sequence number; send an LGOOD packet. with m.State("SEND_ACKS"): # Send an LGOOD command, acknowledging the last received packet header. m.d.comb += [ lc_generator.generate.eq(1), lc_generator.command.eq(LinkCommand.LGOOD), lc_generator.subtype.eq(next_header_to_ack) ] # Wait until our link command is done, and then move on. with m.If(lc_generator.done): # Move to the next header packet in the sequence, and decrease # the number of outstanding ACKs. m.d.comb += dequeue_ack.eq(1) m.d.ss += next_header_to_ack.eq(next_header_to_ack + 1) # If this was the last ACK we had to send, move back to our dispatch state. with m.If(acks_to_send == 1): m.next = "DISPATCH_COMMAND" # ISSUE_CREDITS -- header packet buffers have been freed; and we now need to notify the # other side, so it knows we have buffers available. with m.State("ISSUE_CREDITS"): # Send an LCRD command, indicating that we have a free buffer. m.d.comb += [ lc_generator.generate.eq(1), lc_generator.command.eq(LinkCommand.LCRD), lc_generator.subtype.eq(next_credit_to_issue) ] # Wait until our link command is done, and then move on. with m.If(lc_generator.done): # Move to the next credit... m.d.comb += dequeue_credit_issue.eq(1) m.d.ss += next_credit_to_issue.eq(next_credit_to_issue + 1) # If this was the last credit we had to issue, move back to our dispatch state. with m.If(credits_to_issue == 1): m.next = "DISPATCH_COMMAND" # SEND_LBAD -- we've received a bad header packet; we'll need to let the other side know. with m.State("SEND_LBAD"): m.d.comb += [ lc_generator.generate.eq(1), lc_generator.command.eq(LinkCommand.LBAD), ] # Once we've sent the LBAD, we can mark is as no longer pending and return to our dispatch. # (We can't ever have multiple LBADs queued up; as we ignore future packets after sending one.) with m.If(lc_generator.done): m.d.ss += lbad_pending.eq(0) m.next = "DISPATCH_COMMAND" # SEND_LRTY -- our transmitter has requested that we send an retry indication to the other side. # We'll do our transmitter a favor and do so. with m.State("SEND_LRTY"): m.d.comb += [ lc_generator.generate.eq(1), lc_generator.command.eq(LinkCommand.LRTY) ] with m.If(lc_generator.done): m.d.ss += retry_pending.eq(0) m.next = "DISPATCH_COMMAND" # SEND_KEEPALIVE -- our link layer timer has requested that we send a keep-alive, # indicating that we're still in U0 and the link is still good. Do so. with m.State("SEND_KEEPALIVE"): # Send the correct packet type for the direction our port is facing. command = LinkCommand.LDN if self._is_downstream_facing else LinkCommand.LUP m.d.comb += [ lc_generator.generate.eq(1), lc_generator.command.eq(command) ] # Once we've send the keepalive, we can mark is as no longer pending and return to our dispatch. # (There's no sense in sending repeated keepalives; one gets the message across.) with m.If(lc_generator.done): m.d.ss += keepalive_pending.eq(0) m.next = "DISPATCH_COMMAND" # SEND_LXU -- we're being instructed to reject a requested power-state transfer. # We'll send an LXU packet to inform the other side of the rejection. with m.State("SEND_LXU"): m.d.comb += [ lc_generator.generate.eq(1), lc_generator.command.eq(LinkCommand.LXU) ] with m.If(lc_generator.done): m.d.ss += lxu_pending.eq(0) m.next = "DISPATCH_COMMAND" return m
def elaborate(self, platform): m = Module() # # Credit tracking. # # Keep track of how many transmit credits we currently have. credits_available = Signal(range(self._buffer_count + 1)) credit_received = Signal() credit_consumed = Signal() with m.If(credit_received & ~credit_consumed): m.d.ss += credits_available.eq(credits_available + 1) with m.Elif(credit_consumed & ~credit_received): m.d.ss += credits_available.eq(credits_available - 1) # Provide a flag that indicates when we're done with our full bringup. with m.If(self.enable == 0): m.d.ss += self.bringup_complete.eq(0) with m.Elif(credits_available == self._buffer_count): m.d.ss += self.bringup_complete.eq(1) # # Task "queues". # # Control signals. retire_packet = Signal() # Pending packet count. packets_to_send = Signal(range(self._buffer_count + 1)) enqueue_send = Signal() dequeue_send = Signal() # Un-retired packet count. packets_awaiting_ack = Signal() # If we need to retry sending our packets, we'll need to reset our pending packet count. # Otherwise, we increment and decrement our "to send" counts normally. with m.If(self.retry_required): m.d.ss += packets_to_send.eq(packets_awaiting_ack) with m.Elif(enqueue_send & ~dequeue_send): m.d.ss += packets_to_send.eq(packets_to_send + 1) with m.Elif(dequeue_send & ~enqueue_send): m.d.ss += packets_to_send.eq(packets_to_send - 1) # Track how many packets are yet to be retired. with m.If(enqueue_send & ~retire_packet): m.d.ss += packets_awaiting_ack.eq(packets_awaiting_ack + 1) with m.Elif(retire_packet & ~enqueue_send & packets_awaiting_ack > 0): m.d.ss += packets_awaiting_ack.eq(packets_awaiting_ack - 1) # # Header Packet Buffers # # Track: # - which buffer should be filled next # - which buffer we should send from next # - which buffer we've last acknowledged read_pointer = Signal(range(self._buffer_count)) write_pointer = Signal.like(read_pointer) ack_pointer = Signal.like(read_pointer) # Create buffers to receive any incoming header packets. buffers = Array(HeaderPacket() for _ in range(self._buffer_count)) # If we need to retry sending our packets, we'll need to start reading # again from the last acknowledged packet; so we'll reset our read pointer. with m.If(self.retry_required): m.d.ss += read_pointer.eq(ack_pointer) with m.Elif(dequeue_send): m.d.ss += read_pointer.eq(read_pointer + 1) # Last ACK'd buffer / packet retirement tracker. with m.If(retire_packet): m.d.ss += ack_pointer.eq(ack_pointer + 1) # # Packet acceptance (protocol layer -> link layer). # # If we have link credits available, we're able to accept data from the protocol layer. m.d.comb += self.queue.ready.eq(credits_available != 0) # If the protocol layer is handing us a packet... with m.If(self.queue.valid & self.queue.ready): # ... consume a credit, as we're going to be using up a receiver buffer, and # schedule sending of that packet. m.d.comb += [credit_consumed.eq(1), enqueue_send.eq(1)] # Finally, capture the packet into the buffer for transmission. m.d.ss += [ buffers[write_pointer].eq(self.queue.header), write_pointer.eq(write_pointer + 1) ] # # Packet delivery (link layer -> physical layer) # m.submodules.packet_tx = packet_tx = RawPacketTransmitter() m.d.comb += [ self.source.stream_eq(packet_tx.source), packet_tx.data_sink.stream_eq(self.data_sink) ] # Keep track of the next sequence number we'll need to send... transmit_sequence_number = Signal(self.SEQUENCE_NUMBER_WIDTH) with m.FSM(domain="ss"): # DISPATCH_PACKET -- wait packet transmissions to be scheduled, and prepare # our local transmitter with the proper data to send them. with m.State("DISPATCH_PACKET"): # If we have packets to send, pass them to our transmitter. with m.If(packets_to_send & self.enable): m.d.ss += [ # Grab the packet from our read queue, and pass it to the transmitter; # but override its sequence number field with our current sequence number. packet_tx.header.eq(buffers[read_pointer]), packet_tx.header.sequence_number.eq( transmit_sequence_number), ] # Move on to sending our packet. m.next = "WAIT_FOR_SEND" # WAIT_FOR_SEND -- we've now dispatched our packet; and we're ready to wait for it to be sent. with m.State("WAIT_FOR_SEND"): m.d.comb += packet_tx.generate.eq(1) # Once the packet is done... with m.If(packet_tx.done): m.d.comb += dequeue_send.eq(1) m.d.ss += transmit_sequence_number.eq( transmit_sequence_number + 1) # If this was the last packet we needed to send, resume waiting for one. with m.If(packets_to_send == 1): m.next = "DISPATCH_PACKET" # # Header Packet Timer # # FIXME: implement this # # Link Command Handling # # Core link command receiver. m.submodules.lc_detector = lc_detector = LinkCommandDetector() m.d.comb += [ lc_detector.sink.tap(self.sink), self.link_command_received.eq(lc_detector.new_command) ] # Keep track of which credit we expect to get back next. next_expected_credit = Signal(range(self._buffer_count)) # Keep track of what sequence number we expect to have ACK'd next. next_expected_ack_number = Signal(self.SEQUENCE_NUMBER_WIDTH, reset=-1) # Handle link commands as we receive them. with m.If(lc_detector.new_command): with m.Switch(lc_detector.command): # # Link Credit Reception # with m.Case(LinkCommand.LCRD): # If the credit matches the sequence we're expecting, we can accept it! with m.If(next_expected_credit == lc_detector.subtype): m.d.comb += credit_received.eq(1) # Next time, we'll expect the next credit in the sequence. m.d.ss += next_expected_credit.eq( next_expected_credit + 1) # Otherwise, we've lost synchronization. We'll need to trigger link recovery. with m.Else(): m.d.comb += self.recovery_required.eq(1) # # Packet Acknowledgement # with m.Case(LinkCommand.LGOOD): # If the credit matches the sequence we're expecting, we can accept it! with m.If(next_expected_ack_number == lc_detector.subtype): m.d.comb += retire_packet.eq(1) # Next time, we'll expect the next credit in the sequence. m.d.ss += next_expected_ack_number.eq( next_expected_ack_number + 1) # Otherwise, we've lost synchronization. We'll need to trigger link recovery. with m.Else(): m.d.comb += self.recovery_required.eq(1) # # Packet Negative Acknowledgement # with m.Case(LinkCommand.LBAD): # LBADs aren't sequenced; instead, they require a retry of all unacknowledged # (unretired) packets. Mark ourselves as requiring a retry; which should trigger # our internal logic to execute a retry. m.d.comb += self.retry_required.eq(1) # # Link Partner Retrying Send # with m.Case(LinkCommand.LRTY): # If we see an LRTY, it's an indication that our link partner is performing a # retried send. This information is handled by the Receiver, so we'll forward it along. m.d.comb += self.retry_received.eq(1) # # Link Power State transition request. # with m.Case(LinkCommand.LGO_U): # We don't handle LGO requests locally; so instead, we'll pass this back to the link layer. m.d.comb += [ self.lgo_received.eq(1), self.lgo_target.eq(lc_detector.subtype) ] # # Reset Handling # with m.If(self.usb_reset | Fell(self.enable)): m.d.ss += [ packets_to_send.eq(0), packets_awaiting_ack.eq(0), next_expected_credit.eq(0), read_pointer.eq(0), write_pointer.eq(0), ack_pointer.eq(0), credits_available.eq(0), ] with m.If(self.usb_reset): m.d.ss += [ next_expected_ack_number.eq(0), transmit_sequence_number.eq(0) ] # # Debug outputs. # m.d.comb += [ self.credits_available.eq(credits_available), self.packets_to_send.eq(packets_to_send) ] return m
def elaborate(self, platform): m = Module() spi = self.spi sample_edge = Fell(spi.sck) # Bit counter: counts the number of bits received. max_bit_count = max(self.word_size, self.command_size) bit_count = Signal(range(0, max_bit_count + 1)) # Shift registers for our command and data. current_command = Signal.like(self.command) current_word = Signal.like(self.word_received) # De-assert our control signals unless explicitly asserted. m.d.sync += [self.command_ready.eq(0), self.word_complete.eq(0)] with m.FSM() as fsm: m.d.comb += [ self.idle.eq(fsm.ongoing('IDLE')), self.stalled.eq(fsm.ongoing('STALL')), ] # STALL: entered when we can't accept new bits -- either when # CS starts asserted, or when we've received more data than expected. with m.State("STALL"): # Wait for CS to clear. with m.If(~spi.cs): m.next = 'IDLE' # We ignore all data until chip select is asserted, as that data Isn't For Us (TM). # We'll spin and do nothing until the bus-master addresses us. with m.State('IDLE'): m.d.sync += bit_count.eq(0) with m.If(spi.cs): m.next = 'RECEIVE_COMMAND' # Once CS is low, we'll shift in our command. with m.State('RECEIVE_COMMAND'): # If CS is de-asserted early; our transaction is being aborted. with m.If(~spi.cs): m.next = 'IDLE' # Continue shifting in data until we have a full command. with m.If(bit_count < self.command_size): with m.If(sample_edge): m.d.sync += [ bit_count.eq(bit_count + 1), current_command.eq( Cat(spi.sdi, current_command[:-1])) ] # ... and then pass that command out to our controller. with m.Else(): m.d.sync += [ bit_count.eq(0), self.command_ready.eq(1), self.command.eq(current_command) ] m.next = 'PROCESSING' # Give our controller a wait state to prepare any response they might want to... with m.State('PROCESSING'): m.next = 'LATCH_OUTPUT' # ... and then latch in the response to transmit. with m.State('LATCH_OUTPUT'): m.d.sync += current_word.eq(self.word_to_send) m.next = 'SHIFT_DATA' # Finally, exchange data. with m.State('SHIFT_DATA'): # If CS is de-asserted early; our transaction is being aborted. with m.If(~spi.cs): m.next = 'IDLE' m.d.sync += spi.sdo.eq(current_word[-1]) # Continue shifting data until we have a full word. with m.If(bit_count < self.word_size): with m.If(sample_edge): m.d.sync += [ bit_count.eq(bit_count + 1), current_word.eq(Cat(spi.sdi, current_word[:-1])) ] # ... and then output that word on our bus. with m.Else(): m.d.sync += [ bit_count.eq(0), self.word_complete.eq(1), self.word_received.eq(current_word) ] # Stay in the stall state until CS is de-asserted. m.next = 'STALL' return m
def elaborate(self, platform: Platform) -> Module: m = Module() # UART pins for the AsyncSerial, is needed for doing output enable uart_pins = Record([("rx", [("i", 1)]), ("tx", [("o", 1)])]) # Add the UART resources of the ROCKPro64 platform.add_resources( [Resource("rx_rp64", 0, Pins(self.rx, dir="i"))]) platform.add_resources( [Resource("tx_rp64", 0, Pins(self.tx, dir="oe"))]) rx_pin = platform.request("rx_rp64", 0) tx_pin = platform.request("tx_rp64", 0) m.d.comb += uart_pins.rx.i.eq(rx_pin.i) m.d.comb += tx_pin.o.eq(uart_pins.tx.o) # ROCKPro64 refuses to boot if this is high enable_tx = Signal() m.d.comb += tx_pin.oe.eq(enable_tx) # 1.5 Megabaud UART to the ROCKPro64 uart = AsyncSerial(divisor=int(self.clk / 1.5E6), pins=uart_pins) m.submodules += uart # Send 0x03 (Ctrl C) until the u-boot prompt appears (might take quite a while because apparently it seems to try to connect to the network interface for a long time) # Send 'pci enum' once '=>' is received and init is asserted # Set init_sent to high for 1 cycle after '\n' has been sent # Turn a string into a list of bytes def generate_memory(data_string): result = [] for char in data_string: result.append(ord(char)) return result # Command to send. Don't forget to change depth when changing this command. depth = 10 pci_mem = Memory(width=8, depth=depth, init=generate_memory(' pci enum\n')) pci_rport = m.submodules.pci_rport = pci_mem.read_port() # Hardwired to 1, since boot is not yet controlled m.d.comb += enable_tx.eq(1) # We can always accept data m.d.comb += uart.rx.ack.eq(1) with m.FSM(): with m.State("Wait"): m.d.sync += [ uart.tx.data.eq(0x03), # Spam Ctrl C uart.tx.ack.eq(1), ] # Wait for '=>' with m.If(uart.rx.data == ord('=')): m.next = "uboot-1" with m.State("uboot-1"): m.d.sync += self.init_sent.eq(0) with m.If(uart.rx.data == ord('>')): m.next = "uboot-2" # Arrived at u-boot prompt, ready to sent, waiting for init signal with m.State("uboot-2"): m.d.sync += self.rdy.eq(1) with m.If(self.init): m.next = "send-pci-start" # Go! Set the UART data to what the memory outputs. with m.State("send-pci-start"): m.d.sync += [ self.rdy.eq(0), uart.tx.data.eq(pci_rport.data), pci_rport.addr.eq(0), ] # Once the TX is ready, send data with m.If(uart.tx.rdy): m.next = "send-pci-data" with m.State("send-pci-data"): m.d.sync += uart.tx.data.eq(pci_rport.data) m.d.sync += uart.tx.ack.eq(uart.tx.rdy) # When the TX stops being ready, set the next byte. Doesn't work with 'Rose'. with m.If(Fell(uart.tx.rdy)): m.d.sync += pci_rport.addr.eq(pci_rport.addr + 1) # Once all data has been sent, go back to waiting for '>' and strobe init_sent. with m.If((pci_rport.addr == depth - 1)): m.next = "uboot-1" m.d.sync += self.init_sent.eq(1) uart_pins = platform.request("uart", 0) #m.d.comb += uart_pins.tx.o.eq(tx_pin.o) m.d.comb += uart_pins.tx.o.eq(rx_pin.i) return m