diff options
Diffstat (limited to 'firmware/fx2/b100/usrp_main.c')
-rw-r--r-- | firmware/fx2/b100/usrp_main.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/firmware/fx2/b100/usrp_main.c b/firmware/fx2/b100/usrp_main.c new file mode 100644 index 000000000..7558fadb8 --- /dev/null +++ b/firmware/fx2/b100/usrp_main.c @@ -0,0 +1,394 @@ +/* + * USRP - Universal Software Radio Peripheral + * + * Copyright (C) 2003,2004 Free Software Foundation, Inc. + * + * 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 3 of the License, 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; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA + */ + +#include "usrp_common.h" +#include "usrp_commands.h" +#include "fpga.h" +#include "usrp_gpif_inline.h" +#include "timer.h" +#include "i2c.h" +#include "isr.h" +#include "usb_common.h" +#include "fx2utils.h" +#include "usrp_globals.h" +#include "usrp_i2c_addr.h" +#include <string.h> +#include "eeprom_io.h" +#include "usb_descriptors.h" + +/* + * offsets into boot eeprom for configuration values + */ +#define HW_REV_OFFSET 5 +#define SERIAL_NO_OFFSET 248 +#define SERIAL_NO_LEN 8 + + +#define bRequestType SETUPDAT[0] +#define bRequest SETUPDAT[1] +#define wValueL SETUPDAT[2] +#define wValueH SETUPDAT[3] +#define wIndexL SETUPDAT[4] +#define wIndexH SETUPDAT[5] +#define wLengthL SETUPDAT[6] +#define wLengthH SETUPDAT[7] + + +unsigned char g_tx_enable = 0; +unsigned char g_rx_enable = 0; +unsigned char g_rx_overrun = 0; +unsigned char g_tx_underrun = 0; +bit enable_gpif = 0; + +/* + * the host side fpga loader code pushes an MD5 hash of the bitstream + * into hash1. + */ +#define USRP_HASH_SIZE 16 +xdata at USRP_HASH_SLOT_1_ADDR unsigned char hash1[USRP_HASH_SIZE]; + +void clear_fpga_data_fifo(void); + +static void +get_ep0_data (void) +{ + EP0BCL = 0; // arm EP0 for OUT xfer. This sets the busy bit + + while (EP0CS & bmEPBUSY) // wait for busy to clear + ; +} + +static void initialize_gpif_buffer(int ep) { + //clear the GPIF buffers on startup to keep crap out of the data path + FIFORESET = 0x80; SYNCDELAY; //activate NAKALL + FIFORESET = ep; SYNCDELAY; + FIFORESET = 0x00; SYNCDELAY; +} + +/* + * Handle our "Vendor Extension" commands on endpoint 0. + * If we handle this one, return non-zero. + */ +unsigned char +app_vendor_cmd (void) +{ + if (bRequestType == VRT_VENDOR_IN){ + + ///////////////////////////////// + // handle the IN requests + ///////////////////////////////// + + switch (bRequest){ + + case VRQ_GET_STATUS: //this is no longer done via FX2 -- the FPGA will be queried instead + return 0; + break; + + case VRQ_I2C_READ: + if (!i2c_read (wValueL, EP0BUF, wLengthL)) return 0; + EP0BCH = 0; + EP0BCL = wLengthL; + break; + + case VRQ_SPI_READ: + return 0; + + case VRQ_FW_COMPAT: + EP0BCH = 0; + EP0BCL = 2; + break; + + default: + return 0; + } + } + + else if (bRequestType == VRT_VENDOR_OUT){ + + ///////////////////////////////// + // handle the OUT requests + ///////////////////////////////// + + switch (bRequest){ + + case VRQ_SET_LED: + switch (wIndexL){ + case 0: + set_led_0 (wValueL); + break; + + case 1: + set_led_1 (wValueL); + break; + + default: + return 0; + } + break; + + case VRQ_FPGA_LOAD: + switch (wIndexL){ // sub-command + case FL_BEGIN: + return fpga_load_begin (); + + case FL_XFER: + get_ep0_data (); + return fpga_load_xfer (EP0BUF, EP0BCL); + + case FL_END: + return fpga_load_end (); + + default: + return 0; + } + break; + + case VRQ_FPGA_SET_RESET: + //fpga_set_reset (wValueL); + break; + + case VRQ_I2C_WRITE: + get_ep0_data (); + if (!i2c_write (wValueL, EP0BUF, EP0BCL)) return 0; + //USRP_LED_REG ^= bmLED1; + break; + + case VRQ_RESET_GPIF: + initialize_gpif_buffer(wValueL); + break; + + case VRQ_ENABLE_GPIF: + enable_gpif = (wValueL != 0) ? 1 : 0; + set_led_1(enable_gpif); + break; + + case VRQ_CLEAR_FPGA_FIFO: + clear_fpga_data_fifo(); + break; + + default: + return 0; + } + + } + else + return 0; // invalid bRequestType + + return 1; +} + +static int short_pkt_state = 0; +#define SHORT_PACKET_DETECTED (short_pkt_state != bitSHORT_PACKET_SIGNAL) + +//yes, this is a little opaque +//basically this is necessary because while all the logic to inform the FPGA +//of what we're trying to do via the CTL pins is contained within the flowstates, +//we need to assert the endpoint select pin one clock cycle before the flowstate starts. +//this is the job of the wave descriptor. rather than switch between waves, since that +//involves a little more setup, we just modify the wave table on the fly. +inline static void setup_wave_data_read(void) { + GPIF_WAVE_DATA[80] = 0x06; + GPIF_WAVE_DATA[81] = 0x06; +} + +inline static void setup_wave_ctrl_read(void) { + GPIF_WAVE_DATA[80] = 0x0E; + GPIF_WAVE_DATA[81] = 0x0E; +} + +inline static void setup_wave_data_write(void) { + GPIF_WAVE_DATA[112] = 0x00; + GPIF_WAVE_DATA[113] = 0x00; +} + +inline static void setup_wave_ctrl_write(void) { + GPIF_WAVE_DATA[112] = 0x08; + GPIF_WAVE_DATA[113] = 0x08; +} + +inline static void handle_data_write(void) { + GPIFTCB1 = 0x01; //SYNCDELAY; + GPIFTCB0 = 0x00; + setup_flowstate_data_write (); + setup_wave_data_write(); + GPIFTRIG = bmGPIF_EP2_START | bmGPIF_WRITE; // start the xfer + SYNCDELAY; + while (!(GPIFTRIG & bmGPIF_IDLE)); +} + +inline static void handle_ctrl_write(void) { + GPIFTCB1 = 0x00; + GPIFTCB0 = 0x10; + setup_flowstate_ctrl_write (); + setup_wave_ctrl_write(); + GPIFTRIG = bmGPIF_EP4_START | bmGPIF_WRITE; // start the xfer + SYNCDELAY; + while (!(GPIFTRIG & bmGPIF_IDLE)); +} + +inline static void handle_data_read(void) { + GPIFTCB1 = 0x01; + GPIFTCB0 = 0x00; + setup_flowstate_data_read (); + setup_wave_data_read(); + short_pkt_state = bitSHORT_PACKET_SIGNAL; + GPIFTRIG = bmGPIF_EP6_START | bmGPIF_READ; // start the xfer + SYNCDELAY; + while (!(GPIFTRIG & bmGPIF_IDLE)); + INPKTEND = 0x06; // tell USB we filled buffer (6 is our endpoint num) + SYNCDELAY; + if(SHORT_PACKET_DETECTED) { + while(!(EP6CS & bmEPEMPTY)); //wait for packet to send + INPKTEND = 0x06; //send a ZLP + //toggle_led_1(); //FIXME DEBUG + } +} + +inline static void handle_ctrl_read(void) { + GPIFTCB1 = 0x00; + GPIFTCB0 = 0x10; + setup_flowstate_ctrl_read (); + setup_wave_ctrl_read(); + GPIFTRIG = bmGPIF_EP8_START | bmGPIF_READ; // start the xfer + SYNCDELAY; + while (!(GPIFTRIG & bmGPIF_IDLE)); + INPKTEND = 8; // tell USB we filled buffer (8 is our endpoint num) +} + +//clear the FPGA datapath by reading but not submitting, instead clearing the FIFO after each transaction +void clear_fpga_data_fifo(void) { + while(fpga_has_data_packet_avail()) { + GPIFTCB1 = 0x01; + GPIFTCB0 = 0x00; + setup_flowstate_data_read (); + setup_wave_data_read(); + GPIFTRIG = bmGPIF_EP6_START | bmGPIF_READ; // start the xfer + SYNCDELAY; + while (!(GPIFTRIG & bmGPIF_IDLE)); + initialize_gpif_buffer(6); //reset the FIFO instead of committing it + } +} + +static void +main_loop (void) +{ + while (1){ + if (usb_setup_packet_avail ()) + usb_handle_setup_packet (); + + if(enable_gpif){ + if (fx2_has_ctrl_packet_avail() && fpga_has_room_for_ctrl_packet()) handle_ctrl_write(); + if (fx2_has_room_for_ctrl_packet() && fpga_has_ctrl_packet_avail()) handle_ctrl_read(); + + //we do this + if (fx2_has_data_packet_avail() && fpga_has_room_for_data_packet()) handle_data_write(); + if (fx2_has_room_for_data_packet() && fpga_has_data_packet_avail()) handle_data_read(); + //five times so that + if (fx2_has_data_packet_avail() && fpga_has_room_for_data_packet()) handle_data_write(); + if (fx2_has_room_for_data_packet() && fpga_has_data_packet_avail()) handle_data_read(); + //we can piggyback + if (fx2_has_data_packet_avail() && fpga_has_room_for_data_packet()) handle_data_write(); + if (fx2_has_room_for_data_packet() && fpga_has_data_packet_avail()) handle_data_read(); + //data transfers + if (fx2_has_data_packet_avail() && fpga_has_room_for_data_packet()) handle_data_write(); + if (fx2_has_room_for_data_packet() && fpga_has_data_packet_avail()) handle_data_read(); + //without loop overhead + if (fx2_has_data_packet_avail() && fpga_has_room_for_data_packet()) handle_data_write(); + if (fx2_has_room_for_data_packet() && fpga_has_data_packet_avail()) handle_data_read(); + } + } +} + +/* + * called at 100 Hz from timer2 interrupt + * + * Toggle led 0 + */ +void +isr_tick (void) interrupt +{ + static unsigned char count = 1; + + if (--count == 0){ + count = 50; + USRP_LED_REG ^= bmLED0; + } + + clear_timer_irq (); +} + +/* + * Read h/w rev code and serial number out of boot eeprom and + * patch the usb descriptors with the values. + */ +void +patch_usb_descriptors(void) +{ + static xdata unsigned char hw_rev; + static xdata unsigned char serial_no[8]; + unsigned char i; + + eeprom_read(I2C_ADDR_BOOT, HW_REV_OFFSET, &hw_rev, 1); // LSB of device id + usb_desc_hw_rev_binary_patch_location_0[0] = hw_rev; + usb_desc_hw_rev_binary_patch_location_1[0] = hw_rev; + usb_desc_hw_rev_ascii_patch_location_0[0] = hw_rev + '0'; // FIXME if we get > 9 + + eeprom_read(I2C_ADDR_BOOT, SERIAL_NO_OFFSET, serial_no, SERIAL_NO_LEN); + + for (i = 0; i < SERIAL_NO_LEN; i++){ + unsigned char ch = serial_no[i]; + if (ch == 0xff) // make unprogrammed EEPROM default to '0' + ch = '0'; + usb_desc_serial_number_ascii[i << 1] = ch; + } +} + +void +main (void) +{ + enable_gpif = 0; + + memset (hash1, 0, USRP_HASH_SIZE); // zero fpga bitstream hash. This forces reload + + init_usrp (); + init_gpif (); + + // if (UC_START_WITH_GSTATE_OUTPUT_ENABLED) + //IFCONFIG |= bmGSTATE; // no conflict, start with it on + + set_led_0 (0); + set_led_1 (0); + + EA = 0; // disable all interrupts + + patch_usb_descriptors(); + + setup_autovectors (); + usb_install_handlers (); + //hook_timer_tick ((unsigned short) isr_tick); + + EIEX4 = 1; // disable INT4 FIXME + EA = 1; // global interrupt enable + + fx2_renumerate (); // simulates disconnect / reconnect + + setup_flowstate_common(); + main_loop (); +} |