/* * 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 #include "eeprom_io.h" #include "usb_descriptors.h" /* * offsets into boot eeprom for configuration values */ #define HW_REV_OFFSET 5 #define SERIAL_NO_OFFSET 247 #define SERIAL_NO_LEN 9 #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[SERIAL_NO_LEN]; 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 (); }