diff --git a/module_queue/module_build_info b/module_queue/module_build_info new file mode 100644 index 00000000..84cb704f --- /dev/null +++ b/module_queue/module_build_info @@ -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 diff --git a/module_queue/module_description b/module_queue/module_description new file mode 100644 index 00000000..00a6de45 --- /dev/null +++ b/module_queue/module_description @@ -0,0 +1 @@ +One line module description. diff --git a/module_queue/src/queue.h b/module_queue/src/queue.h new file mode 100644 index 00000000..23e4cea5 --- /dev/null +++ b/module_queue/src/queue.h @@ -0,0 +1,54 @@ +#ifndef QUEUE_H_ +#define QUEUE_H_ + +#include + +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_ */ diff --git a/module_queue/src/queue.xc b/module_queue/src/queue.xc new file mode 100644 index 00000000..58c78248 --- /dev/null +++ b/module_queue/src/queue.xc @@ -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); diff --git a/module_usb_midi/module_build_info b/module_usb_midi/module_build_info index cb931043..bb4cb582 100644 --- a/module_usb_midi/module_build_info +++ b/module_usb_midi/module_build_info @@ -9,3 +9,4 @@ # # You can also set MODULE_XCC_C_FLAGS, MODULE_XCC_XC_FLAGS etc.. +DEPENDENT_MODULES = module_queue diff --git a/module_usb_midi/src/queue.c b/module_usb_midi/src/queue.c deleted file mode 100644 index 1852b466..00000000 --- a/module_usb_midi/src/queue.c +++ /dev/null @@ -1,84 +0,0 @@ -#include -#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; - } - } -} - - diff --git a/module_usb_midi/src/queue.h b/module_usb_midi/src/queue.h deleted file mode 100644 index 1e04c8d0..00000000 --- a/module_usb_midi/src/queue.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef QUEUE_H -#define QUEUE_H - -#include -#include - -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 diff --git a/module_usb_midi/src/usb_midi.xc b/module_usb_midi/src/usb_midi.xc index f037ecd0..cdd6239d 100644 --- a/module_usb_midi/src/usb_midi.xc +++ b/module_usb_midi/src/usb_midi.xc @@ -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< 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); } diff --git a/tests/app_queue_test/Makefile b/tests/app_queue_test/Makefile new file mode 100644 index 00000000..a9c74106 --- /dev/null +++ b/tests/app_queue_test/Makefile @@ -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 diff --git a/tests/app_queue_test/src/app_queue_test.xc b/tests/app_queue_test/src/app_queue_test.xc new file mode 100644 index 00000000..b4acd7f4 --- /dev/null +++ b/tests/app_queue_test/src/app_queue_test.xc @@ -0,0 +1,32 @@ +#include "queue.h" +#include + +#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; +}