Exemple #1
0
    def find_bit_timings(self, m: Module, sync_counter: DividingCounter, got_edge: Signal):
        """Waits for the ten zero bits of the SYNC section to determine the length of an ADAT bit"""
        sync = m.d.sync
        bit_time_44100 = math.ceil(110 * (self.clk_freq/self.adat_freq(44100) / 100))

        # as long as the input does not change, count up
        # else reset
        with m.If(got_edge):
            # if the sync counter is 10% over the sync time @44100Hz, then
            # the signal just woke up from the dead. Start counting again.
            with m.If(sync_counter.counter_out > 10 * bit_time_44100):
                sync += sync_counter.reset_in.eq(1)

            # if we are in the middle of the signal,
            # and got an edge, then we reset the counter on each edge
            with m.Else():
                # when the counter is bigger than 3/4 of the old max, then we have a sync frame
                with m.If(sync_counter.counter_out > 7 * bit_time_44100):
                    sync += sync_counter.active_in.eq(0) # stop counting, we found it
                    m.next = "DECODE"
                with m.Else():
                    sync += sync_counter.reset_in.eq(1)

        # when we have no edge, count...
        with m.Else():
            sync += [
                sync_counter.reset_in.eq(0),
                sync_counter.active_in.eq(1)
            ]
Exemple #2
0
    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"
Exemple #3
0
    def elaborate(self, platform):
        m = Module()

        # This state machine recognizes sequences of 6 bits and drops the 7th
        # bit.  The fsm implements a counter in a series of several states.
        # This is intentional to help absolutely minimize the levels of logic
        # used.
        drop_bit = Signal(1)

        with m.FSM(domain="usb_io"):

            for i in range(6):
                with m.State(f"D{i}"):
                    with m.If(self.i_valid):
                        with m.If(self.i_data):
                            # Receiving '1' increments the bitstuff counter.
                            m.next = (f"D{i + 1}")
                        with m.Else():
                            # Receiving '0' resets the bitstuff counter.
                            m.next = "D0"

            with m.State("D6"):
                with m.If(self.i_valid):
                    m.d.comb += drop_bit.eq(1)
                    # Reset the bitstuff counter, drop the data.
                    m.next = "D0"

        m.d.usb_io += [
            self.o_data.eq(self.i_data),
            self.o_stall.eq(drop_bit | ~self.i_valid),
            self.o_error.eq(drop_bit & self.i_data & self.i_valid),
        ]

        return m
Exemple #4
0
    def elab(self, m: Module):
        buffering = Signal()  # True if there is a value being buffered
        buffered_value = Signal.like(Value.cast(self.input.payload))

        # Pipe valid and ready back and forth
        m.d.comb += [
            self.input.ready.eq(~buffering | self.output.ready),
            self.output.valid.eq(buffering | self.input.valid),
            self.output.payload.eq(
                Mux(buffering, buffered_value, self.input.payload))
        ]

        # Buffer when have incoming value but cannot output just now
        with m.If(~buffering & ~self.output.ready & self.input.valid):
            m.d.sync += buffering.eq(True)
            m.d.sync += buffered_value.eq(self.input.payload)

        # Handle cases when transfering out from buffer
        with m.If(buffering & self.output.ready):
            with m.If(self.input.valid):
                m.d.sync += buffered_value.eq(self.input.payload)
            with m.Else():
                m.d.sync += buffering.eq(False)

        # Reset all state
        with m.If(self.reset):
            m.d.sync += buffering.eq(False)
            m.d.sync += buffered_value.eq(0)
Exemple #5
0
    def elaborate(self, platform):
        m = Module()
        led0 = platform.request("led", 0)

        with m.If(self.c > 10):
            m.d.sync += self.a.eq(self.a+1)
        with m.Else():
            m.d.sync += self.a.eq(1)

        m.d.sync += self.b.eq(2)
        m.d.sync += self.c.eq(self.a * self.b)
        with m.If(self.c > 10):
            m.d.comb += led0.eq(0)
        with m.Else():
            m.d.comb += led0.eq(1)
        return m
Exemple #6
0
 def elaborate(self, platform):
     m = Module()
     led0 = platform.request("led", 0)
     led1 = platform.request("led", 1)
     m.submodules.rdport = rdport = self.mem.read_port()
     with m.If(rdport.data == 255):
         m.d.comb += led0.eq(0)
     with m.Else():
         m.d.comb += led0.eq(1)
     timer = Signal(range(round(100E6)))
     with m.If(timer > 100):
         m.d.comb += rdport.addr.eq(100)
     with m.Else():
         m.d.comb += rdport.addr.eq(0)
     m.d.sync += timer.eq(timer + 1)
     m.d.comb += led1.o.eq(timer[-1])
     return m
Exemple #7
0
    def elaborate(self, platform):
        m = Module()

        with m.If(self.op):
            m.d.comb += self.y.eq(self.a + self.b)
        with m.Else():
            m.d.comb += self.y.eq(self.a - self.b)

        return m
Exemple #8
0
    def elaborate(self, platform):
        m = Module()

        beta = self.beta
        temp = Signal(signed(self.totalbits * 2))

        n = len(self.coeff)
        j = Signal(range(1, n))
        k = Signal.like(j)
        with m.FSM(reset='INIT') as algo:
            with m.State('INIT'):
                m.d.sync += self.done.eq(0)
                for i in range(n):
                    m.d.sync += beta[i].eq(self.coeff[i])
                m.d.sync += [k.eq(0), j.eq(1)]
                m.next = 'UPDATE'
            with m.FSM('UPDATE'):
                m.d.sync += temp.eq(beta[k] * (1 - self.t) +
                                    beta[k + 1] * self.t)
                m.next = 'MULTIPLICATIONFIX'
            # Fixed point arithmetic need fix
            # see multiplication as https://vha3.github.io/FixedPoint/FixedPoint.html
            with m.FSM('MULTIPLICATIONFIX'):
                m.d.sync += beta[k].eq(
                    temp[self.fractionalbits:self.fractionalbits +
                         self.totalbits])
                with m.If(k != n - j):
                    m.d.sync += k.eq(k + 1)
                    m.next = 'UPDATE'
                with m.Else():
                    with m.If(j != n):
                        m.d.sync += j.eq(j + 1)
                        m.d.sync += k.eq(0)
                        m.next = 'UPDATE'
                    with m.Else():
                        m.next = 'FINISH'
            with m.FSM('FINISH'):
                m.d.sync += self.done.eq(1)
                m.next = 'FINISH'
        return m
Exemple #9
0
    def elaborate(self, platform):
        m = Module()
        stuff_bit = Signal()

        with m.FSM(domain="usb"):

            for i in range(5):

                with m.State(f"D{i}"):
                    # Receiving '1' increments the bitstuff counter.
                    with m.If(self.i_data):
                        m.next = f"D{i+1}"

                    # Receiving '0' resets the bitstuff counter.
                    with m.Else():
                        m.next = "D0"

            with m.State("D5"):
                with m.If(self.i_data):

                    # There's a '1', so indicate we might stall on the next loop.
                    m.d.comb += self.o_will_stall.eq(1),
                    m.next = "D6"

                with m.Else():
                    m.next = "D0"

            with m.State("D6"):
                m.d.comb += stuff_bit.eq(1)
                m.next = "D0"

        m.d.comb += [self.o_stall.eq(stuff_bit)]

        # flop outputs
        with m.If(stuff_bit):
            m.d.usb += self.o_data.eq(0),
        with m.Else():
            m.d.usb += self.o_data.eq(self.i_data)

        return m
Exemple #10
0
    def elaborate(self, platform):
        m = Module()

        if self.own_register_window:
            m.submodules.reg_window = self.register_window

        # Add the registers that represent each of our signals.
        self.populate_ulpi_registers(m)

        # Generate logic to handle changes on each of our registers.
        first_element = True
        for address, signals in self._register_signals.items():

            conditional = m.If if first_element else m.Elif
            first_element = False

            # If we're requesting a write on the given register, pass that to our
            # register window.
            with conditional(signals['write_requested']):

                # Keep track of when we'll be okay to start a write:
                # it's when there's a write request, we're not complete.
                # and the bus is idle. We'll use this below.
                request_write = \
                    signals['write_requested'] & \
                    ~self.register_window.done & \
                    self.bus_idle

                m.d.comb += [

                    # Control signals.
                    signals['write_done']              .eq(self.register_window.done),

                    # Register window signals.
                    self.register_window.address       .eq(address),
                    self.register_window.write_data    .eq(signals['write_value']),
                    self.register_window.write_request .eq(request_write),

                    # Status signals
                ]
                m.d.usb += self.busy.eq(request_write | self.register_window.busy)

        # If no register accesses are active, provide default signal values.
        with m.Else():
            m.d.comb += self.register_window.write_request.eq(0)
            m.d.usb  += self.busy.eq(self.register_window.busy)

        # Ensure our register window is never performing a read.
        m.d.comb += self.register_window.read_request.eq(0)

        return m
Exemple #11
0
 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
Exemple #12
0
    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
Exemple #13
0
    def elab(self, m: Module):
        # One signal for each input stream, indicating whether
        # there is a value being buffered:
        buffering = {name: Signal(name=f'buffering_{name}')
                     for name in self.field_names}
        # A buffer for each input stream:
        buffered_values = \
            {name: Signal(self.field_shapes[name], name=f'buffered_{name}')
             for name in self.field_names}

        # For each field of the concatenated output, present either the
        # buffered value if we have one, or else plumb through the input.
        for name in self.field_names:
            m.d.comb += self.output.payload[name].eq(
                    Mux(buffering[name],
                        buffered_values[name],
                        self.inputs[name].payload))

        # The output is valid if we have either a buffered value or a valid
        # input for every slice in the output.
        valid_or_buffering = (Cat(*[ep.valid for ep in self.inputs.values()]) |
                              Cat(*buffering.values()))
        m.d.comb += self.output.valid.eq(valid_or_buffering.all())

        for name, input in self.inputs.items():
            # We can accept an input if the buffer is not occupied,
            # or if we can output this cycle.
            m.d.comb += input.ready.eq(~buffering[name] |
                                       self.output.is_transferring())

            with m.If(input.valid):
                # Buffer it if the buffer is not occupied and we can't output.
                with m.If(~buffering[name] & ~self.output.is_transferring()):
                    m.d.sync += buffering[name].eq(True)
                    m.d.sync += buffered_values[name].eq(input.payload)
                # Buffer it if the buffer is occupied but we are outputting.
                with m.If(self.output.is_transferring()):
                    m.d.sync += buffered_values[name].eq(input.payload)
            with m.Else():
                with m.If(self.output.is_transferring()):
                    m.d.sync += buffering[name].eq(False)

        # Reset all state
        with m.If(self.reset):
            for b in buffering.values():
                m.d.sync += b.eq(False)
            for bv in buffered_values.values():
                m.d.sync += bv.eq(0)
Exemple #14
0
    def elaborate(self, platform):
        m = Module()

        # Create a set of registers, and expose them over SPI.
        board_spi = platform.request("debug_spi")
        spi_registers = SPIRegisterInterface(default_read_value=-1)
        m.submodules.spi_registers = spi_registers

        # Identify ourselves as the SPI flash bridge.
        spi_registers.add_read_only_register(REGISTER_ID, read=0x53504946)

        #
        # SPI flash passthrough connections.
        #
        flash_sdo = Signal()

        spi_flash_bus = platform.request('spi_flash')
        spi_flash_passthrough = ECP5ConfigurationFlashInterface(
            bus=spi_flash_bus)

        m.submodules += spi_flash_passthrough
        m.d.comb += [
            spi_flash_passthrough.sck.eq(board_spi.sck),
            spi_flash_passthrough.sdi.eq(board_spi.sdi),
            flash_sdo.eq(spi_flash_passthrough.sdo),
        ]

        #
        # Structural connections.
        #
        spi = synchronize(m, board_spi)

        # Select the passthrough or gateware SPI based on our chip-select values.
        gateware_sdo = Signal()
        with m.If(board_spi.cs):
            m.d.comb += board_spi.sdo.eq(gateware_sdo)
        with m.Else():
            m.d.comb += board_spi.sdo.eq(flash_sdo)

        # Connect our register interface to our board SPI.
        m.d.comb += [
            spi_registers.spi.sck.eq(spi.sck),
            spi_registers.spi.sdi.eq(spi.sdi),
            gateware_sdo.eq(spi_registers.spi.sdo),
            spi_registers.spi.cs.eq(spi.cs)
        ]

        return m
Exemple #15
0
    def elaborate(self, platform):
        m = Module()

        # Make the output `rollover` always equal to this comparison,
        # which will only be 1 for a single cycle every counter period.
        m.d.comb += self.rollover.eq(self.counter == self.limit - 1)

        # Conditionally reset the counter to 0 on rollover, otherwise
        # increment it. We could write the comparison out again here
        # to the same effect.
        with m.If(self.rollover):
            m.d.sync += self.counter.eq(0)
        with m.Else():
            m.d.sync += self.counter.eq(self.counter + 1)

        return m
Exemple #16
0
    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
Exemple #17
0
    def elaborate(self, platform):
        m = Module()
        uart = platform.request("uart")

        clock_freq = int(60e6)
        char_freq = int(6e6)

        # Create our UART transmitter.
        transmitter = UARTTransmitter(divisor=int(clock_freq // 115200))
        m.submodules.transmitter = transmitter
        stream = transmitter.stream

        # Create a counter that will let us transmit ten times per second.
        counter = Signal(range(0, char_freq))
        with m.If(counter == (char_freq - 1)):
            m.d.sync += counter.eq(0)
        with m.Else():
            m.d.sync += counter.eq(counter + 1)

        # Create a simple ROM with a message for ourselves...
        letters = Array(ord(i) for i in "Hello, world! \r\n")

        # ... and count through it whenever we send a letter.
        current_letter = Signal(range(0, len(letters)))
        with m.If(stream.ready):
            m.d.sync += current_letter.eq(current_letter + 1)

        # Hook everything up.
        m.d.comb += [
            stream.payload.eq(letters[current_letter]),
            stream.valid.eq(counter == 0),
            uart.tx.o.eq(transmitter.tx),
        ]

        # If this platform has an output-enable control on its UART, drive it iff
        # we're actively driving a transmission.
        if hasattr(uart.tx, 'oe'):
            m.d.comb += uart.tx.oe.eq(transmitter.driving),

        # Turn on a single LED, just to show something's running.
        led = Cat(platform.request('led', i) for i in range(6))
        m.d.comb += led.eq(~transmitter.tx)

        return m
Exemple #18
0
    def elaborate(self, platform):
        m = Module()

        # Attach our SPI interface.
        m.submodules.interface = self.interface
        self._connect_interface(m)

        # Split the command into our "write" and "address" signals.
        m.d.comb += [
            self._is_write.eq(self.interface.command[-1]),
            self._address.eq(self.interface.command[0:-1])
        ]

        # Create the control/write logic for each of our registers.
        for address, connections in self.registers.items():
            self._elaborate_register(m, address, connections)

        # Build the logic to select the 'to_send' value, which is selected
        # from all of our registers according to the selected register address.
        first_item = True
        for address, connections in self.registers.items():
            statement = m.If if first_item else m.Elif

            with statement(self._address == address):

                # Hook up the word-to-send signal either to the read value for the relevant
                # register, or to the default read value.
                if connections['read'] is not None:
                    m.d.comb += self.interface.word_to_send.eq(
                        connections['read'])
                else:
                    m.d.comb += self.interface.word_to_send.eq(
                        self.default_read_value)

            # We've already created the m.If; from now on, use m.Elif
            first_item = False

        # Finally, tie all non-handled register values to always respond with the default.
        with m.Else():
            m.d.comb += self.interface.word_to_send.eq(self.default_read_value)

        return m
Exemple #19
0
    def elaborate(self, platform):
        m = Module()

        # neat way of setting carry flag
        res_and_carry = Cat(self.res, self.carry)

        m.d.comb += res_and_carry.eq(
            Mux(self.sub, self.src1 - self.src2, self.src1 + self.src2))

        with m.If(self.sub):
            with m.If((self.src1[-1] != self.src2[-1])
                      & (self.src1[-1] != self.res[-1])):
                m.d.comb += self.overflow.eq(1)
        with m.Else():
            # add
            with m.If((self.src1[-1] == self.src2[-1])
                      & (self.src1[-1] != self.res[-1])):
                m.d.comb += self.overflow.eq(1)

        return m
Exemple #20
0
    def elaborate(self, platform):
        m = Module()
        width = self._width

        # Instead of using a counter, we will use a sentinel bit in the shift
        # register to indicate when it is full.
        shift_reg = Signal(width + 1, reset=0b1)

        m.d.comb += self.o_data.eq(shift_reg[0:width])
        m.d.usb_io += self.o_put.eq(shift_reg[width - 1] & ~shift_reg[width]
                                    & self.i_valid),

        with m.If(self.reset):
            m.d.usb_io += shift_reg.eq(1)

        with m.If(self.i_valid):
            with m.If(shift_reg[width]):
                m.d.usb_io += shift_reg.eq(Cat(self.i_data, Const(1)))
            with m.Else():
                m.d.usb_io += shift_reg.eq(Cat(self.i_data,
                                               shift_reg[0:width])),

        return m
Exemple #21
0
    def elaborate(self, platform):
        m = Module()

        # Synchronize the USB signals at our I/O boundary.
        # Despite the assumptions made in ValentyUSB, this line rate recovery FSM
        # isn't enough to properly synchronize these inputs. We'll explicitly synchronize.
        sync_dp = synchronize(m, self._usbp, o_domain="usb_io")
        sync_dn = synchronize(m, self._usbn, o_domain="usb_io")

        #######################################################################
        # Line State Recovery State Machine
        #
        # The receive path doesn't use a differential receiver.  Because of
        # this there is a chance that one of the differential pairs will appear
        # to have changed to the new state while the other is still in the old
        # state.  The following state machine detects transitions and waits an
        # extra sampling clock before decoding the state on the differential
        # pair.  This transition period  will only ever last for one clock as
        # long as there is no noise on the line.  If there is enough noise on
        # the line then the data may be corrupted and the packet will fail the
        # data integrity checks.
        #
        dpair = Cat(sync_dp, sync_dn)

        # output signals for use by the clock recovery stage
        line_state_in_transition = Signal()

        with m.FSM(domain="usb_io") as fsm:
            m.d.usb_io += [
                self.line_state_se0.eq(fsm.ongoing("SE0")),
                self.line_state_se1.eq(fsm.ongoing("SE1")),
                self.line_state_dj.eq(fsm.ongoing("DJ")),
                self.line_state_dk.eq(fsm.ongoing("DK")),
            ]

            # If we are in a transition state, then we can sample the pair and
            # move to the next corresponding line state.
            with m.State("DT"):
                m.d.comb += line_state_in_transition.eq(1)

                with m.Switch(dpair):
                    with m.Case(0b10):
                        m.next = "DJ"
                    with m.Case(0b01):
                        m.next = "DK"
                    with m.Case(0b00):
                        m.next = "SE0"
                    with m.Case(0b11):
                        m.next = "SE1"

            # If we are in a valid line state and the value of the pair changes,
            # then we need to move to the transition state.
            with m.State("DJ"):
                with m.If(dpair != 0b10):
                    m.next = "DT"

            with m.State("DK"):
                with m.If(dpair != 0b01):
                    m.next = "DT"

            with m.State("SE0"):
                with m.If(dpair != 0b00):
                    m.next = "DT"

            with m.State("SE1"):
                with m.If(dpair != 0b11):
                    m.next = "DT"

        #######################################################################
        # Clock and Data Recovery
        #
        # The DT state from the line state recovery state machine is used to align to
        # transmit clock.  The line state is sampled in the middle of the bit time.
        #
        # Example of signal relationships
        # -------------------------------
        # line_state        DT  DJ  DJ  DJ  DT  DK  DK  DK  DK  DK  DK  DT  DJ  DJ  DJ
        # line_state_valid  ________----____________----____________----________----____
        # bit_phase         0   0   1   2   3   0   1   2   3   0   1   2   0   1   2
        #

        # We 4x oversample, so make the line_state_phase have
        # 4 possible values.
        line_state_phase = Signal(2)
        m.d.usb_io += self.line_state_valid.eq(line_state_phase == 1)

        with m.If(line_state_in_transition):
            m.d.usb_io += [
                # re-align the phase with the incoming transition
                line_state_phase.eq(0),

                # make sure we never assert valid on a transition
                self.line_state_valid.eq(0),
            ]
        with m.Else():
            # keep tracking the clock by incrementing the phase
            m.d.usb_io += line_state_phase.eq(line_state_phase + 1)

        return m
Exemple #22
0
    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
Exemple #23
0
    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
Exemple #24
0
    def elaborate(self, platform):
        m = Module()
        bit_stuffing_disabled = (self.op_mode == self.OP_MODE_NO_BIT_STUFFING)

        with m.FSM(domain="usb") as fsm:

            # Mark ourselves as busy whenever we're not in idle.
            m.d.comb += self.busy.eq(~fsm.ongoing('IDLE'))

            # IDLE: our transmitter is ready and
            with m.State('IDLE'):
                m.d.comb += self.ulpi_stp.eq(0)

                # Start once a transmit is started, and we can access the bus.
                with m.If(self.tx_valid & self.bus_idle):

                    # If bit-stuffing is disabled, we'll need to prefix our transmission with a NOPID command.
                    # In this case, we'll never accept the first byte (as we're not ready to transmit it, yet),
                    # and thus TxReady will always be 0.
                    with m.If(bit_stuffing_disabled):
                        m.d.usb  += self.ulpi_out_req.eq(1),
                        m.d.comb += [
                            self.ulpi_data_out .eq(self.TRANSMIT_COMMAND),
                            self.tx_ready      .eq(0)
                        ]

                    # Otherwise, this transmission starts with a PID. Extract the PID from the first data byte
                    # and present it as part of the Transmit Command. In this case, the NXT signal is
                    # has the same meaning as the UTMI TxReady signal; and can be passed along directly.
                    with m.Else():
                        m.d.usb  += self.ulpi_out_req.eq(1),
                        m.d.comb += [
                            self.ulpi_data_out .eq(self.TRANSMIT_COMMAND | self.tx_data[0:4]),
                            self.tx_ready      .eq(self.ulpi_nxt)
                        ]


                    # Once the PHY has accepted the command byte, we're ready to move into our main transmit state.
                    with m.If(self.ulpi_nxt):
                        m.next = 'TRANSMIT'


            # TRANSMIT: we're in the body of a transmit; the UTMI and ULPI interface signals
            # are roughly equivalent; we'll just pass them through.
            with m.State('TRANSMIT'):
                m.d.comb += [
                    self.ulpi_data_out .eq(self.tx_data),
                    self.tx_ready      .eq(self.ulpi_nxt),
                    self.ulpi_stp      .eq(0),
                ]

                # Once the transmission has ended, we'll need to issue a ULPI stop.
                with m.If(~self.tx_valid):
                    m.d.usb += self.ulpi_out_req.eq(0),
                    m.next = 'IDLE'

                    # STOP: our packet has just terminated; we'll generate a ULPI stop event for a single cycle.
                    # [ULPI: 3.8.2.2]
                    m.d.comb += self.ulpi_stp.eq(1)

                    # If we've disabled bit stuffing, we'll want to termainate by generating a bit-stuff error.
                    with m.If(bit_stuffing_disabled):

                        # Drive 0xFF as we stop, to generate a bit-stuff error. [ULPI: 3.8.2.3]
                        m.d.comb += self.ulpi_data_out .eq(0xFF)

                    # Otherwise, we'll generate a normal stop.
                    with m.Else():
                        m.d.comb += self.ulpi_data_out .eq(0)


        return m
Exemple #25
0
    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
Exemple #26
0
    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
Exemple #27
0
    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
Exemple #28
0
    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
Exemple #29
0
    def elaborate(self, platform):
        m = Module()

        # Grab signals that detect when we should shift in and out.
        sample_edge, output_edge = self.spi_edge_detectors(m)

        # We'll use separate buffers for transmit and receive,
        # as this makes the code a little more readable.
        bit_count = Signal(range(0, self.word_size), reset=0)
        current_tx = Signal.like(self.word_out)
        current_rx = Signal.like(self.word_in)

        # Signal that tracks if this is our first bit of the word.
        is_first_bit = Signal()

        # A word is ready one cycle after we complete a transaction
        # (and latch in the next word to be sent).
        with m.If(self.word_accepted):
            m.d.sync += [self.word_in.eq(current_rx), self.word_complete.eq(1)]
        with m.Else():
            m.d.sync += self.word_complete.eq(0)

        # De-assert our control signals unless explicitly asserted.
        m.d.sync += [
            self.word_accepted.eq(0),
        ]

        # If the chip is selected, process our I/O:
        chip_selected = self.spi.cs if not self.cs_idles_high else ~self.spi.cs

        with m.If(chip_selected):

            # Shift in data on each sample edge.
            with m.If(sample_edge):
                m.d.sync += [bit_count.eq(bit_count + 1), is_first_bit.eq(0)]

                if self.msb_first:
                    m.d.sync += current_rx.eq(
                        Cat(self.spi.sdi, current_rx[:-1]))
                else:
                    m.d.sync += current_rx.eq(Cat(current_rx[1:],
                                                  self.spi.sdi))

                # If we're just completing a word, handle I/O.
                with m.If(bit_count + 1 == self.word_size):
                    m.d.sync += [
                        self.word_accepted.eq(1),
                        current_tx.eq(self.word_out)
                    ]

            # Shift out data on each output edge.
            with m.If(output_edge):
                if self.msb_first:
                    m.d.sync += Cat(current_tx[1:],
                                    self.spi.sdo).eq(current_tx)
                else:
                    m.d.sync += Cat(self.spi.sdo,
                                    current_tx[:-1]).eq(current_tx)

        with m.Else():
            m.d.sync += [
                current_tx.eq(self.word_out),
                bit_count.eq(0),
                is_first_bit.eq(1)
            ]

        return m
Exemple #30
0
    def elaborate(self, platform):

        m = Module()
        spi = self.spi

        sample_edge = Fell(spi.sck, domain="sync")

        # Bit counter: counts the number of bits received.
        max_bit_count = max(self.word_size, self.command_size)
        bit_count = Signal(range(0, max_bit_count + 1))

        # Shift registers for our command and data.
        current_command = Signal.like(self.command)
        current_word = Signal.like(self.word_received)

        # De-assert our control signals unless explicitly asserted.
        m.d.sync += [self.command_ready.eq(0), self.word_complete.eq(0)]

        with m.FSM() as fsm:
            m.d.comb += [
                self.idle.eq(fsm.ongoing('IDLE')),
                self.stalled.eq(fsm.ongoing('STALL')),
            ]

            # STALL: entered when we can't accept new bits -- either when
            # CS starts asserted, or when we've received more data than expected.
            with m.State("STALL"):

                # Wait for CS to clear.
                with m.If(~spi.cs):
                    m.next = 'IDLE'

            # We ignore all data until chip select is asserted, as that data Isn't For Us (TM).
            # We'll spin and do nothing until the bus-master addresses us.
            with m.State('IDLE'):
                m.d.sync += bit_count.eq(0)

                with m.If(spi.cs):
                    m.next = 'RECEIVE_COMMAND'

            # Once CS is low, we'll shift in our command.
            with m.State('RECEIVE_COMMAND'):

                # If CS is de-asserted early; our transaction is being aborted.
                with m.If(~spi.cs):
                    m.next = 'IDLE'

                # Continue shifting in data until we have a full command.
                with m.If(bit_count < self.command_size):
                    with m.If(sample_edge):
                        m.d.sync += [
                            bit_count.eq(bit_count + 1),
                            current_command.eq(
                                Cat(spi.sdi, current_command[:-1]))
                        ]

                # ... and then pass that command out to our controller.
                with m.Else():
                    m.d.sync += [
                        bit_count.eq(0),
                        self.command_ready.eq(1),
                        self.command.eq(current_command)
                    ]
                    m.next = 'PROCESSING'

            # Give our controller a wait state to prepare any response they might want to...
            with m.State('PROCESSING'):
                m.next = 'LATCH_OUTPUT'

            # ... and then latch in the response to transmit.
            with m.State('LATCH_OUTPUT'):
                m.d.sync += current_word.eq(self.word_to_send)
                m.next = 'SHIFT_DATA'

            # Finally, exchange data.
            with m.State('SHIFT_DATA'):

                # If CS is de-asserted early; our transaction is being aborted.
                with m.If(~spi.cs):
                    m.next = 'IDLE'

                m.d.sync += spi.sdo.eq(current_word[-1])

                # Continue shifting data until we have a full word.
                with m.If(bit_count < self.word_size):
                    with m.If(sample_edge):
                        m.d.sync += [
                            bit_count.eq(bit_count + 1),
                            current_word.eq(Cat(spi.sdi, current_word[:-1]))
                        ]

                # ... and then output that word on our bus.
                with m.Else():
                    m.d.sync += [
                        bit_count.eq(0),
                        self.word_complete.eq(1),
                        self.word_received.eq(current_word)
                    ]

                    # Stay in the stall state until CS is de-asserted.
                    m.next = 'STALL'

        return m