Add a new module implementing a queue data structure.

Since the functions are simple make them inline functions. This provides a
small code size saving.
This commit is contained in:
Richard Osborne
2013-11-29 18:36:35 +00:00
parent 93590833cf
commit a695e58524
10 changed files with 164 additions and 126 deletions

View File

@@ -0,0 +1,14 @@
# You can set flags specifically for your module by using the MODULE_XCC_FLAGS
# variable. So the following
#
# MODULE_XCC_FLAGS = $(XCC_FLAGS) -O3
#
# specifies that everything in the modules should have the application
# build flags with -O3 appended (so the files will build at
# optimization level -O3).
#
# You can also set MODULE_XCC_C_FLAGS, MODULE_XCC_XC_FLAGS etc..
MODULE_XCC_XC_FLAGS = $(XCC_XC_FLAGS)
DEPENDENT_MODULES = module_xassert

View File

@@ -0,0 +1 @@
One line module description.

54
module_queue/src/queue.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef QUEUE_H_
#define QUEUE_H_
#include <xassert.h>
typedef struct queue_t {
/// Read index.
unsigned rdptr;
/// Write index.
unsigned wrptr;
unsigned size;
unsigned mask;
} queue_t;
inline int is_power_of_2(unsigned x) {
return x != 0 && (x & (x - 1)) == 0;
}
inline void queue_init(queue_t &q, unsigned size) {
assert(is_power_of_2(size));
q.rdptr = 0;
q.wrptr = 0;
q.size = size;
q.mask = size - 1; // Assumes power of two.
}
inline int queue_is_empty(const queue_t &q) {
return q.wrptr == q.rdptr;
}
inline int queue_is_full(const queue_t &q) {
return q.wrptr - q.rdptr == q.size;
}
inline void queue_push_word(queue_t &q, unsigned array[], unsigned data)
{
assert(!queue_is_full(q));
array[q.wrptr++ & q.mask] = data;
}
inline unsigned queue_pop_word(queue_t &q, unsigned array[]) {
assert(!queue_is_empty(q));
return array[q.rdptr++ & q.mask];
}
inline unsigned queue_items(const queue_t &q) {
return q.wrptr - q.rdptr;
}
inline unsigned queue_space(const queue_t &q) {
return q.size - queue_items(q);
}
#endif /* QUEUE_H_ */

11
module_queue/src/queue.xc Normal file
View File

@@ -0,0 +1,11 @@
#include "queue.h"
// Force external definitions of inline functions.
extern inline int is_power_of_2(unsigned x);
extern inline void queue_init(queue_t &q, unsigned size);
extern inline int queue_is_empty(const queue_t &q);
extern inline int queue_is_full(const queue_t &q);
extern inline void queue_push_word(queue_t &q, unsigned array[], unsigned data);
extern inline unsigned queue_pop_word(queue_t &q, unsigned array[]);
extern inline unsigned queue_space(const queue_t &q);
extern inline unsigned queue_items(const queue_t &q);

View File

@@ -9,3 +9,4 @@
#
# You can also set MODULE_XCC_C_FLAGS, MODULE_XCC_XC_FLAGS etc..
DEPENDENT_MODULES = module_queue

View File

@@ -1,84 +0,0 @@
#include <stdio.h>
#include "queue.h"
// Queue implementation
// Offers no protection against adding when full or dequeueing when empty.
// Uses read and write counts for pointers to distinguish full and empty cases.
// Works from c and xc
// Must allocate the memory outside of this and pass it in to init_queue so can statically allocate
// Must work for different element sizes
// This presumes that the xc compiler will not re-use the mem passed to init_queue
void init_queue(queue *q, unsigned char arr[], int size, int element_size) {
q->rdptr = 0;
q->wrptr = 0;
q->data = (uintptr_t)arr;
q->size = size; // in items, presume that size is power of two
q->element_size = element_size; // The size of each element in bytes
q->mask = size - 1;
}
extern inline void enqueue(queue *q, unsigned value) {
switch (q->element_size) {
case 4:
((unsigned *)q->data)[q->wrptr & q->mask] = value;
break;
case 1:
((unsigned char *)q->data)[q->wrptr & q->mask] = (unsigned char)value;
break;
default:
break;
}
q->wrptr++;
}
extern inline unsigned dequeue(queue *q) {
unsigned retval;
switch (q->element_size) {
case 4:
retval = ((unsigned *)q->data)[q->rdptr & q->mask];
break;
case 1:
retval = ((unsigned char *)q->data)[q->rdptr & q->mask];
break;
default:
break;
}
q->rdptr++;
return retval;
}
extern inline int isempty(queue *q) {
return (q->rdptr == q->wrptr);
}
extern inline int isfull(queue *q) {
return ((q->wrptr - q->rdptr) == q->size);
}
extern inline int items(queue *q) {
int items = q->wrptr - q->rdptr;
return items;
}
// How to calculate size? Could make it a function call or leave it as a variable within the struct
extern inline int space(queue *q) {
return q->size - items(q);
}
void dump(queue *q) {
for (int i = q->rdptr; i != q->wrptr; i++) {
switch (q->element_size) {
case 4:
printf("a[%d] = %d\n", i & q->mask, ((unsigned *)q->data)[i & q->mask]);
break;
case 1:
printf("a[%d] = %d\n", i & q->mask, ((unsigned char *)q->data)[i & q->mask]);
break;
default:
break;
}
}
}

View File

@@ -1,25 +0,0 @@
#ifndef QUEUE_H
#define QUEUE_H
#include <stdint.h>
#include <xccompat.h>
typedef struct queue {
uintptr_t data;
int rdptr; // Using absolute indices which count reads and writes so this needs to be considered when accessing.
int wrptr;
int size;
int element_size;
int mask;
} queue;
void init_queue(REFERENCE_PARAM(queue, q), unsigned char arr[], int size, int element_size);
void enqueue(REFERENCE_PARAM(queue, q), unsigned value);
unsigned dequeue(REFERENCE_PARAM(queue, q));
int isempty(REFERENCE_PARAM(queue, q));
int isfull(REFERENCE_PARAM(queue, q));
int items(REFERENCE_PARAM(queue, q));
int space(REFERENCE_PARAM(queue, q));
void dump(REFERENCE_PARAM(queue, q));
#endif // QUEUE_H

View File

@@ -49,6 +49,8 @@ extern unsigned polltime;
timer iAPTimer;
#endif
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
void usb_midi(port ?p_midi_in, port ?p_midi_out,
clock ?clk_midi,
chanend ?c_midi,
@@ -71,23 +73,23 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
timer t2;
// One place buffer for data going out to host
queue midi_to_host_fifo;
unsigned char midi_to_host_fifo_arr[4]; // Used for 32bit USB MIDI events
queue_t midi_to_host_fifo;
unsigned midi_to_host_fifo_arr[1]; // Used for 32bit USB MIDI events
unsigned outputting_symbol, outputted_symbol;
struct midi_in_parse_state mips;
// the symbol fifo (to go out of uart)
queue symbol_fifo;
unsigned char symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE * 4]; // Used for 32bit USB MIDI events
queue_t symbol_fifo;
unsigned symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; // Used for 32bit USB MIDI events
unsigned rxPT, txPT;
int midi_from_host_overflow = 0;
//configure_clock_rate(clk_midi, 100, 1);
init_queue(symbol_fifo, symbol_fifo_arr, USB_MIDI_DEVICE_OUT_FIFO_SIZE, 4);
init_queue(midi_to_host_fifo, midi_to_host_fifo_arr, 1, 4);
queue_init(symbol_fifo, ARRAY_SIZE(symbol_fifo_arr));
queue_init(midi_to_host_fifo, ARRAY_SIZE(midi_to_host_fifo_arr));
configure_out_port(p_midi_out, clk_midi, 1<<MIDI_SHIFT_TX);
configure_in_port(p_midi_in, clk_midi);
@@ -174,7 +176,7 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
// }
{valid, event} = midi_in_parse(mips, cable_number, rxByte);
if (valid && isempty(midi_to_host_fifo))
if (valid && queue_is_empty(midi_to_host_fifo))
{
event = byterev(event);
@@ -189,7 +191,7 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
}
else
{
enqueue(midi_to_host_fifo, event);
queue_push_word(midi_to_host_fifo, midi_to_host_fifo_arr, event);
}
}
else if (valid)
@@ -215,10 +217,10 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
// When it has just finished sending a symbol
// Take from FIFO
outputting_symbol = dequeue(symbol_fifo);
outputting_symbol = queue_pop_word(symbol_fifo, symbol_fifo_arr);
symbol = makeSymbol(outputting_symbol);
if (space(symbol_fifo) > 3 && midi_from_host_overflow)
if (queue_space(symbol_fifo) > 3 && midi_from_host_overflow)
{
midi_from_host_overflow = 0;
midi_send_ack(c_midi);
@@ -244,7 +246,7 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
// Finished sending byte
uout_count++;
outputted_symbol = outputting_symbol;
if (isempty(symbol_fifo))
if (queue_is_empty(symbol_fifo))
{ // FIFO empty
isTX = 0;
}
@@ -259,10 +261,10 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
{
// have we got more data to send
//printstr("ack\n");
if (!isempty(midi_to_host_fifo))
if (!queue_is_empty(midi_to_host_fifo))
{
//printstr("uart->decouple\n");
outuint(c_midi, dequeue(midi_to_host_fifo));
outuint(c_midi, queue_pop_word(midi_to_host_fifo, midi_to_host_fifo_arr));
th_count++;
}
else
@@ -278,7 +280,7 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
int event = byterev(datum);
mr_count++;
#ifdef MIDI_LOOPBACK
if (isempty(midi_to_host_fifo))
if (queue_is_empty(midi_to_host_fifo))
{
// data to send to host
if (!waiting_for_ack)
@@ -292,7 +294,7 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
else
{
event = byterev(event);
enqueue(midi_to_host_fifo, event);
queue_push_word(midi_to_host_fifo, midi_to_host_fifo_arr, event);
}
midi_send_ack(c_midi);
}
@@ -305,10 +307,10 @@ void usb_midi(port ?p_midi_in, port ?p_midi_out,
for (int i = 0; i != size; i++)
{
// add symbol to fifo
enqueue(symbol_fifo, midi[i]);
queue_push_word(symbol_fifo, symbol_fifo_arr, midi[i]);
}
if (space(symbol_fifo) > 3)
if (queue_space(symbol_fifo) > 3)
{
midi_send_ack(c_midi);
}

View File

@@ -0,0 +1,32 @@
# The TARGET variable determines what target system the application is
# compiled for. It either refers to an XN file in the source directories
# or a valid argument for the --target option when compiling
TARGET = XP-SKC-SU1
# The APP_NAME variable determines the name of the final .xe file. It should
# not include the .xe postfix. If left blank the name will default to
# the project name
APP_NAME = app_queue_test
# The USED_MODULES variable lists other module used by the application.
USED_MODULES = module_queue
# The flags passed to xcc when building the application
# You can also set the following to override flags for a particular language:
# XCC_XC_FLAGS, XCC_C_FLAGS, XCC_ASM_FLAGS, XCC_CPP_FLAGS
# If the variable XCC_MAP_FLAGS is set it overrides the flags passed to
# xcc for the final link (mapping) stage.
XCC_FLAGS = -O0 -g
# The VERBOSE variable, if set to 1, enables verbose output from the make system.
VERBOSE = 0
# This change to the module path is so that this application can be in the
# tests sub-directory in it's git repo
ifeq ($(notdir $(abspath ..)),tests)
PATHSEP = $(if $(findstring Windows, $(OS))$(findstring WINDOWS,$(OS)),;,:)
XMOS_MODULE_PATH := $(XMOS_MODULE_PATH)$(PATHSEP)../../..
endif
XMOS_MAKE_PATH ?= ../..
-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common

View File

@@ -0,0 +1,32 @@
#include "queue.h"
#include <stdlib.h>
#define VERIFY(x) do { if (!(x)) _Exit(1); } while(0)
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
int main()
{
queue_t q;
unsigned array[4];
const unsigned size = ARRAY_SIZE(array);
queue_init(q, size);
VERIFY(queue_is_empty(q));
VERIFY(!queue_is_full(q));
VERIFY(queue_items(q) == 0);
VERIFY(queue_space(q) == size);
queue_push_word(q, array, 1);
VERIFY(!queue_is_empty(q));
VERIFY(queue_items(q) == 1);
VERIFY(queue_space(q) == size - 1);
for (unsigned i = 1; i < size; i++) {
queue_push_word(q, array, i + 1);
}
VERIFY(queue_is_full(q));
VERIFY(queue_items(q) == size);
VERIFY(queue_space(q) == 0);
for (unsigned i = 0; i < size; i++) {
VERIFY(queue_pop_word(q, array) == i + 1);
}
return 0;
}