cpld: Stream CPLD data from host

Previously CPLD programming involved a large 64kbyte buffer into which
the the entire bitstream would be completely downloaded, and at which
point the programming process would commence. This is needlessly
wasteful of memory. Moreover, it could lead to USB timeouts as the
firmware needs to wait the entire duration of the programming process
before returning an ACK to the host after the final SETUP data phase
packet.

Instead, we now receive 512 byte chunks of the bitstream and stream them
to the CPLD one at a time. We wait for each packet to be streamed out to
the CPLD before ACKing the packet to prevent active data being
overwritten.
This commit is contained in:
Ben Gamari
2013-09-02 19:44:22 -04:00
parent d2b2b11998
commit dc9c6c9667
5 changed files with 205 additions and 84 deletions

View File

@ -26,8 +26,9 @@
#include <libopencm3/lpc43xx/scu.h>
#include <stdint.h>
uint32_t xsvf_len;
unsigned char* xsvf_data;
static refill_buffer_cb refill_buffer;
static uint32_t xsvf_buffer_len, xsvf_pos;
static unsigned char* xsvf_buffer;
void cpld_jtag_setup(void) {
scu_pinmux(SCU_PINMUX_CPLD_TDO, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4);
@ -58,11 +59,16 @@ void cpld_jtag_release(void) {
}
/* return 0 if success else return error code see xsvfExecute() */
int cpld_jtag_program(const uint32_t len, unsigned char* const data) {
int cpld_jtag_program(
const uint32_t buffer_length,
unsigned char* const buffer,
refill_buffer_cb refill
) {
int error;
cpld_jtag_setup();
xsvf_data = data;
xsvf_len = len;
xsvf_buffer = buffer;
xsvf_buffer_len = buffer_length;
refill_buffer = refill;
error = xsvfExecute();
cpld_jtag_release();
@ -71,12 +77,12 @@ int cpld_jtag_program(const uint32_t len, unsigned char* const data) {
/* this gets called by the XAPP058 code */
unsigned char cpld_jtag_get_next_byte(void) {
unsigned char byte = *xsvf_data;
if (xsvf_len > 1) {
xsvf_data++;
xsvf_len--;
}
if (xsvf_pos == xsvf_buffer_len) {
refill_buffer();
xsvf_pos = 0;
}
unsigned char byte = xsvf_buffer[xsvf_pos];
xsvf_pos++;
return byte;
}

View File

@ -24,9 +24,20 @@
#include <stdint.h>
typedef void (*refill_buffer_cb)(void);
void cpld_jtag_release(void);
/* return 0 if success else return error code see xsvfExecute() see micro.h */
int cpld_jtag_program(const uint32_t len, unsigned char* const data);
/* Return 0 if success else return error code see xsvfExecute() see micro.h.
*
* We expect the buffer to be initially full of data. After the entire
* contents of the buffer has been streamed to the CPLD the given
* refill_buffer callback will be called. */
int cpld_jtag_program(
const uint32_t buffer_length,
unsigned char* const buffer,
refill_buffer_cb refill
);
unsigned char cpld_jtag_get_next_byte(void);
#endif//__CPLD_JTAG_H__

View File

@ -50,10 +50,11 @@ uint8_t* const usb_bulk_buffer = (uint8_t*)0x20004000;
static volatile uint32_t usb_bulk_buffer_offset = 0;
static const uint32_t usb_bulk_buffer_mask = 32768 - 1;
/* TODO remove this big buffer and use streaming for CPLD */
#define CPLD_XSVF_MAX_LEN (65536)
uint8_t cpld_xsvf_buffer[CPLD_XSVF_MAX_LEN];
uint16_t write_cpld_idx = 0;
usb_transfer_descriptor_t usb_td_bulk[2] ATTR_ALIGNED(64);
const uint_fast8_t usb_td_bulk_count = sizeof(usb_td_bulk) / sizeof(usb_td_bulk[0]);
uint8_t cpld_xsvf_buffer[512];
volatile bool cpld_wait = false;
uint8_t spiflash_buffer[W25Q80BV_PAGE_LEN];
char version_string[] = VERSION_STRING;
@ -191,9 +192,23 @@ usb_configuration_t usb_configuration_full_speed = {
.descriptor = usb_descriptor_configuration_full_speed,
};
usb_configuration_t usb_configuration_cpld_update_full_speed = {
.number = 2,
.speed = USB_SPEED_FULL,
.descriptor = usb_descriptor_configuration_cpld_update_full_speed,
};
usb_configuration_t usb_configuration_cpld_update_high_speed = {
.number = 2,
.speed = USB_SPEED_HIGH,
.descriptor = usb_descriptor_configuration_cpld_update_high_speed,
};
usb_configuration_t* usb_configurations[] = {
&usb_configuration_high_speed,
&usb_configuration_full_speed,
&usb_configuration_cpld_update_full_speed,
&usb_configuration_cpld_update_high_speed,
0,
};
@ -549,69 +564,6 @@ usb_request_status_t usb_vendor_request_read_spiflash(
}
}
usb_request_status_t usb_vendor_request_write_cpld(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage)
{
int error, i;
uint16_t total_len;
uint16_t len;
#define WAIT_LOOP_DELAY (6000000)
#define ALL_LEDS (PIN_LED1|PIN_LED2|PIN_LED3)
if (stage == USB_TRANSFER_STAGE_SETUP)
{
// len is limited to 64KB 16bits no overflow can happen
total_len = endpoint->setup.value;
len = endpoint->setup.length;
usb_transfer_schedule_block(endpoint->out, &cpld_xsvf_buffer[write_cpld_idx], len,
NULL, NULL);
return USB_REQUEST_STATUS_OK;
} else if (stage == USB_TRANSFER_STAGE_DATA)
{
// len is limited to 64KB 16bits no overflow can happen
total_len = endpoint->setup.value;
len = endpoint->setup.length;
write_cpld_idx = write_cpld_idx + len;
// Check if all bytes received and write CPLD
if(write_cpld_idx == total_len)
{
write_cpld_idx = 0;
error = cpld_jtag_program(total_len, &cpld_xsvf_buffer[write_cpld_idx]);
// TO FIX ACK shall be not delayed so much as cpld prog can take up to 5s.
if(error == 0)
{
usb_transfer_schedule_ack(endpoint->in);
/* blink LED1, LED2, and LED3 on success */
while (1)
{
gpio_set(PORT_LED1_3, ALL_LEDS); /* LEDs on */
for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */
__asm__("nop");
gpio_clear(PORT_LED1_3, ALL_LEDS); /* LEDs off */
for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */
__asm__("nop");
}
return USB_REQUEST_STATUS_OK;
}else
{
/* LED3 (Red) steady on error */
gpio_set(PORT_LED1_3, PIN_LED3); /* LEDs on */
while (1);
return USB_REQUEST_STATUS_STALL;
}
}else
{
usb_transfer_schedule_ack(endpoint->in);
return USB_REQUEST_STATUS_OK;
}
} else
{
return USB_REQUEST_STATUS_OK;
}
}
usb_request_status_t usb_vendor_request_read_board_id(
usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage)
{
@ -815,7 +767,7 @@ static const usb_request_handler_fn vendor_request_handler[] = {
usb_vendor_request_erase_spiflash,
usb_vendor_request_write_spiflash,
usb_vendor_request_read_spiflash,
usb_vendor_request_write_cpld,
NULL, // used to be write_cpld
usb_vendor_request_read_board_id,
usb_vendor_request_read_version_string,
usb_vendor_request_set_freq,
@ -853,13 +805,72 @@ const usb_request_handlers_t usb_request_handlers = {
.reserved = 0,
};
static void cpld_buffer_refilled(void* user_data, unsigned int length)
{
cpld_wait = false;
}
static void refill_cpld_buffer(void)
{
cpld_wait = true;
usb_transfer_schedule(
&usb_endpoint_bulk_out,
cpld_xsvf_buffer,
sizeof(cpld_xsvf_buffer),
cpld_buffer_refilled,
NULL
);
// Wait until transfer finishes
while (cpld_wait);
}
static void cpld_update(void)
{
#define WAIT_LOOP_DELAY (6000000)
#define ALL_LEDS (PIN_LED1|PIN_LED2|PIN_LED3)
int i;
int error;
usb_queue_flush_endpoint(&usb_endpoint_bulk_in);
usb_queue_flush_endpoint(&usb_endpoint_bulk_out);
refill_cpld_buffer();
error = cpld_jtag_program(sizeof(cpld_xsvf_buffer),
cpld_xsvf_buffer,
refill_cpld_buffer);
if(error == 0)
{
/* blink LED1, LED2, and LED3 on success */
while (1)
{
gpio_set(PORT_LED1_3, ALL_LEDS); /* LEDs on */
for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */
__asm__("nop");
gpio_clear(PORT_LED1_3, ALL_LEDS); /* LEDs off */
for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */
__asm__("nop");
}
}else
{
/* LED3 (Red) steady on error */
gpio_set(PORT_LED1_3, PIN_LED3); /* LEDs on */
while (1);
}
}
void usb_configuration_changed(
usb_device_t* const device
) {
set_transceiver_mode(transceiver_mode);
if( device->configuration->number ) {
if( device->configuration->number == 1 ) {
// transceiver mode
gpio_set(PORT_LED1_3, PIN_LED1);
} else if( device->configuration->number == 2 ) {
// CPLD update mode
cpld_update();
} else {
gpio_clear(PORT_LED1_3, PIN_LED1);
}

View File

@ -51,7 +51,7 @@ uint8_t usb_descriptor_device[] = {
0x01, // iManufacturer
0x02, // iProduct
0x00, // iSerialNumber
0x01 // bNumConfigurations
0x02 // bNumConfigurations
};
uint8_t usb_descriptor_device_qualifier[] = {
@ -62,7 +62,7 @@ uint8_t usb_descriptor_device_qualifier[] = {
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
64, // bMaxPacketSize0
0x01, // bNumOtherSpeedConfigurations
0x02, // bNumOtherSpeedConfigurations
0x00 // bReserved
};
@ -140,6 +140,80 @@ uint8_t usb_descriptor_configuration_high_speed[] = {
0, // TERMINATOR
};
uint8_t usb_descriptor_configuration_cpld_update_full_speed[] = {
9, // bLength
USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType
USB_WORD(32), // wTotalLength
0x01, // bNumInterfaces
0x02, // bConfigurationValue
0x04, // iConfiguration
0x80, // bmAttributes: USB-powered
250, // bMaxPower: 500mA
9, // bLength
USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0xFF, // bInterfaceClass: vendor-specific
0xFF, // bInterfaceSubClass
0xFF, // bInterfaceProtocol: vendor-specific
0x00, // iInterface
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_IN_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize
0x00, // bInterval: no NAK
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_OUT_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize
0x00, // bInterval: no NAK
0, // TERMINATOR
};
uint8_t usb_descriptor_configuration_cpld_update_high_speed[] = {
9, // bLength
USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType
USB_WORD(32), // wTotalLength
0x01, // bNumInterfaces
0x02, // bConfigurationValue
0x04, // iConfiguration
0x80, // bmAttributes: USB-powered
250, // bMaxPower: 500mA
9, // bLength
USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0xFF, // bInterfaceClass: vendor-specific
0xFF, // bInterfaceSubClass
0xFF, // bInterfaceProtocol: vendor-specific
0x00, // iInterface
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_IN_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize
0x00, // bInterval: no NAK
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_OUT_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize
0x00, // bInterval: no NAK
0, // TERMINATOR
};
uint8_t usb_descriptor_string_languages[] = {
0x04, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
@ -197,11 +271,28 @@ uint8_t usb_descriptor_string_config1_description[] = {
'r', 0x00,
};
uint8_t usb_descriptor_string_config2_description[] = {
24, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'C', 0x00,
'P', 0x00,
'L', 0x00,
'D', 0x00,
' ', 0x00,
'u', 0x00,
'p', 0x00,
'd', 0x00,
'a', 0x00,
't', 0x00,
'e', 0x00,
};
uint8_t* const usb_descriptor_strings[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product,
usb_descriptor_string_config1_description,
usb_descriptor_string_config2_description,
0, // TERMINATOR
};

View File

@ -25,6 +25,8 @@ extern uint8_t usb_descriptor_device[];
extern uint8_t usb_descriptor_device_qualifier[];
extern uint8_t usb_descriptor_configuration_full_speed[];
extern uint8_t usb_descriptor_configuration_high_speed[];
extern uint8_t usb_descriptor_configuration_cpld_update_full_speed[];
extern uint8_t usb_descriptor_configuration_cpld_update_high_speed[];
extern uint8_t usb_descriptor_string_languages[];
extern uint8_t usb_descriptor_string_manufacturer[];
extern uint8_t usb_descriptor_string_product[];