Esempio n. 1
0
static void xlnx_zynqmp_qspips_update_ixr(XlnxZynqMPQSPIPS *s)
{
    uint32_t gqspi_int;
    int new_irqline;

    s->regs[R_GQSPI_ISR] &= ~IXR_SELF_CLEAR;
    s->regs[R_GQSPI_ISR] |=
        (fifo32_is_empty(&s->fifo_g) ? IXR_GENERIC_FIFO_EMPTY : 0) |
        (fifo32_is_full(&s->fifo_g) ? IXR_GENERIC_FIFO_FULL : 0) |
        (s->fifo_g.fifo.num < s->regs[R_GQSPI_GFIFO_THRESH] ?
                                    IXR_GENERIC_FIFO_NOT_FULL : 0) |
        (fifo8_is_empty(&s->rx_fifo_g) ? IXR_RX_FIFO_EMPTY : 0) |
        (fifo8_is_full(&s->rx_fifo_g) ? IXR_RX_FIFO_FULL : 0) |
        (s->rx_fifo_g.num >= s->regs[R_GQSPI_RX_THRESH] ?
                                    IXR_RX_FIFO_NOT_EMPTY : 0) |
        (fifo8_is_empty(&s->tx_fifo_g) ? IXR_TX_FIFO_EMPTY : 0) |
        (fifo8_is_full(&s->tx_fifo_g) ? IXR_TX_FIFO_FULL : 0) |
        (s->tx_fifo_g.num < s->regs[R_GQSPI_TX_THRESH] ?
                                    IXR_TX_FIFO_NOT_FULL : 0);

    /* GQSPI Interrupt Trigger Status */
    gqspi_int = (~s->regs[R_GQSPI_IMR]) & s->regs[R_GQSPI_ISR] & GQSPI_IXR_MASK;
    new_irqline = !!(gqspi_int & IXR_ALL);

    /* drive external interrupt pin */
    if (new_irqline != s->gqspi_irqline) {
        s->gqspi_irqline = new_irqline;
        qemu_set_irq(XILINX_SPIPS(s)->irq, s->gqspi_irqline);
    }
}
Esempio n. 2
0
static void spi_flush_txfifo(XilinxSPI *s)
{
    uint32_t tx;
    uint32_t rx;

    while (!fifo8_is_empty(&s->tx_fifo)) {
        tx = (uint32_t)fifo8_pop(&s->tx_fifo);
        DB_PRINT("data tx:%x\n", tx);
        rx = ssi_transfer(s->spi, tx);
        DB_PRINT("data rx:%x\n", rx);
        if (fifo8_is_full(&s->rx_fifo)) {
            s->regs[R_IPISR] |= IRQ_DRR_OVERRUN;
        } else {
            fifo8_push(&s->rx_fifo, (uint8_t)rx);
            if (fifo8_is_full(&s->rx_fifo)) {
                s->regs[R_SPISR] |= SR_RX_FULL;
                s->regs[R_IPISR] |= IRQ_DRR_FULL;
            }
        }

        s->regs[R_SPISR] &= ~SR_RX_EMPTY;
        s->regs[R_SPISR] &= ~SR_TX_FULL;
        s->regs[R_SPISR] |= SR_TX_EMPTY;

        s->regs[R_IPISR] |= IRQ_DTR_EMPTY;
        s->regs[R_IPISR] |= IRQ_DRR_NOT_EMPTY;
    }

}
Esempio n. 3
0
static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
{
    for (;;) {
        int i;
        uint8_t rx;
        uint8_t tx = 0;

        for (i = 0; i < num_effective_busses(s); ++i) {
            if (!i || s->snoop_state == SNOOP_STRIPING) {
                if (fifo8_is_empty(&s->tx_fifo)) {
                    s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW;
                    xilinx_spips_update_ixr(s);
                    return;
                } else {
                    tx = fifo8_pop(&s->tx_fifo);
                }
            }
            rx = ssi_transfer(s->spi[i], (uint32_t)tx);
            DB_PRINT("tx = %02x rx = %02x\n", tx, rx);
            if (!i || s->snoop_state == SNOOP_STRIPING) {
                if (fifo8_is_full(&s->rx_fifo)) {
                    s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW;
                    DB_PRINT("rx FIFO overflow");
                } else {
                    fifo8_push(&s->rx_fifo, (uint8_t)rx);
                }
            }
        }

        switch (s->snoop_state) {
        case (SNOOP_CHECKING):
            switch (tx) { /* new instruction code */
            case READ: /* 3 address bytes, no dummy bytes/cycles */
            case PP:
            case DPP:
            case QPP:
                s->snoop_state = 3;
                break;
            case FAST_READ: /* 3 address bytes, 1 dummy byte */
            case DOR:
            case QOR:
            case DIOR: /* FIXME: these vary between vendor - set to spansion */
                s->snoop_state = 4;
                break;
            case QIOR: /* 3 address bytes, 2 dummy bytes */
                s->snoop_state = 6;
                break;
            default:
                s->snoop_state = SNOOP_NONE;
            }
            break;
        case (SNOOP_STRIPING):
        case (SNOOP_NONE):
            break;
        default:
            s->snoop_state--;
        }
    }
}
Esempio n. 4
0
static inline void tx_data_bytes(XilinxSPIPS *s, uint32_t value, int num)
{
    int i;
    for (i = 0; i < num && !fifo8_is_full(&s->tx_fifo); ++i) {
        if (s->regs[R_CONFIG] & ENDIAN) {
            fifo8_push(&s->tx_fifo, (uint8_t)(value >> 24));
            value <<= 8;
        } else {
Esempio n. 5
0
static void xilinx_spips_update_ixr(XilinxSPIPS *s)
{
    if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) {
        s->regs[R_INTR_STATUS] &= ~IXR_SELF_CLEAR;
        s->regs[R_INTR_STATUS] |=
            (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
            (s->rx_fifo.num >= s->regs[R_RX_THRES] ?
                                    IXR_RX_FIFO_NOT_EMPTY : 0) |
            (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
            (fifo8_is_empty(&s->tx_fifo) ? IXR_TX_FIFO_EMPTY : 0) |
            (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
    }
    int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] &
                                                                IXR_ALL);
    if (new_irqline != s->irqline) {
        s->irqline = new_irqline;
        qemu_set_irq(s->irq, s->irqline);
    }
}
Esempio n. 6
0
static void xilinx_spips_update_ixr(XilinxSPIPS *s)
{
    /* These are set/cleared as they occur */
    s->regs[R_INTR_STATUS] &= (IXR_TX_FIFO_UNDERFLOW | IXR_RX_FIFO_OVERFLOW |
                                IXR_TX_FIFO_MODE_FAIL);
    /* these are pure functions of fifo state, set them here */
    s->regs[R_INTR_STATUS] |=
        (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) |
        (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) |
        (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) |
        (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0);
    /* drive external interrupt pin */
    int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] &
                                                                IXR_ALL);
    if (new_irqline != s->irqline) {
        s->irqline = new_irqline;
        qemu_set_irq(s->irq, s->irqline);
    }
}
Esempio n. 7
0
static inline void tx_data_bytes(Fifo8 *fifo, uint32_t value, int num, bool be)
{
    int i;
    for (i = 0; i < num && !fifo8_is_full(fifo); ++i) {
        if (be) {
            fifo8_push(fifo, (uint8_t)(value >> 24));
            value <<= 8;
        } else {
            fifo8_push(fifo, (uint8_t)value);
            value >>= 8;
        }
    }
Esempio n. 8
0
static void xlx_spi_update_irq(XilinxSPI *s)
{
    uint32_t pending;

    s->regs[R_IPISR] |=
            (!fifo8_is_empty(&s->rx_fifo) ? IRQ_DRR_NOT_EMPTY : 0) |
            (fifo8_is_full(&s->rx_fifo) ? IRQ_DRR_FULL : 0);

    pending = s->regs[R_IPISR] & s->regs[R_IPIER];

    pending = pending && (s->regs[R_DGIER] & R_DGIER_IE);
    pending = !!pending;

    /* This call lies right in the data paths so don't call the
       irq chain unless things really changed.  */
    if (pending != s->irqline) {
        s->irqline = pending;
        DB_PRINT("irq_change of state %d ISR:%x IER:%X\n",
                    pending, s->regs[R_IPISR], s->regs[R_IPIER]);
        qemu_set_irq(s->irq, pending);
    }

}
Esempio n. 9
0
static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
{
    int debug_level = 0;

    for (;;) {
        int i;
        uint8_t tx = 0;
        uint8_t tx_rx[num_effective_busses(s)];

        if (fifo8_is_empty(&s->tx_fifo)) {
            if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) {
                s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW;
            }
            xilinx_spips_update_ixr(s);
            return;
        } else if (s->snoop_state == SNOOP_STRIPING) {
            for (i = 0; i < num_effective_busses(s); ++i) {
                tx_rx[i] = fifo8_pop(&s->tx_fifo);
            }
            stripe8(tx_rx, num_effective_busses(s), false);
        } else {
            tx = fifo8_pop(&s->tx_fifo);
            for (i = 0; i < num_effective_busses(s); ++i) {
                tx_rx[i] = tx;
            }
        }

        for (i = 0; i < num_effective_busses(s); ++i) {
            DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]);
            tx_rx[i] = ssi_transfer(s->spi[i], (uint32_t)tx_rx[i]);
            DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]);
        }

        if (fifo8_is_full(&s->rx_fifo)) {
            s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW;
            DB_PRINT_L(0, "rx FIFO overflow");
        } else if (s->snoop_state == SNOOP_STRIPING) {
            stripe8(tx_rx, num_effective_busses(s), true);
            for (i = 0; i < num_effective_busses(s); ++i) {
                fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[i]);
            }
        } else {
           fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[0]);
        }

        DB_PRINT_L(debug_level, "initial snoop state: %x\n",
                   (unsigned)s->snoop_state);
        switch (s->snoop_state) {
        case (SNOOP_CHECKING):
            switch (tx) { /* new instruction code */
            case READ: /* 3 address bytes, no dummy bytes/cycles */
            case PP:
            case DPP:
            case QPP:
                s->snoop_state = 3;
                break;
            case FAST_READ: /* 3 address bytes, 1 dummy byte */
            case DOR:
            case QOR:
            case DIOR: /* FIXME: these vary between vendor - set to spansion */
                s->snoop_state = 4;
                break;
            case QIOR: /* 3 address bytes, 2 dummy bytes */
                s->snoop_state = 6;
                break;
            default:
                s->snoop_state = SNOOP_NONE;
            }
            break;
        case (SNOOP_STRIPING):
        case (SNOOP_NONE):
            /* Once we hit the boring stuff - squelch debug noise */
            if (!debug_level) {
                DB_PRINT_L(0, "squelching debug info ....\n");
                debug_level = 1;
            }
            break;
        default:
            s->snoop_state--;
        }
        DB_PRINT_L(debug_level, "final snoop state: %x\n",
                   (unsigned)s->snoop_state);
    }
}
Esempio n. 10
0
static void
spi_write(void *opaque, hwaddr addr,
            uint64_t val64, unsigned int size)
{
    XilinxSPI *s = opaque;
    uint32_t value = val64;

    DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, value);
    addr >>= 2;
    switch (addr) {
    case R_SRR:
        if (value != 0xa) {
            DB_PRINT("Invalid write to SRR %x\n", value);
        } else {
            xlx_spi_do_reset(s);
        }
        break;

    case R_SPIDTR:
        s->regs[R_SPISR] &= ~SR_TX_EMPTY;
        fifo8_push(&s->tx_fifo, (uint8_t)value);
        if (fifo8_is_full(&s->tx_fifo)) {
            s->regs[R_SPISR] |= SR_TX_FULL;
        }
        if (!spi_master_enabled(s)) {
            goto done;
        } else {
            DB_PRINT("DTR and master enabled\n");
        }
        spi_flush_txfifo(s);
        break;

    case R_SPISR:
        DB_PRINT("Invalid write to SPISR %x\n", value);
        break;

    case R_IPISR:
        /* Toggle the bits.  */
        s->regs[addr] ^= value;
        break;

    /* Slave Select Register.  */
    case R_SPISSR:
        s->regs[addr] = value;
        xlx_spi_update_cs(s);
        break;

    case R_SPICR:
        /* FIXME: reset irq and sr state to empty queues.  */
        if (value & R_SPICR_RXFF_RST) {
            rxfifo_reset(s);
        }

        if (value & R_SPICR_TXFF_RST) {
            txfifo_reset(s);
        }
        value &= ~(R_SPICR_RXFF_RST | R_SPICR_TXFF_RST);
        s->regs[addr] = value;

        if (!(value & R_SPICR_MTI)) {
            spi_flush_txfifo(s);
        }
        break;

    default:
        if (addr < ARRAY_SIZE(s->regs)) {
            s->regs[addr] = value;
        }
        break;
    }

done:
    xlx_spi_update_irq(s);
}
Esempio n. 11
0
static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
{
    int debug_level = 0;
    XilinxQSPIPS *q = (XilinxQSPIPS *) object_dynamic_cast(OBJECT(s),
                                                           TYPE_XILINX_QSPIPS);

    for (;;) {
        int i;
        uint8_t tx = 0;
        uint8_t tx_rx[MAX_NUM_BUSSES] = { 0 };
        uint8_t dummy_cycles = 0;
        uint8_t addr_length;

        if (fifo8_is_empty(&s->tx_fifo)) {
            xilinx_spips_update_ixr(s);
            return;
        } else if (s->snoop_state == SNOOP_STRIPING) {
            for (i = 0; i < num_effective_busses(s); ++i) {
                tx_rx[i] = fifo8_pop(&s->tx_fifo);
            }
            stripe8(tx_rx, num_effective_busses(s), false);
        } else if (s->snoop_state >= SNOOP_ADDR) {
            tx = fifo8_pop(&s->tx_fifo);
            for (i = 0; i < num_effective_busses(s); ++i) {
                tx_rx[i] = tx;
            }
        } else {
            /* Extract a dummy byte and generate dummy cycles according to the
             * link state */
            tx = fifo8_pop(&s->tx_fifo);
            dummy_cycles = 8 / s->link_state;
        }

        for (i = 0; i < num_effective_busses(s); ++i) {
            int bus = num_effective_busses(s) - 1 - i;
            if (dummy_cycles) {
                int d;
                for (d = 0; d < dummy_cycles; ++d) {
                    tx_rx[0] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[0]);
                }
            } else {
                DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]);
                tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]);
                DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]);
            }
        }

        if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) {
            DB_PRINT_L(debug_level, "dircarding drained rx byte\n");
            /* Do nothing */
        } else if (s->rx_discard) {
            DB_PRINT_L(debug_level, "dircarding discarded rx byte\n");
            s->rx_discard -= 8 / s->link_state;
        } else if (fifo8_is_full(&s->rx_fifo)) {
            s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW;
            DB_PRINT_L(0, "rx FIFO overflow");
        } else if (s->snoop_state == SNOOP_STRIPING) {
            stripe8(tx_rx, num_effective_busses(s), true);
            for (i = 0; i < num_effective_busses(s); ++i) {
                fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[i]);
                DB_PRINT_L(debug_level, "pushing striped rx byte\n");
            }
        } else {
           DB_PRINT_L(debug_level, "pushing unstriped rx byte\n");
           fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[0]);
        }

        if (s->link_state_next_when) {
            s->link_state_next_when--;
            if (!s->link_state_next_when) {
                s->link_state = s->link_state_next;
            }
        }

        DB_PRINT_L(debug_level, "initial snoop state: %x\n",
                   (unsigned)s->snoop_state);
        switch (s->snoop_state) {
        case (SNOOP_CHECKING):
            /* Store the count of dummy bytes in the txfifo */
            s->cmd_dummies = xilinx_spips_num_dummies(q, tx);
            addr_length = get_addr_length(s, tx);
            if (s->cmd_dummies < 0) {
                s->snoop_state = SNOOP_NONE;
            } else {
                s->snoop_state = SNOOP_ADDR + addr_length - 1;
            }
            switch (tx) {
            case DPP:
            case DOR:
            case DOR_4:
                s->link_state_next = 2;
                s->link_state_next_when = addr_length + s->cmd_dummies;
                break;
            case QPP:
            case QPP_4:
            case QOR:
            case QOR_4:
                s->link_state_next = 4;
                s->link_state_next_when = addr_length + s->cmd_dummies;
                break;
            case DIOR:
            case DIOR_4:
                s->link_state = 2;
                break;
            case QIOR:
            case QIOR_4:
                s->link_state = 4;
                break;
            }
            break;
        case (SNOOP_ADDR):
            /* Address has been transmitted, transmit dummy cycles now if
             * needed */
            if (s->cmd_dummies < 0) {
                s->snoop_state = SNOOP_NONE;
            } else {
                s->snoop_state = s->cmd_dummies;
            }
            break;
        case (SNOOP_STRIPING):
        case (SNOOP_NONE):
            /* Once we hit the boring stuff - squelch debug noise */
            if (!debug_level) {
                DB_PRINT_L(0, "squelching debug info ....\n");
                debug_level = 1;
            }
            break;
        default:
            s->snoop_state--;
        }
        DB_PRINT_L(debug_level, "final snoop state: %x\n",
                   (unsigned)s->snoop_state);
    }
}
Esempio n. 12
0
static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s)
{
    while (s->regs[R_GQSPI_DATA_STS] || !fifo32_is_empty(&s->fifo_g)) {
        uint8_t tx_rx[2] = { 0 };
        int num_stripes = 1;
        uint8_t busses;
        int i;

        if (!s->regs[R_GQSPI_DATA_STS]) {
            uint8_t imm;

            s->regs[R_GQSPI_GF_SNAPSHOT] = fifo32_pop(&s->fifo_g);
            DB_PRINT_L(0, "GQSPI command: %x\n", s->regs[R_GQSPI_GF_SNAPSHOT]);
            if (!s->regs[R_GQSPI_GF_SNAPSHOT]) {
                DB_PRINT_L(0, "Dummy GQSPI Delay Command Entry, Do nothing");
                continue;
            }
            xlnx_zynqmp_qspips_update_cs_lines(s);

            imm = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA);
            if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) {
                /* immedate transfer */
                if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) ||
                    ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
                    s->regs[R_GQSPI_DATA_STS] = 1;
                /* CS setup/hold - do nothing */
                } else {
                    s->regs[R_GQSPI_DATA_STS] = 0;
                }
            } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, EXPONENT)) {
                if (imm > 31) {
                    qemu_log_mask(LOG_UNIMP, "QSPI exponential transfer too"
                                  " long - 2 ^ %" PRId8 " requested\n", imm);
                }
                s->regs[R_GQSPI_DATA_STS] = 1ul << imm;
            } else {
                s->regs[R_GQSPI_DATA_STS] = imm;
            }
        }
        /* Zero length transfer check */
        if (!s->regs[R_GQSPI_DATA_STS]) {
            continue;
        }
        if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE) &&
            fifo8_is_full(&s->rx_fifo_g)) {
            /* No space in RX fifo for transfer - try again later */
            return;
        }
        if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, STRIPE) &&
            (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) ||
             ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE))) {
            num_stripes = 2;
        }
        if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) {
            tx_rx[0] = ARRAY_FIELD_EX32(s->regs,
                                        GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA);
        } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT)) {
            for (i = 0; i < num_stripes; ++i) {
                if (!fifo8_is_empty(&s->tx_fifo_g)) {
                    tx_rx[i] = fifo8_pop(&s->tx_fifo_g);
                    s->tx_fifo_g_align++;
                } else {
                    return;
                }
            }
        }
        if (num_stripes == 1) {
            /* mirror */
            tx_rx[1] = tx_rx[0];
        }
        busses = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_BUS_SELECT);
        for (i = 0; i < 2; ++i) {
            DB_PRINT_L(1, "bus %d tx = %02x\n", i, tx_rx[i]);
            tx_rx[i] = ssi_transfer(XILINX_SPIPS(s)->spi[i], tx_rx[i]);
            DB_PRINT_L(1, "bus %d rx = %02x\n", i, tx_rx[i]);
        }
        if (s->regs[R_GQSPI_DATA_STS] > 1 &&
            busses == 0x3 && num_stripes == 2) {
            s->regs[R_GQSPI_DATA_STS] -= 2;
        } else if (s->regs[R_GQSPI_DATA_STS] > 0) {
            s->regs[R_GQSPI_DATA_STS]--;
        }
        if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
            for (i = 0; i < 2; ++i) {
                if (busses & (1 << i)) {
                    DB_PRINT_L(1, "bus %d push_byte = %02x\n", i, tx_rx[i]);
                    fifo8_push(&s->rx_fifo_g, tx_rx[i]);
                    s->rx_fifo_g_align++;
                }
            }
        }
        if (!s->regs[R_GQSPI_DATA_STS]) {
            for (; s->tx_fifo_g_align % 4; s->tx_fifo_g_align++) {
                fifo8_pop(&s->tx_fifo_g);
            }
            for (; s->rx_fifo_g_align % 4; s->rx_fifo_g_align++) {
                fifo8_push(&s->rx_fifo_g, 0);
            }
        }
    }
}