diff mbox

[RFC,v1,2/2] util/fifo: Generalise for common integer widths

Message ID 0ac60aeb3e0907fdf6106f61f6358385ef845657.1388713158.git.peter.crosthwaite@xilinx.com
State New
Headers show

Commit Message

Peter Crosthwaite Jan. 3, 2014, 1:43 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 endianess
issue WRT VMSD.

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

 hw/char/serial.c      |  4 +--
 hw/ssi/xilinx_spi.c   |  4 +--
 hw/ssi/xilinx_spips.c |  4 +--
 include/qemu/fifo.h   | 15 ++++++---
 util/fifo.c           | 91 ++++++++++++++++++++++++++++++++++++++++++++++-----
 5 files changed, 98 insertions(+), 20 deletions(-)
diff mbox

Patch

diff --git a/hw/char/serial.c b/hw/char/serial.c
index 9b43e36..cc71249 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -657,8 +657,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/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 c3d9c05..aeff06c 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 e488387..a1737c3 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_pop:
@@ -53,7 +58,7 @@  void fifo_push(Fifo *fifo, uint8_t data);
  * Returns: The popped data value.
  */
 
-uint8_t fifo_pop(Fifo *fifo);
+uint64_t fifo_pop(Fifo *fifo);
 
 /**
  * fifo_reset:
diff --git a/util/fifo.c b/util/fifo.c
index 1adaa11..33356ee 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,26 +30,55 @@  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++;
 }
 
-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 */
+    }
 }
 
 void fifo_reset(Fifo *fifo)
@@ -65,13 +96,55 @@  bool fifo_is_full(Fifo *fifo)
     return (fifo->num == fifo->capacity);
 }
 
+/* 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()