def elaborate(self, platform): m = Module() # This state machine recognizes sequences of 6 bits and drops the 7th # bit. The fsm implements a counter in a series of several states. # This is intentional to help absolutely minimize the levels of logic # used. drop_bit = Signal(1) with m.FSM(domain="usb_io"): for i in range(6): with m.State(f"D{i}"): with m.If(self.i_valid): with m.If(self.i_data): # Receiving '1' increments the bitstuff counter. m.next = (f"D{i + 1}") with m.Else(): # Receiving '0' resets the bitstuff counter. m.next = "D0" with m.State("D6"): with m.If(self.i_valid): m.d.comb += drop_bit.eq(1) # Reset the bitstuff counter, drop the data. m.next = "D0" m.d.usb_io += [ self.o_data.eq(self.i_data), self.o_stall.eq(drop_bit | ~self.i_valid), self.o_error.eq(drop_bit & self.i_data & self.i_valid), ] return m
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 elaborate(self, platform): m = Module() pkt_start = Signal() pkt_active = Signal() pkt_end = Signal() with m.FSM(domain="usb_io"): for i in range(5): with m.State(f"D{i}"): with m.If(self.i_valid): with m.If(self.i_data | self.i_se0): # Receiving '1' or SE0 early resets the packet start counter. m.next = "D0" with m.Else(): # Receiving '0' increments the packet start counter. m.next = f"D{i + 1}" with m.State("D5"): with m.If(self.i_valid): with m.If(self.i_se0): m.next = "D0" # once we get a '1', the packet is active with m.Elif(self.i_data): m.d.comb += pkt_start.eq(1) m.next = "PKT_ACTIVE" with m.State("PKT_ACTIVE"): m.d.comb += pkt_active.eq(1) with m.If(self.i_valid & self.i_se0): m.d.comb += [pkt_active.eq(0), pkt_end.eq(1)] m.next = "D0" # pass all of the outputs through a pipe stage m.d.comb += [ self.o_pkt_start.eq(pkt_start), self.o_pkt_active.eq(pkt_active), self.o_pkt_end.eq(pkt_end), ] return m
def elaborate(self, platform): m = Module() comb = m.d.comb with m.FSM(): with m.State("WB_SLV_TRY_HANDLE"): comb += self.wb_bus.ack.eq(0) with m.If(self.wb_bus.cyc): self.owner.handle_transaction(m) with m.If(self.owner.get_handled_signal()): m.next = "WB_SLV_DONE" with m.State("WB_SLV_DONE"): dat_r = self.owner.get_dat_r() comb += [ self.wb_bus.dat_r.eq(dat_r), self.wb_bus.ack.eq(1), ] m.next = "WB_SLV_TRY_HANDLE" return m
def elaborate(self, platform): m = Module() # Counter that stores how many cycles we've spent in reset. cycles_in_reset = Signal(range(0, self.reset_length_cycles)) reset_state = 'RESETTING' if self.power_on_reset else 'IDLE' with m.FSM(reset=reset_state, domain='sync') as fsm: # Drive the PHY reset whenever we're in the RESETTING cycle. m.d.comb += [ self.phy_reset.eq(fsm.ongoing('RESETTING')), self.phy_stop.eq(~fsm.ongoing('IDLE')) ] with m.State('IDLE'): m.d.sync += cycles_in_reset.eq(0) # Wait for a reset request. with m.If(self.trigger): m.next = 'RESETTING' # RESETTING: hold the reset line active for the given amount of time with m.State('RESETTING'): m.d.sync += cycles_in_reset.eq(cycles_in_reset + 1) with m.If(cycles_in_reset + 1 == self.reset_length_cycles): m.d.sync += cycles_in_reset.eq(0) m.next = 'DEFERRING_STARTUP' # DEFERRING_STARTUP: Produce a signal that will defer startup for # the provided amount of time. This allows line state to stabilize # before the PHY will start interacting with us. with m.State('DEFERRING_STARTUP'): m.d.sync += cycles_in_reset.eq(cycles_in_reset + 1) with m.If(cycles_in_reset + 1 == self.stop_length_cycles): m.d.sync += cycles_in_reset.eq(0) m.next = 'IDLE' return m
def elaborate(self, platform): m = Module() stuff_bit = Signal() with m.FSM(domain="usb"): for i in range(5): with m.State(f"D{i}"): # Receiving '1' increments the bitstuff counter. with m.If(self.i_data): m.next = f"D{i+1}" # Receiving '0' resets the bitstuff counter. with m.Else(): m.next = "D0" with m.State("D5"): with m.If(self.i_data): # There's a '1', so indicate we might stall on the next loop. m.d.comb += self.o_will_stall.eq(1), m.next = "D6" with m.Else(): m.next = "D0" with m.State("D6"): m.d.comb += stuff_bit.eq(1) m.next = "D0" m.d.comb += [self.o_stall.eq(stuff_bit)] # flop outputs with m.If(stuff_bit): m.d.usb += self.o_data.eq(0), with m.Else(): m.d.usb += self.o_data.eq(self.i_data) return m
def elab(self, m: Module): # Registers accumulate incoming values registers = [Signal(8, name="register{i}") for i in range(3)] waiting_to_send = Signal() # Handle accumulating values with m.FSM(reset="BYTE_0"): with m.State("BYTE_0"): m.d.comb += self.input.ready.eq(1) m.d.sync += registers[0].eq(self.input.payload) with m.If(self.input.is_transferring()): m.next = "BYTE_1" with m.State("BYTE_1"): m.d.comb += self.input.ready.eq(1) m.d.sync += registers[1].eq(self.input.payload) with m.If(self.input.is_transferring()): m.next = "BYTE_2" with m.State("BYTE_2"): m.d.comb += self.input.ready.eq(1) m.d.sync += registers[2].eq(self.input.payload) with m.If(self.input.is_transferring()): m.next = "BYTE_3" with m.State("BYTE_3"): m.d.comb += self.input.ready.eq(~waiting_to_send) with m.If(self.input.is_transferring()): m.d.sync += waiting_to_send.eq(1) m.d.sync += self.output.payload.eq( Cat(registers[0], registers[1], registers[2], self.input.payload)) m.next = "BYTE_0" # Handle output whenever ready m.d.comb += self.output.valid.eq(waiting_to_send) with m.If(self.output.is_transferring()): m.d.sync += waiting_to_send.eq(0)
def elaborate(self, platform): m = Module() jt51 = self.jt51 ready = Signal() valid = Signal() address = Signal(8) data = Signal(8) busy = Signal() m.d.comb += [ self.input_stream.ready.eq(ready), valid.eq(self.input_stream.valid), busy.eq(jt51.dout[7]), ] with m.If(valid & ready): m.d.jt51 += [ address.eq(self.input_stream.payload[8:]), data.eq(self.input_stream.payload[:8]), ready.eq(0), # only read one FIFO entry ] with m.FSM(domain="jt51"): with m.State("IDLE"): # address comes always first m.d.jt51 += [ jt51.wr_n.eq(1), # address comes always first jt51.a0.eq(0), ] with m.If(valid & ~busy): # read a FIFO entry m.d.jt51 += ready.eq(1) m.next = "FIFO_READ" with m.State("FIFO_READ"): # in this cycle, ready is 1 # and the read values appear # in the next cycle m.d.jt51 += ready.eq(0) m.next = "WRITE_ADDRESS" with m.State("WRITE_ADDRESS"): m.d.jt51 += [ jt51.din.eq(address), jt51.wr_n.eq(0), ] m.next = "ADDRESS_DONE" with m.State("ADDRESS_DONE"): m.d.jt51 += jt51.wr_n.eq(1) m.next = "WRITE_DATA" with m.State("WRITE_DATA"): m.d.jt51 += [ jt51.a0.eq(1), jt51.din.eq(data), jt51.wr_n.eq(0), ] m.next = "WAIT_ONE" # if data has been written, it takes one cycle # for the busy signal to appear with m.State("WAIT_ONE"): m.d.jt51 += jt51.wr_n.eq(1) m.next = "IDLE" return m
def elaborate(self, platform) -> Module: m = Module() sync = m.d.sync adat = m.d.adat comb = m.d.comb samples_write_port = self.mem.write_port() samples_read_port = self.mem.read_port(domain='comb') m.submodules += [samples_write_port, samples_read_port] # the highest bit in the FIFO marks a frame border frame_border_flag = 24 m.submodules.transmit_fifo = transmit_fifo = AsyncFIFO(width=25, depth=self._fifo_depth, w_domain="sync", r_domain="adat") # needed for output processing m.submodules.nrzi_encoder = nrzi_encoder = NRZIEncoder() transmitted_frame = Signal(30) transmit_counter = Signal(5) comb += [ self.ready_out .eq(transmit_fifo.w_rdy), self.fifo_level_out .eq(transmit_fifo.w_level), self.adat_out .eq(nrzi_encoder.nrzi_out), nrzi_encoder.data_in .eq(transmitted_frame.bit_select(transmit_counter, 1)), self.underflow_out .eq(0) ] # # Fill the transmit FIFO in the sync domain # channel_counter = Signal(3) # make sure, en is only asserted when explicitly strobed comb += samples_write_port.en.eq(0) write_frame_border = [ transmit_fifo.w_data .eq((1 << frame_border_flag) | self.user_data_in), transmit_fifo.w_en .eq(1) ] with m.FSM(): with m.State("DATA"): with m.If(self.ready_out): with m.If(self.valid_in): comb += [ samples_write_port.data.eq(self.sample_in), samples_write_port.addr.eq(self.addr_in), samples_write_port.en.eq(1) ] with m.If(self.last_in): sync += channel_counter.eq(0) comb += write_frame_border m.next = "COMMIT" # underflow: repeat last frame with m.Elif(transmit_fifo.w_level == 0): sync += channel_counter.eq(0) comb += self.underflow_out.eq(1) comb += write_frame_border m.next = "COMMIT" with m.State("COMMIT"): with m.If(transmit_fifo.w_rdy): comb += [ self.ready_out.eq(0), samples_read_port.addr .eq(channel_counter), transmit_fifo.w_data .eq(samples_read_port.data), transmit_fifo.w_en .eq(1) ] sync += channel_counter.eq(channel_counter + 1) with m.If(channel_counter == 7): m.next = "DATA" # # Read the FIFO and send data in the adat domain # # 4b/5b coding: Every 24 bit channel has 6 nibbles. # 1 bit before the sync pad and one bit before the user data nibble filler_bits = [Const(1, 1) for _ in range(7)] adat += transmit_counter.eq(transmit_counter - 1) comb += transmit_fifo.r_en.eq(0) with m.If(transmit_counter == 0): with m.If(transmit_fifo.r_rdy): comb += transmit_fifo.r_en.eq(1) with m.If(transmit_fifo.r_data[frame_border_flag] == 0): adat += [ transmit_counter.eq(29), # generate the adat data for one channel 0b1dddd1dddd1dddd1dddd1dddd1dddd where d is the PCM audio data transmitted_frame.eq(Cat(zip(list(self.chunks(transmit_fifo.r_data[:25], 4)), filler_bits))) ] with m.Else(): adat += [ transmit_counter.eq(15), # generate the adat sync_pad along with the user_bits 0b100000000001uuuu where u is user_data transmitted_frame.eq((1 << 15) | (1 << 4) | transmit_fifo.r_data[:5]) ] with m.Else(): # this should not happen: panic / stop transmitting. adat += [ transmitted_frame.eq(0x00), transmit_counter.eq(4) ] return m
def elaborate(self, platform): m = Module() # If we're standalone, generate the things we need. if self.standalone: # Create our tokenizer... m.submodules.tokenizer = tokenizer = USBTokenDetector( utmi=self.utmi) m.d.comb += tokenizer.interface.connect(self.tokenizer) # ... and our timer. m.submodules.timer = timer = USBInterpacketTimer() timer.add_interface(self.timer) m.d.comb += timer.speed.eq(self.speed) # Create a data-packet-deserializer, which we'll use to capture the # contents of the setup data packets. m.submodules.data_handler = data_handler = \ USBDataPacketDeserializer(utmi=self.utmi, max_packet_size=8, create_crc_generator=self.standalone) m.d.comb += self.data_crc.connect(data_handler.data_crc) # Instruct our interpacket timer to begin counting when we complete receiving # our setup packet. This will allow us to track interpacket delays. m.d.comb += self.timer.start.eq(data_handler.new_packet) # Keep our output signals de-asserted unless specified. m.d.usb += [ self.packet.received.eq(0), ] with m.FSM(domain="usb"): # IDLE -- we haven't yet detected a SETUP transaction directed at us with m.State('IDLE'): pid_matches = (self.tokenizer.pid == self.SETUP_PID) # If we're just received a new SETUP token addressed to us, # the next data packet is going to be for us. with m.If(pid_matches & self.tokenizer.new_token): m.next = 'READ_DATA' # READ_DATA -- we've just seen a SETUP token, and are waiting for the # data payload of the transaction, which contains the setup packet. with m.State('READ_DATA'): # If we receive a token packet before we receive a DATA packet, # this is a PID mismatch. Bail out and start over. with m.If(self.tokenizer.new_token): m.next = 'IDLE' # If we have a new packet, parse it as setup data. with m.If(data_handler.new_packet): # If we got exactly eight bytes, this is a valid setup packet. with m.If(data_handler.length == 8): # Collect the signals that make up our bmRequestType [USB2, 9.3]. request_type = Cat(self.packet.recipient, self.packet.type, self.packet.is_in_request) m.d.usb += [ # Parse the setup data itself... request_type.eq(data_handler.packet[0]), self.packet.request.eq(data_handler.packet[1]), self.packet.value.eq( Cat(data_handler.packet[2], data_handler.packet[3])), self.packet.index.eq( Cat(data_handler.packet[4], data_handler.packet[5])), self.packet.length.eq( Cat(data_handler.packet[6], data_handler.packet[7])), # ... and indicate that we have new data. self.packet.received.eq(1), ] # We'll now need to wait a receive-transmit delay before initiating our ACK. # Per the USB 2.0 and ULPI 1.1 specifications: # - A HS device needs to wait 8 HS bit periods before transmitting [USB2, 7.1.18.2]. # Each ULPI cycle is 8 HS bit periods, so we'll only need to wait one cycle. # - We'll use our interpacket delay timer for everything else. with m.If(self.timer.tx_allowed | (self.speed == USBSpeed.HIGH)): # If we're a high speed device, we only need to wait for a single ULPI cycle. # Processing delays mean we've already met our interpacket delay; and we can ACK # immediately. m.d.comb += self.ack.eq(1) m.next = "IDLE" # For other cases, handle the interpacket delay by waiting. with m.Else(): m.next = "INTERPACKET_DELAY" # Otherwise, this isn't; and we should ignore it. [USB2, 8.5.3] with m.Else(): m.next = "IDLE" # INTERPACKET -- wait for an inter-packet delay before responding with m.State('INTERPACKET_DELAY'): # ... and once it equals zero, ACK and return to idle. with m.If(self.timer.tx_allowed): m.d.comb += self.ack.eq(1) m.next = "IDLE" return m
def elaborate(self, platform): m = Module() # Memory read and write ports. m.submodules.read = mem_read_port = self.mem.read_port(domain="usb") m.submodules.write = mem_write_port = self.mem.write_port(domain="usb") # Store the memory address of our active packet header, which will store # packet metadata like the packet size. header_location = Signal.like(mem_write_port.addr) write_location = Signal.like(mem_write_port.addr) # Read FIFO status. read_location = Signal.like(mem_read_port.addr) fifo_count = Signal.like(mem_read_port.addr, reset=0) fifo_new_data = Signal() # Current receive status. packet_size = Signal(16) # # Read FIFO logic. # m.d.comb += [ # We have data ready whenever there's data in the FIFO. self.stream.valid.eq((fifo_count != 0) & self.idle), # Our data_out is always the output of our read port... self.stream.payload.eq(mem_read_port.data), self.sampling.eq(mem_write_port.en) ] # Once our consumer has accepted our current data, move to the next address. with m.If(self.stream.ready & self.stream.valid): m.d.usb += read_location.eq(read_location + 1) m.d.comb += mem_read_port.addr.eq(read_location + 1) with m.Else(): m.d.comb += mem_read_port.addr.eq(read_location), # # FIFO count handling. # fifo_full = (fifo_count == self.mem_size) data_pop = Signal() data_push = Signal() m.d.comb += [ data_pop.eq(self.stream.ready & self.stream.valid), data_push.eq(fifo_new_data & ~fifo_full) ] # If we have both a read and a write, don't update the count, # as we've both added one and subtracted one. with m.If(data_push & data_pop): pass # Otherwise, add when data's added, and subtract when data's removed. with m.Elif(data_push): m.d.usb += fifo_count.eq(fifo_count + 1) with m.Elif(data_pop): m.d.usb += fifo_count.eq(fifo_count - 1) # # Core analysis FSM. # with m.FSM(domain="usb") as f: m.d.comb += [ self.idle.eq(f.ongoing("IDLE")), self.overrun.eq(f.ongoing("OVERRUN")), self.capturing.eq(f.ongoing("CAPTURE")), ] # IDLE: wait for an active receive. with m.State("IDLE"): # Wait until a transmission is active. # TODO: add triggering logic? with m.If(self.utmi.rx_active): m.next = "CAPTURE" m.d.usb += [ header_location.eq(write_location), write_location.eq(write_location + self.HEADER_SIZE_BYTES), packet_size.eq(0), ] # Capture data until the packet is complete. with m.State("CAPTURE"): byte_received = self.utmi.rx_valid & self.utmi.rx_active # Capture data whenever RxValid is asserted. m.d.comb += [ mem_write_port.addr.eq(write_location), mem_write_port.data.eq(self.utmi.rx_data), mem_write_port.en.eq(byte_received), fifo_new_data.eq(byte_received), ] # Advance the write pointer each time we receive a bit. with m.If(byte_received): m.d.usb += [ write_location.eq(write_location + 1), packet_size.eq(packet_size + 1) ] # If this would be filling up our data memory, # move to the OVERRUN state. with m.If(fifo_count == self.mem_size - 1 - self.HEADER_SIZE_BYTES): m.next = "OVERRUN" # If we've stopped receiving, move to the "finalize" state. with m.If(~self.utmi.rx_active): m.next = "EOP_1" # Optimization: if we didn't receive any data, there's no need # to create a packet. Clear our header from the FIFO and disarm. with m.If(packet_size == 0): m.next = "IDLE" m.d.usb += [write_location.eq(header_location)] with m.Else(): m.next = "EOP_1" # EOP: handle the end of the relevant packet. with m.State("EOP_1"): # Now that we're done, add the header to the start of our packet. # This will take two cycles, currently, as we're using a 2-byte header, # but we only have an 8-bit write port. m.d.comb += [ mem_write_port.addr.eq(header_location), mem_write_port.data.eq(packet_size[8:16]), mem_write_port.en.eq(1), fifo_new_data.eq(1) ] m.next = "EOP_2" with m.State("EOP_2"): # Add the second byte of our header. # Note that, if this is an adjacent read, we should have # just captured our packet header _during_ the stop turnaround. m.d.comb += [ mem_write_port.addr.eq(header_location + 1), mem_write_port.data.eq(packet_size[0:8]), mem_write_port.en.eq(1), fifo_new_data.eq(1) ] m.next = "IDLE" # BABBLE -- handles the case in which we've received a packet beyond # the allowable size in the USB spec with m.State("BABBLE"): # Trap here, for now. pass with m.State("OVERRUN"): # TODO: we should probably set an overrun flag and then emit an EOP, here? pass 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. 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
def elaborate(self, platform): m = Module() # Shortcuts. interface = self.interface out_stream = interface.tx new_frame = interface.tokenizer.new_frame targeting_ep_num = ( interface.tokenizer.endpoint == self._endpoint_number) targeting_us = targeting_ep_num & interface.tokenizer.is_in data_requested = targeting_us & interface.tokenizer.ready_for_response # Track our state in our transmission. bytes_left_in_frame = Signal.like(self.bytes_in_frame) bytes_left_in_packet = Signal(range(0, self._max_packet_size + 1), reset=self._max_packet_size - 1) next_data_pid = Signal(2) # Reset our state at the start of each frame. with m.If(new_frame): m.d.usb += [ # Latch in how many bytes we'll be transmitting this frame. bytes_left_in_frame.eq(self.bytes_in_frame), # And start with a full packet to transmit. bytes_left_in_packet.eq(self._max_packet_size) ] # If it'll take more than two packets to send our data, start off with DATA2. # We'll follow with DATA1 and DATA0. with m.If(self.bytes_in_frame > (2 * self._max_packet_size)): m.d.usb += next_data_pid.eq(2) # Otherwise, if we need two, start with DATA1. with m.Elif(self.bytes_in_frame > self._max_packet_size): m.d.usb += next_data_pid.eq(1) # Otherwise, we'll start (and end) with DATA0. with m.Else(): m.d.usb += next_data_pid.eq(0) m.d.comb += [ # Always pass our ``value`` directly through to our transmitter. # We'll provide ``address``/``next_address`` to our user code to help # orchestrate this timing. out_stream.payload.eq(self.value), # Provide our data pid through to to the transmitter. interface.tx_pid_toggle.eq(next_data_pid) ] # # Core sequencing FSM. # with m.FSM(domain="usb"): # IDLE -- the host hasn't yet requested data from our endpoint. with m.State("IDLE"): m.d.usb += [ # Remain targeting the first byte in our frame. self.address.eq(0), out_stream.first.eq(0) ] m.d.comb += self.next_address.eq(0) # Once the host requests a packet from us... with m.If(data_requested): # If we have data to send, send it. with m.If(bytes_left_in_frame): m.d.usb += out_stream.first.eq(1) m.next = "SEND_DATA" # Otherwise, we'll send a ZLP. with m.Else(): m.next = "SEND_ZLP" # SEND_DATA -- our primary data-transmission state; handles packet transmission with m.State("SEND_DATA"): last_byte_in_packet = (bytes_left_in_packet <= 1) last_byte_in_frame = (bytes_left_in_frame <= 1) byte_terminates_send = last_byte_in_packet | last_byte_in_frame m.d.comb += [ # Our data is always valid in this state... out_stream.valid.eq(1), # ... and we're terminating our packet if we're on the last byte of it. out_stream.last.eq(byte_terminates_send), ] # ``address`` should always move to the value presented in # ``next_address`` on each clock edge. m.d.usb += self.address.eq(self.next_address) # By default, don't advance. m.d.comb += self.next_address.eq(self.address) # We'll advance each time our data is accepted. with m.If(out_stream.ready): m.d.usb += out_stream.first.eq(0) # Mark the relevant byte as sent... m.d.usb += [ bytes_left_in_frame.eq(bytes_left_in_frame - 1), bytes_left_in_packet.eq(bytes_left_in_packet - 1), ] # ... and advance to the next address. m.d.comb += self.next_address.eq(self.address + 1) # If we've just completed transmitting a packet, or we've # just transmitted a full frame, end our transmission. with m.If(byte_terminates_send): m.d.usb += [ # Move to the next DATA pid, which is always one DATA PID less. # [USB2.0: 5.9.2]. We'll reset this back to its maximum value when # the next frame starts. next_data_pid.eq(next_data_pid - 1), # Mark our next packet as being a full one. bytes_left_in_packet.eq(self._max_packet_size) ] m.next = "IDLE" # SEND_ZLP -- sends a zero-length packet, and then return to idle. with m.State("SEND_ZLP"): # We'll request a ZLP by strobing LAST and VALID without strobing FIRST. m.d.comb += [ out_stream.valid.eq(1), out_stream.last.eq(1), ] m.next = "IDLE" return m
def elaborate(self, platform): m = Module() bit_stuffing_disabled = (self.op_mode == self.OP_MODE_NO_BIT_STUFFING) with m.FSM(domain="usb") as fsm: # Mark ourselves as busy whenever we're not in idle. m.d.comb += self.busy.eq(~fsm.ongoing('IDLE')) # IDLE: our transmitter is ready and with m.State('IDLE'): m.d.comb += self.ulpi_stp.eq(0) # Start once a transmit is started, and we can access the bus. with m.If(self.tx_valid & self.bus_idle): # If bit-stuffing is disabled, we'll need to prefix our transmission with a NOPID command. # In this case, we'll never accept the first byte (as we're not ready to transmit it, yet), # and thus TxReady will always be 0. with m.If(bit_stuffing_disabled): m.d.usb += self.ulpi_out_req.eq(1), m.d.comb += [ self.ulpi_data_out .eq(self.TRANSMIT_COMMAND), self.tx_ready .eq(0) ] # Otherwise, this transmission starts with a PID. Extract the PID from the first data byte # and present it as part of the Transmit Command. In this case, the NXT signal is # has the same meaning as the UTMI TxReady signal; and can be passed along directly. with m.Else(): m.d.usb += self.ulpi_out_req.eq(1), m.d.comb += [ self.ulpi_data_out .eq(self.TRANSMIT_COMMAND | self.tx_data[0:4]), self.tx_ready .eq(self.ulpi_nxt) ] # Once the PHY has accepted the command byte, we're ready to move into our main transmit state. with m.If(self.ulpi_nxt): m.next = 'TRANSMIT' # TRANSMIT: we're in the body of a transmit; the UTMI and ULPI interface signals # are roughly equivalent; we'll just pass them through. with m.State('TRANSMIT'): m.d.comb += [ self.ulpi_data_out .eq(self.tx_data), self.tx_ready .eq(self.ulpi_nxt), self.ulpi_stp .eq(0), ] # Once the transmission has ended, we'll need to issue a ULPI stop. with m.If(~self.tx_valid): m.d.usb += self.ulpi_out_req.eq(0), m.next = 'IDLE' # STOP: our packet has just terminated; we'll generate a ULPI stop event for a single cycle. # [ULPI: 3.8.2.2] m.d.comb += self.ulpi_stp.eq(1) # If we've disabled bit stuffing, we'll want to termainate by generating a bit-stuff error. with m.If(bit_stuffing_disabled): # Drive 0xFF as we stop, to generate a bit-stuff error. [ULPI: 3.8.2.3] m.d.comb += self.ulpi_data_out .eq(0xFF) # Otherwise, we'll generate a normal stop. with m.Else(): m.d.comb += self.ulpi_data_out .eq(0) return m
def elaborate(self, platform): m = Module() # # Delayed input and output. # if self.in_skew is not None: data_in = delay(m, self.bus.dq.i, self.in_skew) else: data_in = self.bus.dq.i data_oe = self.bus.dq.oe if self.out_skew is not None: data_out = Signal.like(self.bus.dq.o) delay(m, data_out, self.out_skew, out=self.bus.dq.o) else: data_out = self.bus.dq.o # # Transaction clock generator. # advance_clock = Signal() reset_clock = Signal() if self.clock_skew is not None: out_clock = Signal() delay(m, out_clock, self.clock_skew, out=self.bus.clk) else: out_clock = self.bus.clk with m.If(reset_clock): m.d.sync += out_clock.eq(0) with m.Elif(advance_clock): m.d.sync += out_clock.eq(~out_clock) # # Latched control/addressing signals. # is_read = Signal() is_register = Signal() current_address = Signal(32) is_multipage = Signal() # # FSM datapath signals. # # Tracks whether we need to add an extra latency period between our # command and the data body. extra_latency = Signal() # Tracks how many cycles of latency we have remaining between a command # and the relevant data stages. latency_edges_remaining = Signal(range(0, self.HIGH_LATENCY_EDGES + 1)) # One cycle delayed version of RWDS. # This is used to detect edges in RWDS during reads, which semantically mean # we should accept new data. last_rwds = Signal.like(self.bus.rwds.i) m.d.sync += last_rwds.eq(self.bus.rwds.i) # Create a sync-domain version of our 'new data ready' signal. new_data_ready = self.new_data_ready # # Core operation FSM. # # Provide defaults for our control/status signals. m.d.sync += [ advance_clock.eq(1), reset_clock.eq(0), new_data_ready.eq(0), self.bus.cs.eq(1), self.bus.rwds.oe.eq(0), self.bus.dq.oe.eq(0), ] with m.FSM() as fsm: # IDLE state: waits for a transaction request with m.State('IDLE'): m.d.sync += reset_clock.eq(1) m.d.comb += self.idle.eq(1) # Once we have a transaction request, latch in our control # signals, and assert our chip-select. with m.If(self.start_transfer): m.next = 'LATCH_RWDS' m.d.sync += [ is_read.eq(~self.perform_write), is_register.eq(self.register_space), is_multipage.eq(~self.single_page), current_address.eq(self.address), ] with m.Else(): m.d.sync += self.bus.cs.eq(0) # LATCH_RWDS -- latch in the value of the RWDS signal, which determines # our read/write latency. Note that we advance the clock in this state, # as our out-of-phase clock signal will output the relevant data before # the next edge can occur. with m.State("LATCH_RWDS"): m.d.sync += extra_latency.eq(self.bus.rwds.i), m.next = "SHIFT_COMMAND0" # Commands, in order of bytes sent: # - WRBAAAAA # W => selects read or write; 1 = read, 0 = write # R => selects register or memory; 1 = register, 0 = memory # B => selects burst behavior; 0 = wrapped, 1 = linear # AAAAA => address bits [27:32] # # - AAAAAAAA => address bits [19:27] # - AAAAAAAA => address bits [11:19] # - AAAAAAAA => address bits [ 3:16] # - 00000000 => [reserved] # - 00000AAA => address bits [ 0: 3] # SHIFT_COMMANDx -- shift each of our command bytes out with m.State('SHIFT_COMMAND0'): m.next = 'SHIFT_COMMAND1' # Build our composite command byte. command_byte = Cat(current_address[27:32], is_multipage, is_register, is_read) # Output our first byte of our command. m.d.sync += [data_out.eq(command_byte), data_oe.eq(1)] # Note: it's felt that this is more readable with each of these # states defined explicitly. If you strongly disagree, feel free # to PR a for-loop, here.~ with m.State('SHIFT_COMMAND1'): m.d.sync += [ data_out.eq(current_address[19:27]), data_oe.eq(1) ] m.next = 'SHIFT_COMMAND2' with m.State('SHIFT_COMMAND2'): m.d.sync += [ data_out.eq(current_address[11:19]), data_oe.eq(1) ] m.next = 'SHIFT_COMMAND3' with m.State('SHIFT_COMMAND3'): m.d.sync += [data_out.eq(current_address[3:16]), data_oe.eq(1)] m.next = 'SHIFT_COMMAND4' with m.State('SHIFT_COMMAND4'): m.d.sync += [data_out.eq(0), data_oe.eq(1)] m.next = 'SHIFT_COMMAND5' with m.State('SHIFT_COMMAND5'): m.d.sync += [data_out.eq(current_address[0:3]), data_oe.eq(1)] # If we have a register write, we don't need to handle # any latency. Move directly to our SHIFT_DATA state. with m.If(is_register & ~is_read): m.next = 'WRITE_DATA_MSB' # Otherwise, react with either a short period of latency # or a longer one, depending on what the RAM requested via # RWDS. with m.Else(): m.next = "HANDLE_LATENCY" with m.If(extra_latency): m.d.sync += latency_edges_remaining.eq( self.HIGH_LATENCY_EDGES) with m.Else(): m.d.sync += latency_edges_remaining.eq( self.LOW_LATENCY_EDGES) # HANDLE_LATENCY -- applies clock edges until our latency period is over. with m.State('HANDLE_LATENCY'): m.d.sync += latency_edges_remaining.eq( latency_edges_remaining - 1) with m.If(latency_edges_remaining == 0): with m.If(is_read): m.next = 'READ_DATA_MSB' with m.Else(): m.next = 'WRITE_DATA_MSB' # STREAM_DATA_MSB -- scans in or out the first byte of data with m.State('READ_DATA_MSB'): # If RWDS has changed, the host has just sent us new data. with m.If(self.bus.rwds.i != last_rwds): m.d.sync += self.read_data[8:16].eq(data_in) m.next = 'READ_DATA_LSB' # STREAM_DATA_LSB -- scans in or out the second byte of data with m.State('READ_DATA_LSB'): # If RWDS has changed, the host has just sent us new data. # Sample it, and indicate that we now have a valid piece of new data. with m.If(self.bus.rwds.i != last_rwds): m.d.sync += [ self.read_data[0:8].eq(data_in), new_data_ready.eq(1) ] # If our controller is done with the transcation, end it. with m.If(self.final_word): m.next = 'RECOVERY' m.d.sync += advance_clock.eq(0) with m.Else(): #m.next = 'READ_DATA_MSB' m.next = 'RECOVERY' # WRITE_DATA_MSB -- write the first of our two bytes of data to the to the PSRAM with m.State("WRITE_DATA_MSB"): m.d.sync += [ data_out.eq(self.write_data[8:16]), data_oe.eq(1), ] m.next = "WRITE_DATA_LSB" # WRITE_DATA_LSB -- write the first of our two bytes of data to the to the PSRAM with m.State("WRITE_DATA_LSB"): m.d.sync += [ data_out.eq(self.write_data[0:8]), data_oe.eq(1), ] m.next = "WRITE_DATA_LSB" # If we just finished a register write, we're done -- there's no need for recovery. with m.If(is_register): m.next = 'IDLE' m.d.sync += advance_clock.eq(0) with m.Elif(self.final_word): m.next = 'RECOVERY' m.d.sync += advance_clock.eq(0) with m.Else(): #m.next = 'READ_DATA_MSB' m.next = 'RECOVERY' # RECOVERY state: wait for the required period of time before a new transaction with m.State('RECOVERY'): m.d.sync += [self.bus.cs.eq(0), advance_clock.eq(0)] # TODO: implement recovery m.next = 'IDLE' return m
def elaborate(self, platform): m = Module() if platform and self.top: board_spi = platform.request("debug_spi") spi2 = synchronize(m, board_spi) m.d.comb += self.spi.connect(spi2) if self.platform: platform = self.platform spi = self.spi interf = SPICommandInterface(command_size=COMMAND_BYTES * 8, word_size=WORD_BYTES * 8) m.d.comb += interf.spi.connect(spi) m.submodules.interf = interf # FIFO connection fifo = TransactionalizedFIFO(width=MEMWIDTH, depth=platform.memdepth) if platform.name == 'Test': self.fifo = fifo m.submodules.fifo = fifo m.d.comb += [ self.read_data.eq(fifo.read_data), fifo.read_commit.eq(self.read_commit), fifo.read_discard.eq(self.read_discard), fifo.read_en.eq(self.read_en), self.empty.eq(fifo.empty) ] # Parser mtrcntr = Signal(range(platform.motors)) wordsreceived = Signal(range(wordsinmove(platform) + 1)) error = Signal() # Peripheral state state = Signal(8) m.d.sync += [ state[STATE.PARSING].eq(self.parse), state[STATE.FULL].eq(fifo.space_available <= 1), state[STATE.ERROR].eq(self.dispatcherror | error) ] # remember which word we are processing instruction = Signal(8) with m.FSM(reset='RESET', name='parser'): with m.State('RESET'): m.d.sync += [ self.parse.eq(1), wordsreceived.eq(0), error.eq(0) ] m.next = 'WAIT_COMMAND' with m.State('WAIT_COMMAND'): with m.If(interf.command_ready): word = Cat(state[::-1], self.pinstate[::-1]) with m.If(interf.command == COMMANDS.EMPTY): m.next = 'WAIT_COMMAND' with m.Elif(interf.command == COMMANDS.START): m.next = 'WAIT_COMMAND' m.d.sync += self.parse.eq(1) with m.Elif(interf.command == COMMANDS.STOP): m.next = 'WAIT_COMMAND' m.d.sync += self.parse.eq(0) with m.Elif(interf.command == COMMANDS.WRITE): m.d.sync += interf.word_to_send.eq(word) with m.If(state[STATE.FULL] == 0): m.next = 'WAIT_WORD' with m.Else(): m.next = 'WAIT_COMMAND' with m.Elif(interf.command == COMMANDS.READ): m.d.sync += interf.word_to_send.eq(word) m.next = 'WAIT_COMMAND' with m.Elif(interf.command == COMMANDS.POSITION): # position is requested multiple times for multiple # motors with m.If(mtrcntr < platform.motors - 1): m.d.sync += mtrcntr.eq(mtrcntr + 1) with m.Else(): m.d.sync += mtrcntr.eq(0) m.d.sync += interf.word_to_send.eq( self.position[mtrcntr]) m.next = 'WAIT_COMMAND' with m.State('WAIT_WORD'): with m.If(interf.word_complete): byte0 = interf.word_received[:8] with m.If(wordsreceived == 0): with m.If((byte0 > 0) & (byte0 < 6)): m.d.sync += [ instruction.eq(byte0), fifo.write_en.eq(1), wordsreceived.eq(wordsreceived + 1), fifo.write_data.eq(interf.word_received) ] m.next = 'WRITE' with m.Else(): m.d.sync += error.eq(1) m.next = 'WAIT_COMMAND' with m.Else(): m.d.sync += [ fifo.write_en.eq(1), wordsreceived.eq(wordsreceived + 1), fifo.write_data.eq(interf.word_received) ] m.next = 'WRITE' with m.State('WRITE'): m.d.sync += fifo.write_en.eq(0) wordslaser = wordsinscanline( params(platform)['BITSINSCANLINE']) wordsmotor = wordsinmove(platform) with m.If(((instruction == INSTRUCTIONS.MOVE) & (wordsreceived >= wordsmotor)) | (instruction == INSTRUCTIONS.WRITEPIN) | (instruction == INSTRUCTIONS.LASTSCANLINE) | ((instruction == INSTRUCTIONS.SCANLINE) & (wordsreceived >= wordslaser))): m.d.sync += [wordsreceived.eq(0), fifo.write_commit.eq(1)] m.next = 'COMMIT' with m.Else(): m.next = 'WAIT_COMMAND' with m.State('COMMIT'): m.d.sync += fifo.write_commit.eq(0) m.next = 'WAIT_COMMAND' return m
def elaborate(self, platform): m = Module() usbp = Signal() usbn = Signal() oe = Signal() # wait for new packet to start with m.FSM(domain="usb_io"): with m.State("IDLE"): m.d.comb += [ usbp.eq(1), usbn.eq(0), oe.eq(0), ] with m.If(self.i_valid & self.i_oe): # first bit of sync always forces a transition, we idle # in J so the first output bit is K. m.next = "DK" # the output line is in state J with m.State("DJ"): m.d.comb += [ usbp.eq(1), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): with m.If(~self.i_oe): m.next = "SE0A" with m.Elif(self.i_data): m.next = "DJ" with m.Else(): m.next = "DK" # the output line is in state K with m.State("DK"): m.d.comb += [ usbp.eq(0), usbn.eq(1), oe.eq(1), ] with m.If(self.i_valid): with m.If(~self.i_oe): m.next = "SE0A" with m.Elif(self.i_data): m.next = "DK" with m.Else(): m.next = "DJ" # first bit of the SE0 state with m.State("SE0A"): m.d.comb += [ usbp.eq(0), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): m.next = "SE0B" # second bit of the SE0 state with m.State("SE0B"): m.d.comb += [ usbp.eq(0), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): m.next = "EOPJ" # drive the bus back to J before relinquishing control with m.State("EOPJ"): m.d.comb += [ usbp.eq(1), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): m.next = "IDLE" m.d.usb_io += [ self.o_oe.eq(oe), self.o_usbp.eq(usbp), self.o_usbn.eq(usbn), ] return m
def elaborate(self, platform): m = Module() # Parser parser = SPIParser(self.platform) m.submodules.parser = parser # Busy used to detect move or scanline in action # disabled "dispatching" busy = Signal() # Polynomal Move polynomal = Polynomal(self.platform) m.submodules.polynomal = polynomal if platform: board_spi = platform.request("debug_spi") spi = synchronize(m, board_spi) laserheadpins = platform.request("laserscanner") steppers = [res for res in get_all_resources(platform, "stepper")] bldc = platform.request("bldc") leds = [res.o for res in get_all_resources(platform, "led")] assert len(steppers) != 0 else: platform = self.platform self.spi = SPIBus() self.parser = parser self.pol = polynomal spi = synchronize(m, self.spi) self.laserheadpins = platform.laserhead self.steppers = steppers = platform.steppers self.busy = busy laserheadpins = platform.laserhead bldc = platform.bldc leds = platform.leds # Local laser signal clones enable_prism = Signal() lasers = Signal(2) # Laserscan Head if self.simdiode: laserhead = DiodeSimulator(platform=platform, addfifo=False) lh = laserhead m.d.comb += [ lh.enable_prism_in.eq(enable_prism | lh.enable_prism), lh.laser0in.eq(lasers[0] | lh.lasers[0]), laserhead.laser1in.eq(lasers[1] | lh.lasers[1]) ] else: laserhead = Laserhead(platform=platform) m.d.comb += laserhead.photodiode.eq(laserheadpins.photodiode) m.submodules.laserhead = laserhead if platform.name == 'Test': self.laserhead = laserhead # polynomal iterates over count coeffcnt = Signal(range(len(polynomal.coeff) + 1)) # Prism motor prism_driver = Driver(platform) m.submodules.prism_driver = prism_driver # connect prism motor for idx in range(len(leds)): m.d.comb += leds[idx].eq(prism_driver.leds[idx]) m.d.comb += prism_driver.enable_prism.eq(enable_prism) m.d.comb += [ bldc.uL.eq(prism_driver.uL), bldc.uH.eq(prism_driver.uH), bldc.vL.eq(prism_driver.vL), bldc.vH.eq(prism_driver.vH), bldc.wL.eq(prism_driver.wL), bldc.wH.eq(prism_driver.wH) ] m.d.comb += [ prism_driver.hall[0].eq(bldc.sensor0), prism_driver.hall[1].eq(bldc.sensor1), prism_driver.hall[2].eq(bldc.sensor2) ] # connect laserhead m.d.comb += [ # TODO: fix removal # laserheadpins.pwm.eq(laserhead.pwm), # laserheadpins.en.eq(laserhead.enable_prism | enable_prism), laserheadpins.laser0.eq(laserhead.lasers[0] | lasers[0]), laserheadpins.laser1.eq(laserhead.lasers[1] | lasers[1]), ] # connect Parser m.d.comb += [ self.read_data.eq(parser.read_data), laserhead.read_data.eq(parser.read_data), laserhead.empty.eq(parser.empty), self.empty.eq(parser.empty), parser.read_commit.eq(self.read_commit | laserhead.read_commit), parser.read_en.eq(self.read_en | laserhead.read_en), parser.read_discard.eq(self.read_discard | laserhead.read_discard) ] # connect motors for idx, stepper in enumerate(steppers): step = (polynomal.step[idx] & ((stepper.limit == 0) | stepper.dir)) if idx != (list(platform.stepspermm.keys()).index( platform.laser_axis)): direction = polynomal.dir[idx] m.d.comb += [ stepper.step.eq(step), stepper.dir.eq(direction), parser.pinstate[idx].eq(stepper.limit) ] # connect the motor in which the laserhead moves to laser core else: m.d.comb += [ parser.pinstate[idx].eq(stepper.limit), stepper.step.eq((step & (~laserhead.process_lines)) | (laserhead.step & (laserhead.process_lines))), stepper.dir.eq( (polynomal.dir[idx] & (~laserhead.process_lines)) | (laserhead.dir & (laserhead.process_lines))) ] m.d.comb += (parser.pinstate[len(steppers):].eq( Cat(laserhead.photodiode_t, laserhead.synchronized))) # update position stepper_d = Array(Signal() for _ in range(len(steppers))) for idx, stepper in enumerate(steppers): pos = parser.position[idx] m.d.sync += stepper_d[idx].eq(stepper.step) with m.If(stepper.limit == 1): m.d.sync += parser.position[idx].eq(0) # assuming position is signed # TODO: this might eat LUT, optimize pos_max = pow(2, pos.width - 1) - 2 with m.Elif((pos > pos_max) | (pos < -pos_max)): m.d.sync += parser.position[idx].eq(0) with m.Elif((stepper.step == 1) & (stepper_d[idx] == 0)): with m.If(stepper.dir): m.d.sync += pos.eq(pos + 1) with m.Else(): m.d.sync += pos.eq(pos - 1) # Busy signal m.d.comb += busy.eq(polynomal.busy | laserhead.process_lines) # connect spi m.d.comb += parser.spi.connect(spi) # pins you can write to pins = Cat(lasers, enable_prism, laserhead.synchronize) with m.FSM(reset='RESET', name='dispatcher'): with m.State('RESET'): m.next = 'WAIT_INSTRUCTION' m.d.sync += pins.eq(0) with m.State('WAIT_INSTRUCTION'): m.d.sync += [self.read_commit.eq(0), polynomal.start.eq(0)] with m.If((self.empty == 0) & parser.parse & (busy == 0)): m.d.sync += self.read_en.eq(1) m.next = 'PARSEHEAD' # check which instruction we r handling with m.State('PARSEHEAD'): byte0 = self.read_data[:8] m.d.sync += self.read_en.eq(0) with m.If(byte0 == INSTRUCTIONS.MOVE): m.d.sync += [ polynomal.ticklimit.eq(self.read_data[8:]), coeffcnt.eq(0) ] m.next = 'MOVE_POLYNOMAL' with m.Elif(byte0 == INSTRUCTIONS.WRITEPIN): m.d.sync += [ pins.eq(self.read_data[8:]), self.read_commit.eq(1) ] m.next = 'WAIT' with m.Elif((byte0 == INSTRUCTIONS.SCANLINE) | (byte0 == INSTRUCTIONS.LASTSCANLINE)): m.d.sync += [ self.read_discard.eq(1), laserhead.synchronize.eq(1), laserhead.expose_start.eq(1) ] m.next = 'SCANLINE' with m.Else(): m.next = 'ERROR' m.d.sync += parser.dispatcherror.eq(1) with m.State('MOVE_POLYNOMAL'): with m.If(coeffcnt < len(polynomal.coeff)): with m.If(self.read_en == 0): m.d.sync += self.read_en.eq(1) with m.Else(): m.d.sync += [ polynomal.coeff[coeffcnt].eq(self.read_data), coeffcnt.eq(coeffcnt + 1), self.read_en.eq(0) ] with m.Else(): m.next = 'WAIT' m.d.sync += [polynomal.start.eq(1), self.read_commit.eq(1)] with m.State('SCANLINE'): m.d.sync += [ self.read_discard.eq(0), laserhead.expose_start.eq(0) ] m.next = 'WAIT' # NOTE: you need to wait for busy to be raised # in time with m.State('WAIT'): m.d.sync += polynomal.start.eq(0) m.next = 'WAIT_INSTRUCTION' # NOTE: system never recovers user must reset with m.State('ERROR'): m.next = 'ERROR' return m
def elaborate(self, platform): m = Module() in_stream = self.unprocessed_stream out_stream = self.processed_stream # We'll buffer a single byte of the stream, so we can always be one byte ahead. buffered_byte = Signal(8) is_first_byte = Signal() buffered_complete = Signal() buffered_invalid = Signal() with m.FSM(domain='usb'): # WAIT_FOR_FIRST_BYTE -- we're not actively receiving data, yet. Wait for the # first byte of a new packet. with m.State('WAIT_FOR_FIRST_BYTE'): m.d.usb += out_stream.valid.eq(0) m.d.usb += [ # We have no data to output, so this can't be our first or last bytes... self.first.eq(0), self.last.eq(0), out_stream.next.eq(0), # ... and we can't have gotten a complete or invalid strobe that matters to us. buffered_complete.eq(0), buffered_invalid.eq(0), self.complete_out.eq(0), self.invalid_out.eq(0), ] # Once we've received our first byte, buffer it, and mark it as our first byte. with m.If(in_stream.valid & in_stream.next): m.d.usb += [ buffered_byte.eq(in_stream.payload), is_first_byte.eq(1) ] m.next = 'RECEIVE_AND_TRANSMIT' # RECEIVE_AND_TRANSMIT -- receive incoming bytes, and transmit our buffered bytes. # We'll transmit one byte per byte received; ensuring we always retain a single byte -- # our last byte. with m.State('RECEIVE_AND_TRANSMIT'): m.d.usb += [out_stream.valid.eq(1), out_stream.next.eq(0)] # Buffer any complete/invalid signals we get while receiving, so we don't output # them before we finish outputting our processed stream. m.d.usb += [ buffered_complete.eq(buffered_complete | self.complete_in), buffered_invalid.eq(buffered_invalid | self.invalid_in) ] # If we get a new byte, emit our buffered byte, and store the incoming byte. with m.If(in_stream.valid & in_stream.next): m.d.usb += [ # Output our buffered byte... out_stream.payload.eq(buffered_byte), out_stream.next.eq(1), # indicate whether our current byte was the first byte captured... self.first.eq(is_first_byte), # ... and store the new, incoming byte. buffered_byte.eq(in_stream.payload), is_first_byte.eq(0) ] # Once we no longer have an active packet, transmit our _last_ byte, # and move back to waiting for an active packet. with m.If(~in_stream.valid): m.d.usb += [ # Output our buffered byte... out_stream.payload.eq(buffered_byte), out_stream.next.eq(1), self.first.eq(is_first_byte), # ... and indicate that it's the last byte in our stream. self.last.eq(1) ] m.next = 'OUTPUT_STROBES' with m.State('OUTPUT_STROBES'): m.d.usb += [ # We've just finished transmitting our processed stream; so clear our data strobes... self.first.eq(0), self.last.eq(0), out_stream.next.eq(0), # ... and output our buffered complete/invalid strobes. self.complete_out.eq(buffered_complete), self.invalid_out.eq(buffered_invalid) ] m.next = 'WAIT_FOR_FIRST_BYTE' if self._domain != "usb": m = DomainRenamer({"usb": self._domain})(m) return m
def elaborate(self, platform): m = Module() dct = self.dct # Pulse generator for prism motor pwmcnt = Signal(range(dct['POLYPERIOD'])) # photodiode_triggered photodiodecnt = Signal(range(dct['TICKSINFACET'] * 2)) triggered = Signal() with m.If(photodiodecnt < (dct['TICKSINFACET'] * 2 - 1)): with m.If(~self.photodiode): m.d.sync += triggered.eq(1) m.d.sync += photodiodecnt.eq(photodiodecnt + 1) with m.Else(): m.d.sync += [ self.photodiode_t.eq(triggered), photodiodecnt.eq(0), triggered.eq(0) ] # step generator, i.e. slowest speed is 1/(2^4-1) stephalfperiod = Signal(dct['BITSINSCANLINE'].bit_length() + 4) stepcnt = Signal.like(stephalfperiod) # pwm is always created but can be deactivated with m.If(pwmcnt == 0): m.d.sync += [ self.pwm.eq(~self.pwm), pwmcnt.eq(dct['POLYPERIOD'] - 1) ] with m.Else(): m.d.sync += pwmcnt.eq(pwmcnt - 1) # Laser FSM # stable thresh is changed, larger at start and then lowered stablethresh = Signal(range(dct['STABLETICKS'])) facetcnt = Signal(range(dct['FACETS'])) lasercnt = Signal(range(dct['LASERTICKS'])) scanbit = Signal(range(dct['BITSINSCANLINE'] + 1)) tickcounter = Signal(range(max(dct['SPINUPTICKS'], dct['STABLETICKS']))) scanlinenumber = Signal(range(255)) photodiode = self.photodiode read_data = self.read_data write_data_2 = self.write_data_2 write_new = Signal.like(write_data_2) read_old = Signal.like(read_data) readbit = Signal(range(MEMWIDTH)) photodiode_d = Signal() lasers = self.lasers if self.platform.name == 'Test': self.stephalfperiod = stephalfperiod self.tickcounter = tickcounter self.scanbit = scanbit self.lasercnt = lasercnt self.facetcnt = facetcnt # Exposure start detector expose_start_d = Signal() m.d.sync += expose_start_d.eq(self.expose_start) with m.If((expose_start_d == 0) & self.expose_start): m.d.sync += [self.process_lines.eq(1), self.expose_finished.eq(0)] with m.FSM(reset='RESET') as laserfsm: with m.State('RESET'): m.d.sync += self.error.eq(0) m.next = 'STOP' with m.State('STOP'): m.d.sync += [ stablethresh.eq(dct['STABLETICKS'] - 1), tickcounter.eq(0), self.synchronized.eq(0), self.enable_prism.eq(0), readbit.eq(0), facetcnt.eq(0), scanbit.eq(0), lasercnt.eq(0), lasers.eq(0) ] with m.If(self.synchronize & (~self.error)): # laser is off, photodiode cannot be triggered with m.If(self.photodiode == 0): m.d.sync += self.error.eq(1) m.next = 'STOP' with m.Else(): m.d.sync += [self.error.eq(0), self.enable_prism.eq(1)] m.next = 'SPINUP' with m.State('SPINUP'): with m.If(tickcounter > dct['SPINUPTICKS'] - 1): # turn on laser m.d.sync += [ self.lasers.eq(int('1' * 2, 2)), tickcounter.eq(0) ] m.next = 'WAIT_STABLE' with m.Else(): m.d.sync += tickcounter.eq(tickcounter + 1) with m.State('WAIT_STABLE'): m.d.sync += photodiode_d.eq(photodiode) with m.If(tickcounter >= stablethresh): m.d.sync += self.error.eq(1) m.next = 'STOP' with m.Elif(~photodiode & ~photodiode_d): m.d.sync += [tickcounter.eq(0), lasers.eq(0)] with m.If( (tickcounter > (dct['TICKSINFACET'] - 1) - dct['JITTERTICKS'])): m.d.sync += [ self.synchronized.eq(1), tickcounter.eq(0) ] with m.If(facetcnt == dct['FACETS'] - 1): m.d.sync += facetcnt.eq(0) with m.Else(): m.d.sync += facetcnt.eq(facetcnt + 1) with m.If(dct['SINGLE_FACET'] & (facetcnt > 0)): m.next = 'WAIT_END' with m.Elif(self.empty | ~self.process_lines): m.next = 'WAIT_END' with m.Else(): # TODO: 10 is too high, should be lower thresh = min(round(10.1 * dct['TICKSINFACET']), dct['STABLETICKS']) m.d.sync += [ stablethresh.eq(thresh), self.read_en.eq(1) ] m.next = 'READ_INSTRUCTION' with m.Else(): m.d.sync += self.synchronized.eq(0) m.next = 'WAIT_END' with m.Else(): m.d.sync += tickcounter.eq(tickcounter + 1) with m.State('READ_INSTRUCTION'): m.d.sync += [ self.read_en.eq(0), tickcounter.eq(tickcounter + 1) ] with m.If(read_data[0:8] == INSTRUCTIONS.SCANLINE): with m.If(scanlinenumber < 255): m.d.sync += scanlinenumber.eq(scanlinenumber + 1) with m.Else(): m.d.sync += scanlinenumber.eq(0) m.d.sync += [ write_data_2.eq(scanlinenumber), self.dir.eq(read_data[8]), stephalfperiod.eq(read_data[9:]) ] m.next = 'WAIT_FOR_DATA_RUN' with m.Elif(read_data == INSTRUCTIONS.LASTSCANLINE): m.d.sync += [ self.expose_finished.eq(1), self.read_commit.eq(1), self.process_lines.eq(0) ] m.next = 'WAIT_END' with m.Else(): m.d.sync += self.error.eq(1) m.next = 'READ_INSTRUCTION' with m.State('WAIT_FOR_DATA_RUN'): m.d.sync += [ tickcounter.eq(tickcounter + 1), readbit.eq(0), scanbit.eq(0), lasercnt.eq(0) ] tickcnt_thresh = int(dct['START%'] * dct['TICKSINFACET']) assert tickcnt_thresh > 0 with m.If(tickcounter >= tickcnt_thresh): m.d.sync += [self.read_en.eq(1), self.write_en_2.eq(1)] m.next = 'DATA_RUN' with m.State('DATA_RUN'): m.d.sync += tickcounter.eq(tickcounter + 1) # NOTE: # readbit is your current position in memory # scanbit current byte position in scanline # lasercnt used to pulse laser at certain freq with m.If(lasercnt == 0): with m.If(stepcnt >= stephalfperiod): m.d.sync += [self.step.eq(~self.step), stepcnt.eq(0)] with m.Else(): m.d.sync += stepcnt.eq(stepcnt + 1) with m.If(scanbit >= dct['BITSINSCANLINE']): m.d.sync += [ self.write_commit_2.eq(1), self.lasers.eq(0) ] with m.If(dct['SINGLE_LINE'] & self.empty): m.d.sync += self.read_discard.eq(1) with m.Else(): m.d.sync += self.read_commit.eq(1) m.next = 'WAIT_END' with m.Else(): m.d.sync += [ lasercnt.eq(dct['LASERTICKS'] - 1), scanbit.eq(scanbit + 1) ] m.d.sync += write_new[0].eq(self.photodiode_2) with m.If(readbit == 0): m.d.sync += [ self.lasers[0].eq(read_data[0]), read_old.eq(read_data >> 1), self.read_en.eq(0), self.write_en_2.eq(0) ] with m.Elif(readbit == MEMWIDTH - 1): m.d.sync += [ write_data_2.eq(write_new), self.lasers[0].eq(read_old[0]) ] with m.Else(): m.d.sync += self.lasers[0].eq(read_old[0]) with m.Else(): m.d.sync += lasercnt.eq(lasercnt - 1) # NOTE: read enable can only be high for 1 cycle # as a result this is done right before the "read" with m.If(lasercnt == 1): with m.If(readbit == 0): m.d.sync += [readbit.eq(readbit + 1)] # final read bit copy memory # move to next address, i.e. byte, if end is reached with m.Elif(readbit == MEMWIDTH - 1): # If fifo is empty it will give errors later # so it can be ignored here # Only grab a new line if more than current # is needed # -1 as counting in python is different with m.If(scanbit < (dct['BITSINSCANLINE'])): m.d.sync += [ self.read_en.eq(1), self.write_en_2.eq(1) ] m.d.sync += readbit.eq(0) with m.Else(): m.d.sync += [ readbit.eq(readbit + 1), read_old.eq(read_old >> 1) ] with m.State('WAIT_END'): m.d.sync += [ tickcounter.eq(tickcounter + 1), self.write_commit_2.eq(0) ] with m.If(dct['SINGLE_LINE'] & self.empty): m.d.sync += self.read_discard.eq(0) with m.Else(): m.d.sync += self.read_commit.eq(0) # -1 as you count till range-1 in python # -2 as you need 1 tick to process with m.If(tickcounter >= round(dct['TICKSINFACET'] - dct['JITTERTICKS'] - 2)): m.d.sync += lasers.eq(int('11', 2)) m.next = 'WAIT_STABLE' # if user disables synhcronization exit with m.If(~self.synchronize): m.next = 'STOP' if self.platform.name == 'Test': self.laserfsm = laserfsm return m
def elaborate(self, platform): m = Module() # Synchronize the USB signals at our I/O boundary. # Despite the assumptions made in ValentyUSB, this line rate recovery FSM # isn't enough to properly synchronize these inputs. We'll explicitly synchronize. sync_dp = synchronize(m, self._usbp, o_domain="usb_io") sync_dn = synchronize(m, self._usbn, o_domain="usb_io") ####################################################################### # Line State Recovery State Machine # # The receive path doesn't use a differential receiver. Because of # this there is a chance that one of the differential pairs will appear # to have changed to the new state while the other is still in the old # state. The following state machine detects transitions and waits an # extra sampling clock before decoding the state on the differential # pair. This transition period will only ever last for one clock as # long as there is no noise on the line. If there is enough noise on # the line then the data may be corrupted and the packet will fail the # data integrity checks. # dpair = Cat(sync_dp, sync_dn) # output signals for use by the clock recovery stage line_state_in_transition = Signal() with m.FSM(domain="usb_io") as fsm: m.d.usb_io += [ self.line_state_se0.eq(fsm.ongoing("SE0")), self.line_state_se1.eq(fsm.ongoing("SE1")), self.line_state_dj.eq(fsm.ongoing("DJ")), self.line_state_dk.eq(fsm.ongoing("DK")), ] # If we are in a transition state, then we can sample the pair and # move to the next corresponding line state. with m.State("DT"): m.d.comb += line_state_in_transition.eq(1) with m.Switch(dpair): with m.Case(0b10): m.next = "DJ" with m.Case(0b01): m.next = "DK" with m.Case(0b00): m.next = "SE0" with m.Case(0b11): m.next = "SE1" # If we are in a valid line state and the value of the pair changes, # then we need to move to the transition state. with m.State("DJ"): with m.If(dpair != 0b10): m.next = "DT" with m.State("DK"): with m.If(dpair != 0b01): m.next = "DT" with m.State("SE0"): with m.If(dpair != 0b00): m.next = "DT" with m.State("SE1"): with m.If(dpair != 0b11): m.next = "DT" ####################################################################### # Clock and Data Recovery # # The DT state from the line state recovery state machine is used to align to # transmit clock. The line state is sampled in the middle of the bit time. # # Example of signal relationships # ------------------------------- # line_state DT DJ DJ DJ DT DK DK DK DK DK DK DT DJ DJ DJ # line_state_valid ________----____________----____________----________----____ # bit_phase 0 0 1 2 3 0 1 2 3 0 1 2 0 1 2 # # We 4x oversample, so make the line_state_phase have # 4 possible values. line_state_phase = Signal(2) m.d.usb_io += self.line_state_valid.eq(line_state_phase == 1) with m.If(line_state_in_transition): m.d.usb_io += [ # re-align the phase with the incoming transition line_state_phase.eq(0), # make sure we never assert valid on a transition self.line_state_valid.eq(0), ] with m.Else(): # keep tracking the clock by incrementing the phase m.d.usb_io += line_state_phase.eq(line_state_phase + 1) return m
def elaborate(self, platform): m = Module() # add 1 MHz clock domain cntr = Signal(range(self.divider)) # pos max_bits = (self.max_steps << self.bit_shift).bit_length() cntrs = Array( Signal(signed(max_bits + 1)) for _ in range(len(self.coeff))) assert max_bits <= 64 ticks = Signal(MOVE_TICKS.bit_length()) if self.top: steppers = [res for res in get_all_resources(platform, "stepper")] assert len(steppers) != 0 for idx, stepper in enumerate(steppers): m.d.comb += [ stepper.step.eq(self.step[idx]), stepper.dir.eq(self.dir[idx]) ] else: self.ticks = ticks self.cntrs = cntrs # steps for motor in range(self.motors): m.d.comb += self.step[motor].eq(cntrs[motor * self.order][self.bit_shift]) # directions counter_d = Array( Signal(signed(max_bits + 1)) for _ in range(self.motors)) for motor in range(self.motors): m.d.sync += counter_d[motor].eq(cntrs[motor * self.order]) # negative case --> decreasing with m.If(counter_d[motor] > cntrs[motor * self.order]): m.d.sync += self.dir[motor].eq(0) # positive case --> increasing with m.Elif(counter_d[motor] < cntrs[motor * self.order]): m.d.sync += self.dir[motor].eq(1) with m.FSM(reset='RESET', name='polynomen'): with m.State('RESET'): m.next = 'WAIT_START' m.d.sync += self.busy.eq(0) with m.State('WAIT_START'): with m.If(self.start): for motor in range(self.motors): coef0 = motor * self.order step_bit = self.bit_shift + 1 m.d.sync += [ cntrs[coef0].eq(cntrs[coef0][:step_bit]), counter_d[motor].eq(counter_d[motor][:step_bit]) ] for degree in range(1, self.order): m.d.sync += cntrs[coef0 + degree].eq(0) m.d.sync += self.busy.eq(1) m.next = 'RUNNING' with m.Else(): m.d.sync += self.busy.eq(0) with m.State('RUNNING'): with m.If((ticks < self.ticklimit) & (cntr >= self.divider - 1)): m.d.sync += [ticks.eq(ticks + 1), cntr.eq(0)] for motor in range(self.motors): order = self.order idx = motor * order op3, op2, op1 = 0, 0, 0 if order > 2: op3 += 3 * 2 * self.coeff[idx + 2] + cntrs[idx + 2] op2 += cntrs[idx + 2] op1 += self.coeff[idx + 2] + cntrs[idx + 2] m.d.sync += cntrs[idx + 2].eq(op3) if order > 1: op2 += (2 * self.coeff[idx + 1] + cntrs[idx + 1]) m.d.sync += cntrs[idx + 1].eq(op2) op1 += (self.coeff[idx + 1] + self.coeff[idx] + cntrs[idx + 1] + cntrs[idx]) m.d.sync += cntrs[idx].eq(op1) with m.Elif(ticks < self.ticklimit): m.d.sync += cntr.eq(cntr + 1) with m.Else(): m.d.sync += ticks.eq(0) m.next = 'WAIT_START' return m
def elaborate(self, platform): m = Module() spi = self.spi sample_edge = Fell(spi.sck, domain="sync") # 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): m = Module() def get_all_resources(name): resources = [] for number in itertools.count(): try: resources.append(platform.request(name, number)) except ResourceError: break return resources if platform and self.top: board_spi = platform.request("debug_spi") leds = [res.o for res in get_all_resources("led")] bldc = platform.request("bldc") # connect to signal out # ALS ie 0 is lees je hoog, als ie laag is lees je hoog m.d.comb += [ leds[0].eq(bldc.sensor), board_spi.sdo.eq(bldc.sensor), self.on.eq(board_spi.sdi) ] else: platform = self.platform bldc = platform.bldc maxcnt = int(platform.laser_var['CRYSTAL_HZ'] / (self.frequency * self.states)) maxrotations = 30 * self.states * self.frequency timer = Signal(maxcnt.bit_length() + 1) rotations = Signal(range(maxrotations)) state = Signal(range(self.states)) with m.FSM(reset='INIT', name='algo'): with m.State('INIT'): m.d.sync += rotations.eq(0) # with m.If(self.on): m.next = 'ROTATION' with m.State('ROTATION'): # state with m.If(timer == maxcnt): m.d.sync += timer.eq(0) # m.d.sync += state.eq(0) with m.If(state == self.states - 1): m.d.sync += state.eq(0) with m.Else(): m.d.sync += state.eq(state + 1) with m.Else(): m.d.sync += timer.eq(timer + 1) # duty cycle with m.If(rotations == maxrotations): m.d.sync += rotations.eq(maxrotations) with m.Else(): m.d.sync += rotations.eq(rotations + 1) # with m.If(~self.on): # m.next = 'INIT' thresh = Signal.like(timer) # with m.If(rotations<(10*self.states*self.frequency)): m.d.comb += thresh.eq(int(maxcnt * self.dutycyclestart)) # with m.If(rotations<(10*self.states*self.frequency)): # m.d.comb += thresh.eq(int(maxcnt*self.dutycyclestart)) # with m.Else(): # m.d.comb += thresh.eq(int(maxcnt*self.dutycyclelong)) # six states and one off state with m.If(timer > thresh): m.d.comb += [ bldc.uL.eq(0), bldc.uH.eq(0), bldc.vL.eq(0), bldc.vH.eq(0), bldc.wL.eq(0), bldc.wH.eq(0) ] with m.Elif(state == 0): m.d.comb += [ bldc.uL.eq(0), bldc.uH.eq(0), bldc.vL.eq(0), bldc.vH.eq(1), bldc.wL.eq(1), bldc.wH.eq(0) ] with m.Elif(state == 1): m.d.comb += [ bldc.uL.eq(1), bldc.uH.eq(0), bldc.vL.eq(0), bldc.vH.eq(1), bldc.wL.eq(0), bldc.wH.eq(0) ] with m.Elif(state == 2): m.d.comb += [ bldc.uL.eq(1), bldc.uH.eq(0), bldc.vL.eq(0), bldc.vH.eq(0), bldc.wL.eq(0), bldc.wH.eq(1) ] with m.Elif(state == 3): m.d.comb += [ bldc.uL.eq(0), bldc.uH.eq(0), bldc.vL.eq(1), bldc.vH.eq(0), bldc.wL.eq(0), bldc.wH.eq(1) ] with m.Elif(state == 4): m.d.comb += [ bldc.uL.eq(0), bldc.uH.eq(1), bldc.vL.eq(1), bldc.vH.eq(0), bldc.wL.eq(0), bldc.wH.eq(0) ] with m.Elif(state == 5): m.d.comb += [ bldc.uL.eq(0), bldc.uH.eq(1), bldc.vL.eq(0), bldc.vH.eq(0), bldc.wL.eq(1), bldc.wH.eq(0) ] return m
def elaborate(self, platform) -> Module: """build the module""" m = Module() sync = m.d.sync comb = m.d.comb nrzidecoder = NRZIDecoder(self.clk_freq) m.submodules.nrzi_decoder = nrzidecoder framedata_shifter = InputShiftRegister(24) m.submodules.framedata_shifter = framedata_shifter output_pulser = EdgeToPulse() m.submodules.output_pulser = output_pulser active_channel = Signal(3) # counts the number of bits output bit_counter = Signal(8) # counts the bit position inside a nibble nibble_counter = Signal(3) # counts, how many 0 bits it got in a row sync_bit_counter = Signal(4) comb += [ nrzidecoder.nrzi_in.eq(self.adat_in), self.synced_out.eq(nrzidecoder.running), self.recovered_clock_out.eq(nrzidecoder.recovered_clock_out), ] with m.FSM(): # wait for SYNC with m.State("WAIT_SYNC"): # reset invalid frame bit to be able to start again with m.If(nrzidecoder.invalid_frame_in): sync += nrzidecoder.invalid_frame_in.eq(0) with m.If(nrzidecoder.running): sync += [ bit_counter.eq(0), nibble_counter.eq(0), active_channel.eq(0), output_pulser.edge_in.eq(0) ] with m.If(nrzidecoder.data_out_en): m.d.sync += sync_bit_counter.eq(Mux(nrzidecoder.data_out, 0, sync_bit_counter + 1)) with m.If(sync_bit_counter == 9): m.d.sync += sync_bit_counter.eq(0) m.next = "READ_FRAME" with m.State("READ_FRAME"): # at which bit of bit_counter to output sample data at output_at = Signal(8) # user bits have been read with m.If(bit_counter == 5): sync += [ # output user bits self.user_data_out.eq(framedata_shifter.value_out[0:4]), # at bit 35 the first channel has been read output_at.eq(35) ] # when each channel has been read, output the channel's sample with m.If((bit_counter > 5) & (bit_counter == output_at)): sync += [ self.output_enable.eq(1), self.addr_out.eq(active_channel), self.sample_out.eq(framedata_shifter.value_out), output_at.eq(output_at + 30), active_channel.eq(active_channel + 1) ] with m.Else(): sync += self.output_enable.eq(0) # we work and count only when we get # a new bit fron the NRZI decoder with m.If(nrzidecoder.data_out_en): comb += [ framedata_shifter.bit_in.eq(nrzidecoder.data_out), # skip sync bit, which is first framedata_shifter.enable_in.eq(~(nibble_counter == 0)) ] sync += [ nibble_counter.eq(nibble_counter + 1), bit_counter.eq(bit_counter + 1), ] # check 4b/5b sync bit with m.If((nibble_counter == 0) & ~nrzidecoder.data_out): sync += nrzidecoder.invalid_frame_in.eq(1) m.next = "WAIT_SYNC" with m.Else(): sync += nrzidecoder.invalid_frame_in.eq(0) with m.If(nibble_counter >= 4): sync += nibble_counter.eq(0) # 239 channel bits and 5 user bits (including sync bits) with m.If(bit_counter >= (239 + 5)): sync += [ bit_counter.eq(0), output_pulser.edge_in.eq(1) ] m.next = "READ_SYNC" with m.Else(): comb += framedata_shifter.enable_in.eq(0) with m.If(~nrzidecoder.running): m.next = "WAIT_SYNC" # read the sync bits with m.State("READ_SYNC"): sync += [ self.output_enable.eq(output_pulser.pulse_out), self.addr_out.eq(active_channel), self.sample_out.eq(framedata_shifter.value_out), ] with m.If(nrzidecoder.data_out_en): sync += [ nibble_counter.eq(0), bit_counter.eq(bit_counter + 1), ] with m.If(bit_counter == 9): comb += [ framedata_shifter.enable_in.eq(0), framedata_shifter.clear_in.eq(1), ] #check last sync bit before sync trough with m.If((bit_counter == 0) & ~nrzidecoder.data_out): sync += nrzidecoder.invalid_frame_in.eq(1) m.next = "WAIT_SYNC" #check all the null bits in the sync trough with m.Elif((bit_counter > 0) & nrzidecoder.data_out): sync += nrzidecoder.invalid_frame_in.eq(1) m.next = "WAIT_SYNC" with m.Elif((bit_counter == 10) & ~nrzidecoder.data_out): sync += [ bit_counter.eq(0), nibble_counter.eq(0), active_channel.eq(0), output_pulser.edge_in.eq(0), nrzidecoder.invalid_frame_in.eq(0) ] m.next = "READ_FRAME" with m.Else(): sync += nrzidecoder.invalid_frame_in.eq(0) with m.If(~nrzidecoder.running): m.next = "WAIT_SYNC" return m
def elaborate(self, platform): m = Module() # Create our core, single-byte-wide endpoint, and attach it directly to our interface. m.submodules.stream_ep = stream_ep = USBStreamInEndpoint( endpoint_number=self._endpoint_number, max_packet_size=self._max_packet_size) stream_ep.interface = self.interface # Create semantic aliases for byte-wise and word-wise streams; # so the code below reads more clearly. byte_stream = stream_ep.stream word_stream = self.stream # We'll put each word to be sent through an shift register # that shifts out words a byte at a time. data_shift = Signal.like(word_stream.payload) # Latched versions of our first and last signals. first_latched = Signal() last_latched = Signal() # Count how many bytes we have left to send. bytes_to_send = Signal(range(0, self._byte_width + 1)) # Always provide our inner transmitter with the least byte of our shift register. m.d.comb += byte_stream.payload.eq(data_shift[0:8]) with m.FSM(domain="usb"): # IDLE: transmitter is waiting for input with m.State("IDLE"): m.d.comb += word_stream.ready.eq(1) # Once we get a send request, fill in our shift register, and start shifting. with m.If(word_stream.valid): m.d.usb += [ data_shift.eq(word_stream.payload), first_latched.eq(word_stream.first), last_latched.eq(word_stream.last), bytes_to_send.eq(self._byte_width - 1), ] m.next = "TRANSMIT" # TRANSMIT: actively send each of the bytes of our word with m.State("TRANSMIT"): m.d.comb += byte_stream.valid.eq(1) # Once the byte-stream is accepting our input... with m.If(byte_stream.ready): is_first_byte = (bytes_to_send == self._byte_width - 1) is_last_byte = (bytes_to_send == 0) # Pass through our First and Last signals, but only on the first and # last bytes of our word, respectively. m.d.comb += [ byte_stream.first.eq(first_latched & is_first_byte), byte_stream.last.eq(last_latched & is_last_byte) ] # ... if we have bytes left to send, move to the next one. with m.If(bytes_to_send > 0): m.d.usb += [ bytes_to_send.eq(bytes_to_send - 1), data_shift.eq(data_shift[8:]), ] # Otherwise, complete the frame. with m.Else(): m.d.comb += word_stream.ready.eq(1) # If we still have data to send, move to the next byte... with m.If(self.stream.valid): m.d.usb += [ data_shift.eq(word_stream.payload), first_latched.eq(word_stream.first), last_latched.eq(word_stream.last), bytes_to_send.eq(self._byte_width - 1), ] # ... otherwise, move to our idle state. with m.Else(): m.next = "IDLE" return m
def elaborate(self, platform): m = Module() current_address = Signal(6) current_write = Signal(8) # Keep our control signals low unless explicitly asserted. m.d.usb += [ self.ulpi_out_req.eq(0), self.ulpi_stop .eq(0), self.done .eq(0) ] with m.FSM(domain="usb") as fsm: # We're busy whenever we're not IDLE; indicate so. m.d.comb += self.busy.eq(~fsm.ongoing('IDLE')) # IDLE: wait for a request to be made with m.State('IDLE'): # Apply a NOP whenever we're idle. # # This doesn't technically help for normal ULPI # operation, as the controller should handle this, # but it cleans up the output in our tests and allows # this unit to be used standalone. m.d.usb += self.ulpi_data_out.eq(0) # Constantly latch in our arguments while IDLE. # We'll stop latching these in as soon as we're busy. m.d.usb += [ current_address .eq(self.address), current_write .eq(self.write_data) ] with m.If(self.read_request): m.next = 'START_READ' with m.If(self.write_request): m.next = 'START_WRITE' # # Read handling. # # START_READ: wait for the bus to be idle, so we can transmit. with m.State('START_READ'): # Wait for the bus to be idle. with m.If(~self.ulpi_dir): m.next = 'SEND_READ_ADDRESS' # Once it is, start sending our command. m.d.usb += [ self.ulpi_data_out .eq(self.COMMAND_REG_READ | self.address), self.ulpi_out_req .eq(1) ] # SEND_READ_ADDRESS: Request sending the read address, which we # start sending on the next clock cycle. Note that we don't want # to come into this state writing, as we need to lead with a # bus-turnaround cycle. with m.State('SEND_READ_ADDRESS'): m.d.usb += self.ulpi_out_req.eq(1) # If DIR has become asserted, we're being interrupted. # We'll have to restart the read after the interruption is over. with m.If(self.ulpi_dir): m.next = 'START_READ' m.d.usb += self.ulpi_out_req.eq(0) # If NXT becomes asserted without us being interrupted by # DIR, then the PHY has accepted the read. Release our write # request, so the next cycle can properly act as a bus turnaround. with m.Elif(self.ulpi_next): m.d.usb += [ self.ulpi_out_req .eq(0), self.ulpi_data_out .eq(0), ] m.next = 'READ_TURNAROUND' # READ_TURNAROUND: wait for the PHY to take control of the ULPI bus. with m.State('READ_TURNAROUND'): # After one cycle, we should have a data byte ready. m.next = 'READ_COMPLETE' # READ_COMPLETE: the ULPI read exchange is complete, and the read data is ready. with m.State('READ_COMPLETE'): m.next = 'IDLE' # Latch in the data, and indicate that we have new, valid data. m.d.usb += [ self.read_data .eq(self.ulpi_data_in), self.done .eq(1) ] # # Write handling. # # START_WRITE: wait for the bus to be idle, so we can transmit. with m.State('START_WRITE'): # Wait for the bus to be idle. with m.If(~self.ulpi_dir): m.next = 'SEND_WRITE_ADDRESS' # Once it is, start sending our command. m.d.usb += [ self.ulpi_data_out .eq(self.COMMAND_REG_WRITE | self.address), self.ulpi_out_req .eq(1) ] # SEND_WRITE_ADDRESS: Continue sending the write address until the # target device accepts it. with m.State('SEND_WRITE_ADDRESS'): m.d.usb += self.ulpi_out_req.eq(1) # If DIR has become asserted, we're being interrupted. # We'll have to restart the write after the interruption is over. with m.If(self.ulpi_dir): m.next = 'START_WRITE' m.d.usb += self.ulpi_out_req.eq(0) # Hold our address until the PHY has accepted the command; # and then move to presenting the PHY with the value to be written. with m.Elif(self.ulpi_next): m.d.usb += self.ulpi_data_out.eq(self.write_data) m.next = 'HOLD_WRITE' # Hold the write data on the bus until the device acknowledges it. with m.State('HOLD_WRITE'): m.d.usb += self.ulpi_out_req.eq(1) # Handle interruption. with m.If(self.ulpi_dir): m.next = 'START_WRITE' m.d.usb += self.ulpi_out_req.eq(0) # Hold the data present until the device has accepted it. # Once it has, pulse STP for a cycle to complete the transaction. with m.Elif(self.ulpi_next): m.d.usb += [ self.ulpi_data_out.eq(0), self.ulpi_stop.eq(1), ] m.next = 'STOPPING' with m.State('STOPPING'): m.d.usb += self.ulpi_stop.eq(0) # Check again for interruption since DIR may have # been asserted during the previous cycle. with m.If(self.ulpi_dir): m.next = 'START_WRITE' m.d.usb += self.ulpi_out_req.eq(0) with m.Else(): m.d.usb += [ self.ulpi_out_req.eq(0), self.done.eq(1) ] m.next = 'IDLE' 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() # # 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