Skip to content

Commit dcf6a7d

Browse files
committed
Adding ring buffers to remove USB bottleneck
1 parent 869d478 commit dcf6a7d

File tree

7 files changed

+115
-49
lines changed

7 files changed

+115
-49
lines changed

boot.mk

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ $(TARGET)_SRC += \
1111
boot/usb.c \
1212
$(USB_PATH)/class/dfu/dfu.c
1313

14-
$(TARGET)_LDSCRIPT = deps/sam0/linker_scripts/samd21/gcc/samd21g15a_flash.ld
15-
$(TARGET)_DEFINE += -D __SAMD21G15A__
14+
$(TARGET)_LDSCRIPT = deps/sam0/linker_scripts/samd21/gcc/samd21g18a_flash.ld
15+
$(TARGET)_DEFINE += -D __SAMD21G18A__

common/samd21g15a_firmware_partition.ld common/samd21g18a_firmware_partition.ld

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ SEARCH_DIR(.)
4949
/* Memory Spaces Definitions */
5050
MEMORY
5151
{
52-
rom (rx) : ORIGIN = 0x00001000, LENGTH = 0x00008000
52+
rom (rx) : ORIGIN = 0x00001000, LENGTH = 0x0003efff
5353
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00001000
5454
}
5555

firmware.mk

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ $(TARGET)_SRC += \
1212
firmware/usbpipe.c \
1313
firmware/usbserial.c \
1414

15-
$(TARGET)_LDSCRIPT = common/samd21g15a_firmware_partition.ld
16-
$(TARGET)_DEFINE += -D __SAMD21G15A__
15+
$(TARGET)_LDSCRIPT = common/samd21g18a_firmware_partition.ld
16+
$(TARGET)_DEFINE += -D __SAMD21G18A__

firmware/usbpipe.c

+66-22
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,103 @@
11
#include "firmware.h"
2-
3-
#define FLASH_BUFFER_SIZE 64
2+
#define OUT_RING_SIZE 10
3+
#define PACKET_SIZE 64
4+
u8 out_ring_buf[OUT_RING_SIZE][PACKET_SIZE];
5+
volatile u8 out_ring_count = 0; // Number of packets in the ring buffer
6+
volatile u8 out_ring_write_pos = 0; // Packet index in which we're currently receiving a packet, or will once it's free
7+
volatile u8 out_ring_read_pos = 0; // Packet index from which we're currently sending a packet, or will once it's filled.
8+
volatile u8 out_ring_short_packet = 0; // If nonzero, the ring ends with a short packet of this size
9+
volatile bool out_usb_pending = false;
10+
volatile bool out_bridge_pending = false;
411

512
typedef enum {
613
PIPE_DISABLE,
714
PIPE_WAIT_FOR_USB,
815
PIPE_WAIT_FOR_BRIDGE,
916
} PipeState;
1017

11-
PipeState pipe_state_pc_to_soc;
1218
PipeState pipe_state_soc_to_pc;
13-
USB_ALIGN u8 pipe_buffer_pc_to_soc[BRIDGE_BUF_SIZE];
1419
USB_ALIGN u8 pipe_buffer_soc_to_pc[BRIDGE_BUF_SIZE];
1520

1621
void usbpipe_init() {
1722
usb_enable_ep(USB_EP_PIPE_OUT, USB_EP_TYPE_BULK, 64);
1823
usb_enable_ep(USB_EP_PIPE_IN, USB_EP_TYPE_BULK, 64);
1924

20-
usb_ep_start_out(USB_EP_PIPE_OUT, pipe_buffer_pc_to_soc, FLASH_BUFFER_SIZE);
21-
pipe_state_pc_to_soc = PIPE_WAIT_FOR_USB;
25+
usb_ep_start_out(USB_EP_PIPE_OUT, out_ring_buf[out_ring_write_pos], PACKET_SIZE);
26+
out_usb_pending = true;
27+
out_bridge_pending = false;
2228

2329
bridge_start_out(BRIDGE_USB, pipe_buffer_soc_to_pc);
24-
pipe_state_soc_to_pc = PIPE_WAIT_FOR_BRIDGE;
30+
pipe_state_soc_to_pc = PIPE_WAIT_FOR_BRIDGE;
2531

2632
bridge_enable_chan(BRIDGE_USB); // Tells SPI Daemon to start connection to USB Daemon
2733
}
2834

2935
void usbpipe_disable() {
3036
usb_disable_ep(USB_EP_PIPE_IN);
3137
usb_disable_ep(USB_EP_PIPE_OUT);
32-
pipe_state_pc_to_soc = PIPE_DISABLE;
38+
out_ring_count = 0;
39+
out_ring_write_pos = 0;
40+
out_ring_read_pos = 0;
41+
out_ring_short_packet = 0;
3342
pipe_state_soc_to_pc = PIPE_DISABLE;
3443
bridge_disable_chan(BRIDGE_USB); // Tells SPI Daemon to close connection to USB Daemon
3544
}
3645

37-
// Received from USB, send to bridge
38-
void pipe_usb_out_completion() {
39-
if (pipe_state_pc_to_soc == PIPE_WAIT_FOR_USB) {
40-
u32 len = usb_ep_out_length(USB_EP_PIPE_OUT);
41-
bridge_start_in(BRIDGE_USB, pipe_buffer_pc_to_soc, len);
42-
pipe_state_pc_to_soc = PIPE_WAIT_FOR_BRIDGE;
43-
} else {
44-
invalid();
46+
void out_ring_step() {
47+
// If we are not currently receiving
48+
// And there is an empty buffer to receive data into
49+
// And there isn't an unprocessed short packet
50+
if (!out_usb_pending && out_ring_count < OUT_RING_SIZE && out_ring_short_packet == 0) {
51+
// Start reading data in over USB to the buffer at the correct position (up to 64 bytes)
52+
usb_ep_start_out(USB_EP_PIPE_OUT, out_ring_buf[out_ring_write_pos], PACKET_SIZE);
53+
// We are waiting for the transfer to complete
54+
out_usb_pending = true;
4555
}
4656

57+
// If we are not waiting on a bridge transaction to complete and we have packets to send
58+
if (!out_bridge_pending && out_ring_count > 0) {
59+
// The size of the packet is 64 bytes (unless the below case is true)
60+
u8 len = PACKET_SIZE;
61+
// If we only have one outgoing packet and it is a short packet
62+
if (out_ring_count == 1 && out_ring_short_packet != 0) {
63+
// The length is actually a subset of a full packet
64+
len = out_ring_short_packet;
65+
// Reset the short packet var
66+
out_ring_short_packet = 0;
67+
}
68+
// Start sending data to the spi daemon
69+
bridge_start_in(BRIDGE_USB, out_ring_buf[out_ring_read_pos], len);
70+
// We are currently waiting on the SPI
71+
out_bridge_pending = true;
72+
}
73+
}
74+
75+
// Received from USB, send to bridge
76+
void pipe_usb_out_completion() {
77+
// Get the length of the packet from USB
78+
u32 len = usb_ep_out_length(USB_EP_PIPE_OUT);
79+
// If it is less than one full packet, mark the short packet var with the length
80+
if (len < PACKET_SIZE) out_ring_short_packet = len;
81+
// Increase the next writable buffer by 1 (but loop to the beginning if necessary)
82+
out_ring_write_pos = (out_ring_write_pos + 1) % OUT_RING_SIZE;
83+
// Mark that we have one packet that needs attention
84+
out_ring_count += 1;
85+
// We are no longer operating over USB
86+
out_usb_pending = false;
87+
// Push the data to the correct place
88+
out_ring_step();
4789
}
4890

4991
// Finished sending on bridge, start receive from USB
5092
void pipe_bridge_in_completion() {
51-
if (pipe_state_pc_to_soc == PIPE_WAIT_FOR_BRIDGE) {
52-
usb_ep_start_out(USB_EP_PIPE_OUT, pipe_buffer_pc_to_soc, FLASH_BUFFER_SIZE);
53-
pipe_state_pc_to_soc = PIPE_WAIT_FOR_USB;
54-
} else {
55-
invalid();
56-
}
93+
// Increment the location of where we will read from next (to send over USB)
94+
out_ring_read_pos = (out_ring_read_pos + 1) % OUT_RING_SIZE;
95+
// Decrement the number of packets that need reading
96+
out_ring_count -= 1;
97+
// Mark the bridge transfer as complete
98+
out_bridge_pending = false;
99+
// Move data along
100+
out_ring_step();
57101
}
58102

59103
// Received from bridge, send to USB

soc/spid.c

+22
Original file line numberDiff line numberDiff line change
@@ -518,42 +518,64 @@ int main(int argc, char** argv) {
518518

519519
for (int chan=0; chan<N_CHANNEL; chan++) {
520520
int size = channels[chan].out_length;
521+
// If the coprocessor is ready to receive, and we have data to send
521522
if (rx_buf[1] & (1<<chan) && size > 0) {
523+
debug("coprocessor is ready to receive and we have %d bytes from channel %d", size, chan);
524+
// Make this channel readable by others
522525
CONN_POLL(chan).events |= POLLIN;
526+
// Set the length to the size we need to send
523527
transfer[desc].len = size;
528+
// Point the output buffer to the correct place
524529
transfer[desc].tx_buf = (unsigned long) &channels[chan].out_buf[0];
530+
// Note that we will have no more data to send (once this is sent)
525531
channels[chan].out_length = 0;
532+
// Mark that we need to make a SPI transaction
526533
desc++;
527534
}
528535

536+
// The number of bytes the coprocessor wants to send to a channel
529537
size = rx_buf[2+chan];
538+
// Check that the channel is writable and there is data that needs to be received
530539
if (get_channel_bitmask_state(&channels_writable_bitmask, chan) && size > 0) {
540+
debug("Channel %d is ready to have %d bytes written to it from bridge", chan, size);
541+
// Set the appropriate size
531542
transfer[desc].len = size;
543+
// Point our receive buffer to the in buf of the appropriate channel
532544
transfer[desc].rx_buf = (unsigned long) &channels[chan].in_buf[0];
545+
// Mark that we need a SPI transaction to take place
533546
desc++;
534547
}
535548
}
536549

550+
// If the previous logic designated the need for a SPI transaction
537551
if (desc != 0) {
538552
debug("Performing transfer on %i channels\n", desc);
539553

554+
// Make the SPI transaction
540555
int status = ioctl(spi_fd, SPI_IOC_MESSAGE(desc), transfer);
541556

557+
// Ensure there were no errors
542558
if (status < 0) {
543559
fatal("SPI_IOC_MESSAGE: data: %s", strerror(errno));
544560
}
545561

546562
// Write received data to the appropriate socket
547563
for (int chan=0; chan<N_CHANNEL; chan++) {
564+
// Get the length of the received data for this channel
548565
int size = rx_buf[2+chan];
566+
// Make sure that channel is writable and we have data to send to it
549567
if (get_channel_bitmask_state(&channels_writable_bitmask, chan) && size > 0) {
568+
// Write this data to the pipe
550569
int r = write(CONN_POLL(chan).fd, &channels[chan].in_buf[0], size);
551570
debug("%i: Write %u %i\n", chan, size, r);
571+
// Ensure there were no errors
552572
if (r < 0) {
553573
error("Error in write %i: %s\n", chan, strerror(errno));
554574
}
555575

576+
// Mark we want to know when this pipe is writable again
556577
CONN_POLL(chan).events |= POLLOUT;
578+
// Set the state to not writable
557579
set_channel_bitmask_state(&channels_writable_bitmask, chan, false);
558580
}
559581
}

soc/usbexecd.c

+21-21
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ enum Commands {
4242
CMD_CLOSE_STDERR = 0x33,
4343
};
4444

45-
#define debug(args...) syslog(LOG_INFO, args)
45+
#define debug(args...)
4646
#define info(args...) syslog(LOG_INFO, args)
4747
#define error(args...) syslog(LOG_ERR, args)
4848
#define fatal(args...) ({ \
@@ -66,7 +66,7 @@ struct epoll_event spid_event;
6666
// Static array for those events to be stored
6767
struct epoll_event events[MAX_EPOLL_EVENTS];
6868

69-
#define PIPE_BUF 4096
69+
#define INTERNAL_PIPE_BUF_SIZE 32768
7070
#define MAX_CTRL_ARGS 255
7171
#define MAX_WRITE_LEN 255
7272

@@ -109,7 +109,7 @@ typedef struct {
109109
// The number of 'active' bytes
110110
int bufcount;
111111
// The internal buffer used for back pressure
112-
char buffer[PIPE_BUF];
112+
char buffer[INTERNAL_PIPE_BUF_SIZE];
113113
} pipebuf_t;
114114

115115
typedef struct {
@@ -252,9 +252,9 @@ int write_from_pipebuf(pipebuf_t *pb, int fd, int write_len) {
252252
to_write = write_len;
253253

254254
// If the data would go past the end of the buffer
255-
if (pb->startpos + to_write > PIPE_BUF) {
255+
if (pb->startpos + to_write > INTERNAL_PIPE_BUF_SIZE) {
256256
// Only write until the end of the buffer
257-
to_write = PIPE_BUF - pb->startpos;
257+
to_write = INTERNAL_PIPE_BUF_SIZE - pb->startpos;
258258
}
259259

260260
// Write this batch to the file descriptor from the internal buffer
@@ -266,7 +266,7 @@ int write_from_pipebuf(pipebuf_t *pb, int fd, int write_len) {
266266
pb->bufcount -= written;
267267

268268
// If the start position marker is at the buffer end
269-
if (pb->startpos == PIPE_BUF) {
269+
if (pb->startpos == INTERNAL_PIPE_BUF_SIZE) {
270270
// Set it at the start of the buffer for the next read
271271
pb->startpos = 0;
272272
}
@@ -421,9 +421,9 @@ int pipebuf_in_write_to_sock(pipebuf_t* pb, size_t num_to_write) {
421421
int remaining = num_to_write - written;
422422
int packet_write_size = (remaining < MAX_WRITE_LEN) ? remaining : MAX_WRITE_LEN;
423423
// Send the header so the CLI knows it's about to receive data
424-
send_header(CMD_WRITE_CONTROL + pb->role, pb->id, 0, packet_write_size);
424+
send_header(CMD_WRITE_CONTROL + pb->role, pb->id, packet_write_size >> 8, packet_write_size & 0xFF);
425425
// Send the data and increment the counter of the number of bytes written
426-
pipebuf_common_debug(pb, "WRiting from stdout/stderr internal to CLI");
426+
pipebuf_common_debug(pb, "Writing from stdout/stderr internal to CLI");
427427
debug("{%d bytes}", packet_write_size);
428428
written += write_from_pipebuf(pb, sock_fd, packet_write_size);
429429
}
@@ -459,7 +459,7 @@ void pipebuf_in_ack(pipebuf_t* pb, size_t ack_number_size) {
459459

460460
// If this pipe buffer was previously full
461461
// But will now be able to send out data
462-
if (pb->bufcount == PIPE_BUF && ack_size > 0) {
462+
if (pb->bufcount == INTERNAL_PIPE_BUF_SIZE && ack_size > 0) {
463463
// Enable notifications of when the internal pipe buffer is written to
464464
add_pipebuf_epoll(pb);
465465
}
@@ -492,17 +492,17 @@ void pipebuf_in_to_internal_buffer(pipebuf_t* pb) {
492492
else to read */
493493
int r = 0;
494494
int try_to_read = 0;
495-
int space_available = PIPE_BUF - pb->bufcount;
495+
int space_available = INTERNAL_PIPE_BUF_SIZE - pb->bufcount;
496496
pipebuf_common_debug(pb, "stdout/stderr has data from child to be read");
497497
// While we have space in the pipe buffer
498498
while (space_available) {
499499
// Try to read the entire space
500500
try_to_read = space_available;
501501

502502
// If the space extends past the pipe buffer end
503-
if (pb->endpos + try_to_read > PIPE_BUF) {
503+
if (pb->endpos + try_to_read > INTERNAL_PIPE_BUF_SIZE) {
504504
// Set this read to only read up to the end
505-
try_to_read = PIPE_BUF - pb->endpos;
505+
try_to_read = INTERNAL_PIPE_BUF_SIZE - pb->endpos;
506506
}
507507

508508
pipebuf_common_debug(pb, "Attempting to write from stdout/stderr child to internal");
@@ -524,7 +524,7 @@ void pipebuf_in_to_internal_buffer(pipebuf_t* pb) {
524524
pb->endpos += r;
525525

526526
// If the start position marker is at the buffer end
527-
if (pb->endpos == PIPE_BUF) {
527+
if (pb->endpos == INTERNAL_PIPE_BUF_SIZE) {
528528
// Set it at the start of the buffer for the next read
529529
pb->endpos = 0;
530530
}
@@ -555,7 +555,7 @@ void pipebuf_in_to_internal_buffer(pipebuf_t* pb) {
555555
}
556556

557557
// If the pipe buffer is full
558-
if (pb->bufcount == PIPE_BUF) {
558+
if (pb->bufcount == INTERNAL_PIPE_BUF_SIZE) {
559559
// remove it from the epoll
560560
delete_pipebuf_epoll(pb);
561561
}
@@ -576,9 +576,9 @@ Returns: the file descriptor to communicate with the stream
576576
int pipebuf_out_init(pipebuf_t* pb, int id, int role) {
577577
int fd = pipebuf_common_init(pb, id, role, EPOLLOUT, 1);
578578

579-
pipebuf_out_ack(pb, PIPE_BUF);
579+
pipebuf_out_ack(pb, INTERNAL_PIPE_BUF_SIZE);
580580

581-
pb->credit = PIPE_BUF;
581+
pb->credit = INTERNAL_PIPE_BUF_SIZE;
582582

583583
return fd;
584584
}
@@ -621,9 +621,9 @@ void pipebuf_out_to_internal_buffer(pipebuf_t* pb, int read_len) {
621621
to_read = read_len;
622622

623623
// If this read would read further than the end of the ring buffer
624-
if (pb->endpos + read_len > PIPE_BUF) {
624+
if (pb->endpos + read_len > INTERNAL_PIPE_BUF_SIZE) {
625625
// Only read until the end of the ring buffer
626-
to_read = PIPE_BUF - pb->endpos;
626+
to_read = INTERNAL_PIPE_BUF_SIZE - pb->endpos;
627627
}
628628

629629
// Read from the socket into the buffer
@@ -645,7 +645,7 @@ void pipebuf_out_to_internal_buffer(pipebuf_t* pb, int read_len) {
645645
pb->endpos += to_read;
646646

647647
// If the end position marker is at the buffer end
648-
if (pb->endpos == PIPE_BUF) {
648+
if (pb->endpos == INTERNAL_PIPE_BUF_SIZE) {
649649
// Set it at the start of the buffer for the next read
650650
pb->endpos = 0;
651651
}
@@ -855,12 +855,12 @@ void handle_socket_readable() {
855855

856856
case CMD_WRITE_CONTROL:
857857
debug("CMD: Write to CTRL buf of process with id %d", id);
858-
pipebuf_out_to_internal_buffer(&p->ctrl, header[3]);
858+
pipebuf_out_to_internal_buffer(&p->ctrl, header[3] | (header[2] << 8));
859859
break;
860860

861861
case CMD_WRITE_STDIN:
862862
debug("CMD: Write to STDIN buf of process with id %d", id);
863-
pipebuf_out_to_internal_buffer(&p->stdin, header[3]);
863+
pipebuf_out_to_internal_buffer(&p->stdin, header[3] | (header[2] << 8));
864864
break;
865865

866866
case CMD_ACK_STDOUT:

test_rig.mk

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ $(TARGET)_SRC += \
1313
test_rig/button.c \
1414

1515
$(TARGET)_DEFINE += -D __SAMD21J18A__
16-
$(TARGET)_LDSCRIPT = common/samd21g15a_firmware_partition.ld
16+
$(TARGET)_LDSCRIPT = common/samd21g18a_firmware_partition.ld

0 commit comments

Comments
 (0)