def decode_nrzi(self, m: Module, bit_time: Signal, got_edge: Signal, sync_counter: DividingCounter): """Do the actual decoding of the NRZI bitstream""" sync = m.d.sync bit_counter = Signal(7) # this counter is used to detect a dead signal # to determine when to go back to SYNC state dead_counter = Signal(8) output = Signal(reset=1) # recover ADAT clock with m.If(bit_counter <= (bit_time >> 1)): m.d.comb += self.recovered_clock_out.eq(1) with m.Else(): m.d.comb += self.recovered_clock_out.eq(0) # when the frame decoder got garbage # then we need to go back to SYNC state with m.If(self.invalid_frame_in): sync += [ sync_counter.reset_in.eq(1), bit_counter.eq(0), dead_counter.eq(0) ] m.next = "SYNC" sync += bit_counter.eq(bit_counter + 1) with m.If(got_edge): sync += [ # latch 1 until we read it in the middle of the bit output.eq(1), # resynchronize at each bit edge, 1 to compensate # for sync delay bit_counter.eq(1), # when we get an edge, the signal is alive, reset counter dead_counter.eq(0) ] with m.Else(): sync += dead_counter.eq(dead_counter + 1) # wrap the counter with m.If(bit_counter == bit_time): sync += bit_counter.eq(0) # output at the middle of the bit with m.Elif(bit_counter == (bit_time >> 1)): sync += [ self.data_out.eq(output), self.data_out_en.eq(1), # pulse out_en output.eq(0) # edge has been output, wait for new edge ] with m.Else(): sync += self.data_out_en.eq(0) # when we had no edge for 16 bits worth of time # then we go back to sync state with m.If(dead_counter >= bit_time << 4): sync += dead_counter.eq(0) m.next = "SYNC"
def elaborate(self, platform): m = Module() interface = self.interface tokenizer = interface.tokenizer tx = interface.tx # Counter that stores how many bytes we have left to send. bytes_to_send = Signal(range(0, self._max_packet_size + 1), reset=0) # True iff we're the active endpoint. endpoint_selected = \ tokenizer.is_in & \ (tokenizer.endpoint == self._endpoint_number) \ # Pulses when the host is requesting a packet from us. packet_requested = \ endpoint_selected \ & tokenizer.ready_for_response # # Transmit logic # # Schedule a packet send whenever a packet is requested. with m.If(packet_requested): m.d.usb += bytes_to_send.eq(self._max_packet_size) # Count a byte as send each time the PHY accepts a byte. with m.Elif((bytes_to_send != 0) & tx.ready): m.d.usb += bytes_to_send.eq(bytes_to_send - 1) m.d.comb += [ # Always send our constant value. tx.payload.eq(self._constant), # Send bytes, whenever we have them. tx.valid.eq(bytes_to_send != 0), tx.first.eq(bytes_to_send == self._max_packet_size), tx.last.eq(bytes_to_send == 1) ] # # Data-toggle logic # # Toggle our data pid when we get an ACK. with m.If(interface.handshakes_in.ack & endpoint_selected): m.d.usb += interface.tx_pid_toggle.eq(~interface.tx_pid_toggle) return m
def elaborate(self, platform): m = Module() m.submodules.ila = self.ila transaction_start = Rose(self.spi.cs) # Connect up our SPI transciever to our public interface. interface = SPIDeviceInterface(word_size=self.bits_per_word, clock_polarity=self.clock_polarity, clock_phase=self.clock_phase) m.submodules.spi = interface m.d.comb += [ interface.spi.connect(self.spi), # Always output the captured sample. interface.word_out.eq(self.ila.captured_sample) ] # Count where we are in the current transmission. current_sample_number = Signal(range(0, self.ila.sample_depth)) # Our first piece of data is latched in when the transaction # starts, so we'll move on to sample #1. with m.If(self.spi.cs): with m.If(transaction_start): m.d.sync += current_sample_number.eq(1) # From then on, we'll move to the next sample whenever we're finished # scanning out a word (and thus our current samples are latched in). # This only works correctly because the SPI interface will accept a word # more than one clock cycle after the edge which latches the new address # to read, allowing the backing memory to have a clock to latch in the # correct value. with m.Elif(interface.word_accepted): m.d.sync += current_sample_number.eq(current_sample_number + 1) # Whenever CS is low, we should be providing the very first sample, # so reset our sample counter to 0. with m.Else(): m.d.sync += current_sample_number.eq(0) # Ensure our ILA module outputs the right sample. m.d.sync += [self.ila.captured_sample_number.eq(current_sample_number)] # Convert our sync domain to the domain requested by the user, if necessary. if self.domain != "sync": m = DomainRenamer(self.domain)(m) 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): assert Funct3.SLLI == Funct3.SLL assert Funct3.SRL == Funct3.SRLI assert Funct3.SRA == Funct3.SRAI m = Module() with m.Switch(self.funct3): with m.Case(Funct3.SLL): m.d.comb += self.res.eq(self.src1 << self.shift) with m.Case(Funct3.SRL): assert Funct3.SRL == Funct3.SRA assert Funct7.SRL != Funct7.SRA with m.If(self.funct7 == Funct7.SRL): m.d.comb += self.res.eq(self.src1 >> self.shift) with m.Elif(self.funct7 == Funct7.SRA): m.d.comb += [ self.src1signed.eq(self.src1), self.res.eq(self.src1signed >> self.shift), ] return m
def elaborate(self, platform): m = Module() ac = Signal(self.width+1) ac_next = Signal.like(ac) temp = Signal.like(ac) q1 = Signal(self.width) q1_next = Signal.like(q1) i = Signal(range(self.width)) # combinatorial with m.If(ac >= self.y): m.d.comb += [temp.eq(ac-self.y), Cat(q1_next, ac_next).eq( Cat(1, q1, temp[0:self.width-1]))] with m.Else(): m.d.comb += [Cat(q1_next, ac_next).eq(Cat(q1, ac) << 1)] # synchronized with m.If(self.start): m.d.sync += [self.valid.eq(0), i.eq(0)] with m.If(self.y == 0): m.d.sync += [self.busy.eq(0), self.dbz.eq(1)] with m.Else(): m.d.sync += [self.busy.eq(1), self.dbz.eq(0), Cat(q1, ac).eq(Cat(Const(0, 1), self.x, Const(0, self.width)))] with m.Elif(self.busy): with m.If(i == self.width-1): m.d.sync += [self.busy.eq(0), self.valid.eq(1), i.eq(0), self.q.eq(q1_next), self.r.eq(ac_next >> 1)] with m.Else(): m.d.sync += [i.eq(i+1), ac.eq(ac_next), q1.eq(q1_next)] 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() # 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() 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): 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() 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() state = Signal(3) if platform and self.top: leds = [res.o for res in get_all_resources(platform, "led")] for idx in range(len(self.leds)): m.d.comb += self.leds[idx].eq(leds[idx]) bldc = platform.request("bldc") m.d.comb += self.enable_prism.eq(1) m.d.comb += [ bldc.uL.eq(self.uL), bldc.uH.eq(self.uH), bldc.vL.eq(self.vL), bldc.vH.eq(self.vH), bldc.wL.eq(self.wL), bldc.wH.eq(self.wH) ] m.d.comb += [ self.hall[0].eq(bldc.sensor0), self.hall[1].eq(bldc.sensor1), self.hall[2].eq(bldc.sensor2) ] for idx in range(len(self.leds)): m.d.comb += self.leds[idx].eq(self.hall[idx]) # tested by trying out all possibilities m.d.sync += state.eq(Cat(self.hall[0], self.hall[1], self.hall[2])) target = 200000 max_measurement = target * 10 measurement = Signal(range(max_measurement)) timer = Signal().like(measurement) statefilter = Signal(3) stateold = Signal(3) max_delay = 1000 delay = Signal(range(max_delay)) # Statefilter # The current motor can reach state 0,0,0 # The measurements of the hall sensors are # filtered to prevent this with m.If((state >= 1) & (state <= 6)): m.d.sync += statefilter.eq(state) m.d.sync += stateold.eq(statefilter) # PID controller step = max_delay // 100 with m.If((measurement > target) & (delay >= step)): m.d.sync += delay.eq(delay - step) with m.Elif((measurement < target) & (delay < (max_delay - step))): m.d.sync += delay.eq(delay + step) with m.Else(): m.d.sync += delay.eq(0) # Measure Cycle # Measures the speed of the motor by checking # how often state 1 is reached with m.If(timer >= max_measurement - 1): m.d.sync += [timer.eq(0), measurement.eq(timer)] with m.Elif((statefilter == 1) & (stateold != 1)): m.d.sync += [measurement.eq(timer), timer.eq(0)] with m.Else(): m.d.sync += timer.eq(timer + 1) off = Signal() duty = Signal(range(max_delay)) # Duty cycle # Regulates power to the motor by turning it off # if larger than duty cycle with m.If(duty < max_delay): m.d.sync += duty.eq(0) with m.Else(): m.d.sync += duty.eq(duty + 1) # Motor On / Off with m.If(duty < delay): m.d.sync += off.eq(1) with m.Else(): m.d.sync += off.eq(0) # https://www.mathworks.com/help/mcb/ref/sixstepcommutation.html with m.If((~self.enable_prism) | off): m.d.comb += [ self.uL.eq(0), self.uH.eq(0), self.vL.eq(0), self.vH.eq(0), self.wL.eq(0), self.wH.eq(0) ] with m.Elif(statefilter == 1): # V --> W, 001 m.d.comb += [ self.uL.eq(0), self.uH.eq(0), self.vL.eq(0), self.vH.eq(1), self.wL.eq(1), self.wH.eq(0) ] with m.Elif(statefilter == 3): # V --> U, 011 m.d.comb += [ self.uL.eq(1), self.uH.eq(0), self.vL.eq(0), self.vH.eq(1), self.wL.eq(0), self.wH.eq(0) ] with m.Elif(statefilter == 2): # W --> U, 010 m.d.comb += [ self.uL.eq(1), self.uH.eq(0), self.vL.eq(0), self.vH.eq(0), self.wL.eq(0), self.wH.eq(1) ] with m.Elif(statefilter == 6): # W --> V, 110 m.d.comb += [ self.uL.eq(0), self.uH.eq(0), self.vL.eq(1), self.vH.eq(0), self.wL.eq(0), self.wH.eq(1) ] with m.Elif(statefilter == 4): # U --> V, 100 m.d.comb += [ self.uL.eq(0), self.uH.eq(1), self.vL.eq(1), self.vH.eq(0), self.wL.eq(0), self.wH.eq(0) ] with m.Elif(statefilter == 5): # U --> W, 101 m.d.comb += [ self.uL.eq(0), self.uH.eq(1), self.vL.eq(0), self.vH.eq(0), self.wL.eq(1), self.wH.eq(0) ] # with m.Else(): # disabled # 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)] return m
def elaborate(self, platform): m = Module() # Range shortcuts for internal signals. address_range = range(0, self.depth + 1) # # Core internal "backing store". # memory = Memory(width=self.width, depth=self.depth + 1, name=self.name) m.submodules.read_port = read_port = memory.read_port() m.submodules.write_port = write_port = memory.write_port() # Always connect up our memory's data/en ports to ours. m.d.comb += [ self.read_data.eq(read_port.data), write_port.data.eq(self.write_data), write_port.en.eq(self.write_en & ~self.full) ] # # Write port. # # We'll track two pieces of data: our _committed_ write position, and our current un-committed write one. # This will allow us to rapidly backtrack to our pre-commit position. committed_write_pointer = Signal(address_range) current_write_pointer = Signal(address_range) m.d.comb += write_port.addr.eq(current_write_pointer) # Compute the location for the next write, accounting for wraparound. We'll not assume a binary-sized # buffer; so we'll compute the wraparound manually. next_write_pointer = Signal.like(current_write_pointer) with m.If(current_write_pointer == self.depth): m.d.comb += next_write_pointer.eq(0) with m.Else(): m.d.comb += next_write_pointer.eq(current_write_pointer + 1) # If we're writing to the fifo, update our current write position. with m.If(self.write_en & ~self.full): m.d.sync += current_write_pointer.eq(next_write_pointer) # If we're committing a FIFO write, update our committed position. with m.If(self.write_commit): m.d.sync += committed_write_pointer.eq(current_write_pointer) # If we're discarding our current write, reset our current position, with m.If(self.write_discard): m.d.sync += current_write_pointer.eq(committed_write_pointer) # # Read port. # # We'll track two pieces of data: our _committed_ read position, and our current un-committed read one. # This will allow us to rapidly backtrack to our pre-commit position. committed_read_pointer = Signal(address_range) current_read_pointer = Signal(address_range) # Compute the location for the next read, accounting for wraparound. We'll not assume a binary-sized # buffer; so we'll compute the wraparound manually. next_read_pointer = Signal.like(current_read_pointer) with m.If(current_read_pointer == self.depth): m.d.comb += next_read_pointer.eq(0) with m.Else(): m.d.comb += next_read_pointer.eq(current_read_pointer + 1) # Our memory always takes a single cycle to provide its read output; so we'll update its address # "one cycle in advance". Accordingly, if we're about to advance the FIFO, we'll use the next read # address as our input. If we're not, we'll use the current one. with m.If(self.read_en & ~self.empty): m.d.comb += read_port.addr.eq(next_read_pointer) with m.Else(): m.d.comb += read_port.addr.eq(current_read_pointer) # If we're reading from our the fifo, update our current read position. with m.If(self.read_en & ~self.empty): m.d.sync += current_read_pointer.eq(next_read_pointer) # If we're committing a FIFO write, update our committed position. with m.If(self.read_commit): m.d.sync += committed_read_pointer.eq(current_read_pointer) # If we're discarding our current write, reset our current position, with m.If(self.read_discard): m.d.sync += current_read_pointer.eq(committed_read_pointer) # # FIFO status. # # Our FIFO is empty if our read and write pointers are in the same. We'll use the current # read position (which leads ahead) and the committed write position (which lags behind). m.d.comb += self.empty.eq( current_read_pointer == committed_write_pointer) # For our space available, we'll use the current write position (which leads ahead) and our committed # read position (which lags behind). This yields two cases: one where the buffer isn't wrapped around, # and one where it is. with m.If(self.full): m.d.comb += self.space_available.eq(0) with m.Elif(committed_read_pointer <= current_write_pointer): m.d.comb += self.space_available.eq(self.depth - (current_write_pointer - committed_read_pointer)) with m.Else(): m.d.comb += self.space_available.eq(committed_read_pointer - current_write_pointer - 1) # Our FIFO is full if we don't have any space available. m.d.comb += self.full.eq(next_write_pointer == committed_read_pointer) # If we're not supposed to be in the sync domain, rename our sync domain to the target. if self.domain != "sync": m = DomainRenamer({"sync": self.domain})(m) return m
def elaborate(self, platform): m = Module() # Create the component parts of our ULPI interfacing hardware. m.submodules.register_window = register_window = ULPIRegisterWindow() m.submodules.control_translator = control_translator = ULPIControlTranslator(register_window=register_window) m.submodules.rxevent_decoder = rxevent_decoder = ULPIRxEventDecoder(ulpi_bus=self.ulpi) m.submodules.transmit_translator = transmit_translator = ULPITransmitTranslator() # If we're choosing to honor any registers defined in the platform file, apply those # before continuing with elaboration. if self.use_platform_registers and hasattr(platform, 'ulpi_extra_registers'): for address, value in platform.ulpi_extra_registers.items(): self.add_extra_register(address, value) # Some platforms may need to have a raw clock domain for their I/O; e.g. if they need # to do some simple processing for their internal clock domain. if self.handle_clocking and hasattr(platform, 'ulpi_raw_clock_domain'): raw_clock_domain = platform.ulpi_raw_clock_domain else: raw_clock_domain = 'usb' # Add any extra registers provided by the user to our control translator. for address, values in self._extra_registers.items(): control_translator.add_composite_register(m, address, values['value'], reset_value=values['default']) # Keep track of when any of our components are busy any_busy = \ register_window.busy | \ transmit_translator.busy | \ control_translator.busy | \ self.ulpi.dir # If we're handling ULPI clocking, do so. if self.handle_clocking: # We can't currently handle bidirectional clock lines, as we don't know if they # should be used in input or output modes. if hasattr(self.ulpi.clk, 'oe'): raise TypeError("ULPI records with bidirectional clock lines require manual handling.") # Just Input (TM) and Just Output (TM) clocks are simpler: we know how to drive them. elif hasattr(self.ulpi.clk, 'o'): m.d.comb += self.ulpi.clk.eq(ClockSignal(raw_clock_domain)) elif hasattr(self.ulpi.clk, 'i'): m.d.comb += ClockSignal(raw_clock_domain).eq(self.ulpi.clk) # Clocks that don't seem to be I/O pins aren't what we're expecting; fail out. else: raise TypeError(f"ULPI `clk` was an unexpected type {type(self.ulpi.clk)}." \ " You may need to handle clocking manually.") # Hook up our reset signal iff our ULPI bus has one. if hasattr(self.ulpi, 'rst'): m.d.comb += self.ulpi.rst .eq(ResetSignal(raw_clock_domain)), # Connect our ULPI control signals to each of our subcomponents. m.d.comb += [ # Drive the bus whenever the target PHY isn't. self.ulpi.data.oe .eq(~self.ulpi.dir), # Generate our busy signal. self.busy .eq(any_busy), # Connect our data inputs to the event decoder. # Note that the event decoder is purely passive. rxevent_decoder.register_operation_in_progress.eq(register_window.busy), self.last_rx_command .eq(rxevent_decoder.last_rx_command), # Connect our inputs to our transmit translator. transmit_translator.ulpi_nxt .eq(self.ulpi.nxt), transmit_translator.op_mode .eq(self.op_mode), transmit_translator.bus_idle .eq(~control_translator.busy & ~self.ulpi.dir), transmit_translator.tx_data .eq(self.tx_data), transmit_translator.tx_valid .eq(self.tx_valid), self.tx_ready .eq(transmit_translator.tx_ready), # Connect our inputs to our control translator / register window. control_translator.bus_idle .eq(~transmit_translator.busy), register_window.ulpi_data_in .eq(self.ulpi.data.i), register_window.ulpi_dir .eq(self.ulpi.dir), register_window.ulpi_next .eq(self.ulpi.nxt), ] # Control our the source of our ULPI data output. # If a transmit request is active, prioritize that over # any register reads/writes. with m.If(transmit_translator.ulpi_out_req): m.d.comb += [ self.ulpi.data.o .eq(transmit_translator.ulpi_data_out), self.ulpi.stp .eq(transmit_translator.ulpi_stp) ] # Otherwise, yield control to the register handler. # This is a slight optimization: since it properly generates NOPs # while not in use, we can let it handle idle, as well, saving a mux. with m.Else(): m.d.comb += [ self.ulpi.data.o .eq(register_window.ulpi_data_out), self.ulpi.stp .eq(register_window.ulpi_stop) ] # Connect our RxEvent status signals from our RxEvent decoder. for signal_name, _ in self.RXEVENT_STATUS_SIGNALS: signal = getattr(rxevent_decoder, signal_name) m.d.comb += self.__dict__[signal_name].eq(signal) # Connect our control signals through the control translator. for signal_name, _ in self.CONTROL_SIGNALS: signal = getattr(control_translator, signal_name) m.d.comb += signal.eq(self.__dict__[signal_name]) # RxActive handler: # A transmission starts when DIR goes high with NXT, or when an RxEvent indicates # a switch from RxActive = 0 to RxActive = 1. A transmission stops when DIR drops low, # or when the RxEvent RxActive bit drops from 1 to 0, or an error occurs.A dir_rising_edge = Rose(self.ulpi.dir.i, domain="usb") dir_based_start = dir_rising_edge & self.ulpi.nxt with m.If(~self.ulpi.dir | rxevent_decoder.rx_stop): # TODO: this should probably also trigger if RxError m.d.usb += self.rx_active.eq(0) with m.Elif(dir_based_start | rxevent_decoder.rx_start): m.d.usb += self.rx_active.eq(1) # Data-out: we'll connect this almost direct through from our ULPI # interface, as it's essentially the same as in the UTMI spec. We'll # add a one cycle processing delay so it matches the rest of our signals. # RxValid: equivalent to NXT whenever a Rx is active. m.d.usb += [ self.rx_data .eq(self.ulpi.data.i), self.rx_valid .eq(self.ulpi.nxt & self.rx_active) ] 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() # # Clock/Data recovery. # clock_data_recovery = RxClockDataRecovery(self.i_usbp, self.i_usbn) m.submodules.clock_data_recovery = clock_data_recovery m.d.comb += self.o_bit_strobe.eq(clock_data_recovery.line_state_valid) # # NRZI decoding # m.submodules.nrzi = nrzi = RxNRZIDecoder() m.d.comb += [ nrzi.i_valid.eq(self.o_bit_strobe), nrzi.i_dj.eq(clock_data_recovery.line_state_dj), nrzi.i_dk.eq(clock_data_recovery.line_state_dk), nrzi.i_se0.eq(clock_data_recovery.line_state_se0), ] # # Packet boundary detection. # m.submodules.detect = detect = ResetInserter(self.reset)( RxPacketDetect()) m.d.comb += [ detect.i_valid.eq(nrzi.o_valid), detect.i_se0.eq(nrzi.o_se0), detect.i_data.eq(nrzi.o_data), ] # # Bitstuff remover. # m.submodules.bitstuff = bitstuff = \ ResetInserter(~detect.o_pkt_active)(RxBitstuffRemover()) m.d.comb += [ bitstuff.i_valid.eq(nrzi.o_valid), bitstuff.i_data.eq(nrzi.o_data), self.o_receive_error.eq(bitstuff.o_error) ] # # 1bit->8bit (1byte) gearing # m.submodules.shifter = shifter = RxShifter(width=8) m.d.comb += [ shifter.reset.eq(detect.o_pkt_end), shifter.i_data.eq(bitstuff.o_data), shifter.i_valid.eq(~bitstuff.o_stall & Past(detect.o_pkt_active, domain="usb_io")), ] # # Clock domain crossing. # flag_start = Signal() flag_end = Signal() flag_valid = Signal() m.submodules.payload_fifo = payload_fifo = AsyncFIFOBuffered( width=8, depth=4, r_domain="usb", w_domain="usb_io") m.d.comb += [ payload_fifo.w_data.eq(shifter.o_data[::-1]), payload_fifo.w_en.eq(shifter.o_put), self.o_data_payload.eq(payload_fifo.r_data), self.o_data_strobe.eq(payload_fifo.r_rdy), payload_fifo.r_en.eq(1) ] m.submodules.flags_fifo = flags_fifo = AsyncFIFOBuffered( width=2, depth=4, r_domain="usb", w_domain="usb_io") m.d.comb += [ flags_fifo.w_data[1].eq(detect.o_pkt_start), flags_fifo.w_data[0].eq(detect.o_pkt_end), flags_fifo.w_en.eq(detect.o_pkt_start | detect.o_pkt_end), flag_start.eq(flags_fifo.r_data[1]), flag_end.eq(flags_fifo.r_data[0]), flag_valid.eq(flags_fifo.r_rdy), flags_fifo.r_en.eq(1), ] # Packet flag signals (in 12MHz domain) m.d.comb += [ self.o_pkt_start.eq(flag_start & flag_valid), self.o_pkt_end.eq(flag_end & flag_valid), ] with m.If(self.o_pkt_start): m.d.usb += self.o_pkt_in_progress.eq(1) with m.Elif(self.o_pkt_end): m.d.usb += self.o_pkt_in_progress.eq(0) return m
def elaborate(self, platform) -> 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() # # General state signals. # # Our line state is always taken directly from D- and D+. m.d.comb += self.line_state.eq(Cat(self._io.d_n.i, self._io.d_p.i)) # If we have a ``vbus_valid`` indication, use it to drive our ``vbus_valid`` # signal. Otherwise, we'll pretend ``vbus_valid`` is always true, for compatibility. if hasattr(self._io, 'vbus_valid'): m.d.comb += [ self.vbus_valid.eq(self._io.vbus_valid), self.session_end.eq(~self._io.vbus_valid) ] else: m.d.comb += [self.vbus_valid.eq(1), self.session_end.eq(0)] # # General control signals. # # If we have a pullup signal, drive it based on ``term_select``. if hasattr(self._io, 'pullup'): m.d.comb += self._io.pullup.eq(self.term_select) # If we have a pulldown signal, drive it based on our pulldown controls. if hasattr(self._io, 'pulldown'): m.d.comb += self._io.pullup.eq(self.dm_pulldown | self.dp_pulldown) # # Transmitter # in_normal_mode = (self.op_mode == self.OP_MODE_NORMAL) in_non_encoding_mode = (self.op_mode == self.OP_MODE_NO_ENCODING) m.submodules.transmitter = transmitter = TxPipeline() # When we're in normal mode, we'll drive the USB bus with our standard USB transmitter data. with m.If(in_normal_mode): m.d.comb += [ # UTMI Transmit data. transmitter.i_data_payload.eq(self.tx_data), transmitter.i_oe.eq(self.tx_valid), self.tx_ready.eq(transmitter.o_data_strobe), # USB output. self._io.d_p.o.eq(transmitter.o_usbp), self._io.d_n.o.eq(transmitter.o_usbn), # USB tri-state control. self._io.d_p.oe.eq(transmitter.o_oe), self._io.d_n.oe.eq(transmitter.o_oe), ] # When we're in non-encoding mode ("disable bitstuff and NRZI"), # we'll output to D+ and D- directly when tx_valid is true. with m.Elif(in_non_encoding_mode): m.d.comb += [ # USB output. self._io.d_p.o.eq(self.tx_data), self._io.d_n.o.eq(~self.tx_data), # USB tri-state control. self._io.d_p.oe.eq(self.tx_valid), self._io.d_n.oe.eq(self.tx_valid) ] # When we're in other modes (non-driving or invalid), we'll not output at all. # This block does nothing, as signals default to zero, but it makes the intention obvious. with m.Else(): m.d.comb += [ self._io.d_p.oe.eq(0), self._io.d_n.oe.eq(0), ] # Generate our USB clock strobe, which should pulse at 12MHz. counter = Signal(2) m.d.usb_io += counter.eq(counter + 1) m.d.comb += transmitter.i_bit_strobe.eq(counter == 0) # # Receiver # m.submodules.receiver = receiver = RxPipeline() m.d.comb += [ # We'll listen for packets on D+ and D- _whenever we're not transmitting._. # (If we listen while we're transmitting, we'll hear our own packets.) receiver.i_usbp.eq(self._io.d_p & ~transmitter.o_oe), receiver.i_usbn.eq(self._io.d_n & ~transmitter.o_oe), self.rx_data.eq(receiver.o_data_payload), self.rx_valid.eq(receiver.o_data_strobe & receiver.o_pkt_in_progress), self.rx_active.eq(receiver.o_pkt_in_progress), self.rx_error.eq(receiver.o_receive_error) ] m.d.usb += self.rx_complete.eq(receiver.o_pkt_end) return m
def elaborate(self, platform): m = Module() state = Signal(3) 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") spi2 = synchronize(m, board_spi) leds = [res.o for res in get_all_resources("led")] bldc = platform.request("bldc") m.d.comb += self.spi.connect(spi2) m.d.comb += [ leds[0].eq(bldc.sensor0), leds[1].eq(bldc.sensor1), leds[2].eq(bldc.sensor2) ] # tested by trying out all possibilities m.d.sync += state.eq(Cat(bldc.sensor0, bldc.sensor1, bldc.sensor2)) else: platform = self.platform bldc = platform.bldc target = 200000 max_measurement = target * 10 measurement = Signal(range(max_measurement)) timer = Signal().like(measurement) statefilter = Signal(3) stateold = Signal(3) max_delay = 1000 delay = Signal(range(max_delay)) # Statefilter with m.If((state >= 1) & (state <= 6)): m.d.sync += statefilter.eq(state) m.d.sync += stateold.eq(statefilter) # PID controller step = max_delay // 100 with m.If((measurement > target) & (delay >= step)): m.d.sync += delay.eq(delay - step) with m.Elif((measurement < target) & (delay < (max_delay - step))): m.d.sync += delay.eq(delay + step) with m.Else(): m.d.sync += delay.eq(0) # Measure Cycle with m.If(timer >= max_measurement - 1): m.d.sync += [timer.eq(0), measurement.eq(timer)] with m.Elif((statefilter == 1) & (stateold != 1)): m.d.sync += [measurement.eq(timer), timer.eq(0)] with m.Else(): m.d.sync += timer.eq(timer + 1) spi = self.spi interf = SPICommandInterface(command_size=1 * 8, word_size=4 * 8) m.d.comb += interf.spi.connect(spi) m.submodules.interf = interf m.d.sync += interf.word_to_send.eq(measurement) off = Signal() duty = Signal(range(max_delay)) # Duty timer with m.If(duty < max_delay): m.d.sync += duty.eq(0) with m.Else(): m.d.sync += duty.eq(duty + 1) # Motor On / Off with m.If(duty < delay): m.d.sync += off.eq(1) with m.Else(): m.d.sync += off.eq(0) # https://www.mathworks.com/help/mcb/ref/sixstepcommutation.html with m.If(off): 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(statefilter == 1): # V --> W, 001 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(statefilter == 3): # V --> U, 011 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(statefilter == 2): # W --> U, 010 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(statefilter == 6): # W --> V, 110 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(statefilter == 4): # U --> V, 100 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(statefilter == 5): # U --> W, 101 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) ] # with m.Else(): # disabled # 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)] return m
def elaborate(self, platform): m = Module() # Generate our clock domains. clocking = LunaECP5DomainGenerator(clock_frequencies=CLOCK_FREQUENCIES) m.submodules.clocking = clocking registers = JTAGRegisterInterface(default_read_value=0xDEADBEEF) m.submodules.registers = registers # Simple applet ID register. registers.add_read_only_register(REGISTER_ID, read=0x54455354) # LED test register. led_reg = registers.add_register(REGISTER_LEDS, size=6, name="leds", reset=0b111111) led_out = Cat( [platform.request("led", i, dir="o") for i in range(0, 6)]) m.d.comb += led_out.eq(led_reg) # # Target power test register. # Note: these values assume you've populated the correct AP22814 for # your revision (AP22814As for rev0.2+, and AP22814Bs for rev0.1). # bits [1:0]: 0 = power off # 1 = provide A-port VBUS # 2 = pass through target VBUS # power_test_reg = Signal(3) power_test_write_strobe = Signal() power_test_write_value = Signal(2) registers.add_sfr(REGISTER_TARGET_POWER, read=power_test_reg, write_strobe=power_test_write_strobe, write_signal=power_test_write_value) # Store the values for our enable bits. with m.If(power_test_write_strobe): m.d.sync += power_test_reg[0:2].eq(power_test_write_value) # Decode the enable bits and control the two power supplies. power_a_port = platform.request("power_a_port") power_passthrough = platform.request("pass_through_vbus") with m.If(power_test_reg[0:2] == 1): m.d.comb += [power_a_port.eq(1), power_passthrough.eq(0)] with m.Elif(power_test_reg[0:2] == 2): m.d.comb += [power_a_port.eq(0), power_passthrough.eq(1)] with m.Else(): m.d.comb += [power_a_port.eq(0), power_passthrough.eq(0)] # # User IO GPIO registers. # # Data direction register. user_io_dir = registers.add_register(REGISTER_USER_IO_DIR, size=2) # Pin (input) state register. user_io_in = Signal(2) registers.add_sfr(REGISTER_USER_IO_IN, read=user_io_in) # Output value register. user_io_out = registers.add_register(REGISTER_USER_IO_OUT, size=2) # Grab and connect each of our user-I/O ports our GPIO registers. for i in range(2): pin = platform.request("user_io", i) m.d.comb += [ pin.oe.eq(user_io_dir[i]), user_io_in[i].eq(pin.i), pin.o.eq(user_io_out[i]) ] # # ULPI PHY windows # self.add_ulpi_registers(m, platform, ulpi_bus="target_phy", register_base=REGISTER_TARGET_ADDR) self.add_ulpi_registers(m, platform, ulpi_bus="host_phy", register_base=REGISTER_HOST_ADDR) self.add_ulpi_registers(m, platform, ulpi_bus="sideband_phy", register_base=REGISTER_SIDEBAND_ADDR) # # HyperRAM test connections. # ram_bus = platform.request('ram') psram = HyperRAMInterface(bus=ram_bus, **platform.ram_timings) m.submodules += psram psram_address_changed = Signal() psram_address = registers.add_register( REGISTER_RAM_REG_ADDR, write_strobe=psram_address_changed) registers.add_sfr(REGISTER_RAM_VALUE, read=psram.read_data) # Hook up our PSRAM. m.d.comb += [ ram_bus.reset.eq(0), psram.single_page.eq(0), psram.perform_write.eq(0), psram.register_space.eq(1), psram.final_word.eq(1), psram.start_transfer.eq(psram_address_changed), psram.address.eq(psram_address), ] 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) -> 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() 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() 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() # 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() 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() sync = m.d.sync comb = m.d.comb cfg = self.mem_config # TODO XXX self.no_match on decoder m.submodules.bridge = GenericInterfaceToWishboneMasterBridge( generic_bus=self.generic_bus, wb_bus=self.wb_bus) self.decoder = m.submodules.decoder = WishboneBusAddressDecoder( wb_bus=self.wb_bus, word_size=cfg.word_size) self.initialize_mmio_devices(self.decoder, m) pe = m.submodules.pe = self.pe = PriorityEncoder(width=len(self.ports)) sorted_ports = [port for priority, port in sorted(self.ports.items())] # force 'elaborate' invocation for all mmio modules. for mmio_module, addr_space in self.mmio_cfg: setattr(m.submodules, addr_space.basename, mmio_module) addr_translation_en = self.addr_translation_en = Signal() bus_free_to_latch = self.bus_free_to_latch = Signal(reset=1) if self.with_addr_translation: m.d.comb += addr_translation_en.eq(self.csr_unit.satp.mode & ( self.exception_unit.current_priv_mode == PrivModeBits.USER)) else: m.d.comb += addr_translation_en.eq(False) with m.If(~addr_translation_en): # when translation enabled, 'bus_free_to_latch' is low during page-walk. # with no translation it's simpler - just look at the main bus. m.d.comb += bus_free_to_latch.eq(~self.generic_bus.busy) with m.If(bus_free_to_latch): # no transaction in-progress for i, p in enumerate(sorted_ports): m.d.sync += pe.i[i].eq(p.en) virtual_req_bus_latch = LoadStoreInterface() phys_addr = self.phys_addr = Signal(32) # translation-unit controller signals. start_translation = self.start_translation = Signal() translation_ack = self.translation_ack = Signal() gb = self.generic_bus with m.If(self.decoder.no_match & self.wb_bus.cyc): m.d.comb += self.exception_unit.badaddr.eq(gb.addr) with m.If(gb.store): m.d.comb += self.exception_unit.m_store_error.eq(1) with m.Elif(gb.is_fetch): m.d.comb += self.exception_unit.m_fetch_error.eq(1) with m.Else(): m.d.comb += self.exception_unit.m_load_error.eq(1) with m.If(~pe.none): # transaction request occured for i, priority in enumerate(sorted_ports): with m.If(pe.o == i): bus_owner_port = sorted_ports[i] with m.If(~addr_translation_en): # simple case, no need to calculate physical address comb += gb.connect(bus_owner_port) with m.Else(): # page-walk performs multiple memory operations - will reconnect 'generic_bus' multiple times with m.FSM(): first = self.first = Signal( ) # TODO get rid of that with m.State("TRANSLATE"): comb += [ start_translation.eq(1), bus_free_to_latch.eq(0), ] sync += virtual_req_bus_latch.connect( bus_owner_port, exclude=[ name for name, _, dir in generic_bus_layout if dir == DIR_FANOUT ]) with m.If( translation_ack ): # wait for 'phys_addr' to be set by page-walk algorithm. m.next = "REQ" sync += first.eq(1) with m.State("REQ"): comb += gb.connect(bus_owner_port, exclude=["addr"]) comb += gb.addr.eq( phys_addr) # found by page-walk with m.If(first): sync += first.eq(0) with m.Else(): # without 'first' signal '~gb.busy' would be high at the very beginning with m.If(~gb.busy): comb += bus_free_to_latch.eq(1) m.next = "TRANSLATE" req_is_write = Signal() pte = self.pte = Record(pte_layout) vaddr = Record(virt_addr_layout) comb += [ req_is_write.eq(virtual_req_bus_latch.store), vaddr.eq(virtual_req_bus_latch.addr), ] @unique class Issue(IntEnum): OK = 0 PAGE_INVALID = 1 WRITABLE_NOT_READABLE = 2 LACK_PERMISSIONS = 3 FIRST_ACCESS = 4 MISALIGNED_SUPERPAGE = 5 LEAF_IS_NO_LEAF = 6 self.error_code = Signal(Issue) def error(code: Issue): m.d.sync += self.error_code.eq(code) # Code below implements algorithm 4.3.2 in Risc-V Privileged specification, v1.10 sv32_i = Signal(reset=1) root_ppn = self.root_ppn = Signal(22) if not self.with_addr_translation: return m with m.FSM(): with m.State("IDLE"): with m.If(start_translation): sync += sv32_i.eq(1) sync += root_ppn.eq(self.csr_unit.satp.ppn) m.next = "TRANSLATE" with m.State("TRANSLATE"): vpn = self.vpn = Signal(10) comb += vpn.eq(Mux( sv32_i, vaddr.vpn1, vaddr.vpn0, )) comb += [ gb.en.eq(1), gb.addr.eq(Cat(Const(0, 2), vpn, root_ppn)), gb.store.eq(0), gb.mask.eq(0b1111), # TODO use -1 ] with m.If(gb.ack): sync += pte.eq(gb.read_data) m.next = "PROCESS_PTE" with m.State("PROCESS_PTE"): with m.If(~pte.v): error(Issue.PAGE_INVALID) with m.If(pte.w & ~pte.r): error(Issue.WRITABLE_NOT_READABLE) is_leaf = lambda pte: pte.r | pte.x with m.If(is_leaf(pte)): with m.If(~pte.u & (self.exception_unit.current_priv_mode == PrivModeBits.USER)): error(Issue.LACK_PERMISSIONS) with m.If(~pte.a | (req_is_write & ~pte.d)): error(Issue.FIRST_ACCESS) with m.If(sv32_i.bool() & pte.ppn0.bool()): error(Issue.MISALIGNED_SUPERPAGE) # phys_addr could be 34 bits long, but our interconnect is 32-bit long. # below statement cuts lowest two bits of r-value. sync += phys_addr.eq( Cat(vaddr.page_offset, pte.ppn0, pte.ppn1)) with m.Else(): # not a leaf with m.If(sv32_i == 0): error(Issue.LEAF_IS_NO_LEAF) sync += root_ppn.eq( Cat(pte.ppn0, pte.ppn1)) # pte a is pointer to the next level m.next = "NEXT" with m.State("NEXT"): # Note that we cannot check 'sv32_i == 0', becuase superpages can be present. with m.If(is_leaf(pte)): sync += sv32_i.eq(1) comb += translation_ack.eq( 1) # notify that 'phys_addr' signal is set m.next = "IDLE" with m.Else(): sync += sv32_i.eq(0) m.next = "TRANSLATE" return m