Esempio n. 1
0
    def __init__(self):
        self.submodules.fifo = f = fifo.SyncFIFOBuffered(width=8, depth=64)
        in_reg = CSRStorage(8,
                            name="in",
                            description="""
                    Write half of the FIFO to send data out the Messible.
                    Writing to this register advances the write pointer automatically."""
                            )
        out_reg = CSRStatus(8,
                            name="out",
                            description="""
                    Read half of the FIFO to receive data on the Messible.
                    Reading from this register advances the read pointer automatically."""
                            )

        self.__setattr__("in", in_reg)
        self.__setattr__("out", out_reg)
        self.status = status = CSRStatus(fields=[
            CSRField(
                "full",
                description="``0`` if more data can fit into the IN FIFO."),
            CSRField(
                "have",
                description="``1`` if data can be read from the OUT FIFO."),
        ])

        self.intro = ModuleDoc("""
                Messible: An Ansible for Messages

                An Ansible is a system for instant communication across vast distances, from
                a small portable device to a huge terminal far away.  A Messible is a message-
                passing system from embedded devices to a host system.  You can use it to get
                very simple printf()-style support over a debug channel.

                The Messible assumes the host has a way to peek into the device's memory space.
                This is the case with the Wishbone bridge, which allows both the device and
                the host to access the same memory.

                At its core, a Messible is a FIFO.  As long as the ``STATUS.FULL`` bit is ``0``, the
                device can write data into the Messible by writing into the ``IN``.  However, if this
                value is ``1``, you need to decide if you want to wait for it to empty (if the other
                side is just slow), or if you want to drop the message.

                From the host side, you need to read ``STATUS.HAVE`` to see if there is data
                in the FIFO.  If there is, read ``OUT`` to get the most recent byte, which automatically
                advances the ``READ`` pointer.
                """)

        self.comb += [
            f.din.eq(in_reg.storage),
            f.we.eq(in_reg.re),
            out_reg.status.eq(f.dout),
            f.re.eq(out_reg.we),
            status.fields.full.eq(~f.writable),
            status.fields.have.eq(f.readable),
        ]
Esempio n. 2
0
    def __init__(self, pads, debounce_ms=5):
        self.background = ModuleDoc("""Matrix Keyboard Driver
A hardware key scanner that can run even when the CPU is powered down or stopped.

The hardware is assumed to be a matrix of switches, divided into rows and columns.
The number of rows and columns is inferred by the number of pins dedicated in
the `pads` record.

The rows are inputs, and require a `PULLDOWN` to be inferred on the pins, to prevent
them from floating when the keys are not pressed. Note that `PULLDOWN` is not offered
on all FPGA architectures, but at the very least is present on 7-Series FPGAs.

The columns are driven by tristate drivers. The state of the columns is either driven
high (`1`), or hi-Z (`Z`).

The KeyScan module also expects a `kbd` clock domain. The preferred implementation
makes this a 32.768KHz always-on clock input with no PLL. This allows the keyboard
module to continue scanning even if the CPU is powered down.

Columns are scanned sequentially using a `kbd`-domain state machine by driving each
column in order. When a column is driven, its electrical state goes from `hi-z` to `1`.
The rows are then sampled with each column scan state. Since each row has
a pulldown on it, if no keys are hit, the result is `0`. When a key is pressed, it
will short a row to a column, and the `1` driven on a column will flip the row
state to a `1`, thus registering a key press.

Columns are driven for a minimum period of time known as a `settling` period. The
settling time must be at least 2 because a Multireg (2-stage synchronizer) is used
to sample the data on the receiving side. In this module, settling time is fixed
to `4` cycles.

Thus a 1 in the `rowdat` registers indicate the column intersection that was active for
a given row in the matrix.

There is also a row shadow register that is maintained by the hardware. The row shadow
is used to maintain the previous state of the key matrix, so that a "key press change"
interrupt can be generated.

There is also a `keypressed` interrupt which fires every time there is a change in
the row registers.

`debounce` is the period in ms for the keyboard matrix to stabilize before triggering
an interrupt.
        """)
        rows_unsync = pads.row
        cols        = Signal(pads.col.nbits)

        self.uart_inject = Signal(8) # character to inject from the UART
        self.inject_strobe = Signal() # on rising edge, latch uart_inject and raise an interrupt
        self.uart_char = CSRStatus(9, fields = [
            CSRField("char", size=8, description="character value being injected"),
            CSRField("stb", size=1, description="FIFO has readable characters"),
        ])
        self.submodules.injectfifo = injectfifo = fifo.SyncFIFOBuffered(width=8, depth=16)
        self.comb += [
            injectfifo.din.eq(self.uart_inject),
            injectfifo.we.eq(self.inject_strobe),

            #self.uart_char.fields.char.eq(self.uart_inject),
            self.uart_char.fields.char.eq(injectfifo.dout),
            self.uart_char.fields.stb.eq(injectfifo.readable),
            injectfifo.re.eq(self.uart_char.we),
        ]
        for c in range(0, cols.nbits):
            cols_ts = TSTriple(1)
            self.specials += cols_ts.get_tristate(pads.col[c])
            self.comb += cols_ts.oe.eq(cols[c])
            self.comb += cols_ts.o.eq(1)

        # row and col are n-bit signals that correspond to the row and columns of the keyboard matrix
        # each row will generate a column register with the column result in it
        rows = Signal(rows_unsync.nbits)
        self.specials += MultiReg(rows_unsync, rows, "kbd")

        # setattr(self, name, object) is the same as self.name = object, except in this case "name" can be dynamically generated
        # this is necessary here because we CSRStatus is not iterable, so we have to manage the attributes manually
        for r in range(0, rows.nbits):
            setattr(self, "row" + str(r) + "dat", CSRStatus(cols.nbits, name="row" + str(r) + "dat", description="""Column data for the given row"""))

        settling = 4  # 4 cycles to settle: 2 cycles for MultiReg stabilization + slop. Must be > 2, and a power of 2
        colcount = Signal(max=(settling*cols.nbits+2))

        update_shadow = Signal()
        reset_scan    = Signal()
        scan_done     = Signal()
        col_r         = Signal(cols.nbits)
        scan_done_sys = Signal()
        self.specials += MultiReg(scan_done, scan_done_sys)
        for r in range(0, rows.nbits):
            row_scan = Signal(cols.nbits)
            row_scan_sys = Signal(cols.nbits)
            # below is in sysclock domain; row_scan is guaranteed stable by state machine sequencing when scan_done gating is enabled
            self.sync += [
                row_scan_sys.eq(row_scan), # loosen up the clock domain crossing timing
                If(scan_done_sys,
                    getattr(self, "row" + str(r) + "dat").status.eq(row_scan_sys)
                ).Else(
                    getattr(self, "row" + str(r) + "dat").status.eq(getattr(self, "row" + str(r) + "dat").status)
                )
            ]
            self.sync.kbd += [
                If(reset_scan,
                   row_scan.eq(0)
                ).Else(
                    If(rows[r] & (colcount[0:2] == 3),  # sample row only on the 4th cycle of colcount
                       row_scan.eq(row_scan | col_r)
                    ).Else(
                        row_scan.eq(row_scan)
                    )
                )
            ]

            rowshadow = Signal(cols.nbits)
            self.sync.kbd += If(update_shadow, rowshadow.eq(row_scan)).Else(rowshadow.eq(rowshadow))

            setattr(self, "row_scan" + str(r), row_scan)
            setattr(self, "rowshadow" + str(r), rowshadow)
            # create a simple, one-scan delayed version of row_scan for debouncing purposes
            row_debounce = Signal(cols.nbits)
            self.sync.kbd += [
                If(scan_done,
                    row_debounce.eq(row_scan),
                ).Else(
                    row_debounce.eq(row_debounce),
                )
            ]
            setattr(self, "row_debounce" + str(r), row_debounce)

        pending_kbd = Signal()
        pending_kbd_f = Signal()
        key_ack = Signal()
        self.sync.kbd += [
            pending_kbd_f.eq(pending_kbd),
            colcount.eq(colcount + 1),
            scan_done.eq(0),
            update_shadow.eq(0),
            reset_scan.eq(0),
            If(colcount == (settling*cols.nbits+2), colcount.eq(0)),
            If(colcount == (settling*cols.nbits), scan_done.eq(1)),
            # Only update the shadow if the pending bit has been cleared (e.g., CPU has acknowledged
            # it has fetched the current key state)
            If(~pending_kbd & pending_kbd_f,
                If(colcount == (settling * cols.nbits + 1),
                    update_shadow.eq(1),
                    key_ack.eq(0),
                ).Else(
                    key_ack.eq(1),
                )
            ).Else(
                If(colcount == (settling*cols.nbits+1),
                    update_shadow.eq(key_ack),
                    key_ack.eq(0),
                ).Else(
                    key_ack.eq(key_ack)
                )
            ),
            If(colcount == (settling*cols.nbits+2), reset_scan.eq(1)),
        ]

        # Drive the columns based on the colcount counter
        self.submodules.coldecoder = Decoder(cols.nbits)
        self.comb += [
            self.coldecoder.i.eq(colcount[log2_int(settling):]),
            self.coldecoder.n.eq(~(colcount < settling*cols.nbits)),
            cols.eq(self.coldecoder.o)
        ]
        self.sync.kbd += col_r.eq(self.coldecoder.o)

        self.submodules.ev = EventManager()
        self.ev.keypressed = EventSourcePulse(description="Triggered every time there is a difference in the row state") # Rising edge triggered
        self.ev.inject = EventSourcePulse(description="Key injection request received")
        self.ev.finalize()
        self.sync += [
            self.ev.inject.trigger.eq(self.inject_strobe)
        ]

        # zero state auto-clear: the "delta" methodology gets stuck if the initial sampling state of the keyboard matrix isn't 0. this fixes that.
        rows_nonzero = Signal(rows.nbits)
        col_zeros = Signal(cols.nbits) # form multi-bit zeros of the right width, otherwise we get 1'd0 as the rhs of equality statements
        row_zeros = Signal(rows.nbits)
        for r in range(0, rows.nbits):
            self.sync.kbd +=  rows_nonzero[r].eq(getattr(self, "row_scan" + str(r)) != col_zeros)
        all_zeros = Signal()
        clear_deltas = Signal()
        self.comb += all_zeros.eq(rows_nonzero == row_zeros)
        clear_kbd = Signal()
        self.submodules.clear_sync = BlindTransfer("sys", "kbd")
        self.comb += [
            self.clear_sync.i.eq(self.ev.keypressed.clear),
            clear_kbd.eq(self.clear_sync.o)
        ]

        # debounce timers and clocks
        debounce_clocks = int((debounce_ms * 0.001) * 32768.0)
        debounce_timer  = Signal(max=(debounce_clocks + 1))
        debounced = Signal()
        # Extract any changes just before the shadow takes its new values
        rowdiff = Signal(rows.nbits)
        for r in range(0, rows.nbits):
            self.sync.kbd += [
                If(clear_deltas,
                    rowdiff[r].eq(0),
                ).Elif(debounced, # was scan_done
                   rowdiff[r].eq( ~((getattr(self, "row_scan" + str(r)) ^ getattr(self, "rowshadow" + str(r))) == 0) )
                ).Else(
                    rowdiff[r].eq(rowdiff[r])
                )
            ]

        # debouncing:
        #   1. compute the delta of the current scan vs. previous scan
        #   2. if the delta is non-zero, start a debounce timer.
        #   3. Count down as long as the delta remains zero; if the delta is non-zero again, reset the timer.
        #   4. When the timer hits zero, latch the final value for sampling to the CPU
        rowchanging = Signal(rows.nbits)
        for r in range(0, rows.nbits):
            self.sync.kbd += [
                If(clear_deltas,
                    rowchanging[r].eq(0),
                ).Elif(scan_done,
                    rowchanging[r].eq( ~((getattr(self, "row_scan" + str(r)) ^ getattr(self, "row_debounce" + str(r))) == 0) )
                ).Else(
                    rowchanging[r].eq(rowchanging[r])
                )
            ]

        db_fsm = ClockDomainsRenamer("kbd")(FSM(reset_state="IDLE"))
        self.submodules += db_fsm
        db_fsm.act("IDLE",
            If(rowchanging != 0,
                NextValue(debounce_timer, debounce_clocks),
                NextState("DEBOUNCING")
            )
        )
        db_fsm.act("DEBOUNCING",
            If(rowchanging == 0,
                NextValue(debounce_timer, debounce_timer - 1),
                If(debounce_timer == 0,
                    NextState("DEBOUNCED")
                )
            ).Else(
                NextValue(debounce_timer, debounce_clocks),
                NextState("DEBOUNCING")
            )
        )
        db_fsm.act("DEBOUNCED",
            If(scan_done,
                debounced.eq(1),
                NextState("IDLE")
            )
        )
        self.comb += clear_deltas.eq(all_zeros & clear_kbd & db_fsm.ongoing("IDLE"))

        # Fire an interrupt during the reset_scan phase.
        changed  = Signal()
        changed_sys = Signal()
        kp_r  = Signal()
        changed_kbd = Signal()
        debounced_d = Signal()
        self.sync.kbd += debounced_d.eq(debounced)
        self.kbd_wakeup = Signal()
        self.comb += changed.eq(debounced_d & (rowdiff != 0))
        self.sync.kbd += [
            If(changed,
                changed_kbd.eq(1)
            ).Elif(key_ack,
                changed_kbd.eq(0)
            ).Else(
                changed_kbd.eq(changed_kbd)
            )
        ]
        self.comb += self.kbd_wakeup.eq(changed_kbd | changed)

        self.specials += MultiReg(changed_kbd, changed_sys)
        self.sync += kp_r.eq(changed_sys)
        self.comb += self.ev.keypressed.trigger.eq(changed_sys & ~kp_r)

        self.submodules.pending_sync = BlindTransfer("sys", "kbd")
        # pulse-convert pending into a single pulse on the falling edge -- otherwise we get a pulse train
        kp_pending_sys = Signal()
        kp_pending_sys_r = Signal()
        self.sync += [
            kp_pending_sys.eq(self.ev.keypressed.pending),
            kp_pending_sys_r.eq(kp_pending_sys),
        ]
        self.comb += [
            self.pending_sync.i.eq(~kp_pending_sys & kp_pending_sys_r),
            pending_kbd.eq(self.pending_sync.o),
        ]
Esempio n. 3
0
    def __init__(self, platform, mem, minimal=False):

        self.intro = ModuleDoc("""FOMU Apple II+
            A virtual computer within a virtual computer inside a USB port.
            Instantiate 6502 processor with 1MHz core clock.
            Tie in to system memory as a wishbone master.
            Create virtual keyboard, video display and disk drive.
            """)

        addr = Signal(16)
        dout = Signal(8)
        din = Signal(8)
        wren = Signal()
        iosel = Signal()
        ior_addr = Signal(8)
        r_memsel = Signal()
        w_memsel = Signal()
        clk_en = Signal()  # Clock divider limits CPU activity
        active = Signal()  # CPU is active this cycle
        available = Signal()  # Key press ready for reading
        div1M = 4
        idlecount = Signal(div1M)  # Counter to slow CPU clock

        # Disk II Controller Registers
        disk_phase = Signal(4)
        disk_motor = Signal()
        disk_drive = Signal()
        #disk_write   = Signal()
        disk_reading = Signal()  # DOS trying to read sector
        disk_data_available = Signal()  # Data available for DOS to read
        disk_data_wanted = Signal()  # Data wanted by DOS
        disk_read = Signal()  # Data read by DOS so clear readable

        simulation = getenv("SIMULATION")
        synthesis = not simulation

        # Wishbone visible registers
        self.control = CSRStorage(fields=[
            CSRField(
                "Reset",
                reset=1 if synthesis else 0,  # auto-start in sim
                description="6502 Reset line - 1: Reset Asserted, 0: Running"),
            CSRField("RWROM", size=1, description="Allow writes to ROM"),
            #CSRField("Pause", description="Halt processor allowing stepping"),
            #CSRField("Step",  description="Single step 6502 one clock cycle"),
            #CSRField("NMI", size=1, description="Non-maskable interrupt"),
            #CSRField("IRQ", size=1, description="Maskable interrupt request"),
            #CSRField("RDY", size=1, description=""),
            CSRField(
                "Divisor",
                size=div1M,
                offset=8,
                reset=11 if synthesis else 0,  # Over-clock simulation
                description="Clock divider minius 1: 11 for 1MHz, 0 for 12MHz"
            ),
            #CSRField("DI", size=8, offset=16, description=""),
        ])
        self.keyboard = CSRStorage(8,
                                   write_from_dev=True,
                                   description="Keyboard input ($C000)")
        self.strobe = CSR(1)  #, description="Keyboard strobe ($C010)")
        self.screen = CSRStatus(
            fields=[
                CSRField("Character",
                         size=8,
                         description="Character written to screen"),
                CSRField("Valid", size=1, description="Character is valid"),
                CSRField(
                    "More",
                    size=1,
                    description="Additional characters are available to read"),
                #CSRField("Move", size=1,
                #    description="Character is not adjacent to previous"),
                CSRField("Repeat",
                         size=1,
                         offset=11,
                         description=
                         "Previous character repeated to current position"),
                CSRField("ScrollStart",
                         size=1,
                         offset=12,
                         description="Start of scroll region detected"),
                CSRField("ScrollEnd",
                         size=1,
                         offset=13,
                         description="End of scroll region"),
                CSRField(
                    "Horizontal",
                    size=6,
                    offset=16,
                    description="Location of current character in screen memory"
                ),
                CSRField(
                    "Vertical",
                    size=5,
                    offset=24,
                    description="Location of current character in screen memory"
                ),
            ],
            description="Video Display Output")
        self.diskctrl = CSRStatus(
            fields=[
                CSRField("Phase",
                         size=4,
                         description=
                         "Four phases of the track selection stepper motor"),
                CSRField("Motor", size=1, description="Drive is spinning"),
                CSRField(
                    "Drive",
                    size=1,
                    description="Drive select: drive 1 if clear, drive 2 if set"
                ),
                CSRField("Wanted",
                         size=1,
                         description="Drive is waiting for data"),
                CSRField("Pending",
                         size=1,
                         description="Drive has not yet read data written"),
                #CSRField("WriteMode", size=1,
                #    description="Drive is reading when clear, writing when set"),
            ],
            description="Disk drive control ($C0EX)")
        self.diskdata = CSRStorage(8, description="Disk drive data ($C0EC)")

        #self.bus=CSRStatus(32, fields=[
        #    CSRField("Addr", size=16, description="Address bus"),
        #    CSRField("Data", size=8, description="Data bus"),
        #    CSRField("WrEn", size=1, description="Write enable"),
        #    ], description="Address and data bus")
        #self.debug=CSRStatus(32, fields=[
        #    CSRField("PC", size=8,
        #        description="Program counter"),
        #    CSRField("A", size=8,
        #        description="Accumulator"),
        #    CSRField("X", size=8,
        #        description="X index register"),
        #    CSRField("Y", size=8,
        #        description="Y index register"),
        #    ], description="Address and data bus")

        if not minimal:
            # The minimal configuration provides all registers the host needs to
            # see so software can run unmodified. However, it does not implement
            # the 6502 to save gates. The video driver is also greatly reduced.

            # TODO eliminate wire [31:0] apple2_display_fifo_wrport_dat_r
            self.submodules.display_fifo = fifo.SyncFIFOBuffered(width=32,
                                                                 depth=256)

            self.comb += [
                mem.addr6502.eq(addr),
                mem.din6502.eq(dout),
                mem.wren6502.eq(wren),
                self.strobe.w.eq(available),
                #self.bus.fields.Addr.eq(addr),
                #self.bus.fields.Data.eq(dout),
                #self.bus.fields.WrEn.eq(wren),
                If(addr[8:16] == 0xC0, iosel.eq(1), w_memsel.eq(0),
                   mem.access6502.eq(0)).Else(iosel.eq(0), w_memsel.eq(1),
                                              mem.access6502.eq(active)),
                disk_read.eq(0),
                If(
                    r_memsel,
                    din.eq(mem.dout6502),
                ).Else(
                    # I/O Read Address Decoder (reading but not from memory)
                    If(
                        ior_addr[4:8] == 0x0,
                        din.eq(Cat(self.keyboard.storage[0:7], available)),
                    ),
                    # Disk II Controller Card in slot 6  (0x8 | 0x6)
                    # The only data to be read are locations C and D. Simplify the
                    # logic to only look at bit 0 of the address.
                    If(
                        ior_addr[4:8] == 0xE,
                        din[0:7].eq(self.diskdata.storage[0:7]),
                        # Write protect - TODO write is not supported
                        #If(ior_addr[0],
                        #    # Return high bit set - protected
                        #    din[7].eq(1)
                        #).Else(
                        disk_read.eq(1),
                        If(
                            disk_data_available | self.diskdata.re,
                            # Return byte given by host
                            din[7].eq(self.diskdata.storage[7]),
                        ).Else(
                            # Return high bit clear - data not available
                            din[7].eq(0), ),
                        #),
                    ),
                ),
                active.eq(clk_en & self.display_fifo.writable),
            ]

            self.sync += [
                # Slow clock to prototypical speed or as configured by user
                If(
                    idlecount == self.control.fields.Divisor,
                    idlecount.eq(0),
                    clk_en.eq(1),
                ).Else(
                    idlecount.eq(idlecount + 1),
                    clk_en.eq(0),
                ),
                # Read (DI) follows Write (AB/DO) by one clock cycle
                If(
                    active,
                    r_memsel.eq(w_memsel),
                    ior_addr.eq(addr[0:8]),
                ),
                # Keyboard key available when written by host
                If(
                    self.keyboard.re,
                    available.eq(1),
                ),
                If(
                    iosel,
                    # I/O Write Address Decoder
                    If(
                        addr[4:8] == 0x1,
                        # KBDSTRB
                        # Strobe cleared on read or write to KBDSTRB
                        # Any read or write to this address clears the pending key
                        available.eq(0),
                    ),
                ),
            ]

            self.specials += Instance(
                "cpu",
                i_clk=ClockSignal(),
                i_reset=ResetSignal() | self.control.storage[0],
                i_DI=din,
                # &(self.rand.we|self.cfg.storage[1])),
                o_AB=addr,  # .dat_w,
                o_DO=dout,  # .dat_w,
                o_WE=wren,
                i_IRQ=False,  # self.control.fields.IRQ,
                i_NMI=False,  # self.control.fields.NMI,  # seed.re,
                i_RDY=active,  # self.control.fields.RDY,
            )
            platform.add_source("rtl/verilog-6502/cpu.v")
            platform.add_source("rtl/verilog-6502/ALU.v")

            #===============================================================================
            #       Disk Drive - Emulate Disk II controller in slot 6
            #===============================================================================
            # The Disk II controller card has 16 addressable locations. Eight of these are
            # dedicated to moving the arm, two for motor control, two for drive selection
            # and four that handle read, write, and write protection detection.
            #===============================================================================

            self.comb += [
                self.diskctrl.fields.Phase.eq(disk_phase),
                self.diskctrl.fields.Motor.eq(disk_motor),
                self.diskctrl.fields.Drive.eq(disk_drive),
                #self.diskctrl.fields.WriteMode.eq(disk_write),
                self.diskctrl.fields.Wanted.eq(disk_data_wanted),
                self.diskctrl.fields.Pending.eq(disk_data_available),
            ]

            self.sync += [
                If(
                    self.diskdata.re,
                    disk_data_available.eq(1),
                ),
                # Set false again on read
                If(
                    active & disk_read,
                    disk_reading.eq(0),
                    If(
                        disk_data_available,
                        disk_data_wanted.eq(0),
                    ).Else(disk_data_wanted.eq(1), ),
                    disk_data_available.eq(0),
                ),
                If(
                    iosel,
                    # Disk II Controller Card in slot 6

                    # C0E0 PHASEOFF  Stepper motor phase 0 off.
                    # C0E1 PHASEON   Stepper motor phase 0 on.
                    # C0E2 PHASE1OFF Stepper motor phase 1 off.
                    # C0E3 PHASElON  Stepper motor phase 1 on.
                    # C0E4 PHASE2OFF Stepper motor phase 2 off.
                    # C0E5 PHASE2ON  Stepper notor phase 2 on.
                    # C0E6 PHASE3OFF Stepper motor phase 3 off.
                    # C0E7 PHASE3ON  Stepper motor phase 3 on.
                    # C0E8 MOTOROFF  Turn motor off.
                    # C0E9 MOTORON   Turn motor on.
                    # C0EA DRV0EN    Engage drive 1.
                    # C0EB DRV1EN    Engage drive 2.
                    # C0EC Q6L       Strobe Data Latch for I/O.
                    # C0ED Q6H       Load Data Latch.
                    # C0EE Q7H       Prepare latch for input (read from disk).
                    # C0EF Q7L       Prepare latch for output (write to disk).

                    # Q7L with Q6L = Read
                    # Q7L with Q6H = Sense Write Protect
                    # Q7H with Q6L = Write
                    # Q7H with Q6H = Load Write Latch
                    If(
                        addr[4:8] == 0xE,  # (8|6) 
                        # Addresses 0-7 simply update a bit in the status register
                        If(addr[0:4] == 0x0, disk_phase[0].eq(0)),
                        If(addr[0:4] == 0x1, disk_phase[0].eq(1)),
                        If(addr[0:4] == 0x2, disk_phase[1].eq(0)),
                        If(addr[0:4] == 0x3, disk_phase[1].eq(1)),
                        If(addr[0:4] == 0x4, disk_phase[2].eq(0)),
                        If(addr[0:4] == 0x5, disk_phase[2].eq(1)),
                        If(addr[0:4] == 0x6, disk_phase[3].eq(0)),
                        If(addr[0:4] == 0x7, disk_phase[3].eq(1)),
                        # Likewise, motor active and drive select update status
                        If(addr[0:4] == 0x8, disk_motor.eq(0)),
                        If(addr[0:4] == 0x9, disk_motor.eq(1)),
                        If(addr[0:4] == 0xA, disk_drive.eq(0)),
                        If(addr[0:4] == 0xB, disk_drive.eq(1)),
                        # Write is ignored and read must be delayed one clock tick
                        If(addr[0:4] == 0xC, disk_reading.eq(1)),
                        #If(addr[0:4]==0xD, disk_ior_wp.eq(1)),
                        #If(addr[0:4]==0xE, disk_write.eq(0)),
                        #If(addr[0:4]==0xF, disk_write.eq(1)),
                    ),
                ),
            ]

            #===============================================================================
            #       Video Output - Text Mode
            #===============================================================================
            # The Apple II screen memory contains 8 segments containing 3 rows each in a 128
            # byte block leaving 8 unused bytes in each of the 8 blocks To assist with
            # scroll detection, we convert memory addresses to screen coordinates.
            #===============================================================================

            # Video memory - Frame Buffer access shortcuts
            fbsel = Signal()
            fb_r = Signal()
            fb_w = Signal()

            # Conversion from memory address to X,Y screen coordinates
            segment = Signal(3)
            triple = Signal(7)
            third = Signal(2)
            horiz = Signal(6)
            vert = Signal(5)
            move = Signal()

            scroll_active = Signal()
            scroll_match = Signal()
            scroll_start = Signal()
            scroll_end = Signal()
            scroll_read = Signal()
            scroll_write_valid = Signal()
            scroll_next_col = Signal()
            scroll_next_row = Signal()
            scroll_sequential = Signal()
            read_horiz = Signal(6)
            read_vert = Signal(5)

            repeat_active = Signal()
            repeat_match = Signal()
            repeat_start = Signal()
            repeat_end = Signal()
            repeat_next_col = Signal()
            repeat_next_row = Signal()
            repeat_sequential = Signal()

            # Registers shared by scroll and repeat compression circuits
            #horiz_start = Signal(max=40)
            #vert_start = Signal(max=24)
            #horiz_end = Signal(max=40)
            #vert_end = Signal(max=24)
            prev_horiz = Signal(max=40)
            prev_vert = Signal(max=24)
            prev_char = Signal(8)
            prev_start = Signal()
            push_save = Signal()
            push_saving = Signal()

            fifo_out = Signal(32)

            self.comb += [
                # Detect access to frame memory: Address range 0x0400-0x7ff
                fbsel.eq((addr[10:15] == 0x1) & active),
                fb_r.eq(fbsel & ~wren),
                fb_w.eq(fbsel & wren),
                # Convert memory address to X,Y coordinates
                segment.eq(addr[7:10]),
                triple.eq(addr[0:7]),
                # TODO This generates reg - change to cause only wire
                If(
                    triple >= 80,
                    third.eq(2),
                    horiz.eq(addr[0:7] - 80),
                ).Else(
                    If(
                        triple >= 40,
                        third.eq(1),
                        horiz.eq(addr[0:7] - 40),
                    ).Else(
                        third.eq(0),
                        horiz.eq(addr[0:7]),
                    )),
                vert.eq(Cat(segment, third)),

                # TODO Detect scroll - frame buffer read immediately followed by
                # frame buffer write to character on previous line, directly above.
                # Scroll is Right to Left (asm: DEY at FC90 in Autostart ROM)
                scroll_match.eq((horiz == read_horiz)
                                & (vert + 1 == read_vert)),
                # & (din==read_char))  <== TODO Need to delay din by 1 cycle
                scroll_write_valid.eq(scroll_read & fb_w & scroll_match),
                scroll_start.eq(scroll_write_valid & ~scroll_active),
                # Scroll ends on any write that does not follow the required pattern
                scroll_end.eq(scroll_active & fb_w & ~scroll_write_valid),
                scroll_next_col.eq((horiz + 1 == prev_horiz)
                                   & (vert == prev_vert)),
                scroll_next_row.eq((horiz == 39) & (prev_horiz == 0)
                                   & (vert == prev_vert + 1)),
                scroll_sequential.eq(scroll_next_col | scroll_next_row),

                # Detect repeated charaters (spaces)
                # Clear is Left to Right (asm: INY at FCA2 in Autostart ROM)
                #               repeat_match.eq(fb_w & (dout==prev_char)),
                #               repeat_start.eq(repeat_match & repeat_sequential & ~repeat_active),
                #               repeat_end.eq(fb_w & repeat_active &
                #                   (~repeat_match |~repeat_sequential)),
                #               repeat_next_col.eq((horiz==prev_horiz+1) & (vert==prev_vert)),
                #               repeat_next_row.eq((horiz==0) & (prev_horiz==39) &
                #                   (vert==prev_vert+1)),
                #               repeat_sequential.eq(repeat_next_col | repeat_next_row),
                #               repeat_sequential.eq(repeat_next_col),  # This or the previous one

                # Place writes in the fifo
                self.display_fifo.din[8].eq(0),  # Valid is calculated
                self.display_fifo.din[9].eq(0),  # More is calculated
                #self.display_fifo.din[   10].eq(move),
                self.display_fifo.din[11].eq(repeat_end),
                If(
                    push_save,
                    self.display_fifo.din[0:8].eq(prev_char),
                    self.display_fifo.din[12].eq(prev_start),
                    self.display_fifo.din[13].eq(scroll_end),
                    #self.display_fifo.din[14:16].eq(0), # Reserved
                    self.display_fifo.din[16:22].eq(prev_horiz
                                                    ),  # 2 bits padding
                    self.display_fifo.din[24:29].eq(
                        prev_vert),  # 3 bits padding
                ).Elif(
                    push_saving,
                    # push_save will be valid on the next cycle - so push previous
                    self.display_fifo.din[0:8].eq(prev_char),
                    #self.display_fifo.din[    8].eq(0), # Valid is calculated
                    #self.display_fifo.din[    9].eq(0), # More is calculated
                    #self.display_fifo.din[   10].eq(move),
                    #self.display_fifo.din[   11].eq(repeat_end),
                    self.display_fifo.din[12].eq(scroll_start),
                    self.display_fifo.din[13].eq(scroll_end),
                    #self.display_fifo.din[14:16].eq(0), # Reserved
                    self.display_fifo.din[16:22].eq(prev_horiz
                                                    ),  # 2 bits padding
                    self.display_fifo.din[24:29].eq(
                        prev_vert),  # 3 bits padding
                ).Else(
                    #self.display_fifo.din.eq(Cat(dout, horiz, vert,
                    #    move, scroll_start, scroll_end, repeat_start, repeat_end)),
                    self.display_fifo.din[0:8].eq(dout),
                    #self.display_fifo.din[    8].eq(0), # Valid is calculated
                    #self.display_fifo.din[    9].eq(0), # More is calculated
                    #self.display_fifo.din[   10].eq(move),
                    #self.display_fifo.din[   11].eq(repeat_end),
                    self.display_fifo.din[12].eq(scroll_start),
                    self.display_fifo.din[13].eq(scroll_end),
                    #self.display_fifo.din[14:16].eq(0), # Reserved
                    self.display_fifo.din[16:22].eq(horiz),  # 2 bits padding
                    self.display_fifo.din[24:29].eq(vert),  # 3 bits padding
                ),
                self.display_fifo.we.eq(push_save | repeat_end | scroll_start
                                        | scroll_end
                                        | (fb_w & ~scroll_active
                                           & ~repeat_active & ~repeat_start)),
                push_saving.eq(((repeat_end & ~repeat_sequential) | scroll_end)
                               & ~push_save),

                # Retrieve characters from fifo
                self.display_fifo.re.eq(self.screen.we),
                self.screen.we.eq(self.display_fifo.re),
                self.screen.fields.Valid.eq(self.display_fifo.readable),
                self.screen.fields.More.eq(self.display_fifo.readable),
                self.screen.fields.Character.eq(fifo_out[0:8]),
                self.screen.fields.Horizontal.eq(fifo_out[16:22]),
                self.screen.fields.Vertical.eq(fifo_out[24:29]),
                #self.screen.fields.Move.eq(fifo_out[10]),
                self.screen.fields.Repeat.eq(fifo_out[11]),
                self.screen.fields.ScrollStart.eq(fifo_out[12]),
                self.screen.fields.ScrollEnd.eq(fifo_out[13]),
            ]

            self.sync += [
                fifo_out.eq(self.display_fifo.dout),

                # Scroll
                If(
                    scroll_start,
                    scroll_active.eq(1),
                    #horiz_start.eq(horiz),
                    #vert_start.eq(vert),
                    #horiz_end.eq(horiz),
                    #vert_end.eq(vert),
                ),
                If(
                    scroll_end,
                    push_save.eq(1),
                    scroll_active.eq(0),
                    # These happen on any write to the frame buffer
                    #prev_horiz.eq(horiz),
                    #prev_vert.eq(vert),
                    #prev_char.eq(dout),
                    prev_start.eq(scroll_start),
                ),
                If(
                    fb_r,
                    If((scroll_read & (scroll_sequential | ~scroll_active)) |
                       (repeat_active & repeat_sequential),
                       # A faithful 6502 model issues a read of the target
                       # address before the write cycle of an indirect store
                       # instruction.  Do nothing if we suspect the current read is
                       # actually part of a store instruction.
                       ).
                    Else(
                        scroll_read.eq(1),
                        read_horiz.eq(horiz),
                        read_vert.eq(vert),
                        # TODO read_char should be din on the next clock cycle
                        # read_char.eq(dout),
                    ),
                ),

                # Write to the frame buffer: remember the location and character
                # that generate the sequential and repeat signals which are used
                # by the scroll and clear functions. Also, update scroll signals.
                If(
                    fb_w,
                    scroll_read.eq(0),
                    prev_vert.eq(vert),
                    prev_horiz.eq(horiz),
                    prev_char.eq(dout),

                    # Repeat - Mostly needed for screen clearing operations but made
                    # generic to conserve bandwidth and allow any character to be
                    # repeated.
                    If(
                        repeat_match & repeat_sequential,
                        # Supress output, begin sequence if not started already
                        # Store location and character as this will be needed if the
                        # following write is not sequential
                        # Setting repeat is redundant if already active
                        repeat_active.eq(1),
                    ).Elif(
                        repeat_active & ~repeat_sequential,
                        # The cursor moved and we must terminate repeat mode
                        # indicating the last character in the sequence.  This is
                        # required whether or not the same character is present.
                        # Output saved location with Repeat flag set to mark end.
                        # Then output current signal set on next cycle
                        push_save.eq(1),
                        prev_start.eq(scroll_start),
                        repeat_active.eq(0),
                    ).Else(
                        # Push current character on stack either because:
                        # a. character is different breaking repeat
                        # b. character is different preventing repeat
                        # c. location is not sequential breaking repeat
                        # d. location is not sequential preventing repeat
                        # Cases a,c need to clear repeat. This is irrelevant for b,d
                        repeat_active.eq(0), ),
                ),
                # We can safely use the cycle after a write to frame memory as a
                # second push into the character fifo knowing that the only time the
                # 6502 has two consecutive write cycles is the JSR instruction which
                # writes the return address onto the stack, and the RMW instructions
                # INC, DEC, LSR, ASR, ROL, ROR which write the original and the new
                # values in two consecutive cycles. It is extremely poor programming
                # practice to keep the stack inside the frame buffer while clearing
                # or scrolling the screen so no special handling of these cases is
                # taken.
                If(
                    push_save,
                    # Auto-clear after one clock cycle
                    push_save.eq(0),
                ),
            ]
Esempio n. 4
0
    def __init__(self, usb_core):

        self.submodules.data_buf = buf = ResetInserter()(fifo.SyncFIFOBuffered(
            width=8, depth=66))

        self.data = data = CSRStatus(fields=[
            CSRField("data",
                     8,
                     description="The top byte of the receive FIFO."),
        ],
                                     description="""
                Data received from the host will go into a FIFO.  This register
                reflects the contents of the top byte in that FIFO.  Reading from
                this register advances the FIFO pointer.""")

        self.ctrl = ctrl = CSRStorage(fields=[
            CSRField(
                "epno",
                4,
                description=
                "The endpoint number to update the ``enable`` and ``status`` bits for."
            ),
            CSRField(
                "enable",
                description="Write a ``1`` here to enable receiving data"),
            CSRField(
                "reset",
                pulse=True,
                description="Write a ``1`` here to reset the ``OUT`` handler"),
            CSRField("stall",
                     description="Write a ``1`` here to stall an endpoint"),
        ],
                                      description="""
                Controls for receiving packet data.  To enable an endpoint, write its value to ``epno``,
                with the ``enable`` bit set to ``1`` to enable an endpoint, or ``0`` to disable it.
                Resetting the OutHandler will set all ``enable`` bits to 0.

                Similarly, you can adjust the ``STALL`` state by setting or clearing the ``stall`` bit."""
                                      )

        self.status = CSRStatus(
            fields=[
                CSRField(
                    "epno",
                    4,
                    description=
                    "The destination endpoint for the most recent ``OUT`` packet."
                ),
                CSRField("have",
                         description="``1`` if there is data in the FIFO."),
                CSRField("pend",
                         description="``1`` if there is an IRQ pending."),
            ],
            description="Status about the current state of the `OUT` endpoint."
        )

        self.submodules.ev = ev.EventManager()
        self.ev.submodules.packet = ev.EventSourcePulse(name="done",
                                                        description="""
            Indicates that an ``OUT`` packet has successfully been transferred
            from the host.  This bit must be cleared in order to receive
            additional packets.""")
        self.ev.finalize()

        self.usb_reset = Signal()

        self.stalled = Signal()
        self.enabled = Signal()
        stall_status = Signal(16)
        enable_status = Signal(16)
        ep_mask = Signal(16, reset=1)
        self.comb += [
            If(
                usb_core.setup | usb_core.commit,
                ep_mask.eq(1 << usb_core.endp),
            ).Else(ep_mask.eq(1 << ctrl.fields.epno), ),
            self.stalled.eq(stall_status >> usb_core.endp),
            self.enabled.eq(enable_status >> usb_core.endp),
        ]
        self.sync += [
            If(
                ctrl.fields.reset | self.usb_reset,
                stall_status.eq(0),
            ).Elif(
                usb_core.setup | (ctrl.re & ~ctrl.fields.stall),
                # If a SETUP packet comes in, clear the STALL bit.
                stall_status.eq(stall_status & ~ep_mask),
            ).Elif(
                ctrl.re,
                stall_status.eq(stall_status | ep_mask),
            ),
        ]

        # The endpoint number of the most recently received packet
        epno = Signal(4)

        # How to respond to requests:
        #  - 1 - ACK
        #  - 0 - NAK
        # Send a NAK if the buffer contains data, or if "ENABLE" has not been set.
        self.response = Signal()
        responding = Signal()
        is_out_packet = Signal()

        # Keep track of whether we're currently responding.
        self.comb += is_out_packet.eq(usb_core.tok == PID.OUT)
        self.comb += self.response.eq(self.enabled & is_out_packet
                                      & ~self.ev.packet.pending)
        self.sync += If(usb_core.poll, responding.eq(self.response))

        # Connect the buffer to the USB system
        self.data_recv_payload = Signal(8)
        self.data_recv_put = Signal()
        self.comb += [
            buf.din.eq(self.data_recv_payload),
            buf.we.eq(self.data_recv_put & responding),
            buf.reset.eq(ctrl.fields.reset),
            self.data.fields.data.eq(buf.dout),

            # When data is read, advance the FIFO
            buf.re.eq(data.we),
            self.status.fields.epno.eq(epno),
            self.status.fields.have.eq(buf.readable),
            self.status.fields.pend.eq(self.ev.packet.pending),

            # When data is successfully transferred, the buffer becomes full.
            # This is true even if "no" data was transferred, because the
            # buffer will then contain two bytes of CRC16 data.
            # Therefore, if the FIFO is readable, an interrupt must be triggered.
            self.ev.packet.trigger.eq(responding & usb_core.commit),
        ]

        # If we get a packet, turn off the "IDLE" flag and keep it off until the packet has finished.
        self.sync += [
            If(
                ctrl.fields.reset,
                enable_status.eq(0),
            ).Elif(
                usb_core.commit & responding,
                epno.eq(usb_core.endp),
                # Disable this EP when a transfer finishes
                enable_status.eq(enable_status & ~ep_mask),
                responding.eq(0),
            ).Elif(
                ctrl.re,
                # Enable or disable the EP as necessary
                If(
                    ctrl.fields.enable,
                    enable_status.eq(enable_status | ep_mask),
                ).Else(enable_status.eq(enable_status & ~ep_mask), ),
            ),
        ]
Esempio n. 5
0
    def __init__(self, usb_core):
        self.dtb = Signal()

        # Keep track of the current DTB for each of the 16 endpoints
        dtbs = Signal(16, reset=0x0001)

        # A list of endpoints that are stalled
        stall_status = Signal(16)

        self.submodules.data_buf = buf = ResetInserter()(fifo.SyncFIFOBuffered(
            width=8, depth=64))

        self.data = CSRStorage(fields=[
            CSRField("data",
                     8,
                     description="The next byte to add to the queue."),
        ],
                               description="""
                Each byte written into this register gets added to an outgoing FIFO. Any
                bytes that are written here will be transmitted in the order in which
                they were added.  The FIFO queue is automatically advanced with each write.
                The FIFO queue is 64 bytes deep.  If you exceed this amount, the result is undefined."""
                               )

        self.ctrl = ctrl = CSRStorage(fields=[
            CSRField(
                "epno",
                4,
                description=
                "The endpoint number for the transaction that is queued in the FIFO."
            ),
            CSRField("reset",
                     offset=5,
                     description=
                     "Write a ``1`` here to clear the contents of the FIFO.",
                     pulse=True),
            CSRField("stall",
                     description=
                     "Write a ``1`` here to stall the EP written in ``EP``.",
                     pulse=True),
        ],
                                      description="""
                Enables transmission of data in response to ``IN`` tokens,
                or resets the contents of the FIFO.""")

        self.status = CSRStatus(fields=[
            CSRField(
                "idle",
                description=
                "This value is ``1`` if the packet has finished transmitting."
            ),
            CSRField("have",
                     offset=4,
                     description="This value is ``0`` if the FIFO is empty."),
            CSRField("pend",
                     offset=5,
                     description="``1`` if there is an IRQ pending."),
        ],
                                description="""
                Status about the IN handler.  As soon as you write to `IN_DATA`,
                ``IN_STATUS.HAVE`` should go to ``1``.""")

        self.submodules.ev = ev.EventManager()
        self.ev.submodules.packet = ev.EventSourcePulse(name="done",
                                                        description="""
            Indicates that the host has successfully transferred an ``IN`` packet,
            and that the FIFO is now empty.
            """)
        self.ev.finalize()

        # Control bits
        ep_stall_mask = Signal(16)
        self.comb += [
            ep_stall_mask.eq(1 << ctrl.fields.epno),
        ]

        # Keep track of which endpoints are currently stalled
        self.stalled = Signal()
        self.comb += self.stalled.eq(stall_status >> usb_core.endp)
        self.sync += [
            If(
                ctrl.fields.reset,
                stall_status.eq(0),
            ).Elif(
                usb_core.setup | (ctrl.re & ~ctrl.fields.stall),
                # If a SETUP packet comes in, clear the STALL bit.
                stall_status.eq(stall_status & ~ep_stall_mask),
            ).Elif(
                ctrl.re,
                stall_status.eq(stall_status | ep_stall_mask),
            ),
        ]

        # How to respond to requests:
        #  - 0 - ACK
        #  - 1 - NAK
        self.response = Signal()

        # This value goes "1" when data is pending, and returns to "0" when it's done.
        queued = Signal()
        was_queued = Signal()

        # This goes to "1" when "queued" is 1 when a "start" occurs.  It is used
        # to avoid skipping packets when a packet is queued during a transmission.
        transmitted = Signal()

        self.dtb_reset = Signal()
        self.comb += [
            buf.reset.eq(ctrl.fields.reset
                         | (usb_core.commit & transmitted & queued)),
        ]

        # Outgoing data will be placed on this signal
        self.data_out = Signal(8)

        # This is "1" if `data_out` contains data
        self.data_out_have = Signal()

        # Pulse this to advance the data output
        self.data_out_advance = Signal()

        # Used to detect when an IN packet finished
        is_our_packet = Signal()
        is_in_packet = Signal()

        self.comb += [
            # We will respond with "ACK" if the register matches the current endpoint number
            self.response.eq(queued & is_our_packet & is_in_packet),

            # Wire up the "status" register
            self.status.fields.have.eq(buf.readable),
            self.status.fields.idle.eq(~queued),
            self.status.fields.pend.eq(self.ev.packet.pending),

            # Cause a trigger event when the `queued` value goes to 0
            self.ev.packet.trigger.eq(~queued & was_queued),
            self.dtb.eq(dtbs >> usb_core.endp),
            self.data_out.eq(buf.dout),
            self.data_out_have.eq(buf.readable),
            buf.re.eq(self.data_out_advance & is_in_packet & is_our_packet),
            buf.we.eq(self.data.re),
            buf.din.eq(self.data.storage),
            is_our_packet.eq(usb_core.endp == ctrl.fields.epno),
            is_in_packet.eq(usb_core.tok == PID.IN),
        ]

        self.sync += [
            If(
                ctrl.fields.reset,
                queued.eq(0),
                was_queued.eq(0),
                transmitted.eq(0),
                dtbs.eq(0x0001),
            ).Elif(
                self.dtb_reset,
                dtbs.eq(dtbs | 1),
            )
            # When the user updates the `ctrl` register, enable writing.
            .Elif(
                ctrl.re & ~ctrl.fields.stall,
                queued.eq(1),
            ).Elif(
                usb_core.poll & self.response,
                transmitted.eq(1),
            )
            # When the USB core finishes operating on this packet,
            # de-assert the queue flag
            .Elif(
                usb_core.commit & transmitted & self.response & ~self.stalled,
                queued.eq(0),
                transmitted.eq(0),
                # Toggle the "DTB" line if we transmitted data
                dtbs.eq(dtbs ^ (1 << ctrl.fields.epno)),
            ).Else(was_queued.eq(queued), ),
        ]
Esempio n. 6
0
            def __init__(self):
                self.submodules.data = buf = fifo.SyncFIFOBuffered(width=8,
                                                                   depth=10)

                # Indicates which byte of `SETUP` data we're currently on.
                data_byte = Signal(4)

                # If the incoming `SETUP` token indicates there will be
                # a DATA stage, this will be set to 1.
                self.have_data_stage = have_data_stage = Signal()

                # If the incoming `SETUP` token is an OUT packet, this
                # will be 1.
                self.is_in = is_in = Signal()

                self.empty = Signal()
                self.comb += self.empty.eq(~buf.readable)

                # Wire up the `STATUS` register
                self.comb += [
                    status.fields.have.eq(buf.readable),
                    status.fields.is_in.eq(is_in),
                    status.fields.epno.eq(epno),
                    status.fields.pend.eq(pending),
                    status.fields.data.eq(have_data_stage),
                ]

                # Wire up the "SETUP" endpoint.
                self.comb += [
                    # Set the FIFO output to be the current buffer HEAD
                    data.fields.data.eq(buf.dout),

                    # Advance the FIFO when a byte is read
                    buf.re.eq(data.we),
                    If(
                        usb_core.tok == PID.SETUP,
                        buf.din.eq(data_recv_payload),
                        buf.we.eq(data_recv_put),
                    ),

                    # Tie the trigger to the STATUS.HAVE bit
                    trigger.eq(buf.readable & usb_core.setup),
                ]

                self.sync += [
                    # The 6th and 7th bytes of SETUP data are
                    # the wLength field.  If these are nonzero,
                    # then there will be a Data stage following
                    # this Setup stage.
                    If(
                        data_recv_put,
                        If(
                            data_byte == 0,
                            epno.eq(usb_core.endp),
                            is_in.eq(data_recv_payload[7]),
                        ).Elif(
                            data_byte == 6,
                            If(
                                data_recv_payload,
                                have_data_stage.eq(1),
                            ),
                        ).Elif(
                            data_byte == 7,
                            If(
                                data_recv_payload,
                                have_data_stage.eq(1),
                            ),
                        ),
                        data_byte.eq(data_byte + 1),
                    )
                ]