From 0f81d361c7542cd7c460adbd2f542a03637b6065 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Wed, 10 Oct 2012 22:11:44 -0700 Subject: [PATCH 01/22] Added code to support transmit from usb_test host program. Reorganization of code to support making usb_test a useful utility... --- host/usb_test/usb_test.c | 135 ++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 30 deletions(-) diff --git a/host/usb_test/usb_test.c b/host/usb_test/usb_test.c index 29f08a86..5ce79244 100644 --- a/host/usb_test/usb_test.c +++ b/host/usb_test/usb_test.c @@ -24,10 +24,20 @@ #include #include +#include #include #include +const uint16_t hackrf_usb_pid = 0x1d50; +const uint16_t hackrf_usb_vid = 0x604b; + +typedef enum { + TRANSCEIVER_MODE_RX, + TRANSCEIVER_MODE_TX, +} transceiver_mode_t; +static transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_RX; + static float TimevalDiff(const struct timeval *a, const struct timeval *b) { @@ -48,31 +58,27 @@ void write_callback(struct libusb_transfer* transfer) { } } -int main(int argc, char** argv) { - if( argc != 2 ) { - printf("Usage: usb_test \n"); - return -1; - } - - const uint32_t buffer_size = 16384; - - fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG | S_IRWXO); - if( fd == -1 ) { - printf("Failed to open file for write\n"); - return -2; +void read_callback(struct libusb_transfer* transfer) { + if( transfer->status == LIBUSB_TRANSFER_COMPLETED ) { + byte_count += transfer->actual_length; + read(fd, transfer->buffer, transfer->actual_length); + libusb_submit_transfer(transfer); + } else { + printf("transfer status was not 'completed'\n"); } +} - libusb_context* context = NULL; +libusb_device_handle* open_device(libusb_context* const context) { int result = libusb_init(NULL); if( result != 0 ) { printf("libusb_init() failed: %d\n", result); - return -4; + return NULL; } - libusb_device_handle* device = libusb_open_device_with_vid_pid(context, 0x1d50, 0x604b); + libusb_device_handle* device = libusb_open_device_with_vid_pid(context, hackrf_usb_pid, hackrf_usb_vid); if( device == NULL ) { printf("libusb_open_device_with_vid_pid() failed\n"); - return -5; + return NULL; } //int speed = libusb_get_device_speed(device); @@ -80,26 +86,46 @@ int main(int argc, char** argv) { result = libusb_set_configuration(device, 1); if( result != 0 ) { + libusb_close(device); printf("libusb_set_configuration() failed: %d\n", result); - return -6; + return NULL; } result = libusb_claim_interface(device, 0); if( result != 0 ) { + libusb_close(device); printf("libusb_claim_interface() failed: %d\n", result); - return -7; + return NULL; } - - unsigned char endpoint_address = 0x81; - //unsigned char endpoint_address = 0x02; - const uint32_t transfer_count = 1024; - struct libusb_transfer* transfers[transfer_count]; + return device; +} + +void free_transfers(struct libusb_transfer** const transfers, const uint32_t transfer_count) { + for(uint32_t transfer_index=0; transfer_indexbuffer == 0 ) { + if( transfers[transfer_index]->buffer == NULL ) { + free_transfers(transfers, transfer_count); printf("malloc() failed\n"); - return -7; + return NULL; } int error = libusb_submit_transfer(transfers[transfer_index]); if( error != 0 ) { + free_transfers(transfers, transfer_count); printf("libusb_submit_transfer() failed: %d\n", error); - return -8; + return NULL; } } + return transfers; +} + +int main(int argc, char** argv) { + if( argc != 2 ) { + printf("Usage: usb_test \n"); + return -1; + } + + const char* const path = argv[1]; + + fd = -1; + uint_fast8_t endpoint_address = 0; + libusb_transfer_cb_fn callback = NULL; + + if( transceiver_mode == TRANSCEIVER_MODE_RX ) { + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU | S_IRWXG | S_IRWXO); + endpoint_address = 0x81; + callback = &write_callback; + } else { + fd = open(path, O_RDONLY, S_IRWXU | S_IRWXG | S_IRWXO); + endpoint_address = 0x02; + callback = &read_callback; + } + + if( fd == -1 ) { + printf("Failed to open file: errno %d\n", errno); + return fd; + } + + libusb_context* const context = NULL; + libusb_device_handle* const device = open_device(context); + if( device == NULL ) { + return -3; + } + + const uint32_t transfer_count = 1024; + const uint32_t buffer_size = 16384; + struct libusb_transfer** const transfers = prepare_transfers( + device, endpoint_address, transfer_count, buffer_size, callback + ); + if( transfers == NULL ) { + return -4; + } + ////////////////////////////////////////////////////////////// struct timeval timeout = { 0, 500000 }; @@ -158,7 +231,9 @@ int main(int argc, char** argv) { call_count += 1; } while(1); - result = libusb_release_interface(device, 0); + free_transfers(transfers, transfer_count); + + int result = libusb_release_interface(device, 0); if( result != 0 ) { printf("libusb_release_interface() failed: %d\n", result); return -2000; From 8291d7fac040ecb7c4882db300da16d246908495 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 13:04:30 -0700 Subject: [PATCH 02/22] Add SGPIO configuration function for "deep" (8-slice) TX mode. --- firmware/common/sgpio.c | 79 +++++++++++++++++++++++++++++++++++++++++ firmware/common/sgpio.h | 1 + 2 files changed, 80 insertions(+) diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 2db69608..e38c3d3e 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -146,6 +146,85 @@ void sgpio_configure_for_tx() { ; } +void sgpio_configure_for_tx_deep() { + // Disable all counters during configuration + SGPIO_CTRL_ENABLE = 0; + + sgpio_configure_pin_functions(); + + // Set SGPIO output values. + SGPIO_GPIO_OUTREG = + (1L << 11) | // direction + (1L << 10); // disable + + // Enable SGPIO pin outputs. + SGPIO_GPIO_OENREG = + (1L << 11) | // direction: TX: data to CPLD + (1L << 10) | // disable + (0L << 9) | // capture + (0L << 8) | // clock + 0xFF; // data: output + + SGPIO_OUT_MUX_CFG( 8) = 0; // SGPIO: Input: clock + SGPIO_OUT_MUX_CFG( 9) = 0; // SGPIO: Input: qualifier + SGPIO_OUT_MUX_CFG(10) = (0L << 4) | (4L << 0); // GPIO: Output: disable + SGPIO_OUT_MUX_CFG(11) = (0L << 4) | (4L << 0); // GPIO: Output: direction + + for(uint_fast8_t i=0; i<8; i++) { + // SGPIO pin 0 outputs slice A bit "i". + SGPIO_OUT_MUX_CFG(i) = + (0L << 4) | // P_OE_CFG = 0 + (11L << 0); // P_OUT_CFG = 11, dout_doutm8c (8-bit mode 8c) + } + + const uint_fast8_t slice_indices[] = { + SGPIO_SLICE_A, + SGPIO_SLICE_I, + SGPIO_SLICE_E, + SGPIO_SLICE_J, + SGPIO_SLICE_C, + SGPIO_SLICE_K, + SGPIO_SLICE_F, + SGPIO_SLICE_L, + }; + + uint32_t slice_enable_mask = 0; + for(uint_fast8_t i=0; i<8; i++) { + uint_fast8_t slice_index = slice_indices[i]; + const uint_fast8_t concat_order = 3; + const uint_fast8_t concat_enable = 1; + SGPIO_MUX_CFG(slice_index) = + (concat_order << 12) | // CONCAT_ORDER = 3 (eight slices) + (concat_enable << 11) | // CONCAT_ENABLE = 1 (concatenate data) + (0L << 9) | // QUALIFIER_SLICE_MODE = X + (1L << 7) | // QUALIFIER_PIN_MODE = 1 (SGPIO9) + (3L << 5) | // QUALIFIER_MODE = 3 (external SGPIO pin) + (0L << 3) | // CLK_SOURCE_SLICE_MODE = X + (0L << 1) | // CLK_SOURCE_PIN_MODE = 0 (SGPIO8) + (1L << 0); // EXT_CLK_ENABLE = 1, external clock signal (slice) + + SGPIO_SLICE_MUX_CFG(slice_index) = + (0L << 8) | // INV_QUALIFIER = 0 (use normal qualifier) + (3L << 6) | // PARALLEL_MODE = 3 (shift 8 bits per clock) + (0L << 4) | // DATA_CAPTURE_MODE = 0 (detect rising edge) + (0L << 3) | // INV_OUT_CLK = X + (1L << 2) | // CLKGEN_MODE = 1 (use external pin clock) + (1L << 1) | // CLK_CAPTURE_MODE = 1 (use falling clock edge) + (0L << 0); // MATCH_MODE = 0 (do not match data) + + SGPIO_PRESET(slice_index) = 0; // External clock, don't care + SGPIO_COUNT(slice_index) = 0; // External clock, don't care + SGPIO_POS(slice_index) = (0x1f << 8) | (0x1f << 0); + SGPIO_REG(slice_index) = 0xFFFFFFFF; // Primary output data register + SGPIO_REG_SS(slice_index) = 0xFFFFFFFF; // Shadow output data register + + slice_enable_mask |= (1 << slice_index); + } + + // Start SGPIO operation by enabling slice clocks. + SGPIO_CTRL_ENABLE = slice_enable_mask; +} + void sgpio_configure_for_rx() { // Disable all counters during configuration SGPIO_CTRL_ENABLE = 0; diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index 8b8f471c..d41acef4 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -25,6 +25,7 @@ void sgpio_configure_pin_functions(); void sgpio_test_interface(); void sgpio_configure_for_tx(); +void sgpio_configure_for_tx_deep(); void sgpio_configure_for_rx(); void sgpio_configure_for_rx_deep(); void sgpio_cpld_stream_enable(); From fb9ce63f7f5ca464028eff2783a0bb7b6894f38c Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 13:06:27 -0700 Subject: [PATCH 03/22] Add (manual) switch for TX and RX mode. Add code for configuring TX mode. TODO: Consolidate lots of repetitive code. --- firmware/usb_performance/usb_performance.c | 85 ++++++++++++++++------ 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/firmware/usb_performance/usb_performance.c b/firmware/usb_performance/usb_performance.c index 5a6989e3..b5eaa6fa 100644 --- a/firmware/usb_performance/usb_performance.c +++ b/firmware/usb_performance/usb_performance.c @@ -38,6 +38,12 @@ #include "usb_descriptor.h" #include "usb_standard_request.h" +typedef enum { + TRANSCEIVER_MODE_RX, + TRANSCEIVER_MODE_TX, +} transceiver_mode_t; +static transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_TX; + 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; @@ -202,12 +208,15 @@ bool usb_set_configuration( // hard-coding, this whole function can move into a shared/reusable // library. if( device->configuration && (device->configuration->number == 1) ) { - usb_endpoint_init(&usb_endpoint_bulk_in); - usb_endpoint_init(&usb_endpoint_bulk_out); + if( transceiver_mode == TRANSCEIVER_MODE_RX ) { + sgpio_configure_for_rx_deep(); + usb_endpoint_init(&usb_endpoint_bulk_in); + } else { + sgpio_configure_for_tx_deep(); + usb_endpoint_init(&usb_endpoint_bulk_out); + } usb_init_buffers_bulk(); - - sgpio_configure_for_rx_deep(); nvic_set_priority(NVIC_M4_SGPIO_IRQ, 0); nvic_enable_irq(NVIC_M4_SGPIO_IRQ); @@ -237,15 +246,26 @@ void sgpio_irqhandler() { SGPIO_CLR_STATUS_1 = 0xFFFFFFFF; uint32_t* const p = (uint32_t*)&usb_bulk_buffer[usb_bulk_buffer_offset]; - p[7] = SGPIO_REG_SS(SGPIO_SLICE_A); - p[6] = SGPIO_REG_SS(SGPIO_SLICE_I); - p[5] = SGPIO_REG_SS(SGPIO_SLICE_E); - p[4] = SGPIO_REG_SS(SGPIO_SLICE_J); - p[3] = SGPIO_REG_SS(SGPIO_SLICE_C); - p[2] = SGPIO_REG_SS(SGPIO_SLICE_K); - p[1] = SGPIO_REG_SS(SGPIO_SLICE_F); - p[0] = SGPIO_REG_SS(SGPIO_SLICE_L); - + if( transceiver_mode == TRANSCEIVER_MODE_RX ) { + p[7] = SGPIO_REG_SS(SGPIO_SLICE_A); + p[6] = SGPIO_REG_SS(SGPIO_SLICE_I); + p[5] = SGPIO_REG_SS(SGPIO_SLICE_E); + p[4] = SGPIO_REG_SS(SGPIO_SLICE_J); + p[3] = SGPIO_REG_SS(SGPIO_SLICE_C); + p[2] = SGPIO_REG_SS(SGPIO_SLICE_K); + p[1] = SGPIO_REG_SS(SGPIO_SLICE_F); + p[0] = SGPIO_REG_SS(SGPIO_SLICE_L); + } else { + SGPIO_REG_SS(SGPIO_SLICE_A) = p[7]; + SGPIO_REG_SS(SGPIO_SLICE_I) = p[6]; + SGPIO_REG_SS(SGPIO_SLICE_E) = p[5]; + SGPIO_REG_SS(SGPIO_SLICE_J) = p[4]; + SGPIO_REG_SS(SGPIO_SLICE_C) = p[3]; + SGPIO_REG_SS(SGPIO_SLICE_K) = p[2]; + SGPIO_REG_SS(SGPIO_SLICE_F) = p[1]; + SGPIO_REG_SS(SGPIO_SLICE_L) = p[0]; + } + usb_bulk_buffer_offset = (usb_bulk_buffer_offset + 32) & usb_bulk_buffer_mask; } @@ -290,14 +310,37 @@ int main(void) { max2837_rx(); ssp1_set_mode_max5864(); max5864_xcvr(); - - while(true) { - while( usb_bulk_buffer_offset < 16384 ); - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[0]); - - while( usb_bulk_buffer_offset >= 16384 ); - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[1]); - } + // TODO: If this turns out to be the same code, simplify. + if( transceiver_mode == TRANSCEIVER_MODE_RX ) { + while(true) { + // Wait until buffer 0 is received. + while( usb_bulk_buffer_offset < 16384 ); + + // Set up IN transfer to send buffer 0. + usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[0]); + + // Wait until buffer 1 is received. + while( usb_bulk_buffer_offset >= 16384 ); + + // Set up IN transfer to send buffer 1. + usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[1]); + } + } else { + while(true) { + // Wait until buffer 0 is transmitted. + while( usb_bulk_buffer_offset < 16384 ); + + // Set up OUT transfer to fill buffer 0. + usb_endpoint_schedule_no_int(&usb_endpoint_bulk_out, &usb_td_bulk[0]); + + // Wait until buffer 1 is transmitted. + while( usb_bulk_buffer_offset >= 16384 ); + + // Set up OUT transfer to fill buffer 1. + usb_endpoint_schedule_no_int(&usb_endpoint_bulk_out, &usb_td_bulk[1]); + } + } + return 0; } From c0cb64f3166a7c3b3d026369e6de8222a7e49db6 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 13:07:25 -0700 Subject: [PATCH 04/22] Add support for TX mode. Add getopt control of RX or TX mode. --- host/usb_test/usb_test.c | 55 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/host/usb_test/usb_test.c b/host/usb_test/usb_test.c index 5ce79244..c984898c 100644 --- a/host/usb_test/usb_test.c +++ b/host/usb_test/usb_test.c @@ -21,7 +21,9 @@ #include #include +#include #include +#include #include #include @@ -156,13 +158,56 @@ struct libusb_transfer** prepare_transfers( return transfers; } +static void usage() { + printf("Usage:\n"); + printf("\tGo fish.\n"); +} + int main(int argc, char** argv) { - if( argc != 2 ) { - printf("Usage: usb_test \n"); - return -1; - } + int opt; + bool receive = false; + bool transmit = false; + const char* path = NULL; - const char* const path = argv[1]; + while( (opt = getopt(argc, argv, "r:t:")) != EOF ) { + switch( opt ) { + case 'r': + receive = true; + path = optarg; + break; + + case 't': + transmit = true; + path = optarg; + break; + + default: + usage(); + return 1; + } + } + + if( transmit == receive ) { + if( transmit == true ) { + fprintf(stderr, "receive and transmit options are mutually exclusive\n"); + } else { + fprintf(stderr, "specify either transmit or receive option\n"); + } + return 1; + } + + if( receive ) { + transceiver_mode = TRANSCEIVER_MODE_RX; + } + + if( transmit ) { + transceiver_mode = TRANSCEIVER_MODE_TX; + } + + if( path == NULL ) { + fprintf(stderr, "specify a path to a file to transmit/receive\n"); + return 1; + } fd = -1; uint_fast8_t endpoint_address = 0; From 6f0fda0beeafc7e4739fe9078fcb8dbb951b0740 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 15:12:25 -0700 Subject: [PATCH 05/22] Moved transceiver_mode_t to hackrf_core.h. Changed transceiver_mode to volatile, since it's liable to be accessed at interrupt time. --- firmware/common/hackrf_core.h | 5 +++++ firmware/usb_performance/usb_performance.c | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index 37722b1c..7eb57698 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -200,6 +200,11 @@ extern "C" /* TODO add other Pins */ +typedef enum { + TRANSCEIVER_MODE_RX, + TRANSCEIVER_MODE_TX, +} transceiver_mode_t; + void delay(uint32_t duration); void cpu_clock_init(void); diff --git a/firmware/usb_performance/usb_performance.c b/firmware/usb_performance/usb_performance.c index b5eaa6fa..a4f7fd0d 100644 --- a/firmware/usb_performance/usb_performance.c +++ b/firmware/usb_performance/usb_performance.c @@ -38,11 +38,7 @@ #include "usb_descriptor.h" #include "usb_standard_request.h" -typedef enum { - TRANSCEIVER_MODE_RX, - TRANSCEIVER_MODE_TX, -} transceiver_mode_t; -static transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_TX; +static volatile transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_TX; uint8_t* const usb_bulk_buffer = (uint8_t*)0x20004000; static volatile uint32_t usb_bulk_buffer_offset = 0; From 1bad2d8536e7486616aa7234ead9f42c700e966a Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 15:18:16 -0700 Subject: [PATCH 06/22] Consolidated two "deep" SGPIO configuration functions into one, qualified by transceiver mode (RX or TX). 95-ish % of the code was common. --- firmware/common/sgpio.c | 101 +++------------------ firmware/common/sgpio.h | 5 +- firmware/usb_performance/usb_performance.c | 4 +- 3 files changed, 20 insertions(+), 90 deletions(-) diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index e38c3d3e..d81a5ac2 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -146,85 +146,6 @@ void sgpio_configure_for_tx() { ; } -void sgpio_configure_for_tx_deep() { - // Disable all counters during configuration - SGPIO_CTRL_ENABLE = 0; - - sgpio_configure_pin_functions(); - - // Set SGPIO output values. - SGPIO_GPIO_OUTREG = - (1L << 11) | // direction - (1L << 10); // disable - - // Enable SGPIO pin outputs. - SGPIO_GPIO_OENREG = - (1L << 11) | // direction: TX: data to CPLD - (1L << 10) | // disable - (0L << 9) | // capture - (0L << 8) | // clock - 0xFF; // data: output - - SGPIO_OUT_MUX_CFG( 8) = 0; // SGPIO: Input: clock - SGPIO_OUT_MUX_CFG( 9) = 0; // SGPIO: Input: qualifier - SGPIO_OUT_MUX_CFG(10) = (0L << 4) | (4L << 0); // GPIO: Output: disable - SGPIO_OUT_MUX_CFG(11) = (0L << 4) | (4L << 0); // GPIO: Output: direction - - for(uint_fast8_t i=0; i<8; i++) { - // SGPIO pin 0 outputs slice A bit "i". - SGPIO_OUT_MUX_CFG(i) = - (0L << 4) | // P_OE_CFG = 0 - (11L << 0); // P_OUT_CFG = 11, dout_doutm8c (8-bit mode 8c) - } - - const uint_fast8_t slice_indices[] = { - SGPIO_SLICE_A, - SGPIO_SLICE_I, - SGPIO_SLICE_E, - SGPIO_SLICE_J, - SGPIO_SLICE_C, - SGPIO_SLICE_K, - SGPIO_SLICE_F, - SGPIO_SLICE_L, - }; - - uint32_t slice_enable_mask = 0; - for(uint_fast8_t i=0; i<8; i++) { - uint_fast8_t slice_index = slice_indices[i]; - const uint_fast8_t concat_order = 3; - const uint_fast8_t concat_enable = 1; - SGPIO_MUX_CFG(slice_index) = - (concat_order << 12) | // CONCAT_ORDER = 3 (eight slices) - (concat_enable << 11) | // CONCAT_ENABLE = 1 (concatenate data) - (0L << 9) | // QUALIFIER_SLICE_MODE = X - (1L << 7) | // QUALIFIER_PIN_MODE = 1 (SGPIO9) - (3L << 5) | // QUALIFIER_MODE = 3 (external SGPIO pin) - (0L << 3) | // CLK_SOURCE_SLICE_MODE = X - (0L << 1) | // CLK_SOURCE_PIN_MODE = 0 (SGPIO8) - (1L << 0); // EXT_CLK_ENABLE = 1, external clock signal (slice) - - SGPIO_SLICE_MUX_CFG(slice_index) = - (0L << 8) | // INV_QUALIFIER = 0 (use normal qualifier) - (3L << 6) | // PARALLEL_MODE = 3 (shift 8 bits per clock) - (0L << 4) | // DATA_CAPTURE_MODE = 0 (detect rising edge) - (0L << 3) | // INV_OUT_CLK = X - (1L << 2) | // CLKGEN_MODE = 1 (use external pin clock) - (1L << 1) | // CLK_CAPTURE_MODE = 1 (use falling clock edge) - (0L << 0); // MATCH_MODE = 0 (do not match data) - - SGPIO_PRESET(slice_index) = 0; // External clock, don't care - SGPIO_COUNT(slice_index) = 0; // External clock, don't care - SGPIO_POS(slice_index) = (0x1f << 8) | (0x1f << 0); - SGPIO_REG(slice_index) = 0xFFFFFFFF; // Primary output data register - SGPIO_REG_SS(slice_index) = 0xFFFFFFFF; // Shadow output data register - - slice_enable_mask |= (1 << slice_index); - } - - // Start SGPIO operation by enabling slice clocks. - SGPIO_CTRL_ENABLE = slice_enable_mask; -} - void sgpio_configure_for_rx() { // Disable all counters during configuration SGPIO_CTRL_ENABLE = 0; @@ -287,24 +208,30 @@ void sgpio_configure_for_rx() { ; } -void sgpio_configure_for_rx_deep() { +void sgpio_configure_deep(const transceiver_mode_t transceiver_mode) { // Disable all counters during configuration SGPIO_CTRL_ENABLE = 0; sgpio_configure_pin_functions(); // Set SGPIO output values. + const uint_fast8_t cpld_direction = + (transceiver_mode == TRANSCEIVER_MODE_TX) ? 1 : 0; SGPIO_GPIO_OUTREG = - (0L << 11) | // direction + (cpld_direction << 11) | // direction (1L << 10); // disable // Enable SGPIO pin outputs. + const uint_fast16_t sgpio_gpio_data_direction = + (transceiver_mode == TRANSCEIVER_MODE_TX) + ? (0xFF << 0) + : (0x00 << 0); SGPIO_GPIO_OENREG = - (1L << 11) | // direction: RX: data from CPLD + (1L << 11) | // direction (1L << 10) | // disable (0L << 9) | // capture (0L << 8) | // clock - 0x00; // data: input + sgpio_gpio_data_direction; // data: output SGPIO_OUT_MUX_CFG( 8) = 0; // SGPIO: Input: clock SGPIO_OUT_MUX_CFG( 9) = 0; // SGPIO: Input: qualifier @@ -312,9 +239,10 @@ void sgpio_configure_for_rx_deep() { SGPIO_OUT_MUX_CFG(11) = (0L << 4) | (4L << 0); // GPIO: Output: direction for(uint_fast8_t i=0; i<8; i++) { + // SGPIO pin 0 outputs slice A bit "i". SGPIO_OUT_MUX_CFG(i) = (0L << 4) | // P_OE_CFG = 0 - (9L << 0); // P_OUT_CFG = 9, dout_doutm8a (8-bit mode 8a) + (11L << 0); // P_OUT_CFG = 11, dout_doutm8c (8-bit mode 8c) } const uint_fast8_t slice_indices[] = { @@ -331,8 +259,9 @@ void sgpio_configure_for_rx_deep() { uint32_t slice_enable_mask = 0; for(uint_fast8_t i=0; i<8; i++) { uint_fast8_t slice_index = slice_indices[i]; - const uint_fast8_t concat_order = (slice_index == SGPIO_SLICE_A) ? 0 : 3; - const uint_fast8_t concat_enable = (slice_index == SGPIO_SLICE_A) ? 0 : 1; + const bool input_slice = (i == 0) && (transceiver_mode == TRANSCEIVER_MODE_RX); + const uint_fast8_t concat_order = input_slice ? 0 : 3; + const uint_fast8_t concat_enable = input_slice ? 0 : 1; SGPIO_MUX_CFG(slice_index) = (concat_order << 12) | // CONCAT_ORDER = 3 (eight slices) (concat_enable << 11) | // CONCAT_ENABLE = 1 (concatenate data) diff --git a/firmware/common/sgpio.h b/firmware/common/sgpio.h index d41acef4..622ed802 100644 --- a/firmware/common/sgpio.h +++ b/firmware/common/sgpio.h @@ -22,12 +22,13 @@ #ifndef __SGPIO_H__ #define __SGPIO_H__ +#include + void sgpio_configure_pin_functions(); void sgpio_test_interface(); void sgpio_configure_for_tx(); -void sgpio_configure_for_tx_deep(); void sgpio_configure_for_rx(); -void sgpio_configure_for_rx_deep(); +void sgpio_configure_deep(const transceiver_mode_t transceiver_mode); void sgpio_cpld_stream_enable(); void sgpio_cpld_stream_disable(); bool sgpio_cpld_stream_is_enabled(); diff --git a/firmware/usb_performance/usb_performance.c b/firmware/usb_performance/usb_performance.c index a4f7fd0d..7e8752ef 100644 --- a/firmware/usb_performance/usb_performance.c +++ b/firmware/usb_performance/usb_performance.c @@ -205,10 +205,10 @@ bool usb_set_configuration( // library. if( device->configuration && (device->configuration->number == 1) ) { if( transceiver_mode == TRANSCEIVER_MODE_RX ) { - sgpio_configure_for_rx_deep(); + sgpio_configure_deep(transceiver_mode); usb_endpoint_init(&usb_endpoint_bulk_in); } else { - sgpio_configure_for_tx_deep(); + sgpio_configure_deep(transceiver_mode); usb_endpoint_init(&usb_endpoint_bulk_out); } From 1b5574d2b9f28376c94b0254f82e4731c946c0f5 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 15:19:01 -0700 Subject: [PATCH 07/22] RX and TX sample buffer loops turned out to be the same, so they're consolidated. --- firmware/usb_performance/usb_performance.c | 37 ++++++---------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/firmware/usb_performance/usb_performance.c b/firmware/usb_performance/usb_performance.c index 7e8752ef..1a54927a 100644 --- a/firmware/usb_performance/usb_performance.c +++ b/firmware/usb_performance/usb_performance.c @@ -307,35 +307,18 @@ int main(void) { ssp1_set_mode_max5864(); max5864_xcvr(); - // TODO: If this turns out to be the same code, simplify. - if( transceiver_mode == TRANSCEIVER_MODE_RX ) { - while(true) { - // Wait until buffer 0 is received. - while( usb_bulk_buffer_offset < 16384 ); + while(true) { + // Wait until buffer 0 is transmitted/received. + while( usb_bulk_buffer_offset < 16384 ); - // Set up IN transfer to send buffer 0. - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[0]); - - // Wait until buffer 1 is received. - while( usb_bulk_buffer_offset >= 16384 ); + // Set up IN transfer of buffer 0. + usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[0]); + + // Wait until buffer 1 is transmitted/received. + while( usb_bulk_buffer_offset >= 16384 ); - // Set up IN transfer to send buffer 1. - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[1]); - } - } else { - while(true) { - // Wait until buffer 0 is transmitted. - while( usb_bulk_buffer_offset < 16384 ); - - // Set up OUT transfer to fill buffer 0. - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_out, &usb_td_bulk[0]); - - // Wait until buffer 1 is transmitted. - while( usb_bulk_buffer_offset >= 16384 ); - - // Set up OUT transfer to fill buffer 1. - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_out, &usb_td_bulk[1]); - } + // Set up IN transfer of buffer 1. + usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[1]); } return 0; From 9b579232a72f9b47ed507b5679c6e0bf5fdb160b Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 15:45:28 -0700 Subject: [PATCH 08/22] Consolidated single-slice SGPIO configuration functions into single sgpio_configure() function. --- firmware/common/sgpio.c | 156 +++------------------ firmware/common/sgpio.h | 7 +- firmware/sgpio-rx/sgpio-rx.c | 4 +- firmware/sgpio/sgpio.c | 4 +- firmware/usb_performance/usb_performance.c | 4 +- 5 files changed, 30 insertions(+), 145 deletions(-) diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index d81a5ac2..c04f39da 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -83,132 +83,10 @@ void sgpio_test_interface() { } } -void sgpio_configure_for_tx() { - // Disable all counters during configuration - SGPIO_CTRL_ENABLE = 0; - - sgpio_configure_pin_functions(); - - // Set SGPIO output values. - SGPIO_GPIO_OUTREG = - (1L << 11) | // direction - (1L << 10); // disable - - // Enable SGPIO pin outputs. - SGPIO_GPIO_OENREG = - (1L << 11) | // direction: TX: data to CPLD - (1L << 10) | // disable - (0L << 9) | // capture - (0L << 8) | // clock - 0xFF; // data: output - - SGPIO_OUT_MUX_CFG( 8) = 0; // SGPIO: Input: clock - SGPIO_OUT_MUX_CFG( 9) = 0; // SGPIO: Input: qualifier - SGPIO_OUT_MUX_CFG(10) = (0L << 4) | (4L << 0); // GPIO: Output: disable - SGPIO_OUT_MUX_CFG(11) = (0L << 4) | (4L << 0); // GPIO: Output: direction - - for(uint_fast8_t i=0; i<8; i++) { - // SGPIO pin 0 outputs slice A bit "i". - SGPIO_OUT_MUX_CFG(i) = - (0L << 4) | // P_OE_CFG = 0 - (9L << 0); // P_OUT_CFG = 9, dout_doutm8a (8-bit mode 8a) - } - - // Slice A - SGPIO_MUX_CFG(SGPIO_SLICE_A) = - (0L << 12) | // CONCAT_ORDER = 0 (self-loop) - (1L << 11) | // CONCAT_ENABLE = 1 (concatenate data) - (0L << 9) | // QUALIFIER_SLICE_MODE = X - (1L << 7) | // QUALIFIER_PIN_MODE = 1 (SGPIO9) - (3L << 5) | // QUALIFIER_MODE = 3 (external SGPIO pin) - (0L << 3) | // CLK_SOURCE_SLICE_MODE = X - (0L << 1) | // CLK_SOURCE_PIN_MODE = 0 (SGPIO8) - (1L << 0); // EXT_CLK_ENABLE = 1, external clock signal (slice) - - SGPIO_SLICE_MUX_CFG(SGPIO_SLICE_A) = - (0L << 8) | // INV_QUALIFIER = 0 (use normal qualifier) - (3L << 6) | // PARALLEL_MODE = 3 (shift 8 bits per clock) - (0L << 4) | // DATA_CAPTURE_MODE = 0 (detect rising edge) - (0L << 3) | // INV_OUT_CLK = 0 (normal clock) - (1L << 2) | // CLKGEN_MODE = 1 (use external pin clock) - (0L << 1) | // CLK_CAPTURE_MODE = 0 (use rising clock edge) - (0L << 0); // MATCH_MODE = 0 (do not match data) - - SGPIO_PRESET(SGPIO_SLICE_A) = 0; - SGPIO_COUNT(SGPIO_SLICE_A) = 0; - SGPIO_POS(SGPIO_SLICE_A) = (0x3L << 8) | (0x3L << 0); - SGPIO_REG(SGPIO_SLICE_A) = 0x80808080; // Primary output data register - SGPIO_REG_SS(SGPIO_SLICE_A) = 0x80808080; // Shadow output data register - - // Start SGPIO operation by enabling slice clocks. - SGPIO_CTRL_ENABLE = - (1L << SGPIO_SLICE_A) - ; -} - -void sgpio_configure_for_rx() { - // Disable all counters during configuration - SGPIO_CTRL_ENABLE = 0; - - sgpio_configure_pin_functions(); - - // Set SGPIO output values. - SGPIO_GPIO_OUTREG = - (0L << 11) | // direction - (1L << 10); // disable - - // Enable SGPIO pin outputs. - SGPIO_GPIO_OENREG = - (1L << 11) | // direction: RX: data from CPLD - (1L << 10) | // disable - (0L << 9) | // capture - (0L << 8) | // clock - 0x00; // data: input - - SGPIO_OUT_MUX_CFG( 8) = 0; // SGPIO: Input: clock - SGPIO_OUT_MUX_CFG( 9) = 0; // SGPIO: Input: qualifier - SGPIO_OUT_MUX_CFG(10) = (0L << 4) | (4L << 0); // GPIO: Output: disable - SGPIO_OUT_MUX_CFG(11) = (0L << 4) | (4L << 0); // GPIO: Output: direction - - for(uint_fast8_t i=0; i<8; i++) { - SGPIO_OUT_MUX_CFG(i) = - (0L << 4) | // P_OE_CFG = 0 - (9L << 0); // P_OUT_CFG = 9, dout_doutm8a (8-bit mode 8a) - } - - // Slice A - SGPIO_MUX_CFG(SGPIO_SLICE_A) = - (0L << 12) | // CONCAT_ORDER = X - (0L << 11) | // CONCAT_ENABLE = 0 (concatenate data) - (0L << 9) | // QUALIFIER_SLICE_MODE = X - (1L << 7) | // QUALIFIER_PIN_MODE = 1 (SGPIO9) - (3L << 5) | // QUALIFIER_MODE = 3 (external SGPIO pin) - (0L << 3) | // CLK_SOURCE_SLICE_MODE = X - (0L << 1) | // CLK_SOURCE_PIN_MODE = 0 (SGPIO8) - (1L << 0); // EXT_CLK_ENABLE = 1, external clock signal (slice) - - SGPIO_SLICE_MUX_CFG(SGPIO_SLICE_A) = - (0L << 8) | // INV_QUALIFIER = 0 (use normal qualifier) - (3L << 6) | // PARALLEL_MODE = 3 (shift 8 bits per clock) - (0L << 4) | // DATA_CAPTURE_MODE = 0 (detect rising edge) - (0L << 3) | // INV_OUT_CLK = X - (1L << 2) | // CLKGEN_MODE = 1 (use external pin clock) - (1L << 1) | // CLK_CAPTURE_MODE = 1 (use falling clock edge) - (0L << 0); // MATCH_MODE = 0 (do not match data) - - SGPIO_PRESET(SGPIO_SLICE_A) = 0; - SGPIO_COUNT(SGPIO_SLICE_A) = 0; - SGPIO_POS(SGPIO_SLICE_A) = (0 << 8) | (0 << 0); - SGPIO_REG(SGPIO_SLICE_A) = 0xCAFEBABE; // Primary output data register - SGPIO_REG_SS(SGPIO_SLICE_A) = 0xDEADBEEF; // Shadow output data register - - // Start SGPIO operation by enabling slice clocks. - SGPIO_CTRL_ENABLE = - (1L << SGPIO_SLICE_A) - ; -} - -void sgpio_configure_deep(const transceiver_mode_t transceiver_mode) { +void sgpio_configure( + const transceiver_mode_t transceiver_mode, + const bool multi_slice +) { // Disable all counters during configuration SGPIO_CTRL_ENABLE = 0; @@ -238,11 +116,13 @@ void sgpio_configure_deep(const transceiver_mode_t transceiver_mode) { SGPIO_OUT_MUX_CFG(10) = (0L << 4) | (4L << 0); // GPIO: Output: disable SGPIO_OUT_MUX_CFG(11) = (0L << 4) | (4L << 0); // GPIO: Output: direction + const uint_fast8_t output_multiplexing_mode = + multi_slice ? 11 : 9; for(uint_fast8_t i=0; i<8; i++) { // SGPIO pin 0 outputs slice A bit "i". SGPIO_OUT_MUX_CFG(i) = (0L << 4) | // P_OE_CFG = 0 - (11L << 0); // P_OUT_CFG = 11, dout_doutm8c (8-bit mode 8c) + (output_multiplexing_mode << 0); } const uint_fast8_t slice_indices[] = { @@ -256,15 +136,19 @@ void sgpio_configure_deep(const transceiver_mode_t transceiver_mode) { SGPIO_SLICE_L, }; + const bool single_slice = !multi_slice; + const uint_fast8_t slice_count = multi_slice ? 8 : 1; + uint32_t slice_enable_mask = 0; - for(uint_fast8_t i=0; i<8; i++) { - uint_fast8_t slice_index = slice_indices[i]; + for(uint_fast8_t i=0; i void tx_test() { - sgpio_configure_for_tx(); + sgpio_configure(TRANSCEIVER_MODE_TX, false); // LSB goes out first, samples are 0x volatile uint32_t buffer[] = { @@ -54,7 +54,7 @@ void tx_test() { } void rx_test() { - sgpio_configure_for_rx(); + sgpio_configure(TRANSCEIVER_MODE_RX, false); volatile uint32_t buffer[4096]; uint32_t i = 0; diff --git a/firmware/sgpio/sgpio.c b/firmware/sgpio/sgpio.c index 88008313..fbf40ffd 100644 --- a/firmware/sgpio/sgpio.c +++ b/firmware/sgpio/sgpio.c @@ -31,7 +31,7 @@ #include void tx_test() { - sgpio_configure_for_tx(); + sgpio_configure(TRANSCEIVER_MODE_TX, false); // LSB goes out first, samples are 0x volatile uint32_t buffer[] = { @@ -52,7 +52,7 @@ void tx_test() { } void rx_test() { - sgpio_configure_for_rx(); + sgpio_configure(TRANSCEIVER_MODE_RX, false); volatile uint32_t buffer[4096]; uint32_t i = 0; diff --git a/firmware/usb_performance/usb_performance.c b/firmware/usb_performance/usb_performance.c index 1a54927a..2766981d 100644 --- a/firmware/usb_performance/usb_performance.c +++ b/firmware/usb_performance/usb_performance.c @@ -205,10 +205,10 @@ bool usb_set_configuration( // library. if( device->configuration && (device->configuration->number == 1) ) { if( transceiver_mode == TRANSCEIVER_MODE_RX ) { - sgpio_configure_deep(transceiver_mode); + sgpio_configure(transceiver_mode, true); usb_endpoint_init(&usb_endpoint_bulk_in); } else { - sgpio_configure_deep(transceiver_mode); + sgpio_configure(transceiver_mode, true); usb_endpoint_init(&usb_endpoint_bulk_out); } From a1af4356b62eec3ae978be02ed9eba38ea7016e8 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 11 Oct 2012 20:46:21 -0700 Subject: [PATCH 09/22] Reworked sgpio.c to make use of #defines for fields, instead of hard-coding shifts. Proper. --- firmware/common/sgpio.c | 99 ++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index c04f39da..451e9c52 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -59,17 +59,23 @@ void sgpio_test_interface() { // Make all SGPIO controlled by SGPIO's "GPIO" registers for (uint_fast8_t i = 0; i < 16; i++) { - SGPIO_OUT_MUX_CFG(i) = (0L << 4) | (4L << 0); + SGPIO_OUT_MUX_CFG(i) = + SGPIO_OUT_MUX_CFG_P_OE_CFG(0) + | SGPIO_OUT_MUX_CFG_P_OUT_CFG(4); } // Set SGPIO output values. - SGPIO_GPIO_OUTREG = (1L << host_direction_sgpio_pin) - | (1L << host_disable_sgpio_pin); + SGPIO_GPIO_OUTREG = + (1L << host_direction_sgpio_pin) + | (1L << host_disable_sgpio_pin); // Enable SGPIO pin outputs. - SGPIO_GPIO_OENREG = (1L << host_direction_sgpio_pin) - | (1L << host_disable_sgpio_pin) | (0L << host_capture_sgpio_pin) - | (0L << host_clock_sgpio_pin) | (0xFF << 0); + SGPIO_GPIO_OENREG = + (1L << host_direction_sgpio_pin) + | (1L << host_disable_sgpio_pin) + | (0L << host_capture_sgpio_pin) + | (0L << host_clock_sgpio_pin) + | (0xFF << 0); // Configure SGPIO slices. @@ -96,8 +102,9 @@ void sgpio_configure( const uint_fast8_t cpld_direction = (transceiver_mode == TRANSCEIVER_MODE_TX) ? 1 : 0; SGPIO_GPIO_OUTREG = - (cpld_direction << 11) | // direction - (1L << 10); // disable + (cpld_direction << 11) + | (1L << 10) // disable + ; // Enable SGPIO pin outputs. const uint_fast16_t sgpio_gpio_data_direction = @@ -105,24 +112,38 @@ void sgpio_configure( ? (0xFF << 0) : (0x00 << 0); SGPIO_GPIO_OENREG = - (1L << 11) | // direction - (1L << 10) | // disable - (0L << 9) | // capture - (0L << 8) | // clock - sgpio_gpio_data_direction; // data: output + (1L << 11) // direction + | (1L << 10) // disable + | (0L << 9) // capture + | (0L << 8) // clock + | sgpio_gpio_data_direction + ; - SGPIO_OUT_MUX_CFG( 8) = 0; // SGPIO: Input: clock - SGPIO_OUT_MUX_CFG( 9) = 0; // SGPIO: Input: qualifier - SGPIO_OUT_MUX_CFG(10) = (0L << 4) | (4L << 0); // GPIO: Output: disable - SGPIO_OUT_MUX_CFG(11) = (0L << 4) | (4L << 0); // GPIO: Output: direction + SGPIO_OUT_MUX_CFG( 8) = // SGPIO: Input: clock + SGPIO_OUT_MUX_CFG_P_OE_CFG(0) + | SGPIO_OUT_MUX_CFG_P_OUT_CFG(0) + ; + SGPIO_OUT_MUX_CFG( 9) = // SGPIO: Input: qualifier + SGPIO_OUT_MUX_CFG_P_OE_CFG(0) + | SGPIO_OUT_MUX_CFG_P_OUT_CFG(0) + ; + SGPIO_OUT_MUX_CFG(10) = // GPIO: Output: disable + SGPIO_OUT_MUX_CFG_P_OE_CFG(0) + | SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) + ; + SGPIO_OUT_MUX_CFG(11) = // GPIO: Output: direction + SGPIO_OUT_MUX_CFG_P_OE_CFG(0) + | SGPIO_OUT_MUX_CFG_P_OUT_CFG(4) + ; const uint_fast8_t output_multiplexing_mode = multi_slice ? 11 : 9; for(uint_fast8_t i=0; i<8; i++) { // SGPIO pin 0 outputs slice A bit "i". SGPIO_OUT_MUX_CFG(i) = - (0L << 4) | // P_OE_CFG = 0 - (output_multiplexing_mode << 0); + SGPIO_OUT_MUX_CFG_P_OE_CFG(0) + | SGPIO_OUT_MUX_CFG_P_OUT_CFG(output_multiplexing_mode) + ; } const uint_fast8_t slice_indices[] = { @@ -146,28 +167,34 @@ void sgpio_configure( const uint_fast8_t concat_order = (input_slice || single_slice) ? 0 : 3; const uint_fast8_t concat_enable = (input_slice || single_slice) ? 0 : 1; const uint_fast8_t pos = multi_slice ? 0x1f : 0x03; + SGPIO_MUX_CFG(slice_index) = - (concat_order << 12) | - (concat_enable << 11) | - (0L << 9) | // QUALIFIER_SLICE_MODE = X - (1L << 7) | // QUALIFIER_PIN_MODE = 1 (SGPIO9) - (3L << 5) | // QUALIFIER_MODE = 3 (external SGPIO pin) - (0L << 3) | // CLK_SOURCE_SLICE_MODE = X - (0L << 1) | // CLK_SOURCE_PIN_MODE = 0 (SGPIO8) - (1L << 0); // EXT_CLK_ENABLE = 1, external clock signal (slice) + SGPIO_MUX_CFG_CONCAT_ORDER(concat_order) + | SGPIO_MUX_CFG_CONCAT_ENABLE(concat_enable) + | SGPIO_MUX_CFG_QUALIFIER_SLICE_MODE(0) + | SGPIO_MUX_CFG_QUALIFIER_PIN_MODE(1) + | SGPIO_MUX_CFG_QUALIFIER_MODE(3) + | SGPIO_MUX_CFG_CLK_SOURCE_SLICE_MODE(0) + | SGPIO_MUX_CFG_CLK_SOURCE_PIN_MODE(0) + | SGPIO_MUX_CFG_EXT_CLK_ENABLE(1) + ; SGPIO_SLICE_MUX_CFG(slice_index) = - (0L << 8) | // INV_QUALIFIER = 0 (use normal qualifier) - (3L << 6) | // PARALLEL_MODE = 3 (shift 8 bits per clock) - (0L << 4) | // DATA_CAPTURE_MODE = 0 (detect rising edge) - (0L << 3) | // INV_OUT_CLK = X - (1L << 2) | // CLKGEN_MODE = 1 (use external pin clock) - (1L << 1) | // CLK_CAPTURE_MODE = 1 (use falling clock edge) - (0L << 0); // MATCH_MODE = 0 (do not match data) + SGPIO_SLICE_MUX_CFG_INV_QUALIFIER(0) + | SGPIO_SLICE_MUX_CFG_PARALLEL_MODE(3) + | SGPIO_SLICE_MUX_CFG_DATA_CAPTURE_MODE(0) + | SGPIO_SLICE_MUX_CFG_INV_OUT_CLK(0) + | SGPIO_SLICE_MUX_CFG_CLKGEN_MODE(1) + | SGPIO_SLICE_MUX_CFG_CLK_CAPTURE_MODE(1) + | SGPIO_SLICE_MUX_CFG_MATCH_MODE(0) + ; SGPIO_PRESET(slice_index) = 0; // External clock, don't care SGPIO_COUNT(slice_index) = 0; // External clock, don't care - SGPIO_POS(slice_index) = (pos << 8) | (pos << 0); + SGPIO_POS(slice_index) = + SGPIO_POS_POS_RESET(pos) + | SGPIO_POS_POS(pos) + ; SGPIO_REG(slice_index) = 0x80808080; // Primary output data register SGPIO_REG_SS(slice_index) = 0x80808080; // Shadow output data register @@ -190,4 +217,4 @@ void sgpio_cpld_stream_disable() { bool sgpio_cpld_stream_is_enabled() { return (SGPIO_GPIO_OUTREG & (1L << 10)) == 0; -} \ No newline at end of file +} From 9824a723b779981e3c5ddd320627e1fcb0741770 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 12 Oct 2012 09:45:18 -0700 Subject: [PATCH 10/22] Moved invariant in sgpio_configure() loop out of loop. Originally, so I could use the value to calculate another invariant. --- firmware/common/sgpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/sgpio.c b/firmware/common/sgpio.c index 451e9c52..d53c3f87 100644 --- a/firmware/common/sgpio.c +++ b/firmware/common/sgpio.c @@ -157,6 +157,7 @@ void sgpio_configure( SGPIO_SLICE_L, }; + const uint_fast8_t pos = multi_slice ? 0x1f : 0x03; const bool single_slice = !multi_slice; const uint_fast8_t slice_count = multi_slice ? 8 : 1; @@ -166,7 +167,6 @@ void sgpio_configure( const bool input_slice = (i == 0) && (transceiver_mode == TRANSCEIVER_MODE_RX); const uint_fast8_t concat_order = (input_slice || single_slice) ? 0 : 3; const uint_fast8_t concat_enable = (input_slice || single_slice) ? 0 : 1; - const uint_fast8_t pos = multi_slice ? 0x1f : 0x03; SGPIO_MUX_CFG(slice_index) = SGPIO_MUX_CFG_CONCAT_ORDER(concat_order) From 8d14de21ce0d502daaa42c7ecee37a153a64ce06 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 12 Oct 2012 09:46:49 -0700 Subject: [PATCH 11/22] Fixed prior commit, where I lost the switch between endpoints based on RX or TX mode. --- firmware/usb_performance/usb_performance.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/firmware/usb_performance/usb_performance.c b/firmware/usb_performance/usb_performance.c index 2766981d..376863b8 100644 --- a/firmware/usb_performance/usb_performance.c +++ b/firmware/usb_performance/usb_performance.c @@ -312,13 +312,21 @@ int main(void) { while( usb_bulk_buffer_offset < 16384 ); // Set up IN transfer of buffer 0. - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[0]); + usb_endpoint_schedule_no_int( + (transceiver_mode == TRANSCEIVER_MODE_RX) + ? &usb_endpoint_bulk_in : &usb_endpoint_bulk_out, + &usb_td_bulk[0] + ); // Wait until buffer 1 is transmitted/received. while( usb_bulk_buffer_offset >= 16384 ); // Set up IN transfer of buffer 1. - usb_endpoint_schedule_no_int(&usb_endpoint_bulk_in, &usb_td_bulk[1]); + usb_endpoint_schedule_no_int( + (transceiver_mode == TRANSCEIVER_MODE_RX) + ? &usb_endpoint_bulk_in : &usb_endpoint_bulk_out, + &usb_td_bulk[1] + ); } return 0; From f4b6f08500bd5984f42d975686baa1a90adcd851 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 11:06:39 -0700 Subject: [PATCH 12/22] Fixed incorrect field with for MAX2837 VGAgain_SPI_EN field. For boards that have no B7:B1 connections, this could prove extra problematic...and for boards that do, but aren't driving those pins from the LPC. --- firmware/common/max2837_regs.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/max2837_regs.def b/firmware/common/max2837_regs.def index 96291866..097be4f7 100644 --- a/firmware/common/max2837_regs.def +++ b/firmware/common/max2837_regs.def @@ -133,7 +133,7 @@ __MREG__(MAX2837_LPF_MODE_SEL,6,9,1) // set to enable mode in reg 2 ModeCtrl /* REG 8 */ __MREG__(MAX2837_LNAgain_SPI_EN,8,0,1) // set to override pin control of LNA -__MREG__(MAX2837_VGAgain_SPI_EN,8,1,0) // set to override pin control of VGA +__MREG__(MAX2837_VGAgain_SPI_EN,8,1,1) // set to override pin control of VGA __MREG__(MAX2837_EN_Bias_Trim,8,2,1) // route bias current to bondpad __MREG__(MAX2837_BIAS_TRIM_SPI,8,7,3) // down=00000, up=11111, nom=10000 __MREG__(MAX2837_BIAS_TRIM_CNTRL,8,8,1) // enable BIAS_TRIM_SPI value From 168c92a3d0080a957d17288654b16d7dc407ea45 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 11:07:17 -0700 Subject: [PATCH 13/22] Now that RxVGA gain control is working correctly, gain needs to be backed WAAAY off to get a usable capture in a fairly noisy 2.4GHz environment. --- firmware/common/max2837.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c index 633fbca1..8ad9dadf 100644 --- a/firmware/common/max2837.c +++ b/firmware/common/max2837.c @@ -110,7 +110,7 @@ void max2837_setup(void) set_MAX2837_LNAgain_SPI_EN(1); set_MAX2837_LNAgain(MAX2837_LNAgain_MAX); /* maximum gain */ set_MAX2837_VGAgain_SPI_EN(1); - set_MAX2837_VGA(0x00); /* minimum attenuation */ + set_MAX2837_VGA(0x18); /* reasonable gain for noisy 2.4GHz environment */ /* maximum rx output common-mode voltage */ set_MAX2837_BUFF_VCM(MAX2837_BUFF_VCM_1_25); From 57866227bfd1629444a8c3e471a8564c1a9f4e51 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 11:37:54 -0700 Subject: [PATCH 14/22] Remove weird LDSCRIPT that isn't necessary now. Change .bin generation to include only ROM-able sections. TODO: Is there a better way to do the objcopy? It'd be great to put everything in the "rom" region into the .bin, instead of having to list out -j section for each of the sections that go to ROM. --- firmware/common/LPC4330_M4_ram_only.ld | 38 -------------------------- firmware/common/Makefile_inc.mk | 2 +- firmware/usb_performance/Makefile | 1 - 3 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 firmware/common/LPC4330_M4_ram_only.ld diff --git a/firmware/common/LPC4330_M4_ram_only.ld b/firmware/common/LPC4330_M4_ram_only.ld deleted file mode 100644 index 4149fbd7..00000000 --- a/firmware/common/LPC4330_M4_ram_only.ld +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012 Michael Ossmann - * Copyright 2012 Jared Boone - * - * This file is part of HackRF - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -/* Linker script for HackRF Jellybean (LPC4330, 1M SPI flash, 264K SRAM). */ - -MEMORY -{ - ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 128K - ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 72K - ram_ahb1 (rwx) : ORIGIN = 0x20000000, LENGTH = 16K - /* Removed 32K of AHB SRAM for USB buffer. Straddles two blocks of RAM - * to get performance benefit of having two USB buffers addressable - * simultaneously (on two different buses of the AHB multilayer matrix) - */ - ram_ahb2 (rwx) : ORIGIN = 0x2000C000, LENGTH = 16K -} - -/* Include the common ld script. */ -INCLUDE libopencm3_lpc43xx_ram_only.ld diff --git a/firmware/common/Makefile_inc.mk b/firmware/common/Makefile_inc.mk index 8ea2f6be..58b9588d 100644 --- a/firmware/common/Makefile_inc.mk +++ b/firmware/common/Makefile_inc.mk @@ -80,7 +80,7 @@ flash: $(BINARY).flash %.bin: %.elf @#printf " OBJCOPY $(*).bin\n" - $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + $(Q)$(OBJCOPY) -Obinary -j .text -j .ARM.exidx $(*).elf $(*).bin %.hex: %.elf @#printf " OBJCOPY $(*).hex\n" diff --git a/firmware/usb_performance/Makefile b/firmware/usb_performance/Makefile index 7222a2f4..cdde8dd1 100644 --- a/firmware/usb_performance/Makefile +++ b/firmware/usb_performance/Makefile @@ -35,5 +35,4 @@ SRC = $(BINARY).c \ ../common/max5864.c \ ../common/rffc5071.c -LDSCRIPT = ../common/LPC4330_M4_ram_only.ld include ../common/Makefile_inc.mk From 4cf0ba236d5f41028a5b526fdb875b1ff1de72cd Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 12:29:03 -0700 Subject: [PATCH 15/22] Extended common Makefile to include targets for DFU file generation and programming. --- firmware/common/Makefile_inc.mk | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/firmware/common/Makefile_inc.mk b/firmware/common/Makefile_inc.mk index 58b9588d..891495c0 100644 --- a/firmware/common/Makefile_inc.mk +++ b/firmware/common/Makefile_inc.mk @@ -75,9 +75,20 @@ all: images images: $(BINARY).images flash: $(BINARY).flash +program: $(BINARY).dfu + $(Q)dfu-util --device 1fc9:000c --alt 0 --download $(BINARY).dfu + %.images: %.bin %.hex %.srec %.list @#echo "*** $* images generated ***" +%.dfu: %.bin + $(Q)rm -f _tmp.dfu _header.bin + $(Q)cp $(*).bin _tmp.dfu + $(Q)dfu-suffix --vid=0x1fc9 --pid=0x000c --did=0x0 -s 0 -a _tmp.dfu + $(Q)python -c "import os.path; import struct; print('0000000: da ff ' + ' '.join(map(lambda s: '%02x' % ord(s), struct.pack(' _header.bin + $(Q)cat _header.bin _tmp.dfu >$(*).dfu + $(Q)rm -f _tmp.dfu _header.bin + %.bin: %.elf @#printf " OBJCOPY $(*).bin\n" $(Q)$(OBJCOPY) -Obinary -j .text -j .ARM.exidx $(*).elf $(*).bin @@ -107,6 +118,8 @@ clean: $(Q)rm -f *.d $(Q)rm -f *.elf $(Q)rm -f *.bin + $(Q)rm -f *.dfu + $(Q)rm -f _tmp.dfu _header.bin $(Q)rm -f *.hex $(Q)rm -f *.srec $(Q)rm -f *.list From d398cfcc1df723ef2e2bad6e171ccda992805eb9 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 16:58:22 -0700 Subject: [PATCH 16/22] Use new ssp_transfer() function instead of ssp_write(). Implement max2837_spi_read(). --- firmware/common/max2837.c | 7 +++---- firmware/common/max5864.c | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/firmware/common/max2837.c b/firmware/common/max2837.c index 8ad9dadf..464aa8b3 100644 --- a/firmware/common/max2837.c +++ b/firmware/common/max2837.c @@ -121,10 +121,9 @@ void max2837_setup(void) /* SPI register read. */ uint16_t max2837_spi_read(uint8_t r) { gpio_clear(PORT_XCVR_CS, PIN_XCVR_CS); - // FIXME: Unimplemented. - r=r; + const uint16_t value = ssp_transfer(SSP1_NUM, (uint16_t)((1 << 15) | (r << 10))); gpio_set(PORT_XCVR_CS, PIN_XCVR_CS); - return 0; + return value & 0x3ff; } /* SPI register write */ @@ -137,7 +136,7 @@ void max2837_spi_write(uint8_t r, uint16_t v) { LOG("0x%03x -> reg%d\n", v, r); #else gpio_clear(PORT_XCVR_CS, PIN_XCVR_CS); - ssp_write(SSP1_NUM, (uint16_t)((r << 10) | (v & 0x3ff))); + ssp_transfer(SSP1_NUM, (uint16_t)((r << 10) | (v & 0x3ff))); gpio_set(PORT_XCVR_CS, PIN_XCVR_CS); #endif } diff --git a/firmware/common/max5864.c b/firmware/common/max5864.c index a0f5dcff..094ed700 100644 --- a/firmware/common/max5864.c +++ b/firmware/common/max5864.c @@ -29,7 +29,7 @@ void max5864_spi_write(uint_fast8_t value) { gpio_clear(PORT_AD_CS, PIN_AD_CS); - ssp_write(SSP1_NUM, value); + ssp_transfer(SSP1_NUM, value); gpio_set(PORT_AD_CS, PIN_AD_CS); } From 7e34950b24d7e6bb75c03c90883546acbe94ad60 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 17:01:42 -0700 Subject: [PATCH 17/22] Relocate compiler #defines for PACKED, ALIGNED, SECTION. --- firmware/usb_performance/usb.h | 3 --- firmware/usb_performance/usb_type.h | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/firmware/usb_performance/usb.h b/firmware/usb_performance/usb.h index 7b9c9390..a2ab9fb1 100644 --- a/firmware/usb_performance/usb.h +++ b/firmware/usb_performance/usb.h @@ -28,9 +28,6 @@ #include "usb_type.h" -#define ATTR_ALIGNED(x) __attribute__ ((aligned(x))) -#define ATTR_SECTION(x) __attribute__ ((section(x))) - extern bool usb_set_configuration( usb_device_t* const device, const uint_fast8_t configuration_number diff --git a/firmware/usb_performance/usb_type.h b/firmware/usb_performance/usb_type.h index 54287fd5..1bfb3205 100644 --- a/firmware/usb_performance/usb_type.h +++ b/firmware/usb_performance/usb_type.h @@ -25,6 +25,11 @@ #include #include +// TODO: Move this to some common compiler-tricks location. +#define ATTR_PACKED __attribute__((packed)) +#define ATTR_ALIGNED(x) __attribute__ ((aligned(x))) +#define ATTR_SECTION(x) __attribute__ ((section(x))) + typedef struct { uint8_t request_type; uint8_t request; From 048feb131652ad33b322fa9e7a70e06dee05cb1b Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 17:02:55 -0700 Subject: [PATCH 18/22] Miscellaneous USB notes and naming clean-up. --- firmware/usb_performance/usb.h | 2 ++ firmware/usb_performance/usb_request.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/firmware/usb_performance/usb.h b/firmware/usb_performance/usb.h index a2ab9fb1..ccaa9184 100644 --- a/firmware/usb_performance/usb.h +++ b/firmware/usb_performance/usb.h @@ -28,6 +28,8 @@ #include "usb_type.h" +// TODO: This is a lame move, requiring an extern to be defined to complete +// the interface between this API and the application. extern bool usb_set_configuration( usb_device_t* const device, const uint_fast8_t configuration_number diff --git a/firmware/usb_performance/usb_request.h b/firmware/usb_performance/usb_request.h index a637924f..2d32bcea 100644 --- a/firmware/usb_performance/usb_request.h +++ b/firmware/usb_performance/usb_request.h @@ -38,7 +38,7 @@ typedef enum { } usb_transfer_stage_t; typedef void (*usb_request_handler_fn)( - usb_endpoint_t* const response, + usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage ); From 3c17bad7432c732d5b822cc31c5a041391449fd2 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 17:03:28 -0700 Subject: [PATCH 19/22] Stall USB endpoint if control request is not handled. --- firmware/usb_performance/usb_request.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/usb_performance/usb_request.c b/firmware/usb_performance/usb_request.c index df6ae427..bfa4ea75 100644 --- a/firmware/usb_performance/usb_request.c +++ b/firmware/usb_performance/usb_request.c @@ -19,6 +19,7 @@ * Boston, MA 02110-1301, USA. */ +#include "usb.h" #include "usb_request.h" #include @@ -49,6 +50,9 @@ static void usb_request( if( handler ) { handler(endpoint, stage); + } else { + // USB 2.0 section 9.2.7 "Request Error" + usb_endpoint_stall(endpoint); } } From f32c6b34caef56e6bc84fbccfb5ce24832baafa7 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Sat, 13 Oct 2012 17:06:24 -0700 Subject: [PATCH 20/22] Create unions in usb_setup_t so that value, index, and length can be read as words, not just high/low bytes. --- .../usb_performance/usb_standard_request.c | 4 +-- firmware/usb_performance/usb_type.h | 29 ++++++++++++++----- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/firmware/usb_performance/usb_standard_request.c b/firmware/usb_performance/usb_standard_request.c index 1b42bd5e..286d783b 100644 --- a/firmware/usb_performance/usb_standard_request.c +++ b/firmware/usb_performance/usb_standard_request.c @@ -67,7 +67,7 @@ static void usb_send_descriptor( usb_endpoint_t* const endpoint, uint8_t* const descriptor_data ) { - const uint32_t setup_length = (endpoint->setup.length_h << 8) | endpoint->setup.length_l; + const uint32_t setup_length = endpoint->setup.length; uint32_t descriptor_length = descriptor_data[0]; if( descriptor_data[1] == USB_DESCRIPTOR_TYPE_CONFIGURATION ) { descriptor_length = (descriptor_data[3] << 8) | descriptor_data[2]; @@ -221,7 +221,7 @@ static void usb_standard_request_set_configuration( static void usb_standard_request_get_configuration_setup( usb_endpoint_t* const endpoint ) { - if( (endpoint->setup.length_h == 0) && (endpoint->setup.length_l == 1) ) { + if( endpoint->setup.length == 1 ) { endpoint->buffer[0] = 0; if( endpoint->device->configuration ) { endpoint->buffer[0] = endpoint->device->configuration->number; diff --git a/firmware/usb_performance/usb_type.h b/firmware/usb_performance/usb_type.h index 1bfb3205..fed3172b 100644 --- a/firmware/usb_performance/usb_type.h +++ b/firmware/usb_performance/usb_type.h @@ -30,15 +30,30 @@ #define ATTR_ALIGNED(x) __attribute__ ((aligned(x))) #define ATTR_SECTION(x) __attribute__ ((section(x))) -typedef struct { +typedef struct ATTR_PACKED { uint8_t request_type; uint8_t request; - uint8_t value_l; - uint8_t value_h; - uint8_t index_l; - uint8_t index_h; - uint8_t length_l; - uint8_t length_h; + union { + struct { + uint8_t value_l; + uint8_t value_h; + }; + uint16_t value; + }; + union { + struct { + uint8_t index_l; + uint8_t index_h; + }; + uint16_t index; + }; + union { + struct { + uint8_t length_l; + uint8_t length_h; + }; + uint16_t length; + }; } usb_setup_t; typedef enum { From f64e42dbee452b64da5510c30b4a0221a2f4ebff Mon Sep 17 00:00:00 2001 From: Michael Ossmann Date: Sat, 13 Oct 2012 21:13:56 -0600 Subject: [PATCH 21/22] updated readme photo from lemondrop/jellybean to jawbreaker --- Readme.md | 5 +++-- doc/jawbreaker.jpeg | Bin 0 -> 444816 bytes 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 doc/jawbreaker.jpeg diff --git a/Readme.md b/Readme.md index ea9e9afe..fa6e199a 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,8 @@ This repository contains hardware designs and software for HackRF, a project to produce a low cost, open source software radio platform. -![Lemondrop and Jellybean](https://github.com/wishi/hackrf/raw/master/doc/lemonAndjelly.jpeg) +![Jawbreaker](https://github.com/wishi/hackrf/raw/master/doc/jawbreaker.jpeg) principal author: Michael Ossmann -source repository: https://github.com/mossmann/hackrf + +http://greatscottgadgets.com/hackrf/ diff --git a/doc/jawbreaker.jpeg b/doc/jawbreaker.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..6d3a8e78fe6285524fc06ec4a7e5ea910833af28 GIT binary patch literal 444816 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