diff mbox

[for-2.1,v2,2/2] util/fifo: Generalise for common integer widths

Message ID d62027a6230c84d51394c406a240d142bb889810.1396920202.git.peter.crosthwaite@xilinx.com
State New
Headers show

Commit Message

Peter Crosthwaite April 8, 2014, 2:05 a.m. UTC
Add support for 16, 32 and 64 bit width FIFOs. The push and pop
functions are patched to accept uint64_t always to support up to 64bit
integer elements. The element width is set at creation time.

The backing storage for all element types is still uint8_t regardless of
element width so some save-load logic is needed to handle endianness
issue WRT VMSD.

Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
---

 hw/char/serial.c        |   4 +-
 hw/net/allwinner_emac.c |   6 +--
 hw/ssi/xilinx_spi.c     |   4 +-
 hw/ssi/xilinx_spips.c   |   4 +-
 include/qemu/fifo.h     |  19 +++++----
 util/fifo.c             | 109 +++++++++++++++++++++++++++++++++++++++++-------
 6 files changed, 114 insertions(+), 32 deletions(-)

Comments

Beniamino Galvani April 8, 2014, 9:42 p.m. UTC | #1
On Mon, Apr 07, 2014 at 07:05:18PM -0700, Peter Crosthwaite wrote:
> Add support for 16, 32 and 64 bit width FIFOs. The push and pop
> functions are patched to accept uint64_t always to support up to 64bit
> integer elements. The element width is set at creation time.
> 
> The backing storage for all element types is still uint8_t regardless of
> element width so some save-load logic is needed to handle endianness
> issue WRT VMSD.
> 
> Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
> ---
> 
>  hw/char/serial.c        |   4 +-
>  hw/net/allwinner_emac.c |   6 +--
>  hw/ssi/xilinx_spi.c     |   4 +-
>  hw/ssi/xilinx_spips.c   |   4 +-
>  include/qemu/fifo.h     |  19 +++++----
>  util/fifo.c             | 109 +++++++++++++++++++++++++++++++++++++++++-------
>  6 files changed, 114 insertions(+), 32 deletions(-)
> 
> diff --git a/hw/char/serial.c b/hw/char/serial.c
> index a0d8024..3060769 100644
> --- a/hw/char/serial.c
> +++ b/hw/char/serial.c
> @@ -659,8 +659,8 @@ void serial_realize_core(SerialState *s, Error **errp)
>  
>      qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
>                            serial_event, s);
> -    fifo_create(&s->recv_fifo, UART_FIFO_LENGTH);
> -    fifo_create(&s->xmit_fifo, UART_FIFO_LENGTH);
> +    fifo_create(&s->recv_fifo, UART_FIFO_LENGTH, 8);
> +    fifo_create(&s->xmit_fifo, UART_FIFO_LENGTH, 8);
>  }
>  
>  void serial_exit_core(SerialState *s)
> diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
> index e804411..2a0d2ac 100644
> --- a/hw/net/allwinner_emac.c
> +++ b/hw/net/allwinner_emac.c
> @@ -455,9 +455,9 @@ static void aw_emac_realize(DeviceState *dev, Error **errp)
>                            object_get_typename(OBJECT(dev)), dev->id, s);
>      qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
>  
> -    fifo_create(&s->rx_fifo, RX_FIFO_SIZE);
> -    fifo_create(&s->tx_fifo[0], TX_FIFO_SIZE);
> -    fifo_create(&s->tx_fifo[1], TX_FIFO_SIZE);
> +    fifo_create(&s->rx_fifo, RX_FIFO_SIZE, 8);
> +    fifo_create(&s->tx_fifo[0], TX_FIFO_SIZE, 8);
> +    fifo_create(&s->tx_fifo[1], TX_FIFO_SIZE, 8);
>  }
>  
>  static Property aw_emac_properties[] = {
> diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c
> index 8fe3072..cac666b 100644
> --- a/hw/ssi/xilinx_spi.c
> +++ b/hw/ssi/xilinx_spi.c
> @@ -341,8 +341,8 @@ static int xilinx_spi_init(SysBusDevice *sbd)
>  
>      s->irqline = -1;
>  
> -    fifo_create(&s->tx_fifo, FIFO_CAPACITY);
> -    fifo_create(&s->rx_fifo, FIFO_CAPACITY);
> +    fifo_create(&s->tx_fifo, FIFO_CAPACITY, 8);
> +    fifo_create(&s->rx_fifo, FIFO_CAPACITY, 8);
>  
>      return 0;
>  }
> diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
> index d7d4c41..a7a639f 100644
> --- a/hw/ssi/xilinx_spips.c
> +++ b/hw/ssi/xilinx_spips.c
> @@ -669,8 +669,8 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
>  
>      s->irqline = -1;
>  
> -    fifo_create(&s->rx_fifo, xsc->rx_fifo_size);
> -    fifo_create(&s->tx_fifo, xsc->tx_fifo_size);
> +    fifo_create(&s->rx_fifo, xsc->rx_fifo_size, 8);
> +    fifo_create(&s->tx_fifo, xsc->tx_fifo_size, 8);
>  }
>  
>  static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
> diff --git a/include/qemu/fifo.h b/include/qemu/fifo.h
> index 44766b3..8738027 100644
> --- a/include/qemu/fifo.h
> +++ b/include/qemu/fifo.h
> @@ -5,8 +5,12 @@
>  
>  typedef struct {
>      /* All fields are private */
> +    int width; /* byte width each each element */

of each element

> +    uint32_t capacity; /* number of elements */
> +
>      uint8_t *data;
> -    uint32_t capacity;
> +    uint32_t buffer_size;
> +
>      uint32_t head;
>      uint32_t num;
>  } Fifo;
> @@ -14,13 +18,14 @@ typedef struct {
>  /**
>   * fifo_create:
>   * @fifo: struct Fifo to initialise with new FIFO
> - * @capacity: capacity of the newly created FIFO
> + * @capacity: capacity (number of elements) of the newly created FIFO
> + * @width: integer width of each element. Must be 8, 16, 32 or 64.
>   *
>   * Create a FIFO of the specified size. Clients should call fifo_destroy()
>   * when finished using the fifo. The FIFO is initially empty.
>   */
>  
> -void fifo_create(Fifo *fifo, uint32_t capacity);
> +void fifo_create(Fifo *fifo, uint32_t capacity, int width);
>  
>  /**
>   * fifo_destroy:
> @@ -41,7 +46,7 @@ void fifo_destroy(Fifo *fifo);
>   * Clients are responsible for checking for fullness using fifo_is_full().
>   */
>  
> -void fifo_push(Fifo *fifo, uint8_t data);
> +void fifo_push(Fifo *fifo, uint64_t data);
>  
>  /**
>   * fifo_push_all:
> @@ -54,7 +59,7 @@ void fifo_push(Fifo *fifo, uint8_t data);
>   * fifo_num_free().
>   */
>  
> -void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num);
> +void fifo_push_all(Fifo *fifo, const void *data, uint32_t num);
>  
>  /**
>   * fifo_pop:
> @@ -66,7 +71,7 @@ void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num);
>   * Returns: The popped data value.
>   */
>  
> -uint8_t fifo_pop(Fifo *fifo);
> +uint64_t fifo_pop(Fifo *fifo);
>  
>  /**
>   * fifo_pop_buf:
> @@ -93,7 +98,7 @@ uint8_t fifo_pop(Fifo *fifo);
>   * Returns: A pointer to popped data.
>   */
>  
> -const uint8_t *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num);
> +const void *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num);
>  
>  /**
>   * fifo_reset:
> diff --git a/util/fifo.c b/util/fifo.c
> index ffadf55..51318ad 100644
> --- a/util/fifo.c
> +++ b/util/fifo.c
> @@ -15,9 +15,11 @@
>  #include "qemu-common.h"
>  #include "qemu/fifo.h"
>  
> -void fifo_create(Fifo *fifo, uint32_t capacity)
> +void fifo_create(Fifo *fifo, uint32_t capacity, int width)
>  {
> -    fifo->data = g_new(uint8_t, capacity);
> +    assert(width == 8 || width == 16 || width == 32 || width == 64);
> +    fifo->width = width / 8;
> +    fifo->data = g_new(uint8_t, capacity * fifo->width);
>      fifo->capacity = capacity;
>      fifo->head = 0;
>      fifo->num = 0;
> @@ -28,16 +30,33 @@ void fifo_destroy(Fifo *fifo)
>      g_free(fifo->data);
>  }
>  
> -void fifo_push(Fifo *fifo, uint8_t data)
> +void fifo_push(Fifo *fifo, uint64_t data)
>  {
> +    uint32_t next_idx = (fifo->head + fifo->num) % fifo->capacity;
> +
>      if (fifo->num == fifo->capacity) {
>          abort();
>      }
> -    fifo->data[(fifo->head + fifo->num) % fifo->capacity] = data;
> +    switch (fifo->width) {
> +    case(1):

To me 'case 1:' looks better, but it's only a personal taste :)

> +        ((uint8_t *)fifo->data)[next_idx] = data;
> +        break;
> +    case(2):
> +        ((uint16_t *)fifo->data)[next_idx] = data;
> +        break;
> +    case(4):
> +        ((uint32_t *)fifo->data)[next_idx] = data;
> +        break;
> +    case(8):
> +        ((uint64_t *)fifo->data)[next_idx] = data;
> +        break;
> +    default:
> +        abort();
> +    }
>      fifo->num++;
>  }
>  
> -void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num)
> +void fifo_push_all(Fifo *fifo, const void *data, uint32_t num)
>  {
>      uint32_t start, avail;
>  
> @@ -48,38 +67,51 @@ void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num)
>      start = (fifo->head + fifo->num) % fifo->capacity;
>  
>      if (start + num <= fifo->capacity) {
> -        memcpy(&fifo->data[start], data, num);
> +        memcpy(&fifo->data[start * fifo->width], data, num * fifo->width);
>      } else {
>          avail = fifo->capacity - start;
> -        memcpy(&fifo->data[start], data, avail);
> -        memcpy(&fifo->data[0], &data[avail], num - avail);
> +        memcpy(&fifo->data[start * fifo->width], data, avail * fifo->width);
> +        memcpy(&fifo->data[0], data + avail * fifo->width,
> +               (num - avail) * fifo->width);
>      }
>  
>      fifo->num += num;
>  }
>  
> -uint8_t fifo_pop(Fifo *fifo)
> +uint64_t fifo_pop(Fifo *fifo)
>  {
> -    uint8_t ret;
> +    uint32_t next_idx;
>  
>      if (fifo->num == 0) {
>          abort();
>      }
> -    ret = fifo->data[fifo->head++];
> +    next_idx = fifo->head++;
>      fifo->head %= fifo->capacity;
>      fifo->num--;
> -    return ret;
> +    switch (fifo->width) {
> +    case(1):
> +        return ((uint8_t *)fifo->data)[next_idx];
> +    case(2):
> +        return ((uint16_t *)fifo->data)[next_idx];
> +    case(4):
> +        return ((uint32_t *)fifo->data)[next_idx];
> +    case(8):
> +        return ((uint64_t *)fifo->data)[next_idx];
> +    default:
> +        abort();
> +        return 0; /* unreachable */
> +    }
>  }
>  
> -const uint8_t *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num)
> +const void *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num)
>  {
> -    uint8_t *ret;
> +    void *ret;
>  
>      if (max == 0 || max > fifo->num) {
>          abort();
>      }
>      *num = MIN(fifo->capacity - fifo->head, max);
> -    ret = &fifo->data[fifo->head];
> +    ret = &fifo->data[fifo->head * fifo->width];
>      fifo->head += *num;
>      fifo->head %= fifo->capacity;
>      fifo->num -= *num;
> @@ -112,13 +144,58 @@ uint32_t fifo_num_used(Fifo *fifo)
>      return fifo->num;
>  }
>  
> +/* Always store buffer data in little (arbitrarily chosen) endian format to
> + * allow for migration to/from BE <-> LE hosts.
> + */
> +
> +static inline void fifo_save_load_swap(Fifo *fifo)
> +{
> +#ifdef HOST_WORDS_BIGENDIAN
> +    int i;
> +    uint16_t *d16 = (uint16_t *)fifo->data;
> +    uint32_t *d32 = (uint32_t *)fifo->data;
> +    uint64_t *d64 = (uint64_t *)fifo->data;
> +
> +    for (i = 0; i < fifo->capacity; ++i) {
> +        switch (fifo->width) {
> +        case(1):
> +            return;
> +        case(2):
> +            d16[i] = bswap16(d16[i]);
> +            break;
> +        case(4):
> +            d32[i] = bswap32(d32[i]);
> +            break;
> +        case(8):
> +            d64[i] = bswap64(d64[i]);
> +            break;
> +        default:
> +            abort();
> +        }
> +    }
> +#endif
> +}
> +
> +static void fifo_pre_save(void *opaque)
> +{
> +    fifo_save_load_swap((Fifo *)opaque);
> +}
> +
> +static int fifo_post_load(void *opaque, int version_id)
> +{
> +    fifo_save_load_swap((Fifo *)opaque);
> +    return 0;
> +}
> +
>  const VMStateDescription vmstate_fifo = {
>      .name = "Fifo8",
>      .version_id = 1,
>      .minimum_version_id = 1,
>      .minimum_version_id_old = 1,
> +    .pre_save = fifo_pre_save,
> +    .post_load = fifo_post_load,
>      .fields      = (VMStateField[]) {
> -        VMSTATE_VBUFFER_UINT32(data, Fifo, 1, NULL, 0, capacity),
> +        VMSTATE_VBUFFER_UINT32(data, Fifo, 1, NULL, 0, buffer_size),
>          VMSTATE_UINT32(head, Fifo),
>          VMSTATE_UINT32(num, Fifo),
>          VMSTATE_END_OF_LIST()
> -- 
> 1.9.1.1.gbb9f595


I'm not familiar with vmsd save/load code, but the rest of the patch
seems good to me.

Beniamino
Peter Crosthwaite April 9, 2014, 1:51 a.m. UTC | #2
On Wed, Apr 9, 2014 at 7:42 AM, Beniamino Galvani <b.galvani@gmail.com> wrote:
> On Mon, Apr 07, 2014 at 07:05:18PM -0700, Peter Crosthwaite wrote:
>> Add support for 16, 32 and 64 bit width FIFOs. The push and pop
>> functions are patched to accept uint64_t always to support up to 64bit
>> integer elements. The element width is set at creation time.
>>
>> The backing storage for all element types is still uint8_t regardless of
>> element width so some save-load logic is needed to handle endianness
>> issue WRT VMSD.
>>
>> Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
>> ---
>>
>>  hw/char/serial.c        |   4 +-
>>  hw/net/allwinner_emac.c |   6 +--
>>  hw/ssi/xilinx_spi.c     |   4 +-
>>  hw/ssi/xilinx_spips.c   |   4 +-
>>  include/qemu/fifo.h     |  19 +++++----
>>  util/fifo.c             | 109 +++++++++++++++++++++++++++++++++++++++++-------
>>  6 files changed, 114 insertions(+), 32 deletions(-)
>>
>> diff --git a/hw/char/serial.c b/hw/char/serial.c
>> index a0d8024..3060769 100644
>> --- a/hw/char/serial.c
>> +++ b/hw/char/serial.c
>> @@ -659,8 +659,8 @@ void serial_realize_core(SerialState *s, Error **errp)
>>
>>      qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
>>                            serial_event, s);
>> -    fifo_create(&s->recv_fifo, UART_FIFO_LENGTH);
>> -    fifo_create(&s->xmit_fifo, UART_FIFO_LENGTH);
>> +    fifo_create(&s->recv_fifo, UART_FIFO_LENGTH, 8);
>> +    fifo_create(&s->xmit_fifo, UART_FIFO_LENGTH, 8);
>>  }
>>
>>  void serial_exit_core(SerialState *s)
>> diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
>> index e804411..2a0d2ac 100644
>> --- a/hw/net/allwinner_emac.c
>> +++ b/hw/net/allwinner_emac.c
>> @@ -455,9 +455,9 @@ static void aw_emac_realize(DeviceState *dev, Error **errp)
>>                            object_get_typename(OBJECT(dev)), dev->id, s);
>>      qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
>>
>> -    fifo_create(&s->rx_fifo, RX_FIFO_SIZE);
>> -    fifo_create(&s->tx_fifo[0], TX_FIFO_SIZE);
>> -    fifo_create(&s->tx_fifo[1], TX_FIFO_SIZE);
>> +    fifo_create(&s->rx_fifo, RX_FIFO_SIZE, 8);
>> +    fifo_create(&s->tx_fifo[0], TX_FIFO_SIZE, 8);
>> +    fifo_create(&s->tx_fifo[1], TX_FIFO_SIZE, 8);
>>  }
>>
>>  static Property aw_emac_properties[] = {
>> diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c
>> index 8fe3072..cac666b 100644
>> --- a/hw/ssi/xilinx_spi.c
>> +++ b/hw/ssi/xilinx_spi.c
>> @@ -341,8 +341,8 @@ static int xilinx_spi_init(SysBusDevice *sbd)
>>
>>      s->irqline = -1;
>>
>> -    fifo_create(&s->tx_fifo, FIFO_CAPACITY);
>> -    fifo_create(&s->rx_fifo, FIFO_CAPACITY);
>> +    fifo_create(&s->tx_fifo, FIFO_CAPACITY, 8);
>> +    fifo_create(&s->rx_fifo, FIFO_CAPACITY, 8);
>>
>>      return 0;
>>  }
>> diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
>> index d7d4c41..a7a639f 100644
>> --- a/hw/ssi/xilinx_spips.c
>> +++ b/hw/ssi/xilinx_spips.c
>> @@ -669,8 +669,8 @@ static void xilinx_spips_realize(DeviceState *dev, Error **errp)
>>
>>      s->irqline = -1;
>>
>> -    fifo_create(&s->rx_fifo, xsc->rx_fifo_size);
>> -    fifo_create(&s->tx_fifo, xsc->tx_fifo_size);
>> +    fifo_create(&s->rx_fifo, xsc->rx_fifo_size, 8);
>> +    fifo_create(&s->tx_fifo, xsc->tx_fifo_size, 8);
>>  }
>>
>>  static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
>> diff --git a/include/qemu/fifo.h b/include/qemu/fifo.h
>> index 44766b3..8738027 100644
>> --- a/include/qemu/fifo.h
>> +++ b/include/qemu/fifo.h
>> @@ -5,8 +5,12 @@
>>
>>  typedef struct {
>>      /* All fields are private */
>> +    int width; /* byte width each each element */
>
> of each element
>

Will fix.

>> +    uint32_t capacity; /* number of elements */
>> +
>>      uint8_t *data;
>> -    uint32_t capacity;
>> +    uint32_t buffer_size;
>> +
>>      uint32_t head;
>>      uint32_t num;
>>  } Fifo;
>> @@ -14,13 +18,14 @@ typedef struct {
>>  /**
>>   * fifo_create:
>>   * @fifo: struct Fifo to initialise with new FIFO
>> - * @capacity: capacity of the newly created FIFO
>> + * @capacity: capacity (number of elements) of the newly created FIFO
>> + * @width: integer width of each element. Must be 8, 16, 32 or 64.
>>   *
>>   * Create a FIFO of the specified size. Clients should call fifo_destroy()
>>   * when finished using the fifo. The FIFO is initially empty.
>>   */
>>
>> -void fifo_create(Fifo *fifo, uint32_t capacity);
>> +void fifo_create(Fifo *fifo, uint32_t capacity, int width);
>>
>>  /**
>>   * fifo_destroy:
>> @@ -41,7 +46,7 @@ void fifo_destroy(Fifo *fifo);
>>   * Clients are responsible for checking for fullness using fifo_is_full().
>>   */
>>
>> -void fifo_push(Fifo *fifo, uint8_t data);
>> +void fifo_push(Fifo *fifo, uint64_t data);
>>
>>  /**
>>   * fifo_push_all:
>> @@ -54,7 +59,7 @@ void fifo_push(Fifo *fifo, uint8_t data);
>>   * fifo_num_free().
>>   */
>>
>> -void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num);
>> +void fifo_push_all(Fifo *fifo, const void *data, uint32_t num);
>>
>>  /**
>>   * fifo_pop:
>> @@ -66,7 +71,7 @@ void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num);
>>   * Returns: The popped data value.
>>   */
>>
>> -uint8_t fifo_pop(Fifo *fifo);
>> +uint64_t fifo_pop(Fifo *fifo);
>>
>>  /**
>>   * fifo_pop_buf:
>> @@ -93,7 +98,7 @@ uint8_t fifo_pop(Fifo *fifo);
>>   * Returns: A pointer to popped data.
>>   */
>>
>> -const uint8_t *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num);
>> +const void *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num);
>>
>>  /**
>>   * fifo_reset:
>> diff --git a/util/fifo.c b/util/fifo.c
>> index ffadf55..51318ad 100644
>> --- a/util/fifo.c
>> +++ b/util/fifo.c
>> @@ -15,9 +15,11 @@
>>  #include "qemu-common.h"
>>  #include "qemu/fifo.h"
>>
>> -void fifo_create(Fifo *fifo, uint32_t capacity)
>> +void fifo_create(Fifo *fifo, uint32_t capacity, int width)
>>  {
>> -    fifo->data = g_new(uint8_t, capacity);
>> +    assert(width == 8 || width == 16 || width == 32 || width == 64);
>> +    fifo->width = width / 8;
>> +    fifo->data = g_new(uint8_t, capacity * fifo->width);
>>      fifo->capacity = capacity;
>>      fifo->head = 0;
>>      fifo->num = 0;
>> @@ -28,16 +30,33 @@ void fifo_destroy(Fifo *fifo)
>>      g_free(fifo->data);
>>  }
>>
>> -void fifo_push(Fifo *fifo, uint8_t data)
>> +void fifo_push(Fifo *fifo, uint64_t data)
>>  {
>> +    uint32_t next_idx = (fifo->head + fifo->num) % fifo->capacity;
>> +
>>      if (fifo->num == fifo->capacity) {
>>          abort();
>>      }
>> -    fifo->data[(fifo->head + fifo->num) % fifo->capacity] = data;
>> +    switch (fifo->width) {
>> +    case(1):
>
> To me 'case 1:' looks better, but it's only a personal taste :)
>

Ok, will fix.

Regards,
Peter
diff mbox

Patch

diff --git a/hw/char/serial.c b/hw/char/serial.c
index a0d8024..3060769 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -659,8 +659,8 @@  void serial_realize_core(SerialState *s, Error **errp)
 
     qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
                           serial_event, s);
-    fifo_create(&s->recv_fifo, UART_FIFO_LENGTH);
-    fifo_create(&s->xmit_fifo, UART_FIFO_LENGTH);
+    fifo_create(&s->recv_fifo, UART_FIFO_LENGTH, 8);
+    fifo_create(&s->xmit_fifo, UART_FIFO_LENGTH, 8);
 }
 
 void serial_exit_core(SerialState *s)
diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
index e804411..2a0d2ac 100644
--- a/hw/net/allwinner_emac.c
+++ b/hw/net/allwinner_emac.c
@@ -455,9 +455,9 @@  static void aw_emac_realize(DeviceState *dev, Error **errp)
                           object_get_typename(OBJECT(dev)), dev->id, s);
     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
 
-    fifo_create(&s->rx_fifo, RX_FIFO_SIZE);
-    fifo_create(&s->tx_fifo[0], TX_FIFO_SIZE);
-    fifo_create(&s->tx_fifo[1], TX_FIFO_SIZE);
+    fifo_create(&s->rx_fifo, RX_FIFO_SIZE, 8);
+    fifo_create(&s->tx_fifo[0], TX_FIFO_SIZE, 8);
+    fifo_create(&s->tx_fifo[1], TX_FIFO_SIZE, 8);
 }
 
 static Property aw_emac_properties[] = {
diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c
index 8fe3072..cac666b 100644
--- a/hw/ssi/xilinx_spi.c
+++ b/hw/ssi/xilinx_spi.c
@@ -341,8 +341,8 @@  static int xilinx_spi_init(SysBusDevice *sbd)
 
     s->irqline = -1;
 
-    fifo_create(&s->tx_fifo, FIFO_CAPACITY);
-    fifo_create(&s->rx_fifo, FIFO_CAPACITY);
+    fifo_create(&s->tx_fifo, FIFO_CAPACITY, 8);
+    fifo_create(&s->rx_fifo, FIFO_CAPACITY, 8);
 
     return 0;
 }
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index d7d4c41..a7a639f 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -669,8 +669,8 @@  static void xilinx_spips_realize(DeviceState *dev, Error **errp)
 
     s->irqline = -1;
 
-    fifo_create(&s->rx_fifo, xsc->rx_fifo_size);
-    fifo_create(&s->tx_fifo, xsc->tx_fifo_size);
+    fifo_create(&s->rx_fifo, xsc->rx_fifo_size, 8);
+    fifo_create(&s->tx_fifo, xsc->tx_fifo_size, 8);
 }
 
 static void xilinx_qspips_realize(DeviceState *dev, Error **errp)
diff --git a/include/qemu/fifo.h b/include/qemu/fifo.h
index 44766b3..8738027 100644
--- a/include/qemu/fifo.h
+++ b/include/qemu/fifo.h
@@ -5,8 +5,12 @@ 
 
 typedef struct {
     /* All fields are private */
+    int width; /* byte width each each element */
+    uint32_t capacity; /* number of elements */
+
     uint8_t *data;
-    uint32_t capacity;
+    uint32_t buffer_size;
+
     uint32_t head;
     uint32_t num;
 } Fifo;
@@ -14,13 +18,14 @@  typedef struct {
 /**
  * fifo_create:
  * @fifo: struct Fifo to initialise with new FIFO
- * @capacity: capacity of the newly created FIFO
+ * @capacity: capacity (number of elements) of the newly created FIFO
+ * @width: integer width of each element. Must be 8, 16, 32 or 64.
  *
  * Create a FIFO of the specified size. Clients should call fifo_destroy()
  * when finished using the fifo. The FIFO is initially empty.
  */
 
-void fifo_create(Fifo *fifo, uint32_t capacity);
+void fifo_create(Fifo *fifo, uint32_t capacity, int width);
 
 /**
  * fifo_destroy:
@@ -41,7 +46,7 @@  void fifo_destroy(Fifo *fifo);
  * Clients are responsible for checking for fullness using fifo_is_full().
  */
 
-void fifo_push(Fifo *fifo, uint8_t data);
+void fifo_push(Fifo *fifo, uint64_t data);
 
 /**
  * fifo_push_all:
@@ -54,7 +59,7 @@  void fifo_push(Fifo *fifo, uint8_t data);
  * fifo_num_free().
  */
 
-void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num);
+void fifo_push_all(Fifo *fifo, const void *data, uint32_t num);
 
 /**
  * fifo_pop:
@@ -66,7 +71,7 @@  void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num);
  * Returns: The popped data value.
  */
 
-uint8_t fifo_pop(Fifo *fifo);
+uint64_t fifo_pop(Fifo *fifo);
 
 /**
  * fifo_pop_buf:
@@ -93,7 +98,7 @@  uint8_t fifo_pop(Fifo *fifo);
  * Returns: A pointer to popped data.
  */
 
-const uint8_t *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num);
+const void *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num);
 
 /**
  * fifo_reset:
diff --git a/util/fifo.c b/util/fifo.c
index ffadf55..51318ad 100644
--- a/util/fifo.c
+++ b/util/fifo.c
@@ -15,9 +15,11 @@ 
 #include "qemu-common.h"
 #include "qemu/fifo.h"
 
-void fifo_create(Fifo *fifo, uint32_t capacity)
+void fifo_create(Fifo *fifo, uint32_t capacity, int width)
 {
-    fifo->data = g_new(uint8_t, capacity);
+    assert(width == 8 || width == 16 || width == 32 || width == 64);
+    fifo->width = width / 8;
+    fifo->data = g_new(uint8_t, capacity * fifo->width);
     fifo->capacity = capacity;
     fifo->head = 0;
     fifo->num = 0;
@@ -28,16 +30,33 @@  void fifo_destroy(Fifo *fifo)
     g_free(fifo->data);
 }
 
-void fifo_push(Fifo *fifo, uint8_t data)
+void fifo_push(Fifo *fifo, uint64_t data)
 {
+    uint32_t next_idx = (fifo->head + fifo->num) % fifo->capacity;
+
     if (fifo->num == fifo->capacity) {
         abort();
     }
-    fifo->data[(fifo->head + fifo->num) % fifo->capacity] = data;
+    switch (fifo->width) {
+    case(1):
+        ((uint8_t *)fifo->data)[next_idx] = data;
+        break;
+    case(2):
+        ((uint16_t *)fifo->data)[next_idx] = data;
+        break;
+    case(4):
+        ((uint32_t *)fifo->data)[next_idx] = data;
+        break;
+    case(8):
+        ((uint64_t *)fifo->data)[next_idx] = data;
+        break;
+    default:
+        abort();
+    }
     fifo->num++;
 }
 
-void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num)
+void fifo_push_all(Fifo *fifo, const void *data, uint32_t num)
 {
     uint32_t start, avail;
 
@@ -48,38 +67,51 @@  void fifo_push_all(Fifo *fifo, const uint8_t *data, uint32_t num)
     start = (fifo->head + fifo->num) % fifo->capacity;
 
     if (start + num <= fifo->capacity) {
-        memcpy(&fifo->data[start], data, num);
+        memcpy(&fifo->data[start * fifo->width], data, num * fifo->width);
     } else {
         avail = fifo->capacity - start;
-        memcpy(&fifo->data[start], data, avail);
-        memcpy(&fifo->data[0], &data[avail], num - avail);
+        memcpy(&fifo->data[start * fifo->width], data, avail * fifo->width);
+        memcpy(&fifo->data[0], data + avail * fifo->width,
+               (num - avail) * fifo->width);
     }
 
     fifo->num += num;
 }
 
-uint8_t fifo_pop(Fifo *fifo)
+uint64_t fifo_pop(Fifo *fifo)
 {
-    uint8_t ret;
+    uint32_t next_idx;
 
     if (fifo->num == 0) {
         abort();
     }
-    ret = fifo->data[fifo->head++];
+    next_idx = fifo->head++;
     fifo->head %= fifo->capacity;
     fifo->num--;
-    return ret;
+    switch (fifo->width) {
+    case(1):
+        return ((uint8_t *)fifo->data)[next_idx];
+    case(2):
+        return ((uint16_t *)fifo->data)[next_idx];
+    case(4):
+        return ((uint32_t *)fifo->data)[next_idx];
+    case(8):
+        return ((uint64_t *)fifo->data)[next_idx];
+    default:
+        abort();
+        return 0; /* unreachable */
+    }
 }
 
-const uint8_t *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num)
+const void *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num)
 {
-    uint8_t *ret;
+    void *ret;
 
     if (max == 0 || max > fifo->num) {
         abort();
     }
     *num = MIN(fifo->capacity - fifo->head, max);
-    ret = &fifo->data[fifo->head];
+    ret = &fifo->data[fifo->head * fifo->width];
     fifo->head += *num;
     fifo->head %= fifo->capacity;
     fifo->num -= *num;
@@ -112,13 +144,58 @@  uint32_t fifo_num_used(Fifo *fifo)
     return fifo->num;
 }
 
+/* Always store buffer data in little (arbitrarily chosen) endian format to
+ * allow for migration to/from BE <-> LE hosts.
+ */
+
+static inline void fifo_save_load_swap(Fifo *fifo)
+{
+#ifdef HOST_WORDS_BIGENDIAN
+    int i;
+    uint16_t *d16 = (uint16_t *)fifo->data;
+    uint32_t *d32 = (uint32_t *)fifo->data;
+    uint64_t *d64 = (uint64_t *)fifo->data;
+
+    for (i = 0; i < fifo->capacity; ++i) {
+        switch (fifo->width) {
+        case(1):
+            return;
+        case(2):
+            d16[i] = bswap16(d16[i]);
+            break;
+        case(4):
+            d32[i] = bswap32(d32[i]);
+            break;
+        case(8):
+            d64[i] = bswap64(d64[i]);
+            break;
+        default:
+            abort();
+        }
+    }
+#endif
+}
+
+static void fifo_pre_save(void *opaque)
+{
+    fifo_save_load_swap((Fifo *)opaque);
+}
+
+static int fifo_post_load(void *opaque, int version_id)
+{
+    fifo_save_load_swap((Fifo *)opaque);
+    return 0;
+}
+
 const VMStateDescription vmstate_fifo = {
     .name = "Fifo8",
     .version_id = 1,
     .minimum_version_id = 1,
     .minimum_version_id_old = 1,
+    .pre_save = fifo_pre_save,
+    .post_load = fifo_post_load,
     .fields      = (VMStateField[]) {
-        VMSTATE_VBUFFER_UINT32(data, Fifo, 1, NULL, 0, capacity),
+        VMSTATE_VBUFFER_UINT32(data, Fifo, 1, NULL, 0, buffer_size),
         VMSTATE_UINT32(head, Fifo),
         VMSTATE_UINT32(num, Fifo),
         VMSTATE_END_OF_LIST()