Refactor usb midi

This commit is contained in:
Russell
2011-12-16 12:26:17 +00:00
parent 9bcc2cf2b0
commit 6bf8d1c475
4 changed files with 227 additions and 166 deletions

View File

@@ -0,0 +1,84 @@
#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

@@ -0,0 +1,25 @@
#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

@@ -40,7 +40,7 @@ void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum);
INLINE void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum) { INLINE void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum) {
if (testct(c)) { if (testct(c)) {
is_ack = 1; is_ack = 1;
(void) inct(c); (void) inct(c); // read 1-bytes control token
(void) inuchar(c); (void) inuchar(c);
(void) inuchar(c); (void) inuchar(c);
(void) inuchar(c); (void) inuchar(c);
@@ -52,7 +52,6 @@ INLINE void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum) {
} }
#endif #endif
INLINE void midi_send_ack(chanend c) { INLINE void midi_send_ack(chanend c) {
outct(c, MIDI_ACK); outct(c, MIDI_ACK);
outuchar(c, 0); outuchar(c, 0);
@@ -60,5 +59,4 @@ INLINE void midi_send_ack(chanend c) {
outuchar(c, 0); outuchar(c, 0);
} }
#endif // __usb_midi_h__ #endif // __usb_midi_h__

View File

@@ -1,9 +1,11 @@
#include <xs1.h> #include <xs1.h>
#include <xclib.h> #include <xclib.h>
#include <print.h>
#include <stdint.h>
#include "usb_midi.h" #include "usb_midi.h"
#include "midiinparse.h" #include "midiinparse.h"
#include "midioutparse.h" #include "midioutparse.h"
#include <print.h> #include "queue.h"
//#define MIDI_LOOPBACK 1 //#define MIDI_LOOPBACK 1
#ifndef MIDI_SHIFT #ifndef MIDI_SHIFT
@@ -12,6 +14,7 @@
static unsigned makeSymbol(unsigned data) { static unsigned makeSymbol(unsigned data) {
// Start and stop bits to the data packet // Start and stop bits to the data packet
// like 10'b1dddddddd0
return (data << 1) | 0x200; return (data << 1) | 0x200;
} }
@@ -20,8 +23,9 @@ static unsigned makeSymbol(unsigned data) {
static unsigned bit_time = XS1_TIMER_MHZ * 1000000 / (unsigned) RATE; static unsigned bit_time = XS1_TIMER_MHZ * 1000000 / (unsigned) RATE;
static unsigned bit_time_2 = (XS1_TIMER_MHZ * 1000000 / (unsigned) RATE) / 2; static unsigned bit_time_2 = (XS1_TIMER_MHZ * 1000000 / (unsigned) RATE) / 2;
int mr_count = 0; // For debugging
int th_count = 0; int mr_count = 0; // MIDI received (from HOST)
int th_count = 0; // MIDI sent (To Host)
#ifdef MIDI_LOOPBACK #ifdef MIDI_LOOPBACK
static inline void handle_byte_from_uart(chanend c_midi, struct midi_in_parse_state &mips, int cable_number, static inline void handle_byte_from_uart(chanend c_midi, struct midi_in_parse_state &mips, int cable_number,
@@ -51,46 +55,46 @@ static inline void handle_byte_from_uart(chanend c_midi, struct midi_in_parse_
} }
#endif #endif
int uout_count = 0; int uout_count = 0; // UART bytes out
int uin_count = 0; int uin_count = 0; // UART bytes in
void usb_midi(in port ?p_midi_in, out port ?p_midi_out, void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
clock ?clk_midi, clock ?clk_midi,
chanend c_midi, chanend c_midi,
unsigned cable_number) unsigned cable_number
)
{ {
int is_ack; unsigned symbol = 0x0; // Symbol in progress of being sent out
unsigned int datum; unsigned isTX = 0; // Guard when outputting data
unsigned symbol = 0x0; unsigned txT; // Timer value used for outputting
unsigned outputting = 0;
unsigned time;
//unsigned inputPortState, newInputPortState; //unsigned inputPortState, newInputPortState;
int waiting_for_ack = 0; int waiting_for_ack = 0;
// Receiver // Receiver
unsigned rxByte; unsigned rxByte;
int rxI; int rxI;
int rxT; int rxT;
int isRX = 0; int isRX = 0; // Guard when receiving data
timer t; timer t;
timer t2; timer t2;
// these two vars make a one place buffer for data going out to host // One place buffer for data going out to host
int got_next_event = 0; queue midi_to_host_fifo;
int next_event; unsigned char midi_to_host_fifo_arr[4]; // Used for 32bit USB MIDI events
unsigned outputting_symbol, outputted_symbol; unsigned outputting_symbol, outputted_symbol;
struct midi_in_parse_state mips; struct midi_in_parse_state mips;
// the symbol fifo (to go out of uart) // the symbol fifo (to go out of uart)
unsigned symbol_fifo[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; queue symbol_fifo;
int rdptr = 0; unsigned char symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE * 4]; // Used for 32bit USB MIDI events
int wrptr = 0;
unsigned rxPT, txPT; unsigned rxPT, txPT;
int midi_from_host_overflow = 0; int midi_from_host_overflow = 0;
int space_left;
//configure_clock_rate(clk_midi, 100, 1); //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);
configure_out_port_no_ready(p_midi_out, clk_midi, 1); configure_out_port_no_ready(p_midi_out, clk_midi, 1);
configure_in_port(p_midi_in, clk_midi); configure_in_port(p_midi_in, clk_midi);
@@ -101,20 +105,17 @@ void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
reset_midi_state(mips); reset_midi_state(mips);
t :> time; t :> txT;
t2 :> rxT; t2 :> rxT;
#ifndef MIDI_LOOPBACK #ifndef MIDI_LOOPBACK
p_midi_out <: 1<<MIDI_SHIFT; // Start with high bit. p_midi_out <: 1<<MIDI_SHIFT; // Start with high bit.
// printstr("mout0");
#endif #endif
while (1) { while (1) {
select int is_ack;
{ unsigned int datum;
select {
// Input to read the start bit // Input to read the start bit
#ifndef MIDI_LOOPBACK #ifndef MIDI_LOOPBACK
#ifdef MIDI_IN_4BIT_PORT #ifdef MIDI_IN_4BIT_PORT
@@ -132,33 +133,30 @@ void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
break; break;
// Input to read the remaining bits // Input to read the remaining bits
case isRX => t2 when timerafter(rxT) :> int _ : case isRX => t2 when timerafter(rxT) :> int _ :
if (rxI++ < 8)
{ {
unsigned bit; unsigned bit;
p_midi_in :> bit; p_midi_in :> bit;
if (rxI++ < 8) {
// shift in bits into the high end of a word
rxByte = (bit << 31) | (rxByte >> 1); rxByte = (bit << 31) | (rxByte >> 1);
rxT += bit_time; rxT += bit_time;
rxPT += bit_time; rxPT += bit_time;
asm("setpt res[%0],%1"::"r"(p_midi_in),"r"(rxPT)); asm("setpt res[%0],%1"::"r"(p_midi_in),"r"(rxPT));
} } else {
else
{
unsigned bit;
// rcv and check stop bit // rcv and check stop bit
p_midi_in :> bit; if ((bit & 0x1) == 1) {
if ((bit & 0x1) == 1)
{
unsigned valid = 0; unsigned valid = 0;
unsigned event = 0; unsigned event = 0;
uin_count++; uin_count++;
rxByte >>= 24; rxByte >>= 24;
// if (rxByte != outputted_symbol) { // if (rxByte != outputted_symbol) {
// // Loopback check
// printhexln(rxByte); // printhexln(rxByte);
// printhexln(outputted_symbol); // printhexln(outputted_symbol);
// } // }
{valid, event} = midi_in_parse(mips, cable_number, rxByte); {valid, event} = midi_in_parse(mips, cable_number, rxByte);
if (valid && !got_next_event) { if (valid && isempty(midi_to_host_fifo)) {
event = byterev(event); event = byterev(event);
// data to send to host - add to fifo // data to send to host - add to fifo
if (!waiting_for_ack) { if (!waiting_for_ack) {
@@ -167,63 +165,59 @@ void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
outuint(c_midi, event); outuint(c_midi, event);
waiting_for_ack = 1; waiting_for_ack = 1;
th_count++; th_count++;
} else {
enqueue(midi_to_host_fifo, event);
} }
else { } else if (valid) {
next_event = event;
got_next_event = 1;
}
}
else if (valid) {
// printstr("g"); // printstr("g");
} }
} }
isRX = 0; isRX = 0;
} }
break; break;
}
// Output // Output
// If outputting then feed the bits out one at a time // If isTX then feed the bits out one at a time
// until symbol is zero expect pattern like 10'b1dddddddd0 // until symbol is zero expect pattern like 10'b1dddddddd0
// This code will leave the output high afterwards due to the stop bit added with makeSymbol // This code will leave the output high afterwards due to the stop bit added with makeSymbol
case outputting => t when timerafter(time) :> int _: case isTX => t when timerafter(txT) :> int _:
if (symbol == 0) if (symbol == 0) {
{ // Got something to output but not mid-symbol.
uout_count++; // Start sending symbol.
outputted_symbol = outputting_symbol; // This case is reached when a symbol has been received from the host but not started AND
// have we got another symbol to send to uart? // When it has just finished sending a symbol
if (rdptr != wrptr) {
outputting_symbol = symbol_fifo[rdptr];
symbol = makeSymbol(symbol_fifo[rdptr]);
rdptr++;
if (rdptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1)
rdptr = 0;
space_left = rdptr - wrptr; // Take from FIFO
if (space_left < 0) outputting_symbol = dequeue(symbol_fifo);
space_left += USB_MIDI_DEVICE_OUT_FIFO_SIZE; symbol = makeSymbol(outputting_symbol);
if (space_left > 3 && midi_from_host_overflow) { if (space(symbol_fifo) > 3 && midi_from_host_overflow) {
midi_from_host_overflow = 0; midi_from_host_overflow = 0;
midi_send_ack(c_midi); midi_send_ack(c_midi);
} }
p_midi_out <: (1<<MIDI_SHIFT) @ txPT; p_midi_out <: (1<<MIDI_SHIFT) @ txPT;
// printstr("mout1\n"); // printstr("mout1\n");
t :> time; t :> txT;
time += bit_time; txT += bit_time;
txPT += bit_time; txPT += bit_time;
} isTX = 1;
else } else {
outputting = 0; // Mid-symbol
} txT += bit_time; // Should this be after the output otherwise be double the length of the high before the start bit
else
{
time += bit_time;
txPT += bit_time; txPT += bit_time;
p_midi_out @ txPT <: ((symbol & 1)<<MIDI_SHIFT); p_midi_out @ txPT <: ((symbol & 1)<<MIDI_SHIFT);
// printstr("mout2\n"); // printstr("mout2\n");
symbol >>= 1; symbol >>= 1;
if (symbol == 0) {
// Finished sending byte
uout_count++;
outputted_symbol = outputting_symbol;
if (isempty(symbol_fifo)) { // FIFO empty
isTX = 0;
}
}
} }
break; break;
#endif #endif
@@ -232,26 +226,21 @@ void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
if (is_ack) { if (is_ack) {
// have we got more data to send // have we got more data to send
//printstr("ack\n"); //printstr("ack\n");
if (got_next_event) { if (!isempty(midi_to_host_fifo)) {
//printstr("uart->decouple\n"); //printstr("uart->decouple\n");
outuint(c_midi, next_event); outuint(c_midi, dequeue(midi_to_host_fifo));
th_count++; th_count++;
got_next_event = 0; } else {
}
else {
waiting_for_ack = 0; waiting_for_ack = 0;
} }
} } else {
else {
int event;
unsigned midi[3]; unsigned midi[3];
unsigned size; unsigned size;
int valid;
// received data from host // received data from host
event = byterev(datum); int event = byterev(datum);
mr_count++; mr_count++;
#ifdef MIDI_LOOPBACK #ifdef MIDI_LOOPBACK
if (!got_next_event) { if (isempty(midi_to_host_fifo)) {
// data to send to host // data to send to host
if (!waiting_for_ack) { if (!waiting_for_ack) {
// send data // send data
@@ -259,62 +248,27 @@ void usb_midi(in port ?p_midi_in, out port ?p_midi_out,
outuint(c_midi, event); outuint(c_midi, event);
th_count++; th_count++;
waiting_for_ack = 1; waiting_for_ack = 1;
} } else {
else {
event = byterev(event); event = byterev(event);
next_event = event; enqueue(midi_to_host_fifo, event);
got_next_event = 1;
} }
} }
#else #else
{midi[0], midi[1], midi[2], size} = midi_out_parse(event); {midi[0], midi[1], midi[2], size} = midi_out_parse(event);
for (int i = 0; i != size; i++) { for (int i = 0; i != size; i++) {
// add symbol to fifo // add symbol to fifo
unsigned sym = midi[i]; enqueue(symbol_fifo, midi[i]);
int new_wrptr = wrptr + 1;
if (new_wrptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1) {
new_wrptr = 0;
} }
symbol_fifo[wrptr] = sym; if (space(symbol_fifo) > 3) {
wrptr = new_wrptr;
}
space_left = rdptr - wrptr;
if (space_left < 0)
space_left += USB_MIDI_DEVICE_OUT_FIFO_SIZE;
if (space_left > 3) {
midi_send_ack(c_midi); midi_send_ack(c_midi);
} } else {
else {
midi_from_host_overflow = 1; midi_from_host_overflow = 1;
} }
// Drop through to the isTX guarded case
if (wrptr != rdptr && !outputting) { if (!isTX) {
outputting_symbol = symbol_fifo[rdptr]; t :> txT; // Should be enough to trigger the other case
symbol = makeSymbol(symbol_fifo[rdptr]); isTX = 1;
rdptr++;
if (rdptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1)
rdptr = 0;
if (space_left > 2 && midi_from_host_overflow) {
midi_from_host_overflow = 0;
midi_send_ack(c_midi);
}
#ifdef MIDI_LOOPBACK
handle_byte_from_uart(c_midi, mips, cable_number, got_next_event, next_event, waiting_for_ack, symbol);
#else
p_midi_out <: (1<<MIDI_SHIFT) @ txPT;
t :> time;
time += bit_time;
txPT += bit_time;
outputting = 1;
#endif
} }
#endif #endif
} }