diff options
Diffstat (limited to 'firmware')
48 files changed, 5538 insertions, 1 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/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/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 a576d1ccb..ae123f531 100644 --- a/firmware/microblaze/lib/Makefile.inc +++ b/firmware/microblaze/lib/Makefile.inc @@ -41,6 +41,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/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/loader_parser.c b/firmware/microblaze/lib/loader_parser.c new file mode 100644 index 000000000..96457a164 --- /dev/null +++ b/firmware/microblaze/lib/loader_parser.c @@ -0,0 +1,324 @@ +/* -*- 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 "loader_parser.h" +#include <quadradio/loader_bits.h> +#include <quadradio/flashdir.h> +#include <quadradio/simple_binary_format.h> +#include <spi_flash.h> +#include <nonstdio.h> +//#include <assert.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include "ethernet.h" +#include "qr_settings.h" + +#define min(a,b) ((a) < (b) ? (a) : (b)) + +static spi_flash_async_state_t async_state; + + +static caldiv_eeprom_setter_t _caldiv_set_rev = NULL; +static caldiv_eeprom_setter_t _caldiv_set_ser = NULL; +static caldiv_eeprom_setter_t _caldiv_set_mod = NULL; + +void +register_caldiv_eeprom_setters(caldiv_eeprom_setter_t set_rev, +			       caldiv_eeprom_setter_t set_ser, +			       caldiv_eeprom_setter_t set_mod) +{ +  _caldiv_set_rev = set_rev; +  _caldiv_set_ser = set_ser; +  _caldiv_set_mod = set_mod; +} + + +// big-endian +static uint32_t  +get32(const unsigned char *s) +{ +  return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; +} + +// big-endian +static unsigned char * +put32(unsigned char *s, uint32_t v) +{ +  s[0] = (v >> 24) & 0xff; +  s[1] = (v >> 16) & 0xff; +  s[2] = (v >>  8) & 0xff; +  s[3] = v & 0xff; +  return s + 4; +} + +static bool +erased_p(uint32_t flash_addr, size_t nbytes) +{ +  unsigned char	buf[64]; + +  size_t n; +  for (size_t i = 0; i < nbytes; i += n, flash_addr += n){ +    n = min(nbytes - i, sizeof(buf)); +    spi_flash_read(flash_addr, n, buf); +    for (size_t j = 0; j < n; j++) +      if (buf[j] != 0xff) +	return false; +  } +  return true; +} + +static bool +erase_flash(uint32_t addr, uint32_t len) +{ +  if (addr % spi_flash_sector_size() != 0) +    return false; + +  if (len % spi_flash_sector_size() != 0) +    return false; + +  spi_flash_async_erase_start(&async_state, addr, len); +  // FIXME? check to see if erase was successful +  return true; +} + +static bool +map_slot(uint32_t slot, uint32_t *slot_start, uint32_t *slot_len, uint32_t *status) +{ +  // This case doesn't require a valid flashdir, and in fact can be used as  +  // part of writing the intial flashdir. +  if (QLD_SLOT_DOM(slot) == QLD_DOM_UNMAPPED){ +    int flash_size = get_flash_size(); +    if (flash_size == 0){ +      *status = QLDS_FAILED;	// Can't find the flash.  most likely a h/w problem. +      return false; +    } +    *slot_start = 0; +    *slot_len = flash_size; +    return true; +  } + +  const struct flashdir *fd = get_flashdir(); +  if (fd == 0) +    return false; + +  uint32_t slot_num = QLD_SLOT_NUM(slot); + +  switch(QLD_SLOT_DOM(slot)){ +  case QLD_DOM_FPGA: +    if (slot_num >= fd->fpga_nslots){ +      *status = QLDS_INVALID_ARG; +      return false; +    } +    *slot_start = fd->slot[slot_num + fd->fpga_slot0].start << spi_flash_log2_sector_size(); +    *slot_len = fd->slot[slot_num + fd->fpga_slot0].len << spi_flash_log2_sector_size(); +    return true; + +  case QLD_DOM_FW: +    if (slot_num >= fd->fw_nslots){ +      *status = QLDS_INVALID_ARG; +      return false; +    } +    *slot_start = fd->slot[slot_num + fd->fw_slot0].start << spi_flash_log2_sector_size(); +    *slot_len = fd->slot[slot_num + fd->fw_slot0].len << spi_flash_log2_sector_size(); +    return true; + +  default: +    *status = QLDS_INVALID_ARG; +    return false; +  } +} + + +static bool +check_flashdir(void) +{ +  return get_flashdir() != 0; +} + +void +loader_parser(const unsigned char *input, size_t ilen, +	      unsigned char *output, size_t max_olen, size_t *actual_olen) +{ +  //assert (max_olen >= 8); +  if (!(max_olen >= 8)) +    abort(); + +  *actual_olen = 0; +  uint32_t status = QLDS_BAD_PKT; + +  uint32_t cmd = get32(input); +  uint32_t nonce = get32(input+4); +  uint32_t slot = 0; +  uint32_t addr = 0; +  uint32_t len = 0; + +  if (ilen < 8){ +    nonce = -1; +    goto done; +  } + +  uint32_t slot_start;		// offset in flash +  uint32_t slot_len;		// length in bytes +   +  if (ilen >= 5 * sizeof(uint32_t)){ +    slot = get32(input+8); +    addr = get32(input+12); +    len = get32(input+16); +  } + +  switch (cmd){ +  case QLD_FLASH_ERASE_START: +    // <QLD_FLASH_ERASE_START> <nonce> <slot> <addr> <len> +    if (ilen != 5 * sizeof(uint32_t)) +      goto done; + +    if (!check_flashdir()){ +      status = QLDS_BAD_FLASHDIR; +      goto done; +    } +    if (!map_slot(slot, &slot_start, &slot_len, &status)) +      goto done; + +    if (QLD_SLOT_DOM(slot) != QLD_DOM_UNMAPPED){ +      addr = slot_start; +      len = slot_len; +    } +    //printf("flash_erase: addr = 0x%x, len=0x%x\n", addr, len); + +    if (0 && erased_p(addr, len)){	// already erased? +      async_state.first = async_state.last = async_state.current = 0; +      goto ok; +    } + +    if (erase_flash(addr, len)) +      goto ok; + +    status = QLDS_FAILED; +    goto done; +     + +  case QLD_FLASH_ERASE_POLL: +    // <QLD_FLASH_ERASE_POLL> <nonce> +    if (ilen != 2 * sizeof(uint32_t)) +      goto done; + +    if (spi_flash_async_erase_poll(&async_state)) +      goto ok; + +    status = QLDS_BUSY; +    goto done; + + +  case QLD_FLASH_WRITE: +    // <QLD_FLASH_WRITE> <nonce> <slot> <addr> <len> <data ...> +    if (ilen < 5 * sizeof(uint32_t)) +      goto done; + +    if (ilen != 5 * sizeof(uint32_t) + len) +      goto done; + +    if (!check_flashdir()){ +      status = QLDS_BAD_FLASHDIR; +      goto done; +    } +    if (!map_slot(slot, &slot_start, &slot_len, &status)) +      goto done; + +    addr += slot_start; +    len = min(len, slot_len); + +    if (spi_flash_program(addr, len, &input[5*sizeof(uint32_t)])) +      goto ok; + +    status = QLDS_FAILED; +    goto done; +     + +  case QLD_FLASH_READ: +  case QLD_MEM_READ: +  case QLD_MEM_WRITE: +  case QLD_GOTO: +    status = QLDS_NOTIMPLEMENTED; +    goto done; + +  case QLD_PING: +    // <QLD_PING> <nonce> +    if (ilen != 2 * sizeof(uint32_t)) +      goto done; +    goto ok; + +#if 0 +  case QLD_EEPROM_SET_XXX: +  // <QLD_EEPROM_SET_XXX> <nonce> <arg> <idlen> <idstr> <data ...> +  { +    uint32_t arg    = get32(input+2*sizeof(uint32_t)); +    uint32_t idlen  = get32(input+3*sizeof(uint32_t)); +    uint8_t *idstr  = (uint8_t*)input+4*sizeof(uint32_t); +    uint8_t *data_p = idstr+idlen; +     +    //handle the ethernet cases +    if (strncmp((char*)idstr, "ip", idlen) == 0){ +        struct ip_addr addr = {get32(data_p)}; +        ethernet_set_ip_addr(arg, addr); +    } +    else if (strncmp((char*)idstr, "mac", idlen) == 0){ +        eth_mac_addr_t addr; +        memcpy(&addr, data_p, sizeof(addr)); +        ethernet_set_mac_addr(arg, &addr); +    } +    //handle the main board eeprom +    else if (strncmp((char*)idstr, "qrrev", idlen) == 0){ +        qr_set_revision(get32(data_p)); +    } +    else if (strncmp((char*)idstr, "qrser", idlen) == 0){ +        qr_set_serial(get32(data_p)); +    } +    else if (strncmp((char*)idstr, "qrmod", idlen) == 0){ +        qr_set_model(get32(data_p)); +    } +    //handle the caldiv eeprom +    else if (strncmp((char*)idstr, "cdrev", idlen) == 0){ +        if (_caldiv_set_rev) _caldiv_set_rev(get32(data_p)); +    } +    else if (strncmp((char*)idstr, "cdser", idlen) == 0){ +        if (_caldiv_set_ser) _caldiv_set_ser(get32(data_p)); +    } +    else if (strncmp((char*)idstr, "cdmod", idlen) == 0){ +        if (_caldiv_set_ser) _caldiv_set_mod(get32(data_p)); +    } +    else { +        goto done; +    } +  } +  goto ok; +#endif + +  default: +    status = QLDS_UNKNOWN_CMD; +    goto done; +  } + + ok: +  status = QLDS_OK; + + done: +  put32(output, nonce); +  put32(output+4, status); +  *actual_olen = 2*sizeof(uint32_t); +} diff --git a/firmware/microblaze/lib/loader_parser.h b/firmware/microblaze/lib/loader_parser.h new file mode 100644 index 000000000..365317bd7 --- /dev/null +++ b/firmware/microblaze/lib/loader_parser.h @@ -0,0 +1,38 @@ +/* -*- 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 <stddef.h> +#include <stdint.h> + +/* + * max_olen must be at least 8 bytes.  1KB is recommended. + */ +void +loader_parser(const unsigned char *input, size_t ilen, +	      unsigned char *output, size_t max_olen, size_t *actual_olen); + +/* + * Major kludge-master altert! + * This function registers functions for setting caldiv eeprom stuff. + * This way, the parser does not depend on the qpn apps at compile time. + */ +typedef void(*caldiv_eeprom_setter_t)(uint32_t); +void register_caldiv_eeprom_setters( +	caldiv_eeprom_setter_t set_rev, +	caldiv_eeprom_setter_t set_ser, +	caldiv_eeprom_setter_t set_mod); diff --git a/firmware/microblaze/lib/udp_burner_packet.c b/firmware/microblaze/lib/udp_burner_packet.c new file mode 100644 index 000000000..d86a4cf4a --- /dev/null +++ b/firmware/microblaze/lib/udp_burner_packet.c @@ -0,0 +1,38 @@ +/* -*- 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 "udp_burner_packet.h" +#include "net_common.h" +#include "loader_parser.h" +#include <stdint.h> +#include <compiler.h> +#include <nonstdio.h> + + +void +handle_udp_burner_packet(struct socket_address src, struct socket_address dst, +			 unsigned char *payload, int payload_len) +{ +  unsigned char reply[128] _AL4; +  size_t actual_reply_len; +  loader_parser(payload, payload_len, reply, sizeof(reply), &actual_reply_len); +  send_udp_pkt(dst.port, src, reply, actual_reply_len); +} diff --git a/firmware/microblaze/lib/udp_burner_packet.h b/firmware/microblaze/lib/udp_burner_packet.h new file mode 100644 index 000000000..0f4025712 --- /dev/null +++ b/firmware/microblaze/lib/udp_burner_packet.h @@ -0,0 +1,28 @@ +/* -*- 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_UDP_BURNER_PACKET_H +#define INCLUDED_UDP_BURNER_PACKET_H + +#include <net/socket_address.h> + +void +handle_udp_burner_packet(struct socket_address src, struct socket_address dst, +			 unsigned char *payload, int payload_len); + + +#endif /* INCLUDED_UDP_BURNER_PACKET_H */ diff --git a/firmware/microblaze/lib/xilinx_s3_icap.c b/firmware/microblaze/lib/xilinx_s3_icap.c new file mode 100644 index 000000000..8aa7fd297 --- /dev/null +++ b/firmware/microblaze/lib/xilinx_s3_icap.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/>. + */ + + +/* 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 LSB-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 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) +{ +	uint8_t t = swap8(x); + +	icap_regs->icap = t; //DEBUG: does not swap bits +} + +uint8_t +rd_icap(void) +{ +	return swap8(icap_regs->icap); +} + + +void +icap_reload_fpga(uint32_t flash_address) +//this DOES NOT WORK right now. reboot is not getting executed correctly. +{ +	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] = READ_CMD; //legacy command, use FAST_READ_CMD 0x0B after testing + +	//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 + +} 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..ddc7006ca --- /dev/null +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -0,0 +1,66 @@ +# +# 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) \ +	libusrp2p.a \ +	-Wl,-defsym -Wl,_TEXT_START_ADDR=0x8050 \ +	-Wl,-defsym -Wl,_STACK_SIZE=3072 + +#all of this here is to relocate the hardware vectors to somewhere normal. +COMMON_IHX_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 + +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..15d719d73 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader/init_bootloader.c @@ -0,0 +1,108 @@ +/* -*- 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> + +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ +  // nop stub +} + +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"); +} + +//let's clean up this logic. state machine? no, you only have to go through it once. + +//don't need else cases since all these are terminal cases + +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"); + +	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."); +				load_ihex(); +			} +	} + +	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."); +		//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; //_exit will trap in loop +	} +	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..00893db02 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader_utils.c @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +//contains routines for loading programs from Flash. depends on Flash libraries. +#include <string.h> +#include <bootloader_utils.h> +#include <spi_flash.h> + + +int is_valid_fpga_image(uint32_t addr) { +	static const uint8_t fpgaheader[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0x99}; //AA 99 is the standard Xilinx sync sequence, and it's always prefixed with 0xFF padding +	uint8_t buf[10]; +	spi_flash_read(addr, 6, buf); +	return memcmp(buf, fpgaheader, 6) == 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..c72128f43 --- /dev/null +++ b/firmware/microblaze/usrp2p/bootloader_utils.h @@ -0,0 +1,21 @@ +/* -*- 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. +#define FPGA_IMAGE_SIZE_BYTES 2097152 +//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 0x007F0000 +#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00200000 +#define PROD_FW_IMAGE_LOCATION_ADDR 0x00400000 + +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..660f28934 --- /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..9c5b576d7 --- /dev/null +++ b/firmware/microblaze/usrp2p/memory_map.h @@ -0,0 +1,842 @@ +/* -*- 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 << 5) +#define	LED_E		(1 << 3) +#define	LED_D		(1 << 1) +#define	LED_C		(1 << 4) +//      LED_F		// controlled by CPLD +#define	LED_RJ45	(1 << 0) + +#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 +} 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..f65f28477 --- /dev/null +++ b/firmware/microblaze/usrp2p/spi_flash.h @@ -0,0 +1,112 @@ +/* -*- 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 */ + +static inline size_t +spi_flash_sector_size(void) +{ +  return ((size_t) 1) << spi_flash_log2_sector_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..1c65350f7 --- /dev/null +++ b/firmware/microblaze/usrp2p/spi_flash_read.c @@ -0,0 +1,100 @@ +/* -*- 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 + +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]; +  return _spi_flash_log2_sector_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/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 */ | 
