diff options
83 files changed, 7471 insertions, 453 deletions
| diff --git a/firmware/microblaze/Makefile.am b/firmware/microblaze/Makefile.am index 6316b31a2..52fa649c2 100644 --- a/firmware/microblaze/Makefile.am +++ b/firmware/microblaze/Makefile.am @@ -23,4 +23,7 @@ EXTRA_DIST = \  	u2_flash_tool  SUBDIRS = \ -	usrp2 +	usrp2 \ +	usrp2p \ +	usrp2p/bootloader + diff --git a/firmware/microblaze/Makefile.common b/firmware/microblaze/Makefile.common index ceb6a553a..4e726edab 100644 --- a/firmware/microblaze/Makefile.common +++ b/firmware/microblaze/Makefile.common @@ -59,8 +59,13 @@ COMMON_LFLAGS = \  ########################################################################  # Common stuff for building top level microblaze images  ######################################################################## +#we use COMMON_IHX_ARGS to relocate the reset and interrupt vectors to +#just below the start of code. upon creating the BIN, any leading padding +#is thrown out, so the .bin file is valid for uploading to USRP2P. this  +#does not affect USRP2 because the USRP2 already starts at 0x0000, and +#because the relocate_args are not defined for USRP2's Makefile.am.  .elf.bin: -	$(MB_OBJCOPY) -O binary $< $@ +	$(MB_OBJCOPY) -O binary $(RELOCATE_ARGS) $< $@  .elf.dump:  	$(MB_OBJDUMP) -DSC $< > $@ @@ -69,7 +74,7 @@ COMMON_LFLAGS = \  	$(HEXDUMP) -v -e'1/1 "%.2X\n"' $< > $@  .elf.ihx: -	$(MB_OBJCOPY) -O ihex $(COMMON_IHX_ARGS) $< $@ +	$(MB_OBJCOPY) -O ihex $(RELOCATE_ARGS) $< $@  _generated_from_elf = \  	$(noinst_PROGRAMS:.elf=.map) \ diff --git a/firmware/microblaze/apps/blinkenlights.c b/firmware/microblaze/apps/blinkenlights.c new file mode 100644 index 000000000..4cebe5c9d --- /dev/null +++ b/firmware/microblaze/apps/blinkenlights.c @@ -0,0 +1,26 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include "memory_map.h" +#include <nonstdio.h> + +int main(int argc, char *argv[]) { + +	uint32_t c = 0; +	uint8_t i = 0; + +	while(1) { +		//delay(5000000); +		for(c=0;c<5000000;c++) asm("NOP"); +		output_regs->leds = (i++ % 2) ? 0xFF : 0x00; //blink everything on that register +	} + +	return 0; +} + +//void delay(uint32_t t) { +//	while(t-- != 0) asm("NOP"); +//} diff --git a/firmware/microblaze/apps/flash_test.c b/firmware/microblaze/apps/flash_test.c new file mode 100644 index 000000000..5b4569030 --- /dev/null +++ b/firmware/microblaze/apps/flash_test.c @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <hal_io.h> +#include <hal_uart.h> +#include <xilinx_s3_icap.h> +#include <nonstdio.h> +#include <spi_flash.h> +#include <spi.h> +#include <clocks.h> +#include <string.h> + +//just a test to write to SPI flash and retrieve the same values. +//uses the MOBFLEET SPI flash library + +void delay(uint32_t t) { +	while(t-- != 0) asm("NOP"); +} + +int main(int argc, char *argv[]) { +	uint16_t i, t; +	uint8_t buf[260]; +	const uint8_t testdata[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}; + +  hal_disable_ints();	// In case we got here via jmp 0x0 +//	spi_init(); +  hal_uart_init(); +//	clocks_init(); //set up AD9510, enable FPGA clock @ 1x divisor + +	puts("SPI Flash test\n"); +	puts("Initializing SPI\n"); + +	spif_init(); +	delay(800000); +	puts("Erasing sector 1\n"); +	spi_flash_erase(0x00010000, 256); +	delay(800000); +	puts("Reading back data\n"); +	spi_flash_read(0x00010000, 256, buf); +	delay(800000); + +	t=1; +	for(i=4; i<250; i++) { +		if(buf[i] != 0xFF) t=0; +	} + +	if(!t) puts("Data was not initialized to 0xFF. Unsuccessful erase or read\n"); +	else puts("Data initialized to 0xFF, erase confirmed\n"); + +	puts("Writing test buffer\n"); +	spi_flash_program(0x00010000, 16, testdata); +	//memset(buf, 0, 256); + +	delay(800000); +	puts("Wrote data, reading back\n"); + +	spi_flash_read(0x00010000, 16, buf); + +	if(memcmp(testdata, buf, 16)) puts("Data is not the same between read and write. Unsuccessful write or read\n"); +	else puts("Successful write! Flash write correct\n"); + +	return 0; +} diff --git a/firmware/microblaze/apps/hardware_testbed.c b/firmware/microblaze/apps/hardware_testbed.c new file mode 100644 index 000000000..e68e68ff7 --- /dev/null +++ b/firmware/microblaze/apps/hardware_testbed.c @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <nonstdio.h> +#include <hal_io.h> +#include <xilinx_s3_icap.h> +#include <spi_flash.h> +//#include <spi_flash_private.h> +#include <clocks.h> +#include <ihex.h> +#include <bootloader_utils.h> +#include <string.h> +#include <hal_uart.h> +#include <spi.h> + +//so this is just an evolving file used to set up and test different bits of hardware (EEPROM, clock chip, A/D, D/A, PHY) +void delay(uint32_t t) { +	while(t-- != 0) asm("NOP"); +} + +int main(int argc, char *argv[]) { + +	hal_disable_ints(); +  hal_uart_init(); +	spi_init(); + +	puts("Hardware testbed. Init clocks..."); + +	clocks_init(); + +	//now, hopefully, we should be running at 100MHz instead of 50MHz, meaning our UART is twice as fast and we're talking at 230400. + +	while(1) { +		delay(500000); +		puts("Eat at Joe's."); +	} + + + + + +	return 0; +} diff --git a/firmware/microblaze/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c index b7837f207..d00f2bc1f 100644 --- a/firmware/microblaze/apps/txrx_uhd.c +++ b/firmware/microblaze/apps/txrx_uhd.c @@ -42,9 +42,11 @@  #include <string.h>  #include "clocks.h"  #include "usrp2/fw_common.h" +#include <i2c_async.h>  #include <i2c.h>  #include <ethertype.h>  #include <arp_cache.h> +#include "udp_fw_update.h"  /*   * Full duplex Tx and Rx between ethernet and DSP pipelines @@ -346,6 +348,28 @@ void handle_udp_ctrl_packet(          send_udp_pkt(USRP2_UDP_CTRL_PORT, src, &ctrl_data_out, sizeof(ctrl_data_out));          break; +    case USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO:{ +      //executes a readline()-style read, up to num_bytes long, up to and including newline +      int num_bytes = ctrl_data_in->data.uart_args.bytes; +      if(num_bytes > 20) num_bytes = 20; +      num_bytes = fngets_timeout(ctrl_data_in->data.uart_args.dev, (char *) ctrl_data_out.data.uart_args.data, num_bytes); +      ctrl_data_out.id = USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE; +      ctrl_data_out.data.uart_args.bytes = num_bytes; +      break; +    } + +    case USRP2_CTRL_ID_HEY_WRITE_THIS_UART_FOR_ME_BRO:{ +      int num_bytes = ctrl_data_in->data.uart_args.bytes; +      if(num_bytes > 20) num_bytes = 20; +      //before we write to the UART, we flush the receive buffer +      //this assumes that we're interested in the reply +      hal_uart_rx_flush(ctrl_data_in->data.uart_args.dev); +      fnputstr(ctrl_data_in->data.uart_args.dev, (char *) ctrl_data_in->data.uart_args.data, num_bytes); +      ctrl_data_out.id = USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE; +      ctrl_data_out.data.uart_args.bytes = num_bytes; +      break; +    } +      default:          ctrl_data_out.id = USRP2_CTRL_ID_HUH_WHAT;          send_udp_pkt(USRP2_UDP_CTRL_PORT, src, &ctrl_data_out, sizeof(ctrl_data_out)); @@ -487,6 +511,7 @@ main(void)    //2) register callbacks for udp ports we service    register_udp_listener(USRP2_UDP_CTRL_PORT, handle_udp_ctrl_packet);    register_udp_listener(USRP2_UDP_DATA_PORT, handle_udp_data_packet); +  register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet);    //3) setup ethernet hardware to bring the link up    ethernet_register_link_changed_callback(link_changed_callback); diff --git a/firmware/microblaze/apps/uart_flash_loader.c b/firmware/microblaze/apps/uart_flash_loader.c new file mode 100644 index 000000000..d67b84677 --- /dev/null +++ b/firmware/microblaze/apps/uart_flash_loader.c @@ -0,0 +1,169 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +//#include <stdio.h> +#include <stdlib.h> +#include <memory_map.h> +#include <nonstdio.h> +#include <hal_io.h> +#include <hal_uart.h> +#include <xilinx_s3_icap.h> +#include <spi_flash.h> +#include <spi_flash_private.h> +//#include <clocks.h> +#include <ihex.h> +#include <bootloader_utils.h> + +//uses UART to load files to Flash in Intel HEX 16-bit format. +//this is one example of a "safe" firmware image to be loaded by a bootloader into main RAM. +//this CANNOT write to main RAM, since it is resident there. + +//Sector 0-31: Safe FPGA bootloader image +//Sector 32-63: Production bootloader image +//Sector 64: Production firmware image +//Sector 127: Safe firmware image + + +void uart_flash_loader(void) { + +	char buf[256]; //input data buffer +	uint8_t ihx[32]; //ihex data buffer +	uint32_t slot_offset = PROD_FW_IMAGE_LOCATION_ADDR; //initial slot offset to program to. +	uint32_t extended_addr = 0x00000000; //extended Intel hex segment address + +	size_t sector_size = spi_flash_log2_sector_size(); +	ihex_record_t ihex_record; +	ihex_record.data = ihx; +	int i; + + +	//not gonna win a turing prize for my C text parsing +	while(1) { +		gets(buf); +		if(!strncmp(buf, "!SECTORSIZE", 7)) { //return the sector size in log format +			putstr("OK "); +			puthex8((uint32_t) sector_size); //err, this should probably be decimal for human readability. we do have itoa now... +			putstr("\n"); +		} +		else if(!strncmp(buf, "!SETADDR", 7)) { //set start address for programming +			slot_offset = atol(&buf[8]); +			puts("OK"); +//			puthex32(slot_offset); +//			putstr("\n"); +		} +		else if(!strncmp(buf, "!ERASE", 6)) { //erase a sector +			uint32_t sector = atol(&buf[6]); +			uint32_t size = 2 << (sector_size-1); +			uint32_t addr = sector << sector_size; + +			spi_flash_erase(addr, size); //we DO NOT implement write protection here. it is up to the HOST PROGRAM to not issue an ERASE unless it means it. +																	 //unfortunately the Flash cannot write-protect the segments that really matter, so we only use global write-protect +																	 //as a means of avoiding accidental writes from runaway code / garbage on the SPI bus. +			puts("OK"); +		} +//can't exactly run firmware if you're already executing out of main RAM +/*		else if(!strncmp(buf, "!RUNSFW", 7)) { +			if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { +				puts("OK"); +				spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); +				start_program(RAM_BASE); +			} else { +				puts("NOK"); +			} +		} +		else if(!strncmp(buf, "!RUNPFW", 7)) { +			if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) { +				puts("OK"); +				spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES-1, (void *)RAM_BASE); +				start_program(RAM_BASE); +			} else { +				puts("NOK"); +			} +		} +*/ +		else if(!strncmp(buf, "!RUNPFPGA", 8)) { +			if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) { +				puts("OK"); +				//icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR); +			} else { +				puts("NOK"); +			}				 +		} +		else if(!strncmp(buf, "!RUNSFPGA", 8)) { +			if(is_valid_fpga_image(SAFE_FPGA_IMAGE_LOCATION_ADDR)) { +				puts("OK"); +				//icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); +			} else { +				puts("NOK"); +			} +		} +		else if(!strncmp(buf, "!READ", 5)) { +			uint32_t addr = atol(&buf[5]); +			spi_flash_read(addr, 16, ihx); +			for(i=0; i < 16; i++) { +				puthex8(ihx[i]); +			} +			putstr("\n"); +		} + +		else if(!ihex_parse(buf, &ihex_record)) { //last, try to see if the input was a valid IHEX line +			switch (ihex_record.type) { +				case 0: +					spi_flash_program(ihex_record.addr + slot_offset + extended_addr, ihex_record.length, ihex_record.data); +					puts("OK"); +					break; +				case 1: +					//here we would expect a CRC checking or something else to take place. for now we do nothing. +					//well, we set the extended segment addr back to 0 +					extended_addr = 0; +					puts("DONE"); +					break; +				case 4: +					//set the upper 16 bits of the address +					extended_addr = ((ihex_record.data[0] << 8) + ihex_record.data[1]) << 16; +					puts("OK"); +					break; +				default: +					puts("NOK"); +			} +		} +		else puts("NOK"); +	} //while(1) +} + +void delay(uint32_t t) { +	while(t-- != 0) asm("NOP"); +} + +int main(int argc, char *argv[]) { +	uint8_t buf[32]; +	int i = 0; + +  hal_disable_ints();	// In case we got here via jmp 0x0 + +//	delay(10000000); + +	//before anything else you might want to blinkenlights just to indicate code startup to the user. + +  hal_uart_init(); +	spif_init(); +//	i2c_init(); //for EEPROM +	puts("USRP2+ UART firmware loader"); + +//	puts("Debug: loading production image, 10 bytes."); + +//	spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, 10, buf); +//	for(i=0; i < 10; i++) { +//		puthex8(buf[i]); +//	} + +	uart_flash_loader(); + + 	//shouldn't get here. should reboot. +	puts("shit happened.\n"); + +	return 0; +} diff --git a/firmware/microblaze/bin/bin_to_mif.py b/firmware/microblaze/bin/bin_to_mif.py new file mode 100755 index 000000000..cefce4e92 --- /dev/null +++ b/firmware/microblaze/bin/bin_to_mif.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +import struct +import sys + +hextab = ('0000', '0001', '0010', '0011', +          '0100', '0101', '0110', '0111', +          '1000', '1001', '1010', '1011', +          '1100', '1101', '1110', '1111') + +def w_to_binary_ascii(w): +    return ''.join([hextab[(w >> 4*i) & 0xf] for i in range(7,-1,-1)]) + +def bin_to_mif(bin_input_file, mif_output_file): +    ifile = open(bin_input_file, 'rb') +    ofile = open(mif_output_file, 'w') +    idata = ifile.read() +    fmt = ">%dI" % ((len(idata) / 4),) +    words = struct.unpack(fmt, idata) +    for w in words: +        ofile.write(w_to_binary_ascii(w)) +        ofile.write('\n') + +if __name__ == '__main__': +    if len(sys.argv) != 3: +        sys.stderr.write("usage: bin_to_mif bin_input_file mif_output_file\n") +        raise SystemExit, 1 +     +    bin_to_mif(sys.argv[1], sys.argv[2]) diff --git a/firmware/microblaze/bin/bin_to_ram_macro_init.py b/firmware/microblaze/bin/bin_to_ram_macro_init.py new file mode 100755 index 000000000..65cf2dbdf --- /dev/null +++ b/firmware/microblaze/bin/bin_to_ram_macro_init.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import struct +import sys + +def do_8_words(ofile, which_ram, row, words): +    ofile.write("defparam bootram.RAM%d.INIT_%02X=256'h" % (which_ram, row)) +    ofile.write("%08x_%08x_%08x_%08x_%08x_%08x_%08x_%08x;\n" % ( +        words[7], words[6], words[5], words[4], words[3], words[2], words[1], words[0])) + +def bin_to_ram_macro_init(bin_input_file, ram_init_output_file): +    ifile = open(bin_input_file, 'rb') +    ofile = open(ram_init_output_file, 'w') +    idata = ifile.read() +    fmt = ">%dI" % ((len(idata) / 4),) +    words = struct.unpack(fmt, idata) + +    # pad to a multiple of 8 words +    r = len(words) % 8 +    if r != 0: +        words += (8 - r) * (0,) + +    if len(words) > 2048: +        sys.stderr.write("bin_to_macro_init: error: input file %s is > 8KiB\n" % (bin_input_file,)) +        sys.exit(1) + +    # first 2KB +    for i in range(0, min(512, len(words)), 8): +        do_8_words(ofile, 0, i/8, words[i:i+8]) + +    # second 2KB +    for i in range(512, min(1024, len(words)), 8): +        do_8_words(ofile, 1, (i/8) % 64, words[i:i+8]) + +		# third 2KB +    for i in range(1024, min(1536, len(words)), 8): +        do_8_words(ofile, 2, (i/8) % 64, words[i:i+8]) + +		# last 2KB +    for i in range(1536, len(words), 8): +        do_8_words(ofile, 3, (i/8) % 64, words[i:i+8]) + +if __name__ == '__main__': +    if len(sys.argv) != 3: +        sys.stderr.write("usage: bin_to_ram_macro_init bin_input_file ram_init_output_file\n") +        sys.exit(1) +     +    bin_to_ram_macro_init(sys.argv[1], sys.argv[2]) diff --git a/firmware/microblaze/bin/elf_to_sbf b/firmware/microblaze/bin/elf_to_sbf new file mode 100755 index 000000000..d1be10c0d --- /dev/null +++ b/firmware/microblaze/bin/elf_to_sbf @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# +# Copyright 2009 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, see <http://www.gnu.org/licenses/>. +# + +import sys +import struct +from optparse import OptionParser +from pprint import pprint + +import sbf + +# see /usr/include/elf.h for the various magic values + + +_ehdr_fmt = ">16sHH5I6H" +_ehdr_fmt_size = struct.calcsize(_ehdr_fmt) +_phdr_fmt = ">8I" +_phdr_fmt_size = struct.calcsize(_phdr_fmt) + +class elf32_ehdr(object): +    def __init__(self, s): +        (self.ident, self.type, self.machine, self.version, self.entry, +         self.phoff, self.shoff, self.flags, self.ehsize, +         self.phentsize, self.phnum, self.shentsize, self.shnum, +         self.shstrndx) = struct.unpack(_ehdr_fmt, s) + +class elf32_phdr(object): +    def __init__(self, s): +        (self.type, self.offset, self.vaddr, self.paddr, +         self.filesz, self.memsz, +         self.flags, self.align) = struct.unpack(_phdr_fmt, s) + +    def __repr__(self): +        return "<elf32_phdr %s offset=%d paddr=0x%x, filesz=%d>" % ( +            p_type_str(self.type), self.offset, self.paddr, self.filesz) + + +def p_type_str(t): +    if t <= 8: +        return ('NULL', 'LOAD', 'DYNAMIC', 'INTERP', 'NOTE', 'SHLIB', 'PHDR', 'TLS', 'NUM')[t] +    return "0x%x" % (t,) + + + +def _get_ehdr(f): +    if len(f) < _ehdr_fmt_size: +        return False +    ehdr = elf32_ehdr(f[0:_ehdr_fmt_size]) +    return ehdr + +     +def elf32_big_endian_exec_p(f): +    ehdr = _get_ehdr(f) +    if not ehdr: +        return False +     +    #pprint(ehdr, sys.stderr) +    e_ident = ehdr.ident +    if not e_ident.startswith('\177ELF'): +        return False +    if (ord(e_ident[4]) != 1            # EI_CLASS == CLASS32 +        or ord(e_ident[5]) != 2         # EI_DATA == DATA2MSB +        or ord(e_ident[6]) != 1         # EI_VERSION == EV_CURRENT +        ): +        return False + +    if ehdr.type != 2:                  # e_type == ET_EXEC +        return False + +    return True + + + +# return (entry, (phdr, ...)) + +def get_elf32_prog_headers(f): +    ehdr = _get_ehdr(f) +    entry = ehdr.entry +    phoff = ehdr.phoff +    phentsize = ehdr.phentsize +    phnum = ehdr.phnum + +    def extract(i): +        start = phoff + i * phentsize +        end = start + phentsize +        return f[start:end] +     +    return (entry, [elf32_phdr(extract(i)) for i in range(phnum)]) +     + +def main(): +    usage = "%prog: [options] elf_file" +    parser = OptionParser() +    parser.add_option("-o", "--output", default=None, +                      help="specify output filename [default=stdout]") +    (options, args) = parser.parse_args() +    if len(args) != 1: +        parser.print_help() +        sys.exit(1) + +    elf_file = open(args[0], 'rb') + +    elf_contents = elf_file.read() +    if not elf32_big_endian_exec_p(elf_contents): +        sys.stderr.write("%s: not a big-endian 32-bit ELF executable\n" % (args[0],)) +        sys.exit(1) +         +    if options.output is None: +        sbf_file = sys.stdout +    else: +        sbf_file = open(options.output, 'wb') + +    (entry, phdrs) = get_elf32_prog_headers(elf_contents) +    #pprint(phdrs, sys.stderr) + +    def phdr_to_sec_desc(phdr): +        target_addr = phdr.paddr +        data = elf_contents[phdr.offset:phdr.offset+phdr.filesz] +        #print >>sys.stderr, "pdhr_to_sec_desc:", (target_addr, data) +        return sbf.sec_desc(target_addr, data) + +    sections = map(phdr_to_sec_desc, phdrs) +    # pprint(sections, sys.stderr) +    sbf.write_sbf(sbf_file, sbf.header(entry, sections)) + + +if __name__ == '__main__': +    main() diff --git a/firmware/microblaze/bin/sbf.py b/firmware/microblaze/bin/sbf.py new file mode 100644 index 000000000..8e2c868a5 --- /dev/null +++ b/firmware/microblaze/bin/sbf.py @@ -0,0 +1,134 @@ +# +# Copyright 2009 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, see <http://www.gnu.org/licenses/>. +# + +_SBF_MAGIC = 'SBF!' +_SBF_DONT_EXECUTE = 0x1 +_SBF_MAX_SECTIONS = 14 +_SBF_HEADER_LEN = 128 + +import struct +import sys +from pprint import pprint + +def dump_data(f, offset, data): +    L = len(data) // 4 +    for i in range(L): +        f.write('%08x:  %08x\n' % (offset + 4 * i, struct.unpack('>I', data[4*i:4*(i+1)])[0])) +    remainder = len(data) - L * 4 +    if remainder != 0: +        f.write('%08x:  ' % (offset + L*4,)) +        i = 0 +        while i < remainder: +            f.write('%02x' % ((ord(data[L*4 + i]),))) +            i += 1 +        f.write('\n') + + + +class sec_desc(object): +    def __init__(self, target_addr, data): +        self.target_addr = target_addr +        self.data = data + +    def __repr__(self): +        #print >>sys.stderr, "target_addr:", self.target_addr +        #print >>sys.stderr, "data:", self.data +        return "<sec_desc target_addr=0x%x len=%d>" % ( +            self.target_addr, len(self.data)) + + +class header(object): +    def __init__(self, entry, sections): +        self.entry = entry +        self.section = sections + +    def dump(self, f): +        if self.entry == _SBF_DONT_EXECUTE: +            f.write("Entry: DONT_EXECUTE\n") +        else: +            f.write("Entry: 0x%x\n" % (self.entry,)) +        for i in range(len(self.section)): +            s = self.section[i] +            f.write("Section[%d]: target_addr = 0x%x  length = %d\n" % (i, +                                                                        s.target_addr, +                                                                        len(s.data))) +            dump_data(f, s.target_addr, s.data) + +    # +    # Returns an iterator.  Each yield returns (target_addr, data) +    # +    def iterator(self, max_piece=512): +        for s in self.section: +            offset = 0 +            L = len(s.data) +            while offset < L: +                n = min(max_piece, L - offset) +                yield (s.target_addr + offset, +                       s.data[offset:offset+n]) +                offset += n + + + +def read_sbf(input_file): +    """Parse an SBF file""" +     +    f = input_file.read(_SBF_HEADER_LEN) +    #if len(f) < _SBF_HEADER_LEN or not f.startswith(_SBF_MAGIC): +        #raise ValueError, '%s: not an SBF file' % (input_file.name,) +     +    def extract(i): +        start = 16+8*i +        stop = start+8 +        return struct.unpack('>2I', f[start:stop]) + +    def get_data(ss): +        L = ss[1] +        s = input_file.read(L) +        #if len(s) != L: +            #raise ValueError, '%s: file is too short' % (input_file.name(),) +        return s +         +    (magic, entry, nsections, reserved) = struct.unpack('>4s3I', f[0:16]) +    assert nsections <= _SBF_MAX_SECTIONS +    descs = [extract(i) for i in range(nsections)] +    #pprint(descs, sys.stderr) +    data = map(get_data, descs) +    secs = map(lambda ss, data: sec_desc(ss[0], data), descs, data) +    return header(entry, secs) + + +def write_sbf(output_file, sbf_header): +    assert(len(sbf_header.section) <= _SBF_MAX_SECTIONS) +    sbf_header.nsections = len(sbf_header.section) +    f = output_file + +    # write the file header +    f.write(struct.pack('>4s3I', _SBF_MAGIC, sbf_header.entry, sbf_header.nsections, 0)) + +    # write the section headers +    for i in range(sbf_header.nsections): +        f.write(struct.pack('>2I',  +                            sbf_header.section[i].target_addr, +                            len(sbf_header.section[i].data))) +    for i in range(_SBF_MAX_SECTIONS - sbf_header.nsections): +        f.write(struct.pack('>2I', 0, 0)) + +    # write the section data +    for i in range(sbf_header.nsections): +        f.write(sbf_header.section[i].data) + +    return True diff --git a/firmware/microblaze/bin/serial_loader b/firmware/microblaze/bin/serial_loader new file mode 100755 index 000000000..9bd5aada7 --- /dev/null +++ b/firmware/microblaze/bin/serial_loader @@ -0,0 +1,363 @@ +#!/usr/bin/env python +# +# Copyright 2009 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, see <http://www.gnu.org/licenses/>. +# + +import termios +import tty +import os +import sys +import threading +import Queue +from optparse import OptionParser +import time + +import sbf + +GDB_ESCAPE = chr(0x7d) + +# Indexes for termios list. +IFLAG = 0 +OFLAG = 1 +CFLAG = 2 +LFLAG = 3 +ISPEED = 4 +OSPEED = 5 +CC = 6 + +class terminal(object): +    def __init__(self, device, speed_bits): +        fd = os.open(device, os.O_RDWR) +        if not os.isatty(fd): +            raise ValueError(device + " is not a tty") + +        self.read_file = os.fdopen(fd, "rb", 0) +        self.write_file = os.fdopen(os.dup(fd), "wb", 0) +        self.old_attrs = termios.tcgetattr(self.write_file.fileno()) +        #print "old_attrs: ", self.old_attrs +        attrs = list(self.old_attrs)    # copy of attributes +        attrs[ISPEED] = speed_bits      # set input and output speed +        attrs[OSPEED] = speed_bits +        termios.tcsetattr(self.write_file.fileno(), termios.TCSAFLUSH, attrs) +        tty.setraw(self.write_file.fileno())     # enable raw mode +        attrs = termios.tcgetattr(self.write_file.fileno()) +        attrs[CC][termios.VMIN] = 1     # minimim of 1 char +        attrs[CC][termios.VTIME] = 1    # wait no longer than 1/10 +        termios.tcsetattr(self.write_file.fileno(), termios.TCSAFLUSH, attrs) + +    def __del__(self): +        termios.tcsetattr(self.write_file.fileno(), termios.TCSAFLUSH, self.old_attrs) +        self.read_file.close() +        self.write_file.close() + +    def read(self, n): +        """Read at most n bytes from tty""" +        return self.read_file.read(n) + +    def write(self, str): +        """Write str to tty.""" +        return self.write_file.write(str) + + +def hexnibble(i): +    return "0123456789abcdef"[i & 0xf] + +def build_pkt(payload): +    s = ['$'] +    checksum = 0 + +    for p in payload: +        if p in ('$', '#', GDB_ESCAPE): +            s.append(GDB_ESCAPE) +        s.append(p) +        checksum += ord(p) + +    checksum &= 0xff +    s.append('#') +    s.append(hexnibble(checksum >> 4)) +    s.append(hexnibble(checksum)) + +    return ''.join(s) + + +def build_memory_read_pkt(addr, len): +    return build_pkt(('m%x,%x' % (addr, len))) + +def build_memory_write_hex_pkt(addr, s): +    hexdata = ''.join(["%02x" % (ord(c),) for c in s]) +    return build_pkt(('M%x,%x:' % (addr, len(s))) + hexdata) + +def build_memory_write_pkt(addr, s): +    return build_pkt(('X%x,%x:' % (addr, len(s))) + s) + +def build_goto_pkt(addr): +    return build_pkt(('c%x' % (addr,))) + + +def get_packet(f): +    """Return a valid packet, or None on EOF or timeout""" +    LOOKING_FOR_DOLLAR = 0 +    LOOKING_FOR_HASH = 1 +    CSUM1 = 2 +    CSUM2 = 3 + +    fd = f.fileno() +     +    state = LOOKING_FOR_DOLLAR +    buf = [] + +    while True: +        ch = os.read(fd, 1) +        sys.stdout.write(ch) +        if len(ch) == 0: +            print("Returning None") +            return(None) +         +        if state == LOOKING_FOR_DOLLAR: +            if ch == '$': +                buf = [] +                state = LOOKING_FOR_HASH +            elif ch == '#': +                state = LOOKING_FOR_DOLLAR + +        elif state == LOOKING_FOR_HASH: +            if ch == '$': +                state = LOOKING_FOR_DOLLAR +            elif ch == '#': +                state = CSUM1 +            else: +                if ch == GDB_ESCAPE: +                    ch = getc() +                buf.append(ch) + +        elif state == CSUM1: +            chksum1 = ch +            state = CSUM2 + +        elif state == CSUM2: +            chksum2 = ch +            r = ''.join(buf) +            if chksum1 == '.' and chksum2 == '.': +                return r + +            expected_checksum = int(chksum1 + chksum2, 16) +            checksum = 0 +            for c in buf: +                checksum += ord(c) + +            checksum &= 0xff +            if checksum == expected_checksum: +                return r + +            state = LOOKING_FOR_DOLLAR + +        else: +            raise ValueError( "Invalid state") + + +class packet_reader_thread(threading.Thread): +    def __init__(self, tty_in, q): +        threading.Thread.__init__(self) +        self.setDaemon(1) +        self.tty_in = tty_in +        self.q = q +        self._keep_running = True +        self.start() + +    def run(self): +        while self._keep_running == True: +            p = get_packet(self.tty_in) +            if p is not None: +                self.q.put(('pkt', p)) + + +def _make_tr_table(): +    table = [] +    for c in range(256): +        if c < ord(' ') or c > ord('~'): +            table.append('.') +        else: +            table.append(chr(c)) +    return ''.join(table) +         + +class controller(object): +    def __init__(self, tty): +        self.tty = tty +        self.q = Queue.Queue(0) +        self.timers = {} +        self.next_tid = 1 +        self.current_tid = 0 +        self.ntimeouts = 0 +        self.packet_reader = packet_reader_thread(tty.read_file, self.q) +        self.state = None +        self.debug = False +        self.tt = _make_tr_table() + +        self.done = False +        self.addr = None +        self.bits = None + +    def shutdown(self): +        self.packet_reader._keep_running = False + +    def start_timeout(self, timeout_in_secs): +        def callback(tid): +            if self.timers.has_key(tid): +                del self.timers[tid] +            self.q.put(('timeout', tid)) +        self.next_tid += 1 +        tid = self.next_tid +        timer = threading.Timer(timeout_in_secs, callback, (tid,)) +        self.timers[tid] = timer +        timer.start() +        return tid + +    def cancel_timeout(self, tid): +        if self.timers.has_key(tid): +            self.timers[tid].cancel() +            del self.timers[tid] + +    def send_packet(self, pkt): +        if self.debug: +            if len(pkt) > 64: +                s = pkt[0:64] + '...' +            else: +                s = pkt +            sys.stdout.write('-> ' +  s.translate(self.tt) + '\n') +        self.tty.write(pkt); + +    def send_packet_start_timeout(self, pkt, secs): +        self.send_packet(pkt) +        self.current_tid = self.start_timeout(secs) +         +    def upload_code(self, sbf): +        MAX_PIECE = 512                 # biggest piece to send +        MWRITE_TIMEOUT = 0.1 + +        IDLE = 0 +        WAIT_FOR_ACK = 1 +        UPLOAD_DONE = 2 +        DONE = 3 +        FAILED = 4 + +        self.done = False +        it = sbf.iterator(MAX_PIECE) +        entry_addr = sbf.entry + +        def get_next_bits(): +            try: +                (self.addr, self.bits) = it.next() +            except StopIteration: +                self.done = True +             +        def is_done(): +            return self.done +         +        def send_piece(): +            pkt = build_memory_write_pkt(self.addr, self.bits) +            #pkt = build_memory_write_hex_pkt(self.addr, self.bits) +            self.send_packet_start_timeout(pkt, MWRITE_TIMEOUT) +            state = WAIT_FOR_ACK + +        def advance(): +            get_next_bits() +            if is_done(): +                self.state = DONE +                self.send_packet(build_goto_pkt(entry_addr)) +                 +            else: +                self.ntimeouts = 0 +                send_piece() + +        get_next_bits() +        if is_done():                   # empty file +            return True +         +        send_piece()                    # initial transition +         +        while 1: +            (event, value) = self.q.get() + +            if event == 'timeout' and value == self.current_tid: +                self.ntimeouts += 1 +                if self.ntimeouts >= 5: +                    return False        # say we failed +                send_piece()            # resend +                 + +            elif event == 'pkt': +                if value == 'OK': +                    self.cancel_timeout(self.current_tid) +                    advance() +                    if self.state == DONE: +                        return True +                else: +                    print("Error returned from firmware: " + value) +                    return False + +            else: +                print("Unknown event:", (event, value)) + +def main(): +    usage="%prog: [options] filename" +    parser = OptionParser(usage=usage) +    parser.add_option("-t", "--tty", type="string", default="/dev/ttyS0", +                      help="select serial port [default=%default]") + +    (options, args) = parser.parse_args() +    if len(args) != 1: +        parser.print_help() +        raise SystemExit(1) + + +    filename = args[0] +    f = open(filename, "rb") +    try: +        # Try to open the file as an SBF file +        sbf_header = sbf.read_sbf(f) +    except: +        # If that fails, build an SBF from the binary, assuming default +        # load address and entry point +        f.seek(0) +        t = f.read() +        if t.startswith('\177ELF'): +            sys.stderr.write("Can't load an ELF file.  Please use an SBF file instead.\n") +            raise SystemExit( 1) +        sbf_header = sbf.header(0x8000, [sbf.sec_desc(0x8000, t)]) +         + +    tty = terminal(options.tty, termios.B115200) +    ctrl = controller(tty) +    ok = ctrl.upload_code(sbf_header) +     +    if ok: +        print("OK") +        try: +            raw_input("Press Enter to exit: ") +        except KeyboardInterrupt: +            pass +        ctrl.shutdown() +        time.sleep(0.2) +         +    else: +        print("Upload failed") +        ctrl.shutdown() +     +     +     +if __name__ == "__main__": +    main() diff --git a/firmware/microblaze/bin/uart_ihex_flash_loader.py b/firmware/microblaze/bin/uart_ihex_flash_loader.py new file mode 100755 index 000000000..5a3300f34 --- /dev/null +++ b/firmware/microblaze/bin/uart_ihex_flash_loader.py @@ -0,0 +1,138 @@ +#!/usr/bin/python + +import serial +from optparse import OptionParser +import os, sys + +#TODO: pull everything but parser out of main() and put it in a separate function we can call from another script. lets us automate loading RAM+FLASH to produce a fully-loaded image. +#TODO: make it fail gracefully -- if it gets a NOK or times out, do at least one retry. +#TODO: put hooks in (eventually) to allow verifying a firmware image so the user can more safely update the "safe" image +#TODO: how about a progress indicator? FPGA images take FOREVER. you can use wc -l to get the number of lines, or do it with file i/o. + +def main(): +	usage="%prog: [options] filename" +	parser = OptionParser(usage=usage) +	parser.add_option("-t", "--tty", type="string", default="/dev/ttyUSB0", +                     help="select serial port [default=%default]") +	parser.add_option("-b", "--baudrate", type=int, default=115200, +										 help="set baudrate [default=%default]") +	parser.add_option("-F", "--write-safe-firmware", action="store_const", const=1, dest="image", +										 help="write to safe firmware image") +	parser.add_option("-f", "--write-production-firmware", action="store_const", const=2, dest="image", +										 help="write to production firmware image") +	parser.add_option("-P", "--write-safe-fpga", action="store_const", const=3, dest="image", +										 help="write to safe FPGA image") +	parser.add_option("-p", "--write-production-fpga", action="store_const", const=4, dest="image", +										 help="write to production FPGA image") + +	(options, args) = parser.parse_args() + +	if(options.image is None): +		print("At least one of -f, -F, -p, -P must be specified.\n") +		parser.print_help() +		raise SystemExit(1) +	 +	if len(args) != 1: +		parser.print_help() +		raise SystemExit(1) + +	if(options.image == 3): +		print "Are you *really* sure you want to write to the failsafe FPGA image? If you mess this up your USRP2+ will become a brick. Press 'y' to continue, any other key to abort." +		if(raw_input().rstrip() is not "y"): +			print "Good choice." +			raise SystemExit(0) + +	elif(options.image == 1): +		print "Are you *really* sure you want to write to the failsafe firmware image? If you mess this up your USRP2+ will only be able to be reprogrammed via the UART RAM loader.\nPress 'y' to continue, any other key to abort." +		if(raw_input().rstrip() is not "y"): +			print "Good choice." +			raise SystemExit(0) + +	filename = args[0] +	f = open(filename, "r") + +	#now we start doing things... +	if(os.path.exists(options.tty) is False): +		sys.stderr.write("No serial port found at %s\n" % options.tty) +		raise SystemExit(1) +	 +	try: +		ser = serial.Serial(port=options.tty, timeout=1, baudrate=options.baudrate, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, rtscts=0, xonxoff=0) +	except serial.SerialException: +		sys.stderr.write("Unable to open serial port\n") +		raise SystemExit(1) + +	ser.open() + +#test to see if a valid USRP2+ in flash load mode is connected +	ser.write("garbage\n") +	ser.readline() +	ser.write("!SECTORSIZE\n") +	reply = ser.readline().rstrip() +	if("NOK" in reply): +		sys.stderr.write("Error writing to USRP2+. Try again.\n") +		raise SystemExit(1) +	elif("OK" in reply): +		sectorsize = int(reply[3:5], 16) +		print("USRP2+ found with sector size %i. Erasing old image." % 2**sectorsize) +	else: +		sys.stderr.write("Invalid response or no USRP2+ connected.\n") +		raise SystemExit(1) + +	if(options.image == 1): +		sectors = range(127, 128) +		runcmd = "!RUNSFD\n" +	elif(options.image == 2): +		sectors = range(64, 65) +		runcmd = "!RUNPFD\n" +	elif(options.image == 3): +		sectors = range(0,32) +		runcmd = "!RUNSFPGA\n" +	elif(options.image == 4): +		sectors = range(32,64) +		runcmd = "!RUNPFPGA\n" + +	writeaddr = sectors[0] << sectorsize +	if(options.image < 3): +		writeaddr -= 0x8000 #i know this is awkward, but we subtract 0x8000 from the address for firmware loads. the reason we do this is that the IHX files are located at 0x8000 (RAM_BASE), and +												#doing this here allows us to use the same IHX files for RAM load as for Flash load, without relocating in objcopy or relinking with another ld script. +												#this limits us to writing above 32K for our firmware images. since the safe FPGA image located at 0x00000000 takes up 2MB of space this isn't really a worry. +												#FPGA images (.mcs) always start at 0x00000000 so they don't need this relocation. + +	for sector in sectors: +		print "Erasing sector %i" % sector +		ser.write("!ERASE %i\n" % sector) +		reply = ser.readline() +		if("NOK" in reply): +			sys.stderr.write("Error erasing sector %i" % sector) +			raise SystemExit(1) +	 +	print "Setting start address to %i" % writeaddr +	ser.write("!SETADDR %i\n" % writeaddr) +	if("NOK" in reply): +		sys.stderr.write("Error setting address\n") +		raise SystemExit(1) +	else: +		print reply + + +	for line in f: +		ser.write(line.rstrip()+'\n') +		reply = ser.readline() +		if("NOK" in reply): #TODO: simplify this logic, use (reply.rstrip() is "NOK") +			print("Received NOK reply during data write") +			raise SystemExit(1) +		elif("DONE" in reply): +			print("Finished writing program. Loading...\n") +			ser.write(runcmd) +		elif("OK" not in reply): +			print("Received invalid reply %s during data write" % reply) +			raise SystemExit(1) +		else: +			print reply.rstrip() + '\t' + line.rstrip() + +	print ser.readline() + + +if __name__ == '__main__': +	main() diff --git a/firmware/microblaze/bin/uart_ihex_ram_loader.py b/firmware/microblaze/bin/uart_ihex_ram_loader.py new file mode 100755 index 000000000..c90fbe1d8 --- /dev/null +++ b/firmware/microblaze/bin/uart_ihex_ram_loader.py @@ -0,0 +1,70 @@ +#!/usr/bin/python + +import serial +from optparse import OptionParser +import os, sys + +def main(): +	usage="%prog: [options] filename" +	parser = OptionParser(usage=usage) +	parser.add_option("-t", "--tty", type="string", default="/dev/ttyUSB0", +                     help="select serial port [default=%default]") +	parser.add_option("-b", "--baudrate", type=int, default=115200, +										 help="set baudrate [default=%default]") + +	(options, args) = parser.parse_args() +	if len(args) != 1: +		parser.print_help() +		raise SystemExit(1) + +	filename = args[0] +	f = open(filename, "r") + +	#all we have to do is load the IHX file and attempt to spit it out to the serial port. +	if(os.path.exists(options.tty) is False): +		sys.stderr.write("No serial port found at %s\n" % options.tty) +		raise SystemExit(1) +	 +	try: +		ser = serial.Serial(port=options.tty, timeout=1, baudrate=options.baudrate, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, rtscts=0, xonxoff=0) +	except serial.SerialException: +		sys.stderr.write("Unable to open serial port\n") +		raise SystemExit(1) + +	ser.open() + +#test to see if a valid USRP2+ in RAM load mode is connected + +	ser.write("WOOOOO\n"); +	reply = ser.readline() +	if("NOK" not in reply): +		sys.stderr.write("Valid USRP2+ not connected or no response received\n") +		raise SystemExit(1) +	else: +		print("USRP2+ found.") + +	for line in f: +		ser.write(line.rstrip() + '\n') +		reply = ser.readline() +		if("NOK" in reply): #blocks to wait for response +			print("Received NOK reply from USRP2+") +			raise SystemExit(1) +		elif("OK" not in reply): +			print("Received invalid reply!") +			raise SystemExit(1) +#		else: +#			print("OK received") + +	print "USRP2+ RAM programmed.\nLoading program." + +	#at this point it should have sent the end line of the file, which starts the program! +	#we'll just act like a dumb terminal now +#	ser.timeout = 0 +#	try: +#		while 1: +#			print ser.readline() +#	except KeyboardInterrupt: +#		raise SystemExit(0) + +if __name__ == '__main__': +	main() diff --git a/firmware/microblaze/bootstrap b/firmware/microblaze/bootstrap index 5786c1624..26987b0ec 100755 --- a/firmware/microblaze/bootstrap +++ b/firmware/microblaze/bootstrap @@ -18,6 +18,7 @@  rm -rf *.cache  rm -rf libusrp2/.deps +rm -rf libusrp2p/.deps  aclocal  autoconf diff --git a/firmware/microblaze/configure.ac b/firmware/microblaze/configure.ac index e27bcb557..f6986f2dd 100644 --- a/firmware/microblaze/configure.ac +++ b/firmware/microblaze/configure.ac @@ -47,6 +47,8 @@ AC_PATH_PROG([HEXDUMP],    [hexdump])  ##################################################  AC_CONFIG_FILES([ \      Makefile \ +    usrp2p/bootloader/Makefile \      usrp2/Makefile \ +    usrp2p/Makefile \  ])  AC_OUTPUT diff --git a/firmware/microblaze/lib/Makefile.inc b/firmware/microblaze/lib/Makefile.inc index 2b5685d3f..38c630df4 100644 --- a/firmware/microblaze/lib/Makefile.inc +++ b/firmware/microblaze/lib/Makefile.inc @@ -33,6 +33,7 @@ COMMON_SRCS = \  	$(top_srcdir)/lib/hal_io.c \  	$(top_srcdir)/lib/hal_uart.c \  	$(top_srcdir)/lib/i2c.c \ +	$(top_srcdir)/lib/i2c_async.c \  	$(top_srcdir)/lib/mdelay.c \  	$(top_srcdir)/lib/memcpy_wa.c \  	$(top_srcdir)/lib/memset_wa.c \ @@ -42,6 +43,7 @@ COMMON_SRCS = \  	$(top_srcdir)/lib/print_rmon_regs.c \  	$(top_srcdir)/lib/print_buffer.c \  	$(top_srcdir)/lib/printf.c \ +	$(top_srcdir)/lib/ihex.c \  	$(top_srcdir)/lib/spi.c \  	$(top_srcdir)/lib/net_common.c \  	$(top_srcdir)/lib/arp_cache.c \ diff --git a/firmware/microblaze/lib/bootconfig.c b/firmware/microblaze/lib/bootconfig.c new file mode 100644 index 000000000..93adc05c2 --- /dev/null +++ b/firmware/microblaze/lib/bootconfig.c @@ -0,0 +1,101 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "bootconfig.h" +#include "bootconfig_private.h" +#include <stdint.h> +#include <stddef.h> +#include <i2c.h> +#include <quadradio/i2c_addr.h> +#include <mdelay.h> +#include <xilinx_v5_icap.h> +#include <nonstdio.h> + +eeprom_boot_info_t eeprom_shadow; + +static eeprom_boot_info_t eeprom_default = { +  .magic = EEPROM_BOOT_INFO_MAGIC, +  .nattempts = 1, +  .next_boot.fpga_image_number = 0, +  .next_boot.firmware_image_number = 0, +  .default_boot.fpga_image_number = 0, +  .default_boot.firmware_image_number = 0 +}; + +eeprom_boot_info_t * +_bc_get_eeprom_shadow(void) +{ +  return &eeprom_shadow; +} + + +bool +_bc_write_eeprom_shadow(void) +{ +  return eeprom_write(I2C_ADDR_MBOARD, BOOT_INFO_OFFSET, &eeprom_shadow, sizeof(eeprom_shadow)); +} + +void +bootconfig_init(void) +{ +  if (!eeprom_read(I2C_ADDR_MBOARD, BOOT_INFO_OFFSET, &eeprom_shadow, sizeof(eeprom_shadow)) +      || eeprom_shadow.magic != EEPROM_BOOT_INFO_MAGIC){ +    eeprom_shadow = eeprom_default; +    _bc_write_eeprom_shadow(); +  } +} + +bootconfig_t  +bootconfig_get_default(void) +{ +  return eeprom_shadow.default_boot; +} + +bool +bootconfig_set_default(bootconfig_t bc) +{ +  if (!validate_bootconfig(bc)) +    return false; + +  eeprom_shadow.default_boot  = bc; +  eeprom_shadow.next_boot  = bc; +  return _bc_write_eeprom_shadow(); +} + +void +bootconfig_boot(bootconfig_t bc) +{ +  if (!validate_bootconfig(bc)) +    return; + +  eeprom_shadow.next_boot = bc; +  eeprom_shadow.nattempts = 1; +  _bc_write_eeprom_shadow(); + +  if (1){ +    puts("\nbootconfig: chaining to FPGA slot 0 bootloader"); +    mdelay(100); +  } + +  while (1){ +    // Reload fpga with code from SPI flash address 0x0. +    icap_reload_fpga(0x00000000); +  } +}   diff --git a/firmware/microblaze/lib/gdbstub2.c b/firmware/microblaze/lib/gdbstub2.c new file mode 100644 index 000000000..4c63dfce2 --- /dev/null +++ b/firmware/microblaze/lib/gdbstub2.c @@ -0,0 +1,506 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Implement a eensy weensy part of the GDB Remote Serial Protocol + * + * See Appendix D of the GDB manual + * + *   m<addr>,<length> 		-- read <length> bytes of memory starting at <addr> + *     Reply: + *     XX...		XX... is memory contents in hex + *     ENN		ENN   NN is a hex error number + * + *   M<addr>,<length>:XX...     -- write memory, data in hex + *     Reply: + *     OK		for success + *     ENN		for an error.  NN is a hex error number + * + *   X<addr>,<length>:XX...     -- write memory, data in binary + *     Reply: + *     OK		for success + *     ENN		for an error.  NN is a hex error number + * + *   c<addr>			-- continue.  <addr> is the address to resume (goto). + *     Reply: <none> + * + *   \x80 New Format... + */ + +#include "gdbstub2.h" +#include "loader_parser.h" +#include "hal_uart.h" +#include <stdbool.h> +#include <stddef.h> + +#define MAX_PACKET	1024 + +/* + * Get raw character from serial port, no echo. + */ +static inline int  +gdb_getc(void) +{ +  return hal_uart_getc(); +} + +/* + * Put character to serial port.  Raw output. + */ +static inline void +gdb_putc(int ch) +{ +  hal_uart_putc(ch); +} + +// ------------------------------------------------------------------------ + +#define	GDB_ESCAPE 0x7d + +static unsigned char hex_table[16] = {  +  '0', '1', '2', '3', '4', '5', '6', '7', +  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +static int +put_hex8_checksum(int ch, int checksum) +{ +  unsigned char t = hex_table[(ch >> 4) & 0xf]; +  checksum += t; +  gdb_putc(t); + +  t = hex_table[ch & 0xf]; +  checksum += t; +  gdb_putc(t); +  return checksum; +} + +static void +put_hex8(int ch) +{ +  put_hex8_checksum(ch, 0); +} + +static bool +hex4_to_bin(int ch, int *value) +{ +  if ('0' <= ch && ch <= '9'){ +    *value = ch - '0'; +    return true; +  } +  if ('a' <= ch && ch <= 'f'){ +    *value = ch - 'a' + 10; +    return true; +  } +  if ('A' <= ch && ch <= 'F'){ +    *value = ch - 'A' + 10; +    return true; +  } +  *value = 0; +  return false; +} + +static bool +hex8_to_bin(const unsigned char *s, int *value) +{ +  int v0, v1; +  if (hex4_to_bin(s[0], &v0) && hex4_to_bin(s[1], &v1)){ +    *value = (v0 << 4) | v1; +    return true; +  } +  return false; +} + +static bool +hex_to_bin_array(unsigned char *binary_data, const unsigned char *hex_data, size_t nbytes) +{ +  for (size_t i = 0; i < nbytes; i++){ +    int t; +    if (!hex8_to_bin(&hex_data[2*i], &t)) +      return false; +    binary_data[i] = t; +  } +  return true; +} + +static bool +needs_escaping(int ch) +{ +  return ch == '$' || ch == '#' || ch == GDB_ESCAPE; +} + +/* + * \brief Wait for a packet.   + * \param[out] pkt_buf gets the received packet payload. + * \param[in]  max_size is the maximum number of bytes to write into \p pkt_buf. + * \param[out] actual_size is the number of bytes written to \p pkt_buf. + * + * \returns true iff the payload fits and the checksum is OK. + * + * Packets have this format: + * + *  $<packet-data>#<checksum> + * + * Where <packet-data> is anything and <checksum> is a two byte hex + * checksum.  In <packet-data> '$', '#' and 0x7d are escaped with 0x7d. + * The checksum is computed as the modulo 256 sum of all characters + * btween the leading '$' and the trailing '#' (an 8-bit unsigned + * checksum). + */ +static bool +get_packet(unsigned char *pkt_buf, size_t max_size, size_t *actual_size) +{ +  typedef enum states { +    LOOKING_FOR_DOLLAR, +    LOOKING_FOR_HASH, +    CSUM1, +    CSUM2, +  } state_t; + +  *actual_size = 0; +  unsigned char csum[2] = {0, 0}; +  state_t state = LOOKING_FOR_DOLLAR; +  size_t  pi = 0; + +  while (1){ +    int ch = gdb_getc(); + +    switch (state){ +    case LOOKING_FOR_DOLLAR: +      if (ch == '$'){ +	pi = 0; +	state = LOOKING_FOR_HASH; +      } +      else if (ch == '#'){	// most likely missed the $ +	return false; +      } +      break; +	 +    case LOOKING_FOR_HASH: +      if (ch == '$'){ +	return false; +      } +      else if (ch == '#'){ +	state = CSUM1; +      } +      else { +	if (pi >= max_size)	// payload too big +	  return false; + +	if (ch == GDB_ESCAPE) +	  ch = gdb_getc(); + +	pkt_buf[pi++] = ch; +      } +      break; +       +    case CSUM1: +      csum[0] = ch; +      state = CSUM2; +      break; + +    case CSUM2: +      csum[1] = ch; +      *actual_size = pi; + +      // accept .. as a correct checksum +      if (csum[0] == '.' && csum[1] == '.') +	return true; + +      int expected_checksum; +      if (!hex8_to_bin(csum, &expected_checksum)) +	return false; + +      int checksum = 0; +      for (size_t i = 0; i < pi; i++) +	checksum += pkt_buf[i]; + +      checksum &= 0xff; +      return checksum == expected_checksum; +    } +  } +} + +static void +put_packet_trailer(int checksum) +{ +  gdb_putc('#'); +  put_hex8(checksum & 0xff); +  gdb_putc('\r'); +  gdb_putc('\n'); +} + +static void +put_packet(const unsigned char *pkt_buf, size_t size) +{ +  gdb_putc('$'); + +  int checksum = 0; +  for (size_t i = 0; i < size; i++){ +    int ch = pkt_buf[i]; +    if (needs_escaping(ch)) +      gdb_putc(GDB_ESCAPE); +    gdb_putc(ch); +    checksum += ch; +  } +  put_packet_trailer(checksum); +} + +/*! + * Read a hex number + * + * \param[inout] bufptr - pointer to pointer to buffer (updated on return) + * \param[in] end - one past end of valid data in buf + * \param[out] value - the parsed value + * + * \returns true iff a valid hex number was read from bufptr + */ +static bool +parse_number(const unsigned char **bufptr, const unsigned char *end, unsigned int *value) +{ +  const unsigned char *buf = *bufptr; +  unsigned int v = 0; +  bool valid = false; +  int nibble; + +  while (buf < end && hex4_to_bin(*buf, &nibble)){ +    valid = true; +    v = (v << 4) | nibble; +    buf++; +  } +   +  *value = v; +  *bufptr = buf; +  return valid; +} + +static bool +parse_char(const unsigned char **bufptr, const unsigned char *end, unsigned char *ch) +{ +  const unsigned char *buf = *bufptr; +  if (buf < end){ +    *ch = *buf++; +    *bufptr = buf; +    return true; +  } +  return false; +} + +static bool +expect_char(const unsigned char **bufptr, const unsigned char *end, unsigned char expected) +{ +  unsigned char ch; +  return parse_char(bufptr, end, &ch) && ch == expected; +} + +static bool +expect_end(const unsigned char **bufptr, const unsigned char *end) +{ +  return *bufptr == end; +} + +static bool +parse_addr_length(const unsigned char **bufptr, const unsigned char *end, +		  unsigned int *addr, unsigned int *length) +{ +  return (parse_number(bufptr, end, addr) +	  && expect_char(bufptr, end, ',') +	  && parse_number(bufptr, end, length)); +} + +static void +put_error(int error) +{ +  unsigned char buf[3]; +  buf[0] = 'E'; +  buf[1] = hex_table[(error >> 4) & 0xf]; +  buf[2] = hex_table[error & 0xf]; +   +  put_packet(buf, sizeof(buf)); +} + +static void +put_ok(void) +{ +  const unsigned char buf[2] = "OK"; +  put_packet(buf, sizeof(buf)); +} + +/* + * Read memory and send the reply. + * We do it on the fly so that our packet size is effectively unlimited + */ +static void +read_memory(unsigned int addr, unsigned int nbytes) +{ +  int checksum = 0; +  gdb_putc('$'); + +  if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){	// word aligned +    union { +      unsigned int	i; +      unsigned char	c[4]; +    } u; + +    unsigned int *p = (unsigned int *) addr; +    unsigned int length = nbytes / 4; + +    for (unsigned int i = 0; i < length; i++){ +      u.i = p[i];	// do a word read +      checksum = put_hex8_checksum(u.c[0], checksum); +      checksum = put_hex8_checksum(u.c[1], checksum); +      checksum = put_hex8_checksum(u.c[2], checksum); +      checksum = put_hex8_checksum(u.c[3], checksum); +    } +  } +  else {						// byte aligned +    unsigned char *p = (unsigned char *) addr; +    for (unsigned int i = 0; i < nbytes; i++) +      checksum = put_hex8_checksum(p[i], checksum); +  } + +  put_packet_trailer(checksum); +} + +static unsigned int +get_unaligned_int(const unsigned char *p) +{ +  // we're bigendian +  return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]); +} + +static bool +write_memory(unsigned int addr, size_t nbytes, +	     const unsigned char *data) +{ +  if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){	// word-aligned dst +    unsigned int *dst = (unsigned int *) addr; +    size_t length = nbytes / 4; +    for (size_t i = 0; i < length; i++){ +      unsigned int t = get_unaligned_int(&data[4*i]); +      dst[i] = t;					// word writes +    } +  } +  else {						// non-word-aligned dst +    unsigned char *dst = (unsigned char *) addr; +    for (size_t i = 0; i < nbytes; i++){ +      dst[i] = data[i]; +    } +  } +  return true; +} + +void +gdbstub2_main_loop(void) +{ +  unsigned char inpkt[MAX_PACKET + 24]; +  unsigned char binary_data[MAX_PACKET/2] __attribute__((aligned (4))); + +  hal_uart_set_mode(UART_MODE_RAW); //tell UART HAL not to map \n to \r\n + +  while (1){ +    size_t	inpkt_len; +    bool ok = get_packet(inpkt, sizeof(inpkt), &inpkt_len); +    if (!ok){ +      gdb_putc('-'); +      continue; +    } +    gdb_putc('+'); + +    const unsigned char *buf = inpkt; +    const unsigned char *end = inpkt + inpkt_len; +    unsigned char ch; + +    if (!parse_char(&buf, end, &ch)){	// empty packet +      put_packet(0, 0); +      continue; +    } + +    unsigned int addr; +    unsigned int length; + +    switch(ch){ +    case 'm':		// m<addr>,<length>  -- read <length> bytes starting at <addr> +      if (!(parse_addr_length(&buf, end, &addr, &length) && expect_end(&buf, end))){ +	put_error(1); +      } +      else { +	read_memory(addr, length); +      } +      break; + +    case 'M':		// M<addr>,<length>:XX...  -- write <length> bytes starting at <addr> +			//   XX... is the data in hex +      if (!(parse_addr_length(&buf, end, &addr, &length) +	    && expect_char(&buf, end, ':') +	    && (end - buf) == 2 * length)){ +	put_error(1); +      } +      else { +	if (!hex_to_bin_array(binary_data, buf, length)) +	  put_error(2); +	else if (!write_memory(addr, length, binary_data)) +	  put_error(3); +	else +	  put_ok(); +      } +      break; + +    case 'X':		// X<addr>,<length>:XX...  -- write <length> bytes starting at <addr> +			//   XX... is the data in binary +      if (!(parse_addr_length(&buf, end, &addr, &length) +	    && expect_char(&buf, end, ':') +	    && (end - buf) == length)){ +	put_error(1); +      } +      else { +	if (!write_memory(addr, length, buf)) +	  put_error(3); +	else +	  put_ok(); +      } +      break; + +    case 'c':		// c<addr>	-- continue.  <addr> is the address to resume (goto). +      if (!(parse_number(&buf, end, &addr) +	    && expect_end(&buf, end))){ +	put_error(1); +      } +      else { +	typedef void (*fptr_t)(void); +	(*(fptr_t) addr)();	// most likely no return +      } +      break; +/* +    case 0x80: +      { +	unsigned char *output = binary_data;  // reuse +	size_t sizeof_output = sizeof(binary_data); +	size_t actual_olen; +	loader_parser(buf, end-buf, +		      output, sizeof_output, &actual_olen); +	put_packet(output, actual_olen); +      } +      break; +*/ +    default:		// unknown packet type +      put_packet(0, 0); +      break; +    } +  } +} diff --git a/firmware/microblaze/lib/gdbstub2.h b/firmware/microblaze/lib/gdbstub2.h new file mode 100644 index 000000000..15cdde939 --- /dev/null +++ b/firmware/microblaze/lib/gdbstub2.h @@ -0,0 +1,25 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_GDBSTUB_H +#define INCLUDED_GDBSTUB_H + +void gdbstub2_main_loop(void); + +#endif /* INCLUDED_GDBSTUB_H */ + diff --git a/firmware/microblaze/lib/hal_io.c b/firmware/microblaze/lib/hal_io.c index 58b1e681e..be4c570c7 100644 --- a/firmware/microblaze/lib/hal_io.c +++ b/firmware/microblaze/lib/hal_io.c @@ -18,9 +18,9 @@  // conditionalized on HAL_IO_USES_DBOARD_PINS && HAL_IO_USES_UART -#include "hal_io.h"  #include "memory_map.h"  #include "hal_uart.h" +#include "hal_io.h"  #include <stdbool.h>  #include <stdio.h>  #include <string.h> @@ -124,16 +124,29 @@ hal_finish(void)  // %c  inline int +fputchar(hal_uart_name_t u, int ch) +{ +  hal_uart_putc(u, ch); +  return ch; +} + +inline int  putchar(int ch)  { -  hal_uart_putc(ch); +  hal_uart_putc(DEFAULT_UART, ch);    return ch;  }  int +fgetchar(hal_uart_name_t u) +{ +  return hal_uart_getc(u); +} + +int  getchar(void)  { -  return hal_uart_getc(); +  return fgetchar(DEFAULT_UART);  }  #else	// nop all i/o @@ -172,34 +185,87 @@ getchar(void)  // \n  inline void  +fnewline(hal_uart_name_t u) +{ +  fputchar(u, '\n'); +} + +inline void  newline(void)  { -  putchar('\n'); +  fnewline(DEFAULT_UART);  }  int -putstr(const char *s) +fputstr(hal_uart_name_t u, const char *s)  {    while (*s) -    putchar(*s++); +    fputchar(u, *s++);    return 0;  }  int -puts(const char *s) +fnputstr(hal_uart_name_t u, const char *s, int len) +{ +  int x = 0; +  while (*s && (len > x++)) +    fputchar(u, *s++); + +  return x; +} + +int +putstr(const char *s)  { -  putstr(s); -  putchar('\n'); +  return fputstr(DEFAULT_UART, s); +} + +int +fputs(hal_uart_name_t u, const char *s) +{ +  fputstr(u, s); +  fputchar(u, '\n');    return 0;  } +int puts(const char *s) +{ +  return fputs(DEFAULT_UART, s); +} + +char * +fgets(hal_uart_name_t u, char * const s) +{ +  char *x = s; +  while((*x=(char)hal_uart_getc(u)) != '\n') x++; +  *x = 0; +  return s; +} + +int +fngets(hal_uart_name_t u, char * const s, int len) +{ +  char *x = s; +  while(((*x=(char)hal_uart_getc(u)) != '\n') && ((x-s) < len)) x++; +  *x = 0; +  return (x-s); +} + +int +fngets_timeout(hal_uart_name_t u, char * const s, int len) +{ +  char *x = s; + +  while(((*x=(char)hal_uart_getc_timeout(u)) != '\n') && (*x != -1) && ((x-s) < len)) x++; +  *x = 0; +  //printf("Returning from fngets() with string %d of length %d\n", s[0], x-s); +  return (x-s); +} +  char *  gets(char * const s)  { -	char *x = s; -	while((*x=(char)hal_uart_getc()) != '\n') x++; -	*x = 0; -	return s; +  return fgets(DEFAULT_UART, s);  } diff --git a/firmware/microblaze/lib/hal_io.h b/firmware/microblaze/lib/hal_io.h index c67d96c62..950f8d591 100644 --- a/firmware/microblaze/lib/hal_io.h +++ b/firmware/microblaze/lib/hal_io.h @@ -20,10 +20,15 @@  #define INCLUDED_HAL_IO_H  #include "memory_map.h" +#include "hal_uart.h"  void hal_io_init(void);  void hal_finish();  char *gets(char * const s); +int fputstr(hal_uart_name_t u, const char *s); +int fnputstr(hal_uart_name_t u, const char *s, int len); +int fngets(hal_uart_name_t u, char * const s, int len); +int fngets_timeout(hal_uart_name_t u, char * const s, int len);  /*   * ------------------------------------------------------------------------ diff --git a/firmware/microblaze/lib/hal_uart.c b/firmware/microblaze/lib/hal_uart.c index fe3b7515a..7836240fe 100644 --- a/firmware/microblaze/lib/hal_uart.c +++ b/firmware/microblaze/lib/hal_uart.c @@ -16,71 +16,105 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include "memory_map.h"  #include "hal_uart.h"  #include "hal_io.h" -#include "memory_map.h" +#include "mdelay.h" -// First pass, no interrupts - -// Replaced with divisors.py which generates best divisor -//#define CALC_DIVISOR(rate) (WISHBONE_CLK_RATE / ((rate) * 16)) +//just to save you from going insane, note that firmware/FPGA UARTs [0-2] correspond to serial ports [1-3]. +//so in software, we refer to UART_DEBUG as UART0, but it transmits on pin TXD<1>. see the UART assignments in hal_uart.h.  #define NSPEEDS 6  #define	MAX_WB_DIV 4 +//if you're going to recalculate the divisors, it's just uart_clock_rate / baud_rate. +//uart_clock_rate is 50MHz for USRP2.  static const uint16_t -divisor_table[MAX_WB_DIV+1][NSPEEDS] = { -  {   2,   2,   2,   2,  2,  2},   // 0: can't happen -  { 651, 326, 163, 109, 54, 27 },  // 1: 100 MHz -  { 326, 163,  81,  54, 27, 14 },  // 2:  50 MHz -  { 217, 109,  54,  36, 18,  9 },  // 3:  33.3333 MHz -  { 163,  81,  41,  27, 14,  7 },  // 4:  25 MHz +divisor_table[NSPEEDS] = { +  5208,	//    9600 +  2604,	//   19200 +  1302,	//   38400 +  868,	//   57600 +  434,	//  115200 +  217	//  230400  }; -#define u uart_regs +static char uart_mode[4] = { +  [UART_DEBUG] = UART_MODE_ONLCR,  +  [UART_EXP] = UART_MODE_ONLCR,  +  [UART_GPS] = UART_MODE_ONLCR +}; -static char uart_mode = UART_MODE_ONLCR; +static char uart_speeds[4] = { +  [UART_DEBUG] = US_230400, +  [UART_EXP] = US_230400, +  [UART_GPS] = US_115200 +};  void -hal_uart_set_mode(int mode) +hal_uart_set_mode(hal_uart_name_t uart, int mode) +{ +  uart_mode[uart] = mode; +} + +void hal_uart_set_speed(hal_uart_name_t uart, hal_uart_speed_t speed)  { -  uart_mode = mode; +  uart_regs[uart].clkdiv = divisor_table[speed];  }  void  hal_uart_init(void)  { -	hal_uart_set_mode(UART_MODE_ONLCR); -  u->clkdiv = 217;  // 230400 bps +  for(int i = 0; i < 3; i++) { +  	hal_uart_set_mode(i, uart_mode[i]); +    hal_uart_set_speed(i, uart_speeds[i]); +  }  }  void -hal_uart_putc(int ch) +hal_uart_putc(hal_uart_name_t u, int ch)  { -  if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR))		//map \n->\r\n if necessary -    hal_uart_putc('\r'); +  if ((ch == '\n') && (uart_mode[u] == UART_MODE_ONLCR))		//map \n->\r\n if necessary +    hal_uart_putc(u, '\r'); -  while (u->txlevel == 0)	 // wait for fifo to have space +  while (uart_regs[u].txlevel == 0)	 // wait for fifo to have space      ; -  u->txchar = ch; +  uart_regs[u].txchar = ch;  }  void -hal_uart_putc_nowait(int ch) +hal_uart_putc_nowait(hal_uart_name_t u, int ch)  { -  if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR))		//map \n->\r\n if necessary -    hal_uart_putc('\r'); +  if ((ch == '\n') && (uart_mode[u] == UART_MODE_ONLCR))		//map \n->\r\n if necessary +    hal_uart_putc(u, '\r'); -  if(u->txlevel)   // If fifo has space -    u->txchar = ch; +  if(uart_regs[u].txlevel)   // If fifo has space +    uart_regs[u].txchar = ch;  }  int -hal_uart_getc(void) +hal_uart_getc(hal_uart_name_t u)  { -  while ((u->rxlevel) == 0)  // wait for data to be ready +  while ((uart_regs[u].rxlevel) == 0)  // wait for data to be ready      ; -  return u->rxchar; +  return uart_regs[u].rxchar;  } + +int  +hal_uart_getc_timeout(hal_uart_name_t u) +{ +  int timeout = 0; +  while (((uart_regs[u].rxlevel) == 0) && (timeout++ < HAL_UART_TIMEOUT_MS)) +    mdelay(1); +  return (timeout >= HAL_UART_TIMEOUT_MS) ? -1 : uart_regs[u].rxchar; //return -1 if nothing there, cause fngets to quit +} + +int hal_uart_rx_flush(hal_uart_name_t u) +{ +  char x; +  while(uart_regs[u].rxlevel) x = uart_regs[u].rxchar; +  return x; +} + diff --git a/firmware/microblaze/lib/hal_uart.h b/firmware/microblaze/lib/hal_uart.h index dfd73c323..758c8cb5e 100644 --- a/firmware/microblaze/lib/hal_uart.h +++ b/firmware/microblaze/lib/hal_uart.h @@ -25,29 +25,39 @@  #define	UART_MODE_RAW		0x0000	// no mapping on input or output  #define	UART_MODE_ONLCR	0x0001	// map \n to \r\n on output (default) -/* - * \brief Set uart mode - */ -void hal_uart_set_mode(int flags); +#define DEFAULT_UART UART_DEBUG //which UART printf, gets, etc. use -/*! - * \brief one-time call to init - */ -void hal_uart_init(void); +#define HAL_UART_TIMEOUT_MS 300  typedef enum { -  US_9600, -  US_19200, -  US_38400, -  US_57600, -  US_115200, -  US_230400, +  US_9600   = 0, +  US_19200  = 1, +  US_38400  = 2, +  US_57600  = 3, +  US_115200 = 4, +  US_230400 = 5  } hal_uart_speed_t;  typedef struct {    hal_uart_speed_t	speed;  } hal_uart_config_t; +typedef enum { +  UART_DEBUG = 0, +  UART_EXP   = 1, +  UART_GPS   = 2 +} hal_uart_name_t; + +/* + * \brief Set uart mode + */ +void hal_uart_set_mode(hal_uart_name_t uart, int flags); + +/*! + * \brief one-time call to init + */ +void hal_uart_init(void); +  /*!   * \brief Set uart parameters   *  Default is 115,200 bps, 8N1. @@ -62,17 +72,23 @@ void hal_uart_get_config(hal_uart_config_t *c);  /*!   * \brief Enqueue \p ch for output over serial port   */ -void hal_uart_putc(int ch); +void hal_uart_putc(hal_uart_name_t u, int ch);  /*!   * \brief Enqueue \p ch for output over serial port, silent fail if queue is full   */ -void hal_uart_putc_nowait(int ch); +void hal_uart_putc_nowait(hal_uart_name_t u, int ch);  /*   * \brief Blocking read of next char from serial port   */ -int hal_uart_getc(void); +int hal_uart_getc(hal_uart_name_t u); + +/* + * \brief Blocking read of next char from serial port with timeout + */ +int hal_uart_getc_timeout(hal_uart_name_t u); +int hal_uart_rx_flush(hal_uart_name_t u);  #endif /* INCLUDED_HAL_UART_H */ diff --git a/firmware/microblaze/lib/i2c.c b/firmware/microblaze/lib/i2c.c index 177341267..d230f462c 100644 --- a/firmware/microblaze/lib/i2c.c +++ b/firmware/microblaze/lib/i2c.c @@ -20,7 +20,6 @@  #include "memory_map.h"  #include "stdint.h"  #include <string.h> -#include "pic.h"  #include "nonstdio.h"  #define MAX_WB_DIV 4	// maximum wishbone divisor (from 100 MHz MASTER_CLK) @@ -37,18 +36,6 @@ static uint16_t prescaler_values[MAX_WB_DIV+1] = {    PRESCALER(4), // 4:  25 MHz  }; -//asynchronous (interrupt-driven) i2c state variables -volatile uint8_t i2c_buf[17]; //tx/rx data transfer buffer -volatile uint8_t *volatile i2c_bufptr = i2c_buf; //ptr to current position -volatile uint8_t i2c_len = 0; //length remaining in current transfer -volatile i2c_state_t i2c_state = I2C_STATE_IDLE; //current I2C transfer state -i2c_dir_t i2c_dir; //I2C transfer direction - -void  (*volatile i2c_callback)(void); //function pointer to i2c callback to be called when transaction is complete - -static void i2c_irq_handler(unsigned irq); -inline void i2c_async_err(void); -  void  i2c_init(void)  { @@ -64,8 +51,8 @@ i2c_init(void)    i2c_regs->ctrl = I2C_CTRL_EN; //| I2C_CTRL_IE;	// enable core -  // FIXME interrupt driven?   -  pic_register_handler(IRQ_I2C, i2c_irq_handler); +  //now this is done separately to maintain common code for async and sync +  //pic_register_handler(IRQ_I2C, i2c_irq_handler);  }  static inline void @@ -140,163 +127,3 @@ i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len)    return false;  } -static void i2c_irq_handler(unsigned irq) { -//i2c state machine. - -  //printf("I2C irq handler\n"); -  //first let's make sure nothing is f'ed up -  //TODO: uncomment this error checking when we have some way to handle errors -//  if(((i2c_regs->cmd_status & I2C_ST_RXACK) != 0) && i2c_dir == I2C_DIR_WRITE) { //we got a NACK and we didn't send it -//    printf("\tNACK received\n"); -//    i2c_async_err(); -//    return; -//  }// else printf("\tACK received, proceeding\n"); - -  if(i2c_regs->cmd_status & I2C_ST_AL) {  -    printf("\tArbitration lost!\n"); -    i2c_async_err(); -    return; -  } - -  if(i2c_regs->cmd_status & I2C_ST_TIP) { -    //printf("\tI2C still busy in interrupt\n"); -    return; -  } - -  //now decide what to do -  switch(i2c_state) { - -  case I2C_STATE_IDLE: -    //this is an error. in idle state, we shouldn't be transferring data, and the fact that the IRQ fired is terrible bad. -    printf("AAAAAHHHHH INTERRUPT IN THE IDLE STATE AAAHHHHHHHHH\n"); -    i2c_async_err(); -    break; - -  case I2C_STATE_CONTROL_BYTE_SENT: //here we've sent the control byte, and we're either clocking data in or out now, but we haven't received a byte yet. -  case I2C_STATE_DATA:      //here we're sending/receiving data and if we're receiving there's data in the data reg - -    //if(i2c_state == I2C_STATE_DATA) printf("\tI2C in state DATA with dir=%d and len=%d\n", i2c_dir, i2c_len); -    //else printf("\tI2C in state CONTROL_BYTE_SENT with dir=%d and len=%d\n", i2c_dir, i2c_len); - -    if(i2c_dir == I2C_DIR_READ) { -      if(i2c_state == I2C_STATE_DATA) *(i2c_bufptr++) = i2c_regs->data; -      //printf("\tRead %x\n", *(i2c_bufptr-1)); -      //set up another data byte -      if(i2c_len > 1) //only one more byte to transfer -        i2c_regs->cmd_status = I2C_CMD_RD; -      else -        i2c_regs->cmd_status = I2C_CMD_RD | I2C_CMD_NACK | I2C_CMD_STOP; -    } -    else if(i2c_dir == I2C_DIR_WRITE) { -      //write a byte -      //printf("\tWriting %x\n", *i2c_bufptr); -      i2c_regs->data = *(i2c_bufptr++); -      if(i2c_len > 1) -        i2c_regs->cmd_status = I2C_CMD_WR; -      else { -        //printf("\tGenerating STOP\n"); -        i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_STOP; -      } -    }; -    i2c_len--; -    if(i2c_len == 0) i2c_state = I2C_STATE_LAST_BYTE; -    else i2c_state = I2C_STATE_DATA; //takes care of the addr_sent->data transition -    break; - - -  case I2C_STATE_LAST_BYTE: //here we've already sent the last read request and the last data is waiting for us. -    //printf("\tI2C in state LAST BYTE\n"); - -    if(i2c_dir == I2C_DIR_READ) { -      *(i2c_bufptr++) = i2c_regs->data; -      //printf("\tRead %x\n", *(i2c_bufptr-1)); -      i2c_state = I2C_STATE_DATA_READY; -    } else { -      i2c_state = I2C_STATE_IDLE; -    } -    i2c_regs->ctrl &= ~I2C_CTRL_IE; //disable interrupts until next time - -    if(i2c_callback) { -      i2c_callback(); //if we registered a callback, call it! -    } - -    break; - - -  default: //terrible things have happened. -    break; -  } - -} - -void i2c_register_callback(void (*volatile callback)(void)) { -  i2c_callback = callback; -} - -inline void i2c_async_err(void) { -  i2c_state = I2C_STATE_IDLE; -  i2c_regs->ctrl &= ~I2C_CTRL_IE; -  printf("I2C error\n"); -//TODO: set an error flag instead of just dropping things on the floor -  i2c_regs->cmd_status = I2C_CMD_STOP; -} - -bool i2c_async_read(uint8_t addr, unsigned int len) { -  //printf("Starting async read\n"); -  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle -  if(len == 0) return true; //just idiot-proofing -  if(len > sizeof(i2c_buf)) return false; - -  //disable I2C interrupts and clear pending interrupts on the I2C device -  i2c_regs->ctrl &= ~I2C_CTRL_IE; -  i2c_regs->cmd_status |= I2C_CMD_IACK; - -  i2c_len = len; -  i2c_dir = I2C_DIR_READ; -  i2c_bufptr = i2c_buf; -  //then set up the transfer by issuing the control byte -  i2c_regs->ctrl |= I2C_CTRL_IE; -  i2c_regs->data = (addr << 1) | 0x01; //7 bit addr and read bit -  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr -  //update the state so the irq handler knows what's going on -  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; -  return true; -} - -bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len) { -  //printf("Starting async write\n"); -  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle -  if(len > sizeof(i2c_buf)) return false; - -  //disable I2C interrupts and clear pending interrupts on the I2C device -  i2c_regs->ctrl &= ~I2C_CTRL_IE; -  i2c_regs->cmd_status |= I2C_CMD_IACK; - -  //copy the buffer into our own if writing -  memcpy((void *)i2c_buf, buf, len); - -  i2c_len = len; -  i2c_dir = I2C_DIR_WRITE; -  i2c_bufptr = i2c_buf; -  //then set up the transfer by issuing the control byte -  i2c_regs->ctrl |= I2C_CTRL_IE; -  i2c_regs->data = (addr << 1) | 0x00; //7 bit addr and read bit -  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr -  //update the state so the irq handler knows what's going on -  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; - -  return true; -} - -//TODO: determine if it's better to read sequentially into the user's buffer, copy on transfer complete, or copy on request (shown below). probably best to copy on request. -bool i2c_async_data_ready(void *buf) { -  if(i2c_state == I2C_STATE_DATA_READY) { -    i2c_state = I2C_STATE_IDLE; -    memcpy(buf, (void *)i2c_buf, (i2c_bufptr - i2c_buf)); //TODO: not really comfortable with this -    //printf("Copying %d bytes to user buffer\n", i2c_bufptr-i2c_buf); -    return true; -  } -  return false; -} - - diff --git a/firmware/microblaze/lib/i2c.h b/firmware/microblaze/lib/i2c.h index 77129e922..6ff0e6982 100644 --- a/firmware/microblaze/lib/i2c.h +++ b/firmware/microblaze/lib/i2c.h @@ -22,36 +22,15 @@  #include <stdbool.h>  #include "stdint.h" -typedef enum { I2C_STATE_IDLE,  -               I2C_STATE_CONTROL_BYTE_SENT,  -               I2C_STATE_DATA,  -               I2C_STATE_LAST_BYTE,  -               I2C_STATE_DATA_READY,  -               I2C_STATE_ERROR  -             } i2c_state_t; - -typedef enum { I2C_DIR_WRITE=0, I2C_DIR_READ=1 } i2c_dir_t; -  void i2c_init(void);  bool i2c_read (unsigned char i2c_addr, unsigned char *buf, unsigned int len);  bool i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len); -bool i2c_async_read(uint8_t addr, unsigned int len); -bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len); -bool i2c_async_data_ready(void *); -//static void i2c_irq_handler(unsigned irq); -void i2c_register_callback(void (*callback)(void)); - -// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. -// Which EEPROM is determined by i2c_addr.  See i2c_addr.h -  bool eeprom_write (int i2c_addr, int eeprom_offset, const void *buf, int len); -bool eeprom_write_async (int i2c_addr, int eeprom_offset, const void *buf, int len, void (*callback)(void));  // Read 24LC024 / 24LC025 EEPROM on motherboard or daughterboard.  // Which EEPROM is determined by i2c_addr.  See i2c_addr.h  bool eeprom_read (int i2c_addr, int eeprom_offset, void *buf, int len); -bool eeprom_read_async(int i2c_addr, int eeprom_offset, void *buf, int len, void (*callback)(void));  #endif /* INCLUDED_I2C_H */ diff --git a/firmware/microblaze/lib/i2c_async.c b/firmware/microblaze/lib/i2c_async.c new file mode 100644 index 000000000..05c4c3a09 --- /dev/null +++ b/firmware/microblaze/lib/i2c_async.c @@ -0,0 +1,206 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2007,2008 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, see <http://www.gnu.org/licenses/>. + */ +  + //i2c_async.c: asynchronous (interrupt-driven) routines for I2C. + //separated out here so we can have a small I2C lib for bootloader and + //retain interrupt-driven I2C for the main app. + +#include "memory_map.h" +#include "stdint.h" +#include <string.h> +#include "pic.h" +#include "nonstdio.h" +#include "i2c_async.h" +  + //asynchronous (interrupt-driven) i2c state variables +volatile uint8_t i2c_buf[17]; //tx/rx data transfer buffer +volatile uint8_t *volatile i2c_bufptr = i2c_buf; //ptr to current position +volatile uint8_t i2c_len = 0; //length remaining in current transfer +volatile i2c_state_t i2c_state = I2C_STATE_IDLE; //current I2C transfer state +i2c_dir_t i2c_dir; //I2C transfer direction + +void  (*volatile i2c_callback)(void); //function pointer to i2c callback to be called when transaction is complete +static void i2c_irq_handler(unsigned irq); +inline void i2c_async_err(void); + +void i2c_register_handler(void) { +    pic_register_handler(IRQ_I2C, i2c_irq_handler); +} + + +static void i2c_irq_handler(unsigned irq) { +//i2c state machine. + +  //printf("I2C irq handler\n"); +  //first let's make sure nothing is f'ed up +  //TODO: uncomment this error checking when we have some way to handle errors +//  if(((i2c_regs->cmd_status & I2C_ST_RXACK) != 0) && i2c_dir == I2C_DIR_WRITE) { //we got a NACK and we didn't send it +//    printf("\tNACK received\n"); +//    i2c_async_err(); +//    return; +//  }// else printf("\tACK received, proceeding\n"); + +  if(i2c_regs->cmd_status & I2C_ST_AL) {  +    printf("\tArbitration lost!\n"); +    i2c_async_err(); +    return; +  } + +  if(i2c_regs->cmd_status & I2C_ST_TIP) { +    //printf("\tI2C still busy in interrupt\n"); +    return; +  } + +  //now decide what to do +  switch(i2c_state) { + +  case I2C_STATE_IDLE: +    //this is an error. in idle state, we shouldn't be transferring data, and the fact that the IRQ fired is terrible bad. +    printf("AAAAAHHHHH INTERRUPT IN THE IDLE STATE AAAHHHHHHHHH\n"); +    i2c_async_err(); +    break; + +  case I2C_STATE_CONTROL_BYTE_SENT: //here we've sent the control byte, and we're either clocking data in or out now, but we haven't received a byte yet. +  case I2C_STATE_DATA:      //here we're sending/receiving data and if we're receiving there's data in the data reg + +    //if(i2c_state == I2C_STATE_DATA) printf("\tI2C in state DATA with dir=%d and len=%d\n", i2c_dir, i2c_len); +    //else printf("\tI2C in state CONTROL_BYTE_SENT with dir=%d and len=%d\n", i2c_dir, i2c_len); + +    if(i2c_dir == I2C_DIR_READ) { +      if(i2c_state == I2C_STATE_DATA) *(i2c_bufptr++) = i2c_regs->data; +      //printf("\tRead %x\n", *(i2c_bufptr-1)); +      //set up another data byte +      if(i2c_len > 1) //only one more byte to transfer +        i2c_regs->cmd_status = I2C_CMD_RD; +      else +        i2c_regs->cmd_status = I2C_CMD_RD | I2C_CMD_NACK | I2C_CMD_STOP; +    } +    else if(i2c_dir == I2C_DIR_WRITE) { +      //write a byte +      //printf("\tWriting %x\n", *i2c_bufptr); +      i2c_regs->data = *(i2c_bufptr++); +      if(i2c_len > 1) +        i2c_regs->cmd_status = I2C_CMD_WR; +      else { +        //printf("\tGenerating STOP\n"); +        i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_STOP; +      } +    }; +    i2c_len--; +    if(i2c_len == 0) i2c_state = I2C_STATE_LAST_BYTE; +    else i2c_state = I2C_STATE_DATA; //takes care of the addr_sent->data transition +    break; + + +  case I2C_STATE_LAST_BYTE: //here we've already sent the last read request and the last data is waiting for us. +    //printf("\tI2C in state LAST BYTE\n"); + +    if(i2c_dir == I2C_DIR_READ) { +      *(i2c_bufptr++) = i2c_regs->data; +      //printf("\tRead %x\n", *(i2c_bufptr-1)); +      i2c_state = I2C_STATE_DATA_READY; +    } else { +      i2c_state = I2C_STATE_IDLE; +    } +    i2c_regs->ctrl &= ~I2C_CTRL_IE; //disable interrupts until next time + +    if(i2c_callback) { +      i2c_callback(); //if we registered a callback, call it! +    } + +    break; + + +  default: //terrible things have happened. +    break; +  } + +} + +void i2c_register_callback(void (*volatile callback)(void)) { +  i2c_callback = callback; +} + +inline void i2c_async_err(void) { +  i2c_state = I2C_STATE_IDLE; +  i2c_regs->ctrl &= ~I2C_CTRL_IE; +  printf("I2C error\n"); +//TODO: set an error flag instead of just dropping things on the floor +  i2c_regs->cmd_status = I2C_CMD_STOP; +} + +bool i2c_async_read(uint8_t addr, unsigned int len) { +  //printf("Starting async read\n"); +  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle +  if(len == 0) return true; //just idiot-proofing +  if(len > sizeof(i2c_buf)) return false; + +  //disable I2C interrupts and clear pending interrupts on the I2C device +  i2c_regs->ctrl &= ~I2C_CTRL_IE; +  i2c_regs->cmd_status |= I2C_CMD_IACK; + +  i2c_len = len; +  i2c_dir = I2C_DIR_READ; +  i2c_bufptr = i2c_buf; +  //then set up the transfer by issuing the control byte +  i2c_regs->ctrl |= I2C_CTRL_IE; +  i2c_regs->data = (addr << 1) | 0x01; //7 bit addr and read bit +  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr +  //update the state so the irq handler knows what's going on +  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; +  return true; +} + +bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len) { +  //printf("Starting async write\n"); +  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle +  if(len > sizeof(i2c_buf)) return false; + +  //disable I2C interrupts and clear pending interrupts on the I2C device +  i2c_regs->ctrl &= ~I2C_CTRL_IE; +  i2c_regs->cmd_status |= I2C_CMD_IACK; + +  //copy the buffer into our own if writing +  memcpy((void *)i2c_buf, buf, len); + +  i2c_len = len; +  i2c_dir = I2C_DIR_WRITE; +  i2c_bufptr = i2c_buf; +  //then set up the transfer by issuing the control byte +  i2c_regs->ctrl |= I2C_CTRL_IE; +  i2c_regs->data = (addr << 1) | 0x00; //7 bit addr and read bit +  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr +  //update the state so the irq handler knows what's going on +  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; + +  return true; +} + +//TODO: determine if it's better to read sequentially into the user's buffer, copy on transfer complete, or copy on request (shown below). probably best to copy on request. +bool i2c_async_data_ready(void *buf) { +  if(i2c_state == I2C_STATE_DATA_READY) { +    i2c_state = I2C_STATE_IDLE; +    memcpy(buf, (void *)i2c_buf, (i2c_bufptr - i2c_buf)); //TODO: not really comfortable with this +    //printf("Copying %d bytes to user buffer\n", i2c_bufptr-i2c_buf); +    return true; +  } +  return false; +} + diff --git a/firmware/microblaze/lib/i2c_async.h b/firmware/microblaze/lib/i2c_async.h new file mode 100644 index 000000000..e6095fca6 --- /dev/null +++ b/firmware/microblaze/lib/i2c_async.h @@ -0,0 +1,50 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2007,2008 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_I2C_ASYNC_H +#define INCLUDED_I2C_ASYNC_H + +#include <stdbool.h> +#include "stdint.h" + +typedef enum { I2C_STATE_IDLE,  +               I2C_STATE_CONTROL_BYTE_SENT,  +               I2C_STATE_DATA,  +               I2C_STATE_LAST_BYTE,  +               I2C_STATE_DATA_READY,  +               I2C_STATE_ERROR  +             } i2c_state_t; + +typedef enum { I2C_DIR_WRITE=0, I2C_DIR_READ=1 } i2c_dir_t; + +bool i2c_async_read(uint8_t addr, unsigned int len); +bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len); +bool i2c_async_data_ready(void *); +//static void i2c_irq_handler(unsigned irq); +void i2c_register_callback(void (*callback)(void)); +void i2c_register_handler(void); + +// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr.  See i2c_addr.h + +bool eeprom_write_async (int i2c_addr, int eeprom_offset, const void *buf, int len, void (*callback)(void)); +bool eeprom_read_async(int i2c_addr, int eeprom_offset, void *buf, int len, void (*callback)(void)); + +#endif /* INCLUDED_I2C_ASYNC_H */ diff --git a/firmware/microblaze/lib/ihex.c b/firmware/microblaze/lib/ihex.c new file mode 100644 index 000000000..97ecf73b6 --- /dev/null +++ b/firmware/microblaze/lib/ihex.c @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include "ihex.h" +#include <ctype.h> //man that pulls in a lot of shit + +//this is not safe and you should run isxdigit beforehand +uint8_t asc2nibble(char input) { +	if(input > 'Z') return input - 'W'; +	else if(input > '9') return input - '7'; +	else return input - '0'; +} + +int ihex_parse(char input[], ihex_record_t *record) { +	//given a NULL-TERMINATED input string (use gets()) in I16HEX format, write the binary record in record. return 0 on success. + +	uint8_t inputlen; +	uint8_t t, i, checksum_calc=0, checksum_read; + +	//first check for ":" leading character +	if(input[0] != ':') return -1; + +	//then check the string for only valid ASCII ['0'-'F'] +	inputlen=1; +	while(input[inputlen]) { +		if( !isxdigit(input[inputlen++]) ) return -2; +	} + +	//then read the length. +	record->length = (asc2nibble(input[1]) << 4) + asc2nibble(input[2]); +	if(input[(record->length<<1) + 11] != 0) return -3; //if we're missing a null terminator in the right place + +	//then read the address. +	record->addr = (asc2nibble(input[3]) << 12) + (asc2nibble(input[4]) << 8) + (asc2nibble(input[5]) << 4) + asc2nibble(input[6]); + +	//then read the record type. +	record->type = (asc2nibble(input[7]) << 4) + asc2nibble(input[8]); +//	if(record->type > 4) return -4; + +	//then read the data, which goes from input[9] to input[9+length*2]. +	for(i=0; i < record->length; i++) { +		t = 9 + (i<<1); +		record->data[i] = (asc2nibble(input[t]) << 4) + (asc2nibble(input[t + 1])); +		checksum_calc += record->data[i]; //might as well keep a running checksum as we read +	} +	checksum_calc += record->length + record->type + (record->addr >> 8) + (record->addr & 0xFF); //get the rest of the data into that checksum +	checksum_calc = ~checksum_calc + 1;	//checksum is 2's complement + +	//now read the checksum of the record +	checksum_read = (asc2nibble(input[9 + (record->length<<1)]) << 4) + asc2nibble(input[10 + (record->length<<1)]); +	if(checksum_calc != checksum_read) return -5; //compare 'em + +	return 0; +} diff --git a/firmware/microblaze/lib/ihex.h b/firmware/microblaze/lib/ihex.h new file mode 100644 index 000000000..9f471fbe2 --- /dev/null +++ b/firmware/microblaze/lib/ihex.h @@ -0,0 +1,18 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <stdint.h> +#include <stddef.h> + +typedef struct { +	uint8_t type; +	size_t length; +	uint32_t addr; +	uint8_t *data; +} ihex_record_t; + + +int ihex_parse(char input[], ihex_record_t *record); diff --git a/firmware/microblaze/lib/u2_init.c b/firmware/microblaze/lib/u2_init.c index c9b4beb2b..4a553a713 100644 --- a/firmware/microblaze/lib/u2_init.c +++ b/firmware/microblaze/lib/u2_init.c @@ -23,11 +23,22 @@  #include "buffer_pool.h"  #include "hal_uart.h"  #include "i2c.h" +#include "i2c_async.h"  #include "mdelay.h"  #include "clocks.h"  #include "usrp2/fw_common.h"  #include "nonstdio.h" +unsigned char u2_hw_rev_major; +unsigned char u2_hw_rev_minor; + +static inline void +get_hw_rev(void) +{ +  bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV, &u2_hw_rev_minor, 1); +  ok &= eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV+1, &u2_hw_rev_major, 1); +} +  /*   * We ought to arrange for this to be called before main, but for now,   * we require that the user's main call u2_init as the first thing... @@ -49,6 +60,7 @@ u2_init(void)    // init i2c so we can read our rev    pic_init();	// progammable interrupt controller    i2c_init(); +  i2c_register_handler(); //for using async I2C    hal_enable_ints();    bp_init();	// buffer pool diff --git a/firmware/microblaze/lib/udp_fw_update.h b/firmware/microblaze/lib/udp_fw_update.h new file mode 100644 index 000000000..d25525bd2 --- /dev/null +++ b/firmware/microblaze/lib/udp_fw_update.h @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "net_common.h" + +#define USRP2_UDP_UPDATE_PORT 49154 + +typedef enum { +  USRP2_FW_UPDATE_ID_WAT = ' ', + +  USRP2_FW_UPDATE_ID_OHAI_LOL = 'a', +  USRP2_FW_UPDATE_ID_OHAI_OMG = 'A', + +  USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f', +  USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F', + +  USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e', +  USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E', + +  USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd', +  USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D', +  USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B', + +  USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w', +  USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W', + +  USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r', +  USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R', + +  USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's', +  USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S', + +  USRP2_FW_UPDATE_ID_KTHXBAI = '~' + +} usrp2_fw_update_id_t; + +typedef struct { +  uint32_t proto_ver; +  uint32_t id; +  uint32_t seq; +  union { +      uint32_t ip_addr; +    struct { +      uint32_t flash_addr; +      uint32_t length; +      uint8_t  data[256]; +    } flash_args; +    struct { +      uint32_t sector_size_bytes; +      uint32_t memory_size_bytes; +    } flash_info_args; +  } data; +} usrp2_fw_update_data_t; + +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, +                                 unsigned char *payload, int payload_len); diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am index ba426b75c..7a58e7253 100644 --- a/firmware/microblaze/usrp2/Makefile.am +++ b/firmware/microblaze/usrp2/Makefile.am @@ -24,6 +24,8 @@ AM_LDFLAGS = \  	$(COMMON_LFLAGS) \  	-Wl,-defsym -Wl,_TEXT_START_ADDR=0x0050 \  	-Wl,-defsym -Wl,_STACK_SIZE=3072 +	 +LDADD = libusrp2.a  LDADD = libusrp2.a @@ -35,7 +37,8 @@ noinst_LIBRARIES = libusrp2.a  libusrp2_a_SOURCES = \  	$(COMMON_SRCS) \  	sd.c \ -	ethernet.c +	ethernet.c \ +  udp_fw_update.c  noinst_PROGRAMS = \  	usrp2_txrx_uhd.elf diff --git a/firmware/microblaze/usrp2/udp_fw_update.c b/firmware/microblaze/usrp2/udp_fw_update.c new file mode 100644 index 000000000..14eb0b1ee --- /dev/null +++ b/firmware/microblaze/usrp2/udp_fw_update.c @@ -0,0 +1,34 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +//Routines to handle updating the SPI Flash firmware via UDP + +#include "net_common.h" +#include "usrp2/fw_common.h" +#include <nonstdio.h> +#include "udp_fw_update.h" + +//Firmware update packet handler +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, +                                 unsigned char *payload, int payload_len) { + +  usrp2_fw_update_data_t update_data_out; +  update_data_out.id = USRP2_FW_UPDATE_ID_WAT; + +  send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); +} diff --git a/firmware/microblaze/usrp2p/.gitignore b/firmware/microblaze/usrp2p/.gitignore new file mode 100644 index 000000000..18f715618 --- /dev/null +++ b/firmware/microblaze/usrp2p/.gitignore @@ -0,0 +1,9 @@ +/Makefile +/Makefile.in +/*.a +/*.bin +/*.dump +/*.ihx +/*.elf +/*.rom +/*.map diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am new file mode 100644 index 000000000..a5df3ff08 --- /dev/null +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -0,0 +1,70 @@ +# +# Copyright 2010 Ettus Research LLC +# +# 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, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +AM_CFLAGS = \ +	$(COMMON_CFLAGS) + +AM_LDFLAGS = \ +	$(COMMON_LFLAGS) \ +	-Wl,-defsym -Wl,_TEXT_START_ADDR=0x8050 \ +	-Wl,-defsym -Wl,_STACK_SIZE=3072 + +LDADD = libusrp2p.a + +#all of this here is to relocate the hardware vectors to somewhere normal. +RELOCATE_ARGS = \ +	--change-section-address .vectors.sw_exception+0x8000 \ +	--change-section-address .vectors.hw_exception+0x8000 \ +	--change-section-address .vectors.interrupt+0x8000 \ +	--change-section-address .vectors.reset+0x8000 + +#	$(MB_OBJCOPY) -O ihex $< $@ +# the below would work if objcopy weren't written by apes +#	$(MB_OBJCOPY) -O ihex -w --change-section-address .vectors*+0x8000 $< $@ +# using the below will throw away the interrupt vectors when they get relocated below 0x0000. +#	$(MB_OBJCOPY) -O ihex --change-addresses -0x8000 $< $@ + +######################################################################## +# USRP2P specific library and programs +######################################################################## +noinst_LIBRARIES = libusrp2p.a + +libusrp2p_a_SOURCES = \ +	$(COMMON_SRCS) \ +	spif.c \ +	spi_flash.c \ +	spi_flash_read.c \ +	bootloader_utils.c \ +	ethernet.c \ +	xilinx_s3_icap.c \ +  udp_fw_update.c + +noinst_PROGRAMS = \ +	usrp2p_txrx_uhd.elf \ +  usrp2p_blinkenlights.elf \ +  usrp2p_uart_flash_loader.elf + +usrp2p_txrx_uhd_elf_SOURCES = \ +	$(top_srcdir)/apps/txrx_uhd.c + +usrp2p_blinkenlights_elf_SOURCES = \ +  $(top_srcdir)/apps/blinkenlights.c + +usrp2p_uart_flash_loader_elf_SOURCES = \ +  $(top_srcdir)/apps/uart_flash_loader.c diff --git a/firmware/microblaze/usrp2p/bootconfig.h b/firmware/microblaze/usrp2p/bootconfig.h new file mode 100644 index 000000000..35c2726ed --- /dev/null +++ b/firmware/microblaze/usrp2p/bootconfig.h @@ -0,0 +1,61 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + *  + * This file is part of GNU Radio + *  + * GNU Radio 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, or (at your option) + * any later version. + *  + * GNU Radio 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, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_BOOTCONFIG_H +#define INCLUDED_BOOTCONFIG_H + +#include <stdbool.h> + +typedef struct { +  unsigned char	fpga_image_number; +  unsigned char	firmware_image_number; +} bootconfig_t; + +static inline bootconfig_t +make_bootconfig(unsigned char fpga_image_number, unsigned char firmware_image_number) +{ +  bootconfig_t r; +  r.fpga_image_number = fpga_image_number; +  r.firmware_image_number = firmware_image_number; +  return r; +} + +void bootconfig_init(void);	/* One time call to initialize */ + +/*! + * \return default boot configuration + */ +bootconfig_t bootconfig_get_default(void); + +/*! + * \brief Set the default boot configuration. + */ +bool bootconfig_set_default(bootconfig_t bc); + +/*! + * \brief attempt to boot the given fpga and software image. + * + * If successful, this routine does not return. + * If it fail for some reason, it returns. + */ +void bootconfig_boot(bootconfig_t bc); + +#endif /* INCLUDED_BOOTCONFIG_H */ diff --git a/firmware/microblaze/usrp2p/bootloader/.gitignore b/firmware/microblaze/usrp2p/bootloader/.gitignore new file mode 100644 index 000000000..17b0f82f3 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/.gitignore @@ -0,0 +1,11 @@ +/*.ihx +/*.rmi +/*_rom +/*.elf +/*.bin +/*.dump +/*.log +/*.rom +/*.map +/Makefile +/Makefile.in diff --git a/firmware/microblaze/usrp2p/bootloader/Makefile.am b/firmware/microblaze/usrp2p/bootloader/Makefile.am new file mode 100644 index 000000000..1fc5daf9c --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/Makefile.am @@ -0,0 +1,39 @@ +# +# Copyright 2007,2008,2009 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, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +ROM_LINKER_SCRIPT = u2p2-rom.ld + +# loads into 8K boot ram located at 0x0000_0000 +AM_CFLAGS = $(COMMON_CFLAGS) -I$(top_srcdir)/usrp2p +AM_LDFLAGS = -Wl,-T,$(ROM_LINKER_SCRIPT) $(COMMON_LFLAGS) -Wl,-defsym -Wl,_STACK_SIZE=1024 + +EXTRA_DIST = $(ROM_LINKER_SCRIPT) + +LDADD = $(top_srcdir)/usrp2p/libusrp2p.a + +noinst_PROGRAMS = \ +	init_bootloader.elf + +init_bootloader_elf_SOURCES = init_bootloader.c + +.bin.rmi: +	$(top_srcdir)/bin/bin_to_ram_macro_init.py $< $@ + +_generated_from_elf += \ +	$(noinst_PROGRAMS:.elf=.rmi) diff --git a/firmware/microblaze/usrp2p/bootloader/fpga_bootloader.c b/firmware/microblaze/usrp2p/bootloader/fpga_bootloader.c new file mode 100644 index 000000000..9feff6ecd --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/fpga_bootloader.c @@ -0,0 +1,202 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * This code is bootloader f/w for the slot 0 fpga image.  It's job is + * to figure out which fpga image should be loaded, and then to load + * that image from the SPI flash.  (FIXME handle retries, errors, + * etc.) + * + * If the center button is down during boot, it loads firwmare + * from 0:0 instead of its normal action. + */ + +#include <stdlib.h> +#include <hal_io.h> +#include <nonstdio.h> +#include <mdelay.h> +#include <quadradio/flashdir.h> +#include <xilinx_v5_icap.h> +#include <bootconfig.h> +#include <bootconfig_private.h> +#include <spi_flash.h> +#include <string.h> +#include <bootloader_utils.h> +#include <hal_interrupts.h> + +#define VERBOSE 1 + +#define	OUR_FPGA_IMAGE_NUMBER	0	// this code only runs in slot 0 + +void hal_uart_init(void); +void spif_init(void); +void i2c_init(void); +void bootconfig_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ +  // nop stub +} + +static int +flash_addr_of_fpga_slot(unsigned int fpga_slot) +{ +  const struct flashdir *fd = get_flashdir(); +  return fd->slot[fpga_slot + fd->fpga_slot0].start << spi_flash_log2_sector_size(); +} + + +/* + * If the first 256 bytes of the image contain the string of bytes, + * ff ff ff ff aa 99 55 66, we consider it a likely bitstream. + */ +static bool +looks_like_a_bitstream(unsigned int fpga_slot) +{ +  unsigned char buf[256]; +  static const unsigned char pattern[] = { +    0xff, 0xff, 0xff, 0xff, 0xaa, 0x99, 0x55, 0x66 +  }; + +  // Read the first 256 bytes of the bitstream +  spi_flash_read(flash_addr_of_fpga_slot(fpga_slot), sizeof(buf), buf); + +  for (int i = 0; i <= sizeof(buf) - sizeof(pattern); i++) +    if (memcmp(pattern, &buf[i], sizeof(pattern)) == 0) +      return true; + +  return false; +} + +static bool +plausible_bootconfig(bootconfig_t bc) +{ +  // Are the fields in range? +  if (!validate_bootconfig(bc)) +    return false; + +  if (!looks_like_a_bitstream(map_fpga_image_number_to_fpga_slot(bc.fpga_image_number))) +    return false; + +  return true; +} + +// Attempt to boot the fpga image specified in next_boot +static void +initial_boot_attempt(eeprom_boot_info_t *ee) +{ +  if (ee->next_boot.fpga_image_number == OUR_FPGA_IMAGE_NUMBER){ +    load_firmware(); +    return; +  } + +  ee->nattempts = 1; +  _bc_write_eeprom_shadow(); + +  unsigned int target_slot = +    map_fpga_image_number_to_fpga_slot(ee->next_boot.fpga_image_number); +  int flash_addr = flash_addr_of_fpga_slot(target_slot); + +  putstr("fpga_bootloader: chaining to "); +  puthex4(ee->next_boot.fpga_image_number); +  putchar(':'); +  puthex4(ee->next_boot.firmware_image_number); +  newline(); +  mdelay(100); + +  while (1){ +    icap_reload_fpga(flash_addr); +  } +} + +int +main(int argc, char **argv) +{ +  hal_disable_ints();	// In case we got here via jmp 0x0 +  hal_uart_init(); +  i2c_init(); +  bootconfig_init();	// Must come after i2c_init. +  spif_init();		// Needed for get_flashdir. + +  sr_leds->leds =  0xAAAA; + +  putstr("\n\n>>> fpga_bootloader <<<\n"); + +  putstr("\nBOOTSTS "); +  int bootsts = icap_read_config_reg(rBOOTSTS); +  puthex32_nl(bootsts); +  putstr("STAT    "); +  int stat = icap_read_config_reg(rSTAT); +  puthex32_nl(stat); + +  bool fallback = +    ((bootsts & (BOOTSTS_VALID_0 | BOOTSTS_FALLBACK_0)) +     == (BOOTSTS_VALID_0 | BOOTSTS_FALLBACK_0)); + +  if (fallback){ +    puts("FALLBACK_0 is set"); +    // FIXME handle fallback condition. +  } + +  const struct flashdir *fd = get_flashdir(); +  if (fd == 0) +    abort(); + +  eeprom_boot_info_t *ee = _bc_get_eeprom_shadow(); + +  if (VERBOSE){ +    putstr("nattempts: "); +    puthex8_nl(ee->nattempts); +  } + +  mdelay(500);	// wait for low-pass on switches +  putstr("switches: "); puthex32_nl(readback->switches); + +  bool center_btn_down = (readback->switches & BTN_CENTER) != 0; +  if (center_btn_down){ +    putstr("Center button is down!\n"); +    // Force boot of image 0:0 +    ee->next_boot = make_bootconfig(0, 0); +  } + +  // if next_boot is valid, try it +  if (plausible_bootconfig(ee->next_boot)) +    initial_boot_attempt(ee);	// no return + +  // if default_boot is valid, try it +  if (plausible_bootconfig(ee->default_boot)){ +    ee->next_boot = ee->default_boot; +    initial_boot_attempt(ee);	// no return +  } + +  // If we're here, we're in trouble.  Try all of them... +  for (int i = 0; i < 4; i++){ +    bootconfig_t bc = make_bootconfig(i, 0); +    if (plausible_bootconfig(bc)){ +      ee->next_boot = bc; +      initial_boot_attempt(ee);	// no return +    } +  } + +  // FIXME, try to find something we can load +  puts("\n!!! Failed to find a valid FPGA bitstream!\n\n"); + +  return 0; +} diff --git a/firmware/microblaze/usrp2p/bootloader/fw_bootloader.c b/firmware/microblaze/usrp2p/bootloader/fw_bootloader.c new file mode 100644 index 000000000..a2c32bf8e --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/fw_bootloader.c @@ -0,0 +1,50 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <memory_map.h> +#include <nonstdio.h> +#include <stdlib.h> +#include <bootconfig.h> +#include <bootconfig_private.h> +#include <bootloader_utils.h> +#include <hal_interrupts.h> + + +void hal_uart_init(void); +void spif_init(void); +void i2c_init(void); +void bootconfig_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ +  // nop stub +} + +int +main(int argc, char **argv) +{ +  hal_disable_ints();	// In case we got here via jmp 0x0 +  hal_uart_init(); +  i2c_init(); +  bootconfig_init();	// Must come after i2c_init. +  spif_init();		// Needed for get_flashdir. + +  load_firmware(); +} diff --git a/firmware/microblaze/usrp2p/bootloader/icap_test.c b/firmware/microblaze/usrp2p/bootloader/icap_test.c new file mode 100644 index 000000000..5feb9d014 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/icap_test.c @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <hal_io.h> +#include <xilinx_s3_icap.h> +#include <nonstdio.h> + +void delay(uint32_t t) { +	while(t-- != 0) asm("NOP"); +} + + +int main(int argc, char *argv[]) { +	pic_init(); +	hal_uart_init(); +	puts("\nStarting delay...\n"); + +	output_regs->leds = 0xFF; +	delay(4000000); +	output_regs->leds = 0x00; +	delay(4000000); + +	puts("Rebooting FPGA to 0x00000000\n"); +	icap_reload_fpga((uint32_t)0x00000000); + +	return 0; +} diff --git a/firmware/microblaze/usrp2p/bootloader/init_bootloader.c b/firmware/microblaze/usrp2p/bootloader/init_bootloader.c new file mode 100644 index 000000000..2bbbd405e --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/init_bootloader.c @@ -0,0 +1,129 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <nonstdio.h> +#include <hal_io.h> +#include <xilinx_s3_icap.h> +#include <spi_flash.h> +#include <spi_flash_private.h> +//#include <clocks.h> +#include <ihex.h> +#include <bootloader_utils.h> +#include <string.h> +#include <hal_uart.h> +#include <i2c.h> +#include "usrp2/fw_common.h" + +bool find_safe_booted_flag(void); +void set_safe_booted_flag(bool flag); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ +  // nop stub +} + +bool find_safe_booted_flag(void) { +	unsigned char flag_byte; +	eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_BOOTLOADER_FLAGS, &flag_byte, 1); +	return (flag_byte == 0x5E); +} + +void set_safe_booted_flag(bool flag) { +	unsigned char flag_byte = flag ? 0x5E : 0xDC; +	eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_BOOTLOADER_FLAGS, &flag_byte, 1); +} + + +void load_ihex(void) { //simple IHEX parser to load proper records into RAM. loads program when it receives end of record. +	char buf[128]; //input data buffer +	uint8_t ihx[32]; //ihex data buffer + +	ihex_record_t ihex_record; +	ihex_record.data = ihx; + +	while(1) { +		gets(buf); + +		if(!ihex_parse(buf, &ihex_record)) { //RAM data record is valid +			if(ihex_record.addr >= RAM_BASE) { //it's expecting to see FULLY RELOCATED IHX RECORDS. every address referenced to 0x8000, including vectors. +				memcpy((void *) (ihex_record.addr), ihex_record.data, ihex_record.length); +				puts("OK"); +			} else if(ihex_record.type == 1) { //end of record +				puts("OK"); +				//load main firmware +				start_program(RAM_BASE); +				puts("ERROR: main image returned! Back in IHEX load mode."); +			} else puts("NOK"); //RAM loads do not support extended segment address records (04) -- upper 16 bits are always "0". +		} else puts("NOK"); +	} +} + +void delay(uint32_t t) { +	while(t-- != 0) asm("NOP"); +} + +int main(int argc, char *argv[]) { +  hal_disable_ints();	// In case we got here via jmp 0x0 +	output_regs->leds = 0xFF; +	delay(500000); +	output_regs->leds = 0x00; +	hal_uart_init(); +	spif_init(); +	i2c_init(); //for EEPROM +	puts("USRP2+ bootloader\n"); +	 +	bool production_image = find_safe_booted_flag(); +	if(production_image) set_safe_booted_flag(0); //we're the production image, so we clear the flag for the next boot + +	if(BUTTON_PUSHED) { //see memory_map.h +		puts("Starting USRP2+ in safe mode."); +		if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { +				spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); +				start_program(RAM_BASE); +				puts("ERROR: return from main program! This should never happen!"); +				icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); +			} else { +				puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); +				//puts("ERROR: no safe firmware image available. I am a brick."); +				load_ihex(); +			} +	} + +	if(!production_image) { +		puts("Checking for valid production FPGA image..."); +		if(is_valid_fpga_image(PROD_FPGA_IMAGE_LOCATION_ADDR)) { +			puts("Valid production FPGA image found. Attempting to boot."); +			set_safe_booted_flag(1); +			delay(30000); //so serial output can finish +			icap_reload_fpga(PROD_FPGA_IMAGE_LOCATION_ADDR); +		} +		puts("No valid production FPGA image found.\nAttempting to load production firmware..."); +	} +	if(is_valid_fw_image(PROD_FW_IMAGE_LOCATION_ADDR)) { +		puts("Valid production firmware found. Loading..."); +		spi_flash_read(PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); +		start_program(RAM_BASE); +		puts("ERROR: Return from main program! This should never happen!"); +		//if this happens, though, the safest thing to do is reboot the whole FPGA and start over. +		icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); +		return 1; +	} +	puts("No valid production firmware found. Trying safe firmware..."); +	if(is_valid_fw_image(SAFE_FW_IMAGE_LOCATION_ADDR)) { +		spi_flash_read(SAFE_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES, (void *)RAM_BASE); +		start_program(RAM_BASE); +		puts("ERROR: return from main program! This should never happen!"); +		icap_reload_fpga(SAFE_FPGA_IMAGE_LOCATION_ADDR); +		return 1; +	} +	puts("ERROR: no safe firmware image available. I am a brick. Feel free to load IHEX to RAM."); +	load_ihex(); + +	return 0; +} diff --git a/firmware/microblaze/usrp2p/bootloader/serial_loader_burner.c b/firmware/microblaze/usrp2p/bootloader/serial_loader_burner.c new file mode 100644 index 000000000..4ac4df454 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/serial_loader_burner.c @@ -0,0 +1,49 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <hal_io.h> +#include <nonstdio.h> +#include <mdelay.h> +#include <gdbstub2.h> + +void hal_uart_init(void); +void spif_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ +  // nop stub +} + +int +main(int argc, char **argv) +{ +  hal_uart_init(); +  spif_init(); + +  sr_leds->leds =  0; +  mdelay(100); +  sr_leds->leds = ~0; +  mdelay(100); +  sr_leds->leds =  0; + +  puts("\n\n>>> stage1: serial_loader_burner <<<"); + +  gdbstub2_main_loop(); +} diff --git a/firmware/microblaze/usrp2p/bootloader/spi_bootloader.c b/firmware/microblaze/usrp2p/bootloader/spi_bootloader.c new file mode 100644 index 000000000..678e66cf7 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/spi_bootloader.c @@ -0,0 +1,134 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <hal_io.h> +#include <nonstdio.h> +#include <mdelay.h> +#include <spi_flash.h> +#include <quadradio/flashdir.h> +#include <quadradio/simple_binary_format.h> +#include <stdlib.h> + + +void hal_uart_init(void); +void spif_init(void); + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ +  // nop stub +} + +static void +error(int e) +{ +  putstr("ERR"); +  puthex8(e); +  newline(); +} + +static void +load(uint32_t flash_addr, uint32_t ram_addr, uint32_t size) +{ +  spi_flash_read(flash_addr, size, (void *) ram_addr); +} + +static bool +load_from_slot(const struct flashdir *fd, int fw_slot) +{ +    putstr("Loading f/w image "); +    putchar('0' + fw_slot); +    putstr("... "); + +    if (fw_slot >= fd->fw_nslots){ +      error(1); +      return false; +    } + +    int slot = fw_slot + fd->fw_slot0; +    if (fd->slot[slot].start == 0 || fd->slot[slot].start == 0xffff +	|| fd->slot[slot].len == 0 || fd->slot[slot].len == 0xffff){ +      error(2); +      return false; +    } +     +    uint32_t sbf_base = fd->slot[slot].start << spi_flash_log2_sector_size(); +    uint32_t sbf_len  = fd->slot[slot].len << spi_flash_log2_sector_size(); +    uint32_t sbf_offset = 0; + +    struct sbf_header sbf; +    spi_flash_read(sbf_base, sizeof(struct sbf_header), &sbf); +    if (sbf.magic != SBF_MAGIC || sbf.nsections > SBF_MAX_SECTIONS){ +      error(3); +      return false; +    } +    sbf_offset += sizeof(struct sbf_header); + +    unsigned int i; +    for (i = 0; i < sbf.nsections; i++){ +      if (sbf_offset + sbf.sec_desc[i].length > sbf_len){ +	error(4); +	return false; +      } +      load(sbf_offset + sbf_base, +	   sbf.sec_desc[i].target_addr, +	   sbf.sec_desc[i].length); +      sbf_offset += sbf.sec_desc[i].length; +    } +    putstr("Done!"); + +    typedef void (*fptr_t)(void); +    (*(fptr_t) sbf.entry)();		// almost certainly no return + +    return true; +} + +int +main(int argc, char **argv) +{ +  hal_uart_init(); +  spif_init(); + +  sr_leds->leds =  0; +  mdelay(100); +  sr_leds->leds = ~0; +  mdelay(100); +  sr_leds->leds =  0; + +  putstr("\n>>> spi_bootloader <<<\n"); + +  const struct flashdir *fd = get_flashdir(); +  if (fd == 0) +    abort(); + +  while(1){ +    int sw; +    int fw_slot; +     +    sw = readback->switches; +    fw_slot = sw & 0x7; + +    if (!load_from_slot(fd, fw_slot)){ +      if (fw_slot != 0){ +	putstr("Falling back to slot 0\n"); +	load_from_slot(fd, 0); +      } +    } +  } +} diff --git a/firmware/microblaze/usrp2p/bootloader/u2p2-rom.ld b/firmware/microblaze/usrp2p/bootloader/u2p2-rom.ld new file mode 100644 index 000000000..4c9eaa8e5 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/u2p2-rom.ld @@ -0,0 +1,190 @@ +/* + * Same as default, but with bss and stack moved to top 2K of main ram + * Copied from qr-rom.ld + */ + +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf32-microblaze", "", "") +/*SEARCH_DIR("/home/eb/build/Xilinx_EDK_GNU_10.1i/mb/release/lin/mb/microblaze-xilinx-elf/lib");*/ + + +ENTRY(_start) +_TEXT_START_ADDR = DEFINED(_TEXT_START_ADDR) ? _TEXT_START_ADDR : 0x50; +_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x0; +_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x400; +_BSS_START_ADDR = DEFINED(_BSS_START_ADDR) ? _BSS_START_ADDR : 0xF800; +SECTIONS +{ +  .vectors.reset 0x0 : { KEEP (*(.vectors.reset)) } = 0 +  .vectors.sw_exception 0x8 : { KEEP (*(.vectors.sw_exception)) } = 0 +  .vectors.interrupt 0x10 : { KEEP (*(.vectors.interrupt)) } = 0 +  .vectors.debug_sw_break 0x18 : { KEEP (*(.vectors.debug_sw_break)) } = 0 +  .vectors.hw_exception 0x20 : { KEEP (*(.vectors.hw_exception)) } = 0 +  . = _TEXT_START_ADDR; +   _ftext  =  .; +  .text : { +    *(.text) +    *(.text.*) +    *(.gnu.linkonce.t.*) +  } +   _etext  =  .; +  .init : { KEEP (*(.init))	} =0 +  .fini : { KEEP (*(.fini))	} =0 +  PROVIDE (__CTOR_LIST__ = .); +  PROVIDE (___CTOR_LIST__ = .); +  .ctors   : +  { +    /* gcc uses crtbegin.o to find the start of +       the constructors, so we make sure it is +       first.  Because this is a wildcard, it +       doesn't matter if the user does not +       actually link against crtbegin.o; the +       linker won't look for a file to match a +       wildcard.  The wildcard also means that it +       doesn't matter which directory crtbegin.o +       is in.  */ +    KEEP (*crtbegin.o(.ctors)) +    /* We don't want to include the .ctor section from +       from the crtend.o file until after the sorted ctors. +       The .ctor section from the crtend file contains the +       end of ctors marker and it must be last */ +    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) +    KEEP (*(SORT(.ctors.*))) +    KEEP (*(.ctors)) +  } +  PROVIDE (__CTOR_END__ = .); +  PROVIDE (___CTOR_END__ = .); +  PROVIDE (__DTOR_LIST__ = .); +  PROVIDE (___DTOR_LIST__ = .); +   .dtors         : +  { +    KEEP (*crtbegin.o(.dtors)) +    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) +    KEEP (*(SORT(.dtors.*))) +    KEEP (*(.dtors)) +  } +  PROVIDE (__DTOR_END__ = .); +  PROVIDE (___DTOR_END__ = .); +   . = ALIGN(4); +    _frodata = . ; +  .rodata : { +    *(.rodata) +    *(.rodata.*) +    *(.gnu.linkonce.r.*) +    CONSTRUCTORS; /* Is this needed? */ +  } +   _erodata = .; +  /* Alignments by 8 to ensure that _SDA2_BASE_ on a word boundary */ +  /* Note that .sdata2 and .sbss2 must be contiguous */ +  . = ALIGN(8); +   _ssrw = .; +  .sdata2 : { +    *(.sdata2) +    *(.sdata2.*) +    *(.gnu.linkonce.s2.*) +  } +  . = ALIGN(4); +  .sbss2 : { +    PROVIDE (__sbss2_start = .); +    *(.sbss2) +    *(.sbss2.*) +    *(.gnu.linkonce.sb2.*) +    PROVIDE (__sbss2_end = .); +  } +  . = ALIGN(8); +   _essrw = .; +   _ssrw_size = _essrw - _ssrw; +   PROVIDE (_SDA2_BASE_ = _ssrw + (_ssrw_size / 2 )); +   . = ALIGN(4); +   _fdata = .; +  .data : { +    *(.data) +    *(.gnu.linkonce.d.*) +    CONSTRUCTORS; /* Is this needed? */ +  } +   _edata = . ; +   /* Added to handle pic code */ +  .got : { +    *(.got) +  } +  .got1 : { +    *(.got1) +  } +  .got2 : { +    *(.got2) +  } +  /* Added by Sathya to handle C++ exceptions */ +  .eh_frame : { +    *(.eh_frame) +  } +  .jcr : { +    *(.jcr) +  } +  .gcc_except_table : { +    *(.gcc_except_table) +  } +  /* Alignments by 8 to ensure that _SDA_BASE_ on a word boundary */ +  /* Note that .sdata and .sbss must be contiguous */ +  . = ALIGN(8); +   _ssro = .; +  .sdata : { +    *(.sdata) +    *(.sdata.*) +    *(.gnu.linkonce.s.*) +  } +  . = ALIGN(4); +  .sbss : { +    PROVIDE (__sbss_start = .); +    *(.sbss) +    *(.sbss.*) +    *(.gnu.linkonce.sb.*) +    PROVIDE (__sbss_end = .); +  } +  . = ALIGN(8); +   _essro = .; +   _ssro_size = _essro - _ssro; +  PROVIDE (_SDA_BASE_ = _ssro + (_ssro_size / 2 )); +   . = _BSS_START_ADDR; +   . = ALIGN(4); +   _fbss = .; +  .bss : { +    PROVIDE (__bss_start = .); +    *(.bss) +    *(.bss.*) +    *(.gnu.linkonce.b.*) +    *(COMMON) +    . = ALIGN(4); +    PROVIDE (__bss_end = .); +  } +   . = ALIGN(4); +  .heap : { +     _heap = .; +     _heap_start = .; +     . += _HEAP_SIZE; +     _heap_end = .; +  } +  _end = .; +   . = ALIGN(4); +   . = 0xFFF0; +  .stack : { +  /* +     _stack_end = .; +     . += _STACK_SIZE; +     . = ALIGN(8); +     _stack = .; +     _end = .; +   */ +   _stack_end = .; +   _stack = .; +  } +  .tdata : { +    *(.tdata) +    *(.tdata.*) +    *(.gnu.linkonce.td.*) +  } +  .tbss : { +    *(.tbss) +    *(.tbss.*) +    *(.gnu.linkonce.tb.*) +  } +} diff --git a/firmware/microblaze/usrp2p/bootloader_utils.c b/firmware/microblaze/usrp2p/bootloader_utils.c new file mode 100644 index 000000000..fadd225bb --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader_utils.c @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +//contains routines for loading programs from Flash. depends on Flash libraries. +//also contains routines for reading / writing EEPROM flags for the bootloader +#include <stdbool.h> +#include <string.h> +#include <bootloader_utils.h> +#include <spi_flash.h> + +int is_valid_fpga_image(uint32_t addr) { +	uint8_t imgbuf[64]; +	spi_flash_read(addr, 64, imgbuf); +	//we're just looking for leading 0xFF padding, followed by the sync bytes 0xAA 0x99 +	int i = 0; +	for(i; i<63; i++) { +		if(imgbuf[i] == 0xFF) continue; +		if(imgbuf[i] == 0xAA && imgbuf[i+1] == 0x99) return 1; +	} +	 +	return 0; +} + +int is_valid_fw_image(uint32_t addr) { +	static const uint8_t fwheader[] = {0xB0, 0x00, 0x00, 0x00, 0xB8, 0x08}; //just lookin for a jump to anywhere located at the reset vector +	uint8_t buf[12]; +	spi_flash_read(addr, 6, buf); +	return memcmp(buf, fwheader, 6) == 0; +} + +void start_program(uint32_t addr) +{ +	memcpy(0x00000000, addr+0x00000000, 36); //copy the whole vector table, with the reset vector, into boot RAM +	typedef void (*fptr_t)(void); +	(*(fptr_t) 0x00000000)();	// most likely no return +} diff --git a/firmware/microblaze/usrp2p/bootloader_utils.h b/firmware/microblaze/usrp2p/bootloader_utils.h new file mode 100644 index 000000000..f597c0113 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader_utils.h @@ -0,0 +1,22 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <stdint.h> + +//we're working in bytes and byte addresses so we can run the same code with Flash chips of different sector sizes. +//it's really 1463736, but rounded up to 1.5MB +#define FPGA_IMAGE_SIZE_BYTES 1572864 +//instead of 32K, we write 31K because we're using the top 1K for stack space! +#define FW_IMAGE_SIZE_BYTES 31744 + +#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 +#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 +#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 +#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 + +int is_valid_fpga_image(uint32_t addr); +int is_valid_fw_image(uint32_t addr); +void start_program(uint32_t addr); diff --git a/firmware/microblaze/usrp2p/eth_phy.h b/firmware/microblaze/usrp2p/eth_phy.h new file mode 100644 index 000000000..d233e96e8 --- /dev/null +++ b/firmware/microblaze/usrp2p/eth_phy.h @@ -0,0 +1,235 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Much of this was extracted from the Linux e1000_hw.h file */ + +#ifndef INCLUDED_ETH_PHY_H +#define INCLUDED_ETH_PHY_H + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ + +#define PHY_CTRL         0x00 /* Control Register */ +#define PHY_STATUS       0x01 /* Status Regiser */ +#define PHY_ID1          0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2          0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV  0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY   0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP  0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL   0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS   0x0F /* Extended Status Reg */ + +/* PHY 1000 MII Register additions in ET1011C */ +#define PHY_INT_MASK     24 +#define PHY_INT_STATUS   25 +#define PHY_PHY_STATUS   26 +#define PHY_LED2         28 + +/* Bit definitions for some of the registers above */ + +/* PHY Control Register (PHY_CTRL) */ +#define MII_CR_SPEED_SELECT_MSB 0x0040  /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080  /* Collision test enable */ +#define MII_CR_FULL_DUPLEX      0x0100  /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200  /* Restart auto negotiation */ +#define MII_CR_ISOLATE          0x0400  /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN       0x0800  /* Power down */ +#define MII_CR_AUTO_NEG_EN      0x1000  /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000  /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK         0x4000  /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET            0x8000  /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register (PHY_STATUS) */ +#define MII_SR_EXTENDED_CAPS     0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT     0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS       0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS      0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT      0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE  0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS   0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS     0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS     0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS       0x0800 /* 10T   Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS       0x1000 /* 10T   Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS      0x2000 /* 100X  Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS      0x4000 /* 100X  Full Duplex Capable */ +#define MII_SR_100T4_CAPS        0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register (PHY_AUTONEG_ADV) */ +#define NWAY_AR_SELECTOR_FIELD 0x0001   /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS    0x0020   /* 10T   Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS    0x0040   /* 10T   Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS  0x0080   /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS  0x0100   /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS     0x0200   /* 100T4 Capable */ +#define NWAY_AR_PAUSE          0x0400   /* Pause operation desired */ +#define NWAY_AR_ASM_DIR        0x0800   /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT   0x2000   /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE      0x8000   /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) (PHY_LP_ABILITY) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS    0x0020 /* LP is 10T   Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS    0x0040 /* LP is 10T   Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS  0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS  0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS     0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE          0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR        0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT   0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE    0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE      0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register (PHY_AUTONEG_EXP) */ +#define NWAY_ER_LP_NWAY_CAPS      0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD          0x0002 /* LP is 10T   Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS    0x0004 /* LP is 10T   Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT  0x0010 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register (PHY_NEXT_PAGE_TX) */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE         0x0800 /* Toggles between exchanges +                                    * of different NP +                                    */ +#define NPTX_ACKNOWLDGE2    0x1000 /* 1 = will comply with msg +                                    * 0 = cannot comply with msg +                                    */ +#define NPTX_MSG_PAGE       0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE      0x8000 /* 1 = addition NP will follow +                                    * 0 = sending last NP +                                    */ + +/* Link Partner Next Page Register (PHY_LP_NEXT_PAGE) */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE         0x0800 /* Toggles between exchanges +                                       * of different NP +                                       */ +#define LP_RNPR_ACKNOWLDGE2    0x1000 /* 1 = will comply with msg +                                       * 0 = cannot comply with msg +                                       */ +#define LP_RNPR_MSG_PAGE       0x2000  /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE     0x4000  /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE      0x8000  /* 1 = addition NP will follow +                                        * 0 = sending last NP +                                        */ + +/* 1000BASE-T Control Register (PHY_1000T_CTRL) */ +#define CR_1000T_ASYM_PAUSE      0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS         0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS         0x0200 /* Advertise 1000T FD capability  */ +#define CR_1000T_REPEATER_DTE    0x0400 /* 1=Repeater/switch device port */ +                                        /* 0=DTE device */ +#define CR_1000T_MS_VALUE        0x0800 /* 1=Configure PHY as Master */ +                                        /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE       0x1000 /* 1=Master/Slave manual config value */ +                                        /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1     0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2     0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3     0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4     0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register (PHY_1000T_STATUS) */ +#define SR_1000T_IDLE_ERROR_CNT   0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR   0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS       0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS       0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS  0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES    0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT  0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT          12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT           13 +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT    5 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20            20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100           100 + +/* Extended Status Register (PHY_EXT_STATUS) */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK   0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0      /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE  0x0010 /* register 11h bit 4 */ +                                      /* (0=enable, 1=disable) */ + +/* PHY Status Register (PHY_PHY_STATUS) */ +#define PHYSTAT_ASYMMETRIC   (1 << 0) +#define PHYSTAT_PAUSE        (1 << 1) +#define PHYSTAT_AUTONEG_EN   (1 << 2) +#define PHYSTAT_COLLISION    (1 << 3) +#define PHYSTAT_RXSTAT       (1 << 4) +#define PHYSTAT_TXSTAT       (1 << 5) +#define PHYSTAT_LINK         (1 << 6) +#define PHYSTAT_DUPLEX       (1 << 7) +#define PHYSTAT_SPEED_MASK   ((1 << 8) | (1 << 9)) +#define PHYSTAT_SPEED_1000   (1 << 9) +#define PHYSTAT_SPEED_100    (1 << 8) +#define PHYSTAT_SPEED_10     0 +#define PHYSTAT_POLARITY     (1 << 10) +#define PHYSTAT_MDIX         (1 << 11) +#define PHYSTAT_AUTONEG_STAT (1 << 12) +#define PHYSTAT_STANDBY      (1 << 13) + +/* Interrupt status, mask and clear regs (PHY_INT_{STATUS,MASK,CLEAR}) */ +#define PHY_INT_ENABLE         (1 << 0) +#define PHY_INT_DOWNSHIFT      (1 << 1) +#define PHY_INT_LINK_STATUS_CHANGE  (1 << 2) +#define PHY_INT_RX_STATUS_CHANGE (1 << 3) +#define PHY_INT_FIFO_ERROR     (1 << 4) +#define PHY_INT_ERR_CTR_FULL   (1 << 5) +#define PHY_INT_NEXT_PAGE_RX   (1 << 6) +#define PHY_INT_CRC_ERROR      (1 << 7) +#define PHY_INT_AUTONEG_STATUS_CHANGE (1 << 8) +#define PHY_INT_MDIO_SYNC_LOST (1 << 9) +#define PHY_INT_TDR_IP_PHONE   (1 << 10) + +/* PHY LED status register 2 (used for controlling link LED for activity light) */ +#define PHY_LED_TXRX_LSB           12 +#define PHY_LED_LINK_LSB           8 +#define PHY_LED_100_LSB            4 +#define PHY_LED_1000_LSB           0 + +#define LED_1000                   0 +#define LED_100_TX                 1 +#define LED_10                     2 +#define LED_1000_ON_100_BLINK      3 +#define LED_LINK                   4 +#define LED_TX                     5 +#define LED_RX                     6 +#define LED_ACTIVITY               7 +#define LED_FULLDUPLEX             8 +#define LED_COLLISION              9 +#define LED_LINK_ON_ACTIVITY_BLINK 10 +#define LED_LINK_ON_RX_BLINK       11 +#define LED_FULL_DUPLEX_ON_COLLISION_BLINK 12 +#define LED_BLINK                  13 +#define LED_ON                     14 +#define LED_OFF                    15 + + +#endif /* INCLUDED_ETH_PHY_H */ diff --git a/firmware/microblaze/usrp2p/ethernet.c b/firmware/microblaze/usrp2p/ethernet.c new file mode 100644 index 000000000..36d6a17ca --- /dev/null +++ b/firmware/microblaze/usrp2p/ethernet.c @@ -0,0 +1,399 @@ +/* + * Copyright 2007 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, see <http://www.gnu.org/licenses/>. + */ + +//Changes for USRP2P: status registers different (ethernet.h) + +#include "ethernet.h" +#include "memory_map.h" +#include "eth_phy.h" +#include <eth_mac.h> +#include <eth_mac_regs.h> +#include <pic.h> +#include <hal_io.h> +#include <nonstdio.h> +#include <stdbool.h> +#include <i2c.h> +#include "usrp2/fw_common.h" + +#define VERBOSE 0 + +static ethernet_t ed_state; +static ethernet_link_changed_callback_t ed_callback = 0; + +void  +ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback) +{ +  ed_callback = new_callback; +} + + +static void +ed_set_mac_speed(int speed) +{ +  printf("Speed set to %d\n",speed); +  /* +  switch(speed){ +  case 10: +    eth_mac->speed = 1; +    break; +  case 100: +    eth_mac->speed = 2; +    break; +  case 1000: +    eth_mac->speed = 4; +    break; +  default: +    break; +  } +  */ +} + +static void +ed_link_up(int speed) +{ +  // putstr("ed_link_up: "); puthex16_nl(speed); + +  ed_set_mac_speed(speed); + +	//turn on link LED for USRP2P +	hal_set_leds(LED_RJ45, LED_RJ45); + + +  if (ed_callback)		// fire link changed callback +    (*ed_callback)(speed); +} + +static void +ed_link_down(void) +{ +  // putstr("ed_link_down\n"); + +	//turn off link LED for USRP2P +	hal_set_leds(0, LED_RJ45); + +  if (ed_callback)		// fire link changed callback +    (*ed_callback)(0); +} + + +static void +ed_link_speed_change(int speed) +{ +  ed_link_down(); +  ed_link_up(speed); +} + +static void +print_flow_control(int flow_control) +{ +  static const char *flow_control_msg[4] = { +    "NONE", "WE_TX", "WE_RX", "SYMMETRIC" +  }; +  putstr("ethernet flow control: "); +  puts(flow_control_msg[flow_control & 0x3]); +} + +static void +check_flow_control_resolution(void) +{ +  static const unsigned char table[16] = { +    // index = {local_asm, local_pause, partner_asm, partner_pause} +    FC_NONE,  FC_NONE,  FC_NONE,  FC_NONE, +    FC_NONE,  FC_SYMM,  FC_NONE,  FC_SYMM, +    FC_NONE,  FC_NONE,  FC_NONE,  FC_WE_TX, +    FC_NONE,  FC_SYMM,  FC_WE_RX, FC_SYMM +  }; + +  int us = eth_mac_miim_read(PHY_AUTONEG_ADV); +  int lp = eth_mac_miim_read(PHY_LP_ABILITY); +  int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3); +  ed_state.flow_control = table[index]; + +  if (1) +    print_flow_control(ed_state.flow_control); +} + +/* + * Read the PHY state register to determine link state and speed + */ +static void +ed_check_phy_state(void) +{ +  int phystat = eth_mac_miim_read(PHY_PHY_STATUS); +  eth_link_state_t new_state = LS_UNKNOWN; +  int new_speed = S_UNKNOWN; + +  if (VERBOSE){ +    putstr("PHYSTAT: "); +    puthex16_nl(phystat); +  } + +  if (phystat & PHYSTAT_LINK){		// link's up +    if (VERBOSE) +      puts("  LINK_GOOD"); + +    new_state = LS_UP; +    switch (phystat & PHYSTAT_SPEED_MASK){ +    case PHYSTAT_SPEED_10: +      new_speed = 10; +      break; +       +    case PHYSTAT_SPEED_100: +      new_speed = 100; +      break; +       +    case PHYSTAT_SPEED_1000: +      new_speed = 1000; +      break; + +    default: +      new_speed = S_UNKNOWN; +      break; +    } + +    check_flow_control_resolution(); +  } +  else {				// link's down +    if (VERBOSE) +      puts("  NOT LINK_GOOD"); +     +    new_state = LS_DOWN; +    new_speed = S_UNKNOWN; +  } + +  if (new_state != ed_state.link_state){ +    ed_state.link_state = new_state;		// remember new state +    if (new_state == LS_UP) +      ed_link_up(new_speed); +    else if (new_state == LS_DOWN) +      ed_link_down(); +  } +  else if (new_state == LS_UP && new_speed != ed_state.link_speed){ +    ed_state.link_speed = new_speed;		// remember new speed +    ed_link_speed_change(new_speed); +  } +} + +/* + * This is fired when the ethernet PHY state changes + */ +static void +eth_phy_irq_handler(unsigned irq) +{ +  ed_check_phy_state(); +  eth_mac_miim_read(PHY_INT_STATUS); +//  eth_mac_miim_write(PHY_INT_CLEAR, ~0);	// clear all ints +} + +void +ethernet_init(void) +{ +  eth_mac_init(ethernet_mac_addr()); + +  ed_state.link_state = LS_UNKNOWN; +  ed_state.link_speed = S_UNKNOWN; + +  // initialize MAC registers +  //  eth_mac->tx_hwmark = 0x1e; +  //eth_mac->tx_lwmark = 0x19; + +  //eth_mac->crc_chk_en = 1; +  //eth_mac->rx_max_length = 2048; + +  // configure PAUSE frame stuff +  //eth_mac->tx_pause_en = 1;		// pay attn to pause frames sent to us + +  //eth_mac->pause_quanta_set = 38;	// a bit more than 1 max frame 16kb/512 + fudge +  //eth_mac->pause_frame_send_en = 1;	// enable sending pause frames + + +  // setup PHY to interrupt on changes + +  unsigned mask = +    (PHY_INT_ENABLE       //master interrupt enable +     | PHY_INT_LINK_STATUS_CHANGE +     | PHY_INT_RX_STATUS_CHANGE +     ); + +  eth_mac_miim_read(PHY_INT_STATUS); //clear interrupts +  eth_mac_miim_write(PHY_INT_MASK, mask);	// enable the ones we want + +	//set the LED behavior to activity instead of link +	unsigned led = (LED_ACTIVITY << PHY_LED_LINK_LSB) | (LED_TX << PHY_LED_TXRX_LSB); +	eth_mac_miim_write(PHY_LED2, led); + +  pic_register_handler(IRQ_PHY, eth_phy_irq_handler); + +  // Advertise our flow control configuation. +  // +  // We and the link partner each specify two bits in the base page +  // related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR. +  // The bits say what a device is "willing" to do, not what may actually +  // happen as a result of the negotiation.  There are 4 cases: +  // +  // PAUSE  ASM_DIR +  // +  //  0        0        I have no flow control capability. +  // +  //  1        0        I both assert and respond to flow control. +  // +  //  0        1        I assert flow control, but cannot respond.  That is, +  //                    I want to be able to send PAUSE frames, but will ignore any +  //		 	you send to me.  (This is our configuration.) +  // +  //  1        1        I can both assert and respond to flow control AND I am willing +  //                    to operate symmetrically OR asymmetrically in EITHER direction. +  //                    (We hope the link partner advertises this, otherwise we don't +  //			get what we want.) + +  int t = eth_mac_miim_read(PHY_AUTONEG_ADV); +  t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR); +  t |= NWAY_AR_ASM_DIR; + +  // Say we can't to 10BASE-T or 100BASE-TX, half or full duplex +  t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS); + +  eth_mac_miim_write(PHY_AUTONEG_ADV, t); +  int r = eth_mac_miim_read(PHY_AUTONEG_ADV);  		// DEBUG, read back +  if (t != r){ +    printf("PHY_AUTONEG_ADV: wrote 0x%x, got 0x%x\n", t, r); +  } + +  // Restart autonegotation.   +  // We want to ensure that we're advertising our PAUSE capabilities. +  t = eth_mac_miim_read(PHY_CTRL); +  eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG); +} + +static bool  +unprogrammed(const void *t, size_t len) +{ +  int i; +  uint8_t *p = (uint8_t *)t; +  bool all_zeros = true; +  bool all_ones =  true; +  for (i = 0; i < len; i++){ +    all_zeros &= p[i] == 0x00; +    all_ones  &= p[i] == 0xff; +  } +  return all_ones | all_zeros; +} + +//////////////////// MAC Addr Stuff /////////////////////// +/* +static int8_t src_mac_addr_initialized = false; +static eth_mac_addr_t src_mac_addr = {{ +    0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff +  }}; + +const eth_mac_addr_t * +ethernet_mac_addr(void) +{ +  if (!src_mac_addr_initialized){    // fetch from eeprom +    src_mac_addr_initialized = true; + +    // if we're simulating, don't read the EEPROM model, it's REALLY slow +    if (hwconfig_simulation_p()) +      return &src_mac_addr; +     +    eth_mac_addr_t tmp; +    bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, &tmp, sizeof(tmp)); +    if (!ok || unprogrammed(&tmp, sizeof(tmp))){ +      // use the default +    } +    else +      src_mac_addr = tmp; +  } + +  return &src_mac_addr; +} + +bool +ethernet_set_mac_addr(const eth_mac_addr_t *t) +{ +  bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_t)); +  if (ok){ +    src_mac_addr = *t; +    src_mac_addr_initialized = true; +    //eth_mac_set_addr(t); //this breaks the link +  } + +  return ok; +} + +//////////////////// IP Addr Stuff /////////////////////// + +static int8_t src_ip_addr_initialized = false; +static struct ip_addr src_ip_addr = { +    (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) +}; + + +const struct ip_addr *get_ip_addr(void) +{ +  if (!src_ip_addr_initialized){    // fetch from eeprom +    src_ip_addr_initialized = true; + +    // if we're simulating, don't read the EEPROM model, it's REALLY slow +    if (hwconfig_simulation_p()) +      return &src_ip_addr; +     +    struct ip_addr tmp; +    bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, &tmp, sizeof(tmp)); +    if (!ok || unprogrammed(&tmp, sizeof(tmp))){ +      // use the default +    } +    else +      src_ip_addr = tmp; +  } + +  return &src_ip_addr; +} + +bool set_ip_addr(const struct ip_addr *t){ +  bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); +  if (ok){ +    src_ip_addr = *t; +    src_ip_addr_initialized = true; +  } + +  return ok; +} +*/ +int +ethernet_check_errors(void) +{ +  // these registers are reset when read +   +  int	r = 0; +  /* +  if (eth_mac_read_rmon(0x05) != 0) +    r |= RME_RX_CRC; +  if (eth_mac_read_rmon(0x06) != 0) +    r |= RME_RX_FIFO_FULL; +  if (eth_mac_read_rmon(0x07) != 0) +    r |= RME_RX_2SHORT_2LONG; +   +  if (eth_mac_read_rmon(0x25) != 0) +    r |= RME_TX_JAM_DROP; +  if (eth_mac_read_rmon(0x26) != 0) +    r |= RME_TX_FIFO_UNDER; +  if (eth_mac_read_rmon(0x27) != 0) +    r |= RME_TX_FIFO_OVER; +  */ +  return r; +} diff --git a/firmware/microblaze/usrp2p/memory_map.h b/firmware/microblaze/usrp2p/memory_map.h new file mode 100644 index 000000000..8d0d0c365 --- /dev/null +++ b/firmware/microblaze/usrp2p/memory_map.h @@ -0,0 +1,843 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008,2009 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, see <http://www.gnu.org/licenses/>. + */ + +/* Overall Memory Map + *   0000-FFFF  64K    RAM space + * + *   0000-1FFF  8K     Boot RAM + *   2000-5FFF  16K    Buffer pool + *   6000-7FFF  8K     Peripherals + *   8000-FFFF  32K    Main System RAM + + +From u2plus_core.v: +wb_1master #(.decode_w(8), +.s0_addr(8'b0000_0000),.s0_mask(8'b1110_0000),  // 0-8K, Boot RAM +.s1_addr(8'b0100_0000),.s1_mask(8'b1100_0000),  // 16K-32K, Buffer Pool +.s2_addr(8'b0011_0000),.s2_mask(8'b1111_1111),  // SPI 0x3000 +.s3_addr(8'b0011_0001),.s3_mask(8'b1111_1111),  // I2C 0x3100 +.s4_addr(8'b0011_0010),.s4_mask(8'b1111_1111),  // GPIO 0x3200 +.s5_addr(8'b0011_0011),.s5_mask(8'b1111_1111),  // Readback 0x3300 +.s6_addr(8'b0011_0100),.s6_mask(8'b1111_1111),  // Ethernet MAC 0x3400 +.s7_addr(8'b0010_0000),.s7_mask(8'b1111_0000),  // 8-12K, Settings Bus (only uses 1K) 0x2000-0x2FFF +.s8_addr(8'b0011_0101),.s8_mask(8'b1111_1111),  // PIC 0x3500 +.s9_addr(8'b0011_0110),.s9_mask(8'b1111_1111),  // Unused 0x3600 +.sa_addr(8'b0011_0111),.sa_mask(8'b1111_1111),  // UART 0x3700 +.sb_addr(8'b0011_1000),.sb_mask(8'b1111_1111),  // ATR 0x3800 +.sc_addr(8'b0011_1001),.sc_mask(8'b1111_1111),  // Unused 0x3900 +.sd_addr(8'b0011_1010),.sd_mask(8'b1111_1111),  // ICAP 0x3A00 +.se_addr(8'b0011_1011),.se_mask(8'b1111_1111),  // SPI Flash 0x3B00 +.sf_addr(8'b1000_0000),.sf_mask(8'b1000_0000),  // 32-64K, Main RAM 0x8000-0xFFFF +               .dw(dw),.aw(aw),.sw(sw)) wb_1master + + */ + + +#ifndef INCLUDED_MEMORY_MAP_H +#define INCLUDED_MEMORY_MAP_H + +#include <stdint.h> + + +#define MASTER_CLK_RATE        100000000		// 100 MHz + + +//////////////////////////////////////////////////////////////// +// +//         Memory map for embedded wishbone bus +// +//////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////// +// Boot RAM, Slave 0 + +#define BOOTRAM_BASE 0x0000 + + +//////////////////////////////////////////////////////////////// +// Buffer Pool RAM, Slave 1 +// +// The buffers themselves are located in Slave 1, Buffer Pool RAM. +// The status registers are in Slave 5, Buffer Pool Status. +// The control register is in Slave 7, Settings Bus. + +#define BUFFER_POOL_RAM_BASE 0x4000 + +#define	NBUFFERS                8 +#define BP_NLINES	   0x0200	// number of 32-bit lines in a buffer +#define BP_LAST_LINE	(BP_NLINES - 1)	// last line in a buffer + +#define buffer_pool_ram \ +  ((uint32_t *) BUFFER_POOL_RAM_BASE) + +#define buffer_ram(n) (&buffer_pool_ram[(n) * BP_NLINES]) + + +///////////////////////////////////////////////////// +// SPI Core, Slave 2.  See core docs for more info +#define SPI_BASE 0x3000   // Base address (16-bit) is base peripheral addr + +typedef struct { +  volatile uint32_t	txrx0; +  volatile uint32_t	txrx1; +  volatile uint32_t	txrx2; +  volatile uint32_t	txrx3; +  volatile uint32_t	ctrl; +  volatile uint32_t	div; +  volatile uint32_t	ss; +} spi_regs_t; + +#define spi_regs ((spi_regs_t *) SPI_BASE) + + +// Masks for controlling different peripherals +#define SPI_SS_AD9510    1 +#define SPI_SS_AD9777    2 +#define SPI_SS_RX_DAC    4 +#define SPI_SS_RX_ADC    8 +#define SPI_SS_RX_DB    16 +#define SPI_SS_TX_DAC   32 +#define SPI_SS_TX_ADC   64 +#define SPI_SS_TX_DB   128 +#define SPI_SS_ADS62P44 256 + +// Masks for different parts of CTRL reg +#define SPI_CTRL_ASS      (1<<13) +#define SPI_CTRL_IE       (1<<12) +#define SPI_CTRL_LSB      (1<<11) +#define SPI_CTRL_TXNEG    (1<<10) +#define SPI_CTRL_RXNEG    (1<< 9) +#define SPI_CTRL_GO_BSY   (1<< 8) +#define SPI_CTRL_CHAR_LEN_MASK 0x7F + +//////////////////////////////////////////////// +// I2C, Slave 3 +// See Wishbone I2C-Master Core Specification. + +#define I2C_BASE 0x3100 + +typedef struct { +  volatile uint32_t  prescaler_lo;	// r/w +  volatile uint32_t  prescaler_hi;	// r/w +  volatile uint32_t  ctrl;		// r/w +  volatile uint32_t  data;		// wr = transmit reg; rd = receive reg +  volatile uint32_t  cmd_status;	// wr = command reg;  rd = status reg +} i2c_regs_t; + +#define i2c_regs ((i2c_regs_t *) I2C_BASE) + +#define	I2C_CTRL_EN	(1 << 7)	// core enable +#define	I2C_CTRL_IE	(1 << 6)	// interrupt enable + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// +#define	I2C_CMD_START	(1 << 7)	// generate (repeated) start condition +#define I2C_CMD_STOP	(1 << 6)	// generate stop condition +#define	I2C_CMD_RD	(1 << 5)	// read from slave +#define I2C_CMD_WR	(1 << 4)	// write to slave +#define	I2C_CMD_NACK	(1 << 3)	// when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2	(1 << 2)	// reserved +#define	I2C_CMD_RSVD_1	(1 << 1)	// reserved +#define I2C_CMD_IACK	(1 << 0)	// set to clear pending interrupt + +#define I2C_ST_RXACK	(1 << 7)	// Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define	I2C_ST_BUSY	(1 << 6)	// 1 after START signal detected; 0 after STOP signal detected +#define	I2C_ST_AL	(1 << 5)	// Arbitration lost.  1 when core lost arbitration +#define	I2C_ST_RSVD_4	(1 << 4)	// reserved +#define	I2C_ST_RSVD_3	(1 << 3)	// reserved +#define	I2C_ST_RSVD_2	(1 << 2)	// reserved +#define I2C_ST_TIP	(1 << 1)	// Transfer-in-progress +#define	I2C_ST_IP	(1 << 0)	// Interrupt pending + + +//////////////////////////////////////////////// +// GPIO, Slave 4 +// +// These go to the daughterboard i/o pins + +#define GPIO_BASE 0x3200 + +typedef struct { +  volatile uint32_t	io;	  // tx data in high 16, rx in low 16 +  volatile uint32_t     ddr;      // 32 bits, 1 means output. tx in high 16, rx in low 16 +  volatile uint32_t	tx_sel;   // 16 2-bit fields select which source goes to TX DB +  volatile uint32_t	rx_sel;   // 16 2-bit fields select which source goes to RX DB +} gpio_regs_t; + +// each 2-bit sel field is layed out this way +#define GPIO_SEL_SW	   0 // if pin is an output, set by software in the io reg +#define	GPIO_SEL_ATR	   1 // if pin is an output, set by ATR logic +#define	GPIO_SEL_DEBUG_0   2 // if pin is an output, debug lines from FPGA fabric +#define	GPIO_SEL_DEBUG_1   3 // if pin is an output, debug lines from FPGA fabric + +#define gpio_base ((gpio_regs_t *) GPIO_BASE) + +/////////////////////////////////////////////////// +// Buffer Pool Status, Slave 5 +// +// The buffers themselves are located in Slave 1, Buffer Pool RAM. +// The status registers are in Slave 5, Buffer Pool Status. +// The control register is in Slave 7, Settings Bus. + +#define BUFFER_POOL_STATUS_BASE 0x3300 + +typedef struct { +  volatile uint32_t last_line[NBUFFERS]; // last line xfer'd in buffer +  volatile uint32_t status;	         // error and done flags +  volatile uint32_t hw_config;	         // see below +  volatile uint32_t dummy[3]; +  volatile uint32_t irqs; +  volatile uint32_t pri_enc_bp_status; +  volatile uint32_t cycle_count; +} buffer_pool_status_t; + +#define buffer_pool_status ((buffer_pool_status_t *) BUFFER_POOL_STATUS_BASE) + +#define BUTTON_PUSHED ((buffer_pool_status->irqs & PIC_BUTTON) ? 0 : 1) + +/* + * Buffer n's xfer is done. + * Clear this bit by issuing bp_clear_buf(n) + */ +#define BPS_DONE(n)     (0x00000001 << (n)) +#define BPS_DONE_0	BPS_DONE(0) +#define BPS_DONE_1	BPS_DONE(1) +#define BPS_DONE_2	BPS_DONE(2) +#define BPS_DONE_3	BPS_DONE(3) +#define BPS_DONE_4	BPS_DONE(4) +#define BPS_DONE_5	BPS_DONE(5) +#define BPS_DONE_6	BPS_DONE(6) +#define BPS_DONE_7	BPS_DONE(7) + +/* + * Buffer n's xfer had an error. + * Clear this bit by issuing bp_clear_buf(n) + */ +#define BPS_ERROR(n)	(0x00000100 << (n)) +#define BPS_ERROR_0	BPS_ERROR(0) +#define BPS_ERROR_1	BPS_ERROR(1) +#define BPS_ERROR_2	BPS_ERROR(2) +#define BPS_ERROR_3	BPS_ERROR(3) +#define BPS_ERROR_4	BPS_ERROR(4) +#define BPS_ERROR_5	BPS_ERROR(5) +#define BPS_ERROR_6	BPS_ERROR(6) +#define BPS_ERROR_7	BPS_ERROR(7) + +/* + * Buffer n is idle.  A buffer is idle if it's not + * DONE, ERROR, or processing a transaction.  If it's + * IDLE, it's safe to start a new transaction. + * + * Clear this bit by starting a xfer with + * bp_send_from_buf or bp_receive_to_buf. + */ +#define BPS_IDLE(n)     (0x00010000 << (n)) +#define BPS_IDLE_0	BPS_IDLE(0) +#define BPS_IDLE_1	BPS_IDLE(1) +#define BPS_IDLE_2	BPS_IDLE(2) +#define BPS_IDLE_3	BPS_IDLE(3) +#define BPS_IDLE_4	BPS_IDLE(4) +#define BPS_IDLE_5	BPS_IDLE(5) +#define BPS_IDLE_6	BPS_IDLE(6) +#define BPS_IDLE_7	BPS_IDLE(7) + +/* + * Buffer n has a "slow path" packet in it. + * This bit is orthogonal to the bits above and indicates that + * the FPGA ethernet rx protocol engine has identified this packet + * as one requiring firmware intervention. + */ +#define BPS_SLOWPATH(n) (0x01000000 << (n)) +#define BPS_SLOWPATH_0	BPS_SLOWPATH(0) +#define BPS_SLOWPATH_1	BPS_SLOWPATH(1) +#define BPS_SLOWPATH_2	BPS_SLOWPATH(2) +#define BPS_SLOWPATH_3	BPS_SLOWPATH(3) +#define BPS_SLOWPATH_4	BPS_SLOWPATH(4) +#define BPS_SLOWPATH_5	BPS_SLOWPATH(5) +#define BPS_SLOWPATH_6	BPS_SLOWPATH(6) +#define BPS_SLOWPATH_7	BPS_SLOWPATH(7) + + +#define BPS_DONE_ALL	  0x000000ff	// mask of all dones +#define BPS_ERROR_ALL	  0x0000ff00	// mask of all errors +#define BPS_IDLE_ALL      0x00ff0000	// mask of all idles +#define BPS_SLOWPATH_ALL  0xff000000	// mask of all slowpaths + +// The hw_config register + +#define	HWC_SIMULATION		0x80000000 +#define	HWC_WB_CLK_DIV_MASK	0x0000000f + +/*! + * \brief return non-zero if we're running under the simulator + */ +inline static int +hwconfig_simulation_p(void) +{ +  return buffer_pool_status->hw_config & HWC_SIMULATION; +} + +/*! + * \brief Return Wishbone Clock divisor. + * The processor runs at the Wishbone Clock rate which is MASTER_CLK_RATE / divisor. + */ +inline static int +hwconfig_wishbone_divisor(void) +{ +  return buffer_pool_status->hw_config & HWC_WB_CLK_DIV_MASK; +} + +/////////////////////////////////////////////////// +// Ethernet Core, Slave 6 + +#define ETH_BASE 0x3400 + +#include "eth_mac_regs.h" + +#define eth_mac ((eth_mac_regs_t *) ETH_BASE) + +//////////////////////////////////////////////////// +// Settings Bus, Slave #7, Not Byte Addressable! +// +// Output-only from processor point-of-view. +// 1KB of address space (== 256 32-bit write-only regs) + + +#define MISC_OUTPUT_BASE        0x2000 +#define	TX_PROTOCOL_ENGINE_BASE 0x2080 +#define	RX_PROTOCOL_ENGINE_BASE 0x20C0 +#define BUFFER_POOL_CTRL_BASE   0x2100 +#define LAST_SETTING_REG        0x23FC	// last valid setting register + +#define SR_MISC 0 +#define SR_TX_PROT_ENG 32 +#define SR_RX_PROT_ENG 48 +#define SR_BUFFER_POOL_CTRL 64 +#define SR_UDP_SM 96 +#define SR_TX_DSP 208 +#define SR_TX_CTRL 224 +#define SR_RX_DSP 160 +#define SR_RX_CTRL 176 +#define SR_TIME64 192 +#define SR_SIMTIMER 198 +#define SR_LAST 255 + +#define	_SR_ADDR(sr)	(MISC_OUTPUT_BASE + (sr) * sizeof(uint32_t)) + +// --- buffer pool control regs --- + +typedef struct { +  volatile uint32_t ctrl; +} buffer_pool_ctrl_t; + +// buffer pool ports + +#define	PORT_SERDES	0	// serial/deserializer +#define	PORT_DSP	1	// DSP tx or rx pipeline +#define	PORT_ETH	2	// ethernet tx or rx +#define	PORT_RAM	3	// RAM tx or rx + +// the buffer pool ctrl register fields + +#define BPC_BUFFER(n) (((n) & 0xf) << 28) +#define   BPC_BUFFER_MASK      BPC_BUFFER(~0) +#define   BPC_BUFFER_0	       BPC_BUFFER(0) +#define   BPC_BUFFER_1	       BPC_BUFFER(1) +#define   BPC_BUFFER_2	       BPC_BUFFER(2) +#define   BPC_BUFFER_3	       BPC_BUFFER(3) +#define   BPC_BUFFER_4	       BPC_BUFFER(4) +#define   BPC_BUFFER_5	       BPC_BUFFER(5) +#define   BPC_BUFFER_6	       BPC_BUFFER(6) +#define   BPC_BUFFER_7	       BPC_BUFFER(7) +#define	  BPC_BUFFER_NIL       BPC_BUFFER(0x8)	// disable + +#define BPC_PORT(n) (((n) & 0x7) << 25) +#define   BPC_PORT_MASK        BPC_PORT(~0) +#define   BPC_PORT_SERDES      BPC_PORT(PORT_SERDES) +#define   BPC_PORT_DSP	       BPC_PORT(PORT_DSP) +#define   BPC_PORT_ETH         BPC_PORT(PORT_ETH) +#define   BPC_PORT_RAM         BPC_PORT(PORT_RAM) +#define   BPC_PORT_NIL	       BPC_PORT(0x4)   	// disable + +#define	BPC_CLR	       	       (1 << 24)  // mutually excl commands +#define	BPC_READ	       (1 << 23) +#define BPC_WRITE              (1 << 22) + +#define BPC_STEP(step) (((step) & 0xf) << 18) +#define   BPC_STEP_MASK	       BPC_STEP(~0) +#define BPC_LAST_LINE(line) (((line) & 0x1ff) << 9) +#define   BPC_LAST_LINE_MASK   BPC_LAST_LINE(~0) +#define BPC_FIRST_LINE(line) (((line) & 0x1ff) << 0) +#define   BPC_FIRST_LINE_MASK  BPC_FIRST_LINE(~0) + +#define buffer_pool_ctrl ((buffer_pool_ctrl_t *) BUFFER_POOL_CTRL_BASE) + +// --- misc outputs --- + +typedef struct { +  volatile uint32_t	clk_ctrl; +  volatile uint32_t	serdes_ctrl; +  volatile uint32_t	adc_ctrl; +  volatile uint32_t	leds; +  volatile uint32_t	phy_ctrl;	// LSB is reset line to eth phy +  volatile uint32_t	debug_mux_ctrl; +  volatile uint32_t     ram_page;       // FIXME should go somewhere else... +  volatile uint32_t     flush_icache;   // Flush the icache +  volatile uint32_t     led_src;        // HW or SW control for LEDs +} output_regs_t; + +#define CLK_RESET  (1<<4) +#define CLK_ENABLE (1<<3) | (1<<2) +#define CLK_SEL    (1<<1) | (1<<0) + +#define SERDES_ENABLE 8 +#define SERDES_PRBSEN 4 +#define SERDES_LOOPEN 2 +#define SERDES_RXEN   1 + +#define	ADC_CTRL_ON	0x0F +#define	ADC_CTRL_OFF	0x00 + +// crazy order that matches the labels on the case + +#define	LED_A		(1 << 2) +#define	LED_B		(1 << 0) +#define	LED_E		(1 << 3) +#define	LED_D		(1 << 1) +#define	LED_C		(1 << 4) +//      LED_F		// controlled by CPLD +#define	LED_RJ45	(1 << 5) + +#define output_regs ((output_regs_t *) MISC_OUTPUT_BASE) + +// --- udp tx regs --- + +typedef struct { +  // Bits 19:16 are control info; bits 15:0 are data (see below) +  // First two words are unused. +  volatile uint32_t _nope[2]; +  //--- ethernet header - 14 bytes--- +  volatile struct{ +    uint32_t mac_dst_0_1; //word 2 +    uint32_t mac_dst_2_3; +    uint32_t mac_dst_4_5; +    uint32_t mac_src_0_1; +    uint32_t mac_src_2_3; +    uint32_t mac_src_4_5; +    uint32_t ether_type; //word 8 +  } eth_hdr; +  //--- ip header - 20 bytes --- +  volatile struct{ +    uint32_t ver_ihl_tos; //word 9 +    uint32_t total_length; +    uint32_t identification; +    uint32_t flags_frag_off; +    uint32_t ttl_proto; +    uint32_t checksum; +    uint32_t src_addr_high; +    uint32_t src_addr_low; +    uint32_t dst_addr_high; +    uint32_t dst_addr_low; //word 18 +  } ip_hdr; +  //--- udp header - 8 bytes --- +  volatile struct{ +    uint32_t src_port; //word 19 +    uint32_t dst_port; +    uint32_t length; +    uint32_t checksum; //word 22 +  } udp_hdr; +  volatile uint32_t _pad[32-23]; +} sr_udp_sm_t; + +// control bits (all expect UDP_SM_LAST_WORD are mutually exclusive) + +// This is the last word of the header +#define	UDP_SM_LAST_WORD		(1 << 19) + +// Insert IP header checksum here.  Data is the xor of 16'hFFFF and +// the values written into regs 9-13 and 15-18. +#define	UDP_SM_INS_IP_HDR_CHKSUM	(1 << 18) + +// Insert IP Length here (data ignored) +#define	UDP_SM_INS_IP_LEN		(1 << 17) + +// Insert UDP Length here (data ignore) +#define	UDP_SM_INS_UDP_LEN		(1 << 16) + +#define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM)) + +// --- dsp tx regs --- + +#define MIN_CIC_INTERP	1 +#define	MAX_CIC_INTERP  128 + +typedef struct { +  volatile uint32_t     num_chan; +  volatile uint32_t     clear_state;	// clears out state machine, fifos, +} sr_tx_ctrl_t; + +#define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL)) + +typedef struct { +  volatile int32_t	freq; +  volatile uint32_t	scale_iq;	// {scale_i,scale_q} +  volatile uint32_t     interp_rate; +  volatile uint32_t     _padding0;      // padding for the tx_mux +                                        //   NOT freq, scale, interp +  /*! +   * \brief output mux configuration. +   * +   * <pre> +   *     3                   2                   1                        +   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   *  +-------------------------------+-------+-------+-------+-------+ +   *  |                                               | DAC1  |  DAC0 | +   *  +-------------------------------+-------+-------+-------+-------+ +   *  +   *  There are N DUCs (1 now) with complex inputs and outputs. +   *  There are two DACs. +   *  +   *  Each 4-bit DACx field specifies the source for the DAC +   *  Each subfield is coded like this:  +   *  +   *     3 2 1 0 +   *    +-------+ +   *    |   N   | +   *    +-------+ +   *  +   *  N specifies which DUC output is connected to this DAC. +   *  +   *   N   which interp output +   *  ---  ------------------- +   *   0   DUC 0 I +   *   1   DUC 0 Q +   *   2   DUC 1 I +   *   3   DUC 1 Q +   *   F   All Zeros +   *    +   * The default value is 0x10 +   * </pre> +   */ +  volatile uint32_t	tx_mux; + +} dsp_tx_regs_t; +   +#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP)) + +// --- VITA RX CTRL regs --- +typedef struct { +  // The following 3 are logically a single command register. +  // They are clocked into the underlying fifo when time_ticks is written. +  volatile uint32_t	cmd;		// {now, chain, num_samples(30) +  volatile uint32_t	time_secs; +  volatile uint32_t	time_ticks; + +  volatile uint32_t	clear_overrun;	// write anything to clear overrun +  volatile uint32_t	vrt_header;	// word 0 of packet.  FPGA fills in packet counter +  volatile uint32_t	vrt_stream_id;	// word 1 of packet.  +  volatile uint32_t	vrt_trailer; +  volatile uint32_t	nsamples_per_pkt; +  volatile uint32_t     nchannels;      // 1 in basic case, up to 4 for vector sources +  volatile uint32_t     pad[7];         // Make each structure 16 elements long +} sr_rx_ctrl_t; + +#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL)) + +// --- dsp rx regs --- +#define	MIN_CIC_DECIM	1 +#define	MAX_CIC_DECIM   128 + +typedef struct { +  volatile int32_t	freq; +  volatile uint32_t	scale_iq;	// {scale_i,scale_q} +  volatile uint32_t     decim_rate; +  volatile uint32_t     dcoffset_i;     // Bit 31 high sets fixed offset mode, using lower 14 bits, +                                        // otherwise it is automatic  +  volatile uint32_t     dcoffset_q;     // Bit 31 high sets fixed offset mode, using lower 14 bits + +  /*! +   * \brief input mux configuration. +   * +   * This determines which ADC (or constant zero) is connected to  +   * each DDC input.  There are N DDCs (1 now).  Each has two inputs. +   * +   * <pre> +   * Mux value: +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                                                       |Q0 |I0 | +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * +   * The default value is 0x4 +   * </pre> +   */ +  volatile uint32_t     rx_mux;        // called adc_mux in dsp_core_rx.v + +  /*! +   * \brief Streaming GPIO configuration +   * +   * This determines whether the LSBs of I and Q samples come from the DSP +   * pipeline or from the io_rx GPIO pins.  To stream GPIO, one must first +   * set the GPIO data direction register to have io_rx[15] and/or io_rx[14] +   * configured as inputs.  The GPIO pins will be sampled at the time the +   * remainder of the DSP sample is strobed into the RX sample FIFO.  There +   * will be a decimation-dependent fixed time offset between the GPIO +   * sample stream and the associated RF samples. +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                           MBZ                             |Q|I| +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * I         0=LSB comes from DSP pipeline (default) +   *           1=LSB comes from io_rx[15] +   *  +   * Q         0=LSB comes from DSP pipeline (default) +   *           1=LSB comes from io_rx[14] +   */ +  volatile uint32_t gpio_stream_enable; + +} dsp_rx_regs_t; +   +#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP)) + +// ---------------------------------------------------------------- +// VITA49 64 bit time (write only) +  /*! +   * \brief Time 64 flags +   * +   * <pre> +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-----------------------------------------------------------+-+-+ +   * |                                                           |S|P| +   * +-----------------------------------------------------------+-+-+ +   * +   * P - PPS edge selection (0=negedge, 1=posedge, default=0) +   * S - Source (0=sma, 1=mimo, 0=default) +   * +   * </pre> +   */ +typedef struct { +  volatile uint32_t	secs;	// value to set absolute secs to on next PPS +  volatile uint32_t	ticks;	// value to set absolute ticks to on next PPS +  volatile uint32_t flags;  // flags - see chart above +  volatile uint32_t imm;    // set immediate (0=latch on next pps, 1=latch immediate, default=0) +} sr_time64_t; + +#define sr_time64 ((sr_time64_t *) _SR_ADDR(SR_TIME64)) + + +/*  + * --- ethernet tx protocol engine regs (write only) --- + * + * These registers control the transmit portion of the ethernet + * protocol engine (out of USRP2).  The protocol engine handles fifo + * status and sequence number insertion in outgoing packets, and + * automagically generates status packets when required to inform the + * host of changes in fifo availability. + * + * All outgoing packets have their fifo_status field set to the number + * of 32-bit lines of fifo available in the ethernet Rx fifo (see + * usrp2_eth_packet.h).  Seqno's are set if FIXME, else 0. + * + * FIXME clean this up once we know how it's supposed to behave. + */ + +typedef struct { +  volatile uint32_t  flags;	     // not yet fully defined (channel?) +  volatile uint32_t  mac_dst0123;    // 4 bytes of destination mac addr +  volatile uint32_t  mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr +  volatile uint32_t  mac_src2345;    // 4 bytes of destination mac addr +  volatile uint32_t  seqno;	     // Write to init seqno.  It autoincs on match +} tx_proto_engine_regs_t; + +#define tx_proto_engine ((tx_proto_engine_regs_t *) TX_PROTOCOL_ENGINE_BASE) + +/* + * --- ethernet rx protocol engine regs (write only) --- + * + * These registers control the receive portion of the ethernet + * protocol engine (into USRP2).  The protocol engine offloads common + * packet inspection operations so that firmware has less to do on + * "fast path" packets. + * + * The registers define conditions which must be matched for a packet + * to be considered a "fast path" packet.  If a received packet + * matches the src and dst mac address, ethertype, flags field, and + * expected seqno number it is considered a "fast path" packet, and + * the expected seqno is updated.  If the packet fails to satisfy any + * of the above conditions it's a "slow path" packet, and the + * corresponding SLOWPATH flag will be set buffer_status register. + */ + +typedef struct { +  volatile uint32_t  flags;	     // not yet fully defined (channel?) +  volatile uint32_t  mac_dst0123;    // 4 bytes of destination mac addr +  volatile uint32_t  mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr +  volatile uint32_t  mac_src2345;    // 4 bytes of destination mac addr +  volatile uint32_t  ethertype_pad;  // ethertype in high 16-bits +} rx_proto_engine_regs_t; + +#define rx_proto_engine ((rx_proto_engine_regs_t *) RX_PROTOCOL_ENGINE_BASE) + + + +/////////////////////////////////////////////////// +// Simple Programmable Interrupt Controller, Slave 8 + +#define PIC_BASE  0x3500 + +// Interrupt request lines +// Bit numbers (LSB == 0) that correpond to interrupts into PIC + +#define	IRQ_BUFFER	0	// buffer manager +#define	IRQ_ONETIME	1 +#define	IRQ_SPI		2 +#define	IRQ_I2C		3 +#define	IRQ_PHY		4	// ethernet PHY +#define	IRQ_UNDERRUN	5 +#define	IRQ_OVERRUN	6 +#define	IRQ_PPS		7	// pulse per second +#define	IRQ_UART_RX	8 +#define	IRQ_UART_TX	9 +#define	IRQ_SERDES	10 +#define	IRQ_CLKSTATUS	11 +#define IRQ_PERIODIC    12 +#define IRQ_BUTTON	13 + +#define IRQ_TO_MASK(x) (1 << (x)) + +#define PIC_BUFFER_INT    IRQ_TO_MASK(IRQ_BUFFER) +#define PIC_ONETIME_INT   IRQ_TO_MASK(IRQ_ONETIME) +#define PIC_SPI_INT       IRQ_TO_MASK(IRQ_SPI) +#define PIC_I2C_INT       IRQ_TO_MASK(IRQ_I2C) +#define PIC_PHY_INT       IRQ_TO_MASK(IRQ_PHY) +#define PIC_UNDERRUN_INT  IRQ_TO_MASK(IRQ_UNDERRUN) +#define PIC_OVERRUN_INT   IRQ_TO_MASK(IRQ_OVERRUN) +#define PIC_PPS_INT   	  IRQ_TO_MASK(IRQ_PPS) +#define PIC_UART_RX_INT   IRQ_TO_MASK(IRQ_UART_RX) +#define PIC_UART_TX_INT   IRQ_TO_MASK(IRQ_UART_TX) +#define PIC_SERDES        IRQ_TO_MASK(IRQ_SERDES) +#define PIC_CLKSTATUS     IRQ_TO_MASK(IRQ_CLKSTATUS) +#define PIC_BUTTON				IRQ_TO_MASK(IRQ_BUTTON) + +typedef struct { +  volatile uint32_t edge_enable; // mask: 1 -> edge triggered, 0 -> level +  volatile uint32_t polarity;	 // mask: 1 -> rising edge +  volatile uint32_t mask;	 // mask: 1 -> disabled +  volatile uint32_t pending;	 // mask: 1 -> pending; write 1's to clear pending ints +} pic_regs_t; + +#define pic_regs ((pic_regs_t *) PIC_BASE) + +// ---------------------------------------------------------------- +// WB_CLK_RATE is the time base for this +typedef struct { +  volatile uint32_t	onetime;   // Number of wb clk cycles till the onetime interrupt +  volatile uint32_t	periodic;  // Repeat rate of periodic interrupt +} sr_simple_timer_t; + +#define sr_simple_timer ((sr_simple_timer_t *) _SR_ADDR(SR_SIMTIMER)) + +/////////////////////////////////////////////////// +// UNUSED, Slave 9 + +/////////////////////////////////////////////////// +// UART, Slave 10 + +#define UART_BASE  0x3700 + +typedef struct { +  //  All elements are 8 bits except for clkdiv (16), but we use uint32 to make  +  //    the hardware for decoding easier +  volatile uint32_t clkdiv;  // Set to 50e6 divided by baud rate (no x16 factor) +  volatile uint32_t txlevel; // Number of spaces in the FIFO for writes +  volatile uint32_t rxlevel; // Number of available elements in the FIFO for reads +  volatile uint32_t txchar;  // Write characters to be sent here +  volatile uint32_t rxchar;  // Read received characters here +  volatile uint32_t x[3]; //padding to reach 32B +} uart_regs_t; + +#define uart_regs ((uart_regs_t *) UART_BASE) + +/////////////////////////////////////////////////// +// ATR Controller, Slave 11 + +#define ATR_BASE  0x3800 + +typedef struct { +  volatile uint32_t	v[16]; +} atr_regs_t; + +#define	ATR_IDLE	0x0	// indicies into v +#define ATR_TX		0x1 +#define	ATR_RX		0x2 +#define	ATR_FULL	0x3 + +#define atr_regs ((atr_regs_t *) ATR_BASE) + +/////////////////////////////////////////////////// +// UNUSED, Slave 12 + +/////////////////////////////////////////////////// +// ICAP, Slave 13 + +#define ICAP_BASE 0x3A00 +typedef struct { +  uint32_t icap; //only the lower 8 bits matter +} icap_regs_t; + +#define icap_regs ((icap_regs_t *) ICAP_BASE) + +/////////////////////////////////////////////////// +// SPI Flash interface, Slave 14 +// Control register definitions are the same as SPI, so use SPI_CTRL_ASS, etc. +// Peripheral mask not needed since bus is dedicated (CE held low) + +#define SPIF_BASE 0x3B00 +typedef struct { +  volatile uint32_t	txrx0; +  volatile uint32_t	txrx1; +  volatile uint32_t	txrx2; +  volatile uint32_t	txrx3; +  volatile uint32_t	ctrl; +  volatile uint32_t	div; +  volatile uint32_t	ss; +} spif_regs_t; + +#define spif_regs ((spif_regs_t *) SPIF_BASE) + +//////////////////////////////////////////////////////////////// +// Main RAM, Slave 15 + +#define RAM_BASE 0x8000 + + + +/////////////////////////////////////////////////// +#endif + diff --git a/firmware/microblaze/usrp2p/spi_flash.c b/firmware/microblaze/usrp2p/spi_flash.c new file mode 100644 index 000000000..09b74a513 --- /dev/null +++ b/firmware/microblaze/usrp2p/spi_flash.c @@ -0,0 +1,194 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "spi_flash_private.h" +//#include <stdlib.h> +#include <nonstdio.h> + +uint32_t +spi_flash_rdsr(void) +{ +  return spif_transact(SPI_TXRX, SPI_SS_FLASH, RDSR_CMD << 8, 16, FLAGS) & 0xff; +} + +static void +spi_flash_write_enable(void) +{ +//	spif_transact(SPI_TXONLY, SPI_SS_FLASH, (WRSR_CMD << 8) | 0x00, 16, FLAGS); //disable write protection bits +  spif_transact(SPI_TXONLY, SPI_SS_FLASH, WREN_CMD, 8, FLAGS); +} + +bool +spi_flash_done_p(void) +{ +  return (spi_flash_rdsr() & SR_WIP) == 0; +} + +void +spi_flash_wait(void) +{ +  while (!spi_flash_done_p()) +    ; +} + +void +spi_flash_erase_sector_start(uint32_t flash_addr) +{ +  //uprintf(UART_DEBUG, "spi_flash_erase_sector_start: addr = 0x%x\n", flash_addr); + +  spi_flash_wait(); +  spi_flash_write_enable(); +  spif_transact(SPI_TXONLY, SPI_SS_FLASH, +		(SE_CMD << 24) | (flash_addr & 0x00ffffff), +		32, FLAGS); +} + +bool +spi_flash_page_program_start(uint32_t flash_addr, size_t nbytes, const void *buf) +{ +  if (nbytes == 0 || nbytes > SPI_FLASH_PAGE_SIZE) +    return false; + +  uint32_t local_buf[SPI_FLASH_PAGE_SIZE / sizeof(uint32_t)]; +  memset(local_buf, 0xff, sizeof(local_buf));	// init to 0xff (nops when programming) +  memcpy(local_buf, buf, nbytes); + +  spi_flash_wait(); +  spi_flash_write_enable(); +   +  /* +   * We explicitly control the slave select here (/S), so that we can +   * do the entire write operation as a single transaction from +   * device's point of view.  (The most our SPI peripheral can transfer +   * in a single shot is 16 bytes.) +   */ +  spif_wait(); + +  spif_regs->ss = 0; +  spif_regs->ctrl = FLAGS;	// ASS is now clear and no chip select is enabled. +   +  /* write PP_CMD, ADDR2, ADDR1, ADDR0 */ + +  spif_regs->txrx0 = (PP_CMD << 24) | (flash_addr & 0x00ffffff); +  spif_regs->ss = SPI_SS_FLASH;		// assert chip select +  spif_regs->ctrl = FLAGS | LEN(4 * 8);		 +  spif_regs->ctrl = FLAGS | LEN(4 * 8) | SPI_CTRL_GO_BSY; +  spif_wait(); + +  /*  send 256 bytes total, 16 at a time */ +  for (size_t i = 0; i < 16; i++){ +    spif_regs->txrx3 = local_buf[i * 4 + 0]; +    spif_regs->txrx2 = local_buf[i * 4 + 1]; +    spif_regs->txrx1 = local_buf[i * 4 + 2]; +    spif_regs->txrx0 = local_buf[i * 4 + 3]; +     +    spif_regs->ctrl = FLAGS | LEN(16 * 8);	// xfer 16 bytes +    spif_regs->ctrl = FLAGS | LEN(16 * 8) | SPI_CTRL_GO_BSY; +    spif_wait(); +  } +  spif_regs->ss = 0;		// desassert chip select + +  return true; +} + +void +spi_flash_erase(uint32_t flash_addr, size_t nbytes) +{ +  if (nbytes == 0) +    return; + +  uint32_t first = round_down(flash_addr, spi_flash_sector_size()); +  uint32_t last  = round_down(flash_addr + nbytes - 1, spi_flash_sector_size()); +   +  for (uint32_t s = first; s <= last; s += spi_flash_sector_size()){ +    spi_flash_erase_sector_start(s); +  } +  spi_flash_wait(); +} + +bool +spi_flash_program(uint32_t flash_addr, size_t nbytes, const void *buf) +{ +  //uprintf(UART_DEBUG, "\nspi_flash_program: addr = 0x%x, nbytes = %d\n", flash_addr, nbytes); + +  const unsigned char *p = (const unsigned char *) buf; +  size_t n; + +  if (nbytes == 0) +    return true; + +  uint32_t r = flash_addr % SPI_FLASH_PAGE_SIZE; +  if (r){	/* do initial non-aligned page */ +    n = min(SPI_FLASH_PAGE_SIZE - r, nbytes); +    spi_flash_page_program_start(flash_addr, n, p); +    flash_addr += n; +    p += n; +    nbytes -= n; +  } + +  while (nbytes > 0){ +    n = min(SPI_FLASH_PAGE_SIZE, nbytes); +    spi_flash_page_program_start(flash_addr, n, p); +    flash_addr += n; +    p += n; +    nbytes -= n; +  } + +  spi_flash_wait(); +  return true; +} + +void +spi_flash_async_erase_start(spi_flash_async_state_t *s, +			    uint32_t flash_addr, size_t nbytes) +{ +  if (nbytes == 0){ +    s->first = s->last = s->current = 0; +    return; +  } + +  uint32_t first = round_down(flash_addr, spi_flash_sector_size()); +  uint32_t last  = round_down(flash_addr + nbytes - 1, spi_flash_sector_size()); + +  s->first = first; +  s->last = last; +  s->current = first; + +  spi_flash_erase_sector_start(s->current); +} + +bool +spi_flash_async_erase_poll(spi_flash_async_state_t *s) +{ +  if (!spi_flash_done_p()) +    return false; + +  //printf("%d/%d\n", s->current, s->last); + +  // The current sector erase has completed.  See if we're finished or +  // if there's more to do. + +  if (s->current == s->last)	// we're done! +    return true; + +  s->current += spi_flash_sector_size(); +  spi_flash_erase_sector_start(s->current); +  return false; +} + diff --git a/firmware/microblaze/usrp2p/spi_flash.h b/firmware/microblaze/usrp2p/spi_flash.h new file mode 100644 index 000000000..bbe7b650d --- /dev/null +++ b/firmware/microblaze/usrp2p/spi_flash.h @@ -0,0 +1,119 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_SPI_FLASH_H +#define INCLUDED_SPI_FLASH_H + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + + +#define SPI_FLASH_PAGE_SIZE	256 +#define SPI_SS_FLASH 1 + + +uint32_t spi_flash_rdid(void);	/* Read ID */ +uint32_t spi_flash_rdsr(void);	/* Read Status Register */ +size_t spi_flash_log2_sector_size(void) __attribute__((pure));  /* either 16 or 18 */ +size_t spi_flash_log2_memory_size(void); + +static inline size_t +spi_flash_sector_size(void) +{ +  return ((size_t) 1) << spi_flash_log2_sector_size(); +} + +static inline size_t +spi_flash_memory_size(void) +{ +  return ((size_t) 1) << spi_flash_log2_memory_size(); +} + +void spi_flash_read(uint32_t flash_addr,  size_t nbytes, void *buf); + +/* + * Erase all sectors that fall within the interval [flash_addr, flash_addr + nbytes). + * Erasing sets the memory to ones. + */ +void spi_flash_erase(uint32_t flash_addr, size_t nbytes); + +/* + * Program the flash. + * The area must have been erased prior to programming. + */ +bool spi_flash_program(uint32_t flash_addr, size_t nbytes, const void *buf); + +/* + * --- asynchronous routines --- + */ + +/* + * Is the erasing or programming done? + */ +bool spi_flash_done_p(void); + +/* + * Wait for erasing or programming to complete + */ +void spi_flash_wait(void); + +/* + * Start the erase process on a single sector. + * (It takes between 1 and 3 seconds to erase a 64KB sector) + */ +void spi_flash_erase_sector_start(uint32_t flash_addr); + +/* + * Start the programming process within a single page. + * nbytes must be between 1 and 256. + * (It takes between 1.4 and 5 ms to program a page -> 640 ms for 64KB) + */ +bool spi_flash_page_program_start(uint32_t flash_addr, size_t nbytes, const void *buf); + + +/* + * --- high-level async erase --- + */ + +typedef struct { +  uint32_t	first; +  uint32_t	last; +  uint32_t	current; +} spi_flash_async_state_t; + +/* + * Start to erase all sectors that fall within the interval [flash_addr, flash_addr + nbytes). + * Erasing sets the memory to ones. + * + * Initializes s and begins the process.  Call spi_flash_async_erase_poll + * to test for completion and advance state machine. + */ +void spi_flash_async_erase_start(spi_flash_async_state_t *s, +				 uint32_t flash_addr, size_t nbytes); + +/* + * Poll for aysnc flash erase completion. + * Returns true when the erase has completed. + * (This should be called at something >= 4 Hz.  It takes 1 to 3 seconds to + * erase each 64KB sector). + */ +bool spi_flash_async_erase_poll(spi_flash_async_state_t *s); + + +#endif /* INCLUDED_SPI_FLASH_H */ diff --git a/firmware/microblaze/usrp2p/spi_flash_private.h b/firmware/microblaze/usrp2p/spi_flash_private.h new file mode 100644 index 000000000..9a1b8d3e3 --- /dev/null +++ b/firmware/microblaze/usrp2p/spi_flash_private.h @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_SPI_FLASH_PRIVATE_H +#define INCLUDED_SPI_FLASH_PRIVATE_H + +#include "spi_flash.h" +#include "spi.h" +#include "memory_map.h" +#include <string.h> + + +/* M25P64 et al. */ + +#define	WREN_CMD	0x06	// write enable +#define	WRDI_CMD	0x04	// write disable +#define	RDID_CMD	0x9f	// read identification +#define	RDSR_CMD	0x05	// read status register +#define WRSR_CMD	0x01	// write status register +#define	READ_CMD	0x03 +#define	FAST_READ_CMD	0x0b +#define	PP_CMD		0x02	// page program (256 bytes) +#define	SE_CMD		0xd8	// sector erase (64KB) +#define	BE_CMD		0xc7	// bulk erase (all) +#define	RES_CMD		0xab	// read electronic sig (deprecated) + +/* Status register bits */ + +#define	SR_SRWD		0x80 +#define	SR_BP2		0x10	// block protect bit 2 +#define	SR_BP1		0x08	// block protect bit 1 +#define	SR_BP0		0x04	// block protect bit 0 +#define	SR_WEL		0x02	// Write Enable Latch +#define	SR_WIP		0x01	// Write in Progress.  Set if busy w/ program or erase cycle. + + +#define	FLAGS (SPIF_PUSH_FALL | SPIF_LATCH_RISE) + +#define LEN(x) ((x) & SPI_CTRL_CHAR_LEN_MASK) + + +static inline uint32_t +min(uint32_t a, uint32_t b) +{ +  return a < b ? a : b; +} + +static inline uint32_t +round_down(uint32_t x, uint32_t power_of_2) +{ +  return x & -power_of_2; +} + +#endif /* INCLUDED_SPI_FLASH_PRIVATE_H */ diff --git a/firmware/microblaze/usrp2p/spi_flash_read.c b/firmware/microblaze/usrp2p/spi_flash_read.c new file mode 100644 index 000000000..4682c5fe6 --- /dev/null +++ b/firmware/microblaze/usrp2p/spi_flash_read.c @@ -0,0 +1,119 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include "spi_flash_private.h" +#include <stdlib.h>		// abort + +static size_t _spi_flash_log2_memory_size; + +uint32_t  +spi_flash_rdid(void) +{ +  return spif_transact(SPI_TXRX, SPI_SS_FLASH, RDID_CMD << 24, 32, FLAGS) & 0xffffff; +} + +size_t  +spi_flash_log2_sector_size(void) +{ +  static size_t _spi_flash_log2_sector_size; + +  if (_spi_flash_log2_sector_size != 0) +    return _spi_flash_log2_sector_size; + +   +  uint32_t id = spi_flash_rdid(); +  int type = (id >> 8) & 0xff; +  int size = id & 0xff; +  if (type != 0x20 || size < 22 || size > 24) +    abort(); + +  static unsigned char log2_sector_size[3] = { +    16,	/* M25P32  */ +    16, /* M25P64  */ +    18, /* M25P128 */ +  }; + +  _spi_flash_log2_sector_size = log2_sector_size[size - 22]; +  _spi_flash_log2_memory_size = size; //while we're at it +  return _spi_flash_log2_sector_size; +} + +size_t +spi_flash_log2_memory_size(void) +{ +  if (_spi_flash_log2_memory_size != 0) +    return _spi_flash_log2_memory_size; + +  uint32_t id = spi_flash_rdid(); +  int type = (id >> 8) & 0xff; +  int size = id & 0xff; +  if (type != 0x20 || size < 22 || size > 24) +    abort(); + +  _spi_flash_log2_memory_size = size; +  return _spi_flash_log2_memory_size; +} + +void  +spi_flash_read(uint32_t flash_addr,  size_t nbytes, void *buf) +{ +  /* +   * We explicitly control the slave select here (/S), so that we can +   * do the entire read operation as a single transaction from +   * device's point of view.  (The most our SPI peripheral can transfer +   * in a single shot is 16 bytes.) +   */ +  spif_wait(); + +  spif_regs->ss = 0; +  spif_regs->ctrl = FLAGS;	// ASS is now clear and no chip select is enabled. +   +  /* +   * Do the 5 byte instruction tranfer: +   *   FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY +   */ +  spif_regs->txrx1 = FAST_READ_CMD; +  spif_regs->txrx0 = ((flash_addr & 0x00ffffff) << 8); +  spif_regs->ss = SPI_SS_FLASH;		// assert chip select +  spif_regs->ctrl = FLAGS | LEN(5 * 8);		 +  spif_regs->ctrl = FLAGS | LEN(5 * 8) | SPI_CTRL_GO_BSY; +  spif_wait(); + +  /* +   * Read up to 16 bytes at a time until done +   */ +  unsigned char *dst = (unsigned char *) buf; +  size_t m; +  for (size_t n = 0; n < nbytes; n += m, dst += m){ +    spif_regs->ctrl = FLAGS | LEN(16 * 8);	// xfer 16 bytes +    spif_regs->ctrl = FLAGS | LEN(16 * 8) | SPI_CTRL_GO_BSY; +    spif_wait(); + +    uint32_t w[4]; +    w[0] = spif_regs->txrx3;	// txrx3 has first bits in it +    w[1] = spif_regs->txrx2; +    w[2] = spif_regs->txrx1; +    w[3] = spif_regs->txrx0; +    unsigned char *src = (unsigned char *) &w[0]; +    m = min(nbytes - n, 16); +    for (size_t i = 0; i < m; i++) +      dst[i] = src[i]; +  } +  spif_regs->ss = 0;			// deassert chip select +} diff --git a/firmware/microblaze/usrp2p/spif.c b/firmware/microblaze/usrp2p/spif.c new file mode 100644 index 000000000..1c1a348f4 --- /dev/null +++ b/firmware/microblaze/usrp2p/spif.c @@ -0,0 +1,68 @@ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* + * Code for the Flash SPI bus + */ + +#include "spi.h" +#include "memory_map.h" + +void +spif_init(void)  +{ +  /* +   * f_sclk = f_wb / ((div + 1) * 2) +   */ +  spif_regs->div = 1;  // 0 = Div by 2 (31.25 MHz); 1 = Div-by-4 (15.625 MHz) + +  // run dummy transaction to work around invalid initial clock state +  spif_transact(SPI_TXONLY, 0, 0, 8, SPIF_PUSH_FALL | SPIF_LATCH_RISE); +} + +inline void +spif_wait(void)  +{ +  while (spif_regs->ctrl & SPI_CTRL_GO_BSY) +    ; +} + +uint32_t +spif_transact(bool readback_, int slave, uint32_t data, int length, uint32_t flags)  +{ +  flags &= (SPI_CTRL_TXNEG | SPI_CTRL_RXNEG); +  int ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & length) | flags; + +  spif_wait(); + +  // Data we will send +  spif_regs->txrx0 = data; + +  // Run it -- write once and rewrite with GO set +  spif_regs->ctrl = ctrl; +  // Tell it which SPI slave device to access +  spif_regs->ss = slave & 0xff; +  spif_regs->ctrl = ctrl | SPI_CTRL_GO_BSY; + +  if(readback_) { +    spif_wait(); +    return spif_regs->txrx0; +  } +  else +    return 0; +} diff --git a/firmware/microblaze/usrp2p/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c new file mode 100644 index 000000000..55c206b1b --- /dev/null +++ b/firmware/microblaze/usrp2p/udp_fw_update.c @@ -0,0 +1,108 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +//Routines to handle updating the SPI Flash firmware via UDP + +#include <net_common.h> +#include "usrp2/fw_common.h" +#include "spi.h" +#include "spi_flash.h" +#include <nonstdio.h> +#include <string.h> +#include "ethernet.h" +#include "udp_fw_update.h" +#include "xilinx_s3_icap.h" + +//Firmware update packet handler +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, +                                 unsigned char *payload, int payload_len) { + +  const usrp2_fw_update_data_t *update_data_in = (usrp2_fw_update_data_t *) payload; + +  usrp2_fw_update_data_t update_data_out; +  usrp2_fw_update_id_t update_data_in_id = update_data_in->id; + +  //ensure that the protocol versions match +  if (payload_len >= sizeof(uint32_t) && update_data_in->proto_ver != USRP2_FW_COMPAT_NUM){ +    printf("!Error in update packet handler: Expected compatibility number %d, but got %d\n", +        USRP2_FW_COMPAT_NUM, update_data_in->proto_ver +      ); +      update_data_in_id = USRP2_FW_UPDATE_ID_OHAI_LOL; //so we can respond +  } + +  //ensure that this is not a short packet +  if (payload_len < sizeof(usrp2_fw_update_data_t)){ +      printf("!Error in update packet handler: Expected payload length %d, but got %d\n", +          (int)sizeof(usrp2_fw_update_data_t), payload_len +      ); +      update_data_in_id = USRP2_FW_UPDATE_ID_WAT; +  } + +  spi_flash_async_state_t spi_flash_async_state; + +  switch(update_data_in_id) { +  case USRP2_FW_UPDATE_ID_OHAI_LOL: //why hello there you handsome devil +    update_data_out.id = USRP2_FW_UPDATE_ID_OHAI_OMG; +    memcpy(&update_data_out.data.ip_addr, (void *)get_ip_addr(), sizeof(struct ip_addr)); +    break; + +  case USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL: //query sector size, memory size so the host can mind the boundaries +    update_data_out.data.flash_info_args.sector_size_bytes = spi_flash_sector_size(); +    update_data_out.data.flash_info_args.memory_size_bytes = spi_flash_memory_size(); +    update_data_out.id = USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG; +    break; + +  case USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL: //out with the old +    spi_flash_async_erase_start(&spi_flash_async_state, update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length); +    update_data_out.id = USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG; +    break; + +  case USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL: +    //poll for done, set something in the reply packet +    //spi_flash_async_erase_poll() also advances the state machine, so you should call it reasonably often to get things done quicker +    if(spi_flash_async_erase_poll(&spi_flash_async_state)) update_data_out.id = USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG; +    else update_data_out.id = USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG; +    break; + +  case USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL: //and in with the new +    //spi_flash_program() goes pretty quick compared to page erases, so we don't bother polling -- it'll come back in some milliseconds +    //if it doesn't come back fast enough, we'll just write smaller packets at a time until it does +    spi_flash_program(update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length, update_data_in->data.flash_args.data); +    update_data_out.id = USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG; +    break; + +  case USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL: //for verify +    spi_flash_read(update_data_in->data.flash_args.flash_addr,  update_data_in->data.flash_args.length, update_data_out.data.flash_args.data); +    update_data_out.id = USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG; +    break; + +  case USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL: //for if we ever get the ICAP working +    //should reset via icap_reload_fpga(uint32_t flash_address); +    update_data_out.id = USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG; +    //you should note that if you get a reply packet to this the reset has obviously failed +    icap_reload_fpga(0); +    break; + +//  case USRP2_FW_UPDATE_ID_KTHXBAI: //see ya +//    break; + +  default: //uhhhh +    update_data_out.id = USRP2_FW_UPDATE_ID_WAT; +  } +  send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); +} diff --git a/firmware/microblaze/usrp2p/xilinx_s3_icap.c b/firmware/microblaze/usrp2p/xilinx_s3_icap.c new file mode 100644 index 000000000..50c85231c --- /dev/null +++ b/firmware/microblaze/usrp2p/xilinx_s3_icap.c @@ -0,0 +1,99 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + + +/* Changes required to work for the Spartan-3A series: + * The ICAP interface on the 3A is 8 bits wide, instead of 32. + * Everything is Xilinx standard LSBit-first. + * The operations are all different. + * Commands are 16 bits long, presented to the ICAP interface 8 bits at a time. +*/ + +#include <xilinx_s3_icap.h> +#include <memory_map.h> +#include <spi_flash_private.h> //for READ_CMD + + +/* bit swap end-for-end */ +static inline unsigned char +swap8(unsigned char x) +{ +  unsigned char r = 0; +  r |= (x >> 7) & 0x01; +  r |= (x >> 5) & 0x02; +  r |= (x >> 3) & 0x04; +  r |= (x >> 1) & 0x08; + +  r |= (x << 1) & 0x10; +  r |= (x << 3) & 0x20; +  r |= (x << 5) & 0x40; +  r |= (x << 7) & 0x80; + +  return r; +} + +void +wr_icap(uint8_t x) +{ +    icap_regs->icap = swap8(x); +} + +uint8_t +rd_icap(void) +{ +    return swap8(icap_regs->icap); +} + + +void +icap_reload_fpga(uint32_t flash_address) +{ +    union { +        uint32_t i; +        uint8_t c[4]; +    } t; +    t.i = flash_address; + +    //note! t.c[0] MUST contain the byte-wide read command for the flash device used. +    //for the 25P64, and most other flash devices, this is 0x03. +    t.c[0] = FAST_READ_CMD; + +    //TODO: look up the watchdog timer, ensure it won't fire too soon + +    //UG332 p279 +    wr_icap(0xff); +    wr_icap(0xff); //dummy word, probably unnecessary +    wr_icap(0xAA); +    wr_icap(0x99); //sync word +    wr_icap(0x32); +    wr_icap(0x61); //Type 1 write General 1 (1 word) +    wr_icap(t.c[2]); //bits 15-8 +    wr_icap(t.c[3]); //bits 7-0 +    wr_icap(0x32); +    wr_icap(0x81); //Type 1 write General 2 (1 word) +    wr_icap(t.c[0]); //C0-C8, the byte-wide read command +    wr_icap(t.c[1]); //Upper 8 bits of 24-bit address +    wr_icap(0x30); +    wr_icap(0xA1); //Type 1 write CMD (1 word) +    wr_icap(0x00); +    wr_icap(0x0E); //REBOOT command +    wr_icap(0x20); +    wr_icap(0x00); //Type 1 NOP +    wr_icap(0x20); +    wr_icap(0x00); +} diff --git a/firmware/microblaze/usrp2p/xilinx_s3_icap.h b/firmware/microblaze/usrp2p/xilinx_s3_icap.h new file mode 100644 index 000000000..7b7e9eccc --- /dev/null +++ b/firmware/microblaze/usrp2p/xilinx_s3_icap.h @@ -0,0 +1,37 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_XILINX_S3_ICAP_H +#define INCLUDED_XILINX_S3_ICAP_H + +#include <stdint.h> + + +void wr_icap(uint8_t x); +uint8_t rd_icap(void); + +//int icap_read_config_reg(int regno); + +/* + * Attempt to reload the fpga from \p flash_address. + * Shouldn't return, but might. + */ +void icap_reload_fpga(uint32_t flash_address); + + +#endif /* INCLUDED_XILINX_S3_ICAP_H */ diff --git a/host/lib/gain_group.cpp b/host/lib/gain_group.cpp new file mode 100644 index 000000000..1be09dee2 --- /dev/null +++ b/host/lib/gain_group.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/gain_group.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/assert.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <algorithm> +#include <vector> +#include <iostream> + +using namespace uhd; + +static const bool verbose = false; + +static bool compare_by_step_size( +    const size_t &rhs, const size_t &lhs, std::vector<gain_fcns_t> &fcns +){ +    return fcns.at(rhs).get_range().step > fcns.at(lhs).get_range().step; +} + +/*********************************************************************** + * gain group implementation + **********************************************************************/ +class gain_group_impl : public gain_group{ +public: +    gain_group_impl(void){ +        /*NOP*/ +    } + +    gain_range_t get_range(void){ +        float overall_min = 0, overall_max = 0, overall_step = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){ +            const gain_range_t range = fcns.get_range(); +            overall_min += range.min; +            overall_max += range.max; +            //the overall step is the min (zero is invalid, first run) +            if (overall_step == 0) overall_step = range.step; +            overall_step = std::min(overall_step, range.step); +        } +        return gain_range_t(overall_min, overall_max, overall_step); +    } + +    float get_value(void){ +        float overall_gain = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, get_all_fcns()){ +            overall_gain += fcns.get_value(); +        } +        return overall_gain; +    } + +    void set_value(float gain){ +        std::vector<gain_fcns_t> all_fcns = get_all_fcns(); +        if (all_fcns.size() == 0) return; //nothing to set! + +        //get the max step size among the gains +        float max_step = 0; +        BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){ +            max_step = std::max(max_step, fcns.get_range().step); +        } + +        //create gain bucket to distribute power +        std::vector<float> gain_bucket; + +        //distribute power according to priority (round to max step) +        float gain_left_to_distribute = gain; +        BOOST_FOREACH(const gain_fcns_t &fcns, all_fcns){ +            const gain_range_t range = fcns.get_range(); +            gain_bucket.push_back( +                max_step*int(std::clip(gain_left_to_distribute, range.min, range.max)/max_step) +            ); +            gain_left_to_distribute -= gain_bucket.back(); +        } + +        //get a list of indexes sorted by step size large to small +        std::vector<size_t> indexes_step_size_dec; +        for (size_t i = 0; i < all_fcns.size(); i++){ +            indexes_step_size_dec.push_back(i); +        } +        std::sort( +            indexes_step_size_dec.begin(), indexes_step_size_dec.end(), +            boost::bind(&compare_by_step_size, _1, _2, all_fcns) +        ); +        UHD_ASSERT_THROW( +            all_fcns.at(indexes_step_size_dec.front()).get_range().step >= +            all_fcns.at(indexes_step_size_dec.back()).get_range().step +        ); + +        //distribute the remainder (less than max step) +        //fill in the largest step sizes first that are less than the remainder +        BOOST_FOREACH(size_t i, indexes_step_size_dec){ +            const gain_range_t range = all_fcns.at(i).get_range(); +            float additional_gain = range.step*int( +                std::clip(gain_bucket.at(i) + gain_left_to_distribute, range.min, range.max +            )/range.step) - gain_bucket.at(i); +            gain_bucket.at(i) += additional_gain; +            gain_left_to_distribute -= additional_gain; +        } +        if (verbose) std::cout << "gain_left_to_distribute " << gain_left_to_distribute << std::endl; + +        //now write the bucket out to the individual gain values +        for (size_t i = 0; i < gain_bucket.size(); i++){ +            if (verbose) std::cout << gain_bucket.at(i) << std::endl; +            all_fcns.at(i).set_value(gain_bucket.at(i)); +        } +    } + +    void register_fcns( +        const gain_fcns_t &gain_fcns, size_t priority +    ){ +        _registry[priority].push_back(gain_fcns); +    } + +private: +    //! get the gain function sets in order (highest priority first) +    std::vector<gain_fcns_t> get_all_fcns(void){ +        std::vector<gain_fcns_t> all_fcns; +        BOOST_FOREACH(ssize_t key, std::sorted(_registry.keys())){ +            const std::vector<gain_fcns_t> &fcns = _registry[key]; +            all_fcns.insert(all_fcns.begin(), fcns.begin(), fcns.end()); +        } +        return all_fcns; +    } + +    uhd::dict<size_t, std::vector<gain_fcns_t> > _registry; +}; + +/*********************************************************************** + * gain group factory function + **********************************************************************/ +gain_group::sptr gain_group::make(void){ +    return sptr(new gain_group_impl()); +} diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index a328fa033..ac051b843 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -80,6 +80,11 @@ LIBUHD_PYTHON_GEN_SOURCE(  )  LIBUHD_PYTHON_GEN_SOURCE( +    ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_ads62p44_regs.py +    ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/ads62p44_regs.hpp +    ) + +LIBUHD_PYTHON_GEN_SOURCE(      ${CMAKE_SOURCE_DIR}/lib/ic_reg_maps/gen_tuner_4937di5_regs.py      ${CMAKE_BINARY_DIR}/lib/ic_reg_maps/tuner_4937di5_regs.hpp  ) diff --git a/host/lib/ic_reg_maps/gen_ads62p44_regs.py b/host/lib/ic_reg_maps/gen_ads62p44_regs.py new file mode 100755 index 000000000..f0a84d940 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_ads62p44_regs.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# 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, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## address 0 +######################################################################## +reset                   0[1]     0       +serial_readout          0[0]     0       +######################################################################## +## address 16 +######################################################################## +clkout_strength         16[6:7]   0      weaker=1, default=0, stronger=3 +######################################################################## +## address 17 +######################################################################## +dataout_strength        17[0:1]   0      weaker=1, default=0, stronger=3, maximum=2 +lvds_current            17[2:3]   0      3_5ma, 2_5ma, 4_5ma, 1_75ma +lvds_current_double     17[4:5]   0      default, dblclk, dbldataclock +######################################################################## +## address 18 +######################################################################## +lvds_clk_term           18[0:2]   0      none, 300, 180, 110, 150, 100, 81, 60 +lvds_data_term          18[3:5]   0      none, 300, 180, 110, 150, 100, 81, 60 +######################################################################## +## address 19 +######################################################################## +offset_freeze           19[4]     0 +######################################################################## +## address 20 +######################################################################## +power_down              20[0:2]   0      normal, a_dis, b_dis, ab_dis, global_pd, a_sby, b_sby, mux +ref_select              20[3]     0      internal, external +coarse_gain             20[4]     0      0db, 3_5db +output_interface        20[5]     0      cmos, lvds +override                20[7]     0 +######################################################################## +## address 22 +######################################################################## +test_patterns           22[0:2]   0      normal, zeros, ones, toggle, ramp, custom +lvds_bytewise           22[3]     0 +data_format             22[4]     0      twos_complement, binary +######################################################################## +## address 23 +######################################################################## +fine_gain               23[0:3]   0 +######################################################################## +## address 24 and 25 +######################################################################## +custom_low              24[0:7]   0 +custom_high             25[0:5]   0 +######################################################################## +## address 26 +######################################################################## +gain_correction         26[0:3]   0 +offset_tc               26[4:6]   0      1_1s, 0_55s, 0_27s, 0_13s, 2_15s, 4_3s +low_latency             26[7]     0 +######################################################################## +## address 27 +######################################################################## +decimation              27[0:2]   0      decimate_2, decimate_4, decimate_1, decimate_8 +odd_tap_enable          27[3]     0 +filter_enable           27[4]     0 +filter_coeff_sel        27[5]     0      predefined, userdefined +offset_enable           27[7]     0 +######################################################################## +## address 29 +######################################################################## +decimation_filter_bands 29[0:1]   0 +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +boost::uint8_t get_reg(boost::uint8_t addr){ +    boost::uint8_t reg = 0; +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    case $addr: +        #for $reg in filter(lambda r: r.get_addr() == addr, $regs) +        reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); +        #end for +        break; +    #end for +    } +    return reg; +} + +boost::uint16_t get_write_reg(boost::uint8_t addr){ +    return (boost::uint16_t(addr) << 8) | get_reg(addr); +} + +boost::uint16_t get_read_reg(boost::uint8_t addr){ +    return (boost::uint16_t(addr) << 8) | (1 << 7); +} +""" + +if __name__ == '__main__': +    import common; common.generate( +        name='ads62p44_regs', +        regs_tmpl=REGS_TMPL, +        body_tmpl=BODY_TMPL, +        file=__file__, +    ) diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index 078485d6a..f7984fce5 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -44,6 +44,8 @@ IF(ENABLE_USRP2)          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_iface.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dsp_impl.cpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/gps_ctrl.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/gps_ctrl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/io_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/mboard_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/serdes_ctrl.cpp @@ -53,6 +55,7 @@ IF(ENABLE_USRP2)          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.hpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_regs.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_regs.cpp      )  ELSE(ENABLE_USRP2)      MESSAGE(STATUS "  Skipping USRP2 support.") diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index 232f3b32a..1f8e65ce6 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -18,6 +18,7 @@  #include "clock_ctrl.hpp"  #include "ad9510_regs.hpp"  #include "usrp2_regs.hpp" //spi slave constants +#include "usrp2_clk_regs.hpp"  #include <uhd/utils/assert.hpp>  #include <boost/cstdint.hpp>  #include <boost/lexical_cast.hpp> @@ -32,9 +33,10 @@ class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{  public:      usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){          _iface = iface; +        clk_regs = usrp2_clk_regs_t(_iface->get_hw_rev());          _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; -        this->write_reg(0x09); +        this->write_reg(clk_regs.pll_3);          // Setup the clock registers to 100MHz:          //  This was already done by the firmware (or the host couldnt communicate). @@ -44,20 +46,20 @@ public:          _ad9510_regs.pll_power_down = ad9510_regs_t::PLL_POWER_DOWN_NORMAL;          _ad9510_regs.prescaler_value = ad9510_regs_t::PRESCALER_VALUE_DIV2; -        this->write_reg(0x0A); +        this->write_reg(clk_regs.pll_4);          _ad9510_regs.acounter = 0; -        this->write_reg(0x04); +        this->write_reg(clk_regs.acounter);          _ad9510_regs.bcounter_msb = 0;          _ad9510_regs.bcounter_lsb = 5; -        this->write_reg(0x05); -        this->write_reg(0x06); +        this->write_reg(clk_regs.bcounter_msb); +        this->write_reg(clk_regs.bcounter_lsb);          _ad9510_regs.ref_counter_msb = 0;          _ad9510_regs.ref_counter_lsb = 1; // r divider = 1 -        this->write_reg(0x0B); -        this->write_reg(0x0C); +        this->write_reg(clk_regs.ref_counter_msb); +        this->write_reg(clk_regs.ref_counter_lsb);          /* regs will be updated in commands below */ @@ -84,16 +86,13 @@ public:      }      void enable_mimo_clock_out(bool enb){ -        boost::uint16_t rev = boost::lexical_cast<boost::uint16_t>(_iface->mb_eeprom["rev"]); -        boost::uint8_t rev_hi = boost::uint8_t(rev >> 8); -          //calculate the low and high dividers          size_t divider = size_t(this->get_master_clock_rate()/10e6);          size_t high = divider/2;          size_t low = divider - high; -        switch(rev_hi){ -        case 3: //clock 2 +        switch(clk_regs.exp){ +        case 2: //U2 rev 3              _ad9510_regs.power_down_lvpecl_out2 = enb?                  ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_NORMAL :                  ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_SAFE_PD; @@ -102,11 +101,9 @@ public:              _ad9510_regs.divider_low_cycles_out2 = low - 1;              _ad9510_regs.divider_high_cycles_out2 = high - 1;              _ad9510_regs.bypass_divider_out2 = 0; -            this->write_reg(0x3e); -            this->write_reg(0x4c);              break; -        case 4: //clock 5 +        case 5: //U2 rev 4              _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1;              _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_LVDS;              _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA; @@ -114,14 +111,23 @@ public:              _ad9510_regs.divider_low_cycles_out5 = low - 1;              _ad9510_regs.divider_high_cycles_out5 = high - 1;              _ad9510_regs.bypass_divider_out5 = 0; -            this->write_reg(0x41); -            this->write_reg(0x52); +            break; +             +        case 6: //U2+ +            _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1; +            _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_LVDS; +            _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA; +            //set the registers (divider - 1) +            _ad9510_regs.divider_low_cycles_out6 = low - 1; +            _ad9510_regs.divider_high_cycles_out6 = high - 1; +            _ad9510_regs.bypass_divider_out5 = 0;              break; -        //TODO FIXME do i want to throw, what about uninitialized boards? -        //default: throw std::runtime_error("unknown rev hi in mboard eeprom"); -        default: std::cerr << "unknown rev hi: " << rev_hi << std::endl; +        default: +            break;          } +        this->write_reg(clk_regs.output(clk_regs.exp)); +        this->write_reg(clk_regs.div_lo(clk_regs.exp));          this->update_regs();      } @@ -130,7 +136,7 @@ public:          _ad9510_regs.power_down_lvds_cmos_out7 = enb? 0 : 1;          _ad9510_regs.lvds_cmos_select_out7 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT7_CMOS;          _ad9510_regs.output_level_lvds_out7 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT7_1_75MA; -        this->write_reg(0x43); +        this->write_reg(clk_regs.output(clk_regs.rx_db));          this->update_regs();      } @@ -146,8 +152,8 @@ public:          _ad9510_regs.divider_low_cycles_out7 = low - 1;          _ad9510_regs.divider_high_cycles_out7 = high - 1;          //write the registers -        this->write_reg(0x56); -        this->write_reg(0x57); +        this->write_reg(clk_regs.div_lo(clk_regs.rx_db)); +        this->write_reg(clk_regs.div_hi(clk_regs.rx_db));          this->update_regs();      } @@ -157,12 +163,22 @@ public:          return rates;      } -    //uses output clock 6 (cmos) +    //uses output clock 6 (cmos) on USRP2 and output clock 5 (cmos) on USRP2+      void enable_tx_dboard_clock(bool enb){ -        _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1; -        _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_CMOS; -        _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA; -        this->write_reg(0x42); +        switch(clk_regs.tx_db) { +        case 5: //USRP2+ +          _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1; +          _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_CMOS; +          _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA; +          break; +        case 6: //USRP2 +          _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1; +          _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_CMOS; +          _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA; +          break; +        } + +        this->write_reg(clk_regs.output(clk_regs.tx_db));          this->update_regs();      } @@ -174,18 +190,44 @@ public:          //calculate the low and high dividers          size_t high = divider/2;          size_t low = divider - high; -        //set the registers (divider - 1) -        _ad9510_regs.divider_low_cycles_out6 = low - 1; -        _ad9510_regs.divider_high_cycles_out6 = high - 1; + +        switch(clk_regs.tx_db) { +        case 5: //USRP2+ +          _ad9510_regs.bypass_divider_out5 = (divider == 1)? 1 : 0; +          _ad9510_regs.divider_low_cycles_out5 = low - 1; +          _ad9510_regs.divider_high_cycles_out5 = high - 1; +          break; +        case 6: //USRP2 +          //bypass when the divider ratio is one +          _ad9510_regs.bypass_divider_out6 = (divider == 1)? 1 : 0; +          //set the registers (divider - 1) +          _ad9510_regs.divider_low_cycles_out6 = low - 1; +          _ad9510_regs.divider_high_cycles_out6 = high - 1; +          break; +        } +          //write the registers -        this->write_reg(0x54); -        this->write_reg(0x55); +        this->write_reg(clk_regs.div_hi(clk_regs.tx_db)); +        this->write_reg(clk_regs.div_lo(clk_regs.tx_db));          this->update_regs();      }      std::vector<double> get_rates_tx_dboard_clock(void){          return get_rates_rx_dboard_clock(); //same master clock, same dividers...      } +     +    void enable_test_clock(bool enb) { +        _ad9510_regs.power_down_lvpecl_out0 = enb? +            ad9510_regs_t::POWER_DOWN_LVPECL_OUT0_NORMAL : +            ad9510_regs_t::POWER_DOWN_LVPECL_OUT0_SAFE_PD; +        _ad9510_regs.output_level_lvpecl_out0 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT0_810MV; +        _ad9510_regs.divider_low_cycles_out0 = 0; +        _ad9510_regs.divider_high_cycles_out0 = 0; +        _ad9510_regs.bypass_divider_out0 = 1; +        this->write_reg(0x3c); +        this->write_reg(0x48); +        this->write_reg(0x49); +    }      /*!       * If we are to use an external reference, enable the charge pump. @@ -197,7 +239,7 @@ public:              ad9510_regs_t::CHARGE_PUMP_MODE_3STATE ;          _ad9510_regs.pll_mux_control = ad9510_regs_t::PLL_MUX_CONTROL_DLD_HIGH;          _ad9510_regs.pfd_polarity = ad9510_regs_t::PFD_POLARITY_POS; -        this->write_reg(0x08); +        this->write_reg(clk_regs.pll_2);          this->update_regs();      } @@ -220,33 +262,46 @@ private:       */      void update_regs(void){          _ad9510_regs.update_registers = 1; -        this->write_reg(0x5a); +        this->write_reg(clk_regs.update);      }      //uses output clock 3 (pecl) +    //this is the same between USRP2 and USRP2+ and doesn't get a switch statement      void enable_dac_clock(bool enb){          _ad9510_regs.power_down_lvpecl_out3 = (enb)?              ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_NORMAL :              ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_SAFE_PD;          _ad9510_regs.output_level_lvpecl_out3 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT3_810MV;          _ad9510_regs.bypass_divider_out3 = 1; -        this->write_reg(0x3F); -        this->write_reg(0x4F); +        this->write_reg(clk_regs.output(clk_regs.dac)); +        this->write_reg(clk_regs.div_hi(clk_regs.dac));          this->update_regs();      } -    //uses output clock 4 (lvds) +    //uses output clock 4 (lvds) on USRP2 and output clock 2 (lvpecl) on USRP2+      void enable_adc_clock(bool enb){ -        _ad9510_regs.power_down_lvds_cmos_out4 = enb? 0 : 1; -        _ad9510_regs.lvds_cmos_select_out4 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT4_LVDS; -        _ad9510_regs.output_level_lvds_out4 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT4_1_75MA; -        _ad9510_regs.bypass_divider_out4 = 1; -        this->write_reg(0x40); -        this->write_reg(0x51); +        switch(clk_regs.adc) { +        case 2: +          _ad9510_regs.power_down_lvpecl_out2 = enb? ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_NORMAL : ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_SAFE_PD; +          _ad9510_regs.output_level_lvpecl_out2 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT2_500MV; +          _ad9510_regs.bypass_divider_out2 = 1; +          break; +        case 4: +          _ad9510_regs.power_down_lvds_cmos_out4 = enb? 0 : 1; +          _ad9510_regs.lvds_cmos_select_out4 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT4_LVDS; +          _ad9510_regs.output_level_lvds_out4 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT4_1_75MA; +          _ad9510_regs.bypass_divider_out4 = 1; +          break; +        } + +        this->write_reg(clk_regs.output(clk_regs.adc)); +        this->write_reg(clk_regs.div_hi(clk_regs.adc));          this->update_regs();      } - +          usrp2_iface::sptr _iface; + +    usrp2_clk_regs_t clk_regs;      ad9510_regs_t _ad9510_regs;  }; diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index 70a104a81..db6c52c83 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -83,6 +83,12 @@ public:       * \param enb true to enable       */      virtual void enable_external_ref(bool enb) = 0; +     +    /*! +     * Enable/disable test clock output. +     * \param enb true to enable +     */ +    virtual void enable_test_clock(bool enb) = 0;      /*!       * TODO other clock control api here.... diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index 32cc13ded..8680c285a 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -17,10 +17,12 @@  #include "codec_ctrl.hpp"  #include "ad9777_regs.hpp" +#include "ads62p44_regs.hpp"  #include "usrp2_regs.hpp"  #include <boost/cstdint.hpp>  #include <boost/foreach.hpp>  #include <iostream> +#include <uhd/utils/exception.hpp>  static const bool codec_ctrl_debug = false; @@ -57,7 +59,16 @@ public:          }          //power-up adc -        _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_ON); +        if(!_iface->is_usrp2p()) { //if we're on a USRP2 +          _iface->poke32(_iface->regs.misc_ctrl_adc, U2_FLAG_MISC_CTRL_ADC_ON); +        } else { //we're on a USRP2+ +          _ads62p44_regs.reset = 1; +          this->send_ads62p44_reg(0x00); //issue a reset to the ADC +          //everything else should be pretty much default, i think +//          _ads62p44_regs.decimation = DECIMATION_DECIMATE_1; +          _ads62p44_regs.power_down = ads62p44_regs_t::POWER_DOWN_NORMAL; +          this->send_ads62p44_reg(0x14); +        }      }      ~usrp2_codec_ctrl_impl(void){ @@ -66,11 +77,39 @@ public:          this->send_ad9777_reg(0);          //power-down adc -        _iface->poke32(U2_REG_MISC_CTRL_ADC, U2_FLAG_MISC_CTRL_ADC_OFF); +        if(!_iface->is_usrp2p()) { //if we're on a USRP2 +          _iface->poke32(_iface->regs.misc_ctrl_adc, U2_FLAG_MISC_CTRL_ADC_OFF); +        } else { //we're on a USRP2+ +          //send a global power-down to the ADC here... it will get lifted on reset +          _ads62p44_regs.power_down = ads62p44_regs_t::POWER_DOWN_GLOBAL_PD; +          this->send_ads62p44_reg(0x14); +        } +    } + +    void set_rx_digital_gain(float gain) {  //fine digital gain +      if(_iface->is_usrp2p()) { +        _ads62p44_regs.fine_gain = int(gain/0.5); +        this->send_ads62p44_reg(0x17); +      } else UHD_THROW_INVALID_CODE_PATH(); //should never have been called for USRP2 +    } + +    void set_rx_digital_fine_gain(float gain) { //gain correction       +      if(_iface->is_usrp2p()) { +        _ads62p44_regs.gain_correction = int(gain / 0.05); +        this->send_ads62p44_reg(0x1A); +      } else UHD_THROW_INVALID_CODE_PATH(); //should never have been called for USRP2 +    } + +    void set_rx_analog_gain(bool gain) { //turns on/off analog 3.5dB preamp +      if(_iface->is_usrp2p()) { +        _ads62p44_regs.coarse_gain = gain ? ads62p44_regs_t::COARSE_GAIN_3_5DB : ads62p44_regs_t::COARSE_GAIN_0DB; +        this->send_ads62p44_reg(0x14); +      } else UHD_THROW_INVALID_CODE_PATH();      }  private:      ad9777_regs_t _ad9777_regs; +    ads62p44_regs_t _ads62p44_regs;      usrp2_iface::sptr _iface;      void send_ad9777_reg(boost::uint8_t addr){ @@ -81,6 +120,14 @@ private:              reg, 16, false /*no rb*/          );      } + +    void send_ads62p44_reg(boost::uint8_t addr) { +        boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr); +        _iface->transact_spi( +            SPI_SS_ADS62P44, spi_config_t::EDGE_FALL, +            reg, 16, false /*no rb*/ +        ); +    }  };  /*********************************************************************** diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp index ad014e0e1..57a37b94b 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.hpp +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -33,6 +33,27 @@ public:       */      static sptr make(usrp2_iface::sptr iface); +    /*! +     * Set the analog preamplifier on the USRP2+ ADC (ADS62P44). +     * \param gain enable or disable the 3.5dB preamp +     */ + +    virtual void set_rx_analog_gain(bool gain) = 0; + +    /*! +     * Set the digital gain on the USRP2+ ADC (ADS62P44). +     * \param gain from 0-6dB +     */ + +    virtual void set_rx_digital_gain(float gain) = 0; + +    /*! +     * Set the digital gain correction on the USRP2+ ADC (ADS62P44). +     * \param gain from 0-0.5dB +     */ + +    virtual void set_rx_digital_fine_gain(float gain) = 0; +  };  #endif /* INCLUDED_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/codec_impl.cpp b/host/lib/usrp/usrp2/codec_impl.cpp index fc917b102..1c1f60765 100644 --- a/host/lib/usrp/usrp2/codec_impl.cpp +++ b/host/lib/usrp/usrp2/codec_impl.cpp @@ -17,10 +17,23 @@  #include "usrp2_impl.hpp"  #include <uhd/usrp/codec_props.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp>  #include <boost/bind.hpp> +#include <boost/assign/list_of.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/exception.hpp>  using namespace uhd;  using namespace uhd::usrp; +using namespace boost::assign; + +//this only applies to USRP2P +static const uhd::dict<std::string, gain_range_t> codec_rx_gain_ranges = map_list_of +                                  ("analog", gain_range_t(0, 3.5, 3.5)) +                                  ("digital", gain_range_t(0, 6.0, 0.5)) +				  ("digital-fine", gain_range_t(0, 0.5, 0.05)); +  /***********************************************************************   * Helper Methods @@ -40,7 +53,8 @@ void usrp2_mboard_impl::codec_init(void){  /***********************************************************************   * RX Codec Properties   **********************************************************************/ -void usrp2_mboard_impl::rx_codec_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_);      //handle the get request conditioned on the key      switch(key.as<codec_prop_t>()){ @@ -53,21 +67,74 @@ void usrp2_mboard_impl::rx_codec_get(const wax::obj &key, wax::obj &val){          return;      case CODEC_PROP_GAIN_NAMES: -        val = prop_names_t(); //no gain elements to be controlled +        if(_iface->is_usrp2p()) { +          val = prop_names_t(codec_rx_gain_ranges.keys()); +        } else val = prop_names_t(); +        return; + +    case CODEC_PROP_GAIN_I: +    case CODEC_PROP_GAIN_Q: +        assert_has(_codec_rx_gains.keys(), key.name, "codec rx gain name"); +        val = _codec_rx_gains[key.name];          return; +    case CODEC_PROP_GAIN_RANGE: +      assert_has(codec_rx_gain_ranges.keys(), key.name, "codec rx gain range name"); +      val = codec_rx_gain_ranges[key.name]; +      return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } -void usrp2_mboard_impl::rx_codec_set(const wax::obj &, const wax::obj &){ -    UHD_THROW_PROP_SET_ERROR(); +void usrp2_mboard_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +    float gain; + +  switch(key.as<codec_prop_t>()) { +    case CODEC_PROP_GAIN_I: +    case CODEC_PROP_GAIN_Q: +      if(!_iface->is_usrp2p()) UHD_THROW_PROP_SET_ERROR();//this capability is only found in USRP2P + +      gain = val.as<float>(); +      this->rx_codec_set_gain(gain, key.name); +      return; + +    default: +      UHD_THROW_PROP_SET_ERROR(); +  } +} + +/*********************************************************************** + * Helper function to set RX codec gain + ***********************************************************************/ + +void usrp2_mboard_impl::rx_codec_set_gain(float gain, const std::string &name){ +  assert_has(codec_rx_gain_ranges.keys(), name, "codec rx gain name"); + +  _codec_rx_gains[name] = gain; + +  if(name == "analog") { +    _codec_ctrl->set_rx_analog_gain(gain > 0); //just turn it on or off +    return; +  } +  if(name == "digital") { +    _codec_ctrl->set_rx_digital_gain(gain); +    return; +  } +  if(name == "digital-fine") { +    _codec_ctrl->set_rx_digital_fine_gain(gain); +    return; +  } +  UHD_THROW_PROP_SET_ERROR();  } +  /***********************************************************************   * TX Codec Properties   **********************************************************************/ -void usrp2_mboard_impl::tx_codec_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::tx_codec_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_);      //handle the get request conditioned on the key      switch(key.as<codec_prop_t>()){ diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index fdfbf0d17..ab5f62355 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -180,8 +180,8 @@ void usrp2_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value){      //write the selection mux value to register      switch(unit){ -    case UNIT_RX: _iface->poke32(U2_REG_GPIO_RX_SEL, new_sels); return; -    case UNIT_TX: _iface->poke32(U2_REG_GPIO_TX_SEL, new_sels); return; +    case UNIT_RX: _iface->poke32(_iface->regs.gpio_rx_sel, new_sels); return; +    case UNIT_TX: _iface->poke32(_iface->regs.gpio_tx_sel, new_sels); return;      }  } @@ -189,18 +189,18 @@ void usrp2_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value){      _ddr_shadow = \          (_ddr_shadow & ~(0xffff << unit_to_shift[unit])) |          (boost::uint32_t(value) << unit_to_shift[unit]); -    _iface->poke32(U2_REG_GPIO_DDR, _ddr_shadow); +    _iface->poke32(_iface->regs.gpio_ddr, _ddr_shadow);  }  void usrp2_dboard_iface::write_gpio(unit_t unit, boost::uint16_t value){      _gpio_shadow = \          (_gpio_shadow & ~(0xffff << unit_to_shift[unit])) |          (boost::uint32_t(value) << unit_to_shift[unit]); -    _iface->poke32(U2_REG_GPIO_IO, _gpio_shadow); +    _iface->poke32(_iface->regs.gpio_io, _gpio_shadow);  }  boost::uint16_t usrp2_dboard_iface::read_gpio(unit_t unit){ -    return boost::uint16_t(_iface->peek32(U2_REG_GPIO_IO) >> unit_to_shift[unit]); +    return boost::uint16_t(_iface->peek32(_iface->regs.gpio_io) >> unit_to_shift[unit]);  }  void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ @@ -209,16 +209,16 @@ void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t          unit_t, uhd::dict<atr_reg_t, boost::uint32_t>      > unit_to_atr_to_addr = map_list_of          (UNIT_RX, map_list_of -            (ATR_REG_IDLE,        U2_REG_ATR_IDLE_RXSIDE) -            (ATR_REG_TX_ONLY,     U2_REG_ATR_INTX_RXSIDE) -            (ATR_REG_RX_ONLY,     U2_REG_ATR_INRX_RXSIDE) -            (ATR_REG_FULL_DUPLEX, U2_REG_ATR_FULL_RXSIDE) +            (ATR_REG_IDLE,        _iface->regs.atr_idle_rxside) +            (ATR_REG_TX_ONLY,     _iface->regs.atr_intx_rxside) +            (ATR_REG_RX_ONLY,     _iface->regs.atr_inrx_rxside) +            (ATR_REG_FULL_DUPLEX, _iface->regs.atr_full_rxside)          )          (UNIT_TX, map_list_of -            (ATR_REG_IDLE,        U2_REG_ATR_IDLE_TXSIDE) -            (ATR_REG_TX_ONLY,     U2_REG_ATR_INTX_TXSIDE) -            (ATR_REG_RX_ONLY,     U2_REG_ATR_INRX_TXSIDE) -            (ATR_REG_FULL_DUPLEX, U2_REG_ATR_FULL_TXSIDE) +            (ATR_REG_IDLE,        _iface->regs.atr_idle_txside) +            (ATR_REG_TX_ONLY,     _iface->regs.atr_intx_txside) +            (ATR_REG_RX_ONLY,     _iface->regs.atr_inrx_txside) +            (ATR_REG_FULL_DUPLEX, _iface->regs.atr_full_txside)          )      ;      _iface->poke16(unit_to_atr_to_addr[unit][atr], value); @@ -238,8 +238,8 @@ void usrp2_dboard_iface::set_gpio_debug(unit_t unit, int which){      //write the selection mux value to register      switch(unit){ -    case UNIT_RX: _iface->poke32(U2_REG_GPIO_RX_SEL, new_sels); return; -    case UNIT_TX: _iface->poke32(U2_REG_GPIO_TX_SEL, new_sels); return; +    case UNIT_RX: _iface->poke32(_iface->regs.gpio_rx_sel, new_sels); return; +    case UNIT_TX: _iface->poke32(_iface->regs.gpio_tx_sel, new_sels); return;      }  } diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 0c85e643f..c8da03955 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -95,7 +95,7 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val){      case DSP_PROP_FREQ_SHIFT:{              double new_freq = val.as<double>(); -            _iface->poke32(U2_REG_DSP_RX_FREQ, +            _iface->poke32(_iface->regs.dsp_rx_freq,                  dsp_type1::calc_cordic_word_and_update(new_freq, get_master_clock_freq())              );              _ddc_freq = new_freq; //shadow @@ -107,11 +107,11 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val){              _ddc_decim = pick_closest_rate(extact_rate, _allowed_decim_and_interp_rates);              //set the decimation -            _iface->poke32(U2_REG_DSP_RX_DECIM_RATE, dsp_type1::calc_cic_filter_word(_ddc_decim)); +            _iface->poke32(_iface->regs.dsp_rx_decim_rate, dsp_type1::calc_cic_filter_word(_ddc_decim));              //set the scaling              static const boost::int16_t default_rx_scale_iq = 1024; -            _iface->poke32(U2_REG_DSP_RX_SCALE_IQ, +            _iface->poke32(_iface->regs.dsp_rx_scale_iq,                  dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq)              );          } @@ -178,7 +178,7 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key_, const wax::obj &val){      case DSP_PROP_FREQ_SHIFT:{              double new_freq = val.as<double>(); -            _iface->poke32(U2_REG_DSP_TX_FREQ, +            _iface->poke32(_iface->regs.dsp_tx_freq,                  dsp_type1::calc_cordic_word_and_update(new_freq, get_master_clock_freq())              );              _duc_freq = new_freq; //shadow @@ -190,10 +190,10 @@ void usrp2_mboard_impl::duc_set(const wax::obj &key_, const wax::obj &val){              _duc_interp = pick_closest_rate(extact_rate, _allowed_decim_and_interp_rates);              //set the interpolation -            _iface->poke32(U2_REG_DSP_TX_INTERP_RATE, dsp_type1::calc_cic_filter_word(_duc_interp)); +            _iface->poke32(_iface->regs.dsp_tx_interp_rate, dsp_type1::calc_cic_filter_word(_duc_interp));              //set the scaling -            _iface->poke32(U2_REG_DSP_TX_SCALE_IQ, dsp_type1::calc_iq_scale_word(_duc_interp)); +            _iface->poke32(_iface->regs.dsp_tx_scale_iq, dsp_type1::calc_iq_scale_word(_duc_interp));          }          return; diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 4ff31ddfd..6c9596092 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -52,6 +52,14 @@ extern "C" {  #define	USRP2_I2C_ADDR_TX_DB  (USRP2_I2C_DEV_EEPROM | 0x4)  #define	USRP2_I2C_ADDR_RX_DB  (USRP2_I2C_DEV_EEPROM | 0x5) +//////////////////////////////////////////////////////////////////////// +// EEPROM Layout +//////////////////////////////////////////////////////////////////////// +#define USRP2_EE_MBOARD_REV      0x00 //2 bytes, little-endian (historic, don't blame me) +#define USRP2_EE_MBOARD_MAC_ADDR 0x02 //6 bytes +#define USRP2_EE_MBOARD_IP_ADDR  0x0C //uint32, big-endian +#define USRP2_EE_MBOARD_BOOTLOADER_FLAGS 0xF7 +  typedef enum{      USRP2_CTRL_ID_HUH_WHAT = ' ',      //USRP2_CTRL_ID_FOR_SURE, //TODO error condition enums @@ -75,6 +83,12 @@ typedef enum{      USRP2_CTRL_ID_PEEK_AT_THIS_REGISTER_FOR_ME_BRO = 'r',      USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE = 'R', +    USRP2_CTRL_ID_HEY_WRITE_THIS_UART_FOR_ME_BRO = 'u', +    USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE = 'U', + +    USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO = 'v', +    USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE = 'V', +      USRP2_CTRL_ID_PEACE_OUT = '~'  } usrp2_ctrl_id_t; @@ -115,6 +129,11 @@ typedef struct{              __stdint(uint32_t) datahi;              __stdint(uint8_t) num_bytes; //1, 2, 4, 8          } poke_args; +        struct { +            __stdint(uint8_t) dev; +            __stdint(uint8_t) bytes; +            __stdint(uint8_t) data[20]; +        } uart_args;      } data;  } usrp2_ctrl_data_t; diff --git a/host/lib/usrp/usrp2/gps_ctrl.cpp b/host/lib/usrp/usrp2/gps_ctrl.cpp new file mode 100644 index 000000000..2273b2cd9 --- /dev/null +++ b/host/lib/usrp/usrp2/gps_ctrl.cpp @@ -0,0 +1,207 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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, see <http://www.gnu.org/licenses/>. +// + +#include "gps_ctrl.hpp" +#include <uhd/utils/assert.hpp> +#include <boost/cstdint.hpp> +#include <string> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/thread.hpp> +#include <boost/algorithm/string/trim.hpp> +#include <boost/tokenizer.hpp> + +using namespace uhd; +using namespace boost::gregorian; +using namespace boost::posix_time; +using namespace boost::algorithm; + +/*! + * A usrp2 GPS control for Jackson Labs devices + */ + +//TODO: multiple baud rate support (requires mboard_impl changes for poking UART registers) +class usrp2_gps_ctrl_impl : public usrp2_gps_ctrl{ +public: +  usrp2_gps_ctrl_impl(usrp2_iface::sptr iface){ +    _iface = iface; + +    std::string reply; +    bool i_heard_some_nmea = false, i_heard_something_weird = false; + +    gps_type = GPS_TYPE_NONE; + +//    set_uart_baud_rate(GPS_UART, 115200); +    //first we look for a Jackson Labs Firefly (since that's what we sell with the USRP2+...) + +    _iface->read_uart(GPS_UART); //get whatever junk is in the rx buffer right now, and throw it away +    _iface->write_uart(GPS_UART, "HAAAY GUYYYYS\n"); //to elicit a response from the Firefly + +    //then we loop until we either timeout, or until we get a response that indicates we're a JL device +    int timeout = GPS_TIMEOUT_TRIES; +    while(timeout--) { +      reply = safe_gps_read(); +      if(trim_right_copy(reply) == "Command Error") { +        gps_type = GPS_TYPE_JACKSON_LABS; +        break; +      } +      else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response +      else if(reply.length() != 0) i_heard_something_weird = true; //probably wrong baud rate +    } + +    if((i_heard_some_nmea) && (gps_type != GPS_TYPE_JACKSON_LABS)) gps_type = GPS_TYPE_GENERIC_NMEA; + +    //otherwise, we can try some other common baud rates looking to see if a GPS is connected (todo, later) +    if((gps_type == GPS_TYPE_NONE) && i_heard_something_weird) { +      std::cout << "Invalid reply, possible incorrect baud rate" << std::endl; +    } + +    bool found_gprmc = false; + +    switch(gps_type) { +    case GPS_TYPE_JACKSON_LABS: +      std::cout << "Found a Jackson Labs GPS" << std::endl; +      //issue some setup stuff so it spits out the appropriate data +      //none of these should issue replies so we don't bother looking for them +      //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command. +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "SYST:COMM:SER:ECHO OFF\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "SYST:COMM:SER:PRO OFF\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "GPS:GPGGA 0\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "GPS:GGAST 0\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); +      _iface->write_uart(GPS_UART, "GPS:GPRMC 1\n"); +       boost::this_thread::sleep(boost::posix_time::milliseconds(FIREFLY_STUPID_DELAY_MS)); + +//      break; + +    case GPS_TYPE_GENERIC_NMEA: +      if(gps_type == GPS_TYPE_GENERIC_NMEA) std::cout << "Found a generic NMEA GPS device" << std::endl; +      found_gprmc = false; +      //here we loop around looking for a GPRMC packet. if we don't get one, we don't have a usable GPS. +      timeout = GPS_TIMEOUT_TRIES; +      while(timeout--) { +        reply = safe_gps_read(); +        if(reply.substr(0, 6) == "$GPRMC") { +          found_gprmc = true; +          break; +        } +      } +      if(!found_gprmc) { +        if(gps_type == GPS_TYPE_JACKSON_LABS) std::cout << "Firefly GPS not locked or warming up." << std::endl; +        else std::cout << "GPS does not output GPRMC packets. Cannot retrieve time." << std::endl; +        gps_type = GPS_TYPE_NONE; +      } +      break; + +    case GPS_TYPE_NONE: +    default: +      break; + +    } + + +  } + +  ~usrp2_gps_ctrl_impl(void){ + +  } + +  std::string safe_gps_read() { +    std::string reply; +    try { +        reply = _iface->read_uart(GPS_UART); +  	    //std::cerr << "Got reply from GPS: " << reply.c_str() << " with length = " << reply.length() << std::endl; +    } catch (std::runtime_error err) { +      if(err.what() != std::string("usrp2 no control response")) throw; //sorry can't cope with that +      else { //we don't actually have a GPS installed +        reply = std::string(); +      } +    } +    return reply; +  } + +  ptime get_time(void) { +    std::string reply; +    ptime now; +    boost::tokenizer<boost::escaped_list_separator<char> > tok(reply); +    std::vector<std::string> toked; +    int timeout = GPS_TIMEOUT_TRIES; +    bool found_gprmc = false; +    switch(gps_type) { +    case GPS_TYPE_JACKSON_LABS: //deprecated in favor of a single NMEA parser +    case GPS_TYPE_GENERIC_NMEA: + +      while(timeout--) { +        reply = safe_gps_read(); +        if(reply.substr(0, 6) == "$GPRMC") { +          found_gprmc = true; +          break; +        } +      } +      UHD_ASSERT_THROW(found_gprmc); + +      tok.assign(reply); +      toked.assign(tok.begin(), tok.end()); + +      UHD_ASSERT_THROW(toked.size() == 11); //if it's not we got something weird in there + +      now = ptime( date(  +                         greg_year(boost::lexical_cast<int>(toked[8].substr(4, 2)) + 2000), //just trust me on this one +                         greg_month(boost::lexical_cast<int>(toked[8].substr(2, 2))),  +                         greg_day(boost::lexical_cast<int>(toked[8].substr(0, 2)))  +                       ), +                   hours(  boost::lexical_cast<int>(toked[1].substr(0, 2))) +                 + minutes(boost::lexical_cast<int>(toked[1].substr(2, 2))) +                 + seconds(boost::lexical_cast<int>(toked[1].substr(4, 2))) +                 ); +      break; +    case GPS_TYPE_NONE: +    default: +      throw std::runtime_error("get_time(): Unsupported GPS or no GPS detected\n"); +      break; +    } +    return now; +  } + +  bool gps_detected(void) { +    return (gps_type != GPS_TYPE_NONE); +  } + +private: +  usrp2_iface::sptr _iface; + +  enum { +    GPS_TYPE_JACKSON_LABS, +    GPS_TYPE_GENERIC_NMEA, +    GPS_TYPE_NONE +  } gps_type; + +  static const int GPS_UART = 2; //TODO: this should be plucked from fw_common.h or memory_map.h or somewhere in common with the firmware +  static const int GPS_TIMEOUT_TRIES = 5; +  static const int FIREFLY_STUPID_DELAY_MS = 200; + +}; + +/*********************************************************************** + * Public make function for the GPS control + **********************************************************************/ +usrp2_gps_ctrl::sptr usrp2_gps_ctrl::make(usrp2_iface::sptr iface){ +    return sptr(new usrp2_gps_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/usrp2/gps_ctrl.hpp b/host/lib/usrp/usrp2/gps_ctrl.hpp new file mode 100644 index 000000000..5936a6fb6 --- /dev/null +++ b/host/lib/usrp/usrp2/gps_ctrl.hpp @@ -0,0 +1,53 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_GPS_CTRL_HPP +#define INCLUDED_GPS_CTRL_HPP + +#include "usrp2_iface.hpp" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> + +using namespace boost::posix_time; + +class usrp2_gps_ctrl : boost::noncopyable{ +public: +  typedef boost::shared_ptr<usrp2_gps_ctrl> sptr; + +  /*! +   * Make a GPS config for Jackson Labs or generic NMEA GPS devices +   */ +  static sptr make(usrp2_iface::sptr iface); + +  /*! +   * Get the current GPS time and date +   * \return current GPS time and date as boost::posix_time::ptime object +   */ +  virtual ptime get_time(void) = 0; + +  /*! +   * Tell you if there's a supported GPS connected or not +   * \return true if a supported GPS is connected +   */ +  virtual bool gps_detected(void) = 0; + +  //TODO: other fun things you can do with a GPS. + +}; + +#endif /* INCLUDED_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index a6ca7f2d3..5b9dd1fa3 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -24,9 +24,11 @@  #include <uhd/utils/algorithm.hpp>  #include <boost/bind.hpp>  #include <iostream> +#include <boost/date_time/posix_time/posix_time.hpp>  using namespace uhd;  using namespace uhd::usrp; +using namespace boost::posix_time;  /***********************************************************************   * Structors @@ -44,6 +46,9 @@ usrp2_mboard_impl::usrp2_mboard_impl(      _clock_ctrl = usrp2_clock_ctrl::make(_iface);      _codec_ctrl = usrp2_codec_ctrl::make(_iface);      _serdes_ctrl = usrp2_serdes_ctrl::make(_iface); +    //_gps_ctrl = usrp2_gps_ctrl::make(_iface); + +    //if(_gps_ctrl->gps_detected()) std::cout << "GPS time: " << _gps_ctrl->get_time() << std::endl;      //TODO move to dsp impl...      //load the allowed decim/interp rates @@ -64,25 +69,25 @@ usrp2_mboard_impl::usrp2_mboard_impl(      //most if not all junk packets will never make it to the socket.      this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); -    //init the rx control registers -    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _recv_frame_size); -    _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1); -    _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset -    _iface->poke32(U2_REG_RX_CTRL_VRT_HEADER, 0 +    //setup the vrt rx registers +    _iface->poke32(_iface->regs.rx_ctrl_nsamps_per_pkt, _recv_frame_size); +    _iface->poke32(_iface->regs.rx_ctrl_nchannels, 1); +    _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1); //reset +    _iface->poke32(_iface->regs.rx_ctrl_vrt_header, 0          | (0x1 << 28) //if data with stream id          | (0x1 << 26) //has trailer          | (0x3 << 22) //integer time other          | (0x1 << 20) //fractional time sample count      ); -    _iface->poke32(U2_REG_RX_CTRL_VRT_STREAM_ID, 0); -    _iface->poke32(U2_REG_RX_CTRL_VRT_TRAILER, 0); -    _iface->poke32(U2_REG_TIME64_TPS, size_t(get_master_clock_freq())); +    _iface->poke32(_iface->regs.rx_ctrl_vrt_stream_id, 0); +    _iface->poke32(_iface->regs.rx_ctrl_vrt_trailer, 0); +    _iface->poke32(_iface->regs.time64_tps, size_t(get_master_clock_freq()));      //init the tx control registers -    _iface->poke32(U2_REG_TX_CTRL_NUM_CHAN, 0);    //1 channel -    _iface->poke32(U2_REG_TX_CTRL_CLEAR_STATE, 1); //reset -    _iface->poke32(U2_REG_TX_CTRL_REPORT_SID, 1);  //sid 1 (different from rx) -    _iface->poke32(U2_REG_TX_CTRL_POLICY, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET); +    _iface->poke32(_iface->regs.tx_ctrl_num_chan, 0);    //1 channel +    _iface->poke32(_iface->regs.tx_ctrl_clear_state, 1); //reset +    _iface->poke32(_iface->regs.tx_ctrl_report_sid, 1);  //sid 1 (different from rx) +    _iface->poke32(_iface->regs.tx_ctrl_policy, U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET);      //init the ddc      init_ddc_config(); @@ -139,35 +144,45 @@ void usrp2_mboard_impl::update_clock_config(void){      }      //set the pps flags -    _iface->poke32(U2_REG_TIME64_FLAGS, pps_flags); +    _iface->poke32(_iface->regs.time64_flags, pps_flags);      //clock source ref 10mhz -    switch(_clock_config.ref_source){ -    case clock_config_t::REF_INT : _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); break; -    case clock_config_t::REF_SMA : _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); break; -    case clock_config_t::REF_MIMO: _iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); break; -    default: throw std::runtime_error("usrp2: unhandled clock configuration reference source"); +    if(_iface->is_usrp2p()) { +        switch(_clock_config.ref_source){ +        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x12); break; +        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break; +        case clock_config_t::REF_MIMO: _iface->poke32(_iface->regs.misc_ctrl_clock, 0x15); break; +        default: throw std::runtime_error("usrp2: unhandled clock configuration reference source"); +        } +    } else { +        switch(_clock_config.ref_source){ +        case clock_config_t::REF_INT : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x10); break; +        case clock_config_t::REF_SMA : _iface->poke32(_iface->regs.misc_ctrl_clock, 0x1C); break; +        case clock_config_t::REF_MIMO: _iface->poke32(_iface->regs.misc_ctrl_clock, 0x15); break; +        default: throw std::runtime_error("usrp2: unhandled clock configuration reference source"); +        }      }      //clock source ref 10mhz -    bool use_external = _clock_config.ref_source != clock_config_t::REF_INT; +    bool use_external = (_clock_config.ref_source != clock_config_t::REF_INT) +                     || (_iface->is_usrp2p()); //USRP2P has an internal 10MHz TCXO      _clock_ctrl->enable_external_ref(use_external);  }  void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){      //set the ticks -    _iface->poke32(U2_REG_TIME64_TICKS, time_spec.get_tick_count(get_master_clock_freq())); +    _iface->poke32(_iface->regs.time64_ticks, time_spec.get_tick_count(get_master_clock_freq()));      //set the flags register      boost::uint32_t imm_flags = (now)? U2_FLAG_TIME64_LATCH_NOW : U2_FLAG_TIME64_LATCH_NEXT_PPS; -    _iface->poke32(U2_REG_TIME64_IMM, imm_flags); +    _iface->poke32(_iface->regs.time64_imm, imm_flags);      //set the seconds (latches in all 3 registers) -    _iface->poke32(U2_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs())); +    _iface->poke32(_iface->regs.time64_secs, boost::uint32_t(time_spec.get_full_secs()));  }  void usrp2_mboard_impl::handle_overflow(void){ -    _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); +    _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1);      if (_continuous_streaming){ //re-issue the stream command if already continuous          this->issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);      } @@ -175,11 +190,11 @@ void usrp2_mboard_impl::handle_overflow(void){  void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){      _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS; -    _iface->poke32(U2_REG_RX_CTRL_STREAM_CMD, dsp_type1::calc_stream_cmd_word( +    _iface->poke32(_iface->regs.rx_ctrl_stream_cmd, dsp_type1::calc_stream_cmd_word(          stream_cmd, _recv_frame_size      )); -    _iface->poke32(U2_REG_RX_CTRL_TIME_SECS,  boost::uint32_t(stream_cmd.time_spec.get_full_secs())); -    _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); +    _iface->poke32(_iface->regs.rx_ctrl_time_secs,  boost::uint32_t(stream_cmd.time_spec.get_full_secs())); +    _iface->poke32(_iface->regs.rx_ctrl_time_ticks, stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));  }  /*********************************************************************** @@ -189,7 +204,6 @@ static const std::string dboard_name = "0";  void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){      named_prop_t key = named_prop_t::extract(key_); -      //handle the get request conditioned on the key      switch(key.as<mboard_prop_t>()){      case MBOARD_PROP_NAME: @@ -242,7 +256,7 @@ void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){      case MBOARD_PROP_TIME_NOW:{              usrp2_iface::pair64 time64( -                _iface->peek64(U2_REG_TIME64_SECS_RB, U2_REG_TIME64_TICKS_RB) +                _iface->peek64(_iface->regs.time64_secs_rb, _iface->regs.time64_ticks_rb)              );              val = time_spec_t(                  time64.first, time64.second, get_master_clock_freq() @@ -270,8 +284,7 @@ void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){   * MBoard Set Properties   **********************************************************************/  void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){ - -    //handle the get request conditioned on the key +    //handle the set request conditioned on the key      switch(key.as<mboard_prop_t>()){      case MBOARD_PROP_CLOCK_CONFIG: @@ -297,7 +310,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){          //sanity check          UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1);          //set the mux -        _iface->poke32(U2_REG_DSP_RX_MUX, dsp_type1::calc_rx_mux_word( +        _iface->poke32(_iface->regs.dsp_rx_mux, dsp_type1::calc_rx_mux_word(              _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()          ));          return; @@ -308,7 +321,7 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){          //sanity check          UHD_ASSERT_THROW(_tx_subdev_spec.size() == 1);          //set the mux -        _iface->poke32(U2_REG_DSP_TX_MUX, dsp_type1::calc_tx_mux_word( +        _iface->poke32(_iface->regs.dsp_tx_mux, dsp_type1::calc_tx_mux_word(              _dboard_manager->get_tx_subdev(_tx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>()          ));          return; diff --git a/host/lib/usrp/usrp2/serdes_ctrl.cpp b/host/lib/usrp/usrp2/serdes_ctrl.cpp index e83dceb96..1cda22f45 100644 --- a/host/lib/usrp/usrp2/serdes_ctrl.cpp +++ b/host/lib/usrp/usrp2/serdes_ctrl.cpp @@ -27,11 +27,11 @@ class usrp2_serdes_ctrl_impl : public usrp2_serdes_ctrl{  public:      usrp2_serdes_ctrl_impl(usrp2_iface::sptr iface){          _iface = iface; -        _iface->poke32(U2_REG_MISC_CTRL_SERDES, U2_FLAG_MISC_CTRL_SERDES_ENABLE | U2_FLAG_MISC_CTRL_SERDES_RXEN); +        _iface->poke32(_iface->regs.misc_ctrl_serdes, U2_FLAG_MISC_CTRL_SERDES_ENABLE | U2_FLAG_MISC_CTRL_SERDES_RXEN);      }      ~usrp2_serdes_ctrl_impl(void){ -        _iface->poke32(U2_REG_MISC_CTRL_SERDES, 0); //power-down +        _iface->poke32(_iface->regs.misc_ctrl_serdes, 0); //power-down      }  private: diff --git a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp new file mode 100644 index 000000000..d5f80a919 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp @@ -0,0 +1,78 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP2_CLK_REGS_HPP +#define INCLUDED_USRP2_CLK_REGS_HPP + +#include "usrp2_regs.hpp" + +class usrp2_clk_regs_t { +public: +  usrp2_clk_regs_t(void) { ; } +  usrp2_clk_regs_t(boost::uint16_t hw_rev) { +    test = 0; +    fpga = 1; +    adc = (hw_rev >= usrp2_rev_nums(N2XX)) ? 2 : 4; +    dac = 3; +    serdes = (hw_rev >= usrp2_rev_nums(N2XX)) ? 4 : 2; //only used by usrp2+ +    tx_db = (hw_rev >= usrp2_rev_nums(N2XX)) ? 5 : 6; +     +    switch(hw_rev) { +    case usrp2_rev_nums(USRP2_REV3): +        exp = 2; +        break; +    case usrp2_rev_nums(USRP2_REV4): +        exp = 5; +        break; +    case usrp2_rev_nums(N2XX): +        exp = 6; +        break; +    default: +        throw std::runtime_error("Unknown hardware revision"); +        break; +    } +     +    rx_db = 7; +  } + +  static int output(int clknum) { return 0x3C + clknum; } +  static int div_lo(int clknum) { return 0x48 + 2 * clknum; } +  static int div_hi(int clknum) { return 0x49 + 2 * clknum; } + +  const static int acounter = 0x04; +  const static int bcounter_msb = 0x05; +  const static int bcounter_lsb = 0x06; +  const static int pll_1 = 0x07; +  const static int pll_2 = 0x08; +  const static int pll_3 = 0x09; +  const static int pll_4 = 0x0A; +  const static int ref_counter_msb = 0x0B; +  const static int ref_counter_lsb = 0x0C; +  const static int pll_5 = 0x0D; +  const static int update = 0x5A; + +  int test; +  int fpga; +  int adc; +  int dac; +  int serdes; +  int exp; +  int tx_db; +  int rx_db; +}; + +#endif //INCLUDED_USRP2_CLK_REGS_HPP diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index f237d6377..10cb86962 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -24,6 +24,7 @@  #include <boost/asio.hpp> //used for htonl and ntohl  #include <boost/assign/list_of.hpp>  #include <boost/format.hpp> +#include <boost/tokenizer.hpp>  #include <stdexcept>  #include <algorithm> @@ -50,17 +51,18 @@ public:   **********************************************************************/      usrp2_iface_impl(udp_simple::sptr ctrl_transport){          _ctrl_transport = ctrl_transport; +         +        mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100); +        regs = usrp2_get_regs(get_hw_rev());           //check the fpga compatibility number -        const boost::uint32_t fpga_compat_num = this->peek32(U2_REG_COMPAT_NUM_RB); +        const boost::uint32_t fpga_compat_num = this->peek32(this->regs.compat_num_rb);          if (fpga_compat_num != USRP2_FPGA_COMPAT_NUM){              throw std::runtime_error(str(boost::format(                  "Expected fpga compatibility number %d, but got %d:\n"                  "The fpga build is not compatible with the host code build."              ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_compat_num));          } - -        mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100);      }      ~usrp2_iface_impl(void){ @@ -175,6 +177,58 @@ public:      }  /*********************************************************************** + * UART + **********************************************************************/ +    void write_uart(boost::uint8_t dev, const std::string &buf){ +      //first tokenize the string into 20-byte substrings +      boost::offset_separator f(20, 1, true, true); +      boost::tokenizer<boost::offset_separator> tok(buf, f); +      std::vector<std::string> queue(tok.begin(), tok.end()); + +      BOOST_FOREACH(std::string item, queue) { +        //setup the out data +        usrp2_ctrl_data_t out_data; +        out_data.id = htonl(USRP2_CTRL_ID_HEY_WRITE_THIS_UART_FOR_ME_BRO); +        out_data.data.uart_args.dev = dev; +        out_data.data.uart_args.bytes = item.size(); + +        //limitation of uart transaction size +        UHD_ASSERT_THROW(item.size() <= sizeof(out_data.data.uart_args.data)); + +        //copy in the data +        std::copy(item.begin(), item.end(), out_data.data.uart_args.data); + +        //send and recv +        usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_MAN_I_TOTALLY_WROTE_THAT_UART_DUDE); +      } +    } + +    std::string read_uart(boost::uint8_t dev){ +    	int readlen = 20; +      std::string result; +    	while(readlen == 20) { //while we keep receiving full packets +        //setup the out data +        usrp2_ctrl_data_t out_data; +        out_data.id = htonl(USRP2_CTRL_ID_SO_LIKE_CAN_YOU_READ_THIS_UART_BRO); +        out_data.data.uart_args.dev = dev; +        out_data.data.uart_args.bytes = 20; + +        //limitation of uart transaction size +        //UHD_ASSERT_THROW(num_bytes <= sizeof(out_data.data.uart_args.data)); + +        //send and recv +        usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_I_HELLA_READ_THAT_UART_DUDE); +        readlen = in_data.data.uart_args.bytes; + +        //copy out the data +        result += std::string((const char *)in_data.data.uart_args.data, (size_t)readlen); +      } +      return result; +    } + +/***********************************************************************   * Send/Recv over control   **********************************************************************/      usrp2_ctrl_data_t ctrl_send_and_recv(const usrp2_ctrl_data_t &out_data){ @@ -205,6 +259,15 @@ public:          }          throw std::runtime_error("usrp2 no control response");      } +     +    bool is_usrp2p(void) { +        return (get_hw_rev() >= usrp2_rev_nums(N2XX)); +    } + +    boost::uint16_t get_hw_rev(void) { +        return boost::lexical_cast<boost::uint16_t>(mb_eeprom["rev"]); +    } +  private:      //this lovely lady makes it all possible @@ -242,7 +305,6 @@ private:          UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE);          return T(ntohl(in_data.data.poke_args.data));      } -  };  /*********************************************************************** @@ -251,3 +313,4 @@ private:  usrp2_iface::sptr usrp2_iface::make(udp_simple::sptr ctrl_transport){      return usrp2_iface::sptr(new usrp2_iface_impl(ctrl_transport));  } + diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index bf36cbf6e..d7e5df9f5 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -26,6 +26,7 @@  #include <boost/cstdint.hpp>  #include <utility>  #include "fw_common.h" +#include "usrp2_regs.hpp"  /*!   * The usrp2 interface class: @@ -104,6 +105,19 @@ public:          bool readback      ) = 0; +    virtual void write_uart(boost::uint8_t dev, const std::string &buf) = 0; + +    virtual std::string read_uart(boost::uint8_t dev) = 0; +     +    virtual boost::uint16_t get_hw_rev(void) = 0; +     +    virtual bool is_usrp2p(void) = 0; + +    /*! +     * Register map selected from USRP2/USRP2+. +     */ +    usrp2_regs_t regs; +          //motherboard eeprom map structure      uhd::usrp::mboard_eeprom_t mb_eeprom;  }; diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 71f52878c..738c398d9 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -21,6 +21,7 @@  #include "usrp2_iface.hpp"  #include "clock_ctrl.hpp"  #include "codec_ctrl.hpp" +#include "gps_ctrl.hpp"  #include "serdes_ctrl.hpp"  #include <uhd/device.hpp>  #include <uhd/utils/pimpl.hpp> @@ -104,6 +105,7 @@ private:      usrp2_clock_ctrl::sptr _clock_ctrl;      usrp2_codec_ctrl::sptr _codec_ctrl;      usrp2_serdes_ctrl::sptr _serdes_ctrl; +    usrp2_gps_ctrl::sptr _gps_ctrl;      //properties for this mboard      void get(const wax::obj &, wax::obj &); @@ -130,6 +132,9 @@ private:      wax_obj_proxy::sptr _rx_codec_proxy;      wax_obj_proxy::sptr _tx_codec_proxy; +    void rx_codec_set_gain(float, const std::string &); +    uhd::dict<std::string, float> _codec_rx_gains; +      //properties interface for rx dboard      void rx_dboard_get(const wax::obj &, wax::obj &);      void rx_dboard_set(const wax::obj &, const wax::obj &); diff --git a/host/lib/usrp/usrp2/usrp2_regs.cpp b/host/lib/usrp/usrp2/usrp2_regs.cpp new file mode 100644 index 000000000..5853e91e5 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_regs.cpp @@ -0,0 +1,98 @@ +// +// Copyright 2010 Ettus Research LLC +// +// 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, see <http://www.gnu.org/licenses/>. +// + +#include "usrp2_regs.hpp" + +int sr_addr(int misc_output_base, int sr) { +	return misc_output_base + 4 * sr; +} + +usrp2_regs_t usrp2_get_regs(boost::uint16_t hw_rev) { +  //how about you just make this dependent on hw_rev instead of doing the init before main, and give up the const globals, since the application won't ever need both. +  const int misc_output_base = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_MISC_OUTPUT_BASE : USRP2_MISC_OUTPUT_BASE, +            gpio_base        = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_GPIO_BASE        : USRP2_GPIO_BASE, +            atr_base         = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_ATR_BASE         : USRP2_ATR_BASE, +            bp_base          = (hw_rev >= usrp2_rev_nums(N2XX)) ? USRP2P_BP_STATUS_BASE   : USRP2_BP_STATUS_BASE; + +  usrp2_regs_t x; +  x.sr_misc = 0; +  x.sr_tx_prot_eng = 32; +  x.sr_rx_prot_eng = 48; +  x.sr_buffer_pool_ctrl = 64; +  x.sr_udp_sm = 96; +  x.sr_tx_dsp = 208; +  x.sr_tx_ctrl = 224; +  x.sr_rx_dsp = 160; +  x.sr_rx_ctrl = 176; +  x.sr_time64 = 192; +  x.sr_simtimer = 198; +  x.sr_last = 255; +  x.misc_ctrl_clock = sr_addr(misc_output_base, 0); +  x.misc_ctrl_serdes = sr_addr(misc_output_base, 1); +  x.misc_ctrl_adc = sr_addr(misc_output_base, 2); +  x.misc_ctrl_leds = sr_addr(misc_output_base, 3); +  x.misc_ctrl_phy = sr_addr(misc_output_base, 4); +  x.misc_ctrl_dbg_mux = sr_addr(misc_output_base, 5); +  x.misc_ctrl_ram_page = sr_addr(misc_output_base, 6); +  x.misc_ctrl_flush_icache = sr_addr(misc_output_base, 7); +  x.misc_ctrl_led_src = sr_addr(misc_output_base, 8); +  x.time64_secs = sr_addr(misc_output_base, x.sr_time64 + 0); +  x.time64_ticks = sr_addr(misc_output_base, x.sr_time64 + 1); +  x.time64_flags = sr_addr(misc_output_base, x.sr_time64 + 2); +  x.time64_imm = sr_addr(misc_output_base, x.sr_time64 + 3); +  x.time64_tps = sr_addr(misc_output_base, x.sr_time64 + 4); +  x.time64_secs_rb = bp_base + 4*10; +  x.time64_ticks_rb = bp_base + 4*11; +  x.compat_num_rb = bp_base + 4*12; +  x.dsp_tx_freq = sr_addr(misc_output_base, x.sr_tx_dsp + 0); +  x.dsp_tx_scale_iq = sr_addr(misc_output_base, x.sr_tx_dsp + 1); +  x.dsp_tx_interp_rate = sr_addr(misc_output_base, x.sr_tx_dsp + 2); +  x.dsp_tx_mux = sr_addr(misc_output_base, x.sr_tx_dsp + 4); +  x.dsp_rx_freq = sr_addr(misc_output_base, x.sr_rx_dsp + 0); +  x.dsp_rx_scale_iq = sr_addr(misc_output_base, x.sr_rx_dsp + 1); +  x.dsp_rx_decim_rate = sr_addr(misc_output_base, x.sr_rx_dsp + 2); +  x.dsp_rx_dcoffset_i = sr_addr(misc_output_base, x.sr_rx_dsp + 3); +  x.dsp_rx_dcoffset_q = sr_addr(misc_output_base, x.sr_rx_dsp + 4); +  x.dsp_rx_mux = sr_addr(misc_output_base, x.sr_rx_dsp + 5); +  x.gpio_io = gpio_base + 0; +  x.gpio_ddr = gpio_base + 4; +  x.gpio_tx_sel = gpio_base + 8; +  x.gpio_rx_sel = gpio_base + 12; +  x.atr_idle_txside = atr_base + 0; +  x.atr_idle_rxside = atr_base + 2; +  x.atr_intx_txside = atr_base + 4; +  x.atr_intx_rxside = atr_base + 6; +  x.atr_inrx_txside = atr_base + 8; +  x.atr_inrx_rxside = atr_base + 10; +  x.atr_full_txside = atr_base + 12; +  x.atr_full_rxside = atr_base + 14; +  x.rx_ctrl_stream_cmd = sr_addr(misc_output_base, x.sr_rx_ctrl + 0); +  x.rx_ctrl_time_secs = sr_addr(misc_output_base, x.sr_rx_ctrl + 1); +  x.rx_ctrl_time_ticks = sr_addr(misc_output_base, x.sr_rx_ctrl + 2); +  x.rx_ctrl_clear_overrun = sr_addr(misc_output_base, x.sr_rx_ctrl + 3); +  x.rx_ctrl_vrt_header = sr_addr(misc_output_base, x.sr_rx_ctrl + 4); +  x.rx_ctrl_vrt_stream_id = sr_addr(misc_output_base, x.sr_rx_ctrl + 5); +  x.rx_ctrl_vrt_trailer = sr_addr(misc_output_base, x.sr_rx_ctrl + 6); +  x.rx_ctrl_nsamps_per_pkt = sr_addr(misc_output_base, x.sr_rx_ctrl + 7); +  x.rx_ctrl_nchannels = sr_addr(misc_output_base, x.sr_rx_ctrl + 8); +  x.tx_ctrl_num_chan = sr_addr(misc_output_base, x.sr_tx_ctrl + 0); +  x.tx_ctrl_clear_state = sr_addr(misc_output_base, x.sr_tx_ctrl + 1); +  x.tx_ctrl_report_sid = sr_addr(misc_output_base, x.sr_tx_ctrl + 2); +  x.tx_ctrl_policy = sr_addr(misc_output_base, x.sr_tx_ctrl + 3); + +  return x; +} diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 064ad4e95..d84106f36 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -18,6 +18,97 @@  #ifndef INCLUDED_USRP2_REGS_HPP  #define INCLUDED_USRP2_REGS_HPP +#include <boost/cstdint.hpp> + +enum usrp2_rev_nums { +    USRP2_REV3 = 0x0003, +    USRP2_REV4 = 0x0004, +    N2XX       = 0x0A00 +}; + +#define USRP2_MISC_OUTPUT_BASE  0xD400 +#define USRP2_GPIO_BASE         0xC800 +#define USRP2_ATR_BASE          0xE400 +#define USRP2_BP_STATUS_BASE    0xCC00 + +#define USRP2P_MISC_OUTPUT_BASE 0x2000 +#define USRP2P_GPIO_BASE        0x3200 +#define USRP2P_ATR_BASE         0x3800 +#define USRP2P_BP_STATUS_BASE   0x3300 + +typedef struct { +    int sr_misc; +    int sr_tx_prot_eng; +    int sr_rx_prot_eng; +    int sr_buffer_pool_ctrl; +    int sr_udp_sm; +    int sr_tx_dsp; +    int sr_tx_ctrl; +    int sr_rx_dsp; +    int sr_rx_ctrl; +    int sr_time64; +    int sr_simtimer; +    int sr_last; +    int misc_ctrl_clock; +    int misc_ctrl_serdes; +    int misc_ctrl_adc; +    int misc_ctrl_leds; +    int misc_ctrl_phy; +    int misc_ctrl_dbg_mux; +    int misc_ctrl_ram_page; +    int misc_ctrl_flush_icache; +    int misc_ctrl_led_src; +    int time64_secs; // value to set absolute secs to on next PPS +    int time64_ticks; // value to set absolute ticks to on next PPS +    int time64_flags; // flags -- see chart below +    int time64_imm; // set immediate (0=latch on next pps, 1=latch immediate, default=0) +    int time64_tps; // ticks per second rollover count +    int time64_secs_rb; +    int time64_ticks_rb; +    int compat_num_rb; +    int dsp_tx_freq; +    int dsp_tx_scale_iq; +    int dsp_tx_interp_rate; +    int dsp_tx_mux; +    int dsp_rx_freq; +    int dsp_rx_scale_iq; +    int dsp_rx_decim_rate; +    int dsp_rx_dcoffset_i; +    int dsp_rx_dcoffset_q; +    int dsp_rx_mux; +    int gpio_base; +    int gpio_io; +    int gpio_ddr; +    int gpio_tx_sel; +    int gpio_rx_sel; +    int atr_base; +    int atr_idle_txside; +    int atr_idle_rxside; +    int atr_intx_txside; +    int atr_intx_rxside; +    int atr_inrx_txside; +    int atr_inrx_rxside; +    int atr_full_txside; +    int atr_full_rxside; +    int rx_ctrl_stream_cmd; +    int rx_ctrl_time_secs; +    int rx_ctrl_time_ticks; +    int rx_ctrl_clear_overrun; +    int rx_ctrl_vrt_header; +    int rx_ctrl_vrt_stream_id; +    int rx_ctrl_vrt_trailer; +    int rx_ctrl_nsamps_per_pkt; +    int rx_ctrl_nchannels; +    int tx_ctrl_num_chan; +    int tx_ctrl_clear_state; +    int tx_ctrl_report_sid; +    int tx_ctrl_policy; +} usrp2_regs_t; + +extern const usrp2_regs_t usrp2_regs; //the register definitions, set in usrp2_regs.cpp and usrp2p_regs.cpp + +usrp2_regs_t usrp2_get_regs(boost::uint16_t hw_rev); +  ////////////////////////////////////////////////////  // Settings Bus, Slave #7, Not Byte Addressable!  // @@ -25,27 +116,12 @@  // 1KB of address space (== 256 32-bit write-only regs) -#define MISC_OUTPUT_BASE        0xD400 +//#define MISC_OUTPUT_BASE        0xD400  //#define TX_PROTOCOL_ENGINE_BASE 0xD480  //#define RX_PROTOCOL_ENGINE_BASE 0xD4C0  //#define BUFFER_POOL_CTRL_BASE   0xD500  //#define LAST_SETTING_REG        0xD7FC  // last valid setting register -#define SR_MISC 0 -#define SR_TX_PROT_ENG 32 -#define SR_RX_PROT_ENG 48 -#define SR_BUFFER_POOL_CTRL 64 -#define SR_UDP_SM 96 -#define SR_TX_DSP 208 -#define SR_TX_CTRL 224 -#define SR_RX_DSP 160 -#define SR_RX_CTRL 176 -#define SR_TIME64 192 -#define SR_SIMTIMER 198 -#define SR_LAST 255 - -#define _SR_ADDR(sr)    ((MISC_OUTPUT_BASE) + (4*(sr))) -  /////////////////////////////////////////////////  // SPI Slave Constants  //////////////////////////////////////////////// @@ -58,20 +134,11 @@  #define SPI_SS_TX_DAC   32  #define SPI_SS_TX_ADC   64  #define SPI_SS_TX_DB   128 +#define SPI_SS_ADS62P44 256 //for usrp2p  /////////////////////////////////////////////////  // Misc Control  //////////////////////////////////////////////// -#define U2_REG_MISC_CTRL_CLOCK           _SR_ADDR(0) -#define U2_REG_MISC_CTRL_SERDES          _SR_ADDR(1) -#define U2_REG_MISC_CTRL_ADC             _SR_ADDR(2) -#define U2_REG_MISC_CTRL_LEDS            _SR_ADDR(3) -#define U2_REG_MISC_CTRL_PHY             _SR_ADDR(4) // LSB is reset line to eth phy -#define U2_REG_MISC_CTRL_DBG_MUX         _SR_ADDR(5) -#define U2_REG_MISC_CTRL_RAM_PAGE        _SR_ADDR(6) // FIXME should go somewhere else... -#define U2_REG_MISC_CTRL_FLUSH_ICACHE    _SR_ADDR(7) // Flush the icache -#define U2_REG_MISC_CTRL_LED_SRC         _SR_ADDR(8) // HW or SW control for LEDs -  #define U2_FLAG_MISC_CTRL_SERDES_ENABLE 8  #define U2_FLAG_MISC_CTRL_SERDES_PRBSEN 4  #define U2_FLAG_MISC_CTRL_SERDES_LOOPEN 2 @@ -99,15 +166,6 @@     *     * </pre>     */ -#define U2_REG_TIME64_SECS  _SR_ADDR(SR_TIME64 + 0)  // value to set absolute secs to on next PPS -#define U2_REG_TIME64_TICKS _SR_ADDR(SR_TIME64 + 1)  // value to set absolute ticks to on next PPS -#define U2_REG_TIME64_FLAGS _SR_ADDR(SR_TIME64 + 2)  // flags - see chart above -#define U2_REG_TIME64_IMM   _SR_ADDR(SR_TIME64 + 3)  // set immediate (0=latch on next pps, 1=latch immediate, default=0) -#define U2_REG_TIME64_TPS   _SR_ADDR(SR_TIME64 + 4)  // the ticks per second rollover count - -#define U2_REG_TIME64_SECS_RB  (0xCC00 + 4*10) -#define U2_REG_TIME64_TICKS_RB (0xCC00 + 4*11) -#define U2_REG_COMPAT_NUM_RB   (0xCC00 + 4*12)  //pps flags (see above)  #define U2_FLAG_TIME64_PPS_NEGEDGE (0 << 0) @@ -121,34 +179,72 @@  /////////////////////////////////////////////////  // DSP TX Regs  //////////////////////////////////////////////// -#define U2_REG_DSP_TX_FREQ         _SR_ADDR(SR_TX_DSP + 0) -#define U2_REG_DSP_TX_SCALE_IQ     _SR_ADDR(SR_TX_DSP + 1) // {scale_i,scale_q} -#define U2_REG_DSP_TX_INTERP_RATE  _SR_ADDR(SR_TX_DSP + 2) -#define U2_REG_DSP_TX_MUX  _SR_ADDR(SR_TX_DSP + 4) + +  /*! +   * \brief output mux configuration. +   * +   * <pre> +   *     3                   2                   1                        +   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   *  +-------------------------------+-------+-------+-------+-------+ +   *  |                                               | DAC1  |  DAC0 | +   *  +-------------------------------+-------+-------+-------+-------+ +   *  +   *  There are N DUCs (1 now) with complex inputs and outputs. +   *  There are two DACs. +   *  +   *  Each 4-bit DACx field specifies the source for the DAC +   *  Each subfield is coded like this:  +   *  +   *     3 2 1 0 +   *    +-------+ +   *    |   N   | +   *    +-------+ +   *  +   *  N specifies which DUC output is connected to this DAC. +   *  +   *   N   which interp output +   *  ---  ------------------- +   *   0   DUC 0 I +   *   1   DUC 0 Q +   *   2   DUC 1 I +   *   3   DUC 1 Q +   *   F   All Zeros +   *    +   * The default value is 0x10 +   * </pre> +   */ +  /////////////////////////////////////////////////  // DSP RX Regs  //////////////////////////////////////////////// -#define U2_REG_DSP_RX_FREQ         _SR_ADDR(SR_RX_DSP + 0) -#define U2_REG_DSP_RX_SCALE_IQ     _SR_ADDR(SR_RX_DSP + 1) // {scale_i,scale_q} -#define U2_REG_DSP_RX_DECIM_RATE   _SR_ADDR(SR_RX_DSP + 2) -#define U2_REG_DSP_RX_DCOFFSET_I   _SR_ADDR(SR_RX_DSP + 3) // Bit 31 high sets fixed offset mode, using lower 14 bits, -                                                       // otherwise it is automatic  -#define U2_REG_DSP_RX_DCOFFSET_Q   _SR_ADDR(SR_RX_DSP + 4) // Bit 31 high sets fixed offset mode, using lower 14 bits -#define U2_REG_DSP_RX_MUX  _SR_ADDR(SR_RX_DSP + 5)         // called adc_mux in dsp_core_rx.v + +  /*! +   * \brief input mux configuration. +   * +   * This determines which ADC (or constant zero) is connected to  +   * each DDC input.  There are N DDCs (1 now).  Each has two inputs. +   * +   * <pre> +   * Mux value: +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                                                       |Q0 |I0 | +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * +   * The default value is 0x4 +   * </pre> +   */  ////////////////////////////////////////////////  // GPIO, Slave 4  //////////////////////////////////////////////// -// -// These go to the daughterboard i/o pins -// -#define U2_REG_GPIO_BASE 0xC800 - -#define U2_REG_GPIO_IO         U2_REG_GPIO_BASE + 0  // 32 bits, gpio io pins (tx high 16 bits, rx low 16 bits) -#define U2_REG_GPIO_DDR        U2_REG_GPIO_BASE + 4  // 32 bits, gpio ddr, 1 means output (tx high 16 bits, rx low 16 bits) -#define U2_REG_GPIO_TX_SEL     U2_REG_GPIO_BASE + 8  // 16 2-bit fields select which source goes to TX DB -#define U2_REG_GPIO_RX_SEL     U2_REG_GPIO_BASE + 12 // 16 2-bit fields select which source goes to RX DB  // each 2-bit sel field is layed out this way  #define U2_FLAG_GPIO_SEL_GPIO      0 // if pin is an output, set by GPIO register @@ -159,40 +255,20 @@  ///////////////////////////////////////////////////  // ATR Controller, Slave 11  //////////////////////////////////////////////// -#define U2_REG_ATR_BASE  0xE400 -#define U2_REG_ATR_IDLE_TXSIDE  U2_REG_ATR_BASE + 0 -#define U2_REG_ATR_IDLE_RXSIDE  U2_REG_ATR_BASE + 2 -#define U2_REG_ATR_INTX_TXSIDE  U2_REG_ATR_BASE + 4 -#define U2_REG_ATR_INTX_RXSIDE  U2_REG_ATR_BASE + 6 -#define U2_REG_ATR_INRX_TXSIDE  U2_REG_ATR_BASE + 8 -#define U2_REG_ATR_INRX_RXSIDE  U2_REG_ATR_BASE + 10 -#define U2_REG_ATR_FULL_TXSIDE  U2_REG_ATR_BASE + 12 -#define U2_REG_ATR_FULL_RXSIDE  U2_REG_ATR_BASE + 14  ///////////////////////////////////////////////////  // RX CTRL regs  /////////////////////////////////////////////////// -// The following 3 are logically a single command register. -// They are clocked into the underlying fifo when time_ticks is written. -#define U2_REG_RX_CTRL_STREAM_CMD        _SR_ADDR(SR_RX_CTRL + 0) // {now, chain, num_samples(30) -#define U2_REG_RX_CTRL_TIME_SECS         _SR_ADDR(SR_RX_CTRL + 1) -#define U2_REG_RX_CTRL_TIME_TICKS        _SR_ADDR(SR_RX_CTRL + 2) - -#define U2_REG_RX_CTRL_CLEAR_OVERRUN     _SR_ADDR(SR_RX_CTRL + 3) // write anything to clear overrun -#define U2_REG_RX_CTRL_VRT_HEADER        _SR_ADDR(SR_RX_CTRL + 4) // word 0 of packet.  FPGA fills in packet counter -#define U2_REG_RX_CTRL_VRT_STREAM_ID     _SR_ADDR(SR_RX_CTRL + 5) // word 1 of packet. -#define U2_REG_RX_CTRL_VRT_TRAILER       _SR_ADDR(SR_RX_CTRL + 6) -#define U2_REG_RX_CTRL_NSAMPS_PER_PKT    _SR_ADDR(SR_RX_CTRL + 7) -#define U2_REG_RX_CTRL_NCHANNELS         _SR_ADDR(SR_RX_CTRL + 8) // 1 in basic case, up to 4 for vector sources +  ///////////////////////////////////////////////////  // TX CTRL regs  /////////////////////////////////////////////////// -#define U2_REG_TX_CTRL_NUM_CHAN          _SR_ADDR(SR_TX_CTRL + 0) -#define U2_REG_TX_CTRL_CLEAR_STATE       _SR_ADDR(SR_TX_CTRL + 1) -#define U2_REG_TX_CTRL_REPORT_SID        _SR_ADDR(SR_TX_CTRL + 2) -#define U2_REG_TX_CTRL_POLICY            _SR_ADDR(SR_TX_CTRL + 3) +//#define U2_REG_TX_CTRL_NUM_CHAN          _SR_ADDR(SR_TX_CTRL + 0) +//#define U2_REG_TX_CTRL_CLEAR_STATE       _SR_ADDR(SR_TX_CTRL + 1) +//#define U2_REG_TX_CTRL_REPORT_SID        _SR_ADDR(SR_TX_CTRL + 2) +//#define U2_REG_TX_CTRL_POLICY            _SR_ADDR(SR_TX_CTRL + 3)  #define U2_FLAG_TX_CTRL_POLICY_WAIT          (0x1 << 0)  #define U2_FLAG_TX_CTRL_POLICY_NEXT_PACKET   (0x1 << 1) diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py new file mode 100755 index 000000000..456f273df --- /dev/null +++ b/host/utils/usrp2p_fw_update.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python +# +# Copyright 2010 Ettus Research LLC +# +# 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, see <http://www.gnu.org/licenses/>. +# + +# TODO: make it work +# TODO: make it autodetect UHD devices +# TODO: you should probably watch sequence numbers + +import optparse +import math +import os +import re +import struct +import socket +import sys + +######################################################################## +# constants +######################################################################## +UDP_FW_UPDATE_PORT = 49154 +UDP_MAX_XFER_BYTES = 1024 +UDP_TIMEOUT = 3 +UDP_POLL_INTERVAL = 0.10 #in seconds + +USRP2_FW_PROTO_VERSION = 6 + +#from bootloader_utils.h + +FPGA_IMAGE_SIZE_BYTES = 1572864 +FW_IMAGE_SIZE_BYTES = 31744 +SAFE_FPGA_IMAGE_LOCATION_ADDR = 0x00000000 +SAFE_FW_IMAGE_LOCATION_ADDR = 0x003F0000 +PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00180000 +PROD_FW_IMAGE_LOCATION_ADDR = 0x00300000 + +FLASH_DATA_PACKET_SIZE = 256 + +#see fw_common.h +FLASH_ARGS_FMT = '!LLLLL256s' +FLASH_INFO_FMT = '!LLLLL256x' +FLASH_IP_FMT =   '!LLLL260x' + +class update_id_t: +  USRP2_FW_UPDATE_ID_WAT = ord(' ') +  USRP2_FW_UPDATE_ID_OHAI_LOL = ord('a') +  USRP2_FW_UPDATE_ID_OHAI_OMG = ord('A') +  USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = ord('f') +  USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = ord('F') +  USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = ord('e') +  USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = ord('E') +  USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = ord('d') +  USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = ord('D') +  USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = ord('B') +  USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = ord('w') +  USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = ord('W') +  USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = ord('r') +  USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = ord('R') +  USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = ord('s') +  USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = ord('S') +  USRP2_FW_UPDATE_ID_KTHXBAI = ord('~') + +_seq = -1 +def seq(): +  global _seq +  _seq = _seq+1 +  return _seq  + +######################################################################## +# helper functions +######################################################################## +def unpack_flash_args_fmt(s): +  return struct.unpack(FLASH_ARGS_FMT, s) #(proto_ver, pktid, seq, flash_addr, length, data) + +def unpack_flash_info_fmt(s): +  return struct.unpack(FLASH_INFO_FMT, s) #(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def unpack_flash_ip_fmt(s): +  return struct.unpack(FLASH_IP_FMT, s) #(proto_ver, pktid, seq, ip_addr) + +def pack_flash_args_fmt(proto_ver, pktid, seq, flash_addr, length, data): +  return struct.pack(FLASH_ARGS_FMT, proto_ver, pktid, seq, flash_addr, length, data) + +def pack_flash_info_fmt(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes): +  return struct.pack(FLASH_INFO_FMT, proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def send_and_recv(pkt, ip): +  update_socket = create_socket() + +  try: +    update_socket.sendto(pkt, (ip, UDP_FW_UPDATE_PORT)) +  except Exception, e:  +    print e +    sys.exit(1) + +  try: +    (recv_pkt, recv_addr) = update_socket.recvfrom(UDP_MAX_XFER_BYTES) +  except Exception, e:  +    print e +    sys.exit(1) + +  if recv_addr != (options.ip, UDP_FW_UPDATE_PORT): +    raise Exception, "Packet received from invalid IP %s, expected %s" % (recv_addr, options.ip) + +  return recv_pkt + +def create_socket(): +  socket.setdefaulttimeout(UDP_TIMEOUT) +  update_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +  return update_socket + +#just here to validate comms +def init_update(ip): +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0, "") +  in_pkt = send_and_recv(out_pkt, ip) +  (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) +  if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: +    print "USRP2P found." +  else: +    raise Exception, "Invalid reply received from device." + +#  print "Incoming:\n\tVer: %i\n\tID: %c\n\tSeq: %i\n\tIP: %i\n" % (proto_ver, chr(pktid), rxseq, ip_addr) + +def get_flash_info(ip): +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL, seq(), 0, 0, "") +  in_pkt = send_and_recv(out_pkt, ip) + +  (proto_ver, pktid, rxseq, sector_size_bytes, memory_size_bytes) = unpack_flash_info_fmt(in_pkt) + +  if pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG: +    raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + +  return (memory_size_bytes, sector_size_bytes) + +def burn_fw(ip, fw, fpga, reset, safe): +  init_update(ip) +  (flash_size, sector_size) = get_flash_info(ip) + +  print "Flash size: %i\nSector size: %i" % (flash_size, sector_size) + +  if fpga: +    if safe: +        image_location = SAFE_FPGA_IMAGE_LOCATION_ADDR +    else: +        image_location = PROD_FPGA_IMAGE_LOCATION_ADDR +     +    fpga_file = open(fpga, 'rb') +    fpga_image = fpga_file.read() +    erase_image(ip, image_location, FPGA_IMAGE_SIZE_BYTES) +    write_image(ip, fpga_image, image_location) +    verify_image(ip, fpga_image, image_location) + +  if fw: +    if safe: +        image_location = SAFE_FW_IMAGE_LOCATION_ADDR +    else: +        image_location = PROD_FW_IMAGE_LOCATION_ADDR +         +    fw_file = open(fw, 'rb') +    fw_image = fw_file.read() +    erase_image(ip, image_location, FW_IMAGE_SIZE_BYTES) +    write_image(ip, fw_image, image_location) +    verify_image(ip, fw_image, image_location) +     +  if reset: +    reset_usrp(ip) + +def write_image(ip, image, addr): +  print "Writing image" +#we split the image into smaller (256B) bits and send them down the wire +  while image: +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, image[:FLASH_DATA_PACKET_SIZE]) +    in_pkt = send_and_recv(out_pkt, ip) + +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +    if pktid != update_id_t.USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +    image = image[FLASH_DATA_PACKET_SIZE:] +    addr += FLASH_DATA_PACKET_SIZE + +def verify_image(ip, image, addr): +  print "Verifying data" +  readsize = len(image) +  readdata = str() +  while readsize > 0: +    if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize +    else: thisreadsize = FLASH_DATA_PACKET_SIZE +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "") +    in_pkt = send_and_recv(out_pkt, ip) +     +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +    if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +    readdata += data[:thisreadsize] +    readsize -= FLASH_DATA_PACKET_SIZE +    addr += FLASH_DATA_PACKET_SIZE + +  print "Read back %i bytes" % len(readdata) +#  print readdata + +#  for i in range(256, 512): +#    print "out: %i in: %i" % (ord(image[i]), ord(readdata[i])) + +  if readdata != image: +    print "Verify failed. Image did not write correctly." +  else: +    print "Success." +     +def reset_usrp(ip): +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0, "") +    in_pkt = send_and_recv(out_pkt, ip) +     +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) +    if pktid == update_id_t.USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG: +        raise Exception, "Device failed to reset." + +def erase_image(ip, addr, length): +  #get flash info first +  out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length, "") +  in_pkt = send_and_recv(out_pkt, ip) + +  (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +  if pktid != update_id_t.USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG: +    raise Exception, "Invalid reply %c from device." % (chr(pktid)) + +  print "Erasing %i bytes at %i" % (length, addr) + +  #now wait for it to finish +  while(1): +    out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL, seq(), 0, 0, "") +    in_pkt = send_and_recv(out_pkt, ip) + +    (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + +    if pktid == update_id_t.USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG: break +    elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG: +      raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + +######################################################################## +# command line options +######################################################################## +def get_options(): +    parser = optparse.OptionParser() +    parser.add_option("--ip",   type="string",                 help="USRP2P firmware address",        default='') +    parser.add_option("--fw",   type="string",                 help="firmware image path (optional)", default='') +    parser.add_option("--fpga", type="string",                 help="fpga image path (optional)",     default='') +    parser.add_option("--reset", action="store_true",          help="reset the device after writing", default=False) +    parser.add_option("--overwrite-safe", action="store_true", help="never ever use this option", default=False) +    (options, args) = parser.parse_args() + +    return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': +    options = get_options() +    if not options.ip: raise Exception, 'no ip address specified' + +    if not options.fpga and not options.fw and not options.reset: raise Exception, 'Must specify either a firmware image or FPGA image, and/or reset.' +     +    if options.overwrite_safe: +        print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.") +        print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.") +        response = raw_input("""Type "yes" to continue, or anything else to quit: """) +        if response != "yes": +            sys.exit(0) +     +    burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) diff --git a/images/Makefile b/images/Makefile index cfd7ff591..79e7b3362 100644 --- a/images/Makefile +++ b/images/Makefile @@ -78,12 +78,13 @@ $(_usrp1_fpga_4rx_rbf):  	cp $(_usrp1_fpga_dir)/std_4rx_0tx.rbf $@  ######################################################################## -# USRP2 firmware +# USRP2/2+ firmware  ########################################################################  ifdef HAS_MB_GCC  _usrp2_fw_dir = $(TOP_FW_DIR)/microblaze  _usrp2_fw_bin = $(BUILT_IMAGES_DIR)/usrp2_fw.bin +_usrp2p_fw_bin = $(BUILT_IMAGES_DIR)/usrp2p_fw.bin  IMAGES_LIST += $(_usrp2_fw_bin)  $(_usrp2_fw_bin): @@ -91,7 +92,8 @@ $(_usrp2_fw_bin):  	cd $(_usrp2_fw_dir) && ./configure --host=mb  	make -C $(_usrp2_fw_dir) clean  	make -C $(_usrp2_fw_dir) all -	cp $(_usrp2_fw_dir)/usrp2/usrp2_txrx_uhd.bin $@ +	cp $(_usrp2_fw_dir)/usrp2/usrp2_txrx_uhd.bin $(_usrp2_fw_bin) +	cp $(_usrp2_fw_dir)/usrp2p/usrp2p_txrx_uhd.bin $(_usrp2p_fw_bin)  endif @@ -112,6 +114,22 @@ $(_usrp2_fpga_bin):  endif  ######################################################################## +# USRP2 fpga +######################################################################## +ifdef HAS_XTCLSH + +_usrp2p_fpga_dir = $(TOP_FPGA_DIR)/usrp2/top/u2plus +_usrp2p_fpga_bin = $(BUILT_IMAGES_DIR)/usrp2p_fpga.bin +IMAGES_LIST += $(_usrp2p_fpga_bin) + +$(_usrp2p_fpga_bin): +	cd $(_usrp2p_fpga_dir) && make -f Makefile.udp clean +	cd $(_usrp2p_fpga_dir) && make -f Makefile.udp bin +	cp $(_usrp2p_fpga_dir)/build-ISE12/u2plus.bin $@ + +endif + +########################################################################  # Build rules  ########################################################################  ##little rule to make the images directory | 
